Using Oscillators

[Using the SetValueAtTime Procedure] [Using a Delay Node]

This example reproduces the notes from Ihsan Fazal's Pascal program Mazer. The data for the note frequencies and start and stop times are held in parallel arrays to facilitate assignment to the corresponding properties of the oscillators by a loop.

unit Unit1;

interface

uses
  System.Types, SmartCL.System, SmartCL.Components, SmartCL.Application,
  SmartCL.Game, SmartCL.GameApp, SmartCL.Graphics, SmartCL.MediaElements,
  SmartCL.Time, W3C.WebAudio;

type
  TCanvasProject = class(TW3CustomGameApplication)
  private
    const SOUND_LEVEL = 0.5;
    const StartTimes: array[0..26] of float = [0.0, 0.55, 0.8, 1.35, 1.9, 2.25, 2.95, 3.5, 3.75,
                                               4.45, 5.0, 5.25, 5.7, 6.15, 6.7, 6.95, 7.65, 8.2, 8.45,
                                               9.15, 9.7, 9.95, 10.65, 11.2, 11.45, 11.9, 12.12];
    const StopTimes: array[0..26] of float = [0.5, 0.65, 1.35, 1.85, 2.0, 2.95, 3.45, 3.6, 4.45,
                                              4.95, 5.1, 5.7, 6.15, 6.65, 6.8, 7.65, 8.15, 8.3, 9.15,
                                              9.65, 9.8, 10.65, 11.15, 11.3, 11.9, 12.12, 12.37];
    const Frequencies: array[0..26] of integer = [523, 523, 523, 587, 587, 587, 659, 659, 659,
                                                  784, 784, 784, 988, 523, 523, 523, 587, 587, 587,
                                                  659, 659, 659, 784, 784, 988, 880, 784];
    FAudioContext: JAudioContext;
    Oscillators: array [0..26] of JOscillatorNode;
    FGain: JGainNode;
  public
    procedure ApplicationStarting; override;
    procedure ApplicationClosing; override;
    procedure PaintView(Canvas: TW3Canvas); override;
  end;

implementation

procedure TCanvasProject.ApplicationStarting;
begin
  inherited;
  FAudioContext := new JAudioContext;
  FGain := FAudioContext.createGain;
  FGain.gain.value := SOUND_LEVEL;
  FGain.connect(FAudioContext.destination);
  for var i := 0 to 26 do
    begin
      Oscillators[i] := FAudioContext.createOscillator;
      Oscillators[i].frequency.value := Frequencies[i];
      Oscillators[i].start(StartTimes[i]);
      Oscillators[i].stop(StopTimes[i]);
      Oscillators[i].connect(FGain);
    end;
  GameView.Delay := 5000;
  GameView.StartSession(False);
end;

procedure TCanvasProject.PaintView(Canvas: TW3Canvas);
begin
end;

procedure TCanvasProject.ApplicationClosing;
begin
  GameView.EndSession;
  inherited;
end;

end.
    

Using the SetValueAtTime Procedure

The SetValueAtTime procedure is useful for scheduling changes to frequencies and volumes as shown in this simple example. The frequencies of the octave (starting at Middle C) in an "equal tempered scale" are taken from Physics of Music. (Our trials with the ExponentialRampToValueAtTime and LinearRampToValueAtTime procedures seemed to give abrupt rather than gradual changes on our systems).

unit Unit1;

interface

uses
  System.Types, SmartCL.System, SmartCL.Components, SmartCL.Application,
  SmartCL.Game, SmartCL.GameApp, SmartCL.Graphics, SmartCL.MediaElements,
  W3C.WebAudio;

type
  TCanvasProject = class(TW3CustomGameApplication)
  private
    FAudioContext: JAudioContext;
    Oscillator: JOscillatorNode;
    FGain: JGainNode;
    Frequencies: array[1..8] of float := [261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88, 523.25];
  public
    procedure ApplicationStarting; override;
    procedure PaintView(Canvas: TW3Canvas); override;
  end;

implementation

procedure TCanvasProject.ApplicationStarting;
begin
  inherited;
  FAudioContext := new JAudioContext;
  FGain := FAudioContext.createGain;
  FGain.connect(FAudioContext.destination);
  Oscillator := FAudioContext.createOscillator;
  FGain.gain.value := 0.01;
  for var i := 1 to 8 do
    begin
      Oscillator.frequency.setValueAtTime(Frequencies[i], FAudioContext.currentTime + i);
      FGain.gain.setValueAtTime(1.0 - 0.1 * i, FAudioContext.currentTime + i);
    end;
  Oscillator.connect(FGain);
  Oscillator.start(0);
  Oscillator.stop(10);
end;

procedure TCanvasProject.PaintView(Canvas: TW3Canvas);
begin
end;

end.
    

Using a Delay Node

In this example we connect an oscillator to the destination both directly and via a delay node to generate an echo effect.

unit Unit1;

interface

uses
  System.Types, SmartCL.System, SmartCL.Components, SmartCL.Application,
  SmartCL.Game, SmartCL.GameApp, SmartCL.Graphics, SmartCL.MediaElements,
  SmartCL.Time, W3C.WebAudio;

type
  TCanvasProject = class(TW3CustomGameApplication)
  private
    FDelay: JDelayNode;
    FAudioContext: JAudioContext;
    FOscillator: JOscillatorNode;
    FGain1, FGain2: JGainNode;
  public
    procedure ApplicationStarting; override;
    procedure ApplicationClosing; override;
    procedure PaintView(Canvas: TW3Canvas); override;
  end;

implementation

procedure TCanvasProject.ApplicationStarting;
begin
  inherited;
  FAudioContext := new JAudioContext;
  FGain1 := FAudioContext.createGain;
  FGain2 := FAudioContext.createGain;
  FOscillator := FAudioContext.createOscillator;
  FOscillator.frequency.value := 523.2;
  FOscillator.connect(FGain1);
  FGain1.connect(FAudioContext.destination);
  FGain1.gain.setValueAtTime(0.5, 0.0);
  FGain1.gain.setValueAtTime(0.0, 0.25);
  FGain2.gain.value := 0.2;
  FDelay := FAudioContext.createDelay;
  FDelay.delayTime.value := 0.25;
  FGain1.connect(FDelay);
  FDelay.connect(FGain2);
  FGain2.connect(FAudioContext.destination);
  FOscillator.start(0.0);
end;

procedure TCanvasProject.PaintView(Canvas: TW3Canvas);
begin
end;

procedure TCanvasProject.ApplicationClosing;
begin
  GameView.EndSession;
  inherited;
end;

end.    
Programming - a skill for life!

Using Audio in Smart Pascal