HTML5 merekam audio ke file


123

Yang akhirnya ingin saya lakukan adalah merekam dari mikrofon pengguna, dan mengunggah file ke server setelah selesai. Sejauh ini, saya telah berhasil melakukan streaming ke elemen dengan kode berikut:

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);
}

Bagaimana saya beralih dari itu, ke merekam ke file?


2
danml.com/js/recaudio.js adalah lib file tunggal yang sangat pendek (5kb) yang saya bersihkan dari kode berdasarkan posting blog ini: typedarray.org/wp-content/projects/WebAudioRecorder tidak seperti yang lain yang saya temukan (beberapa ditautkan di sini) penggunaannya cukup sederhana: recorder.start () dan recorder.stop (fnCallbackToCatchWAV_URL)
dandavis

Jawaban:


105

Demo rekaman yang cukup lengkap tersedia di: http://webaudiodemos.appspot.com/AudioRecorder/index.html

Ini memungkinkan Anda untuk merekam audio di browser, kemudian memberi Anda opsi untuk mengekspor dan mengunduh apa yang telah Anda rekam.

Anda dapat melihat sumber halaman itu untuk menemukan tautan ke javascript, tetapi untuk meringkas, ada Recorderobjek yang berisi exportWAVmetode, dan forceDownloadmetode.


3
@Fibericon tidak lagi (: Chrome Stabil sekarang juga (Versi 28.0.1500.71 Mac).
JSmyth

6
Sepertinya tidak berfungsi dengan baik di windows 8, pemutaran audio tidak bersuara. Ada ide?
Mark Murphy

2
Tidak apa-apa saat menguji secara online. Tetapi jika saya menyimpan semua file html (js, png, ...), itu tidak berfungsi secara lokal.
Randy Tang

2
Saya telah menguji demo, ini berfungsi dengan baik di Chrome dan Opera tetapi ada masalah dengan firefox (Mikrofon dikenali tetapi tidak suaranya) .. Dan untuk Safari dan IE, mereka tidak tahu cara menangani kode itu
Tofandel

2
dimana saya bisa mendapatkan kode lengkapnya? Saya mencoba mengekstraknya tetapi tidak berfungsi di server lokal saya (xampp)
gadss

43

Kode yang ditunjukkan di bawah ini adalah hak cipta Matt Diamond dan tersedia untuk digunakan di bawah lisensi MIT. File aslinya ada di sini:

Simpan file ini dan gunakan

(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 Anda memberikan kode unduhan untuk file rekaman audio ini.
Iren Patel

2
@Ankit Araynya ini membantu saya. saya terjebak pada masalah ini sejak 3 hari dengan googling yang berat
Hashir Sheikh

1
saya perlu mengubah nama gumpalan yang menyimpan. karena saya mengirim blob ke server menggunakan ajax dengan data form dan sambil mendapatkan nama file yang memberikan blob. Adakah yang bisa Anda bantu dengan itu.
Jennifer

1
@ Jennifer Anda dapat mengubah nama di sisi server
Yassine Sedrani

1
Saya cenderung menjatuhkan suara negatif di sini hari ini, karena ScriptProcessorNode melakukan pemrosesan pada utas utama, dan akan diblokir oleh komputasi tata letak, GC, dan hal-hal serupa lainnya, yang mengarah ke gangguan bahkan dengan ukuran buffer yang tinggi. Tidak apa-apa dalam demo sederhana yang sudah mati atau sebagai bukti konsep, tetapi tidak dalam aplikasi nyata yang cukup kompleks.
John Weisz

16

Perbarui sekarang Chrome juga mendukung MediaRecorder API dari v47. Hal yang sama untuk dilakukan adalah menggunakannya (menebak metode perekaman asli pasti lebih cepat daripada bekerja sebelumnya), API sangat mudah digunakan, dan Anda akan menemukan banyak jawaban tentang cara mengunggah gumpalan untuk server .

Demo - akan berfungsi di Chrome dan Firefox, sengaja tidak memasukkan blob ke server ...

Sumber Kode


Saat ini, ada tiga cara untuk melakukannya:

  1. sebagai wav[semua kode sisi klien, rekaman tidak terkompresi], Anda dapat memeriksa -> Recorderjs . Masalah: ukuran file cukup besar, bandwidth upload lebih banyak.
  2. sebagai mp3[semua kode sisi klien, rekaman terkompresi], Anda dapat memeriksa -> mp3Recorder . Masalah: secara pribadi, saya menemukan kualitasnya buruk, juga ada masalah perizinan ini.
  3. sebagai kode ogg[klien + server ( node.js), perekaman terkompresi, jam perekaman tak terbatas tanpa crash browser], Anda dapat memeriksa -> recordOpus , baik hanya perekaman sisi klien, atau bundling klien-server, pilihan ada di tangan Anda.

    contoh rekaman ogg (hanya 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.
    }
    

    Demo Fiddle untuk merekam ogg.


1
Chromium "script.js: 33 Uncaught TypeError: navigator.mediaDevices.getUserMedia bukan fungsi"
dikirill

@dikirill Anda harus menggunakan server (bekerja secara lokal), tidak akan bekerja dengan file, juga tidak bekerja pada pekerja (saya sangat pusing dalam hal ini), jika Anda tidak tahu cara membuat server Anda harus memasang chrome.google.com/webstore/detail/web-server-for-chrome/…
John Balvin Arias

jawaban yang sangat baik, saya menemukan skrip Anda mudah dan sederhana. Namun, saya mencoba mengubah tombol mulai untuk melakukan pekerjaan aliran permintaan juga, ada ide? github.com/Mido22/MediaRecorder-sample/issues/6
Edo Edo

13

Ini adalah perekam dan editor suara JavaScript sederhana. Kamu bisa mencobanya.

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

Bisa download dari sini

https://github.com/daaain/JSSoundRecorder


15
Perhatikan bahwa jawaban link-only tidak disarankan, jawaban SO harus menjadi titik akhir pencarian solusi (vs. referensi lain yang berhenti, yang cenderung menjadi basi seiring waktu). Harap pertimbangkan untuk menambahkan sinopsis yang berdiri sendiri di sini, dengan menyimpan tautan sebagai referensi.
kleopatra

1
Secara tepat, tautan pertama yang diberikan adalah mati - masalah perutean ulang subdomain. Tautan yang diperbarui adalah http://www.danieldemmel.me/JSSoundRecorder/ tetapi contohnya tidak berfungsi (Chrome 60) karena situs tersebut tidak mendukung HTTPS. Pergi ke versi aman dan melewati peringatan keamanan memungkinkan demo berfungsi.
brichins

6

Inilah proyek gitHub yang melakukan hal itu.

Ini merekam audio dari browser dalam format mp3, dan secara otomatis menyimpannya ke server web. https://github.com/Audior/Recordmp3js

Anda juga dapat melihat penjelasan rinci tentang penerapannya: http://audior.ec/blog/recording-mp3-using-only-html5-and-javascript-recordmp3-js/


3
Berdasarkan proyek dan artikel itu, saya menulis alat kecil lain yang mengubah kode yang digunakan dan menyempurnakannya agar dapat menggunakan banyak perekam dalam satu halaman. Ini dapat ditemukan di bawah: github.com/icatcher-at/MP3RecorderJS
Vapire

6

Anda dapat menggunakan Recordmp3js dari GitHub untuk memenuhi kebutuhan Anda. Anda dapat merekam dari mikrofon pengguna dan kemudian mendapatkan file sebagai mp3. Terakhir unggah ke server Anda.

Saya menggunakan ini di demo saya. Sudah ada sampel yang tersedia dengan kode sumber oleh penulis di lokasi ini: https://github.com/Audior/Recordmp3js

Demo ada di sini: http://audior.ec/recordmp3js/

Namun saat ini hanya berfungsi di Chrome dan Firefox.

Tampaknya berfungsi dengan baik dan cukup sederhana. Semoga ini membantu.


1
Demo Anda tidak berfungsi di Chromium, konsol menampilkan peringatan: getUserMedia () tidak lagi berfungsi pada sumber yang tidak aman.
dikirill

Coba jalankan di http, melalui localhost atau di server langsung?
Mengatakan

1
getUserMedia()hanya berfungsi pada asal yang aman (https, localhost) sejak Chrome 47
Octavian Naicu

Tautan demo rusak.
Heitor

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.