How to create stereo wave files with accurate frequencies (Views: 30)
Problem/Question/Abstract: How do I generate low frequency audio < 1000 Hz with one frequency to the left channel and a slightly different frequency to the right? For example , a tone of 400 Hz is presented to the right ear and a tone of 410 Hz is presented simultaneously to the left ear ? I'm trying to write a small binaural beat test program. I understand the science but not how to generate the two tones in Windows. Answer: Assuming you want precise control over the waveforms, the best thing to do is to create a stereo .WAV file containing the desired data. Here's a function that will do that; you can adapt it to your needs (add MMSystem to your USES list): procedure CreateSineWave(LeftFreq, RightFreq: Single; Duration: Cardinal; const FileName: string); const BitsPerSample = 16; NumChannels = 2; SampleRate = 44100; var ChunkSize: Integer; DataSize: Integer; Factor: Single; Format: TWaveFormatEx; FourCC: array[0..3] of Char; I: Integer; NumSamples: Integer; L: SmallInt; R: SmallInt; WaveStream: TFileStream; begin WaveStream := TFileStream.Create(FileName, fmCreate); try FourCC := 'RIFF'; WaveStream.Write(FourCC, SizeOf(FourCC)); NumSamples := (SampleRate * Duration) div 1000; DataSize := (BitsPerSample shr 3) * NumChannels * NumSamples; ChunkSize := DataSize + SizeOf(TWaveFormatEx) + 20; WaveStream.Write(ChunkSize, SizeOf(ChunkSize)); FourCC := 'WAVE'; WaveStream.Write(FourCC, SizeOf(FourCC)); FourCC := 'fmt '; WaveStream.Write(FourCC, SizeOf(FourCC)); ChunkSize := SizeOf(TWaveFormatEx); WaveStream.Write(ChunkSize, SizeOf(ChunkSize)); with Format do begin wFormatTag := WAVE_FORMAT_PCM; nChannels := NumChannels; nSamplesPerSec := SampleRate; wBitsPerSample := BitsPerSample; nBlockAlign := nChannels * wBitsPerSample shr 3; nAvgBytesPerSec := nSamplesPerSec * nBlockAlign; cbSize := 0 end; WaveStream.Write(Format, SizeOf(Format)); FourCC := 'data'; WaveStream.Write(FourCC, SizeOf(FourCC)); ChunkSize := DataSize; WaveStream.Write(ChunkSize, SizeOf(ChunkSize)); for I := 0 to 999 do begin Factor := Exp(-0.005 * (1000 - I)); L := Round(Factor * 32767 * Sin(2 * Pi * LeftFreq * I / SampleRate)); R := Round(Factor * 32767 * Sin(2 * Pi * RightFreq * I / SampleRate)); WaveStream.Write(L, SizeOf(L)); WaveStream.Write(R, SizeOf(R)) end; for I := 1000 to NumSamples - 1001 do begin L := Round(32767 * Sin(2 * Pi * LeftFreq * I / SampleRate)); R := Round(32767 * Sin(2 * Pi * RightFreq * I / SampleRate)); WaveStream.Write(L, SizeOf(L)); WaveStream.Write(R, SizeOf(R)) end; for I := NumSamples - 1000 to NumSamples - 1 do begin Factor := Exp(0.005 * (NumSamples - 1001 - I)); L := Round(Factor * 32767 * Sin(2 * Pi * LeftFreq * I / SampleRate)); R := Round(Factor * 32767 * Sin(2 * Pi * RightFreq * I / SampleRate)); WaveStream.Write(L, SizeOf(L)); WaveStream.Write(R, SizeOf(R)) end; WaveStream.Position := 0; finally WaveStream.Free end end; So, for example, to create a two-second sample having a 400 Hz left channel and a 410 Hz right channel: CreateSineWave(400.0, 410.0, 2000, 'foo.wav'); You can play the sound like this: sndPlaySound('foo.wav', SND_SYNC); |