Cómo leer los datos en un archivo wav en una matriz

Necesito obtener todas las muestras de un archivo wav en una matriz (o dos si es necesario para mantener el estéreo) para poder aplicarles algunas modificaciones. Me preguntaba si esto se hace fácilmente (preferiblemente sin bibliotecas externas). No tengo experiencia con la lectura de archivos de sonido, así que no sé mucho sobre el tema.

Los archivos WAV (al menos, sin comprimir) son bastante sencillos. Hay un encabezado, luego los datos lo siguen.

Aquí hay una gran referencia: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ ( mirror )

Este código debería hacer el truco. Convierte un archivo wave en una matriz doble normalizada (-1 a 1), pero debería ser trivial para hacerlo una matriz int / short en su lugar (eliminar el bit /32768.0 y agregar 32768 en su lugar). La matriz right[] se establecerá en nulo si se encuentra que el archivo wav cargado es mono.

No puedo afirmar que sea completamente a prueba de balas (posibles errores uno por uno), pero después de crear una matriz de muestras 65536 y crear una onda de -1 a 1, ninguna de las muestras parece pasar a través del techo o piso.

 // convert two bytes to one double in the range -1 to 1 static double bytesToDouble(byte firstByte, byte secondByte) { // convert two bytes to one short (little endian) short s = (secondByte << 8) | firstByte; // convert to range from -1 to (just below) 1 return s / 32768.0; } // Returns left and right double arrays. 'right' will be null if sound is mono. public void openWav(string filename, out double[] left, out double[] right) { byte[] wav = File.ReadAllBytes(filename); // Determine if mono or stereo int channels = wav[22]; // Forget byte 23 as 99.999% of WAVs are 1 or 2 channels // Get past all the other sub chunks to get to the data subchunk: int pos = 12; // First Subchunk ID from 12 to 16 // Keep iterating until we find the data chunk (ie 64 61 74 61 ...... (ie 100 97 116 97 in decimal)) while(!(wav[pos]==100 && wav[pos+1]==97 && wav[pos+2]==116 && wav[pos+3]==97)) { pos += 4; int chunkSize = wav[pos] + wav[pos + 1] * 256 + wav[pos + 2] * 65536 + wav[pos + 3] * 16777216; pos += 4 + chunkSize; } pos += 8; // Pos is now positioned to start of actual sound data. int samples = (wav.Length - pos)/2; // 2 bytes per sample (16 bit sound mono) if (channels == 2) samples /= 2; // 4 bytes per sample (16 bit stereo) // Allocate memory (right will be null if only mono sound) left = new double[samples]; if (channels == 2) right = new double[samples]; else right = null; // Write to double array/s: int i=0; while (pos < length) { left[i] = bytesToDouble(wav[pos], wav[pos + 1]); pos += 2; if (channels == 2) { right[i] = bytesToDouble(wav[pos], wav[pos + 1]); pos += 2; } i++; } } 

Suponiendo que su archivo WAV contiene PCM de 16 bits (que es el más común), puede usar NAudio para leerlo en una matriz de bytes y luego copiarlo en una matriz de enteros de 16 bits para mayor comodidad. Si es estéreo, las muestras se intercalarán a la izquierda, a la derecha.

 using (WaveFileReader reader = new WaveFileReader("myfile.wav")) { Assert.AreEqual(16, reader.WaveFormat.BitsPerSample, "Only works with 16 bit audio"); byte[] buffer = new byte[reader.Length]; int read = reader.Read(buffer, 0, buffer.Length); short[] sampleBuffer = new short[read / 2]; Buffer.BlockCopy(buffer, 0, sampleBuffer, 0, read); } 

Sé que quería evitar las bibliotecas de terceros, pero si quiere asegurarse de manejar los archivos WAV con fragmentos adicionales, le sugiero que evite enfoques como buscar 44 bytes en el archivo.

Al momento de escribir, nadie ha abordado las WAV codificadas en 32 bits o 64 bits.

El siguiente código maneja 16/32/64 bit y mono / stereo:

 static bool readWav( string filename, out float[] L, out float[] R ) { L = R = null; //float [] left = new float[1]; //float [] right; try { using (FileStream fs = File.Open(filename,FileMode.Open)) { BinaryReader reader = new BinaryReader(fs); // chunk 0 int chunkID = reader.ReadInt32(); int fileSize = reader.ReadInt32(); int riffType = reader.ReadInt32(); // chunk 1 int fmtID = reader.ReadInt32(); int fmtSize = reader.ReadInt32(); // bytes for this chunk int fmtCode = reader.ReadInt16(); int channels = reader.ReadInt16(); int sampleRate = reader.ReadInt32(); int byteRate = reader.ReadInt32(); int fmtBlockAlign = reader.ReadInt16(); int bitDepth = reader.ReadInt16(); if (fmtSize == 18) { // Read any extra values int fmtExtraSize = reader.ReadInt16(); reader.ReadBytes(fmtExtraSize); } // chunk 2 int dataID = reader.ReadInt32(); int bytes = reader.ReadInt32(); // DATA! byte[] byteArray = reader.ReadBytes(bytes); int bytesForSamp = bitDepth/8; int samps = bytes / bytesForSamp; float[] asFloat = null; switch( bitDepth ) { case 64: double[] asDouble = new double[samps]; Buffer.BlockCopy(byteArray, 0, asDouble, 0, bytes); asFloat = Array.ConvertAll( asDouble, e => (float)e ); break; case 32: asFloat = new float[samps]; Buffer.BlockCopy(byteArray, 0, asFloat, 0, bytes); break; case 16: Int16 [] asInt16 = new Int16[samps]; Buffer.BlockCopy(byteArray, 0, asInt16, 0, bytes); asFloat = Array.ConvertAll( asInt16, e => e / (float)Int16.MaxValue ); break; default: return false; } switch( channels ) { case 1: L = asFloat; R = null; return true; case 2: L = new float[samps]; R = new float[samps]; for( int i=0, s=0; i 

http://hourlyapps.blogspot.com/2008/07/open-source-wave-graph-c-net-control.html
Aquí hay un control que muestra el espectro de un archivo Wav, que también sirve un byte [] de archivo wav decodificado donde puede reproducir y / o cambiar sus valores.

Simplemente descarga el control y es bastante bueno para la manipulación de archivos WAV.

Para obtener el archivo wav en una matriz, puede hacer esto:

byte [] data = File.ReadAllBytes (“FilePath”);

pero al igual que Fletch, dijo que necesita aislar los datos de los encabezados. Debería ser solo una compensación simple.

Intenta reproducir datos de audio desde una matriz

 PlayerEx pl = new PlayerEx(); private static void PlayArray(PlayerEx pl) { double fs = 8000; // sample freq double freq = 1000; // desired tone short[] mySound = new short[4000]; for (int i = 0; i < 4000; i++) { double t = (double)i / fs; // current time mySound[i] = (short)(Math.Cos(t * freq) * (short.MaxValue)); } IntPtr format = AudioCompressionManager.GetPcmFormat(1, 16, (int)fs); pl.OpenPlayer(format); byte[] mySoundByte = new byte[mySound.Length * 2]; Buffer.BlockCopy(mySound, 0, mySoundByte, 0, mySoundByte.Length); pl.AddData(mySoundByte); pl.StartPlay(); }