Tonejs / Tone.js

A Web Audio framework for making interactive music in the browser.
https://tonejs.github.io
MIT License
13.37k stars 976 forks source link

Changing the bpm stops new ToneEvents from firing #1145

Open rev3rsor opened 1 year ago

rev3rsor commented 1 year ago

Describe the bug

After changing the bpm, any new ToneEvents seem to not fire.

To Reproduce

I'm using the browser console for convenience, instead of creating a repo.

  1. Go to a page with Tone.js loaded https://tonejs.github.io/docs/14.7.77/ToneEvent
  2. Run Tone.start().then(() => Tone.Transport.start())
  3. Run new Tone.ToneEvent(console.log).start() -> expect the time to be logged e.g. 29.58420918367347 null
  4. Run Tone.Transport.bpm.value = 70 to change the bpm
  5. Run new Tone.ToneEvent(console.log).start() again -> the time does not get logged

Expected behavior New events should be scheduled, based on the current time of the transport.

Related to https://github.com/Tonejs/Tone.js/issues/962?

braebo commented 1 year ago

I've noticed that using the bpm.setValueAtTime and debouncing any inputs connected to it helps minimize the buggy behavior.

This is an example of a tempo-slider Svelte component that works nicely for me:

<script lang="ts">
  import { getTransport } from 'tone'
  import { onDestroy } from 'svelte'

  let timer: NodeJS.Timeout

  const setTempo = (e: CustomEvent) => {
    const value = +e.detail.value

    clearTimeout(timer)
    timer = setTimeout(() => {
        getTransport().bpm.setValueAtTime(value, getTransport().now())
    }, 1000)
  }

  onDestroy(clearTimeout(timer))
</script>

<Slider value="120" on:change={setTempo} />
Garrett-Bodley commented 4 months ago

I am also having this issue in React. Changing Tone.Transport.bpm.value while the Transport is playing causes crackling and/or Tone crashes entirely and no longer generates sound until the page is refreshed.

Code

  const updateTempo = (val: number) => {
    setTempo(val);
    Tone.Transport.bpm.value = val
}

I had previously used Tone.Transport.bpm.rampTo() to get around this, but this is also currently failing:

  const updateTempo = (val: number) => {
    setTempo(val);
    Tone.Transport.bpm.value = val
}

The debouncing strategy described by FractalHQ does not seem to work either.

  const updateTempo = (val: number) => {
    setTempo(val);
    if(timer.current != null){ clearTimeout(timer.current) }
    timer.current = setTimeout(() => { Tone.Transport.bpm.value = val}, 1000);
  };

No errors are logged in the console but all sound stops working if I change the volume too quickly.