airtunes2
Version:
an AirTunes v2 implementation: stream wirelessly to audio devices.
67 lines (50 loc) • 1.86 kB
JavaScript
var events = require('events'),
util = require('util'),
config = require('./config.js'),
nu = require('./num_util.js');
function AudioOut() {
events.EventEmitter.call(this);
this.lastSeq = -1;
this.hasAirTunes = false;
}
util.inherits(AudioOut, events.EventEmitter);
AudioOut.prototype.init = function(devices, circularBuffer) {
var self = this;
config.rtp_time_ref = new Date().getTime();
devices.on('airtunes_devices', function(hasAirTunes) {
self.hasAirTunes = hasAirTunes;
});
devices.on('need_sync', function() {
// a sync is forced when a new remote device is added
self.emit('need_sync', self.lastSeq);
});
function sendPacket(seq) {
var packet = circularBuffer.readPacket();
packet.seq = seq;
packet.timestamp = nu.low32(seq*config.frames_per_packet + 2*config.sampling_rate);
if(self.hasAirTunes && seq % config.sync_period == 0)
self.emit('need_sync', seq);
self.emit('packet', packet);
packet.release();
}
function syncAudio() {
/*
* Each time syncAudio() runs, a burst of packet is sent.
* Increasing config.stream_latency lowers CPU usage but increases the size of the burst.
* If the burst size exceeds the UDP windows size (which we do not know), packets are lost.
*/
var elapsed = new Date().getTime() - config.rtp_time_ref;
/*
* currentSeq is the # of the packet we should be sending now. We have some packets to catch-up
* since syncAudio is not always running.
*/
var currentSeq = Math.floor(elapsed*config.sampling_rate/(config.frames_per_packet*1000));
for(var i = self.lastSeq + 1; i <= currentSeq; i++)
sendPacket(i);
self.lastSeq = currentSeq;
// reschedule ourselves later
setTimeout(syncAudio, config.stream_latency);
}
syncAudio();
}
module.exports = AudioOut;