HTML5 grava áudio em arquivo


123

Em última análise, o que eu quero fazer é gravar no microfone do usuário e carregar o arquivo no servidor quando terminar. Até agora, eu consegui fazer um fluxo para um elemento com o seguinte código:

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

Como passo disso, para gravar em um arquivo?


2
danml.com/js/recaudio.js é uma lib de arquivo único muito curta (5kb) que limpei do código com base nesta postagem do blog: typedarray.org/wp-content/projects/WebAudioRecorder, diferente dos outros que encontrei (alguns ligada aqui) o uso é bastante simples: recorder.start () e recorder.stop (fnCallbackToCatchWAV_URL)
dandavis

Respostas:


105

Há uma demonstração de gravação bastante completa disponível em: http://webaudiodemos.appspot.com/AudioRecorder/index.html

Ele permite gravar áudio no navegador e oferece a opção de exportar e baixar o que você gravou.

Você pode ver a fonte dessa página para encontrar links para o javascript, mas para resumir, há um Recorderobjeto que contém um exportWAVmétodo e um forceDownloadmétodo.


3
@Fibericon não é mais (: Stable Chrome does too now (Version 28.0.1500.71 Mac). #
JSmyth 16/13/13

6
Não parece funcionar corretamente no Windows 8, a reprodução de áudio é silenciosa. Alguma ideia?
Mark Murphy

2
É bom ao testar online. Mas se eu salvar todos os arquivos html (js, png, ...), ele não funcionará localmente.
Randy Tang

2
Eu testei a demo, ela funciona bem no Chrome e no Opera, mas há problemas com o Firefox (o microfone é reconhecido, mas não o som). E para o Safari e o IE, eles não sabem como lidar com esse código
Tofandel

2
onde posso obter o código completo? Tentei para extraí-lo, mas não funciona no meu servidor local (xampp)
gadss

43

O código mostrado abaixo é protegido por Matt Diamond e está disponível para uso sob licença MIT. Os arquivos originais estão aqui:

Salve esses arquivos e use

(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, você fornece o código de download para este arquivo de gravação de áudio.
precisa saber é o seguinte

2
@Ankit Araynya isso é útil para mim. i preso sobre este problema desde 3 dias com pesado googling
Hashir Sheikh

1
Estou precisando alterar o nome do blob que está salvando. porque estou enviando blob para o servidor usando ajax com dados do formulário e ao obter o nome do arquivo está dando blob. Existe algo que você possa me ajudar com isso.
Jennifer

1
@Jennifer você pode alterar o nome no lado do servidor
Yassine Sedrani

1
Estou inclinado a fazer uma votação aqui hoje, porque ScriptProcessorNode processa no thread principal e será bloqueado pelos cálculos de layout, GC e outras coisas semelhantes, levando a falhas mesmo com tamanhos de buffer altos. É bom em uma demonstração simples ou como uma prova de conceito, mas não em qualquer aplicativo real razoavelmente complexo.
John Weisz

16

Atualização agora Chrome também suporta MediaRecorder API de V47. A mesma coisa a ser feita seria usá-lo (supondo que o método de gravação nativo seja mais rápido do que o trabalho alternativo), a API é realmente fácil de usar e você encontrará muitas respostas sobre como fazer upload de um blob para o servidor .

Demo - funcionaria no Chrome e no Firefox, intencionalmente deixado de fora empurrando blob para o servidor ...

Código Fonte


Atualmente, existem três maneiras de fazer isso:

  1. como wav[todo o código do cliente, gravação não compactada], você pode conferir -> Recorderjs . Problema: o tamanho do arquivo é muito grande, é necessária mais largura de banda de upload.
  2. como mp3[todo o código do cliente, gravação compactada], você pode conferir -> mp3Recorder . Problema: pessoalmente, acho a qualidade ruim, também existe esse problema de licenciamento.
  3. como ogg[ node.jscódigo cliente + servidor ( ), gravação compactada, horas infinitas de gravação sem travamento do navegador], você pode conferir -> recordOpus , apenas gravação no lado do cliente ou agrupamento de servidor e cliente, a escolha é sua.

    exemplo de gravação ogg (somente 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 para gravação de ogg.


1
Chromium "script.js: 33 TypeError não capturado: navigator.mediaDevices.getUserMedia não é uma função"
dikirill 27/07/16

@dikirill você deve estar usando um servidor (funciona localmente), não funciona com arquivos, também não funciona com os trabalhadores (tive muita dor de cabeça nisso), se você não sabe como criar um servidor, deve instalar chrome.google.com/webstore/detail/web-server-for-chrome/…
John Balvin Arias

excelente resposta, acho seu script fácil e simples. no entanto, eu estava tentando alterar o botão Iniciar para fazer o trabalho de fluxo de solicitações também, alguma idéia? github.com/Mido22/MediaRecorder-sample/issues/6
Edo Edo

13

Este é um simples gravador e editor de som JavaScript. Você pode experimentá-lo.

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

Pode baixar aqui

https://github.com/daaain/JSSoundRecorder


15
Observe que as respostas somente para links são desencorajadas; as respostas SO devem ser o ponto final de uma pesquisa por uma solução (em comparação com outra parada de referências, que tendem a ficar obsoletas ao longo do tempo). Considere adicionar aqui uma sinopse independente, mantendo o link como referência.
precisa

1
Apropriadamente, o primeiro link fornecido é um problema de redirecionamento de subdomínio morto. O link atualizado é http://www.danieldemmel.me/JSSoundRecorder/, mas o exemplo não funciona de maneira alguma (Chrome 60) porque o site não oferece suporte a HTTPS. Ir para a versão segura e ignorar o aviso de segurança permite que a demonstração funcione.
brichins 12/09

6

Aqui está um projeto do gitHub que faz exatamente isso.

Ele grava o áudio do navegador no formato mp3 e o salva automaticamente no servidor da web. https://github.com/Audior/Recordmp3js

Você também pode ver uma explicação detalhada da implementação: http://audior.ec/blog/recording-mp3-using-only-html5-and-javascript-recordmp3-js/


3
Com base nesse projeto e artigo, escrevi outra pequena ferramenta que refatorou o código usado e o aprimorou para poder usar vários gravadores em uma página. Pode ser encontrado em: github.com/icatcher-at/MP3RecorderJS
Vapire

6

Você pode usar o Recordmp3js do GitHub para atingir seus requisitos. Você pode gravar no microfone do usuário e obter o arquivo como um mp3. Finalmente faça o upload para o seu servidor.

Eu usei isso na minha demonstração. Já existe uma amostra disponível com o código-fonte do autor neste local: https://github.com/Audior/Recordmp3js

A demo está aqui: http://audior.ec/recordmp3js/

Mas atualmente funciona apenas no Chrome e Firefox.

Parece funcionar bem e bem simples. Espero que isto ajude.


1
Sua demonstração não funciona no Chromium, o console mostra um aviso: getUserMedia () não funciona mais em origens inseguras.
dikirill

Tente executá-lo em http, via host local ou em um servidor ativo?
Disse

1
getUserMedia()só funciona em origens seguras (https, localhost) desde Chrome 47
Octavian Naicu

O link de demonstração está quebrado.
Heitor

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.