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!