javascriptweb-audio-apitone.js

Tone.js inconsistency of Gain attached to synth


I am new to Tone.js and have a problem with the Gain object. I have a volume slider set up in html as follows:

<button class="play">Play</button>
<button class="stop">Stop</button>  
 <div>
  Vol:&nbsp;
  <input type="range" class="vol-slider slider" min="0" max="10" value="4">
   <div class="vol-text">4</div>
</div>

When 'Play' is clicked, I create a Tone.PolySynth and a Tone.Gain, then connect the Gain to the PolySynth using the .chain() function. The gain value is taken from the volume slider. Notes are played using the Tone.Part function (js code below).

The sequence is played out at the correct volume when the app is first launched. It also plays with increased volume (higher gain) when the volume slider is increased. However, when the volume slider is lowered, the volume of the sequence does not lower. The gain does not have an effect when going from a higher value to a lower value.

I am stumped on this and appreciate knowledge / experience to help.

Please codepen at https://codepen.io/minapre/pen/QWpmdJm

$('.stop').on('click', function(e){
  Tone.Transport.stop();
}) // .stop

  
$(".vol-slider").on('input', function(e){
    let val = $(this).val()
    $(".vol-text").text(val)
 }) // .vol-slider
  
$('.play').on('click', function(e){
  
console.clear()

testnotes = [ {time: "0:0:0", note: "D3", duration: "8n"},
{time: "0:0:0", note: "D3", duration: "16n"},
{time: "0:0:1", note: "E3", duration: "16n"},
{time: "0:0:2", note: "F3", duration: "16n"},
{time: "0:0:3", note: "G3", duration: "16n"},
{time: "0:1:0", note: "A3", duration: "16n"},
{time: "0:1:1", note: "B3", duration: "16n"},
{time: "0:1:2", note: "C4", duration: "16n"},
{time: "0:1:3", note: "D4", duration: "16n"},
{time: "0:2:0", note: "E4", duration: "16n"},
{time: "0:2:1", note: "F4", duration: "8n"},
{time: "0:2:2", note: "G4", duration: "8n"},
{time: "0:2:3", note: "A4", duration: "8n"},
{time: "0:3:0", note: "B4", duration: "8n"},
{time: "0:3:1", note: "C5", duration: "4n"},
]
  
Tone.Transport.stop()
const  synth  = new Tone.PolySynth( )
let vol = parseFloat( $(".vol-slider").val() ) / 10 
console.log("vol: " + vol)
const gain = new Tone.Gain(vol).toDestination()
synth.chain( gain);
const part = new Tone.Part(function(time, note) {
      synth.triggerAttackRelease(note.note, note.duration, time);
    }, testnotes).start(0);
Tone.Transport.start()
 
}) // .play

Solution

  • It looks like you're creating a new gain node each time you click "play". You only need to create those Tone objects once.

    Also, the $(".vol-slider").on('input' code is not modifying the gain node itself. You can use gain.rampTo() to modify the gain while Tone is playing your part.

    This should work:

    $(".stop").on("click", function (e) {
      Tone.Transport.stop();
    }); // .stop
    
    
    // Create Tone objects here.
    
    const synth = new Tone.PolySynth();
    let vol = 1;
    const gain = new Tone.Gain(vol).toDestination();
    synth.chain(gain);
    let testnotes = [
      { time: "0:0:0", note: "D3", duration: "8n" },
      { time: "0:0:0", note: "D3", duration: "16n" },
      { time: "0:0:1", note: "E3", duration: "16n" },
      { time: "0:0:2", note: "F3", duration: "16n" },
      { time: "0:0:3", note: "G3", duration: "16n" },
      { time: "0:1:0", note: "A3", duration: "16n" },
      { time: "0:1:1", note: "B3", duration: "16n" },
      { time: "0:1:2", note: "C4", duration: "16n" },
      { time: "0:1:3", note: "D4", duration: "16n" },
      { time: "0:2:0", note: "E4", duration: "16n" },
      { time: "0:2:1", note: "F4", duration: "8n" },
      { time: "0:2:2", note: "G4", duration: "8n" },
      { time: "0:2:3", note: "A4", duration: "8n" },
      { time: "0:3:0", note: "B4", duration: "8n" },
      { time: "0:3:1", note: "C5", duration: "4n" }
    ];
    const part = new Tone.Part(function (time, note) {
      synth.triggerAttackRelease(note.note, note.duration, time);
    }, testnotes);
    
    
    $(".play").on("click", function (e) {
      console.clear();
      Tone.start();
      Tone.Transport.stop();
      part.start(0);
    
      Tone.Transport.start();
    }); // .play
    
    $(".vol-slider").on("input", function (e) {
      let val = $(this).val(); // This is a string at this point ...
      let valFloat = parseFloat(val);
      $(".vol-text").text(val);
      gain.gain.rampTo(valFloat, 0.1);
    }); // .vol-slider
    
    

    Here's a codepen with it all put together: https://codepen.io/joeweiss/pen/GRWxmML