HTML5 записуйте аудіо у файл


123

Я хочу в кінцевому рахунку зробити запис із мікрофона користувача та завантажити файл на сервер, коли вони закінчені. Поки мені вдалося зробити потік до елемента із наступним кодом:

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

Як перейти від цього до запису у файл?


2
danml.com/js/recaudio.js - це дійсно короткий однофайловий файл (5kb), який я очищав від коду, виходячи з цього повідомлення в блозі: typedarray.org/wp-content/projects/WebAudioRecorder на відміну від інших, які я знайшов (деякі тут пов'язано) використання досить просте: recorder.start () і recorder.stop (fnCallbackToCatchWAV_URL)
dandavis

Відповіді:


105

Досить повний демонстраційний запис доступний на веб-сайті : http://webaudiodemos.appspot.com/AudioRecorder/index.html

Він дозволяє записувати аудіо в браузері, а потім надає можливість експортувати та завантажувати записане.

Ви можете переглянути джерело цієї сторінки, щоб знайти посилання на javascript, але, підсумовуючи, є Recorderоб'єкт, який містить exportWAVметод та forceDownloadметод.


3
@Fibericon більше не (Стабільний Chrome теж зараз (версія 28.0.1500.71 Mac).
JSmyth

6
Здається, не працює належним чином на Windows 8, відтворення звуку беззвучне. Будь-які ідеї?
Марк Мерфі

2
Це добре при тестуванні в Інтернеті. Але якщо я збережу всі html-файли (js, png, ...), він не працює локально.
Ренді Тан

2
Я тестував демонстраційну версію, він працює добре в Chrome і Opera, але є проблеми з firefox (Мікрофон розпізнається, але не звук). А для Safari та IE вони не знають, як обробляти цей код
Tofandel

2
де я можу отримати повний код? Я спробував витягнути його, але не працював на своєму локальному сервері (xampp)
gadss

43

Код, показаний нижче, захищений авторськими правами на Matt Diamond та доступний для використання під ліцензією MIT. Оригінальні файли тут:

Збережіть ці файли та використовуйте

(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 ви надаєте код для завантаження цього аудіо запису.
Ірен Патель

2
@Ankit Araynya це корисно для мене. я затримався на цій проблемі з 3-х днів із важким
гуглінг

1
Мені потрібно змінити назву краплі, яка економить. тому що я надсилаю blob на сервер, використовуючи ajax з даними форми, і при цьому отримую ім'я файлу, надаючи blob. Чи можете ви мені в цьому допомогти.
Дженніфер

1
@Jennifer ви можете змінити ім'я на стороні сервера
Yassine Sedrani

1
Я схильний сьогодні відпустити низку записів, тому що ScriptProcessorNode здійснює обробку на головному потоці, і буде заблокований обчисленнями компонування, GC та іншими подібними матеріалами, що призводить до збоїв навіть при великих розмірах буфера. Це чудово в мертвій простої демонстрації або як доказ концепції, але не в будь-якому досить складному реальному додатку.
Джон

16

Оновіть зараз Chrome також підтримує API MediaRecorder від v47. Те саме, що потрібно зробити, було б використовувати його (здогадуючись, що власний метод запису повинен бути швидшим, ніж робота навколо), API дуже простий у використанні, і ви знайдете безліч відповідей щодо того, як завантажити блоб на сервер .

Демонстрація - працювала б у Chrome та Firefox, навмисно відмовившись від натискання блобу на сервер ...

Джерело коду


Наразі це три способи:

  1. як wav[весь код клієнта, нестиснений запис], ви можете перевірити -> Recorderjs . Проблема: розмір файлу досить великий, потрібно більше пропускної здатності для завантаження.
  2. як mp3[весь код клієнта, стислий запис], ви можете перевірити -> mp3Recorder . Проблема: особисто я вважаю якість поганою, також є ця проблема з ліцензуванням.
  3. як ogg[ node.jsкод клієнта + сервера ( ), стислий запис, нескінченна кількість годин без збою браузера], ви можете перевірити -> recordOpus , або лише запис на стороні клієнта, або поєднання клієнт-сервер, вибір за вами.

    Приклад запису ogg (лише 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 для запису ogg.


1
Chromium "script.js: 33 Uncaught TypeError: navigator.mediaDevices.getUserMedia не функція"
dikirill

@dikirill ви повинні використовувати сервер (він працює локально), він не працюватиме з файлами, також він не працює на працівників (у мене сильно болить голова в цьому), якщо ви не знаєте, як зробити сервер вам слід встановити chrome.google.com/webstore/detail/web-server-for-chrome/…
Джон Балвін Аріас

відмінна відповідь, я вважаю ваш сценарій легким і простим. однак я намагався змінити кнопку запуску, щоб виконати також роботу потокового запиту, будь-які ідеї? github.com/Mido22/MediaRecorder-sample/isissue/6
Edo Edo

13

Це простий звукозапис і редактор JavaScript. Можна спробувати.

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

Завантажити можна звідси

https://github.com/daaain/JSSoundRecorder


15
Зауважте, що відповіді, що стосуються лише посилань, не відштовхують, відповіді на відповідність повинні бути кінцевою точкою пошуку рішення (порівняно з іншим припиненням посилань, які, як правило, з часом старіють). Просимо додати тут окремий конспект, зберігаючи посилання як орієнтир.
Клеопатра

1
Відповідно, перше надане посилання є мертвим - проблема перенаправлення субдомену. Оновлене посилання http://www.danieldemmel.me/JSSoundRecorder/, але приклад не працює (Chrome 60), оскільки сайт не підтримує HTTPS. Перехід до захищеної версії та обхід попередження про безпеку дозволяє демо-версії працювати.
бричін

6

Ось проект gitHub, який робить саме це.

Він записує аудіо з браузера у форматі mp3, і він автоматично зберігає його на веб-сервері. https://github.com/Audior/Recordmp3js

Ви також можете переглянути детальне пояснення реалізації: http://audior.ec/blog/recording-mp3-using-only-html5-and-javascript-recordmp3-js/


3
На основі цього проекту та статті я написав ще один невеликий інструмент, який відновив використаний код та покращив його, щоб мати змогу використовувати декілька записувачів на одній сторінці. Його можна знайти за адресою
Vapire

6

Ви можете використовувати Recordmp3js від GitHub для досягнення своїх вимог. Ви можете записати з мікрофона користувача, а потім отримати файл у форматі mp3. Нарешті завантажте його на свій сервер.

Я використовував це в демонстрації. У цьому місці вже є зразок із вихідним кодом автора: https://github.com/Audior/Recordmp3js

Демонстраційна версія тут: http://audior.ec/recordmp3js/

Але в даний час працює лише на Chrome і Firefox.

Здається, працює добре і досить просто. Сподіваюся, це допомагає.


1
Ваша демонстрація не працює в Chromium, консоль показує попередження: getUserMedia () більше не працює на незахищеному походження.
dikirill

Спробуйте запустити його на http, через localhost або на прямому сервері?
Сказано

1
getUserMedia()працює лише з безпечним джерелом (https, localhost), оскільки Chrome 47
Octavian Naicu

Демо-посилання розірвано.
Хейтор

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.