2012-08-23 19 views
6

Ho provato a mixare 2 flussi audio PCM lineari a 16 bit e non riesco a superare i problemi di rumore. Penso che provengano da overflow quando si miscelano i campioni insieme.Mixare flussi PCM lineari a 16 bit ed evitare clipping/overflow

Ho seguente funzione ...

short int mix_sample(short int sample1, short int sample2) 
{ 
    return #mixing_algorithm#; 
} 

... e qui è quello che ho cercato come # mixing_algorithm #

sample1/2 + sample2/2 
2*(sample1 + sample2) - 2*(sample1*sample2) - 65535 
(sample1 + sample2) - sample1*sample2 
(sample1 + sample2) - sample1*sample2 - 65535 
(sample1 + sample2) - ((sample1*sample2) >> 0x10) // same as divide by 65535 

Alcuni di loro hanno prodotto risultati migliori rispetto ad altri, ma anche il il miglior risultato conteneva un bel po 'di rumore.

Qualche idea su come risolverlo?

+0

puoi scrivere l'algoritmo completo, non riesco a vedere nessun compito !! – perilbrain

+0

Quando dividi sample1 e sample2 per 2, ottieni l'intervallo di errore di 1. –

risposta

7

ecco un'implementazione descrittiva:

short int mix_sample(short int sample1, short int sample2) { 
    const int32_t result(static_cast<int32_t>(sample1) + static_cast<int32_t>(sample2)); 
    typedef std::numeric_limits<short int> Range; 
    if (Range::max() < result) 
     return Range::max(); 
    else if (Range::min() > result) 
     return Range::min(); 
    else 
     return result; 
} 

da miscelare, è sufficiente aggiungere e clip!

per evitare artefatti di ritaglio, è necessario utilizzare la saturazione o un limitatore. idealmente, avrai un piccolo buffer int32_t con una piccola quantità di lookahead. questo introdurrà la latenza.

più comune di limitazione ovunque, è quello di lasciare un po 'di valore di' headroom 'nel segnale.

+0

Questa soluzione ha funzionato bene. Grazie! – Ragnar

+0

@Ragnar great - you are welcome :) – justin

+1

L'unico modo "corretto" per evitare il clipping è dividere per due. C'è un codice illustrativo qui nella sezione "Distorsione e rumore": http://blog.bjornroche.com/2013/05/the-abcs-of-pcm-uncompressed-digital.html –

0

Penso che dovrebbero essere le funzioni di mappatura [MIN_SHORT, MAX_SHORT] -> [MIN_SHORT, MAX_SHORT] e non sono chiaramente (oltre al primo), quindi si verificano overflow.

Se la proposta di svolgimento non funziona si può anche provare:

((long int)(sample1) + sample2)/2 
+0

Mentre aggiungere i segnali è corretto; con semplice * normalizzazione * per mantenere l'intervallo, un segnale influenzerà l'altro indesiderabilmente. Ad esempio se 'sample1' è sempre zero (silenzioso), dovresti * solo *' sample2', ma ottieni 'sample2/2' - cioè l'output è più tranquillo. – Clifford

+0

Sì, hai completamente ragione. Ma risolve il problema di overflow e clipping. La soluzione migliore IMHO sarebbe quella di scalare i segnali in base al loro valore, come 'w (s1, s2) * s1 + (1-w (s1, s2)) * s2' dove' w (s1, s2) 'è alcune funzioni dove 'w (s1,0) = 1',' w (0, s2) = 0' e '0

-2

Dato che ci si trova nel dominio del tempo, le informazioni sulla frequenza si trovano nella differenza tra i campioni successivi, quando si divide per due si danneggia tale informazione. Ecco perché aggiungere e ritagliare funziona meglio. Naturalmente, il clipping aggiungerà un rumore ad altissima frequenza che probabilmente è filtrato.

+0

Mi aspetto che il rumore che l'OP sta ascoltando sia causato dall'integrazione dei valori, piuttosto che da qualcosa di sottile come un singolo bit di risoluzione persa. – Will

9

La soluzione migliore che ho trovato è given by Viktor Toth. Egli fornisce una soluzione per 8 bit PCM senza segno, e cambiando che per 16 bit firmato PCM, produce questo:

int a = 111; // first sample (-32768..32767) 
int b = 222; // second sample 
int m; // mixed result will go here 

// Make both samples unsigned (0..65535) 
a += 32768; 
b += 32768; 

// Pick the equation 
if ((a < 32768) || (b < 32768)) { 
    // Viktor's first equation when both sources are "quiet" 
    // (i.e. less than middle of the dynamic range) 
    m = a * b/32768; 
} else { 
    // Viktor's second equation when one or both sources are loud 
    m = 2 * (a + b) - (a * b)/32768 - 65536; 
} 

// Output is unsigned (0..65536) so convert back to signed (-32768..32767) 
if (m == 65536) m = 65535; 
m -= 32768; 

Utilizzando questo algoritmo significa che non v'è quasi alcuna necessità di fermare l'uscita in quanto è solo un valore a corto di essere nel raggio d'azione. A differenza della media retta, il volume di una sorgente non viene ridotto anche quando l'altra sorgente è silenziosa.

+0

Cosa intendi per "silenzioso"? - che normalmente sarebbe medio * di bassa magnitudine * (* vicino * al centro), ma qui sembra significare * negativo * (al di sotto del centro), mentre l'equazione "forte" viene eseguita quando * uno o entrambi sono positivi * (prima dello spostamento, ovvero aggiungendo un bias DC)). A parte quel * volume * è una percezione del * segnale *, non un singolo campione - un suono "forte" avrà campioni su tutta la gamma. – Clifford

+0

@Clifford: il centro è al centro dell'intervallo disponibile, quindi se i valori sono compresi tra 0 e 65535, il valore medio è 32767. È meglio spiegato al link alla pagina di Viktor Toth. – Malvineous

+0

Mi rendo conto che - la mia domanda era retorica - i termini "tranquillo" e "forte" sono inesatti e fuorvianti in questo contesto. – Clifford

1

Ecco cosa ho fatto sul mio recente progetto di sintetizzatore.

int* unfiltered = (int *)malloc(lengthOfLongPcmInShorts*4); 
int i; 
for(i = 0; i < lengthOfShortPcmInShorts; i++){ 
    unfiltered[i] = shortPcm[i] + longPcm[i]; 
} 
for(; i < lengthOfLongPcmInShorts; i++){ 
    unfiltered[i] = longPcm[i]; 
} 

int max = 0; 
for(int i = 0; i < lengthOfLongPcmInShorts; i++){ 
    int val = unfiltered[i]; 
    if(abs(val) > max) 
     max = val; 
} 

short int *newPcm = (short int *)malloc(lengthOfLongPcmInShorts*2); 
for(int i = 0; i < lengthOfLongPcmInShorts; i++){ 
    newPcm[i] = (unfilted[i]/max) * MAX_SHRT; 
} 

ho aggiunto tutti i dati PCM in un array intero, in modo che ottengo tutti i dati non filtrato.

Dopo averlo cercato, ho cercato il valore massimo assoluto nell'array intero.

Infine, ho preso l'array intero e l'ho messo in un array int breve prendendo ogni elemento che divide per quel valore massimo e quindi moltiplicando per il valore short int medio.

In questo modo si ottiene la quantità minima di 'spazio di testa' necessaria per adattare i dati.

Si potrebbe essere in grado di fare alcune statistiche sull'array intero e integrare qualche ritaglio, ma per quello che mi serviva la quantità minima di headroom era abbastanza buona per me.