[Algorithm] Algoritmo per mixare il suono



Answers

C'è un articolo sul mixaggio here . Sarei interessato a sapere cosa ne pensano gli altri.

Question

Ho due flussi audio grezzi che devo aggiungere insieme. Ai fini di questa domanda, possiamo presumere che abbiano lo stesso bitrate e la stessa profondità di bit (diciamo campione a 16 bit, frequenza di campionamento 44.1khz).

Ovviamente se li aggiungo insieme, trabocchetto e trabatterò il mio spazio a 16 bit. Se li aggiungo e li divido per due, il volume di ciascuno viene dimezzato, il che non è corretto dal punto di vista sonoro - se due persone parlano in una stanza, le loro voci non diventano più basse della metà e un microfono può selezionarle entrambi senza colpire il limitatore.

  • Quindi qual è il metodo corretto per aggiungere questi suoni insieme nel mio mixer software?
  • Ho sbagliato e il metodo corretto è di abbassare il volume di ciascuno della metà?
  • Devo aggiungere un compressore / limitatore o qualche altro stadio di elaborazione per ottenere il volume e l'effetto di missaggio che sto cercando?

-Adamo




convertire i campioni in valori in virgola mobile compresi tra -1.0 e +1.0, quindi:

out = (s1 + s2) - (s1 * s2);

Introdurrà una distorsione pesante quando | s1 + s2 | approccio 1.0 (almeno quando l'ho provato mescolando le onde sinusoidali semplici). Ho letto questa raccomandazione in diverse località, ma a mio modesto parere, è un approccio inutile.

Quello che succede fisicamente quando le onde si "mescolano" è che aggiungono i loro amplitutes, proprio come molti dei poster qui suggeriti. O

  • clip (distorce anche il risultato) o
  • riepiloga i tuoi valori a 16 bit in un numero a 32 bit e poi dividi per il numero delle tue fonti (questo è quello che suggerirei perché è l'unico modo noto per evitare le distorsioni)



Non posso credere che nessuno conosca la risposta corretta. Tutti sono abbastanza vicini ma pur sempre, una filosofia pura. Il più vicino, cioè il migliore era: (s1 + s2) - (s1 * s2). È un approccio eccellente, specialmente per gli MCU.

Quindi, l'algoritmo va:

  1. Scopri il volume in cui desideri che il suono in uscita sia. Può essere la media o il massimo di uno dei segnali.
    factor = average(s1) Si presuppone che entrambi i segnali siano già OK, non in eccesso rispetto al 32767.0
  2. Normalizza entrambi i segnali con questo fattore:
    s1 = (s1/max(s1))*factor
    s2 = (s2/max(s2))*factor
  3. Aggiungili insieme e normalizza il risultato con lo stesso fattore
    output = ((s1+s2)/max(s1+s2))*factor

Nota che dopo il passaggio 1. non hai davvero bisogno di tornare ai numeri interi, puoi lavorare con float nell'intervallo da -1.0 a 1.0 e applicare il ritorno ai numeri interi alla fine con il fattore di potenza scelto in precedenza. Spero di non aver sbagliato ora, perché sono di fretta.




La maggior parte delle applicazioni di missaggio audio eseguiranno il loro mixaggio con numeri in virgola mobile (32 bit è abbastanza buono per mixare un piccolo numero di stream). Converti i campioni a 16 bit in numeri in virgola mobile con l'intervallo da -1,0 a 1,0 che rappresenta il fondo scala nel mondo a 16 bit. Quindi somma i campioni insieme: ora hai un sacco di spazio per la testa. Infine, se si finisce con qualsiasi campione il cui valore vada oltre il fondo scala, è possibile attenuare l'intero segnale o utilizzare limiti duri (valori di ritaglio a 1.0).

Ciò fornirà risultati sonori molto migliori rispetto all'aggiunta di campioni a 16 bit e lasciandoli traboccare. Ecco un esempio di codice molto semplice che mostra come potresti sommare due campioni a 16 bit:

short sample1 = ...;
short sample2 = ...;
float samplef1 = sample1 / 32768.0f;
float samplef2 = sample2 / 32768.0f;
float mixed = samplef1 + sample2f;
// reduce the volume a bit:
mixed *= 0.8;
// hard clipping
if (mixed > 1.0f) mixed = 1.0f;
if (mixed < -1.0f) mixed = -1.0f;
short outputSample = (short)(mixed * 32768.0f)



Se hai bisogno di farlo bene, suggerirei di guardare alle implementazioni del software open source, almeno per la teoria.

Alcuni link:

Audacity

GStreamer

In realtà dovresti probabilmente usare una libreria.




Grazie a tutti per aver condiviso le vostre idee, di recente sto anche facendo del lavoro legato al missaggio del suono. Ho anche fatto qualcosa di sperimentale su questo tema, potrebbe aiutarti ragazzi :).

Notare che sto usando una frequenza di campionamento di 8 Khz e un suono di campionamento a 16 bit (SInt16) in ios RemoteIO AudioUnit.

Lungo i miei esperimenti il ​​risultato migliore che ho trovato era qualcosa di diverso da tutta questa risposta, ma la base è la stessa (come suggerisce Roddy )

" Dovresti aggiungerli insieme, ma ritagliare il risultato nell'intervallo consentito per prevenire l'over / underflow ".

Ma quale dovrebbe essere il modo migliore per aggiungere senza overflow / underflow?

Idea chiave :: Hai due onde sonore che dicono A e B, e l'onda risultante C sarà la superposition di due onde A e B. Il campione con raggio di bit limitato potrebbe farla traboccare. Quindi ora possiamo calcolare la croce del limite massimo al punto di incrocio del limite superiore e del limite minimo sul lato inferiore della forma d'onda di sovrapposizione. Ora sottrarremo il limite massimo del limite al rialzo alla porzione superiore della forma d'onda di sovrapposizione e aggiungiamo il limite minimo del limite inferiore alla porzione inferiore della forma d'onda di sovrapposizione. VOILA ... hai finito.

passi:

  1. Per prima cosa attraversare il circuito dati una volta per il valore massimo della croce del limite superiore e il valore minimo del limite del limite inferiore.
  2. Trasforma un altro traverso ai dati audio, sottrai il valore massimo dalla porzione di dati audio positiva e aggiungi il valore minimo alla parte negativa dei dati audio.

il seguente codice mostrerebbe l'implementazione.

static unsigned long upSideDownValue = 0;
static unsigned long downSideUpValue = 0;
#define SINT16_MIN -32768
#define SINT16_MAX 32767
SInt16* mixTwoVoice (SInt16* RecordedVoiceData, SInt16* RealTimeData, SInt16 *OutputData, unsigned int dataLength){

unsigned long tempDownUpSideValue = 0;
unsigned long tempUpSideDownValue = 0;
//calibrate maker loop
for(unsigned int i=0;i<dataLength ; i++)
{
    SInt32 summedValue = RecordedVoiceData[i] + RealTimeData[i];

    if(SINT16_MIN < summedValue && summedValue < SINT16_MAX)
    {
        //the value is within range -- good boy
    }
    else
    {
       //nasty calibration needed
        unsigned long tempCalibrateValue;
        tempCalibrateValue = ABS(summedValue) - SINT16_MIN; // here an optimization comes ;)

        if(summedValue < 0)
        {
            //check the downside -- to calibrate
            if(tempDownUpSideValue < tempCalibrateValue)
                tempDownUpSideValue = tempCalibrateValue;
        }
        else
        {
            //check the upside ---- to calibrate
            if(tempUpSideDownValue < tempCalibrateValue)
                tempUpSideDownValue = tempCalibrateValue;
        }
    }
}

//here we need some function which will gradually set the value
downSideUpValue = tempUpSideDownValue;
upSideDownValue = tempUpSideDownValue;

//real mixer loop
for(unsigned int i=0;i<dataLength;i++)
{
    SInt32 summedValue = RecordedVoiceData[i] + RealTimeData[i];

    if(summedValue < 0)
    {
        OutputData[i] = summedValue + downSideUpValue;
    }
    else if(summedValue > 0)
    {
        OutputData[i] = summedValue - upSideDownValue;
    }
    else
    {
        OutputData[i] = summedValue;
    }
}

return OutputData;
}

funziona bene per me, ho intenzione più tardi di modificare gradualmente il valore di upSideDownValue e downSideUpValue per ottenere un risultato più fluido.




Hai ragione a aggiungerli insieme. Puoi sempre scansionare la somma dei due file per i punti di picco e ridimensionare l'intero file se colpiscono una specie di soglia (o se la media di esso e i suoi punti circostanti colpiscono una soglia)




// #include <algorithm>
// short ileft, nleft; ...
// short iright, nright; ...

// Mix
float hiL = ileft + nleft;
float hiR = iright + nright;

// Clipping
short left = std::max(-32768.0f, std::min(hiL, 32767.0f));
short right = std::max(-32768.0f, std::min(hiR, 32767.0f));



Ho fatto la seguente cosa:

MAX_VAL = Full 8 or 16 or whatever value
dst_val = your base audio sample
src_val = sample to add to base

Res = (((MAX_VAL - dst_val) * src_val) / MAX_VAL) + dst_val

Moltiplicare il margine sinistro di src per il valore di destinazione normalizzato MAX_VAL e aggiungerlo. Non si fermerà mai, non sarà mai meno rumoroso e suonerà assolutamente naturale.

Esempio:

250.5882 = (((255 - 180) * 240) / 255) + 180

E questo suona bene :)




Links