Sounds mit der Web Audio API abspielen
Sie möchten Audio im Browser mit präziser Kontrolle abspielen – Scheduling, Effekte, Synthese – aber das <audio>-Element reicht nicht aus. Die Web Audio API löst dieses Problem, doch ihre Dokumentation vermischt veraltete Muster mit aktuellen Best Practices. Dieser Artikel erläutert den modernen Ansatz zur Sound-Wiedergabe mit der Web Audio API und behandelt AudioContext-Grundlagen, Source-Nodes und das Audio-Graph-Modell ohne das Altlastengewicht.
Wichtigste Erkenntnisse
- Erstellen Sie einen
AudioContextpro Anwendung und behandeln Sie immer den suspendierten Zustand mitresume()nach Benutzerinteraktion - Verwenden Sie
AudioBufferSourceNodefür vorgeladene Audiodateien undOscillatorNodefür synthetisierte Töne – beide sind Einweg-Nodes - Vermeiden Sie den veralteten
ScriptProcessorNode; verwenden SieAudioWorkletfür benutzerdefinierte Audioverarbeitung - Behandeln Sie das Ausgabegeräte-Routing über
setSinkId()als experimentell mit unvollständiger Browser-Unterstützung
AudioContext-Grundlagen verstehen
Jede Web-Audio-Anwendung beginnt mit einem AudioContext. Dieses Objekt verwaltet alle Audio-Operationen und stellt die Uhr für das Scheduling bereit. Betrachten Sie es als Laufzeitumgebung für Ihren Audio-Graph.
const audioContext = new AudioContext()
Eine kritische Einschränkung: Browser suspendieren neue Contexts, bis eine Benutzergeste erfolgt. Überprüfen Sie immer audioContext.state und rufen Sie resume() von einem Click- oder Keypress-Handler auf:
button.addEventListener('click', async () => {
if (audioContext.state === 'suspended') {
await audioContext.resume()
}
// Jetzt sicher für Audio-Wiedergabe
})
In den meisten Anwendungen ist ein einzelner AudioContext ausreichend und empfohlen. Das Erstellen mehrerer Contexts erhöht die Ressourcennutzung und erschwert die Verwaltung von Timing und Synchronisation. (Siehe: https://developer.mozilla.org/en-US/docs/Web/API/AudioContext)
Das Audio-Graph-Modell
Web Audio verwendet einen gerichteten Graphen aus Nodes. Audio fließt von Source-Nodes über Processing-Nodes zu einem Destination. Das audioContext.destination repräsentiert die Standard-Ausgabe – typischerweise die Lautsprecher des Benutzers.
Nodes werden über die connect()-Methode verbunden:
sourceNode.connect(gainNode)
gainNode.connect(audioContext.destination)
Dieses modulare Design ermöglicht es Ihnen, Filter, Gain-Controls oder Analyzer überall in der Kette einzufügen. Trennen Sie Nodes mit disconnect(), wenn Sie den Graphen neu konfigurieren.
Source-Nodes für Sound-Wiedergabe
Zwei Source-Typen decken die meisten Wiedergabe-Szenarien ab.
AudioBufferSourceNode
Für vorgeladene Audiodateien dekodieren Sie die Daten in einen AudioBuffer und spielen sie dann über einen AudioBufferSourceNode ab:
const response = await fetch('sound.mp3')
const arrayBuffer = await response.arrayBuffer()
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer)
const source = audioContext.createBufferSource()
source.buffer = audioBuffer
source.connect(audioContext.destination)
source.start()
Jeder AudioBufferSourceNode spielt einmal ab. Erstellen Sie für jede Wiedergabe einen neuen Source-Node – sie sind leichtgewichtig. Der zugrunde liegende AudioBuffer ist wiederverwendbar.
Beachten Sie, dass decodeAudioData() in modernen Browsern Promises unterstützt, ältere Safari-Versionen jedoch die Callback-Form erforderten. Dies ist hauptsächlich ein Legacy-Problem, aber dennoch relevant für Long-Tail-Kompatibilität.
OscillatorNode
Für synthetisierte Töne verwenden Sie OscillatorNode:
const oscillator = audioContext.createOscillator()
oscillator.type = 'sine'
oscillator.frequency.setValueAtTime(440, audioContext.currentTime)
oscillator.connect(audioContext.destination)
oscillator.start()
oscillator.stop(audioContext.currentTime + 1)
Oszillatoren spielen ebenfalls nur einmal ab. Planen Sie start()- und stop()-Zeiten mit audioContext.currentTime für sample-genaue Wiedergabe.
Discover how at OpenReplay.com.
AudioWorklet vs. ScriptProcessor
Für benutzerdefinierte Audioverarbeitung vermeiden Sie ScriptProcessorNode. Er ist veraltet, läuft im Haupt-Thread und verursacht Audio-Glitches unter Last.
AudioWorklet ist der moderne Ersatz. Es läuft in einem dedizierten Audio-Rendering-Thread mit deterministischem Timing:
// processor.js
class MyProcessor extends AudioWorkletProcessor {
process(inputs, outputs, parameters) {
// Audio hier verarbeiten
return true
}
}
registerProcessor('my-processor', MyProcessor)
// main.js
await audioContext.audioWorklet.addModule('processor.js')
const workletNode = new AudioWorkletNode(audioContext, 'my-processor')
workletNode.connect(audioContext.destination)
AudioWorklet wird in allen modernen Evergreen-Browsern unterstützt. Die Haupteinschränkungen sind tendenziell umgebungsbedingt (Secure Context, Bundling, Cross-Origin-Setup) und nicht auf fehlende API-Unterstützung zurückzuführen.
Einschränkungen beim Web-Audio-Ausgabe-Routing
Die Ausgabe-Routing-Fähigkeiten der API sind begrenzt und browserabhängig. Standardmäßig wird Audio zu audioContext.destination geleitet, das auf das Standard-Ausgabegerät des Systems abgebildet wird.
AudioContext.setSinkId() ermöglicht die Auswahl spezifischer Ausgabegeräte, aber behandeln Sie dies als experimentell und stark eingeschränkt. Es erfordert einen Secure Context (HTTPS), Benutzererlaubnis und entsprechende Permissions-Policy-Header. In der Praxis ist es auf Safari oder iOS nicht verfügbar und sollte nicht für plattformübergreifendes Lautsprecher-Switching verwendet werden.
Für Anwendungen, die Ausgabegeräte-Auswahl benötigen, erkennen Sie die Unterstützung explizit:
if (typeof audioContext.setSinkId === 'function') {
// Vorhandensein garantiert keine Verwendbarkeit; Berechtigungen und Policy können es dennoch blockieren
}
Selbst wenn die Methode existiert, können Aufrufe aufgrund von Policy- oder Plattform-Einschränkungen fehlschlagen. Bauen Sie Fallbacks ein und kommunizieren Sie diese Einschränkungen klar an die Benutzer.
Praktische Überlegungen
Autoplay-Richtlinien blockieren Audio bis zur Benutzerinteraktion. Gestalten Sie Ihre UI so, dass sie einen Klick vor der Initialisierung der Wiedergabe erfordert.
CORS-Einschränkungen gelten beim Abrufen von Audiodateien über Cross-Origin. Stellen Sie entsprechende Header sicher oder hosten Sie Dateien auf derselben Origin.
Speicherverwaltung ist wichtig für buffer-intensive Anwendungen. Dereferenzieren Sie ungenutzte AudioBuffer-Objekte und trennen Sie Nodes, mit denen Sie fertig sind.
Mobile Browser verhängen zusätzliche Einschränkungen – insbesondere iOS Safari. Testen Sie auf echten Geräten, nicht nur auf Simulatoren.
Fazit
Die Web Audio API bietet leistungsstarke, Low-Level-Audio-Kontrolle durch ein graphenbasiertes Modell. Beginnen Sie mit einem einzelnen AudioContext, respektieren Sie Autoplay-Richtlinien und verwenden Sie moderne Muster: AudioBufferSourceNode für Samples, OscillatorNode für Synthese und AudioWorklet für benutzerdefinierte Verarbeitung. Behandeln Sie Ausgabegeräte-Routing als stark eingeschränkt und plattformabhängig. Feature-Detection für alles, und bauen Sie für die Einschränkungen, die Browser tatsächlich auferlegen.
FAQs
Browser blockieren die AudioContext-Erstellung, bis eine Benutzerinteraktion erfolgt. Ihre Entwicklungsumgebung hat möglicherweise permissivere Einstellungen. Überprüfen Sie immer audioContext.state und rufen Sie resume() innerhalb eines Click- oder Keypress-Handlers auf, bevor Sie versuchen abzuspielen. Diese Autoplay-Richtlinie gilt universell für moderne Browser.
Nein. AudioBufferSourceNode-Instanzen sind per Design Einweg. Nach dem Aufruf von start() kann der Node nicht neu gestartet werden. Erstellen Sie für jede Wiedergabe einen neuen Source-Node, während Sie den zugrunde liegenden AudioBuffer wiederverwenden. Source-Nodes sind leichtgewichtig, daher hat dieses Muster minimale Performance-Auswirkungen.
Verwenden Sie AudioContext.setSinkId(), aber behandeln Sie es als stark eingeschränkt. Es erfordert HTTPS, Benutzererlaubnis und permissive Policies und wird auf Safari oder iOS nicht unterstützt. Führen Sie immer Feature-Detection durch, behandeln Sie Fehler elegant und informieren Sie Benutzer, wenn Geräteauswahl nicht verfügbar ist.
ScriptProcessorNode ist veraltet und läuft im Haupt-Thread, was Audio-Glitches bei intensiver Verarbeitung verursacht. AudioWorklet läuft in einem dedizierten Audio-Rendering-Thread mit deterministischem Timing, was es für Echtzeit-Audio-Manipulation geeignet macht. Verwenden Sie immer AudioWorklet für benutzerdefinierte Verarbeitung in neuen Projekten.
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.