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?
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
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
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