Vai al contenuto

Creare un podcast in automatico a partire da audio vocali e musica

Scritto da:

carlopeluso

Nel 2011, mentre studiavo architettura al Politecnico di Torino e vivevo nel collegio universitario di Grugliasco, mi sono imbattuto in una delle più belle esperienze della mia vita. Insieme al mio amico Angelo ho progettato e realizzato una webradio studentesca che trasmetteva direttamente dal collegio. Il progetto, durato 5 anni è stato di grande scuola per me, sia dal punto di vista umano, sia professionale. Ero il classico “tecnico tutto fare” 🧑‍🔧 : in quel periodo mi sono occupato di piccoli software radiofonici, della programmazione e della gestione del palinsesto, ma ovviamente anche nella manutenzione del server della radio e dello studio dal quale trasmettevamo. Ho conosciuto piu di 60 ragazzi, divisi in gruppi di lavoro, uno per tramissione!

Nell’ultimo periodo (coinciso più o meno con l’ascesa dei podcast su Spotify) è tornata la mia curiosità verso il mondo della radio. Mi sono chiesto quanto fosse possibile migliorare/ottimizzare il processo di creazione di una trasmissione radiofonica o un podcast. Avevo già trattato quest’argomento in un precedente articolo in cui mi concentravo sul come mixare la voce di uno speaker insieme ad altri suoni. In questi giorni ho realizzato un piccolo script che passo passo crea un’intera trasmissione a partire da una cartella di files.

Struttura di una puntata radiofonica

Per realizzare questo piccolo progetto ho utilizzato FFMPEG, nota libreria per la manipolazione del suono (leggi il mio articolo al riguardo con degli esempi d’uso). Questa libreria permette di applicare dei filtri al suono in maniera programmatica, quindi attraverso un linguaggio di programmazione. Studiando la ricchissima documentazione (e con l’aiuto dell’ottima community di FFMPEG) sono riuscito a realizzare questo Batch che programmaticamente mette in sequenza dei file audio, tramite l’uso di dissolvenze o somme di suoni. Vediamo uno schema:

Traducendo questo semplice schema in un codice programmabile, questo somiglia ad un array di oggetti (file audio) che dovranno essere messi in sequenza, ma a certe condizioni:

  • il parlato dovrà sempre avere un sottofondo leggero di musica
  • i brani partiranno con una dissolvenza (fade) nel momento in cui il parlato sta per finire
  • il parlato comincia un momento prima del termine del brano

Ecco uno schema piu preciso di quello che succederà:

Script per elaborare il parlato con un sottofondo

Questo script va eseguito con l’ultima versione di NodeJs installata, e richiede diverse librerie esterne, la piu importante è fluent-ffmpeg, ovvero un wrapper di FFMPEG per NodeJs. Lo script converte una serie di audio registrati con solo voce in diversi file in cui la voce è accompagnata da un sottofondo musicale leggero che parte e finisce in dissolvenza.

var mp3Duration = require('mp3-duration');
var ffmpeg = require('fluent-ffmpeg');
var fs = require('fs')

let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; //un generico array di file audio di voce

let promises = [];

array.forEach(speech => {

    let speechFile = speech + '.mp3';
    let backgroundFile = 'sottofondo.mp3';
    let backgroundLooped = speech + '_onlybackground.mp3';
    let speechWithBackground = speech + '_.mp3'

    let p = mp3Duration(speechFile, function (err, dur) {

        //recupero la durata dello speech
        let durataVoce = Math.round(dur);

        //creo un loop della durata dello speech
        ffmpeg(backgroundFile)
            .inputOption("-stream_loop -1") //loop infinito del sottofondo
            .audioFilters('afade=t=in:ss=0:d=8')  //fadein che dura 8 secondi
            .audioFilters('afade=t=out:st=' + (durataVoce - 10) + ':d=5')   //fadeout che dura 8 secondi e parte 10 secondi prima della fine dell'audio
            .duration(durataVoce) //taglio alla durata della voce
            .audioBitrate(320)
            .output(backgroundLooped)
            .on('end', function () {

                //faccio un mix con la voce

                ffmpeg()
                    .addInput(backgroundLooped)
                    .addInput(speechFile)
                    .complexFilter(['[1]compand=attacks=0.4:points=-80/-80|-12.4/-12.4|-6/-8|0/-6.8|20/-2.8:gain=12:volume=-40[a1]',
                        '[a1]channelsplit=channel_layout=stereo:channels=FR[a2]',
                        '[0][a2]amix=inputs=2:dropout_transition=0.2:weights=1 10,dynaudnorm'])
                    .audioBitrate(320)
                    .save(speechWithBackground)
                    .on('end', function () {
                        //rimuovo il file del background
                        console.log(speech + " - Completato");
                        fs.unlinkSync(backgroundLooped);

                    });
            }).run();

    });

    promises.push(p);
});

Script per mettere in sequenza i file audio

Una volta terminata l’elaborazione dei file audio del parlato, non ci resta che mixare il tutto seguendo lo schema precedente. I brani saranno leggermente anticipati rispetto al termine del parlato, in modo da accentuare l’effetto radiofonico finale. Ogni brano inizierà e finirà con una dissolvenza automatica di 10 secondi (impostata nella variabile fadeDuration). Inoltre verrà applicato un filtro normalizzazione audio, che consentirà di assottigliare le differenze di volume tra i brani, migliorando l’esperienza di ascolto.


var ffmpeg = require('fluent-ffmpeg');

let speeches = [
    {
        tipo: 'musica',
        file: "sigla.mp3"
    },
    {
        tipo: 'voce',
        file: "1_.mp3"
    },
    {
        tipo: 'musica',
        file: "musica1.mp3"
    },
    {
        tipo: 'voce',
        file: "2_.mp3"
    },
    {
        tipo: 'musica',
        file: "musica2.mp3"
    }
]


let command = ffmpeg();
let filters = [];
let combo = 0;
let fadeDuration = 10;

for (let i = 0; i < speeches.length; i++) {

    //aggiungo l'input

    speech = speeches[i];
    command.addInput(speech.file);
    successivo = i + 1;

    //se sono all'ultimo non faccio niente perche mi interessa la combo precedente
    if (i < speeches.length - 1) {

        //se l'attuale è voce
        if (speech.tipo == 'voce') {

            //abbasso il volume della musica successiva
            filters.push('[' + successivo + ']volume=0.3[low' + successivo + ']');

            if (i < 1) {
                filters.push('[' + i + '][low' + successivo + ']acrossfade=d=' + fadeDuration + ':c1=nofade:c2=exp[combo' + combo + ']');
            } else {

                output = '[combo' + (combo + 1) + ']';
                if (successivo == speeches.length - 1) {
                    output = ",loudnorm";
                }

                filters.push('[combo' + combo + '][low' + successivo + ']acrossfade=d=' + fadeDuration + ':c1=nofade:c2=exp' + output);
                combo++;
            }


            //se l'attuale è musica
        } else if (speech.tipo == 'musica') {

            if (i < 1) {
                filters.push('[' + i + ']volume=0.4[low' + i + ']');
                filters.push('[low' + i + '][' + successivo + ']acrossfade=d=' + fadeDuration + ':c1=exp:c2=nofade[combo' + combo + ']');
            } else {

                output = '[combo' + (combo + 1) + ']';
                if (successivo == speeches.length - 1) {
                    output = ",loudnorm";
                }

                filters.push('[combo' + combo + '][' + successivo + ']acrossfade=d=' + fadeDuration + ':c1=exp:c2=nofade' + output);
                combo++;
            }

        }
    }

}

command.complexFilter(filters)
    .save("totale1.mp3")
    .audioBitrate(320)
    .on('end', function () {
        //rimuovo il file del background
        console.log(" - Completato");
    });

Risultato

Ho utilizzato questo piccolo script per realizzare una trasmissione di prova, a partire da un file vocale registrato con lo smartphone. Chiaramente è un progetto ancora molto semplice, ma che crea interessanti spunti su come automatizzare il processo di missaggio (mix) di puntate radiofoniche o podcast.

Articolo precedente

Piccoli siti web sviluppati con un baby-framework in PHP+SCSS

Articolo successivo

Una web app per gestire le videoproiezioni di un evento TEDx

Unisciti alla discussione

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *