Author: Tomas Rutkauskas
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):
1 procedure CreateSineWave(LeftFreq, RightFreq: Single; Duration: Cardinal;
2 const FileName: string);
3 const
4 BitsPerSample = 16;
5 NumChannels = 2;
6 SampleRate = 44100;
7 var
8 ChunkSize: Integer;
9 DataSize: Integer;
10 Factor: Single;
11 Format: TWaveFormatEx;
12 FourCC: array[0..3] of Char;
13 I: Integer;
14 NumSamples: Integer;
15 L: SmallInt;
16 R: SmallInt;
17 WaveStream: TFileStream;
18 begin
19 WaveStream := TFileStream.Create(FileName, fmCreate);
20 try
21 FourCC := 'RIFF';
22 WaveStream.write(FourCC, SizeOf(FourCC));
23 NumSamples := (SampleRate * Duration) div 1000;
24 DataSize := (BitsPerSample shr 3) * NumChannels * NumSamples;
25 ChunkSize := DataSize + SizeOf(TWaveFormatEx) + 20;
26 WaveStream.write(ChunkSize, SizeOf(ChunkSize));
27 FourCC := 'WAVE';
28 WaveStream.write(FourCC, SizeOf(FourCC));
29 FourCC := 'fmt ';
30 WaveStream.write(FourCC, SizeOf(FourCC));
31 ChunkSize := SizeOf(TWaveFormatEx);
32 WaveStream.write(ChunkSize, SizeOf(ChunkSize));
33 with Format do
34 begin
35 wFormatTag := WAVE_FORMAT_PCM;
36 nChannels := NumChannels;
37 nSamplesPerSec := SampleRate;
38 wBitsPerSample := BitsPerSample;
39 nBlockAlign := nChannels * wBitsPerSample shr 3;
40 nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
41 cbSize := 0
42 end;
43 WaveStream.write(Format, SizeOf(Format));
44 FourCC := 'data';
45 WaveStream.write(FourCC, SizeOf(FourCC));
46 ChunkSize := DataSize;
47 WaveStream.write(ChunkSize, SizeOf(ChunkSize));
48 for I := 0 to 999 do
49 begin
50 Factor := Exp(-0.005 * (1000 - I));
51 L := Round(Factor * 32767 * Sin(2 * Pi * LeftFreq * I / SampleRate));
52 R := Round(Factor * 32767 * Sin(2 * Pi * RightFreq * I / SampleRate));
53 WaveStream.write(L, SizeOf(L));
54 WaveStream.write(R, SizeOf(R))
55 end;
56 for I := 1000 to NumSamples - 1001 do
57 begin
58 L := Round(32767 * Sin(2 * Pi * LeftFreq * I / SampleRate));
59 R := Round(32767 * Sin(2 * Pi * RightFreq * I / SampleRate));
60 WaveStream.write(L, SizeOf(L));
61 WaveStream.write(R, SizeOf(R))
62 end;
63 for I := NumSamples - 1000 to NumSamples - 1 do
64 begin
65 Factor := Exp(0.005 * (NumSamples - 1001 - I));
66 L := Round(Factor * 32767 * Sin(2 * Pi * LeftFreq * I / SampleRate));
67 R := Round(Factor * 32767 * Sin(2 * Pi * RightFreq * I / SampleRate));
68 WaveStream.write(L, SizeOf(L));
69 WaveStream.write(R, SizeOf(R))
70 end;
71 WaveStream.Position := 0;
72 finally
73 WaveStream.Free
74 end
75 end;
So, for example, to create a two-second sample having a 400 Hz left channel and a
410 Hz right channel:
76 CreateSineWave(400.0, 410.0, 2000, 'foo.wav');
You can play the sound like this:
77 sndPlaySound('foo.wav', SND_SYNC);
|