aac
Version:
An AAC decoder for Aurora.js
335 lines (279 loc) • 13.1 kB
JavaScript
/*
* AAC.js - Advanced Audio Coding decoder in JavaScript
* Created by Devon Govett
* Copyright (c) 2012, Official.fm Labs
*
* AAC.js is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* AAC.js is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
* Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* If not, see <http://www.gnu.org/licenses/>.
*/
var tables = require('./tables');
var Huffman = require('./huffman');
var TNS = require('./tns');
// Individual Channel Stream
function ICStream(config) {
this.info = new ICSInfo();
this.bandTypes = new Int32Array(MAX_SECTIONS);
this.sectEnd = new Int32Array(MAX_SECTIONS);
this.data = new Float32Array(config.frameLength);
this.scaleFactors = new Float32Array(MAX_SECTIONS);
this.randomState = 0x1F2E3D4C;
this.tns = new TNS(config);
this.specBuf = new Int32Array(4);
}
ICStream.ZERO_BT = 0; // Scalefactors and spectral data are all zero.
ICStream.FIRST_PAIR_BT = 5; // This and later band types encode two values (rather than four) with one code word.
ICStream.ESC_BT = 11; // Spectral data are coded with an escape sequence.
ICStream.NOISE_BT = 13; // Spectral data are scaled white noise not coded in the bitstream.
ICStream.INTENSITY_BT2 = 14; // Scalefactor data are intensity stereo positions.
ICStream.INTENSITY_BT = 15; // Scalefactor data are intensity stereo positions.
ICStream.ONLY_LONG_SEQUENCE = 0;
ICStream.LONG_START_SEQUENCE = 1;
ICStream.EIGHT_SHORT_SEQUENCE = 2;
ICStream.LONG_STOP_SEQUENCE = 3;
const MAX_SECTIONS = 120,
MAX_WINDOW_GROUP_COUNT = 8;
const SF_DELTA = 60,
SF_OFFSET = 200;
ICStream.prototype = {
decode: function(stream, config, commonWindow) {
this.globalGain = stream.read(8);
if (!commonWindow)
this.info.decode(stream, config, commonWindow);
this.decodeBandTypes(stream, config);
this.decodeScaleFactors(stream);
if (this.pulsePresent = stream.read(1)) {
if (this.info.windowSequence === ICStream.EIGHT_SHORT_SEQUENCE)
throw new Error("Pulse tool not allowed in eight short sequence.");
this.decodePulseData(stream);
}
if (this.tnsPresent = stream.read(1)) {
this.tns.decode(stream, this.info);
}
if (this.gainPresent = stream.read(1)) {
throw new Error("TODO: decode gain control/SSR");
}
this.decodeSpectralData(stream);
},
decodeBandTypes: function(stream, config) {
var bits = this.info.windowSequence === ICStream.EIGHT_SHORT_SEQUENCE ? 3 : 5,
groupCount = this.info.groupCount,
maxSFB = this.info.maxSFB,
bandTypes = this.bandTypes,
sectEnd = this.sectEnd,
idx = 0,
escape = (1 << bits) - 1;
for (var g = 0; g < groupCount; g++) {
var k = 0;
while (k < maxSFB) {
var end = k,
bandType = stream.read(4);
if (bandType === 12)
throw new Error("Invalid band type: 12");
var incr;
while ((incr = stream.read(bits)) === escape)
end += incr;
end += incr;
if (end > maxSFB)
throw new Error("Too many bands (" + end + " > " + maxSFB + ")");
for (; k < end; k++) {
bandTypes[idx] = bandType;
sectEnd[idx++] = end;
}
}
}
},
decodeScaleFactors: function(stream) {
var groupCount = this.info.groupCount,
maxSFB = this.info.maxSFB,
offset = [this.globalGain, this.globalGain - 90, 0], // spectrum, noise, intensity
idx = 0,
noiseFlag = true,
scaleFactors = this.scaleFactors,
sectEnd = this.sectEnd,
bandTypes = this.bandTypes;
for (var g = 0; g < groupCount; g++) {
for (var i = 0; i < maxSFB;) {
var runEnd = sectEnd[idx];
switch (bandTypes[idx]) {
case ICStream.ZERO_BT:
for (; i < runEnd; i++, idx++) {
scaleFactors[idx] = 0;
}
break;
case ICStream.INTENSITY_BT:
case ICStream.INTENSITY_BT2:
for(; i < runEnd; i++, idx++) {
offset[2] += Huffman.decodeScaleFactor(stream) - SF_DELTA;
var tmp = Math.min(Math.max(offset[2], -155), 100);
scaleFactors[idx] = tables.SCALEFACTOR_TABLE[-tmp + SF_OFFSET];
}
break;
case ICStream.NOISE_BT:
for(; i < runEnd; i++, idx++) {
if (noiseFlag) {
offset[1] += stream.read(9) - 256;
noiseFlag = false;
} else {
offset[1] += Huffman.decodeScaleFactor(stream) - SF_DELTA;
}
var tmp = Math.min(Math.max(offset[1], -100), 155);
scaleFactors[idx] = -tables.SCALEFACTOR_TABLE[tmp + SF_OFFSET];
}
break;
default:
for(; i < runEnd; i++, idx++) {
offset[0] += Huffman.decodeScaleFactor(stream) - SF_DELTA;
if(offset[0] > 255)
throw new Error("Scalefactor out of range: " + offset[0]);
scaleFactors[idx] = tables.SCALEFACTOR_TABLE[offset[0] - 100 + SF_OFFSET];
}
break;
}
}
}
},
decodePulseData: function(stream) {
var pulseCount = stream.read(2) + 1,
pulseSWB = stream.read(6);
if (pulseSWB >= this.info.swbCount)
throw new Error("Pulse SWB out of range: " + pulseSWB);
if (!this.pulseOffset || this.pulseOffset.length !== pulseCount) {
// only reallocate if needed
this.pulseOffset = new Int32Array(pulseCount);
this.pulseAmp = new Int32Array(pulseCount);
}
this.pulseOffset[0] = this.info.swbOffsets[pulseSWB] + stream.read(5);
this.pulseAmp[0] = stream.read(4);
if (this.pulseOffset[0] > 1023)
throw new Error("Pulse offset out of range: " + this.pulseOffset[0]);
for (var i = 1; i < pulseCount; i++) {
this.pulseOffset[i] = stream.read(5) + this.pulseOffset[i - 1];
if (this.pulseOffset[i] > 1023)
throw new Error("Pulse offset out of range: " + this.pulseOffset[i]);
this.pulseAmp[i] = stream.read(4);
}
},
decodeSpectralData: function(stream) {
var data = this.data,
info = this.info,
maxSFB = info.maxSFB,
windowGroups = info.groupCount,
offsets = info.swbOffsets,
bandTypes = this.bandTypes,
scaleFactors = this.scaleFactors,
buf = this.specBuf;
var groupOff = 0, idx = 0;
for (var g = 0; g < windowGroups; g++) {
var groupLen = info.groupLength[g];
for (var sfb = 0; sfb < maxSFB; sfb++, idx++) {
var hcb = bandTypes[idx],
off = groupOff + offsets[sfb],
width = offsets[sfb + 1] - offsets[sfb];
if (hcb === ICStream.ZERO_BT || hcb === ICStream.INTENSITY_BT || hcb === ICStream.INTENSITY_BT2) {
for (var group = 0; group < groupLen; group++, off += 128) {
for (var i = off; i < off + width; i++) {
data[i] = 0;
}
}
} else if (hcb === ICStream.NOISE_BT) {
// fill with random values
for (var group = 0; group < groupLen; group++, off += 128) {
var energy = 0;
for (var k = 0; k < width; k++) {
this.randomState = (this.randomState * (1664525 + 1013904223))|0;
data[off + k] = this.randomState;
energy += data[off + k] * data[off + k];
}
var scale = scaleFactors[idx] / Math.sqrt(energy);
for (var k = 0; k < width; k++) {
data[off + k] *= scale;
}
}
} else {
for (var group = 0; group < groupLen; group++, off += 128) {
var num = (hcb >= ICStream.FIRST_PAIR_BT) ? 2 : 4;
for (var k = 0; k < width; k += num) {
Huffman.decodeSpectralData(stream, hcb, buf, 0);
// inverse quantization & scaling
for (var j = 0; j < num; j++) {
data[off + k + j] = (buf[j] > 0) ? tables.IQ_TABLE[buf[j]] : -tables.IQ_TABLE[-buf[j]];
data[off + k + j] *= scaleFactors[idx];
}
}
}
}
}
groupOff += groupLen << 7;
}
// add pulse data, if present
if (this.pulsePresent) {
throw new Error('TODO: add pulse data');
}
}
}
// Individual Channel Stream Info
function ICSInfo() {
this.windowShape = new Int32Array(2);
this.windowSequence = ICStream.ONLY_LONG_SEQUENCE;
this.groupLength = new Int32Array(MAX_WINDOW_GROUP_COUNT);
this.ltpData1Present = false;
this.ltpData2Present = false;
}
ICSInfo.prototype = {
decode: function(stream, config, commonWindow) {
stream.advance(1); // reserved
this.windowSequence = stream.read(2);
this.windowShape[0] = this.windowShape[1];
this.windowShape[1] = stream.read(1);
this.groupCount = 1;
this.groupLength[0] = 1;
if (this.windowSequence === ICStream.EIGHT_SHORT_SEQUENCE) {
this.maxSFB = stream.read(4);
for (var i = 0; i < 7; i++) {
if (stream.read(1)) {
this.groupLength[this.groupCount - 1]++;
} else {
this.groupCount++;
this.groupLength[this.groupCount - 1] = 1;
}
}
this.windowCount = 8;
this.swbOffsets = tables.SWB_OFFSET_128[config.sampleIndex];
this.swbCount = tables.SWB_SHORT_WINDOW_COUNT[config.sampleIndex];
this.predictorPresent = false;
} else {
this.maxSFB = stream.read(6);
this.windowCount = 1;
this.swbOffsets = tables.SWB_OFFSET_1024[config.sampleIndex];
this.swbCount = tables.SWB_LONG_WINDOW_COUNT[config.sampleIndex];
this.predictorPresent = !!stream.read(1);
if (this.predictorPresent)
this.decodePrediction(stream, config, commonWindow);
}
},
decodePrediction: function(stream, config, commonWindow) {
throw new Error('Prediction not implemented.');
switch (config.profile) {
case AOT_AAC_MAIN:
throw new Error('Prediction not implemented.');
break;
case AOT_AAC_LTP:
throw new Error('LTP prediction not implemented.');
break;
default:
throw new Error('Unsupported profile for prediction ' + config.profile);
}
}
};
module.exports = ICStream;