我想寫的類,將允許我暫停播放音頻文件。該類接受原始PCM數據,並且您向該類提供發送樣本塊的頻率。例如,您可以指定每20毫秒傳遞一個塊。該類還實現了一個暫停()和resume()函數。Node.js掛在多個setTimeout循環調用
該類還使用了我編寫的SerialQueue模塊,以確保數據不會在緩衝區上同時切片和連接。所以你會在下面的代碼中看到很多引用。
我遇到的問題是它會多次調用setTimeout,但最終會在setTimeout上隨機凍結。我的處理器使用率會立即上升,而沒有其他事情發生。
這裏是整個代碼再加上我的測試代碼:
var nopus = require('./lib/node-opus');
var Speaker = require('speaker');
var fs = require('fs');
var path = require('path');
var SerialQueue = require('./lib/serial-queue');
var Transform = require('stream').Transform;
var inherits = require('util').inherits;
function ThrottlePCM(opts) {
// Default to an empty object
if(!opts)
opts = {};
// Pass through the options
Transform.call(this, opts);
this.milliseconds = opts.milliseconds | 20;
this.bitDepth = opts.bitDepth | 16;
this.channels = opts.channels | 1;
this.sampleRate = opts.sampleRate | 48000;
// Set our frame size
if(this.milliseconds==2.5)
this.frameSize = this.sampleRate/400;
else if(this.milliseconds==5)
this.frameSize = this.sampleRate/200;
else if(this.milliseconds==10)
this.frameSize = this.sampleRate/100;
else if(this.milliseconds==20)
this.frameSize = this.sampleRate/50;
else if(this.milliseconds==40)
this.frameSize = this.sampleRate/25;
else if(this.milliseconds==60)
this.frameSize = 3*this.sampleRate/50;
else
throw new Error("Millisecond value is not supported.");
this.bytesPerBeat = this.frameSize*this.bitDepth/8*this.channels;
console.log("Bytes per beat %d.", this.bytesPerBeat);
this.buffer = null;
this.queue = new SerialQueue();
// Taken from TooTallNate
this.totalBytes = 0;
this.startTime = Date.now();
this.pauseTime = null;
// Can we pass
this.canPass = true;
this.paused = false;
this.flushCallback = null;
}
inherits(ThrottlePCM, Transform);
ThrottlePCM.prototype._transform = function(data, encoding, done) {
var that = this;
this.queue.queue(function() {
// Append the buffer
if(that.buffer)
that.buffer = Buffer.concat([ that.buffer, data ]);
else
that.buffer = data;
// Nen no tame
if(that.canPass)
that.passThrough();
});
// We are ready for more data
done();
};
ThrottlePCM.prototype.pause = function() {
this.paused = true;
this.pauseTime = Date.now();
};
ThrottlePCM.prototype.resume = function() {
this.paused = false;
this.startTime+= Date.now()-this.pauseTime;
console.log("Difference is %d: %d", Date.now()-this.pauseTime, this.startTime);
var that = this;
this.queue.queue(function() {
that.passThrough();
});
};
ThrottlePCM.prototype.passThrough = function() {
// Are we paused?
if(this.paused) {
this.canPass = true;
return;
}
// No pass now
this.canPass = false;
// The rest of us
var that = this;
var totalBeats = (Date.now()-this.startTime)/this.milliseconds;
var expected = totalBeats*this.bytesPerBeat;
function passMe() {
console.log("== Inkasemeen");
that.queue.queue(function() {
if(!that.buffer) {
// Should we just flush?
if(that.flushCallback) {
var callback = that.flushCallback;
that.flushCallback = null;
console.log("Antipass");
callback();
}
else
that.canPass = true; // We can pass now from on timer
return;
}
var output;
if(that.buffer.length>that.bytesPerBeat) {
output = that.buffer.slice(0, that.bytesPerBeat);
that.buffer = that.buffer.slice(that.bytesPerBeat);
}
else {
output = that.buffer;
that.buffer = null;
}
that.push(output);
that.totalBytes+= output.length;
// Re-call us
that.passThrough();
});
}
console.log("--\nTotal Beats: %d\nTotal Bytes: %d\nExpected: %d\nBytes Per Beat: %d\nMilliseconds: %s", totalBeats, this.totalBytes, expected, this.bytesPerBeat, this.milliseconds);
if(this.totalBytes>expected) {
var remainder = this.totalBytes-expected;
var sleepTime = remainder/this.bytesPerBeat*this.milliseconds;
console.log("++\nSleep time: %d", sleepTime);
if(sleepTime) {
setTimeout(passMe, sleepTime);
}
else {
passMe();
}
}
else {
console.log("Bytes are higher by %d (%d-%d)", expected-this.totalBytes, expected, this.totalBytes);
passMe();
}
};
ThrottlePCM.prototype._flush = function(done) {
console.log("Flush called.");
// No action here I don't think
this.flushCallback = done;
var that = this;
this.queue.queue(function() {
// Show ourselves flushy
if(that.canPass)
that.passThrough();
});
};
var format = {
channels: 1,
bitDepth: 16,
sampleRate: 48000,
bitrate: 16000,
milliseconds: 60
};
var rate = nopus.getFrameSizeFromMilliseconds*format.channels*nopus.binding.sizeof_opus_int16;
var speaker = new Speaker(format);
var decoder = new nopus.Decoder(format);
var throttle = decoder.pipe(new ThrottlePCM(format));
throttle.pipe(speaker);
var file = fs.createReadStream(path.join(__dirname, 'files/audio/233'));
file.pipe(decoder);
這將產生以下的輸出:
Bytes per beat 5760.
--
Total Beats: 0.1
Total Bytes: 0
Expected: 576
Bytes Per Beat: 5760
Milliseconds: 60
Bytes are higher by 576 (576-0)
== Inkasemeen
--
Total Beats: 0.15
Total Bytes: 1920
Expected: 864
Bytes Per Beat: 5760
Milliseconds: 60
++
Sleep time: 11
== Inkasemeen
--
Total Beats: 0.26666666666666666
Total Bytes: 7680
Expected: 1536
Bytes Per Beat: 5760
Milliseconds: 60
++
Sleep time: 64
== Inkasemeen
--
Total Beats: 1.3666666666666667
Total Bytes: 13440
Expected: 7872
Bytes Per Beat: 5760
Milliseconds: 60
++
Sleep time: 58
== Inkasemeen
--
Total Beats: 2.3833333333333333
Total Bytes: 19200
Expected: 13728
Bytes Per Beat: 5760
Milliseconds: 60
++
Sleep time: 57
== Inkasemeen
--
Total Beats: 3.283333333333333
Total Bytes: 24960
Expected: 18912
Bytes Per Beat: 5760
Milliseconds: 60
++
Sleep time: 63
== Inkasemeen
--
Total Beats: 4.35
Total Bytes: 30720
Expected: 25055.999999999996
Bytes Per Beat: 5760
Milliseconds: 60
++
Sleep time: 59.000000000000036
它掛在各個不同的點。正如你所看到的,它在調用passMe函數並且打印「== Inkasemeen」之前停止了RIGHT。
我的Node.js版本是v0.10.30。
一如既往,非常感謝!