2007. május 17., csütörtök

How to create stereo wave files with accurate frequencies


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);

Nincsenek megjegyzések:

Megjegyzés küldése