Moin moin und hallo,

im folgenden Artikel möchte ich die Progressive Web App Reihe um die Aufnahme von Audio Dateien erweitern.


Progressive Web App Audio

Ich möchte das Thema PWA ewtas mehr vertiefen und werde in Zukunft einzelne Aspekte, die man bisher vermeintlich nur einer nativen Applikation zugeschrieben hatte, aufgreifen und Lösungen, die mit reinen Web-Handwerkszeug realisiert sind, zeigen.

In diesem Artikel möchte ich das Thema Audio-Streaming aufgreifen.

Sei es um einen Podcast, eine Gesprächsnotiz oder einen anderen Anwendungsfall, wo eine Audioafunahme gefordert ist, zu implementieren.

Die größte Herausforderung steckt, meiner Meinung nach, gar nicht mal so sehr in der Verarbeitung der Daten (dafür gibt es viele freie Encoder), sondern im Abgreifen des Mikrofons.

Wie nimmt man Audio Dateien auf?

Das Abgreifen des Mikrofons muss erst durch den Benutzer genehmigt werden. Dafür gibt es eine spezielle “navigator.getUserMedia”-Methode.

Mit dieser lässt sich auch die Freigabe eines Kamera-Zugriffs erfragen. Man könnte damit z.B. einen Live-Video-Stream realisieren.

Das ist aber nicht im Rahmen dieses Artikels zu lösen.

Wir wollen nur Audio Dateien aufnehmen und diese im Ogg-Format (da besonders Platzsparend) an den Benutzer zurück spielen.

Wie erhält man eine Erlaubnis für Mikrofonzugriff?

Dazu hier ein mal ein kurzes Code-Schnippsel:



  function initAudio() {

    //ist noch keine getUserMedia Methode bekannt, versuchen wir die Chrome 
    //bzw. Firefox Version auf zu setzen
    if (!navigator.getUserMedia) {

        navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
    }

    //Anfrage bzgl der Freigabe
    navigator.getUserMedia({

        "audio": {
            "mandatory": {
                "googEchoCancellation": "false",
                "googAutoGainControl": "false",
                "googNoiseSuppression": "false",
                "googHighpassFilter": "false"
            },
            "optional": []
        },
    }, readStream, function (e) {

        alert('Error getting audio');
        console.log(e);
    });
}

//sobal der browser alle Ressorucne geladen hat, starten wir mit der Anfrage zur Freigabe 
//des Zugriffs auf das Mikrofon
window.addEventListener('load', initAudio);

Die readStream-Funktion wird automatisch vom Browser mit dem aktuellen Audio-Stream Objekt aufgerufen.

Wie verarbietet man einen Audio Stream im Browser?

Wie für auch für die Offline-Fähigkeit wird dafür ein Service Worker benötigt, der auf dei AudioContext zugreift.

Wir werden den Worker aber nicht selbst implementieren, denn dafür gibt es eine sehr gute Bibliothek namens WebAudioRecorder.js.


//wir versuchen den spezifizierten AudioContext ab zu greifen, ansonsten versuchen 
//wir die Firefox Version zu nehmen
window.AudioContext = window.AudioContext || window.webkitAudioContext;

//wir erzeugen eine glibale AudioContext-Instanz
var audioContext = new AudioContext();


//diese funktion wird automatisch mit dem Stream-Objekt aufgerufen
function readStream(stream) {

    //wir erzeugen einen neuen Punkt zum abgreifen des AudioSingnals. 
    //es können auch mehrere erzeugt werden, mit z.B. verschiedenen Effekten 
    //oder Encoding Kanälen, usw.
    inputPoint = audioContext.createGain();

    //wir erzeugen eine neue AudioNode Instanz
    var audioInput = audioContext.createMediaStreamSource(stream);
    audioInput.connect(inputPoint);

    //der Audio Recorder kommt von WebAudioRecorder.js
    var audioRecorder = new WebAudioRecorder(inputPoint, {
        workerDir: "app/lib-minified/", // muss mit Slash enden
        encoding: 'ogg' // das Defautl Encoding wäre WAV
    });

    //sobald wir unsere aufnahme beenden wird dieser Callback aufgerufen
    audioRecorder.onComplete = function (recorder, blob) {

        //erzeuge eine URL-codierte Version usneres Blobs, damit wir diesen ummittelbar in den
        //Audio player und Download Button intergrieren können
        var url = (window.URL || window.webkitURL).createObjectURL(blob);

        //setze Daten dem Downlaod Button ein
        var link = document.getElementById("save");
        link.href = url;
        link.download = 'output.ogg';

        //setze die Daten dem Audioplayer ein und zeige diesen an
        var source = document.getElementById("audioPlayer");
        source.style.display = "block";
        source.src = url;

    }
}

Was noch fehlt ist der Toggle der die Aufnahme startet und beendet:

  function toggleRecording(el) {

    //nehmen wir bereits auf, dann stoppr die afunahme
    if (audioRecorder.isRecording()) {

        audioRecorder.finishRecording()

        //ändere färbung des Aufnahmebuttons
        el.classList.remove("btn-danger");

        el.classList.add("btn-primary");
    } else {

        //da wir noch nicht aufnehmen, starte jetzt mit der Aufnahme
        audioRecorder.startRecording();

        //ändere färbung des Aufnahmebuttons
        el.classList.add("btn-danger");

        el.classList.remove("btn-primary");
    }
}

Fazit

Das war tatsächlich schon alles.

Die Einbindung der externen Ressourcen erfoglt nach dem üblichen Schema und beinhaltet keine Besonderheiten, weshalb ich an dieser Stelle auf eine Ausführung verzichten möchte.

Ich war aber mal so frei und habe eine kleine Test-Applikation unter folgender URL zur Verfügung gestellt: AudioRecorder

Die Anwendung funktioniert übrigens sowohl auf dem Desktop als auch auf dem Smartphone, was ja auch das Ziel war.

Wichtig

Alle Arbeiten, die etwas mit den Worker zu tun haben, funktionieren nur entweder in einer lokalen Umgebung (localhost) oder nur wenn eine gesichterte Verbindung (SSL) vorliegt.


#### In diesem Sinne, bis demnächst!