2010-05-25 8 views
6

Estoy escribiendo una función C# para hacer compresión de rango dinámico (un efecto de audio que básicamente aplasta los picos transitorios y amplifica todo lo demás para producir un sonido más alto). He escrito una función que hace esto (creo):Ayuda con la función de compresión de rango dinámico (audio)

alt text http://www.freeimagehosting.net/uploads/feea390f84.jpg

public static void Compress(ref short[] input, double thresholdDb, double ratio) 
{ 
    double maxDb = thresholdDb - (thresholdDb/ratio); 
    double maxGain = Math.Pow(10, -maxDb/20.0); 

    for (int i = 0; i < input.Length; i += 2) 
    { 
     // convert sample values to ABS gain and store original signs 
     int signL = input[i] < 0 ? -1 : 1; 
     double valL = (double)input[i]/32768.0; 
     if (valL < 0.0) 
     { 
      valL = -valL; 
     } 
     int signR = input[i + 1] < 0 ? -1 : 1; 
     double valR = (double)input[i + 1]/32768.0; 
     if (valR < 0.0) 
     { 
      valR = -valR; 
     } 

     // calculate mono value and compress 
     double val = (valL + valR) * 0.5; 
     double posDb = -Math.Log10(val) * 20.0; 
     if (posDb < thresholdDb) 
     { 
      posDb = thresholdDb - ((thresholdDb - posDb)/ratio); 
     } 

     // measure L and R sample values relative to mono value 
     double multL = valL/val; 
     double multR = valR/val; 

     // convert compressed db value to gain and amplify 
     val = Math.Pow(10, -posDb/20.0); 
     val = val/maxGain; 

     // re-calculate L and R gain values relative to compressed/amplified 
     // mono value 
     valL = val * multL; 
     valR = val * multR; 

     double lim = 1.5; // determined by experimentation, with the goal 
      // being that the lines below should never (or rarely) be hit 
     if (valL > lim) 
     { 
      valL = lim; 
     } 
     if (valR > lim) 
     { 
      valR = lim; 
     } 

     double maxval = 32000.0/lim; 

     // convert gain values back to sample values 
     input[i] = (short)(valL * maxval); 
     input[i] *= (short)signL; 
     input[i + 1] = (short)(valR * maxval); 
     input[i + 1] *= (short)signR; 
    } 
} 

y estoy llamando con threshold valores entre 10,0 dB y 30,0 dB y relaciones entre 1.5 y 4.0. Esta función definitivamente produce un sonido general más fuerte, pero con un nivel inaceptable de distorsión, incluso en valores de umbral bajos y bajas relaciones.

¿Alguien puede ver algo mal con esta función? ¿Estoy manejando el aspecto estéreo correctamente (la función asume la entrada estéreo)? Como yo (vagamente) entiendo las cosas, no quiero comprimir los dos canales por separado, por lo que mi código está intentando comprimir un valor de muestra mono "virtual" y luego aplicar el mismo grado de compresión al valor de muestra L y R por separado . Sin embargo, no estoy seguro de hacerlo bien.

Creo que parte del problema puede ser la "rodilla dura" de mi función, que inicia bruscamente la compresión cuando se cruza el umbral. Creo que puede que tenga que utilizar un "codo suave" de esta manera:

alt text http://www.freeimagehosting.net/uploads/4c1040fda8.jpg

¿Puede alguien sugerir una modificación a la función de producir la curva de codo suave?

Respuesta

1

Creo que su comprensión básica de cómo hacer la compresión es incorrecta (lo siento;)). No se trata de "comprimir" valores de muestras individuales; eso cambiará radicalmente la forma de onda y producirá distorsiones armónicas severas. Necesitas evaluar el volumen de la señal de entrada en muchas muestras (tendría que buscar la fórmula correcta de Google), y usar esto para aplicar un multiplicador que cambia mucho más gradualmente a las muestras de entrada para generar resultados.

El foro de DSP en kvraudio.com/forum podría indicarle la dirección correcta si le resulta difícil encontrar las técnicas habituales.

+0

Mi comprensión de la compresión de rango está entre el 0 y el 100%. :) En este caso específico, no puedo hacer un promedio (fácil, al menos) porque estoy intentando aplicar compresión para separar trozos de una pieza de audio más grande y luego ajustar los trozos comprimidos nuevamente sin problemas. Sin embargo, creo que tienes razón en que necesito aplicar el multiplicador con cambios más graduales, o bien comprimir la pieza más grande a la vez. – MusiGenesis

+0

He revisado los foros de KVR antes. El principal problema al que me enfrento para encontrar ejemplos de código para este tipo de cosas es que casi todas las cosas que encuentro son para efectos en tiempo real, mientras trato de procesar archivos WAV existentes. – MusiGenesis

+0

Solo pensé en una forma en que puedo hacer un promedio con mis partes separadas.Realmente agradecería los enlaces a cualquier buen recurso/código para esto. – MusiGenesis

Cuestiones relacionadas