Split a PCM Stereo into multiple mono files by channel

For this you can read and write the data streams as you like, to keep things simple I am using NAudio.  This should negate the need to write custom stream readers/writers to get channel/header information. 

First thing we need to do is read the audio file into a byte array.  This can be accomplished like this:

using (var s = new WaveFileReader(@"C:\temp\testStereo.wav"))
{
     BinaryReader br = new BinaryReader(s);
     byte[] data = br.ReadBytes((int)s.Length);
     WriteNewAudioFiles(data, @"C:\Temp\", s.WaveFormat.Channels);
}

We then call into our new method WriteNewAudioFiles where we pass in the newly created data byte array, the folder where we want to save the new files, and how many channels the stereo file has.  (s.WaveFormat is a feature of NAudio that gathers the RIFF WAV information).

In WriteNewAudioFiles we want to create a container to hold our channel information. 

var outputs = new List<List<byte>>();

We then want to add placeholders into the container based on the number of channels we have.

for (int i = 0; i < channels; i++)
{
     outputs.Add(new List<byte>());
}

Now we need a way to loop through the byte array and move the proper bytes to the correct channel.  In PCM wav stereo bytes alternate between channels.   An example  pattern of this might be this for two channel stereo:

left left right right left left right right…

If the audio file has three or more channels we would see this

001122001122….

in a standard 16 bit audio file, each channel will be made up of 2 bytes.  In our loop we are going to advance by 2 instead of the standard 1.

In our loop we also need a way to keep an eye on which channel we are currently feeding and reset after we have reached our highest channel.  Channel counts are zero based so we only need to make sure our count does not become greater then our channels, if it does, we reset the count.  The looping code should look like this.

var count = 0;
for (int i = 0; i < input.Length; i+=2)
{
     outputs[count].Add(input[i]);
     outputs[count].Add(input[i + 1]);
     count ++;
     if (count < channels) continue;
     count = 0;
}

Now that we have our output created and each channel is in its own list we can now write those out.  For this we are going to create a counter (or reuse if you want to keep it in the same method [which violates SOLID I know]), this way we can create a unique wav file for each channel.

With the counter in hand, do a foreach loop on the output container, take each list of bytes and convert them to a byte array.   With NAudio we will create a proper header then call it’s wave file writer to create our file:

count = 0;
foreach (var o in outputs)
{
     byte[] data = o.ToArray();
     WaveFormat waveFormat1 = new WaveFormat(8000, 16, 1);
     using (WaveFileWriter writer333 = new WaveFileWriter(string.Format(@"{0}\mono{1}.wav",filePath, count), waveFormat1))
     {
          writer333.Write(data, 0, data.Length);
     }
     count ++;
}

All put together, WriteNewAudioFiles should look like this:

private void WriteNewAudioFiles(byte[] input, string filePath, int channels)
{
     // split the stream into multiple audio files
     var outputs = new List<List<byte>>();
     for (int i = 0; i < channels; i++)
     {
          outputs.Add(new List<byte>());
     }

     int channelCount = channels;
     var count = 0;
     for (int i = 0; i < input.Length; i+=2)
     {
          outputs[count].Add(input[i]);
          outputs[count].Add(input[i + 1]);
          count ++;
          channelCount --;
          if (channelCount >= 1) continue;
          channelCount = channels;
          count = 0;
     }

     // write each byte aray to a new mono file
     count = 0;
     foreach (var o in outputs)
     {
          byte[] data = o.ToArray();
          WaveFormat waveFormat1 = new WaveFormat(8000, 16, 1);
          using (WaveFileWriter writer333 = new WaveFileWriter(string.Format(@"{0}\mono{1}.wav",filePath, count), waveFormat1))
          {
          writer333.Write(data, 0, data.Length);
          }
     count ++;
}
Advertisements
This entry was posted in C# and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s