Combining Waves

This demonstration shows you how to get started with code to experiment with combinations of different types of wave. It plays the notes separately for one second each and then the combined output for a further three seconds. The optional compressor reduces the amplitude when necessary.

You might like to experiment with the fine JavaScript synthesiser and try to emulate some of its features in Smart Pascal.

The Smart Pascal code of the unit and the XML code of the form follow this demonstration. If the program does not work in your current browser, try Chrome.

CombiningWaves.html

If you do not see the demonstration, your school security system might have blocked it. Click here to try it on a separate page.

Smart Pascal Code of Unit

unit Form1;

interface

uses 
  SmartCL.System, SmartCL.Graphics, SmartCL.Components, SmartCL.Forms, 
  SmartCL.Fonts, SmartCL.Borders, SmartCL.Application, SmartCL.Controls.Panel,
  SmartCL.Controls.Button, SmartCL.Controls.Label, SmartCL.Controls.EditBox,
  SmartCL.controls.Combobox, SmartCL.Controls.ToggleSwitch, W3C.WebAudio,
  System.Colors;

type
  TForm1 = class(TW3Form)
    procedure W3Button1Click(Sender: TObject);
  private
    {$I 'Form1:intf'}
    const WAVE_TYPE_NAMES = ['Sine', 'Square', 'SawTooth', 'Triangle'];
    const WAVE_TYPES = [otSine, otSquare, otSawtooth, otTriangle];
    Osc1, Osc2: JOscillatorNode;
    Gain1, Gain2: JGainNode;
    FAudioContext: JAudioContext;
    Compressor: JDynamicsCompressorNode;
  protected
    procedure InitializeForm; override;
    procedure InitializeObject; override;
    procedure ConnectCompressor;
    procedure DisconnectCompressor;
  end;

implementation

procedure TForm1.ConnectCompressor;
begin
  Gain1.disconnect;
  Gain2.disconnect;
  Gain1.connect(Compressor);
  Gain2.connect(Compressor);
  Compressor.Connect(FAudioContext.destination);
end;

procedure TForm1.DisconnectCompressor;
begin
  Gain1.disconnect;
  Gain2.disconnect;
  Compressor.disconnect;
  Gain1.Connect(FAudioContext.destination);
  Gain2.Connect(FAudioContext.destination);
end;

procedure TForm1.W3Button1Click(Sender: TObject);
begin
  var now := FAudioContext.currentTime;

  Osc1 := FAudioContext.createOscillator;
  Osc2 := FAudioContext.createOscillator;
  Osc1.connect(Gain1);
  Osc2.connect(Gain2);

  Osc1.frequency.value := StrToFloat(edtFreq1.Text);
  Osc1.&type := WAVE_TYPES[cboOsc1.SelectedIndex];
  Osc2.frequency.value := StrToFloat(edtFreq2.Text);
  Osc2.&type := WAVE_TYPES[cboOsc2.SelectedIndex];

  Gain1.gain.setValueAtTime(StrToFloat(edtGain1.Text), now);     

  Gain1.gain.setValueAtTime(0, now + 1);
  Gain1.gain.setValueAtTime(StrToFloat(edtGain1.Text), now + 2);
  Gain1.gain.setValueAtTime(0, now + 5);
  Gain2.gain.setValueAtTime(0, now);
  Gain2.gain.setValueAtTime(StrToFloat(edtGain2.Text), now + 1);
  Gain2.gain.setValueAtTime(0, now + 5);

  if W3ToggleSwitch1.checked then
    ConnectCompressor
  else
    DisconnectCompressor;
  Osc1.start(now);
  Osc2.start(now);
  Osc1.stop(now + 5);
  Osc2.stop(now + 5);
end;

procedure TForm1.InitializeForm;
begin
  inherited;
  lblHeading.Font.Color := clBlue;
  lblHeading.Font.Size := 16;
  lblHeading.Font.Weight := 'bold';
  W3Label1.Font.Weight := 'bold';
  W3Label2.Font.Weight := 'bold';
  W3Label3.Font.Weight := 'bold';
  W3Label4.Font.Weight := 'bold';
  W3Label5.Font.Weight := 'bold';
  W3Label6.Font.Weight := 'bold';

  edtFreq1.InputType := itNumber;
  edtFreq2.InputType := itNumber;
  for var i := 0 to 3 do
    begin
      cboOsc1.Add(WAVE_TYPE_NAMES[i]);
      cboOsc2.Add(WAVE_TYPE_NAMES[i]);
    end;
  cboOsc1.SelectedIndex := 0;
  cboOsc2.SelectedIndex := 0;
  W3ToggleSwitch1.Checked := True;

  FAudioContext := new JAudioContext;
  Gain1 := FAudioContext.createGain;
  Gain2 := FAudioContext.createGain;

  Compressor := FAudioContext.createDynamicsCompressor;
  Compressor.threshold.value := -50;
  compressor.knee.value := 40;
  compressor.ratio.value := 12;
  compressor.reduction.value := -20;
  compressor.attack.value := 0;
  compressor.release.value := 0.25;
end;

procedure TForm1.InitializeObject;
begin
  inherited;
  {$I 'Form1:impl'}
end;
 
initialization
  Forms.RegisterForm({$I %FILE%}, TForm1);
end.    

XML Code of Form

<SMART>
  <Form version="2" subversion="2">
    <Created>2015-12-31T13:28:21.501</Created>
    <Modified>2015-12-31T17:01:37.364</Modified>
    <object type="TW3Form">
      <Caption>W3Form</Caption>
      <Name>Form1</Name>
      <object type="TW3Panel">
        <Width>384</Width>
        <Height>240</Height>
        <Name>W3Panel1</Name>
        <object type="TW3Button">
          <Caption>Play</Caption>
          <Width>128</Width>
          <Top>205</Top>
          <Left>252</Left>
          <Height>32</Height>
          <Name>W3Button1</Name>
          <OnClick>W3Button1Click</OnClick>
        </object>
        <object type="TW3Label">
          <Caption>Wave Type</Caption>
          <Width>128</Width>
          <Top>56</Top>
          <Left>26</Left>
          <Height>32</Height>
          <Name>W3Label1</Name>
        </object>
        <object type="TW3Label">
          <Caption>Frequency</Caption>
          <Width>128</Width>
          <Top>104</Top>
          <Left>26</Left>
          <Height>32</Height>
          <Name>W3Label2</Name>
        </object>
        <object type="TW3EditBox">
          <Value>440</Value>
          <Text>440.0</Text>
          <Range></Range>
          <Width>128</Width>
          <Top>104</Top>
          <Left>105</Left>
          <Height>32</Height>
          <Name>edtFreq1</Name>
        </object>
        <object type="TW3EditBox">
          <Value>880</Value>
          <Text>880.0</Text>
          <Range></Range>
          <Width>128</Width>
          <Top>104</Top>
          <Left>250</Left>
          <Height>32</Height>
          <Name>edtFreq2</Name>
        </object>
        <object type="TW3ComboBox">
          <Width>128</Width>
          <Top>56</Top>
          <Left>105</Left>
          <Height>32</Height>
          <Name>cboOsc1</Name>
        </object>
        <object type="TW3ComboBox">
          <Width>128</Width>
          <Top>56</Top>
          <Left>250</Left>
          <Height>32</Height>
          <Name>cboOsc2</Name>
        </object>
        <object type="TW3ToggleSwitch">
          <Width>128</Width>
          <Top>192</Top>
          <Left>91</Left>
          <Height>32</Height>
          <Name>W3ToggleSwitch1</Name>
        </object>
        <object type="TW3Label">
          <Caption>Compressor</Caption>
          <Width>128</Width>
          <Top>192</Top>
          <Left>8</Left>
          <Height>32</Height>
          <Name>W3Label3</Name>
        </object>
        <object type="TW3Label">
          <Caption>Oscillator 1</Caption>
          <Width>128</Width>
          <Top>24</Top>
          <Left>104</Left>
          <Height>32</Height>
          <Name>W3Label4</Name>
        </object>
        <object type="TW3Label">
          <Caption>Oscillator 2</Caption>
          <Width>120</Width>
          <Top>24</Top>
          <Left>248</Left>
          <Height>32</Height>
          <Name>W3Label5</Name>
        </object>
        <object type="TW3Label">
          <Caption>Combining Waves</Caption>
          <Width>180</Width>
          <Left>128</Left>
          <Height>32</Height>
          <Name>lblHeading</Name>
        </object>
        <object type="TW3Label">
          <Caption>Gain</Caption>
          <Width>72</Width>
          <Top>144</Top>
          <Left>24</Left>
          <Height>32</Height>
          <Name>W3Label6</Name>
        </object>
        <object type="TW3EditBox">
          <Value>1.0</Value>
          <Text>1.0</Text>
          <Range></Range>
          <Width>128</Width>
          <Top>144</Top>
          <Left>105</Left>
          <Height>32</Height>
          <Name>edtGain1</Name>
        </object>
        <object type="TW3EditBox">
          <Value>0.2</Value>
          <Text>0.2</Text>
          <Range></Range>
          <Width>128</Width>
          <Top>144</Top>
          <Left>250</Left>
          <Height>32</Height>
          <Name>edtGain2</Name>
        </object>
      </object>
    </object>
  </Form>
</SMART>
Programming - a skill for life!

Using Audio in Smart Pascal