watson-speech
Version:
IBM Watson Speech to Text and Text to Speech SDK for web browsers.
219 lines (175 loc) • 7.06 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Source: webaudio-wav-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: webaudio-wav-stream.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>'use strict';
var Transform = require('stream').Transform;
var util = require('util');
function WebAudioWavStream(opts) {
Transform.call(this, opts);
this.sourceSampleRate = 4800;
this.sampleRate = 16000;
this.bufferUnusedSamples = new Float32Array(0);
var self = this;
this.on('pipe', function (src) {
src.on('format', function (format) {
self.sourceSampleRate = format.sampleRate;
});
});
self.writeHeader();
}
util.inherits(WebAudioWavStream, Transform);
/**
* Converts WebAudio to 'audio/l16' (raw wav) and downsamples to 16 kHz.
*
* Explanation for the math: The raw values captured from the Web Audio API are
* in 32-bit Floating Point, between -1 and 1 (per the specification).
* The values for 16-bit PCM range between -32768 and +32767 (16-bit signed integer).
* Multiply to control the volume of the output. We store in little endian.
*
* @param {Object} buffer Microphone/MediaElement audio chunk
* @return {Buffer} 'audio/l16' chunk
* @deprecated This method is deprecated
*/
WebAudioWavStream.prototype._exportDataBufferTo16Khz = function (nodebuffer) {
var bufferNewSamples = new Float32Array(nodebuffer.buffer),
buffer = null,
newSamples = bufferNewSamples.length,
unusedSamples = this.bufferUnusedSamples.length;
if (unusedSamples > 0) {
buffer = new Float32Array(unusedSamples + newSamples);
for (var i = 0; i < unusedSamples; ++i) {
buffer[i] = this.bufferUnusedSamples[i];
}
for (i = 0; i < newSamples; ++i) {
buffer[unusedSamples + i] = bufferNewSamples[i];
}
} else {
buffer = bufferNewSamples;
}
// downsampling variables
var filter = [
-0.037935, -0.00089024, 0.040173, 0.019989, 0.0047792, -0.058675, -0.056487,
-0.0040653, 0.14527, 0.26927, 0.33913, 0.26927, 0.14527, -0.0040653, -0.056487,
-0.058675, 0.0047792, 0.019989, 0.040173, -0.00089024, -0.037935
],
samplingRateRatio = this.sourceSampleRate / 16000,
nOutputSamples = Math.floor((buffer.length - filter.length) / (samplingRateRatio)) + 1,
pcmEncodedBuffer16k = new ArrayBuffer(nOutputSamples * 2),
dataView16k = new DataView(pcmEncodedBuffer16k),
index = 0,
volume = 0x7FFF, //range from 0 to 0x7FFF to control the volume
nOut = 0;
for (i = 0; i + filter.length - 1 < buffer.length; i = Math.round(samplingRateRatio * nOut)) {
var sample = 0;
for (var j = 0; j < filter.length; ++j) {
sample += buffer[i + j] * filter[j];
}
sample *= volume;
try {
dataView16k.setInt16(index, sample, true); // 'true' -> means little endian
} catch (ex) {
// chrome occasionally throws RangeError: Offset is outside the bounds of the DataView
// todo: actually fix it instead of just ignoring the error..
}
index += 2;
nOut++;
}
var indexSampleAfterLastUsed = Math.round(samplingRateRatio * nOut);
var remaining = buffer.length - indexSampleAfterLastUsed;
if (remaining > 0) {
this.bufferUnusedSamples = new Float32Array(remaining);
for (i = 0; i < remaining; ++i) {
this.bufferUnusedSamples[i] = buffer[indexSampleAfterLastUsed + i];
}
} else {
this.bufferUnusedSamples = new Float32Array(0);
}
return new Buffer(dataView16k.buffer);
};
/**
* The max size of the "data" chunk of a WAVE file. This is the max unsigned
* 32-bit int value, minus 100 bytes (overkill, 44 would be safe) for the header.
*
* From https://github.com/TooTallNate/node-wav/blob/master/lib/writer.js
*
* @api private
*/
var MAX_WAV = 4294967295 - 100;
function writeString(view, offset, string) {
for (var i = 0; i < string.length; i++){
view.setUint8(offset + i, string.charCodeAt(i));
}
}
WebAudioWavStream.prototype.writeHeader = function writeHeader() {
var buffer = new ArrayBuffer(44);
var view = new DataView(buffer);
var length = MAX_WAV; // can't use the actuall length because we don't know it yet
var numChannels = 1;
var sampleRate = 16000;
/* RIFF identifier */
writeString(view, 0, 'RIFF');
/* RIFF chunk length */
view.setUint32(4, 36 + length, 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 PCM) */
view.setUint16(20, 1, true);
/* channel count */
view.setUint16(22, numChannels, 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, numChannels * 2, true);
/* bits per sample */
view.setUint16(34, 16, true);
/* data chunk identifier */
writeString(view, 36, 'data');
/* data chunk length */
view.setUint32(40, length, true);
this.push(new Buffer(buffer));
};
WebAudioWavStream.prototype._transform = function (chunk, encoding, next) {
this.push(this._exportDataBufferTo16Khz(chunk));
next();
};
WebAudioWavStream.prototype._flush = function (next) {
// todo: handle anything left in this.bufferUnusedSamples here...
next();
};
module.exports = WebAudioWavStream;
</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></ul><h3>Events</h3><ul><li><a href="MicrophoneStream.html#event:data">data</a></li><li><a href="MicrophoneStream.html#event:raw">raw</a></li><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><h3>Global</h3><ul><li><a href="global.html#MAX_WAV">MAX_WAV</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 16:11:17 GMT+0000 (UTC)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>