Nagrywanie dźwięku w formacie HTML5 do pliku


123

Ostatecznie chcę nagrać z mikrofonu użytkownika i przesłać plik na serwer, gdy skończą. Do tej pory udało mi się stworzyć strumień do elementu o następującym kodzie:

var audio = document.getElementById("audio_preview");

navigator.getUserMedia  = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
navigator.getUserMedia({video: false, audio: true}, function(stream) {
   audio.src = window.URL.createObjectURL(stream);
}, onRecordFail);

var onRecordFail = function (e) {
   console.log(e);
}

Jak mam przejść od tego do nagrywania do pliku?


2
danml.com/js/recaudio.js to naprawdę krótka, jednoplikowa biblioteka (5kb), którą wyczyściłem z kodu na podstawie tego wpisu na blogu: typedarray.org/wp-content/projects/WebAudioRecorder w przeciwieństwie do innych, które znalazłem (niektóre link tutaj) użycie jest dość proste: recorder.start () i recorder.stop (fnCallbackToCatchWAV_URL)
dandavis

Odpowiedzi:


105

Dość kompletne demo nagrania jest dostępne pod adresem : http://webaudiodemos.appspot.com/AudioRecorder/index.html

Umożliwia nagrywanie dźwięku w przeglądarce, a następnie daje możliwość eksportowania i pobierania tego, co nagrałeś.

Możesz wyświetlić źródło tej strony, aby znaleźć linki do javascript, ale podsumowując, istnieje Recorderobiekt, który zawiera exportWAVmetodę i forceDownloadmetodę.


3
@Fibericon już nie ma (: Stabilny Chrome też teraz (wersja 28.0.1500.71 Mac).
JSmyth

6
Wydaje się, że nie działa poprawnie w systemie Windows 8, odtwarzanie dźwięku jest ciche. Jakieś pomysły?
Mark Murphy,

2
Jest w porządku podczas testowania online. Ale jeśli zapiszę wszystkie pliki html (js, png, ...), to nie działa lokalnie.
Randy Tang

2
Testowałem demo, działa dobrze w Chrome i Opera, ale są problemy z Firefoxem (mikrofon jest rozpoznawany, ale nie dźwięk) .. A dla Safari i IE nie wiedzą, jak obsłużyć ten kod
Tofandel

2
gdzie mogę otrzymać pełny kod? Próbowałem go wyodrębnić, ale nie działa na moim serwerze lokalnym (
Xampp

43

Kod pokazany poniżej jest chroniony prawem autorskim Matt Diamond i jest dostępny do użytku na licencji MIT. Oryginalne pliki są tutaj:

Zapisz te pliki i użyj

(function(window){

      var WORKER_PATH = 'recorderWorker.js';
      var Recorder = function(source, cfg){
        var config = cfg || {};
        var bufferLen = config.bufferLen || 4096;
        this.context = source.context;
        this.node = this.context.createScriptProcessor(bufferLen, 2, 2);
        var worker = new Worker(config.workerPath || WORKER_PATH);
        worker.postMessage({
          command: 'init',
          config: {
            sampleRate: this.context.sampleRate
          }
        });
        var recording = false,
          currCallback;

        this.node.onaudioprocess = function(e){
          if (!recording) return;
          worker.postMessage({
            command: 'record',
            buffer: [
              e.inputBuffer.getChannelData(0),
              e.inputBuffer.getChannelData(1)
            ]
          });
        }

        this.configure = function(cfg){
          for (var prop in cfg){
            if (cfg.hasOwnProperty(prop)){
              config[prop] = cfg[prop];
            }
          }
        }

        this.record = function(){
       
          recording = true;
        }

        this.stop = function(){
        
          recording = false;
        }

        this.clear = function(){
          worker.postMessage({ command: 'clear' });
        }

        this.getBuffer = function(cb) {
          currCallback = cb || config.callback;
          worker.postMessage({ command: 'getBuffer' })
        }

        this.exportWAV = function(cb, type){
          currCallback = cb || config.callback;
          type = type || config.type || 'audio/wav';
          if (!currCallback) throw new Error('Callback not set');
          worker.postMessage({
            command: 'exportWAV',
            type: type
          });
        }

        worker.onmessage = function(e){
          var blob = e.data;
          currCallback(blob);
        }

        source.connect(this.node);
        this.node.connect(this.context.destination);    //this should not be necessary
      };

      Recorder.forceDownload = function(blob, filename){
        var url = (window.URL || window.webkitURL).createObjectURL(blob);
        var link = window.document.createElement('a');
        link.href = url;
        link.download = filename || 'output.wav';
        var click = document.createEvent("Event");
        click.initEvent("click", true, true);
        link.dispatchEvent(click);
      }

      window.Recorder = Recorder;

    })(window);

    //ADDITIONAL JS recorderWorker.js
    var recLength = 0,
      recBuffersL = [],
      recBuffersR = [],
      sampleRate;
    this.onmessage = function(e){
      switch(e.data.command){
        case 'init':
          init(e.data.config);
          break;
        case 'record':
          record(e.data.buffer);
          break;
        case 'exportWAV':
          exportWAV(e.data.type);
          break;
        case 'getBuffer':
          getBuffer();
          break;
        case 'clear':
          clear();
          break;
      }
    };

    function init(config){
      sampleRate = config.sampleRate;
    }

    function record(inputBuffer){

      recBuffersL.push(inputBuffer[0]);
      recBuffersR.push(inputBuffer[1]);
      recLength += inputBuffer[0].length;
    }

    function exportWAV(type){
      var bufferL = mergeBuffers(recBuffersL, recLength);
      var bufferR = mergeBuffers(recBuffersR, recLength);
      var interleaved = interleave(bufferL, bufferR);
      var dataview = encodeWAV(interleaved);
      var audioBlob = new Blob([dataview], { type: type });

      this.postMessage(audioBlob);
    }

    function getBuffer() {
      var buffers = [];
      buffers.push( mergeBuffers(recBuffersL, recLength) );
      buffers.push( mergeBuffers(recBuffersR, recLength) );
      this.postMessage(buffers);
    }

    function clear(){
      recLength = 0;
      recBuffersL = [];
      recBuffersR = [];
    }

    function mergeBuffers(recBuffers, recLength){
      var result = new Float32Array(recLength);
      var offset = 0;
      for (var i = 0; i < recBuffers.length; i++){
        result.set(recBuffers[i], offset);
        offset += recBuffers[i].length;
      }
      return result;
    }

    function interleave(inputL, inputR){
      var length = inputL.length + inputR.length;
      var result = new Float32Array(length);

      var index = 0,
        inputIndex = 0;

      while (index < length){
        result[index++] = inputL[inputIndex];
        result[index++] = inputR[inputIndex];
        inputIndex++;
      }
      return result;
    }

    function floatTo16BitPCM(output, offset, input){
      for (var i = 0; i < input.length; i++, offset+=2){
        var s = Math.max(-1, Math.min(1, input[i]));
        output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
      }
    }

    function writeString(view, offset, string){
      for (var i = 0; i < string.length; i++){
        view.setUint8(offset + i, string.charCodeAt(i));
      }
    }

    function encodeWAV(samples){
      var buffer = new ArrayBuffer(44 + samples.length * 2);
      var view = new DataView(buffer);

      /* RIFF identifier */
      writeString(view, 0, 'RIFF');
      /* file length */
      view.setUint32(4, 32 + samples.length * 2, true);
      /* RIFF type */
      writeString(view, 8, 'WAVE');
      /* format chunk identifier */
      writeString(view, 12, 'fmt ');
      /* format chunk length */
      view.setUint32(16, 16, true);
      /* sample format (raw) */
      view.setUint16(20, 1, true);
      /* channel count */
      view.setUint16(22, 2, true);
      /* sample rate */
      view.setUint32(24, sampleRate, true);
      /* byte rate (sample rate * block align) */
      view.setUint32(28, sampleRate * 4, true);
      /* block align (channel count * bytes per sample) */
      view.setUint16(32, 4, true);
      /* bits per sample */
      view.setUint16(34, 16, true);
      /* data chunk identifier */
      writeString(view, 36, 'data');
      /* data chunk length */
      view.setUint32(40, samples.length * 2, true);

      floatTo16BitPCM(view, 44, samples);

      return view;
    }
<html>
    	<body>
    		<audio controls autoplay></audio>
    		<script type="text/javascript" src="recorder.js"> </script>
                    <fieldset><legend>RECORD AUDIO</legend>
    		<input onclick="startRecording()" type="button" value="start recording" />
    		<input onclick="stopRecording()" type="button" value="stop recording and play" />
                    </fieldset>
    		<script>
    			var onFail = function(e) {
    				console.log('Rejected!', e);
    			};

    			var onSuccess = function(s) {
    				var context = new webkitAudioContext();
    				var mediaStreamSource = context.createMediaStreamSource(s);
    				recorder = new Recorder(mediaStreamSource);
    				recorder.record();

    				// audio loopback
    				// mediaStreamSource.connect(context.destination);
    			}

    			window.URL = window.URL || window.webkitURL;
    			navigator.getUserMedia  = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

    			var recorder;
    			var audio = document.querySelector('audio');

    			function startRecording() {
    				if (navigator.getUserMedia) {
    					navigator.getUserMedia({audio: true}, onSuccess, onFail);
    				} else {
    					console.log('navigator.getUserMedia not present');
    				}
    			}

    			function stopRecording() {
    				recorder.stop();
    				recorder.exportWAV(function(s) {
                                
                                 	audio.src = window.URL.createObjectURL(s);
    				});
    			}
    		</script>
    	</body>
    </html>


1
@ Ankit Araynya podajesz kod do pobrania tego pliku nagrania dźwiękowego.
Iren Patel

2
@Ankit Araynya to pomocne dla mnie.
Utknąłem

1
muszę zmienić nazwę zapisywanego obiektu BLOB. ponieważ wysyłam obiekt BLOB do serwera przy użyciu AJAX z danymi formularza i podczas uzyskiwania nazwy pliku jego nadawanie BLOBA. Czy jest coś, co możesz mi w tym pomóc.
Jennifer,

1
@Jennifer, możesz zmienić nazwę po stronie serwera
Yassine Sedrani

1
Jestem skłonny oddać tutaj głos negatywny dzisiaj, ponieważ ScriptProcessorNode wykonuje przetwarzanie w głównym wątku i będzie blokowany przez obliczenia układu, GC i inne podobne rzeczy, co prowadzi do usterek nawet przy dużych rozmiarach buforów. Jest w porządku w martwej prostej wersji demonstracyjnej lub jako dowód słuszności koncepcji, ale nie w żadnej rozsądnie złożonej prawdziwej aplikacji.
John Weisz,

17

Aktualizuj teraz Chrome obsługuje również MediaRecorder API od V47. To samo, co można by zrobić, to użyć go (domyślając się, że natywna metoda nagrywania z pewnością będzie szybsza niż obejście), interfejs API jest naprawdę łatwy w użyciu i można znaleźć mnóstwo odpowiedzi, jak przesłać obiekt blob na serwer .

Demo - działałoby w Chrome i Firefox, celowo pomijając wypychanie obiektu blob na serwer ...

Źródło kodu


Obecnie można to zrobić na trzy sposoby:

  1. jako wav[cały kod po stronie klienta, nieskompresowane nagranie], możesz sprawdzić -> Recorderjs . Problem: rozmiar pliku jest dość duży, wymagana jest większa przepustowość przesyłania.
  2. jako mp3[cały kod po stronie klienta, skompresowane nagranie], możesz sprawdzić -> mp3Recorder . Problem: osobiście uważam, że jakość jest zła, jest też problem z licencją.
  3. jako kod ogg[klient + serwer ( node.js), skompresowane nagranie, nieskończone godziny nagrywania bez awarii przeglądarki], możesz sprawdzić -> recordOpus , albo tylko nagrywanie po stronie klienta, albo pakowanie klient-serwer, wybór należy do Ciebie.

    przykład nagrania ogg (tylko firefox):

    var mediaRecorder = new MediaRecorder(stream);
    mediaRecorder.start();  // to start recording.    
    ...
    mediaRecorder.stop();   // to stop recording.
    mediaRecorder.ondataavailable = function(e) {
        // do something with the data.
    }

    Fiddle Demo do nagrywania ogg.


1
Chromium "script.js: 33 Uncaught TypeError: navigator.mediaDevices.getUserMedia is not a function"
dikirill

@dikirill musisz używać serwera (działa lokalnie), nie będzie działać z plikami, również nie działa na pracownikach (miałem w tym duży ból głowy), jeśli nie wiesz jak zrobić serwer ci powinien zainstalować chrome.google.com/webstore/detail/web-server-for-chrome/…
John Balvin Arias

doskonała odpowiedź, uważam, że twój skrypt jest łatwy i prosty. jednak próbowałem zmienić przycisk Start, aby wykonywać również zadanie strumienia żądań, jakieś pomysły? github.com/Mido22/MediaRecorder-sample/issues/6
Edo Edo

13

To prosty rejestrator i edytor dźwięku JavaScript. Możesz tego spróbować.

https://www.danieldemmel.me/JSSoundRecorder/

Można pobrać stąd

https://github.com/daaain/JSSoundRecorder


15
Zwróć uwagę, że odradza się udzielanie odpowiedzi zawierających tylko łącze , odpowiedzi SO powinny być końcowym punktem poszukiwania rozwiązania (w porównaniu z kolejnym zatrzymaniem odniesień, które z czasem stają się nieaktualne). Proszę rozważyć dodanie tutaj samodzielnego streszczenia, zachowując link jako odniesienie.
kleopatra

1
Odpowiednio, pierwszy podany link jest martwy - problem z przekierowaniem subdomeny. Zaktualizowany link to http://www.danieldemmel.me/JSSoundRecorder/, ale przykład i tak nie działa (Chrome 60), ponieważ witryna nie obsługuje protokołu HTTPS. Przejście do bezpiecznej wersji i ominięcie ostrzeżenia o zabezpieczeniach pozwala jednak na działanie wersji demonstracyjnej.
brichins

6

Oto projekt gitHub, który właśnie to robi.

Nagrywa dźwięk z przeglądarki w formacie mp3 i automatycznie zapisuje go na serwerze internetowym. https://github.com/Audior/Recordmp3js

Możesz również wyświetlić szczegółowe wyjaśnienie implementacji: http://audior.ec/blog/recording-mp3-using-only-html5-and-javascript-recordmp3-js/


3
W oparciu o ten projekt i artykuł napisałem kolejne małe narzędzie, które refaktoryzowało użyty kod i ulepszyło go, aby móc używać wielu rejestratorów na jednej stronie. Można go znaleźć pod adresem
Vapire

6

Możesz użyć Recordmp3js z GitHub, aby spełnić swoje wymagania. Możesz nagrywać z mikrofonu użytkownika, a następnie pobrać plik jako mp3. Na koniec prześlij go na swój serwer.

Użyłem tego w moim demo. W tej lokalizacji dostępna jest już próbka z kodem źródłowym autora: https://github.com/Audior/Recordmp3js

Demo jest tutaj: http://audior.ec/recordmp3js/

Ale obecnie działa tylko w Chrome i Firefox.

Wydaje się, że działa dobrze i całkiem prosto. Mam nadzieję że to pomoże.


1
Twoje demo nie działa w Chromium, konsola wyświetla ostrzeżenie: getUserMedia () nie działa już na niezabezpieczonych źródłach.
dikirill

Spróbuj uruchomić go na http, przez localhost lub na serwerze rzeczywistym?
Powiedział

1
getUserMedia()działa tylko na bezpiecznych źródłach (https, localhost) od Chrome 47
Octavian Naicu,

Link do wersji demonstracyjnej jest uszkodzony.
Heitor

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.