Prevención de la saturación de la señal de audio

Estoy tratando de implementar un algoritmo en señales de audio. El algoritmo opera con la señal cuadro por cuadro. Una de las etapas intermedias requiere la adición de la señal a los cuadros anteriores. El cuadro de salida se satura al final de esta etapa.

Aclaración: el cuadro de salida de la operación de suma es doble, aunque el cuadro de salida final TIENE que ser corto. De ahí el problema de la saturación.

He intentado lo siguiente para prevenir la saturación:
1. limitación estricta: limite cualquier muestra a un max_val predefinido.
2. Normalización: Calcule el máx. del marco y luego escalar cada muestra en el marco apropiadamente.

Ninguno de estos enfoques ha funcionado ya que tengo que trabajar cuadro por cuadro.

Cualquier ayuda para prevenir la saturación de señales es bienvenida.

¿Qué es la saturación física/virtual? ¿Cuál es el máximo/mínimo de la señal de entrada? ¿Está saturada toda la señal o son solo los picos?
los valores de las tramas de señal (operación posterior a la suma) se saturan. el resultado de la operación anterior es doble, pero debe reducirse. de ahí el problema de la saturación.
¿Esto es para música o voz?
@Kellenjb: para ambos.

Respuestas (6)

Tus intentos

Por definición, cuando "limita fuertemente" los valores en su código, está causando la saturación. Puede que no sea saturación en el sentido de desbordar tu corto, pero aún estás distorsionando la onda cuando pasa por un cierto punto. Aquí hay un ejemplo:

Saturación

Me doy cuenta de que probablemente no estés limitando mucho la parte inferior, pero ya lo había dibujado antes de darme cuenta de eso.

Entonces, en otras palabras, el método de limitación estricta no funcionará.

Ahora, para su segundo enfoque, este método hará que haga lo que algunas personas de audio realmente hacen intencionalmente. Estás haciendo que cada cuadro sea tan fuerte como sea posible. Este método puede funcionar bien si obtiene la escala correcta y no le molesta que su música suene fuerte todo el tiempo, pero no es bueno para la mayoría de las personas.

Una solución

Si conoce la ganancia efectiva máxima posible que su sistema puede crear, puede dividir su entrada por esta cantidad. Para averiguar cuál sería esto, deberá revisar su código y determinar cuál es la entrada máxima, darle una ganancia de x, averiguar cuál es la salida máxima en términos de x y luego determinar en qué debería estar x para no saturar nunca. Aplicaría esta ganancia a su señal de audio entrante antes de hacerle cualquier otra cosa.

Esta solución está bien, pero no es excelente para todos, ya que su rango dinámico puede verse afectado un poco, ya que generalmente no estará funcionando a la entrada máxima todo el tiempo.

La otra solución es hacer algo de ganancia automática. Este método es similar al método anterior, pero su ganancia cambiará con el tiempo. Para hacer esto, puede verificar su valor máximo de cada cuadro de su entrada. Utilizará almacenará este número y colocará un filtro de paso bajo simple en sus valores máximos y decidirá qué ganancia aplicar con este valor.

Aquí hay un ejemplo de cuál sería su ganancia versus el volumen de entrada:

ganancia automática

Este tipo de sistema hará que la mayor parte de su audio tenga un alto rango dinámico, pero a medida que comience a acercarse al volumen máximo, puede reducir lentamente su ganancia.

Análisis de los datos

Si desea averiguar qué tipo de valores obtiene su sistema en tiempo real, necesitará algún tipo de salida de depuración. Esta salida cambiará según la plataforma en la que se esté ejecutando, pero aquí hay una idea general de lo que haría. Si está en un entorno integrado, necesitará tener alguna salida en serie. Lo que hará es en ciertas etapas de la salida de su código a un archivo o pantalla o algo de lo que pueda obtener los datos. Tome estos datos y póngalos en excel de matlab y grafíquelos todos en función del tiempo. Probablemente podrá darse cuenta muy fácilmente de dónde van las cosas mal.

Método muy simple

¿Estás saturando tu doble? No lo parece, sino que suena como si estuvieras saturando cuando cambias a un corto. Una forma muy simple y "sucia" de hacer esto es convertir el máximo de su doble (este valor es diferente según su plataforma) y escalarlo para que sea el valor máximo de su corto. Esto garantizará que, suponiendo que no desborde su doble, tampoco desbordará su corto. Lo más probable es que esto resulte en que su salida sea mucho más suave que su entrada. Solo necesitará jugar y usar algunos de los análisis de datos que describí anteriormente para que el sistema funcione perfectamente para usted.

Métodos más avanzados que probablemente no se aplican a usted

En el mundo digital existe un compromiso entre resolución y rango dinámico. Lo que esto significa es que tiene un número fijo de bits asignados a su audio. Si disminuye el rango en el que puede estar su audio, entonces aumenta los bits por rango que tiene. Si piensa en esto en el sentido de voltios y tiene una entrada de 0-5v y un adc de 10 bits, entonces tiene 10 bits para dar a un rango de 5v, por lo general esto se hace de forma lineal. Entonces 0b0000000000 = 0v, 0b1111111111 = 5v y asignas linealmente los voltajes a los bits. En realidad, con el audio, esto no siempre es un buen uso de tus bits.

En el caso de la voz, sus voltajes frente a la probabilidad de esos voltajes se ven así:

pdf

Esto significa que tiene mucho más de su voz en la amplitud más baja y solo una pequeña cantidad en la cantidad más alta. Entonces, en lugar de asignar sus bits linealmente, puede reasignar sus bits para tener más pasos en el rango de amplitud inferior y, por lo tanto, menos en el rango de amplitud superior. Esto le brinda lo mejor de ambos mundos, resolución donde se encuentra la mayor parte de su audio, pero limita su saturación aumentando su rango dinámico.

Ahora, esta reasignación cambiará la forma en que actúan sus filtros y probablemente necesitará volver a trabajar sus filtros, pero es por eso que está en la sección "avanzada". Además, dado que está haciendo su trabajo con un doble y luego lo convierte en un corto, su corto probablemente necesitará ser lineal de todos modos. Su doble ya le da mucha más precisión que la que le dará su corto, por lo que probablemente no haya necesidad de este método.

precisó que no conoce la señal antes de recibirla y está en un entorno de tiempo real. también especificó que estaba manteniendo el mismo límite estricto para toda la señal, que es de donde proviene la saturación.
+1 ganancia automática / división de señal básica por ganancia máxima es lo que probablemente haría. No es necesario conocer la señal de entrada con anticipación para eso.
@Mark, @kellenjb, ahora que veo que él no tiene absolutamente ningún control sobre su entrada, y no está recibiendo entrada, sugeriría la ganancia automática. No estoy seguro de que este sea otro método. Haría que el nivel de ganancia se determinara lentamente durante un largo período de tiempo.

Si esto es similar a cómo se desbordan los valores en C, para cuando haya hecho la suma, se habrá desbordado. Por lo tanto, necesitaría reducir a la mitad el cuadro actual y el valor del cuadro anterior que está agregando antes de agregar, luego agregar y luego, opcionalmente, normalizar.

Pero en cualquier caso, tendrá dificultades para mantener un nivel constante (compresión de audio) si solo tiene un cuadro para trabajar a la vez, porque necesita un promedio a largo plazo que no salte.

Sí. ese es mi problema No tengo estadísticas a largo plazo (no en un entorno en tiempo real de todos modos). ¿Hay alguna técnica en la literatura que proporcione una manera de hacer esto? en este momento, solo elijo un número al azar (muy alto) y lo multiplico escalar con la señal. eso soluciona el problema, pero le va miserablemente cuando se usa una señal con estadísticas diferentes. lea también el comentario anterior (arriba) para obtener más detalles sobre el problema.

Supongo que tiene que transferir el resultado porque está haciendo dsp en tiempo real con audio.

He visto este problema antes cuando los estudiantes cometen un error en la forma en que transfieren el resultado del idft. A menudo, llevan las cosas incorrectamente y terminan causando una acumulación continua. Según tu comentario a Martin, parece que has evitado esto.

En segundo lugar, es por eso que la radio tiene una perilla de volumen que debe usar. Cuando cambias de estación de radio de un transmisor cercano a uno que está lejos, tienes que cambiar cuál es tu nivel de volumen para escucharlo al mismo volumen. Le sugiero que agregue una perilla de ganancia controlable por el usuario, luego, si se satura, está en su control cambiarla. Un usuario normalmente hará esto automáticamente a medida que se vuelve más fuerte porque no le gusta. También lo subirán cuando sea necesario.

Esta perilla cambiaría por lo que está escalando sus valores. Espero que esto ayude, con más información sobre su aplicación podría brindarle otra ayuda. Puede crear un promedio de respuesta muy lento (filtro de paso bajo bajo) que requiere muchos cuadros para cambiar significativamente y usarlo para brindar control de ganancia. Esto significa que un pico repentino causará saturación, pero si el volumen de audio cambia lentamente, lo compensará fácilmente. Incluso si su audio cambia rápidamente, este tipo de filtro evitará que el usuario lo baje y lo suba si cada nivel de volumen relativo se mantiene durante un período prolongado.

sí, en tiempo real o al menos dsp en tiempo real simulado con audio es lo que estoy tratando de hacer. desafortunadamente, no estoy seguro de poder proporcionar más detalles :(. Estoy interesado en saber un poco más sobre el promedio de respuesta lenta que menciona. ¿Qué pasa con el control automático de ganancia?

La única forma de hacer esto es asegurarse de que no pueda saturar. Presumiblemente, su algoritmo tiene algún tipo de "ganancia máxima", lo que significa que sabe lo máximo que puede crecer la señal por cuadro. Debe asegurarse de que el primer cuadro tenga suficiente margen para que la señal no se sature al final.

Su ejemplo tiene una adición, que (a menos que sepa más de lo que ha dicho hasta ahora) aumenta su salida como máximo un bit sobre la entrada (con más conocimiento sobre el sistema, puede decir que promediará no más de, por ejemplo, 2,4 bits en todo el conjunto de tramas). Pero en este caso simple, si tiene 8 fotogramas, tendrá que dejar 8 bits de espacio libre sobre las muestras originales. Entonces, si muestrea a 8 bits y calcula en 16 bits, debería estar bien.

Pero, ¿y si mi salida tiene que ser nuevamente de 8 bits? ese es mi problema el problema no es la saturación del tipo de datos utilizado para la suma, sino las restricciones impuestas por el tamaño de la salida.

@Kellenjb, gracias por las imágenes. Creo que me ayudaron mucho a entender las cosas.

@Sriram, me gustaría que dijeras algunas palabras más sobre lo que deseas que suceda .

Supongo que lo que desearía que sucediera es algo así como "Quiero una salida que siempre sea lo más alta posible, justo al borde del recorte. Pero no quiero que el volumen cambie radicalmente de un fotograma a otro". a continuación. Quiero que el volumen cambie más como si un humano aumentara lentamente la perilla de volumen durante las partes silenciosas y algo más rápido al bajar el volumen cuando comenzara a saturarse. Pero no quiero que un ser humano real gire la perilla de volumen -- Quiero que el software ajuste automáticamente el volumen por mí".

También asumo que su sistema necesita escupir un marco de datos dentro de un marco o dos de ingresar un marco de datos, en tiempo real, por lo que no puedo analizar toda la canción y luego elegir el nivel de volumen perfecto y mantenlo constante durante toda la canción.

Así que supongo que quieres configurar una canalización algo como esto:

(1) Una rutina de interrupción recopila datos en un búfer. A menudo, las personas configuran 2 marcos de almacenamiento: esta rutina llena lentamente un marco mientras las siguientes etapas trabajan con datos en el otro marco. Luego, cuando un marco se llena, haces ping-ping con doble búfer para llenar el otro marco, mientras que las siguientes etapas cambian para trabajar con un marco. Algunas personas que hacen algoritmos como filtros finitos (FIR) almacenan en búfer muchos, muchos marcos de almacenamiento aquí.

(2) En el bucle principal de "fondo", el software ejecuta algún algoritmo DSP en el último búfer de entrada "completo" y genera algunos resultados de salida en el búfer de salida. A menudo, las personas configuran 2 marcos de almacenamiento de búfer de salida, por lo que esta rutina llena rápidamente un marco de salida, mientras que las siguientes etapas drenan lentamente los datos del otro marco, con un esquema de búfer doble de ping-pong. Algunas personas hacen algoritmos como filtros infinitos (IIR) almacenan en búfer muchos, muchos marcos de almacenamiento con estos valores de salida. La mayoría de las veces, el algoritmo DSP toma como entrada las salidas anteriores de esta etapa, nolos resultados de las siguientes etapas. Esta etapa es donde la mayoría de las personas tienen problemas con el recorte y la saturación, por lo que la mayoría de las respuestas que veo aquí se centran en este paso. Pero a partir de sus respuestas, parece que ya se dio cuenta de que necesita un tipo de datos amplio con muchos bits "adicionales" de precisión para calcular estos valores intermedios.

(3) En el bucle principal, el software multiplica cada valor de la trama intermedia por algún factor de escala V para generar un "valor escalado". (No almacena este valor en ningún búfer).

(4) En el ciclo principal, el software reduce cada "valor escalado" al tipo de salida final (¿8 bits?) requerido por su dispositivo de salida, y almacena el resultado en otro búfer. (Es casi seguro que la salida de esta etapa no debe retroalimentarse al algoritmo DSP en el paso 2. "El codificador de sonido BTC" de Roman Black es uno de los pocos algoritmos que conozco donde dicha retroalimentación es útil).

(5) Una rutina de interrupción toma una muestra a la vez del búfer de salida de (4) y la escupe al dispositivo de salida. (Esto también puede usar un arreglo de ping-pong de doble búfer). A menudo, la frecuencia de muestreo de entrada y la frecuencia de muestreo de salida son las mismas (44100 muestras/s), y la misma rutina de interrupción hace (5) y (1).

De su respuesta a Martin, tengo la impresión de que una configuración de volumen fijo particular V1 para V puede ser adecuada para una canción, y otra configuración de volumen fijo V2 puede ser adecuada para otra canción. Imagina que pudieras escuchar toda la canción y calcular el volumen perfecto para esa canción. ¿Cómo lo harías? ¿Está bien si se cortan algunas muestras de la canción?

Tal vez algo como este control de ganancia automático relativamente simple (AGC o VOGAD) resolvería su problema:

  • Cada vez que la etapa 2 termine de producir un cuadro, calcule un nuevo valor V que se usará para escalar las muestras en ese cuadro durante el paso 3. Una de las formas más simples: encuentre el valor más positivo (máximo) y el valor más negativo en ese cuadro , y multiplique ambos valores por el V actual que se usó para el cuadro anterior. Compruebe si alguna de las muestras se recortará (es decir, el resultado está por encima de +127 o por debajo de -127) o si está cerca del recorte (es decir, por encima de +63 o por debajo de -63), y configure el multiplicador de volumen VM de forma adecuada:

    • Si el volumen es demasiado bajo (ninguna de las muestras se recorta, ninguna de las muestras se acerca al recorte), establezca el multiplicador de volumen en un número ligeramente superior a 1: VM = VM_UP, donde VM_UP es algo así como 1,01 o 1 + 2 ^(-12).

    • Si el volumen es demasiado alto (algunas de las muestras se recortan, o demasiadas muestras se acercan demasiado, o ambas cosas), establezca el multiplicador de volumen VM en un número ligeramente inferior a 1: VM = VM_DOWN, donde VM_DOWN es algo así como 0,98 o 1 - 2^(-8).

    • Si el volumen se ve bien (ni demasiado alto ni demasiado bajo), mantenga VM = 1.

  • Periódicamente (idealmente una vez cada muestra, pero si eso consume mucho tiempo una vez por cuadro o una vez por milisegundo o una vez cada décima de segundo puede ser adecuado), reajuste el volumen con una multiplicación de saturación:

    • V := (V * MV).
    • Si V ahora es demasiado pequeño (menor que V_min), establezca V := V_min.
    • Si V ahora es demasiado grande (más que V_max), establezca V := V_max.

Querrá ajustar las constantes de tiempo de compilación VM_UP y VM_DOWN hasta que subjetivamente "suene bien". Tal vez configure VM_DOWN para que, con entradas muy altas, tome uno o dos segundos subir la perilla de volumen del volumen máximo al volumen mínimo. Tal vez configure VM_UP de modo que, con un silencio absoluto, tarde unos diez segundos en girar muy lentamente la perilla de volumen desde el volumen mínimo hasta el volumen máximo.

Puede garantizar que nunca se saturará al reducir instantáneamente el volumen V siempre que parezca que un cuadro podría saturarse, de modo que (con el volumen V reducido) apenas alcanza los picos de +127 o -127. Pero sospecho que sonará mejor si se reduce lentamente el volumen y se saturan algunas muestras durante algunos fotogramas.

Hay una amplia variedad de formas más complicadas de (a) detección de envolvente/pico/"sonoridad perceptual": calcular un número que indica si un cuadro de datos es demasiado alto o demasiado bajo, y en qué medida. También hay una amplia variedad de formas más complicadas para que un AGC (b) use ese número del cuadro actual (y tal vez para los últimos cuadros) y el volumen usado durante el último cuadro para calcular el volumen a usar para el siguiente cuadro.

Otras lecturas:

Puede usar la codificación predictiva lineal para comprimir la señal en un "residual" y un filtro para recrear ese cuadro de audio más adelante. Es bastante complicado, pero aquí hay un buen artículo. Si mantiene el residuo exactamente, este es un algoritmo sin pérdidas. También puede usar menos bits para codificar el residuo y mantener una buena calidad.

J. Makhoul. Predicción lineal: una revisión tutorial. Actas del IEEE, 63(4):561–580, abril de 1975.

Y un capítulo en DAFX tiene algoritmos de MATLAB escritos para usted.

U. Zolzer, editor. DAFX - Efectos de audio digital. John Wiley & Sons, Ltd, Baffins Lane, Chichester, West Sussex, PO 19 1UD, Inglaterra, 2002.

Básicamente, dado un marco de señal de entrada, puede calcular una envolvente espectral y un residual. Vea las imágenes a continuación y observe la amplitud del residual.texto alternativo texto alternativo

Usa el filtro de la segunda figura y dale el residual cuando quieras recrear el sonido. Observe que el residual tiene una amplitud bastante estable y anote las unidades. Necesitas poder codificar al menos eso. El filtro es un filtro "LTI" y tiene buenas propiedades que probablemente podría usar para combinar marcos.

¿Hay algún enlace público a esto?
Leí esto, y lamento decir que entendí la mayor parte. No entiendo como soluciona nada. Parece que estás diciendo que si le das un retraso mayor puedes compensar mejor el volumen.
No hay enlace público, lo siento, pero sé que el libro DAFX está disponible en línea en alguna parte. (google para un libro electrónico) No estoy agregando demoras, excepto las demoras para implementar el filtro en la segunda figura, que se usa para recrear el sonido en un momento posterior. Esto solo se ocupa de la codificación. Transforma una señal de entrada en un filtro y un residual. El residual tiene una amplitud pequeña y se puede codificar con una pequeña cantidad de bits. Se pueden agregar filtros usándolos en serie. Los residuos se pueden agregar sin desbordamiento ya que tienen valores pequeños y se almacenan como un paso intermedio antes de la salida final.
No estoy seguro de cómo LPC resolvería el problema. incluso si agregar los residuos no se desborda, convolucionarlo con el filtro para obtener el marco original y luego tener que convertirlo en un tipo de datos con un rango más corto causaría la saturación. este algoritmo debe ocurrir en tiempo real (o al menos en tiempo real simulado), por lo que mi límite de tiempo para el procesamiento también es muy bajo.
Obviamente, si está agregando números grandes, no podrá jugarlos. Tendrás que normalizar. Lo que digo es que puede usar estos residuos como almacenamiento temporal para evitar el desbordamiento y mantener el rango dinámico hasta que finalmente alcance su señal de salida. Esto se ejecuta en tiempo real en sistemas integrados. Es más barato que un FFT incluso.