diff --git a/README.md b/README.md index 40fa961..fd8ff12 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,18 @@ API The `Decoder` class is a `Stream` subclass that accepts MP3 data written to it, and outputs raw PCM data. It also emits a `"format"` event when the format of -the MP3 file is determined (usually right at the beginning). +the MP3 file is determined (usually right at the beginning). You can specify +the output PCM data format when creating the decoder instance. + +```javascript +var decoder = new lame.Decoder({ + sampleRate: 44100, // [8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000] + channels: 1, // [1(MONO), 2(STEREO), 3(either)] + signed: false, // true || false + float: true, // true || false + bitDepth: 32, // [8, 16, 24, 32] +}); +``` ### Encoder class diff --git a/lib/decoder.js b/lib/decoder.js index 1e5bf69..f7fe9f0 100644 --- a/lib/decoder.js +++ b/lib/decoder.js @@ -24,6 +24,14 @@ var MPG123_DONE = binding.MPG123_DONE; var MPG123_NEW_ID3 = binding.MPG123_NEW_ID3; var MPG123_NEED_MORE = binding.MPG123_NEED_MORE; var MPG123_NEW_FORMAT = binding.MPG123_NEW_FORMAT; +var MPG123_MONO = binding.MPG123_MONO; +var MPG123_STEREO = binding.MPG123_STEREO; +var MPG123_ENC_8 = binding.MPG123_ENC_8; +var MPG123_ENC_16 = binding.MPG123_ENC_16; +var MPG123_ENC_24 = binding.MPG123_ENC_24; +var MPG123_ENC_32 = binding.MPG123_ENC_32; +var MPG123_ENC_SIGNED = binding.MPG123_ENC_SIGNED; +var MPG123_ENC_FLOAT = binding.MPG123_ENC_FLOAT; /** * One-time calls... @@ -62,6 +70,34 @@ function Decoder (opts) { throw new Error('mpg123_open_feed() failed: ' + ret); } debug('created new Decoder instance'); + + if (opts) { + var sampleRate = opts.sampleRate ? + [opts.sampleRate] : binding.mpg123_rates().list; + var channels = opts.channels || (MPG123_MONO | MPG123_STEREO); + var signed = opts.signed ? MPG123_ENC_SIGNED : 0; + var float = opts.float ? MPG123_ENC_FLOAT : 0 + var bitDepth = MPG123_ENC_8 | MPG123_ENC_16 | MPG123_ENC_24 | MPG123_ENC_32; + if (opts.bitDepth) { + bitDepth = binding['MPG123_ENC_' + opts.bitDepth] + } + if (!bitDepth) { + throw new Error('unsupported output format'); + } + + ret = binding.mpg123_format_none(this.mh); + if (MPG123_OK != ret) { + throw new Error('mpg123_format_none() failed: ' + ret); + } + + var encoding = signed | float | bitDepth + sampleRate.forEach(function(rate) { + ret = binding.mpg123_format(this.mh, rate, channels, encoding) + if (MPG123_OK != ret) { + throw new Error('unsupported output format'); + } + }, this) + } } inherits(Decoder, Transform); diff --git a/src/node_mpg123.cc b/src/node_mpg123.cc index 45e3cb4..e38ee1c 100644 --- a/src/node_mpg123.cc +++ b/src/node_mpg123.cc @@ -99,6 +99,38 @@ NAN_METHOD(node_mpg123_open_feed) { } +NAN_METHOD(node_mpg123_rates) { + UNWRAP_MH; + const long *rates; + size_t nrates = 0; + mpg123_rates(&rates, &nrates); + Local o = Nan::New(); + Local list = New(nrates); + for(size_t i = 0; i < nrates; i ++) { + list->Set(i, Nan::New(rates[i])); + } + Nan::Set(o, Nan::New("list").ToLocalChecked(), list); + Nan::Set(o, Nan::New("number").ToLocalChecked(), Nan::New(nrates)); + info.GetReturnValue().Set(o); +} + + +NAN_METHOD(node_mpg123_format_none) { + UNWRAP_MH; + int ret = mpg123_format_none(mh); + info.GetReturnValue().Set(Nan::New(ret)); +} + + +NAN_METHOD(node_mpg123_format) { + UNWRAP_MH; + long rate = Nan::To(info[1]).FromMaybe(0); + int channels = Nan::To(info[2]).FromMaybe(0); + int encodings = Nan::To(info[3]).FromMaybe(0); + int ret = mpg123_format(mh, rate, channels, encodings); + info.GetReturnValue().Set(Nan::New(ret)); +} + NAN_METHOD(node_mpg123_getformat) { UNWRAP_MH; long rate; @@ -489,6 +521,9 @@ void InitMPG123(Handle target) { Nan::SetMethod(target, "mpg123_decoders", node_mpg123_decoders); Nan::SetMethod(target, "mpg123_current_decoder", node_mpg123_current_decoder); Nan::SetMethod(target, "mpg123_supported_decoders", node_mpg123_supported_decoders); + Nan::SetMethod(target, "mpg123_rates", node_mpg123_rates); + Nan::SetMethod(target, "mpg123_format_none", node_mpg123_format_none); + Nan::SetMethod(target, "mpg123_format", node_mpg123_format); Nan::SetMethod(target, "mpg123_getformat", node_mpg123_getformat); Nan::SetMethod(target, "mpg123_safe_buffer", node_mpg123_safe_buffer); Nan::SetMethod(target, "mpg123_outblock", node_mpg123_outblock); diff --git a/test/decoder.js b/test/decoder.js index 8d2112b..7cd7741 100644 --- a/test/decoder.js +++ b/test/decoder.js @@ -44,6 +44,61 @@ describe('Decoder', function () { file.pipe(decoder); }); + it('should set correct output encoding format', function (done) { + var file = fs.createReadStream(filename); + var decoder = new lame.Decoder({ + sampleRate: 44100, + channels: 1, + signed: false, + float: true, + bitDepth: 32, + }); + decoder.on('format', function (format) { + assert(format); + assert.equal(0x200, format.raw_encoding); + assert.equal(44100, format.sampleRate); + assert.equal(1, format.channels); + assert.equal(false, format.signed); + assert.equal(true, format.float); + assert.equal(32, format.bitDepth); + done(); + }); + file.pipe(decoder); + }); + + it('should throw error on unsupported sampleRate', function (done) { + var file = fs.createReadStream(filename); + assert.throws(function () { + var decoder = new lame.Decoder({ + sampleRate: 44200, + }); + file.pipe(decoder); + }, /unsupported output format/) + done(); + }); + + it('should throw error on unsupported channels', function (done) { + var file = fs.createReadStream(filename); + assert.throws(function () { + var decoder = new lame.Decoder({ + channels: 4, + }); + file.pipe(decoder); + }, /unsupported output format/) + done(); + }); + + it('should throw error on unsupported bitDepth', function (done) { + var file = fs.createReadStream(filename); + assert.throws(function () { + var decoder = new lame.Decoder({ + bitDepth: 30, + }); + file.pipe(decoder); + }, /unsupported output format/) + done(); + }); + it('should emit a single "finish" event', function (done) { var file = fs.createReadStream(filename); var output = fs.createWriteStream(outputName);