Tone.js puts an end to all currently playing sounds

With just a button press, I want to play a sequence of notes using a PolySynth and a Sequence. When the button is pressed repeatedly, I want the currently playing notes to stop and restart.

The challenge: No matter what method I use, I can't seem to completely stop/silence the previously played notes in order to start the sequence again when the button is clicked. This could be due to the envelope's decay/sustain settings.

My Synthesizer Setup:

import { PolySynth } from 'tone'

const synth = new PolySynth(Synth, {
  oscillator: {
    type: 'sine4',
    volume: -6,
  },
  envelope: {
    attack: 0.01,
    decay: 0.5,
    sustain: 0.1,
    release: 1,
  },
}).toDestination()
synth.maxPolyphony = 4 // unsure if this is necessary

My Note Sequence:

import { Sequence } from 'tone'

// Play two notes individually followed by playing them together
const notes = [
  { note: 'C4', duration: '8n' },
  { note: 'G4', duration: '8n' },
  { note: ['C4', 'G4'], duration: '4n' }
]

// The sequence that plays the notes one after another
const sequence = new Sequence({
  subdivision: '8n',
  loop: false,
  events: notes,
  callback: (time, note) => synth.triggerAttackRelease(note.note, note.duration, time),
})

This is how it's triggered using an event handler:

import { start, Transport } from 'tone'

// Event handler attached to a button's onClick
function onButtonClicked() {
  // Begin playback
  start()
  
  // Attempting to stop any current sound
  Transport.cancel()
  Transport.stop()

  // Restart playback
  Transport.start()
  sequence.start()
}

Is there a way to completely silence all sound before starting a new sequence?

Answer №1

A Brief Explanation

After some contemplation, it seems that the behavior you are experiencing is actually intentional. When you trigger a note on a Synth (which essentially functions as an AudioWorkletNode), the note is immediately played and then disappears. The only way to prevent the note from playing further is to mute the synth entirely.

A Detailed Insight

In your comments, you mentioned a potential conceptual misunderstanding, which I believe is worth exploring further.

Let's delve into the process of sound generation through MIDI.

  1. You connect a Synth (which converts MIDI notes into sound) to an output.
  2. You schedule MIDI notes on the transport timeline.
  3. You initiate the transport playback.
  4. When the transport reaches the scheduled time for a MIDI note, the corresponding value is sent to the Synth.
  5. Since the Synth operates as an AudioWorkletNode with an Envelope Generator, it interprets the MIDI note to trigger internal sound creation (via the envelope). This results in sound being generated for a specific duration determined by the attack, decay, sustain, and release phases. Even if the MIDI note's duration is very short, the sound will persist for a slightly longer period due to the envelope stages. To simplify:
    • The MIDI note has a defined start and end point on the timeline.
    • The start initiates the attack-decay-sustain part of the Envelope.
    • The end triggers the release portion of the Envelope.
    • Once the MIDI note activates the Envelope, sound production commences.

Therefore, stopping the transport or sequence does not abruptly halt the sound. If a MIDI note has already triggered the Envelope, it will complete the release phase before ceasing playback completely.

Consequently, the lingering sound from your Synth is attributed to how the Envelope responds to MIDI input, rather than being directly tied to the transport. I trust this explanation sheds some light on the situation. Should there be any misinterpretations on my part, please feel free to clarify.

Answer №2

To address the issue of a note continuing to play even after the transport has stopped, it is important to trigger the release of all notes when the stop button is pushed. I encountered a similar challenge while using tambien/piano, which is based on Tonejs.

    Tone.Transport.toggle()
    if (Tone.Transport.state === 'stopped') {
      for (let j=11; j<103; j++) {
        piano.keyUp({midi: j}, '+0')
      }
    }

Answer №3

While implementing this solution may pose challenges in longer sequences, it should work well in your specific case. I have encountered a similar problem before and found success with this approach.

The issue with polySynth lies in the limitation of only being able to add notes that are played. However, with the normal synth, you have the ability to "kill the sound" by replacing the played note with an empty one.

// This code snippet will play a continuously sustained note.
synth = synth || new Tone.Synth().toMaster();
synth.triggerAttackRelease(noteToPlay);

// This code snippet will mute the sound.
synth = synth || new Tone.Synth().toMaster();
synth.triggerAttackRelease();

You can manually create a polyphonic synth by playing multiple single synths simultaneously.

synth1 = synth1 || new Tone.Synth().toMaster();
synth1.triggerAttackRelease(noteToPlay);
synth2 = synth2 || new Tone.Synth().toMaster();
synth2.triggerAttackRelease(noteToPlay2);
synth3 = synth3 || new Tone.Synth().toMaster();
synth3.triggerAttackRelease(noteToPlay3);

Constructing the sequence may be more complex, but you can mute the playing sequence by incorporating a "soundKiller" at the start of the play function. Importantly, instead of declaring the sequence and then playing it through "Transport", opt to play the notes directly within the event handler as Transport cannot be stopped once initiated.

Answer №4

To stop all currently triggered notes from playing, simply use the releaseAll() method on the PolySynth object:

polySynth.releaseAll()

Keep in mind that for the note release to take effect, Tone.Transport must still be active. Consider using a setTimeout to delay the call to Tone.Transport.stop() if needed.

Answer №5

To pause the Tone.Transport, use the command Tone.Transport.pause()

If you have a play/pause button with the class 'pause-play', refer to the example code below:

$( '.pause-play' ).on('click', function(e) {

    if (Tone.Transport.state === "paused" ) {  
          Tone.Transport.start("+0.1") })
    else {
          Tone.Transport.pause()  }
    }
)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

simulated xhr server along with the locales in polymer appLocalizeBehavior

Currently, I am in the process of developing a web frontend utilizing Polymer. Within my web component, I incorporate various other components such as paper-input or custom web components. To facilitate testing for demonstration purposes, I have integrated ...

What could be the reason behind receiving an "undefined" message when attempting to access db.collection in the provided code snippet?

var express = require('express'); var GoogleUrl = require('google-url'); var favicon = require('serve-favicon'); var mongo = require('mongodb').MongoClient; var app = express(); var db; var googleUrl = new GoogleUrl( ...

Click event for a tree component in Angular 2

How can I trigger a node click event in an Angular tree component? import { TREE_ACTIONS, KEYS, IActionMapping } from 'angular2-tree-component'; const actionMapping:IActionMapping = { mouse: { click: TREE_ACTIONS.TOGGLE_SELECTED_MULTI } ...

Unexpected behavior with AWS DynamoDB ScanInput ExpressionAttributeValue

I crafted a scan query to only retrieve enabled data in the following way: const FilterExpression = 'enabled = :enabled'; const ExpressionAttributeValues = { ':enabled': { 'BOOL': true } }; const scanParameters: Sc ...

Puppeteer's flawed performance leads to the generation of low-quality

Currently, I am utilizing puppeteer to generate a PDF from my static local HTML file. However, the resulting PDF is turning out to be corrupted. When attempting to open the file using Adobe Reader, an error message pops up stating 'Bad file handle&apo ...

The addition operation in JavaScript seems to be malfunctioning as it is not adding up my values

After encountering an issue with calculating the number of payments, I discovered that the addition operator was not functioning as expected. Instead of summing up values, it was treating them as strings. For instance, when trying to add 3 and 5, the out ...

Turn off the chrome react DevTools when deploying to production to ensure the

I have successfully browserified my react app for production using gulp and envify to set up NODE_ENV. This has allowed me to remove react warnings, error reporting in the console, and even disable some features like the require of react-addons-perf. Afte ...

Stopping a NodeJS script execution: Tips and Tricks

While working on my NodeJS application, I've noticed that occasionally my CPU usage spikes to 100%, but there is no memory variation. This causes my Event Loop to become blocked by the operation. I recalled how browsers handle problematic scripts by ...

Attempting to instruct my chrome extension to execute a click action on a specific element found within the webpage

I am currently working on developing a unique chrome extension that has the capability to download mp3s specifically from hiphopdx. I have discovered a potential solution, where once the play button on the website is clicked, it becomes possible to extract ...

Avoiding code duplication in Angular: tips for optimizing functions

Is there a way to avoid repeating the same for loop for a second variable and use only one? I'm trying to apply the "Don't repeat yourself" method here. Should I consider using an array? JS: var app=angular.module('xpCalc', []); app.c ...

Concealing or revealing an image with jQuery when hovering

I currently have 3 <a> tags within my html, each containing 2 images. Specifically, the greyscale images are the ones that are visible while the colored ones are set to display:none. I am aiming to achieve a functionality where upon hovering over th ...

preventing further executions by halting a function after the initial click

I've come across this function: function display() { $.ajax({ url: "new.php", type: "POST", data: { textval: $("#hil").val(), }, success: function(data) { ...

Using Node.js, Express, and Socket.IO to establish real-time communication between two static client pages

I'm in the process of creating a remote control for a gallery using nodejs, express, and socket.io. Here's the project structure: /index.js /public/screen.html /screen.js /remote.html /remote.js The goal is to display a gallery of ima ...

Enhanced form validation using AJAX

Currently, my aim is to implement client-side validation with AJAX in order to check for any empty fields within a basic form. If a field happens to be blank, I intend to alert the user about its invalidity. It is crucial that the form does not get submitt ...

Ways to run evaluations on 'private' functions within an angular service using Karma and Jasmine

In my Angular application, I have a BracketService that consists of various functions for comparing weights and creating brackets based on weight groups. The service includes functions like compareByWeight, filterWeightGroup, and createBracketsByWeightGrou ...

Can HTML tag attributes be accessed in CSS within the Shadow-DOM?

As I work on developing a custom component using StencilJS, I find myself needing to adjust the outline behavior when users navigate through it with either a keyboard or mouse. My component employs ShadowDOM and I aim to extract an HTML tag attribute from ...

Steps to designate a character depending on the frequency of its duplication within an array

I have a series of values in an array that I need to go through and assign incremental numerical values, starting from 1. If the same value appears more than once in the array, I want to append the original assigned number with the letter A, and then B, ac ...

Clicking on a radio button can trigger the selection of another radio button

I'm currently working on a form that includes 8 radio buttons - 4 for System A and 4 for System B. The options for the buttons are CF, ISO, ISO-B, and NW. My goal is to create a functionality where selecting a radio button in System A automatically se ...

Retrieving the input value stored in an inline variable with jQuery after being saved

Upon clicking the Save Button within a document using jQuery, the value is stored in a hidden input field. I am facing challenges trying to display this value inside an onclick event below the input, allowing users to easily identify and delete the item if ...

Error: Unforeseen token encountered while attempting to import React in Javascript

Upon executing the command 'npm run start', I encountered this error: import React from 'react'; ^^^^^ SyntaxError: Unexpected identifier at Module._compile (internal/modules/cjs/loader.js:721:23) at Object.Module._exten ...