How to get from .wav sound into double[] C# - c#

I need to perform FFT and I have a sound sample .wav format.
The function needs double[] xRe, double[] xIm, REAL PART and imaginary part How to convert the sound file into double[]?
I have never seen something like that. Could not find in the internet that kind of operation.
This is this sound sample:
http://www.speedyshare.com/fFD8t/e.wav
Please help, cause I used Pascal and now don't know what to do here.

It's a simple stream operation.
You have to read wav file header.
You have to read data bytes.
Paste from MSDN:
BinaryReader reader = new BinaryReader(waveFileStream);
//Read the wave file header from the buffer.
int chunkID = reader.ReadInt32();
int fileSize = reader.ReadInt32();
int riffType = reader.ReadInt32();
int fmtID = reader.ReadInt32();
int fmtSize = reader.ReadInt32();
int fmtCode = reader.ReadInt16();
int channels = reader.ReadInt16();
int sampleRate = reader.ReadInt32();
int fmtAvgBPS = reader.ReadInt32();
int fmtBlockAlign = reader.ReadInt16();
int bitDepth = reader.ReadInt16();
if (fmtSize == 18)
{
// Read any extra values
int fmtExtraSize = reader.ReadInt16();
reader.ReadBytes(fmtExtraSize);
}
int dataID = reader.ReadInt32();
int dataSize = reader.ReadInt32();
// Store the audio data of the wave file to a byte array.
byteArray = reader.ReadBytes(dataSize);
// After this you have to split that byte array for each channel (Left,Right)
// Wav supports many channels, so you have to read channel from header
Here this is more detailed explanation:
http://msdn.microsoft.com/en-us/library/ff827591.aspx
And here you can read about WAV file format:
https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
And something about WAV as complex numbers and FFT:
How to convert wave data into Complex numbers

Kamil's solution was very close, but didn't work for some of the wav files I ran across. After some research, here is what I discovered. In the description of the canonical WAV PCM sound file format (http://soundfile.sapp.org/doc/WaveFormat/), I discovered this reference in the Notes: There may be additional subchunks in a Wave data stream. If so, each will have a char[4] SubChunkID, and unsigned long SubChunkSize, and SubChunkSize amount of data.
In a few wav files I ran across, there were indeed extra subchunks, so I had to add some code to read until it found the "data" marker. After adding this, it worked universally. My complete code is below.
public static bool ReadWavFile(string filename, out float[] L, out float[] R)
{
L = R = null;
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 -- HERE'S THE NEW STUFF (ignore these subchunks, I don't know what they are!)
int bytes;
while(new string(reader.ReadChars(4)) != "data")
{
bytes = reader.ReadInt32();
reader.ReadBytes(bytes);
}
// DATA!
bytes = reader.ReadInt32();
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 < samps; i++)
{
L[i] = asFloat[s++];
R[i] = asFloat[s++];
}
return true;
default:
return false;
}
}
}
catch
{
Debug.WriteLine("...Failed to load note: " + filename);
return false;
//left = new float[ 1 ]{ 0f };
}
}

Related

What am I doing wrong when parsing a wav file?

I'm trying to parse a wav file. I'm not sure if there can be multiple data chunks in a wav file, but I originally assumed there was only 1 since the wav file format description I was reading only mentioned there being 1.
But I noticed that the subchunk2size was very small (like 26) when the wav file being parsed was something like 36MB and the sample rate was 44100.
So I tried to parse it assuming there were multiple chunks, but after the 1st chunk, there was no subchunk2id to be found.
To go chunk by chunk, I was using the below code
int chunkSize = System.BitConverter.ToInt32(strm, 40);
int widx = 44; //wav data starts at the 44th byte
//strm is a byte array of the wav file
while(widx < strm.Length)
{
widx += chunkSize;
if(widx < 1000)
{
//log "data" or "100 97 116 97" for the subchunkid
//This is only getting printed the 1st time though. All prints after that are garbage
Debug.Log( strm[widx] + " " + strm[widx+1] + " " + strm[widx+2] + " " + strm[widx+3]);
}
if(widx + 8 < strm.Length)
{
widx += 4;
chunkSize = System.BitConverter.ToInt32(strm, widx);
widx += 4;
}else
{
widx += 8;
}
}
A .wav-File has 3 chunks:
Each chunk has a size of 4 Byte
The first chunk is the "RIFF"-chunk. It includes 8 Byte the filesize(4 Byte) and the name of the format(4byte, usually "WAVE").
The next chunk is the "fmt "-chunk (the space in the chunk-name is important). It includes the audio-format(2 Byte), the number of channels (2 Byte), the sample rate (4 Byte), the byte rate (4 Byte), blockalign (2 Byte) and the bits per sample (2 Byte).
The third and last chunk is the data-chunk. Here are the real data and the amplitudes of the samples. It includes 4 Byte for the datasize, which is the number of bytes for the data.
You can find further explanations of the properties of a .wav-file here.
From this knowledge I have already created the following class:
public sealed class WaveFile
{
//privates
private int fileSize;
private string format;
private int fmtChunkSize;
private int audioFormat;
private int numChannels;
private int sampleRate;
private int byteRate;
private int blockAlign;
private int bitsPerSample;
private int dataSize;
private int[][] data;//One array per channel
//publics
public int FileSize => fileSize;
public string Format => format;
public int FmtChunkSize => fmtChunkSize;
public int AudioFormat => audioFormat;
public int NumChannels => numChannels;
public int SampleRate => sampleRate;
public int ByteRate => byteRate;
public int BitsPerSample => bitsPerSample;
public int DataSize => dataSize;
public int[][] Data => data;
public WaveFile(string path)
{
FileStream fs = File.OpenRead(path);
LoadChunk(fs); //read RIFF Chunk
LoadChunk(fs); //read fmt Chunk
LoadChunk(fs); //read data Chunk
fs.Close();
}
private void LoadChunk(FileStream fs)
{
ASCIIEncoding Encoder = new ASCIIEncoding();
byte[] bChunkID = new byte[4];
fs.Read(bChunkID, 0, 4);
string sChunkID = Encoder.GetString(bChunkID);
byte[] ChunkSize = new byte[4];
fs.Read(ChunkSize, 0, 4);
if (sChunkID.Equals("RIFF"))
{
fileSize = BitConverter.ToInt32(ChunkSize, 0);
byte[] Format = new byte[4];
fs.Read(Format, 0, 4);
this.format = Encoder.GetString(Format);
}
if (sChunkID.Equals("fmt "))
{
fmtChunkSize = BitConverter.ToInt32(ChunkSize, 0);
byte[] audioFormat = new byte[2];
fs.Read(audioFormat, 0, 2);
this.audioFormat = BitConverter.ToInt16(audioFormat, 0);
byte[] numChannels = new byte[2];
fs.Read(numChannels, 0, 2);
this.numChannels = BitConverter.ToInt16(numChannels, 0);
byte[] sampleRate = new byte[4];
fs.Read(sampleRate, 0, 4);
this.sampleRate = BitConverter.ToInt32(sampleRate, 0);
byte[] byteRate = new byte[4];
fs.Read(byteRate, 0, 4);
this.byteRate = BitConverter.ToInt32(byteRate, 0);
byte[] blockAlign = new byte[2];
fs.Read(blockAlign, 0, 2);
this.blockAlign = BitConverter.ToInt16(blockAlign, 0);
byte[] bitsPerSample = new byte[2];
fs.Read(bitsPerSample, 0, 2);
this.bitsPerSample = BitConverter.ToInt16(bitsPerSample, 0);
}
if (sChunkID.Equals("data"))
{
dataSize = BitConverter.ToInt32(ChunkSize, 0);
data = new int[this.numChannels][];
byte[] temp = new byte[dataSize];
for (int i = 0; i < this.numChannels; i++)
{
data[i] = new int[this.dataSize / (numChannels * bitsPerSample / 8)];
}
for (int i = 0; i < data[0].Length; i++)
{
for (int j = 0; j < numChannels; j++)
{
if (fs.Read(temp, 0, blockAlign / numChannels) > 0)
{
if (blockAlign / numChannels == 2)
{ data[j][i] = BitConverter.ToInt32(temp, 0); }
else
{ data[j][i] = BitConverter.ToInt16(temp, 0); }
}
}
}
}
}
}
Needed using-directives:
using System;
using System.IO;
using System.Text;
This class reads all chunks byte per byte and sets the properties. You just have to initialize this class and it will return all properties of your selected wave-file.
In the reference you added I dont see any mention of the chunk size being repeated for each data chunk...
Try something like this:
int chunkSize = System.BitConverter.ToInt32(strm, 40);
int widx = 44; //wav data starts at the 44th byte
//strm is a byte array of the wav file
while(widx < strm.Length)
{
if(widx < 1000)
{
//log "data" or "100 97 116 97" for the subchunkid
//This is only getting printed the 1st time though. All prints after that are garbage
Debug.Log( strm[widx] + " " + strm[widx+1] + " " + strm[widx+2] + " " + strm[widx+3]);
}
widx += chunkSize;
}

How to promote a byte by specific value?

So i have this file that i am opening:
static void Encrypt(string fileName)
{
using (FileStream stream = File.OpenRead(fileName))
{
using (BinaryReader reader = new BinaryReader(stream))
{
for (int i = 0; i < stream.Length; i++)
{
byte b = reader.ReadByte();
byte newByte = (byte(b + 5))
}
}
}
}
And i want to add specific value to each byte in my file and save it.
So just store the new bytes in a collection and save them after reading whole file.
var newBytes = new List<byte>();
...
for (int i = 0; i < stream.Length; i++)
{
byte b = reader.ReadByte();
newBytes.Add(b + 5);
}
...
File.WriteAllBytes(filePath, newBytes.ToArray());
You could do something like this:
byte b = reader.ReadByte();
int newNumber = (int)b + 5;
byte newByte = (byte)(newNumber % 256);
To have control over the overflow you may create, I suggest you change from byte to int.
Then this adds 5 to the byte value you read, wrapping to zero when you reach b == 251, as 251 + 5 == 256 and 256 % 256 == 0.

Calculate CRC 8 of data received on serial port c#

I am receiving data from serial port in an byte array
How can I calculate the checksum of the data not included the sync (54) and checksum (F2) byte and want to match with the last check sum byte.
Updated :
int bytes = comport.BytesToRead;
byte indexCRC;
int sumCRC = 0;
byte checksumCRC = 0;
byte checksum;
byte[] RXBuffer = new byte[bytes];
comport.Read(RXBuffer, 0, bytes);
checksum = RXBuffer.Last();
byte[] RXBufferCRC = new byte[bytes];
for (indexCRC = 1; indexCRC < RXBufferCRC.Length; indexCRC++)
{
sumCRC = sumCRC + RXBufferCRC[indexCRC];
}
checksumCRC = (byte)(sumCRC);
Start the index from 1 but before doing that delete the last index of the array and store the array in an other array something like that
int Secbytes = comport.BytesToRead;
byte[] SecRXBuffer = new byte[Secbytes];
Array.Copy(SecRXBuffer, VanguardConstants.RECEIVEINDEX, RXBuffer, 0, Secbytes);
byte[] tmp = new byte[bytes - 1];
Array.Copy(RXBuffer, tmp, Secbytes - 1);
for (i = 1; i < tmp.Length; i++)
{
Sum = (byte)(Sum + tmp[i]);
}
Checksum = ((byte)Sum);
http://err.se/crc8-for-ibutton-in-c/
Here is an implementation of a CRC8 function in C#.

Splitting audio into left and right channel into seperate files

I'm having trouble seperating the channel buffers into a new file.
Here is the code for extracting each channels buffer:
int samplesDesired = 10000;
byte[] buffer = new byte[samplesDesired * 4];
short[] left = new short[samplesDesired];
short[] right = new short[samplesDesired];
using (WaveFileReader pcm = new WaveFileReader(filePath))
{
int bytesRead = pcm.Read(buffer, 0, 10000);
int index = 0;
for (int i = 0; i < bytesRead / 4; i++)
{
left[i] = BitConverter.ToInt16(buffer, index);
index += 2;
right[i] = BitConverter.ToInt16(buffer, index);
index += 2;
}
}
And here is how I try to create a file from the gathered buffers:
using(var leftChannelFile = new WaveFileWriter("test.wav", new WaveFormat()))
{
leftChannelFile.WriteSamples(left, 0, left.Length);
}
The problem is, when I try to play the "file.wav", it is 0 seconds long and 19,5 KB large. Any idea on why is that happening?
Either you set the header of the resulting files indicating it is one channel/mono,
OR
you add a second empty channel to your result files (all 0 Bytes)

What is the correct way to get a byte array from a FileStream?

The Microsoft website has the code snippet:
using (FileStream fsSource = new FileStream(pathSource,
FileMode.Open, FileAccess.Read))
{
// Read the source file into a byte array.
byte[] bytes = new byte[fsSource.Length];
int numBytesToRead = (int)fsSource.Length;
int numBytesRead = 0;
while (numBytesToRead > 0)
{
// Read may return anything from 0 to numBytesToRead.
int n = fsSource.Read(bytes, numBytesRead, numBytesToRead);
// Break when the end of the file is reached.
if (n == 0)
break;
numBytesRead += n;
numBytesToRead -= n;
}
}
What concerns me is that fsSource.Length is a long, whereas numBytesRead is an int so at most only 2 * int.MaxValue can be read into bytes (the head and the tail of the stream). So my questions are:
Is there some reason that this is OK?
If not, how should you read a FileStream into a byte[].
In this situation I wouldn't even bother processing the FileStream manually; use File.ReadAllBytes instead:
byte[] bytes = File.ReadAllBytes(pathSource);
To answer your question:
The sample code is good for most of applications where we are not reaching extremes.
If you have really long stream like say a video, use BufferedStream. Sample code is available at MSDN site
Example using ReadAllBytes:
private byte[] m_cfgBuffer;
m_cfgBuffer = File.ReadAllBytes(m_FileName);
StringBuilder PartNbr = new StringBuilder();
StringBuilder Version = new StringBuilder();
int i, j;
byte b;
i = 356; // We know that the cfg file header ends at position 356 (1st hex(80))
b = m_cfgBuffer[i];
while (b != 0x80) // Scan for 2nd hex(80)
{
i++;
b = m_cfgBuffer[i];
}
// Now extract the part number - 6 bytes after hex(80)
m_PartNbrPos = i + 5;
for (j = m_PartNbrPos; j < m_PartNbrPos + 6; j++)
{
char cP = (char)m_cfgBuffer[j];
PartNbr.Append(cP);
}
m_PartNbr = PartNbr.ToString();
// Now, extract version number - 6 bytes after part number
m_VersionPos = (m_PartNbrPos + 6) + 6;
for (j = m_VersionPos; j < m_VersionPos + 2; j++)
{
char cP = (char)m_cfgBuffer[j];
Version.Append(cP);
}
m_Version = Version.ToString();

Categories

Resources