-
March 18th, 2013, 04:21 PM
#1
Problems writting WAV in C#
Hi. I am trying to write a program in C# that generate a WAVE (.wav) file by merging more WAVE files. The problem comes when generating the header of the WAV format.
I am trying to write the header using this: https://ccrma.stanford.edu/courses/4...ts/WaveFormat/
I must create a 16 bit (bit per sample), 8kHz (samplerate) mono (1 channel) wave file, of unknown time length. Most of the code do the job fine, but I have problems with the ChunkSize and Subchunk2Size bytes. I simply do not understand them. In that document it says it's 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size). I just made it the size of the byte array minus 8 (It says: This is the size of the entire file in bytes minus 8 bytes for the two fields not included in this count). The SubChunk2Size, I just don't understand it. It says it must be NumSamples * NumChannels * BitsPerSample/8, it doesn't work anyway.
But also, I don't know if each one of these properties must have their bytes inverted. Because, according to this document: http://ist.uwaterloo.ca/~schepers/formats/WAV.TXT, those two properties have a "lo/hi" format. I don't know if this is true or not, since I haven't seen something like this specified in any document. So, I'm pretty much confused of what should I do with these properties. The current code of my program is the following code:
Code:
ushort numchannels = 1; // Number of channels
uint samplerate = 8000; //22050
uint numsamples = 0; //It becomes numsamples * (uint)DATA.Length lather
uint bps = 16; //Bits Per Sample
[...]
numsamples = numsamples * (uint)DATA.Length; //DATA is the byte array containing the WAVE
wr.Write(System.Text.Encoding.ASCII.GetBytes("RIFF")); //RIFF
wr.Write(DATA.Length - 8); //ChunkSize = FileSize - 8?
wr.Write(Encoding.ASCII.GetBytes("WAVE")); //WAVE
wr.Write(Encoding.ASCII.GetBytes("fmt ")); //fmt
wr.Write((uint)16); //PCM
wr.Write((short)01); //LINEAR
wr.Write((short)01); //MONO
wr.Write((uint)samplerate); //Sample rate
wr.Write((uint)(samplerate * numchannels * (bps / 8))); //byte Rate
wr.Write((uint)bps); //Bits Per Sample
wr.Write(Encoding.ASCII.GetBytes("DATA")); //DATA
wr.Write((uint)numsamples * numchannels * (bps / 8)); //SubChunk2Size
wr.Write(DATA, 0, DATA.Length); //Write DATA buffer
It should work according to those specifications. The problem is... it doesn't work. The audio files generated with this code seems corrupted with any media player. Why? That's what I'd like to know. There is almost no clear information about how to write WAV headers on the internet, and the information I found is quite unclear in most cases, so I come to you with hopes of clarifying things out. Thanks in advance.
-
March 18th, 2013, 04:30 PM
#2
Re: Problems writting WAV in C#
One thing that I see is that
Code:
uint numsamples = 0; //It becomes numsamples * (uint)DATA.Length lather
...
numsamples = numsamples * (uint)DATA.Length;
is probably not what you want.
I suggest that you single step this in the debugger and check that every single entry get the expected value.
Edit: Welcome to CG by the way and thanks for using code tags in the very first post! Excellent job!
Last edited by S_M_A; March 18th, 2013 at 04:32 PM.
-
March 19th, 2013, 04:34 PM
#3
Re: Problems writting WAV in C#
Hi S_M_A.
Thanks for replying. As you guessed, that line wasn't doing what I wanted it to do. However, I still couldn't get it to work.
I'm still investigating. However, I made something that also work as I want. What I did is, I just created a .wav file with the characteristics I want with Audacity, of 1 hour of length, and then extracted the header.
Now, every time I want to write a wav file, I just copy the header, and then copy the rest of the data. I know this is not how it's supposed to be made, but it does the trick, as I am never going to make a file longer than 1 hour. Also, the files produced this way has the right length. Not 1 hour but whatever it needs.
-
March 21st, 2013, 01:37 PM
#4
Re: Problems writting WAV in C#
I got curious so I played around a bit with it and this is the result. C# isn't my first language but since I think this could be useful I post it anyway. i haven't tried adding several data blocks into the file but I hope you can make it into something useful anyway.
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace WavMaker
{
class Program
{
class WavHeader
{
public WavHeader(uint subChunk1Size, uint subChunk2Size)
{
chunkId = 0x46464952; // "RIFF"
chunkSize = 4 + (8 + subChunk1Size) + (8 + subChunk2Size);
format = 0x45564157; // "WAVE"
}
public void Write(ref BinaryWriter w)
{
w.Write(chunkId);
w.Write(chunkSize);
w.Write(format);
}
uint chunkId; // BigEndian
uint chunkSize; // LittleEndian
uint format; // BigEndian
}
class FmtChunk
{
public FmtChunk(ushort numChannels_, uint sampleRate_, ushort bitsPerSample_)
{
subChunk1Id = 0x20746D66; // "RIFF"
subChunk1Size = 16; // PCM
audioFormat = 1;
numChannels = numChannels_;
sampleRate = sampleRate_;
byteRate = numChannels_ * sampleRate_ * bitsPerSample_ / 8;
blockAlign = (ushort)(numChannels_ * bitsPerSample_ / 8);
bitsPerSample = bitsPerSample_;
}
public void Write(ref BinaryWriter w)
{
w.Write(subChunk1Id);
w.Write(subChunk1Size);
w.Write(audioFormat);
w.Write(numChannels);
w.Write(sampleRate);
w.Write(byteRate);
w.Write(blockAlign);
w.Write(bitsPerSample);
}
public uint Size()
{
return subChunk1Size;
}
uint subChunk1Id; // BigEndian "fmt "
uint subChunk1Size; // LittleEndian
ushort audioFormat; // LittleEndian
ushort numChannels; // LittleEndian
uint sampleRate; // LittleEndian
uint byteRate; // LittleEndian
ushort blockAlign; // LittleEndian
ushort bitsPerSample; // LittleEndian
}
class DataHeader
{
public DataHeader(uint subChunk2Size_)
{
subChunk2Id = 0x61746164; // "data"
subChunk2Size = subChunk2Size_;
}
public void Write(ref BinaryWriter w)
{
w.Write(subChunk2Id);
w.Write(subChunk2Size);
}
uint subChunk2Id; // BigEndian
uint subChunk2Size; // LittleEndian
}
interface Wave
{
void Write(ref BinaryWriter w);
uint Size();
}
class Mono16Tone : Wave
{
public Mono16Tone(uint seconds, uint freq, uint sampleRate)
{
uint samples = seconds * sampleRate;
wave = new short[samples];
for (uint n = 0; n < samples; n++)
{
wave[n] = (short)(32767 * Math.Sin(freq * 2.0 * Math.PI * n / sampleRate));
}
}
void Wave.Write(ref BinaryWriter w)
{
foreach (short e in wave) w.Write(e);
}
uint Wave.Size()
{
return (uint)(sizeof(short) * wave.Length);
}
short[] wave;
}
class Mono16DualTones : Wave
{
public Mono16DualTones(uint seconds, uint freq1, uint freq2, uint sampleRate)
{
uint samples = seconds * sampleRate;
wave = new short[samples];
for (uint n = 0; n < samples; n++)
{
short value1 = (short)(32767 * Math.Sin(freq1 * 2.0 * Math.PI * n / sampleRate));
short value2 = (short)(32767 * Math.Sin(freq2 * 2.0 * Math.PI * n / sampleRate));
wave[n] = (short)((value1 + value2) / 2);
}
}
void Wave.Write(ref BinaryWriter w)
{
foreach (short e in wave) w.Write(e);
}
uint Wave.Size()
{
return (uint)(sizeof(short) * wave.Length);
}
short[] wave;
}
class Stereo16Tone : Wave
{
public Stereo16Tone(uint seconds, uint freq, uint sampleRate)
{
uint samples = seconds * sampleRate;
wave = new short[2 * samples];
for (uint n = 0; n < samples; n++)
{
short value = (short)(32767 * Math.Sin(freq * 2.0 * Math.PI * n / sampleRate));
wave[2 * n] = value;
wave[2 * n + 1] = value;
}
}
void Wave.Write(ref BinaryWriter w)
{
foreach (short e in wave) w.Write(e);
}
uint Wave.Size()
{
return (uint)(sizeof(short) * wave.Length);
}
short[] wave;
}
class Stereo16DualTones : Wave
{
public Stereo16DualTones(uint seconds, uint freq1, uint freq2, uint sampleRate)
{
uint samples = seconds * sampleRate;
wave = new short[2 * samples];
for (uint n = 0; n < samples; n++)
{
short value = (short)(32767 * Math.Sin(freq1 * 2.0 * Math.PI * n / sampleRate));
wave[2 * n] = value;
value = (short)(32767 * Math.Sin(freq2 * 2.0 * Math.PI * n / sampleRate));
wave[2 * n + 1] = value;
}
}
void Wave.Write(ref BinaryWriter w)
{
foreach (short e in wave) w.Write(e);
}
uint Wave.Size()
{
return (uint)(sizeof(short) * wave.Length);
}
short[] wave;
}
static void Main(string[] args)
{
uint seconds = 10;
uint freq = 440;
uint sampleRate = 8000;
ushort bitsPerSample = 16;
ushort numChannels = 1;
ushort tones = 2;
String fname = "";
Wave wave = null;
switch (numChannels)
{
case 1:
switch (tones)
{
case 1:
fname = "mono1.wav";
wave = new Mono16Tone(seconds, freq, sampleRate);
break;
case 2:
fname = "mono2.wav";
wave = new Mono16DualTones(seconds, freq, 2 * freq, sampleRate);
break;
}
break;
case 2:
switch (tones)
{
case 1:
fname = "stereo1.wav";
wave = new Stereo16Tone(seconds, freq, sampleRate);
break;
case 2:
fname = "stereo2.wav";
wave = new Stereo16DualTones(seconds, freq, 2 * freq, sampleRate);
break;
}
break;
}
DataHeader dataHeader = new DataHeader(wave.Size());
FmtChunk fmtChunk = new FmtChunk(numChannels, sampleRate, bitsPerSample);
WavHeader wavHeader = new WavHeader(fmtChunk.Size(), wave.Size());
FileStream f = new FileStream(fname, FileMode.Create, FileAccess.Write);
BinaryWriter w = new BinaryWriter(f);
wavHeader.Write(ref w);
fmtChunk.Write(ref w);
dataHeader.Write(ref w);
wave.Write(ref w);
f.Close();
}
}
}
Last edited by S_M_A; March 21st, 2013 at 03:24 PM.
-
September 28th, 2014, 03:39 PM
#5
Re: Problems writting WAV in C#
S_M_A
Many thanks for posting WavMaker. Beautiful C# code - way beyond my limits. I am using it to create a chirp for my Arduino Due, which will output wav files. Here is the main part of the butchery of your code:
public Mono16DualTones(uint seconds, uint freq1, uint freq2, uint sampleRate)
{
float duration = 0.053333F; //affects but does not equal length of pulse, why?
uint samples = 2048; //lines of data
wave = new short[samples];
float newB = 0;
for (uint n = 1; n < samples; n++)
{
newB = newB + duration / samples; // This code adds one time unit to newB with each line of data
short value1 = (short)(5000 * 2 * Math.PI * Math.Cos((freq2 - freq1) * newB * newB / (2 * duration) + freq1 * newB + 1.55));
wave[n] = (short)value1;
}
}
The algorithm is taken from a paper by D W Caress in Monterey, sort of...
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|