¿Alguna vez te has lastimado físicamente con tu propio código?

¿Alguna vez se ha lastimado a usted mismo oa otros sin darse cuenta debido a un error en su código? Por mí si.



Hace aproximadamente un año trabajé con archivos WAV generados, había varios miles de ellos. Intenté etiquetarlos, clasificarlos en carpetas, crear metadatos. En el proceso, escuché algunas piezas y, para mi disgusto, resultó que todas comenzaron con un silencio bastante largo. Fue muy molesto, especialmente cuando escuchas una serie de archivos seguidos y constantemente tropiezas con pausas antes de reproducir cada uno de los siguientes. Genial, lo que significa que también tienes que hacer algo al respecto.



Ya había pasado algún tiempo buscando soluciones para eliminar el silencio de los archivos cuando de repente me di cuenta: ¡esto es WAV! Los datos de los archivos WAV suelen ser audio PCM, es decir, cada valor del archivo especifica la amplitud del sonido en algún momento. En consecuencia, si realmente tenemos un silencio completo allí, y no ruido blanco, entonces los ceros sólidos deberían corresponder a este silencio en el archivo, ¿verdad?



$ xxd testfile1.wav | head -n 100

00000000: 5249 4646 64b9 0e00 5741 5645 666d 7420  RIFFd...WAVEfmt 
00000010: 1000 0000 0100 0200 44ac 0000 10b1 0200  ........D.......
00000020: 0400 1000 6461 7461 40b9 0e00 0000 0000  ....data@.......
00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
# ... and a lot more zeros below
      
      





Y ahí está. Bueno, eso significa que es más fácil de lo que parecía. Basta con leer los archivos, encontrar el lugar donde terminan estos ceros y eliminar el fragmento correspondiente.



Cómo se leen los archivos WAV



Primero, necesitaba familiarizarme más con el formato WAV para comprender cómo trabajar con esos archivos y administrar los datos que contienen. He recopilado varias fuentes; una de las más útiles resultó ser la página antigua de stanford.edu (el sitio ya no está disponible, pero, afortunadamente, ha sobrevivido en Wayback Machine). Había un diagrama muy claro:





Entonces, la estructura del archivo WAV parece ser bastante simple: primero, un encabezado de 44 bytes y luego los datos reales. Con esta información, ya era posible iniciar el código. Solo era necesario omitir los primeros 44 bytes, eliminar la secuencia de ceros al comienzo de la sección de datos y enviar todo lo demás para su reproducción en su forma original. Aunque no puedo evitar agregar que en otra fuente me encontré con la siguiente información:



“Algunos programas asumen (y esto es muy ingenuo de su parte) que el preámbulo en el encabezado es siempre exactamente 44 bytes (como se indica en la tabla anterior) y que el resto del archivo son solo datos de audio. No es seguro hacer tales suposiciones ".


Bueno, decidí que estaba bien: escribí el programa en C, así que no había necesidad de preocuparse demasiado por la seguridad.



El código



El código fue sencillo, en menos de cien líneas. De hecho, recorrió todo el archivo byte a byte, excepto los primeros cuarenta y cuatro, y contó ceros consecutivos. Tan pronto como se encontrara con algo que no era cero, el programa se detendría, guardaría el índice apropiado y comenzaría a leer el archivo desde el principio. Esta vez, omitió todo lo que precede al índice (sin contar el encabezado) y emite todos los demás bytes de la forma estándar.



No es necesario citar el código completo, pero aquí está la parte que nos interesará:



// index was calculated above to be the index of
// the last consecutive zero byte

FILE *f = fopen(argv[1], "rb");

int ind = 0;
int current_byte;
while ((current_byte = fgetc(f)) != EOF) {
    if (ind < 44 || ind >= index) {
        fputc(current_byte, stdout);
    }
    ind += 1;
}

fclose(f);
      
      





Todo es genial, todo es simple. Es hora de probar. Ejecuté el programa en uno de los archivos con una pausa particularmente larga.



./strip_audio testfile1.wav > testfile1.nosilence.wav
      
      





Comprobó lo que produce xxd para testfile1.nosilence.wav. Genial, sin ceros a la izquierda. Entonces funcionó. Sin duda, abriré rápidamente el archivo en mi reproductor de audio.







Fuente



Inmediatamente, el ruido estático más poderoso que he escuchado en mi vida me golpeó en los oídos. Casi me caigo de la silla y traté desesperadamente de quitarme los auriculares. Recuerdo que fue en medio de la noche, y el perro vino corriendo para comprobar qué me pasaba.



¿Qué hice mal?



Mis oídos todavía zumbaban, me senté y traté de comprender mis decisiones precipitadas.



  • Error número 1: era necesario bajar el volumen.
  • Error # 2: no deberías haber estado usando audífonos.
  • Error # 3: unidad no registrada.


¿Ha notado el tercer error en el código que le di arriba? Pista: mira el comentario. Calculé el índice de la variable como el índice del último byte que representa ceros. Esto significa que, menos 44 bytes del encabezado, ahora solo reproducimos lo que sigue o se superpone con el índice. El índice está en el último cero de la serie, es decir, incluimos un byte cero adicional en la sección de datos.



Esto se puede solucionar de la siguiente manera:



//     replaced >= with just >
if (ind < 44 || ind > index) {
    fputc(current_byte, stdout);
}
      
      





Ahora no hay ceros adicionales en la salida, y si reproduce el archivo, no sucederá nada malo. Arreglé todo ... Pero para.



En los archivos WAV, tenemos audio PCM, y los ceros en este tipo de datos de audio corresponden a un silencio total. Entonces, ¿no debería este byte adicional estar completamente en silencio? ¿Por qué era tan ruidoso y estático?



Primero, comparemos un archivo de audio normal con el monstruo que creé con Audacity:





¿Adivina dónde está el monstruo? Sí, este es el que tiene una amplitud estable casi al máximo. ¿Porqué es eso?



Cómo se leen las muestras de audio



Volví a las fuentes que había seleccionado y traté de averiguar cómo un error de una unidad podía conducir a tal explosión de amplitud. Sabía que en mis archivos la muestra contiene 16 bits y hay dos canales (estéreo), así que comencé a buscar la información adecuada. Esto es lo que dije en la sección sobre audio PCM estéreo de 16 bits:



“Cada muestra está contenida en un entero i, que representa el número mínimo suficiente de bytes para almacenar un tamaño de muestra dado. El byte menos significativo se coloca primero en la tienda ".


"El número mínimo de bytes suficiente para almacenar un tamaño dado": la redacción es innecesariamente confusa. i corresponde al número de bits que contiene la muestra. En nuestro caso, hay dieciséis. En consecuencia, si tenemos un cierto valor con una longitud de 16 bits, por supuesto, se almacenará en dos bytes. Y luego un punto importante: el menos significativo de los bytes se encuentra primero en el almacenamiento. Aquí lo tienes.



Eche un vistazo al gráfico que hice para mostrar qué causó una señal tan fuerte:





La parte superior muestra mi archivo de monstruo, en el que accidentalmente dejé un byte extra con ceros. Cada una de las tres muestras, s1, s2 y s3, contiene dos bytes y la segunda es más significativa. Por lo tanto, al convertir estos pares de bytes a decimales, obtenemos una amplitud muy alta.



Al mismo tiempo, en la parte inferior puede ver que si elimina el byte cero, las muestras se leen como deberían y los valores en el archivo de audio están dentro de límites razonables.



Resulta que si tuviera audio de 8 bits, el byte extra faltante no causaría ningún problema. Pero era de 16 bits y, como resultado, cambié toda la secuencia en muestras, de modo que el byte menos significativo se leyó como el más significativo.



conclusiones



  • Verifique la onda de sonido de un archivo de audio antes de reproducirlo al máximo volumen
  • ( )
  • ,



All Articles