If it helps anyone, I have made a wav_buffer unit which allows you to create WAV format output to a stream like so:
Code:
Program WAV_buffer_test;
Uses
Classes,
wav_buffer;
Const
cDuration_mSec = 1000;
cNumberOfChannels = 1;
cSampleRate = 44100;
cBitsPerSample = 16;
Var
fs : TFileStream;
Buffer : TWAVBuffer;
i : LongWord;
Sample : Single;
Begin
fs := TFileStream.Create('c:\test.wav',fmCreate);
Try
Buffer := TWAVBuffer.Create(fs,cDuration_mSec,cNumberOfChannels,cBitsPerSample,cSampleRate);
For i := 0 To Buffer.NumberOfSamples - 1 Do
Begin
// 1 complete sinewave cycle over the entire duration
Sample := Sin(2 * PI * i / Buffer.NumberOfSamples);
Buffer.WriteSamples([Sample]);
End;
Finally
fs.Free;
End;
End.
for 2 channels (stereo) just add another sample to the Buffer.WriteSamples line like so:
Code:
Buffer.WriteSamples([SampleL,SampleR]);
See unit below:
Code:
Unit wav_buffer;
{$IFDEF fpc}
{$MODE DELPHI} {$H+}
{$ENDIF}
Interface
Uses
Classes;
Type
{..............................................................................}
TWAVBuffer = Class
Private
FStream : TStream;
FDuration_mSec : LongWord;
FNumberOfChannels : Byte;
FBitsPerSample : Byte;
FSampleRate : LongWord;
FNumberOfSamples : LongWord;
FDataSize : LongWord;
Procedure WriteWAVHeader;
Public
Constructor Create(Const AStream : TStream;
Const ADuration_mSec : LongWord;
Const ANumberOfChannels : Byte;
Const ABitsPerSample : Byte;
Const ASampleRate : LongWord);
Procedure Reset;
Procedure WriteSamples(Const ASamples : Array Of Single);
Property NumberOfSamples : LongWord Read FNumberOfSamples;
End;
{..............................................................................}
Implementation
{..............................................................................}
{..............................................................................}
Constructor TWAVBuffer.Create(Const AStream : TStream;
Const ADuration_mSec : LongWord;
Const ANumberOfChannels : Byte;
Const ABitsPerSample : Byte;
Const ASampleRate : LongWord);
Begin
FStream := AStream;
FDuration_mSec := ADuration_mSec;
FNumberOfChannels := ANumberOfChannels;
FBitsPerSample := ABitsPerSample;
FSampleRate := ASampleRate;
If Not (FNumberOfChannels In[1,2]) Then FNumberOfChannels := 1;
If Not (FBitsPerSample In[8,16]) Then FBitsPerSample := 8;
FNumberOfSamples := (FDuration_mSec * FSampleRate) Div 1000;
FDataSize := (FBitsPerSample Shr 3) * FNumberOfChannels * FNumberOfSamples;
WriteWAVHeader;
End;
{..............................................................................}
{..............................................................................}
Procedure TWAVBuffer.WriteWAVHeader;
Const
WAVE_FORMAT_PCM = 1;
RiffId : AnsiString = 'RIFF';
WaveId : AnsiString = 'WAVE';
FmtId : AnsiString = 'fmt ';
DataId : AnsiString = 'data';
Type
TWaveHeader = Packed Record
wFormatTag : Word; { format type }
nChannels : Word; { number of channels (i.e. mono, stereo, etc.) }
nSamplesPerSec : LongWord; { sample rate }
nAvgBytesPerSec : LongWord; { for buffer estimation }
nBlockAlign : Word; { block size of data }
wBitsPerSample : Word; { number of bits per sample of mono data }
cbSize : Word; { the count in bytes of the size of }
End;
Var
WaveHeader : TWaveHeader;
RiffCount : Integer;
TempInt : LongWord;
Begin
With WaveHeader Do
Begin
wFormatTag := WAVE_FORMAT_PCM;
nChannels := FNumberOfChannels;
nSamplesPerSec := FSampleRate;
wBitsPerSample := FBitsPerSample;
nBlockAlign := nChannels * wBitsPerSample Shr 3;
nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
cbSize := 0;
End;
{Calculate length of sound data and of file data}
RiffCount := Length(WaveId) + Length(FmtId) + SizeOf(LongWord) +
SizeOf(TWaveHeader) + Length(DataId) + SizeOf(LongWord) + FDataSize; // file data
{write out the wave header}
FStream.Write(RiffId[1] , 4); // 'RIFF'
FStream.Write(RiffCount , SizeOf(LongWord)); // file data size
FStream.Write(WaveId[1] , Length(WaveId)); // 'WAVE'
FStream.Write(FmtId[1] , Length(FmtId)); // 'fmt '
TempInt := SizeOf(TWaveHeader);
FStream.Write(TempInt , SizeOf(LongWord)); // TWaveFormat data size
FStream.Write(WaveHeader , SizeOf(WaveHeader)); // WaveFormatEx record
FStream.Write(DataId[1] , Length(DataId)); // 'data'
FStream.Write(FDataSize , SizeOf(LongWord)); // sound data size
End;
{..............................................................................}
{..............................................................................}
Procedure TWAVBuffer.Reset;
Begin
FStream.Seek(0,soFromBeginning);
WriteWAVHeader;
End;
{..............................................................................}
{..............................................................................}
Procedure TWAVBuffer.WriteSamples(Const ASamples : Array Of Single);
Var
Sample_8Bit : Byte;
Sample_16Bit : SmallInt;
Sample : Single;
i : Integer;
Begin
For i := 0 To High(ASamples) Do
Begin
Sample := ASamples[i];
// clip sample to between [-1,+1]
If Sample <1> +1.0 Then Sample := +1.0;
// write sample to stream
If FBitsPerSample = 8 Then
Begin
Sample_8Bit := 127 + Trunc(127 * Sample);
FStream.Write(Sample_8Bit,SizeOf(Sample_8Bit));
End
Else
If FBitsPerSample = 16 Then
Begin
Sample_16Bit := Trunc(32767 * Sample);
FStream.Write(Sample_16Bit,SizeOf(Sample_16Bit));
End;
End;
End;
{..............................................................................}
{..............................................................................}
End.
I hope someone finds this useful :-)
cheers,
Paul
Bookmarks