in encoder.js: Recorder.prototype.initAudioContext = function( sourceNode ){ if (sourceNode && sourceNode.context) { this.audioContext = sourceNode.context;
-
** if(!this.analyser){this.analyser = this.audioContext.createAnalyser();} this.closeAudioContext = false; }
else { this.audioContext = new AudioContext();
-
** this.analyser = this.audioContext.createAnalyser(); this.closeAudioContext = true; }
return this.audioContext; };
in encoderWorker.js:
importScripts('encoderWorkerBase64.js'); function _base64ToArrayBuffer(base64) { var binary_string = atob(base64); var len = binary_string.length; var bytes = new Uint8Array( len ); for (var i = 0; i < len; i++) { bytes[i] = binary_string.charCodeAt(i); } return bytes.buffer; } var wasmArrayBuffData = _base64ToArrayBuffer(wasmBase64); ...... ...... ...... function doNativeWasm(global, env, providedBuffer) { if (typeof WebAssembly !== 'object') { err('no native wasm support detected'); return false; } // prepare memory import if (!(Module['wasmMemory'] instanceof WebAssembly.Memory)) { err('no native wasm Memory in use'); return false; } env['memory'] = Module['wasmMemory']; // Load the wasm module and create an instance of using native support in the JS engine. info['global'] = { 'NaN': NaN, 'Infinity': Infinity }; info['global.Math'] = Math; info['env'] = env; // handle a generated wasm instance, receiving its exports and // performing other necessary setup function receiveInstance(instance, module) { exports = instance.exports; if (exports.memory) mergeMemory(exports.memory); Module['asm'] = exports; Module["usingWasm"] = true; removeRunDependency('wasm-instantiate'); } addRunDependency('wasm-instantiate');
// User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback // to manually instantiate the Wasm module themselves. This allows pages to run the instantiation parallel // to any other async startup actions they are performing. if (Module['instantiateWasm']) { try { return Module['instantiateWasm'](info, receiveInstance); } catch(e) { err('Module.instantiateWasm callback failed with error: ' + e); return false; } }
function receiveInstantiatedSource(output) { // 'output' is a WebAssemblyInstantiatedSource object which has both the module and instance. // receiveInstance() will swap in the exports (to Module.asm) so they can be called receiveInstance(output['instance'], output['module']); }
WebAssembly.instantiate(wasmArrayBuffData,info).then( receiveInstantiatedSource, function(reason) { console.error('failed to asynchronously prepare wasm: ' + reason); abort(reason); } );
return {}; // no exports yet; we'll fill them in later }
- Libopus: v1.3 compiled with emscripten 1.38.21
- speexDSP: 1.2RC3 compiled with emscripten 1.38.21
The required files are in the dist
folder. Unminified sources are in dist-unminified
.
Examples for recording, encoding, and decoding are in examples
folder.
The Recorder
object is available in the global namespace and supports CommonJS and AMD imports.
var rec = new Recorder([config]);
Creates a recorder instance.
- config - An optional configuration object.
- bufferLength - (optional) The length of the buffer that the internal JavaScriptNode uses to capture the audio. Can be tweaked if experiencing performance issues. Defaults to
4096
. - encoderPath - (optional) Path to
encoderWorker.min.js
orwaveWorker.min.js
worker script. Defaults toencoderWorker.min.js
- mediaTrackConstraints - (optional) Object to specify media track constraints. Defaults to
true
. - monitorGain - (optional) Sets the gain of the monitoring output. Gain is an a-weighted value between
0
and1
. Defaults to0
- numberOfChannels - (optional) The number of channels to record.
1
= mono,2
= stereo. Defaults to1
. Maximum2
channels are supported. - recordingGain - (optional) Sets the gain of the recording input. Gain is an a-weighted value between
0
and1
. Defaults to1
- encoderApplication - (optional) Supported values are:
2048
- Voice,2049
- Full Band Audio,2051
- Restricted Low Delay. Defaults to2049
. - encoderBitRate - (optional) Target bitrate in bits/sec. The encoder selects an application-specific default when this is not specified.
- encoderComplexity - (optional) Value between 0 and 10 which determines latency and processing for encoding.
0
is fastest with lowest complexity.10
is slowest with highest complexity. The encoder selects a default when this is not specified. - encoderFrameSize - (optional) Specifies the frame size in ms used for encoding. Defaults to
20
. - encoderSampleRate - (optional) Specifies the sample rate to encode at. Defaults to
48000
. Supported values are8000
,12000
,16000
,24000
or48000
. - maxFramesPerPage - (optional) Maximum number of frames to collect before generating an Ogg page. This can be used to lower the streaming latency. The lower the value the more overhead the ogg stream will incur. Defaults to
40
. - originalSampleRateOverride - (optional) Override the ogg opus 'input sample rate' field. Google Speech API requires this field to be
16000
. - resampleQuality - (optional) Value between 0 and 10 which determines latency and processing for resampling.
0
is fastest with lowest quality.10
is slowest with highest quality. Defaults to3
. - streamPages - (optional)
dataAvailable
event will fire after each encoded page. Defaults tofalse
. - reuseWorker - (optional) If true, the worker is not automatically destroyed when
stop
is called. Instead, it is reused for subsequentstart
calls and must be explicitly destroyed after stopping by callingdestroyWorker
. Defaults tofalse
.
- wavBitDepth - (optional) Desired bit depth of the WAV file. Defaults to
16
. Supported values are8
,16
,24
and32
bits per sample.
rec.pause([flush])
pause will keep the stream and monitoring alive, but will not be recording the buffers. If flush
is true
and streamPages
is set, any pending encoded frames of data will be flushed, and it will return a promise that only resolves after the frames have been flushed to ondataavailable
. Will call the onpause
callback when paused. Subsequent calls to resume will add to the current recording.
rec.resume()
resume will resume the recording if paused. Will call the onresume
callback when recording is resumed.
rec.setRecordingGain( gain )
setRecordingGain will set the volume on what will be passed to the recorder. Gain is an a-weighted value between 0
and 1
.
rec.setMonitorGain( gain )
setMonitorGain will set the volume on what will be passed to the monitor. Monitor level does not affect the recording volume. Gain is an a-weighted value between 0
and 1
.
rec.start( [sourceNode] )
start Initalizes the worker, audio context, and an audio stream and begin capturing audio. Returns a promise which resolves when recording is started. Will callback onstart
when started. Optionally accepts a source node which can be used in place of initializing the microphone stream. For iOS support, start
needs to be initiated from a user action. If a sourceNode is provided, then the stream and audioContext will need to be managed by the implementation.
rec.stop()
stop will cease capturing audio and disable the monitoring and mic input stream. Will request the recorded data and then terminate the worker once the final data has been published. Will call the onstop
callback when stopped.
rec.destroyWorker()
destroyWorker will destroy the worker freeing up the browser resources. If the recorder is re-started, a new worker will be created. Note that destroyWorker
is automatically called when stopping unless reuseWorker
is true.
rec.loadWorker()
loadWorker triggers pre-loading of the worker. This can reduce the startup latency when calling start
. Call destroyWorker
to clean the worker when the recorder is stopped/not started, or it will be automatically cleaned up after stopping unless reuseWorker
is true.
rec.encodedSamplePosition
Reads the currently encoded sample position (the number of samples up to and including the most recent data provided to ondataavailable
). For Opus, the encoded sample rate is always 48kHz, so a time position can be determined by dividing by 48000.
Recorder.isRecordingSupported()
Returns a truthy value indicating if the browser supports recording.
rec.ondataavailable( arrayBuffer )
A callback which returns an array buffer of audio data. If streamPages
is true
, this will be called with each page of encoded audio. If streamPages
is false
, this will be called when the recording is finished with the complete data.
rec.onpause()
A callback which occurs when media recording is paused.
rec.onresume()
A callback which occurs when media recording resumes after being paused.
rec.onstart()
A callback which occurs when media recording starts.
rec.onstop()
A callback which occurs when media recording ends.
- To be able to read the mic stream, the page must be served over https
- iOS Safari requires
rec.start()
to be called from a user initiated event - macOS Safari v11 native opus playback is not yet supported
- iOS Safari v11 native opus playback is not yet supported
- Microsoft Edge native opus playback is not yet supported
Supported:
- Chrome v58
- Firefox v53
- Microsoft Edge v41
- Opera v44
- macOS Safari v11
- iOS Safari v11
Unsupported:
- IE 11 and below
- iOS 11 Chrome
- iOS 11.2.2 and iOS 11.2.5 are not working due to a regression in WebAssembly: https://bugs.webkit.org/show_bug.cgi?id=181781
- Firefox does not support sample rates above 48000Hz: https://bugzilla.mozilla.org/show_bug.cgi?id=1124981
- macOS Safari v11 does not support sample rates above 44100Hz
Prebuilt sources are included in the dist folder. However below are instructions if you want to build them yourself. Opus and speex are compiled without SIMD optimizations. Performace is significantly worse with SIMD optimizations enabled.
Mac: Install autotools using MacPorts
port install automake autoconf libtool pkgconfig
Windows: Install autotools using MSYS2
pacman -S make autoconf automake libtool pkgconfig
Install npm dependencies:
npm install
checkout, compile and create the dist from sources:
npm run make
Running the unit tests:
npm test
Clean the dist folder and git submodules:
make clean