HTML5 ghi âm thanh vào tệp


123

Cuối cùng, những gì tôi muốn làm là ghi âm từ micrô của người dùng và tải tệp lên máy chủ khi chúng hoàn tất. Cho đến nay, tôi đã quản lý để tạo luồng tới một phần tử với mã sau:

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

Làm cách nào để chuyển từ đó sang ghi vào tệp?


2
danml.com/js/recaudio.js là một lib tệp đơn (5kb) thực sự ngắn mà tôi đã xóa khỏi mã dựa trên bài đăng trên blog này: typedarray.org/wp-content/projects/WebAudioRecorder không giống như những cái khác mà tôi đã tìm thấy (một số liên kết tại đây) sử dụng khá đơn giản: recorder.start () và recorder.stop (fnCallbackToCatchWAV_URL)
dandavis

Câu trả lời:


105

Có một bản demo ghi âm khá đầy đủ tại: http://webaudiodemos.appspot.com/AudioRecorder/index.html

Nó cho phép bạn ghi lại âm thanh trong trình duyệt, sau đó cung cấp cho bạn tùy chọn xuất và tải xuống những gì bạn đã ghi.

Bạn có thể xem nguồn của trang đó để tìm các liên kết đến javascript, nhưng tóm lại, có một Recorderđối tượng chứa một exportWAVphương thức và một forceDownloadphương thức.


3
@Fibericon không nữa (:. Ổn định Chrome thì quá bây giờ (Version 28.0.1500.71 Mac)
JSmyth

6
Có vẻ như không hoạt động bình thường trên windows 8, âm thanh phát lại không có tiếng. Có ý kiến ​​gì không?
Mark Murphy

2
Nó ổn khi thử nghiệm trực tuyến. Nhưng nếu tôi lưu tất cả các tệp html (js, png, ...), nó không hoạt động cục bộ.
Randy Tang,

2
Tôi đã thử nghiệm bản demo, nó hoạt động tốt trong Chrome và Opera nhưng có vấn đề với firefox (Micrô được nhận dạng nhưng không phát ra âm thanh) .. Và đối với Safari và IE, họ không biết cách xử lý mã đó
Tofandel

2
Tôi có thể có mã hoàn chỉnh ở đâu? Tôi cố gắng để giải nén nó nhưng không làm việc trong máy chủ địa phương của tôi (xampp)
gadss

43

Đoạn mã hiển thị bên dưới là bản quyền của Matt Diamond và có sẵn để sử dụng theo giấy phép của MIT. Các tệp gốc ở đây:

Lưu tệp này và sử dụng

(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 bạn cung cấp mã tải xuống tệp ghi âm này.
Iren Patel

2
@ Cảm ơn Araynya điều này hữu ích cho tôi. tôi đã gặp vấn đề này kể từ 3 ngày với googling nặng
Hashir Sheikh

1
Tôi cần thay đổi tên của blob đang lưu. bởi vì tôi đang gửi blob đến máy chủ bằng cách sử dụng ajax với dữ liệu biểu mẫu và trong khi lấy tên tệp cho blob của nó. Có gì bạn có thể giúp tôi với.
Jennifer

1
@Jennifer, bạn có thể thay đổi tên ở phía máy chủ
Yassine Sedrani

1
Tôi có xu hướng bỏ phiếu phản đối ở đây ngày hôm nay, vì ScriptProcessorNode thực hiện xử lý trên luồng chính và sẽ bị chặn bởi tính toán bố cục, GC và những thứ tương tự khác, dẫn đến trục trặc ngay cả với kích thước bộ đệm cao. Nó tốt trong một bản demo đơn giản chết người hoặc như một bằng chứng về khái niệm, nhưng không phải trong bất kỳ ứng dụng thực tế phức tạp nào.
John Weisz

16

Cập nhật ngay bây giờ Chrome cũng hỗ trợ API MediaRecorder từ v47. Điều tương tự cần làm là sử dụng nó (đoán phương pháp ghi nguyên bản nhất định sẽ nhanh hơn so với cách giải quyết), API thực sự dễ sử dụng và bạn sẽ tìm thấy rất nhiều câu trả lời về cách tải lên một đốm màu cho máy chủ .

Demo - sẽ hoạt động trong Chrome và Firefox, cố tình bỏ qua việc đẩy blob lên máy chủ ...

Nguồn mã


Hiện tại, có ba cách để làm điều đó:

  1. wav[tất cả mã phía máy khách, ghi âm không nén], bạn có thể xem -> Recorderjs . Vấn đề: kích thước tệp khá lớn, cần nhiều băng thông tải lên hơn.
  2. mp3[tất cả mã phía máy khách, ghi âm nén], bạn có thể kiểm tra -> mp3Recorder . Vấn đề: cá nhân, tôi thấy chất lượng không tốt, cũng có vấn đề cấp phép này.
  3. dưới dạng mã ogg[client + server ( node.js), ghi nén, ghi vô hạn giờ mà không bị lỗi trình duyệt], bạn có thể kiểm tra -> recordOpus , chỉ ghi phía client hoặc gói client-server, sự lựa chọn là của bạn.

    Ví dụ ghi ogg (chỉ 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 để ghi âm ogg.


1
Chromium "script.js: 33 Uncaught TypeError: Navigator.mediaDevices.getUserMedia không phải là một chức năng"
dikirill

@dikirill bạn phải đang sử dụng một máy chủ (nó hoạt động cục bộ), nó sẽ không hoạt động với các tệp, cũng như nó không hoạt động trên người lao động (tôi đã rất đau đầu trong việc này), nếu bạn không biết cách tạo một máy chủ cho bạn. nên cài đặt chrome.google.com/webstore/detail/web-server-for-chrome/…
John Balvin Arias

câu trả lời xuất sắc, tôi thấy kịch bản của bạn dễ dàng và đơn giản. tuy nhiên, tôi đã cố gắng thay đổi nút bắt đầu để thực hiện công việc của luồng yêu cầu, có ý kiến ​​gì không? github.com/Mido22/MediaRecorder-sample/issues/6
Edo Edo

13

Đây là một trình ghi và biên tập âm thanh JavaScript đơn giản. Bạn có thể thử nó.

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

Có thể tải xuống từ đây

https://github.com/daaain/JSSoundRecorder


15
Lưu ý rằng các câu trả lời chỉ có liên kết không được khuyến khích, các câu trả lời SO phải là điểm cuối của quá trình tìm kiếm giải pháp (so với một điểm dừng khác của các tham chiếu, có xu hướng cũ dần theo thời gian). Vui lòng xem xét thêm một bản tóm tắt độc lập ở đây, giữ liên kết làm tài liệu tham khảo.
kleopatra

1
Một cách thích hợp, liên kết đầu tiên được cung cấp đã chết - vấn đề định tuyến lại miền phụ. Liên kết được cập nhật là http://www.danieldemmel.me/JSSoundRecorder/ nhưng ví dụ này không hoạt động (Chrome 60) vì trang web không hỗ trợ HTTPS. Mặc dù vậy, chuyển đến phiên bản an toàn và bỏ qua cảnh báo bảo mật cho phép bản demo hoạt động.
brichins

6

Đây là một dự án gitHub thực hiện điều đó.

Nó ghi lại âm thanh từ trình duyệt ở định dạng mp3 và tự động lưu vào máy chủ web. https://github.com/Audior/Recordmp3js

Bạn cũng có thể xem giải thích chi tiết về cách triển khai: http://audior.ec/blog/recording-mp3-using-only-html5-and-javascript-recordmp3-js/


3
Dựa trên dự án và bài báo đó, tôi đã viết một công cụ nhỏ khác cấu trúc lại mã đã sử dụng và nâng cao nó để có thể sử dụng nhiều bộ ghi trên một trang. Nó có thể được tìm thấy dưới: github.com/icatcher-at/MP3RecorderJS
Vapire

6

Bạn có thể sử dụng Recordmp3js từ GitHub để đạt được yêu cầu của mình. Bạn có thể ghi âm từ micrô của người dùng và sau đó nhận tệp dưới dạng mp3. Cuối cùng tải nó lên máy chủ của bạn.

Tôi đã sử dụng cái này trong bản demo của mình. Đã có sẵn một mẫu với mã nguồn của tác giả tại vị trí này: https://github.com/Audior/Recordmp3js

Bản demo ở đây: http://audior.ec/recordmp3js/

Nhưng hiện chỉ hoạt động trên Chrome và Firefox.

Có vẻ hoạt động tốt và khá đơn giản. Hi vọng điêu nay co ich.


1
Bản trình diễn của bạn không hoạt động trong Chromium, bảng điều khiển hiển thị cảnh báo: getUserMedia () không còn hoạt động trên các nguồn không an toàn.
dikirill

Thử chạy nó trên http, qua localhost hoặc trên máy chủ trực tiếp?
Nói

1
getUserMedia()chỉ hoạt động trên nguồn gốc an toàn (https, localhost) kể từ khi Chrome 47
Octavian NAICU

Liên kết demo bị hỏng.
Heitor

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.