Start · Sprachen · JavaScript · Referenz · AudioScheduledSourceNode: ended

AudioScheduledSourceNode: ended

Ereignis

Wird ausgelöst, wenn ein <code>AudioScheduledSourceNode</code> (z. B. <code>AudioBufferSourceNode</code> oder <code>OscillatorNode</code>) die Wiedergabe beendet hat.

seit JavaScript Baseline: alle modernen Browser (Web Audio API) Kategorie: webapi

Signatur

audioScheduledSourceNode.addEventListener("ended", handler)

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 AudioBufferSourceNode ohne loop das Ende seines Puffers (oder der mit start() 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

Typ
void
Beschreibung
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
Ton beendet bei 1.01 s

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();
});
Wiedergabe fertig

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()]);
Spiele Titel 1 Spiele Titel 2 Spiele Titel 3 Wiedergabeliste beendet

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: ended wird 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 mit start(when) planen und das Ereignis nicht dafür verwenden.
  • Endlosschleife: Bei AudioBufferSourceNode mit loop = true und bei OscillatorNode wird ended erst ausgelöst, wenn stop() aufgerufen wird.
  • Nie gestartet: Wurde start() nie aufgerufen, wird auch kein ended ausgelöst.
  • Angehaltener Kontext: Solange der AudioContext suspended ist (Autoplay-Richtlinie), läuft die Zeit nicht weiter und ended kommt nicht an. Rufe ctx.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 war onended direkt an den Unterklassen definiert, heute gehört es zu AudioScheduledSourceNode.