Signatur
Beschreibung
Das Ereignis ended wird an einem AudioScheduledSourceNode ausgelöst, sobald der Knoten nicht mehr abspielt. Zu den Unterklassen gehören AudioBufferSourceNode, OscillatorNode und ConstantSourceNode. Das Ereignis ist vom Typ Event, bubbelt nicht und kann nicht abgebrochen werden.
Ausgelöst wird es, wenn:
- der mit
stop(when)geplante Endzeitpunkt erreicht ist, - ein
AudioBufferSourceNodeohneloopdas Ende seines Puffers (oder der mitstart()angegebenen Dauer) erreicht hat, stop()sofort aufgerufen wurde.
Du registrierst den Handler entweder mit addEventListener("ended", handler) oder über die Eigenschaft onended. Typische Anwendungsfälle sind Aufräumarbeiten (disconnect()), Wiedergabelisten, UI-Updates wie das Zurücksetzen eines Play-Buttons oder das Planen des nächsten Klangs.
Weil Quellknoten nur einmal gestartet werden können, eignet sich ended gut, um Verweise freizugeben und für die nächste Wiedergabe einen neuen Knoten anzulegen.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignisname, hier "ended". |
|
| $handler Pflicht | (event: Event) => void | Callback-Funktion, die ein einfaches Event-Objekt erhält. event.target ist der beendete Quellknoten. |
|
| $options | boolean | AddEventListenerOptions | false | Optionen wie { once: true }. Das ist sinnvoll, weil ein Quellknoten nur einmal endet. |
Rückgabewert
addEventListener() gibt nichts zurück. Die Eigenschaft onended enthält die zugewiesene Funktion oder null.Beispiele
Oszillator mit addEventListener: Ende erkennen und aufräumen
const ctx = new AudioContext();
const osc = new OscillatorNode(ctx, { frequency: 440 });
osc.connect(ctx.destination);
osc.addEventListener('ended', (event) => {
console.log('Ton beendet bei', ctx.currentTime.toFixed(2), 's');
event.target.disconnect(); // Knoten vom Graphen trennen
}, { once: true });
osc.start();
osc.stop(ctx.currentTime + 1); // nach 1 Sekunde stoppen
Sample abspielen und Play-Button über onended zurücksetzen
const ctx = new AudioContext();
const button = document.querySelector('#play');
const loadBuffer = async (url) => {
const res = await fetch(url);
return ctx.decodeAudioData(await res.arrayBuffer());
};
const bufferPromise = loadBuffer('/sounds/klick.mp3');
button.addEventListener('click', async () => {
await ctx.resume(); // Autoplay-Sperre aufheben
const source = new AudioBufferSourceNode(ctx, { buffer: await bufferPromise });
source.connect(ctx.destination);
button.disabled = true;
source.onended = () => {
button.disabled = false;
console.log('Wiedergabe fertig');
};
source.start();
});
Einfache Wiedergabeliste: nächsten Titel bei ended starten
const ctx = new AudioContext();
const playSequence = (buffers, index = 0) => {
if (index >= buffers.length) {
console.log('Wiedergabeliste beendet');
return;
}
const source = new AudioBufferSourceNode(ctx, { buffer: buffers[index] });
source.connect(ctx.destination);
source.addEventListener('ended', () => {
source.disconnect();
playSequence(buffers, index + 1);
}, { once: true });
source.start();
console.log(`Spiele Titel ${index + 1}`);
};
// Test mit synthetischen Puffern (je 0,5 s Stille)
const makeBuffer = () => ctx.createBuffer(1, ctx.sampleRate / 2, ctx.sampleRate);
playSequence([makeBuffer(), makeBuffer(), makeBuffer()]);
Handler wieder entfernen
const ctx = new AudioContext();
const osc = ctx.createOscillator();
osc.connect(ctx.destination);
const onEnded = () => console.log('Ende');
osc.addEventListener('ended', onEnded);
// Doch nicht benachrichtigen:
osc.removeEventListener('ended', onEnded);
osc.onended = null; // Alternative bei Zuweisung über die Eigenschaft
osc.start();
osc.stop(ctx.currentTime + 0.2);
// Wichtig · Fallstricke
- Nicht zeitgenau:
endedwird im Haupt-Thread ausgelöst und kann gegenüber dem tatsächlichen Audio-Ende einige Millisekunden verspätet ankommen. Für sample-genaues Timing solltest du Folgeklänge vorab mitstart(when)planen und das Ereignis nicht dafür verwenden. - Endlosschleife: Bei
AudioBufferSourceNodemitloop = trueund beiOscillatorNodewirdendederst ausgelöst, wennstop()aufgerufen wird. - Nie gestartet: Wurde
start()nie aufgerufen, wird auch keinendedausgelöst. - Angehaltener Kontext: Solange der
AudioContextsuspendedist (Autoplay-Richtlinie), läuft die Zeit nicht weiter undendedkommt nicht an. Rufectx.resume()nach einer Nutzeraktion auf. - Einmalig: Ein Quellknoten lässt sich nicht erneut starten. Für eine weitere Wiedergabe legst du einen neuen Knoten an.
- Kompatibilität: Wird von allen modernen Browsern unterstützt. Ältere Safari-Versionen brauchten
webkitAudioContext. Früher waronendeddirekt an den Unterklassen definiert, heute gehört es zuAudioScheduledSourceNode.