/// <summary> /// Reads the fmt chunk /// </summary> /// <param name="data"></param> /// <param name="idx"></param> /// <returns></returns> private static FormatChunkData ParseFmtChunk(byte[] data, int idx) { var id = Encoding.ASCII.GetString(data, idx, 4); var size = BitConverter.ToInt32(data, idx + 4); idx = idx + 8; var fmt = new FormatChunkData(); fmt.AudioFormat = data[idx]; fmt.NumChannels = data[idx + 2]; fmt.SampleRate = BitConverter.ToInt32(data, idx + 4); fmt.ByteRate = BitConverter.ToInt32(data, idx + 8); fmt.BlockAlign = BitConverter.ToInt16(data, idx + 12); fmt.BitsPerSample = BitConverter.ToInt16(data, idx + 14); fmt.BytesPerSample = fmt.BitsPerSample / 8; return(fmt); }
/// <summary> /// Reads the fmt chunk /// </summary> /// <param name="data"></param> /// <param name="idx"></param> /// <returns></returns> private static FormatChunkData ParseFmtChunk(byte[] data, int idx) { var id = Encoding.ASCII.GetString(data, idx, 4); var size = BitConverter.ToInt32(data, idx + 4); idx = idx + 8; var fmt = new FormatChunkData(); fmt.AudioFormat = data[idx]; fmt.NumChannels = data[idx + 2]; fmt.SampleRate = BitConverter.ToInt32(data, idx + 4); fmt.ByteRate = BitConverter.ToInt32(data, idx + 8); fmt.BlockAlign = BitConverter.ToInt16(data, idx + 12); fmt.BitsPerSample = BitConverter.ToInt16(data, idx + 14); fmt.BytesPerSample = fmt.BitsPerSample / 8; return fmt; }
/// <summary> /// Parses the data chunk, containing the audio data /// </summary> /// <param name="data">data array to work on</param> /// <param name="idx">starting index of the data chunk</param> /// <param name="chunkSize">size of the data chunk</param> /// <param name="format">the format description, read from the 'fmt ' chunk</param> /// <returns></returns> private static double[][] ParseDataChunk(byte[] data, int idx, int chunkSize, FormatChunkData format) { var channels = new List<List<double>>(); for (int i = 0; i < format.NumChannels; i++) channels.Add(new List<double>()); idx += 8; int channel = 0; for (int i = idx; i < chunkSize + idx; i += format.BytesPerSample) { int value = 0; if (format.AudioFormat == 3) // audioFormat 3 indicated IEEE 32bit floating point data { float val = BitConverter.ToSingle(data, i); channels[channel].Add(val); } else if (format.BytesPerSample == 1) // 8 bit PCM data { value = data[i] - 0x80; channels[channel].Add(value / 128.0); } else if (format.BytesPerSample == 2) // 16 bit PCM data { value = BitConverter.ToInt16(data, i); channels[channel].Add(value / 32768.0); } else if (format.BytesPerSample == 3) // 24 bit PCM data { value = BitConverter.ToInt32(new byte[] { 0, data[i], data[i + 1], data[i + 2] }, 0); channels[channel].Add(value / 2147483648.0); } else if (format.BytesPerSample == 4) // 32 bit PCM data { value = BitConverter.ToInt32(data, i); channels[channel].Add(value / 2147483648.0); } channel = (channel + 1) % format.NumChannels; } // convert lists to arrays double[][] output = new double[format.NumChannels][]; for (int i = 0; i < output.Length; i++) output[i] = channels[i].ToArray(); return output; }
/// <summary> /// Read a WAVE file. Supports multiple channels, any bitrate. /// Supported formats are IEEE 32bit floating point and uncompressed PCM 8/16/24/32 bit /// </summary> /// <param name="filename"></param> /// <param name="clmData">a list to be filled with metadata from the clm chunk</param> /// <param name="format"></param> /// <returns></returns> public static double[][] ReadWaveFile(byte[] data, ref List <byte[]> clmData, out FormatChunkData format) { format = null; var waveFormat = new byte[] { data[8], data[9], data[10], data[11] }; string fmt = Encoding.ASCII.GetString(waveFormat); if (fmt != "WAVE") { return(null); } int idx = 12; double[][] output = null; while (idx < data.Length) { string chunkId = Encoding.ASCII.GetString(data, idx, 4); int chunkSize = BitConverter.ToInt32(data, idx + 4); if (chunkId == "fmt ") { format = ParseFmtChunk(data, idx); } else if (chunkId == "data") { if (format == null) // format must preced data. { return(null); } output = ParseDataChunk(data, idx, chunkSize, format); break; } else { var dataChunk = data.Skip(idx).Take(8 + chunkSize).ToArray(); clmData?.Add(dataChunk); } idx = idx + 8 + chunkSize; } return(output); }
/// <summary> /// Parses the data chunk, containing the audio data /// </summary> /// <param name="data">data array to work on</param> /// <param name="idx">starting index of the data chunk</param> /// <param name="chunkSize">size of the data chunk</param> /// <param name="format">the format description, read from the 'fmt ' chunk</param> /// <returns></returns> private static double[][] ParseDataChunk(byte[] data, int idx, int chunkSize, FormatChunkData format) { var channels = new List <List <double> >(); for (int i = 0; i < format.NumChannels; i++) { channels.Add(new List <double>()); } idx += 8; int channel = 0; for (int i = idx; i < chunkSize + idx; i += format.BytesPerSample) { int value = 0; if (format.AudioFormat == 3) // audioFormat 3 indicated IEEE 32bit floating point data { float val = BitConverter.ToSingle(data, i); channels[channel].Add(val); } else if (format.BytesPerSample == 1) // 8 bit PCM data { value = data[i] - 0x80; channels[channel].Add(value / 128.0); } else if (format.BytesPerSample == 2) // 16 bit PCM data { value = BitConverter.ToInt16(data, i); channels[channel].Add(value / 32768.0); } else if (format.BytesPerSample == 3) // 24 bit PCM data { value = BitConverter.ToInt32(new byte[] { 0, data[i], data[i + 1], data[i + 2] }, 0); channels[channel].Add(value / 2147483648.0); } else if (format.BytesPerSample == 4) // 32 bit PCM data { value = BitConverter.ToInt32(data, i); channels[channel].Add(value / 2147483648.0); } channel = (channel + 1) % format.NumChannels; } // convert lists to arrays double[][] output = new double[format.NumChannels][]; for (int i = 0; i < output.Length; i++) { output[i] = channels[i].ToArray(); } return(output); }