UNPKG

watson-speech

Version:

IBM Watson Speech to Text and Text to Speech SDK for web browsers.

203 lines (163 loc) 7.56 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: media-element-audio-stream.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: media-element-audio-stream.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>'use strict'; var Readable = require('stream').Readable; var util = require('util'); /** * Turns a MediaStream object (from getUserMedia) into a Node.js Readable stream and converts the audio to Buffers * * @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getUserMedia * * @param {MediaStream|HTMLMediaElement} source - either https://developer.mozilla.org/en-US/docs/Web/API/MediaStream or https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement * @param {Object} [opts] options * @param {Number|null} [opts.bufferSize=null] https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/createScriptProcessor * @param {Boolean} [opts.muteSource=false] - If true, the audio will not be sent back to the source * * // todo: add option for whether to keep or destroy the context * * @constructor */ function MediaElementAudioStream(source, opts) { opts = util._extend({ // "It is recommended for authors to not specify this buffer size and allow the implementation to pick a good // buffer size to balance between latency and audio quality." // https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/createScriptProcessor // Possible values: null, 256, 512, 1024, 2048, 4096, 8192, 16384 // however, webkitAudioContext (safari) requires it to be set bufferSize: (typeof AudioContext != "undefined" ? null : 4096), muteSource: false, autoplay: true, crossOrigin: "anonymous", // required for cross-domain audio playback objectMode: true // true = emit AudioBuffers w/ audio + some metadata, false = emite node.js Buffers (with binary data only }, opts); // We can only emit one channel's worth of audio, so only one input. (Who has multiple microphones anyways?) var inputChannels = 1; // we shouldn't need any output channels (going back to the browser - that's what the gain node is for), but chrome is buggy and won't give us any audio without one var outputChannels = 1; Readable.call(this, opts); var self = this; var recording = true; // I can't find much documentation for this for &lt;audio> elements, but it seems to be required for cross-domain usage (in addition to CORS headers) source.crossOrigin = opts.crossOrigin; /** * Convert and emit the raw audio data * @see https://developer.mozilla.org/en-US/docs/Web/API/ScriptProcessorNode/onaudioprocess * @param {AudioProcessingEvent} e https://developer.mozilla.org/en-US/docs/Web/API/AudioProcessingEvent */ function processAudio(e) { // onaudioprocess can be called at least once after we've stopped if (recording) { // todo: interleave channels in binary mode self.push( opts.objectMode ? e.inputBuffer : new Buffer(e.inputBuffer.getChannelData(0)) ); } } var AudioContext = window.AudioContext || window.webkitAudioContext; // cache the source node &amp; context since it's not possible to recreate it later var context = source.context = source.context || new AudioContext(); var audioInput = source.node = source.node || context.createMediaElementSource(source); var scriptProcessor = context.createScriptProcessor(opts.bufferSize, inputChannels, outputChannels); scriptProcessor.onaudioprocess = processAudio; if (!opts.muteSource) { var gain = context.createGain(); audioInput.connect(gain); gain.connect(context.destination); } /** * Setup script processor to extract audio and also re-connect it via a no-op gain node if desired * * Delayed to avoid processing the stream of silence received before the file begins playing * */ function connect() { audioInput.connect(scriptProcessor); // other half of workaround for chrome bugs scriptProcessor.connect(context.destination); source.removeEventListener("playing", connect); } source.addEventListener("playing", connect); // https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Media_events // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState function start() { source.play(); source.removeEventListener("canplaythrough", start); } if (opts.autoplay) { // play immediately if we have enough data, otherwise wait for the canplaythrough event if(source.readyState === source.HAVE_ENOUGH_DATA) { source.play(); } else { source.addEventListener("canplaythrough", start); } } function end() { recording = false; scriptProcessor.disconnect(); audioInput.disconnect(); //context.close(); // this prevents us from re-using the same audio element until the page is refreshed self.push(null); self.emit('close'); } source.addEventListener("ended", end); this.stop = function() { source.pause(); end(); }; source.addEventListener("error", this.emit.bind(this, 'error')); process.nextTick(function() { // this is more useful for binary mode than object mode, but it won't hurt either way self.emit('format', { channels: 1, bitDepth: 32, sampleRate: context.sampleRate, signed: true, float: true }); }); } util.inherits(MediaElementAudioStream, Readable); MediaElementAudioStream.prototype._read = function(/* bytes */) { // no-op, (back-pressure flow-control doesn't really work on sound) }; /** * Converts a Buffer back into the raw Float32Array format that browsers use. * Note: this is just a new DataView for the same underlying buffer - * the actual audio data is not copied or changed here. * * @param {Buffer} chunk node-style buffer of audio data from a 'data' event or read() call * @return {Float32Array} raw 32-bit float data view of audio data */ MediaElementAudioStream.toRaw = function toFloat32(chunk) { return new Float32Array(chunk.buffer); }; module.exports = MediaElementAudioStream; </code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="FormatStream.html">FormatStream</a></li><li><a href="MediaElementAudioStream.html">MediaElementAudioStream</a></li><li><a href="RecognizeStream.html">RecognizeStream</a></li><li><a href="TimingStream.html">TimingStream</a></li><li><a href="WebAudioL16Stream.html">WebAudioL16Stream</a></li></ul><h3>Events</h3><ul><li><a href="RecognizeStream.html#event:connection-close">connection-close</a></li><li><a href="RecognizeStream.html#event:data">data</a></li><li><a href="RecognizeStream.html#event:error">error</a></li><li><a href="RecognizeStream.html#event:results">results</a></li></ul> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Mon Feb 08 2016 19:56:04 GMT+0000 (UTC) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>