Skip to content

Commit 4935889

Browse files
committed
encoder: initial progress on the Encoder class
nothing really working yet...
1 parent 388e9c8 commit 4935889

File tree

3 files changed

+172
-1
lines changed

3 files changed

+172
-1
lines changed

index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,4 @@ exports.Decoder = require('./lib/decoder');
3030
* `ogg_packet`s that you can weld into an ogg.Encoder stream.
3131
*/
3232

33-
//exports.Encoder = require('./lib/encoder');
33+
exports.Encoder = require('./lib/encoder');

lib/encoder.js

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
2+
/**
3+
* Module dependencies.
4+
*/
5+
6+
var debug = require('debug')('vorbis:encoder');
7+
var binding = require('./binding');
8+
var inherits = require('util').inherits;
9+
var Transform = require('stream').Transform;
10+
11+
// node v0.8.x compat
12+
if (!Transform) Transform = require('readable-stream/transform');
13+
14+
/**
15+
* Module exports.
16+
*/
17+
18+
module.exports = Encoder;
19+
20+
/**
21+
* The Vorbis `Encoder` class.
22+
* Accepts PCM audio data and outputs `ogg_packet` Buffer instances.
23+
* Input must be 32-bit float samples. You may specify the number of `channels`
24+
* and the `sampleRate`.
25+
* You may also specify the "quality" which is a float number from -0.1 to 1.0
26+
* (low to high quality). If unspecified, the default is 0.6.
27+
*
28+
* @param {Object} opts PCM audio format options
29+
* @api public
30+
*/
31+
32+
function Encoder (opts) {
33+
if (!(this instanceof Encoder)) return new Encoder(opts);
34+
Transform.call(this, opts);
35+
36+
this._writableState.objectMode = true;
37+
this._writableState.lowWaterMark = 0;
38+
this._writableState.highWaterMark = 0;
39+
40+
// set to `true` after the headerout() call
41+
this._headerWritten = false;
42+
43+
// range from -0.1 to 1.0
44+
this._quality = 0.6;
45+
46+
this.vi = new Buffer(binding.sizeof_vorbis_info);
47+
this.vc = new Buffer(binding.sizeof_vorbis_comment);
48+
binding.vorbis_info_init(this.vi);
49+
binding.vorbis_comment_init(this.vc);
50+
51+
// the `vorbis_dsp_state` and `vorbis_block` stucts get allocated when the
52+
// initial 3 packets are being written
53+
this.vd = null;
54+
this.vb = null;
55+
}
56+
inherits(Encoder, Transform);
57+
58+
/**
59+
* Adds a vorbis comment to the output ogg stream.
60+
* All calls to this function must be made *before* any PCM audio data is written
61+
* to this encoder.
62+
*
63+
* @param {String} tag key name (i.e. "ENCODER")
64+
* @param {String} content value (i.e. "my awesome script")
65+
* @api public
66+
*/
67+
68+
Encoder.prototype.addComment = function (tag, contents) {
69+
if (this.headerWritten) {
70+
throw new Error('Can\'t add comment since "comment packet" has already been output');
71+
} else {
72+
binding.vorbis_comment_add_tag(this.vc, tag, contents);
73+
}
74+
};
75+
76+
/**
77+
* Transform stream callback function.
78+
*
79+
* @api private
80+
*/
81+
82+
Encoder.prototype._transform = function (packet, output, fn) {
83+
debug('_transform(%d bytes)', packet.length);
84+
if (!this._headerWritten) {
85+
var err = this._writeHeader(output);
86+
if (err) return fn(err);
87+
}
88+
};
89+
90+
/**
91+
* Initializes the "analysis" data structures and creates the first 3 Vorbis
92+
* packets to be written to the output ogg stream.
93+
*
94+
* @api private
95+
*/
96+
97+
Encoder.prototype._writeHeader = function (output) {
98+
debug('_writeHeader()');
99+
100+
// encoder init (only VBR currently supported)
101+
var channels = 2;
102+
var sampleRate = 44100;
103+
// TODO: async maybe?
104+
r = binding.vorbis_encode_init_vbr(this.vi, channels, sampleRate, this._quality);
105+
if (0 !== r) {
106+
return new Error(r);
107+
}
108+
109+
// synthesis init
110+
this.vd = new Buffer(binding.sizeof_vorbis_dsp_state);
111+
this.vb = new Buffer(binding.sizeof_vorbis_block);
112+
var r = binding.vorbis_analysis_init(this.vd, this.vi);
113+
if (0 !== r) return new Error(r);
114+
r = binding.vorbis_block_init(this.vd, this.vb);
115+
if (0 !== r) return new Error(r);
116+
117+
// create first 3 packets
118+
// TODO: async
119+
var op_header = new Buffer(binding.sizeof_ogg_packet);
120+
var op_comments = new Buffer(binding.sizeof_ogg_packet);
121+
var op_code = new Buffer(binding.sizeof_ogg_packet);
122+
r = binding.vorbis_analysis_headerout(this.vd, this.vc, op_header, op_comments, op_code);
123+
if (0 !== r) return new Error(r);
124+
125+
output(op_header); // automatically gets placed in its own page
126+
output(op_comments);
127+
output(op_code);
128+
129+
// imply that a page flush() call is required
130+
this.emit('flush');
131+
132+
this._headerWritten = true;
133+
};

src/binding.cc

+38
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "node_pointer.h"
2929
#include "ogg/ogg.h"
3030
#include "vorbis/codec.h"
31+
#include "vorbis/vorbisenc.h"
3132

3233
using namespace v8;
3334
using namespace node;
@@ -60,6 +61,15 @@ Handle<Value> node_vorbis_synthesis_init (const Arguments& args) {
6061
}
6162

6263

64+
Handle<Value> node_vorbis_analysis_init (const Arguments& args) {
65+
HandleScope scope;
66+
vorbis_dsp_state *vd = UnwrapPointer<vorbis_dsp_state *>(args[0]);
67+
vorbis_info *vi = UnwrapPointer<vorbis_info *>(args[1]);
68+
int r = vorbis_analysis_init(vd, vi);
69+
return scope.Close(Integer::New(r));
70+
}
71+
72+
6373
Handle<Value> node_vorbis_block_init (const Arguments& args) {
6474
HandleScope scope;
6575
vorbis_dsp_state *vd = UnwrapPointer<vorbis_dsp_state *>(args[0]);
@@ -69,6 +79,30 @@ Handle<Value> node_vorbis_block_init (const Arguments& args) {
6979
}
7080

7181

82+
Handle<Value> node_vorbis_encode_init_vbr (const Arguments& args) {
83+
HandleScope scope;
84+
vorbis_info *vi = UnwrapPointer<vorbis_info *>(args[0]);
85+
long channels = args[1]->IntegerValue();
86+
long rate = args[2]->IntegerValue();
87+
float quality = args[3]->NumberValue();
88+
int r = vorbis_encode_init_vbr(vi, channels, rate, quality);
89+
return scope.Close(Integer::New(r));
90+
}
91+
92+
93+
/* TODO: async */
94+
Handle<Value> node_vorbis_analysis_headerout (const Arguments& args) {
95+
HandleScope scope;
96+
vorbis_dsp_state *vd = UnwrapPointer<vorbis_dsp_state *>(args[0]);
97+
vorbis_comment *vc = UnwrapPointer<vorbis_comment *>(args[1]);
98+
ogg_packet *op_header = UnwrapPointer<ogg_packet *>(args[2]);
99+
ogg_packet *op_comments = UnwrapPointer<ogg_packet *>(args[3]);
100+
ogg_packet *op_code = UnwrapPointer<ogg_packet *>(args[4]);
101+
int r = vorbis_analysis_headerout(vd, vc, op_header, op_comments, op_code);
102+
return scope.Close(Integer::New(r));
103+
}
104+
105+
72106
Handle<Value> node_comment_array (const Arguments& args) {
73107
HandleScope scope;
74108
int i;
@@ -256,6 +290,7 @@ void Initialize(Handle<Object> target) {
256290
SIZEOF(vorbis_comment);
257291
SIZEOF(vorbis_dsp_state);
258292
SIZEOF(vorbis_block);
293+
SIZEOF(ogg_packet);
259294

260295
/* contants */
261296
#define CONST(value) \
@@ -281,7 +316,10 @@ void Initialize(Handle<Object> target) {
281316
NODE_SET_METHOD(target, "vorbis_info_init", node_vorbis_info_init);
282317
NODE_SET_METHOD(target, "vorbis_comment_init", node_vorbis_comment_init);
283318
NODE_SET_METHOD(target, "vorbis_synthesis_init", node_vorbis_synthesis_init);
319+
NODE_SET_METHOD(target, "vorbis_analysis_init", node_vorbis_analysis_init);
284320
NODE_SET_METHOD(target, "vorbis_block_init", node_vorbis_block_init);
321+
NODE_SET_METHOD(target, "vorbis_encode_init_vbr", node_vorbis_encode_init_vbr);
322+
NODE_SET_METHOD(target, "vorbis_analysis_headerout", node_vorbis_analysis_headerout);
285323
NODE_SET_METHOD(target, "vorbis_synthesis_idheader", node_vorbis_synthesis_idheader);
286324
NODE_SET_METHOD(target, "vorbis_synthesis_headerin", node_vorbis_synthesis_headerin);
287325
NODE_SET_METHOD(target, "vorbis_synthesis", node_vorbis_synthesis);

0 commit comments

Comments
 (0)