UNPKG

alac

Version:

An Apple Lossless decoder for Aurora.js

231 lines (208 loc) 8.38 kB
// Generated by CoffeeScript 1.7.1 (function() { var ALACDecoder, AV, Aglib, Dplib, Matrixlib, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; AV = require('av'); Aglib = require('./ag_dec'); Dplib = require('./dp_dec'); Matrixlib = require('./matrix_dec'); ALACDecoder = (function(_super) { var ID_CCE, ID_CPE, ID_DSE, ID_END, ID_FIL, ID_LFE, ID_PCE, ID_SCE; __extends(ALACDecoder, _super); function ALACDecoder() { return ALACDecoder.__super__.constructor.apply(this, arguments); } AV.Decoder.register('alac', ALACDecoder); ID_SCE = 0; ID_CPE = 1; ID_CCE = 2; ID_LFE = 3; ID_DSE = 4; ID_PCE = 5; ID_FIL = 6; ID_END = 7; ALACDecoder.prototype.setCookie = function(cookie) { var data, predictorBuffer, _base; data = AV.Stream.fromBuffer(cookie); if (data.peekString(4, 4) === 'frma') { data.advance(12); } if (data.peekString(4, 4) === 'alac') { data.advance(12); } this.config = { frameLength: data.readUInt32(), compatibleVersion: data.readUInt8(), bitDepth: data.readUInt8(), pb: data.readUInt8(), mb: data.readUInt8(), kb: data.readUInt8(), numChannels: data.readUInt8(), maxRun: data.readUInt16(), maxFrameBytes: data.readUInt32(), avgBitRate: data.readUInt32(), sampleRate: data.readUInt32() }; (_base = this.format).bitsPerChannel || (_base.bitsPerChannel = this.config.bitDepth); this.mixBuffers = [new Int32Array(this.config.frameLength), new Int32Array(this.config.frameLength)]; predictorBuffer = new ArrayBuffer(this.config.frameLength * 4); this.predictor = new Int32Array(predictorBuffer); return this.shiftBuffer = new Int16Array(predictorBuffer); }; ALACDecoder.prototype.readChunk = function(data) { var buf, bytesShifted, ch, chanBits, channelIndex, channels, coefs, count, dataByteAlignFlag, denShift, elementInstanceTag, end, escapeFlag, i, j, kb, maxRun, mb, mixBits, mixRes, mode, num, numChannels, out16, output, params, partialFrame, pb, pbFactor, samples, shift, shiftbits, status, table, tag, unused, val, _i, _j, _k, _l, _m, _n, _o, _ref, _ref1, _ref2; if (!this.stream.available(4)) { return; } data = this.bitstream; samples = this.config.frameLength; numChannels = this.config.numChannels; channelIndex = 0; output = new ArrayBuffer(samples * numChannels * this.config.bitDepth / 8); end = false; while (!end) { tag = data.read(3); switch (tag) { case ID_SCE: case ID_LFE: case ID_CPE: channels = tag === ID_CPE ? 2 : 1; if (channelIndex + channels > numChannels) { throw new Error('Too many channels!'); } elementInstanceTag = data.read(4); unused = data.read(12); if (unused !== 0) { throw new Error('Unused part of header does not contain 0, it should'); } partialFrame = data.read(1); bytesShifted = data.read(2); escapeFlag = data.read(1); if (bytesShifted === 3) { throw new Error("Bytes are shifted by 3, they shouldn't be"); } if (partialFrame) { samples = data.read(32); } if (escapeFlag === 0) { shift = bytesShifted * 8; chanBits = this.config.bitDepth - shift + channels - 1; mixBits = data.read(8); mixRes = data.read(8); mode = []; denShift = []; pbFactor = []; num = []; coefs = []; for (ch = _i = 0; _i < channels; ch = _i += 1) { mode[ch] = data.read(4); denShift[ch] = data.read(4); pbFactor[ch] = data.read(3); num[ch] = data.read(5); table = coefs[ch] = new Int16Array(32); for (i = _j = 0, _ref = num[ch]; _j < _ref; i = _j += 1) { table[i] = data.read(16); } } if (bytesShifted) { shiftbits = data.copy(); data.advance(shift * channels * samples); } _ref1 = this.config, mb = _ref1.mb, pb = _ref1.pb, kb = _ref1.kb, maxRun = _ref1.maxRun; for (ch = _k = 0; _k < channels; ch = _k += 1) { params = Aglib.ag_params(mb, (pb * pbFactor[ch]) / 4, kb, samples, samples, maxRun); status = Aglib.dyn_decomp(params, data, this.predictor, samples, chanBits); if (!status) { throw new Error('Error in Aglib.dyn_decomp'); } if (mode[ch] === 0) { Dplib.unpc_block(this.predictor, this.mixBuffers[ch], samples, coefs[ch], num[ch], chanBits, denShift[ch]); } else { Dplib.unpc_block(this.predictor, this.predictor, samples, null, 31, chanBits, 0); Dplib.unpc_block(this.predictor, this.mixBuffers[ch], samples, coefs[ch], num[ch], chanBits, denShift[ch]); } } } else { chanBits = this.config.bitDepth; shift = 32 - chanBits; for (i = _l = 0; _l < samples; i = _l += 1) { for (ch = _m = 0; _m < channels; ch = _m += 1) { val = (data.read(chanBits) << shift) >> shift; this.mixBuffers[ch][i] = val; } } mixBits = mixRes = 0; bytesShifted = 0; } if (bytesShifted) { shift = bytesShifted * 8; for (i = _n = 0, _ref2 = samples * channels; _n < _ref2; i = _n += 1) { this.shiftBuffer[i] = shiftbits.read(shift); } } switch (this.config.bitDepth) { case 16: out16 = new Int16Array(output, channelIndex); if (channels === 2) { Matrixlib.unmix16(this.mixBuffers[0], this.mixBuffers[1], out16, numChannels, samples, mixBits, mixRes); } else { j = 0; buf = this.mixBuffers[0]; for (i = _o = 0; _o < samples; i = _o += 1) { out16[j] = buf[i]; j += numChannels; } } break; default: throw new Error('Only supports 16-bit samples right now'); } channelIndex += channels; break; case ID_CCE: case ID_PCE: throw new Error("Unsupported element: " + tag); break; case ID_DSE: elementInstanceTag = data.read(4); dataByteAlignFlag = data.read(1); count = data.read(8); if (count === 255) { count += data.read(8); } if (dataByteAlignFlag) { data.align(); } data.advance(count * 8); if (!(data.pos < data.length)) { throw new Error('buffer overrun'); } break; case ID_FIL: count = data.read(4); if (count === 15) { count += data.read(8) - 1; } data.advance(count * 8); if (!(data.pos < data.length)) { throw new Error('buffer overrun'); } break; case ID_END: data.align(); end = true; break; default: throw new Error("Unknown element: " + tag); } if (channelIndex > numChannels) { throw new Error('Channel index too large.'); } } return new Int16Array(output); }; return ALACDecoder; })(AV.Decoder); module.exports = ALACDecoder; }).call(this);