Búsqueda de sitios web

javascript - Tone.js detener completamente todos los sonidos que se están reproduciendo


En resumen, al presionar un botón me gustaría reproducir algunas notas usando un PolySynth y un Sequence. Si el usuario presiona repetidamente el botón, me gustaría que lo que se esté reproduciendo se detenga y se vuelva a iniciar.

El problema: No importa lo que intente, no puedo cancelar o silenciar completamente las notas reproducidas previamente en caso de que la secuencia se inicie de nuevo (se haga clic en el botón nuevamente). Esto es muy probablemente debido al decaimiento/sostenimiento del sobre.

Mi sintetizador:

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 // max notes playing at a time, not sure if necessary

Mi secuencia:

import { Sequence } from 'tone'

// Play the 2 notes individually then play them together
const notes = [
  { note: 'C4', duration: '8n' },
  { note: 'G4', duration: '8n' },
  { note: ['C4', 'G4'], duration: '4n' }
]

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

De la manera en que lo juego, esto es un manejador de eventos:

import { start, Transport } from 'tone'

// Event handler simply attached to a button's onClick
function onButtonClicked() {
  // Call whatever this start is, doc says it can only happen in an event handler
  start()
  
  // Try everything to kill current sound
  Transport.cancel()
  Transport.stop()

  // Start it again
  Transport.start()
  sequence.start()
}

¿Cómo podría eliminar completamente todo el sonido (si es que hay alguno) antes de empezar a reproducirlo?

Respuesta #1

Respuesta corta

Pensando bastante en esto, si te entiendo correctamente, esto en realidad es un comportamiento intencionado. Estás activando una nota en un sintetizador (que básicamente es un AudioWorkletNode). Así que en cuanto se activa una nota en el sintetizador, la nota desaparece. La única forma de evitar que esa nota se reproduzca sería silenciar el propio sintetizador.

Respuesta larga

En los comentarios dijiste que podrías estar perdiéndote de algo conceptualmente y creo que estás en lo correcto con eso.

Pensemos en cómo se genera un sonido con MIDI.

  1. Estás conectando un sintetizador (que recibe notas MIDI y genera sonido) a una salida
  2. Estás programando algunas notas MIDI en el transporte
  3. Tú inicias el transporte
  4. Tan pronto como el transporte llegue a la hora programada para una nota, ese valor MIDI se enviará al sintetizador.
  5. Dado que el Synth es básicamente un AudioWorkletNode con un Generador de Envolvente, el Synth toma esa nota MIDI y activa la generación de sonido interna (a través de la envolvente). Entonces, una nota MIDI en un punto específico en el tiempo activa una duración específica de generación de sonido (que sería la parte ADS). Incluso si la duración de la nota MIDI es de solo 1 ms en tu ejemplo, la generación de sonido se mantendría durante al menos 1.001 segundos (Release más 1 milisegundo de duración de la nota MIDI). Vamos a desglosar esto un poco más:

    • La nota MIDI tiene un punto de inicio y un punto de final en la línea de tiempo imaginaria del transporte.
    • Inicio activa la parte ADS del Sobre.
    • El final activa la parte R del sobre.
    • Una vez que tu nota MIDI activó el sobre, se genera el sonido.

Entonces, cuando detienes tu transporte o la secuencia misma, ¿qué pasaría? Si una nota MIDI ya activó el Sobre, el Sobre recibirá el disparo de final de MIDI y activará el sobre de Liberación.

Así que siempre habrá un sonido residual de tu sintetizador, porque la nota MIDI no determina tu punto de inicio y fin del sintetizador, sino que activa partes de tu envolvente. Así que en realidad tu sintetizador crea el sonido y eso no está ligado al transporte, ni puede estarlo.

Espero que esa explicación te haya ayudado un poco. Si te entendí mal, estoy feliz de corregirlo.

Respuesta #2

La nota sigue sonando porque el transporte se detiene después de que la nota fue activada y antes de que fuera liberada. Por lo tanto, una solución es activar la liberación de todas las notas cuando presionas el botón de detener. Me encontré con un problema similar mientras usaba tambien/piano (basado en Tonejs).

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

Artículos relacionados