HTML5 grabar audio a archivo

123

Lo que finalmente quiero hacer es grabar desde el micrófono del usuario y subir el archivo al servidor cuando haya terminado. Hasta ahora, he logrado hacer una transmisión a un elemento con el siguiente 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);
}

¿Cómo paso de eso a grabar en un archivo?

Fibericon
fuente
2
danml.com/js/recaudio.js es una lib realmente corta de un solo archivo (5kb) que limpié del código basado en esta publicación de blog: typedarray.org/wp-content/projects/WebAudioRecorder a diferencia de los otros que encontré (algunos vinculado aquí) el uso es bastante simple: recorder.start () y recorder.stop (fnCallbackToCatchWAV_URL)
dandavis
1
Desde 2016: stackoverflow.com/questions/34820578/…
Bennett Brown

Respuestas:

105

Hay una demostración de grabación bastante completa disponible en: http://webaudiodemos.appspot.com/AudioRecorder/index.html

Le permite grabar audio en el navegador, luego le da la opción de exportar y descargar lo que ha grabado.

Puede ver la fuente de esa página para encontrar enlaces al javascript, pero para resumir, hay un Recorderobjeto que contiene un exportWAVmétodo y un forceDownloadmétodo.

Brad Montgomery
fuente
3
@Fibericon ya no (: Chrome estable también lo hace ahora (Versión 28.0.1500.71 Mac).
JSmyth
66
No parece funcionar correctamente en Windows 8, la reproducción de audio es silenciosa. ¿Algunas ideas?
Mark Murphy
2
Está bien cuando se prueba en línea. Pero si guardo todos los archivos html (js, png, ...), no funciona localmente.
Randy Tang
2
He probado la demostración, funciona bien en Chrome y Opera, pero hay problemas con firefox (el micrófono es reconocido pero no el sonido). Y para Safari e IE, no saben cómo manejar ese código
Tofandel
2
¿Dónde puedo tener el código completo? Traté de extraerlo pero no funciona en mi servidor local (xampp)
gadss
43

El código que se muestra a continuación tiene derechos de autor de Matt Diamond y está disponible para su uso bajo licencia MIT. Los archivos originales están aquí:

Guarde estos archivos y 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>

Ankit Aranya
fuente
1
@ Ankit Araynya proporciona el código de descarga para este archivo de grabación de audio.
Iren Patel
2
@Ankit Araynya esto es útil para mí.
Me atasqué
1
Necesito cambiar el nombre del blob que se está guardando. porque estoy enviando blob al servidor usando ajax con datos de formulario y al obtener el nombre del archivo está dando blob. ¿Hay algo que me puedan ayudar con eso?
Jennifer
1
@ Jennifer puedes cambiar el nombre en el lado del servidor
Yassine Sedrani
1
Me inclino a dejar un voto negativo aquí hoy, porque ScriptProcessorNode procesa en el hilo principal, y será bloqueado por cálculos de diseño, GC y otras cosas similares, lo que provocará fallas incluso con tamaños de búfer altos. Está bien en una demostración simple o como prueba de concepto, pero no en una aplicación real razonablemente compleja.
John Weisz
16

Actualice ahora Chrome también es compatible con MediaRecorder API desde v47. Lo mismo sería usarlo (adivinar que el método de grabación nativo será más rápido que evitarlo), la API es realmente fácil de usar y encontrará toneladas de respuestas sobre cómo cargar un blob para el servidor .

Demostración : funcionaría en Chrome y Firefox, dejando fuera intencionalmente empujar blob al servidor ...

Código fuente


Actualmente, hay tres formas de hacerlo:

  1. como wav[todo el código del lado del cliente, grabación sin comprimir], puede consultar -> Recorderjs . Problema: el tamaño del archivo es bastante grande, se requiere más ancho de banda de carga.
  2. como mp3[todo el código del lado del cliente, grabación comprimida], puede consultar -> mp3Recorder . Problema: personalmente, encuentro que la calidad es mala, también existe este problema de licencia.
  3. como ogg[ node.jscódigo cliente + servidor ( ), grabación comprimida, horas infinitas de grabación sin bloqueo del navegador], puede consultar -> recordOpus , ya sea solo grabación del lado del cliente o agrupación cliente-servidor, la elección es suya.

    Ejemplo de grabación de ogg (solo 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 grabación ogg.

mido
fuente
1
Chromium "script.js: 33 Error de tipo no capturado: navigator.mediaDevices.getUserMedia no es una función"
dikirill
@dikirill debes estar usando un servidor (funciona localmente), no funcionará con archivos, tampoco funciona en trabajadores (tuve mucho dolor de cabeza en esto), si no sabes cómo hacer un servidor debería instalar chrome.google.com/webstore/detail/web-server-for-chrome/…
John Balvin Arias
Excelente respuesta, encuentro su guión fácil y simple. sin embargo, estaba tratando de cambiar el botón de inicio para hacer el trabajo de flujo de solicitud también, ¿alguna idea? github.com/Mido22/MediaRecorder-sample/issues/6
Edo Edo
13

Este es un simple grabador y editor de sonido JavaScript. Puedes probarlo.

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

Puede descargar desde aquí

https://github.com/daaain/JSSoundRecorder

jhpratt
fuente
15
Tenga en cuenta que se desaconsejan las respuestas de solo enlace , las respuestas SO deben ser el punto final de una búsqueda de una solución (frente a otra escala de referencias, que tienden a quedarse obsoletas con el tiempo). Considere agregar una sinopsis independiente aquí, manteniendo el enlace como referencia.
kleopatra
1
Apropiadamente, el primer enlace proporcionado está muerto: problema de redireccionamiento de subdominio. El enlace actualizado es http://www.danieldemmel.me/JSSoundRecorder/ pero el ejemplo no funciona de todos modos (Chrome 60) porque el sitio no admite HTTPS. Sin embargo, ir a la versión segura y omitir la advertencia de seguridad permite que la demostración funcione.
brichins
6

Aquí hay un proyecto de gitHub que hace exactamente eso.

Graba el audio del navegador en formato mp3 y lo guarda automáticamente en el servidor web. https://github.com/Audior/Recordmp3js

También puede ver una explicación detallada de la implementación: http://audior.ec/blog/recording-mp3-using-only-html5-and-javascript-recordmp3-js/

Remus Negrota
fuente
3
Basado en ese proyecto y artículo, escribí otra pequeña herramienta que refactorizó el código usado y lo mejoré para poder usar múltiples grabadoras en una página. Se puede encontrar en: github.com/icatcher-at/MP3RecorderJS
Vapire
6

Puede usar Recordmp3js de GitHub para cumplir sus requisitos. Puede grabar desde el micrófono del usuario y luego obtener el archivo como mp3. Finalmente cárguelo a su servidor.

Usé esto en mi demo. Ya hay una muestra disponible con el código fuente del autor en esta ubicación: https://github.com/Audior/Recordmp3js

La demostración está aquí: http://audior.ec/recordmp3js/

Pero actualmente solo funciona en Chrome y Firefox.

Parece funcionar bien y bastante simple. Espero que esto ayude.

Adithya Kumaranchath
fuente
1
Su demostración no funciona en Chromium, la consola muestra una advertencia: getUserMedia () ya no funciona en orígenes inseguros.
dikirill
¿Intenta ejecutarlo en http, a través de localhost o en un servidor en vivo?
Sayed
1
getUserMedia()solo funciona en orígenes seguros (https, localhost) desde Chrome 47
Octavian Naicu
El enlace de demostración está roto.
Heitor