javascript - Tone.js stoppa helt alla spelande ljud
Kort sagt, vid ett knapptryck skulle jag vilja spela några toner med en PolySynth och en Sequence. Om användaren trycker på knappen upprepade gånger skulle jag vilja att det som spelas stoppas och startas igen.
Problemet: Oavsett vad jag försöker kan jag inte helt avbryta/tysta de tidigare spelade tonerna om sekvensen startas igen (knappen klickas igen). Detta beror troligen på antingen envelope:ns decay/sustain.
Min synt:
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
Min sekvens:
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),
})
Så som jag spelar det, är detta en händelsehanterare:
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()
}
Hur skulle jag helt kunna döda allt ljud (om det finns något) innan jag börjar spela det?
Svar #1
Kort svar
Jag har tänkt ganska mycket på detta. Om jag förstår dig korrekt är detta faktiskt avsedd funktion. Du triggar ett ljud på en synth (som i princip är en AudioWorkletNode). Så snart en ton triggas på synthen är tonen borta. Det enda sättet att stoppa den tonen från att spelas är att tysta själva synthen.
Långt svar
I kommentarerna sa du att du kanske saknar något konceptuellt och jag tycker att du är på rätt spår med det.
Låt oss tänka på hur ett ljud genereras med MIDI.
- Du kopplar en synt (som tar MIDI-noter och genererar ljud) till en utgång
- Du schemalägger några MIDI-noter på transporten
- Du startar transporten
- Så snart transporten når den schemalagda tiden för en not, kommer det MIDI-värdet att skickas till synten.
Eftersom Synthen i princip är en AudioWorkletNode med en Envelope Generator, tar Synthen den MIDI-noten och triggar den interna ljudgenereringen (via envelopen). Så en MIDI-note vid en viss tidpunkt triggar en specifik längd av ljudgenerering (vilket skulle vara ADS-delen). Även om MIDI-notens varaktighet bara är 1 ms lång i ditt exempel, skulle ljudgenereringen hålla på i minst 1,001 sekunder (Release plus 1 millisekund MIDI-varaktighet). Låt oss bryta ner detta lite mer:
- MIDI-noten har en start- och slutpunkt på den imaginära transporttidslinjen.
- Start triggar ADS-delen av envelopen.
- Slut triggar R-delen av kuvertet.
- När din MIDI-not triggar omslaget genereras ljudet.
Så när du stoppar din transport eller själva sekvensen, vad skulle det göra? Om en MIDI-not redan har triggat Envelopen, kommer Envelopen att ta emot MIDI-sluttriggen och trigga Release-envelopen.
Så det kommer alltid att finnas ett eftersläpande ljud från din Synth, eftersom MIDI-noten inte bestämmer start- och slutpunkt för din Synth, utan triggar delar av ditt Envelope. Så egentligen skapar din Synth ljudet och det är varken kopplat till transporten, och det kan det inte vara.
Hoppas att den förklaringen hjälpte dig lite. Om jag missförstod dig är jag glad att rätta till det.
Svar #2
Noten fortsätter att spela eftersom transporten stannar efter att noten utlösts och innan den släpptes. Därför är en lösning att utlösa släppet av alla noter när du trycker på stoppknappen. Jag stötte på ett liknande problem när jag använde tambien/piano (baserat på Tonejs).
Tone.Transport.toggle()
if (Tone.Transport.state === 'stopped') {
for (let i=9; i<97; i++) {
piano.keyUp({midi: i}, '+0')
}
}
