Homepage-Webhilfe Event-Banner

Audio-Wiedergabe mit dem Browser

geschrieben von Benjamin Jung am 31.03.2018 19:42:30

In diesem Blogbeitrag wollen wir uns damit beschäftigen, wie man mit Hilfe von JavaScript und der Web Audio API, einer Schnittstelle für Skriptsprachen in Browsern, Töne und Audio-Daten wiedergeben kann.


Informationen zur Web Audio API

Die Web Audio API ist, ähnlich wie die File API, eine relativ neue Schnittstelle, die es einer Skriptsprache wie JavaScript Zugriff auf den Browser gewährt. So ist es mit Hilfe der Web Audio API möglich, auf Wiedergabegeräte (i. d. R. einen Lautsprecher) und Aufnahmegeräte (i. d. R. ein Mikrofon) zuzugreifen. Gerade bei letzterem ist im Regelfall aus Sicherheitsgründen eine Bestätigung durch den Benutzer erforderlich.

Der Browsersupport für die Web Audio API liegt aktuell ( Stand März 2018 ) bei über 80%. Zu beachten ist jedoch, dass der Microsoft Internet Explorer die API nicht unterstützt. Microsoft Edge, welcher den Internet Explorer ab Version 12 ersetzt, unterstützt die API dagegen vollständig. Für die Browser Safari und ältere Versionen von Google Chrome wird das webkit-Präfix benötigt. Die Browser Opera Mini und der Android-Browser, welche auf Mobilgeräten zum Einsatz kommen, unterstützen die API bisher noch nicht.

Wichtig: Die Web Audio API befindet sich aktuell noch im Arbeitsentwurf (engl. Working Draft). Es sind also immer Änderungen und vor allem Neuerungen möglich.


Audio-Kontext erstellen

Bevor wir mit der Web Audio API arbeiten können, müssen wir zunächst ein AudioContext-Objekt instanziieren:

var oAudio = new AudioContext();

Wie bereits oben erwähnt, unterstützen einige Browser die API nur mit dem webkit-Präfix. In diesem Fall müssen wir ein webkitAudioContext-Objekt erzeugen. Eine korrekte Instanziierung würde bspw. so aussehen:

var oAudio = new (window.AudioContext || window.webkitAudioContext)();

Datenpuffer vorbereiten

Nachdem wir den Audio-Kontext erzeugt haben, können wir als nächstes unseren Datenpuffer für die Audiodaten, welche wir wiedergeben möchten, anlegen. Dazu nutzen wir die Funktion createBuffer() des AudioContext-Objekts. Als Parameter übergeben wir die Anzahl an Kanälen (i. d. R. 1 für Mono oder 2 für Stereo), die Anzahl an Samples (Dauer in Sekunden * Abtastrate) und die Abtastrate. Möchten wir die Abtastrate des Audio-Kontext nutzen, so können wir diese über die Eigenschaft sampleRate abfragen. Im folgenden Beispiel wird ein 2-kanaliger Datenpuffer mit einer 5-sekündigen Länge erzeugt:

var oAudioBuffer = oAudio.createBuffer(2, 5 * oAudio.sampleRate, oAudio.sampleRate);

Als Rückgabe von der Funktion erhalten wir ein AudioBuffer-Objekt. Die vorher gesetzten Einstellungen des Datenpuffers können nicht mehr verändert werden, jedoch über die Eigenschaften numberOfChannels (Anzahl an Kanälen), length (Anzahl an Samples) und sampleRate (Abtastrate) lesend abgerufen werden.

Das AudioBuffer-Objekt hält die Audiodaten in Form von linearen PCM-Samples (Pulse-Code-Modulation) als 32-Bit Gleitkommazahlen (Werte zwischen -1 und +1). Um das Objekt nun mit Daten zu füllen, müssen wir die Funktion getChannelData() aufrufen. Der Funktion wird dabei als Parameter die Kanalnummer (0-basierend) übergeben.

var aAudioSamples = oAudioBuffer.getChannelData(0);

Als Rückgabe von der Funktion erhalten wir ein Objekt des typisierten Arrays Float32Array. Der Zugriff auf ein Element im Array erfolgt, wie bei einem „normalen“ Array, über die eckigen Klammern []. Zu beachten ist jedoch, dass typisierte Arrays eine feste Länge haben und somit Funktionen wie push(), pop(), shift() und unshift() nicht existieren.

aAudioSamples[0] = 0.1475817941;
aAudioSamples[1] = 0.1894612372;

Daten zur Wiedergabe nutzen und diese steuern

Sobald wir den Datenpuffer angelegt und mit Daten gefüllt haben, können wir diesen nun zur Wiedergabe nutzen. Hierfür müssen wir zunächst ein AudioBufferSourceNode-Objekt erzeugen. Dieses müssen wir aber nicht selbst instanziieren, sondern erhalten wir als Rückgabewert von der Funktion createBufferSource():

var oAudioBufferSource = oAudio.createBufferSource();

Das AudioBufferSourceNode-Objekt stellt die Quelle für eine zeitgesteuerte Wiedergabe eines AudioBuffer-Objekts dar. Um das AudioBuffer-Objekt mit dem AudioBufferSourceNode-Objekt zu verbinden, müssen wir den Datenpuffer über die Eigenschaft buffer zuweisen:

oAudioBufferSource.buffer = oAudioBuffer;

Bevor wir die Wiedergabe starten können, müssen wir das AudioBufferSourceNode-Objekt noch mit einem Wiedergabegerät verbinden. Hierfür verwenden wir die Funktion connect(). Als Parameter übergeben wir den Wert der destination-Eigenschaft des AudioContext-Objekts:

oAudioBufferSource.connect(oAudio.destination);

Nachdem wir unsere Daten in die richtige Form gebracht und alle notwendigen Einstellungen gesetzt haben, können wir unsere Daten nun wiedergeben. Dazu nutzen wir die Funktion start(). Als Parameter kann ein Startwert, ein Offset und eine Wiedergabelänge angegeben werden. Alle drei Werte sind Gleitkommazahlen und stellen eine Zeit in Sekunden dar. Der Startwert muss dabei im Zeitraster des AudioContext-Objekts liegen, welcher über die Eigenschaft currentTime abgerufen werden kann. Möchten wir die Wiedergabe sofort starten, so muss lediglich 0 übergeben werden. Der folgende Code würde die Wiedergabe der Audiodaten in 3 Sekunden starten:

oAudioBufferSource.start(oAudio.currentTime + 3);

Möchten wir die Audio-Wiedergabe stoppen, so können wir die Funktion stop() nutzen. Als Parameter kann hier ein Stoppwert übergeben werden. Die Bedeutung entspricht dabei dem Startwert der Funktion start(). Folgender Code stoppt die Audio-Ausgabe in 1 Sekunde:

oAudioBufferSource.stop(oAudio.currentTime + 1);

Wichtig: Es ist nicht möglich, eine Audio-Wiedergabe zu pausieren und dann wieder fortzusetzen. Einen solchen Mechanismus müssen Sie, falls Sie diesen benötigen, selber programmieren. Eine mögliche Implementierung wird auch im Beispiel unten verwendet. Bitte beachten Sie auch, dass ein AudioBufferSourceNode-Objekt nicht mehrmals verwendet werden kann, d. h. die start()-Funktion darf nur ein einziges Mal aufgerufen werden. Das AudioBuffer-Objekt darf hingegen beliebig oft verwendet werden.

Übrigens: Falls Sie auf das Beenden einer Wiedergabe des AudioBufferSourceNode-Objekts reagieren möchten, so können Sie das Event ended verwenden. Das Ereignis tritt ein, unabhängig ob die Wiedergabe vollständig durchgeführt oder über die Funktion stop() angehalten wurde.


Beispiel

Im folgenden Beispiel sehen Sie einen Audio-Player, welcher die C-Dur Tonleiter (C-D-E-F-G-A-H-C) wiedergibt. Die Wiedergabe kann dabei auf Wunsch pausiert und wieder fortgesetzt werden. Die Benutzerinteraktion erfolgt durch die 4 Buttons auf der Seite. Auf die Generierung der PCM-Daten wollen wir an dieser Stelle nicht weiter eingehen. Um es kurz zusammenzufassen: Es werden für jeden Ton der Tonleiter mehrere Sinuskurven erzeugt, sodass jeder Ton 0.3 Sekunden lang ist.

HTML-Code:

<form name="control">
    <input type="button" name="start" value="Wiedergabe starten" style="display: none;" onclick="OnStartAudioPlaying()" />
    <input type="button" name="pause" value="Wiedergabe pausieren" style="display: none;" onclick="OnPauseAudioPlaying()" />
    <input type="button" name="resume" value="Wiedergabe fortsetzen" style="display: none;" onclick="OnResumeAudioPlaying()" />
    <input type="button" name="stop" value="Wiedergabe stoppen" style="display: none;" onclick="OnStopAudioPlaying()" />
</form>

JavaScript-Code:

const aNotes = [ 261.626, 293.665, 329.628, 349.228, 391.995, 440.000, 493.883, 525.251 ];
const iSampleRate = 44000;
const fNoteLength = 0.3;

var oAudioPlayer = null;
var oAudioBuffer = null;
var oAudioBufferSource = null;
var fAudioStartTime = 0;
var fAudioPauseTime = 0;
var bAudioSuppressEvent = false;

function GenerateAudioData()
{
    var aAudioSamples;
    
    // Audio-Buffer erzeugen (dieser kann mehrmals zur Wiedergabe verwendet werden)
    oAudioBuffer = oAudioPlayer.createBuffer(1, iSampleRate * fNoteLength * aNotes.length, iSampleRate);
    aAudioSamples = oAudioBuffer.getChannelData(0);
    
    // Daten für Audio-Wiedergabe erzeugen
    for (var i = 0, k = 0; i < aNotes.length; i++)
    {
        for (var j = 0; j < iSampleRate * fNoteLength; j++, k++)
            aAudioSamples[k] = Math.sin(j / ((iSampleRate / aNotes[i]) / (Math.PI * 2)));
    }
}

function PlayAudioData(fOffset)
{
    // Audio-Buffer-Source erzeugen
    oAudioBufferSource = oAudioPlayer.createBufferSource();
    oAudioBufferSource.buffer = oAudioBuffer;
    
    // Event, welches bei Beendigung der Wiedergabe aufgerufen wird
    oAudioBufferSource.onended = OnAudioDataPlayed;
    
    // Audio-Buffer-Source mit Wiedergabegerät verbinden und sofort wiedergeben
    oAudioBufferSource.connect(oAudioPlayer.destination);
    oAudioBufferSource.start(0, fOffset);
}

function StopPlayAudioData()
{
    // Bit für Event-Unterdrückung setzen
    bAudioSuppressEvent = true;
    
    // Wiedergabe des Audio-Buffer-Source sofort beenden
    oAudioBufferSource.stop(0);
}

function OnAudioDataPlayed()
{
    // Prüfen, ob das Event nicht unterdrückt wurde
    if (!bAudioSuppressEvent)
    {
        // Anzeige von Buttons anpassen
        document.forms.control.start.style.display = "";
        document.forms.control.pause.style.display = "none";
        document.forms.control.resume.style.display = "none";
        document.forms.control.stop.style.display = "none";
    }
    
    // Bit für Event-Unterdrückung zurücksetzen
    bAudioSuppressEvent = false;
}

function OnInitAudioPlayer()
{
    // Initialisierung des Audio-Players
    if (window.AudioContext)
        oAudioPlayer = new AudioContext();
    else if (window.webkitAudioContext)
        oAudioPlayer = new webkitAudioContext();
    
    // Prüfen, ob der Audio-Player initialisiert werden konnte
    if (oAudioPlayer != null)
    {
        // Anzeige von Buttons anpassen
        document.forms.control.start.style.display = "";
        
        // Audio-Daten erzeugen
        GenerateAudioData();
    }
    // Andernfalls Meldung ausgeben
    else
        alert("Ihr Browser unterstützt die Web Audio API nicht!");
}

function OnStartAudioPlaying()
{
    // Anzeige von Buttons anpassen
    document.forms.control.start.style.display = "none";
    document.forms.control.pause.style.display = "";
    document.forms.control.stop.style.display = "";
    
    // Audio-Daten ab dem Anfang wiedergeben
    PlayAudioData();
    
    // Startzeit merken
    fAudioStartTime = oAudioPlayer.currentTime;
}

function OnPauseAudioPlaying()
{
    // Anzeige von Buttons anpassen
    document.forms.control.pause.style.display = "none";
    document.forms.control.resume.style.display = "";
    
    // Pausezeit merken
    fAudioPauseTime = oAudioPlayer.currentTime;
    
    // Audio-Wiedergabe stoppen
    StopPlayAudioData();
}

function OnResumeAudioPlaying()
{
    // Anzeige von Buttons anpassen
    document.forms.control.pause.style.display = "";
    document.forms.control.resume.style.display = "none";
    
    // Audio-Daten ab der pausierten Stelle wiedergeben
    PlayAudioData(fAudioPauseTime - fAudioStartTime);
}

function OnStopAudioPlaying()
{
    // Anzeige von Buttons anpassen
    document.forms.control.start.style.display = "";
    document.forms.control.pause.style.display = "none";
    document.forms.control.resume.style.display = "none";
    document.forms.control.stop.style.display = "none";
    
    // Audio-Wiedergabe stoppen
    StopPlayAudioData();
}

// Ereignis zur Initialisierung des Audio-Players registrieren
window.onload = OnInitAudioPlayer;
Vorschau

Übrigens: Die Generierung und Wiedergabe von Sinuskurven kann mit Hilfe des OscillatorNode-Objekts der Web Audio API sogar noch vereinfacht werden. Da in diesem Fall jedoch die Verwendung des AudioBuffer-Objekts wegfällt, haben wir auf die Verwendung des OscillatorNode-Objekts in diesem Beispiel verzichtet.


Neben den hier vorgestellten Objekten, bietet die Web Audio API natürlich noch viel mehr. Das Vorstellen aller Objekte, Funktionen und Eigenschaften würde jedoch den Umfang dieses Blogbeitrags sprengen. biggrin Daher haben wir uns dazu entschiedenen, in diesem Blogbeitrag lediglich die Grundlagen der Web Audio API zu beschreiben.

» Hallo Gast • Anmelden
Um unsere Webseite für Sie optimal zu gestalten und fortlaufend verbessern zu können, verwenden wir Cookies. Durch die weitere Nutzung der Webseite stimmen Sie der Verwendung von Cookies zu. Weitere Informationen OK