Skip to content

Instantly share code, notes, and snippets.

@pensacola1989
Created August 14, 2014 12:45
Show Gist options
  • Save pensacola1989/335b5d25047cf531ab58 to your computer and use it in GitHub Desktop.
Save pensacola1989/335b5d25047cf531ab58 to your computer and use it in GitHub Desktop.
namespace Audio
{
using System;
using System.IO;
/// <summary>
/// Provides methods for saving byte arrays of audio samples to a wav file in mono or stereo.
/// </summary>
public static class WaveSaver
{
/// <summary>
/// Saves the waveform as a mono audio file in wav format.
/// </summary>
/// <param name="samples">The individual samples that make up the waveform.</param>
/// <param name="filename">The filename of the output wav file.</param>
public static void SaveToWave(short[] samples, string filename)
{
const short NumberOfChannels = 1; // mono audio
const short BytesPerSample = 2; // 16bit samples
const int SamplingRate = 44100; // 44.1 kHz
int totalBytes = checked(44 + (samples.Length * BytesPerSample * NumberOfChannels)); // size of headers + data
var output = new byte[totalBytes];
Buffer.BlockCopy(GetLEBytes(0x46464952), 0, output, 0, 4); // "RIFF"
Buffer.BlockCopy(GetLEBytes(totalBytes - 8), 0, output, 4, 4); // RIFF chunk size
Buffer.BlockCopy(GetLEBytes(0x45564157), 0, output, 8, 4); // "WAVE"
Buffer.BlockCopy(GetLEBytes(0x20746D66), 0, output, 12, 4); // "fmt "
Buffer.BlockCopy(GetLEBytes(16), 0, output, 16, 4); // fmt chunk size
Buffer.BlockCopy(GetLEBytes((short)1), 0, output, 20, 2); // compression code (1 - PCM/Uncompressed)
Buffer.BlockCopy(GetLEBytes((short)NumberOfChannels), 0, output, 22, 2); // number of channels
Buffer.BlockCopy(GetLEBytes(SamplingRate), 0, output, 24, 4); // sampling rate
Buffer.BlockCopy(GetLEBytes(SamplingRate * BytesPerSample * NumberOfChannels), 0, output, 28, 4); // bytes/second
Buffer.BlockCopy(GetLEBytes((short)(BytesPerSample * NumberOfChannels)), 0, output, 32, 2); // block size
Buffer.BlockCopy(GetLEBytes((short)(BytesPerSample * 8)), 0, output, 34, 2); // bits per sample
Buffer.BlockCopy(GetLEBytes(0x61746164), 0, output, 36, 4); // "data"
Buffer.BlockCopy(GetLEBytes(totalBytes - 44), 0, output, 40, 4); // data chunk size
for (var i = 0; i < samples.Length; i++)
{
Buffer.BlockCopy(GetLEBytes(samples[i]), 0, output, (BytesPerSample * i * NumberOfChannels) + 44, BytesPerSample);
}
File.WriteAllBytes(filename, output);
}
/// <summary>
/// Saves the waveform as a stereo audio file in wav format.
/// </summary>
/// <param name="leftSamples">The individual samples that make up the left channel of the waveform.</param>
/// <param name="rightSamples">The individual samples that make up the right channel of the waveform.</param>
/// <param name="filename">The filename of the output wav file.</param>
public static void SaveToWave(short[] leftSamples, short[] rightSamples, string filename)
{
const short NumberOfChannels = 2; // stereo audio
const short BytesPerSample = 2; // 16bit samples
const int SamplingRate = 44100; // 44.1 kHz
var shortChannel = (leftSamples.Length < rightSamples.Length) ? leftSamples : rightSamples;
int totalBytes = checked(44 + (shortChannel.Length * BytesPerSample * NumberOfChannels)); // size of headers + data
var output = new byte[totalBytes];
Buffer.BlockCopy(GetLEBytes(0x46464952), 0, output, 0, 4); // "RIFF"
Buffer.BlockCopy(GetLEBytes(totalBytes - 8), 0, output, 4, 4); // RIFF chunk size
Buffer.BlockCopy(GetLEBytes(0x45564157), 0, output, 8, 4); // "WAVE"
Buffer.BlockCopy(GetLEBytes(0x20746D66), 0, output, 12, 4); // "fmt "
Buffer.BlockCopy(GetLEBytes(16), 0, output, 16, 4); // fmt chunk size
Buffer.BlockCopy(GetLEBytes((short)1), 0, output, 20, 2); // compression code (1 - PCM/Uncompressed)
Buffer.BlockCopy(GetLEBytes((short)NumberOfChannels), 0, output, 22, 2); // number of channels
Buffer.BlockCopy(GetLEBytes(SamplingRate), 0, output, 24, 4); // sampling rate
Buffer.BlockCopy(GetLEBytes(SamplingRate * BytesPerSample * NumberOfChannels), 0, output, 28, 4); // bytes/second
Buffer.BlockCopy(GetLEBytes((short)(BytesPerSample * NumberOfChannels)), 0, output, 32, 2); // block size
Buffer.BlockCopy(GetLEBytes((short)(BytesPerSample * 8)), 0, output, 34, 2); // bits per sample
Buffer.BlockCopy(GetLEBytes(0x61746164), 0, output, 36, 4); // "data"
Buffer.BlockCopy(GetLEBytes(totalBytes - 44), 0, output, 40, 4); // data chunk size
for (var i = 0; i < shortChannel.Length; i++)
{
Buffer.BlockCopy(GetLEBytes(leftSamples[i]), 0, output, (BytesPerSample * i * NumberOfChannels) + 44, BytesPerSample);
Buffer.BlockCopy(GetLEBytes(rightSamples[i]), 0, output, (BytesPerSample * i * NumberOfChannels) + 44 + BytesPerSample, BytesPerSample);
}
File.WriteAllBytes(filename, output);
}
/// <summary>
/// Gets a <c>byte</c> array that represents the specified <c>short</c> in little endian.
/// </summary>
/// <param name="value">The value to represent in the <c>byte</c> array.</param>
/// <returns>The <c>byte</c> array.</returns>
private static byte[] GetLEBytes(short value)
{
if (BitConverter.IsLittleEndian)
{
return BitConverter.GetBytes(value);
}
else
{
return BitConverter.GetBytes((short)((value & 0xFF) << 8 | (value & 0xFF00) >> 8));
}
}
/// <summary>
/// Gets a <c>byte</c> array that represents the specified <c>int</c> in little endian.
/// </summary>
/// <param name="value">The value to represent in the <c>byte</c> array.</param>
/// <returns>The <c>byte</c> array.</returns>
private static byte[] GetLEBytes(int value)
{
if (BitConverter.IsLittleEndian)
{
return BitConverter.GetBytes(value);
}
else
{
return BitConverter.GetBytes((int)((value & 0xFF) << 24) | (int)((value & 0xFF00) << 8)
| (int)((value & 0xFF0000) >> 8) | (int)((value & 0xFF000000) >> 24));
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment