Skip to content

Instantly share code, notes, and snippets.

@nfriedly
Created May 7, 2015 01:08
Show Gist options
  • Save nfriedly/c4549e76440d7e703d43 to your computer and use it in GitHub Desktop.
Save nfriedly/c4549e76440d7e703d43 to your computer and use it in GitHub Desktop.
streaming opus decoder

This is a streaming opus player that doesn't work. It requires nodejs 0.10 due to a issue in the ogg decoder (there's a PR waiting to be merged.)

Output from saving the opus stream to disk and running opusinfo on it is:

$ opusinfo test.opus
Processing file "test.opus"...

New logical stream (#1, serial: c3ebd6b6): type opus
Encoded with Lavf56.18.100
User comments section follows...
	encoder=Lavc56.20.100 libopus
Opus stream 1:
	Pre-skip: 156
	Playback gain: 0 dB
	Channels: 1
	Original sample rate: 24000Hz
	Packet duration:   20.0ms (max),   20.0ms (avg),   20.0ms (min)
	Page duration:   1000.0ms (max),  693.3ms (avg),   80.0ms (min)
	Total data length: 18741 bytes (overhead: 1.77%)
	Playback length: 0m:02.069s
	Average bitrate: 72.45 kb/s, w/o overhead: 71.17 kb/s
Logical stream 1 ended

The file, of course, plays perfectly fine in VLC.

var fs = require('fs');
var watson = require('watson-developer-cloud');
var ogg = require('ogg');
var opus = require('./opus_stream.js');
var wav = require('wav');
var Speaker = require('speaker');
var oggDecoder = new ogg.Decoder();
var text_to_speech = watson.text_to_speech({
username: '0eaef628-d28e-4365-b0db-069046f37fef',
password: 'Mm1DWPHFC7sq',
version: 'v1'
});
var params = {
text: 'Hello from IBM Watson',
accept: 'audio/ogg; codec=opus' // or audio/wav or audio/ogg; codec=opus
};
var stream = text_to_speech.synthesize(params);
//stream.pipe(fs.createWriteStream('./test.opus'));
//var stream = fs.createReadStream('./test.opus');
stream.pipe(oggDecoder);
oggDecoder.on('stream', function (stream) {
stream.on('error', console.error.bind(console, 'oggDecoder stream error'));
var opusDecoder = new opus.Decoder({
channels: 1,
rate: 24000, // 24000Hz
frame_size: 480 // 20ms frame length
});
opusDecoder.on('error', console.error.bind(console, 'opusDecoder error'));
// this doesn't seem to work, the wav reader always complains about the chunk id being "\u0000\u0000\u0000\u0000"]
// so, skip it and pipe directly to the speaker
//var wavReader = new wav.Reader();
//wavReader.on('error', console.error.bind(console, 'wavReader error'));
//wavReader.on('format', function(format) {
// console.log('wav format', format);
// opusDecoder.pipe(new Speaker(format));
//});
//stream.pipe(opusDecoder).pipe(wavReader);
var speaker = new Speaker({
channels: 1, // # channels
bitDepth: 16, // #-bit samples
sampleRate: 24000 // # Hz sample rate
});
speaker.on('error', console.error.bind(console, 'speaker error'));
stream.pipe(opusDecoder).pipe(speaker);
}).on('error', console.error.bind(console, 'oggDecoder error'));
var Opus = require('node-opus');
var util = require('util');
var Transform = require('stream').Transform;
util.inherits(BaseStream, Transform);
util.inherits(Decoder, BaseStream);
util.inherits(Encoder, BaseStream);
function BaseStream(options) {
options = options || {};
options.channels = options.channels || 2;
this._rate = options.rate || 48000;
this._frame_size = options.frame_size || this._rate / 100;
this._codec = new Opus.OpusEncoder( this._rate, options.channels );
Transform.call(this, options);
}
BaseStream.prototype._flush = function(callback) {
callback();
};
function Decoder(options) {
if (!(this instanceof Decoder))
return new Decoder(options);
BaseStream.call(this, options);
}
Decoder.prototype._transform = function(chunk, encoding, done) {
done(null, this._codec.decode(chunk, this._frame_size));
};
function Encoder(options) {
if (!(this instanceof Encoder))
return new Encoder(options);
BaseStream.call(this, options);
}
Encoder.prototype._transform = function(chunk, encoding, done) {
done(null, this._codec.encode(chunk, this._frame_size));
};
exports.Decoder = Decoder;
exports.Encoder = Encoder;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment