21

One line version:

What open source software (WAMI-Recorder)/web browser (via getUserMedia) will give me the best quality audio recordings?

High quality would be defined as (44.1 or 48 sample rate) and 16 bit sample size.

More Information:

So currently my solution is WAMI-Recorder, but I am wondering if the HTML5 specification has matured to a point in browser such that I can record without Flash and get equal or higher quality audio recordings. Currently it looks like WAMI maxes out at 22050.

I do not need cross browser support as this is for internal business use.

A non-Flash solution would also be preferred.

thatidiotguy
  • 8,701
  • 13
  • 60
  • 105
  • What does open source have to do with the browser API? And... I believe getUserMedia can return whatever the recording device is using by default, which is generally around your specification. – Brad Apr 26 '13 at 21:46
  • @Brad Open source is required as it may be used in an internal commercial capacity and I am specifying that the solution (i.e. WAMI is a flash solution, not inherent to any browser) does not have to be a browser implementation. Do you have any source that shows that getUserMedia records at whatever rate the microphone does? – thatidiotguy Apr 28 '13 at 00:42
  • See here: https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#AudioContext-section – Brad Apr 28 '13 at 00:45
  • @Brad This information is very valuable, but there is no information as to what browsers currently support all features of this specification. My question is directly: What browser (or all) can currently record at the highest fidelity. – thatidiotguy Apr 28 '13 at 00:51
  • 1
    I understand now. I thought you were trying to figure out how to write some code to give you high quality recordings. Sorry, I don't have browser compatibility information on this for you. If I did... it would only be relevant for a couple months. getUserMedia is rapidly changing. Any time I've needed to use it, I've had best luck with Chrome. – Brad Apr 28 '13 at 01:14
  • This might give you an indication of where to look: http://caniuse.com/stream. – likeitlikeit May 03 '13 at 22:34
  • @likeitlikeit What post? I think you forgot to link, but that is exactly the information I am looking for. – thatidiotguy May 04 '13 at 14:52
  • Sorry... This [post](http://lists.w3.org/Archives/Public/public-audio/2013JanMar/0051.html), coming from a Google guy, states that getUserMedia() in Chromium uses the interface's standard sampling rate and depth, which would usually default to CD-Quality 44.1khz/16bit. – likeitlikeit May 04 '13 at 14:56
  • I think the fidelity will depend on the system's preferences. Also, 16bit audio isn't high quality as far as the audio industry is concerned (44.1khz/24 bit is the bare minimum, since 16 bits already introduce noticeable artifacts). – Myst Dec 06 '17 at 04:03
  • Look like similar to https://stackoverflow.com/questions/31426551/get-consistent-audio-quality-with-getusermedia-using-different-browsers?rq=1 – A. STEFANI Dec 06 '17 at 04:20

5 Answers5

11

I found something here. hope it will help you to record audio

<html>
  <body>
    <audio controls autoplay></audio>
    <script type="text/javascript" src="recorder.js"> </script>

    <input onclick="startRecording()" type="button" value="start recording" />
    <input onclick="stopRecording()" type="button" value="stop recording and play" />

    <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>

download the sample https://github.com/rokgregoric/html5record/archive/master.zip

Chamika Sandamal
  • 23,565
  • 5
  • 63
  • 86
  • 11
    This does not answer the question of whether or not this method delivers the highest quality audio recordings possible. Does Chrome only record at a maximum sample rate or size of any sort? That is the information I am looking for. NOT how to record audio in Javascript. – thatidiotguy May 09 '13 at 13:58
2

The WebRTC project is the only available solution for high quality audio that I know of. There is currently a stable version for chrome. I believe firefox is still in the beta stage. As WebRTC matures I am sure all browsers will provide support for this.

http://www.webrtc.org/

Four_lo
  • 1,150
  • 10
  • 28
2

Check this tutorial on how to capture audio and video using HTML5.

http://www.html5rocks.com/en/tutorials/getusermedia/intro/

I don't see any reference about the sample rate or the sample size in the API's, but I would assume that will be possible.

Your major problem could be the implementation status by different browser vendors.

Paulo Fidalgo
  • 21,709
  • 7
  • 99
  • 115
1

Save this files and use

//HTML FILE

    <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>

//JS FILES RECORDER.JS
(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.createJavaScriptNode(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;
}
Ankit Aranya
  • 930
  • 1
  • 10
  • 17
0

If you use Chrome Web Browser, and as you ask for High Quality Audio Recording, you probably be better to build a native client module, you may take a look here (chrome developper native client dev guide):

Selecting a sample frame count for an audio stream involves a tradeoff between latency and CPU usage. If you want your module to have short audio latency so that it can rapidly change what’s playing in the audio stream, you should request a small sample frame count.

[...]

After the module obtains a sample frame count, it can create an audio configuration resource. Currently the Pepper audio API supports audio streams with the configuration settings shown above

And here you will find audio configuration which fit your requirements

The Pepper audio API currently lets Native Client modules play audio streams with the following configurations:

sample rate: 44,100 Hz or 48,000 Hz

bit depth: 16

channels: 2 (stereo)

This official link could be also helpful.

Regards

Community
  • 1
  • 1
A. STEFANI
  • 6,707
  • 1
  • 23
  • 48
  • Similar to https://stackoverflow.com/questions/31426551/get-consistent-audio-quality-with-getusermedia-using-different-browsers?rq=1 – A. STEFANI Dec 06 '17 at 04:19