mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
278 lines
7.9 KiB
JavaScript
278 lines
7.9 KiB
JavaScript
var consts = require('A:/tvdos/include/js-mp3/consts.js');
|
|
|
|
var Frameheader = {
|
|
createNew: function (value) {
|
|
|
|
// A mepg1FrameHeader is MPEG1 Layer 1-3 frame header
|
|
var fh = {
|
|
value: value
|
|
};
|
|
|
|
/**
|
|
* ID returns this header's ID stored in position 20,19
|
|
*
|
|
* @return {number}
|
|
*/
|
|
fh.id = function () {
|
|
return ((fh.value & 0x00180000) >>> 19) >>> 0;
|
|
};
|
|
|
|
/**
|
|
* Layer returns the mpeg layer of this frame stored in position 18,17
|
|
*
|
|
* @return {number}
|
|
*/
|
|
fh.layer = function () {
|
|
return ((fh.value & 0x00060000) >>> 17) >>> 0;
|
|
};
|
|
|
|
/**
|
|
* ProtectionBit returns the protection bit stored in position 16
|
|
*
|
|
* @return {number}
|
|
*/
|
|
fh.protectionBit = function () {
|
|
return ((fh.value & 0x00010000) >>> 16) >>> 0;
|
|
};
|
|
|
|
/**
|
|
* BirateIndex returns the bitrate index stored in position 15,12
|
|
*
|
|
* @return {number}
|
|
*/
|
|
fh.bitrateIndex = function () {
|
|
return ((fh.value & 0x0000f000) >>> 12) >>> 0;
|
|
};
|
|
|
|
/**
|
|
* SamplingFrequency returns the SamplingFrequency in Hz stored in position 11,10
|
|
*
|
|
* @returns {*}
|
|
* @constructor
|
|
*/
|
|
fh.samplingFrequency = function () {
|
|
return consts.newSamplingFrequencyInstance(((fh.value & 0x00000c00) >>> 10) >>> 0)
|
|
};
|
|
|
|
/**
|
|
* PaddingBit returns the padding bit stored in position 9
|
|
*
|
|
* @return {number}
|
|
*/
|
|
fh.paddingBit = function () {
|
|
return ((fh.value & 0x00000200) >>> 9) >>> 0;
|
|
};
|
|
|
|
/**
|
|
* PrivateBit returns the private bit stored in position 8 - this bit may be used to store
|
|
* arbitrary data to be used by an application
|
|
*
|
|
* @return {number}
|
|
*/
|
|
fh.privateBit = function () {
|
|
return ((fh.value & 0x00000100) >>> 8) >>> 0;
|
|
};
|
|
|
|
/**
|
|
* Mode returns the channel mode, stored in position 7,6
|
|
*
|
|
* @return {number}
|
|
*/
|
|
fh.mode = function () {
|
|
return ((fh.value & 0x000000c0) >>> 6) >>> 0;
|
|
};
|
|
|
|
/**
|
|
* modeExtension returns the mode_extension - for use with Joint Stereo - stored in
|
|
* position 4,5
|
|
*
|
|
* @returns {number}
|
|
*/
|
|
fh.modeExtension = function () {
|
|
return ((fh.value & 0x00000030) >>> 4) >>> 0;
|
|
};
|
|
|
|
/**
|
|
* UseMSStereo returns a boolean value indicating whether the frame uses middle/side stereo.
|
|
*
|
|
* @returns {*}
|
|
*/
|
|
fh.useMSStereo = function () {
|
|
if (fh.mode() !== consts.ModeJointStereo) {
|
|
return false;
|
|
}
|
|
return (fh.modeExtension() & 0x2) !== 0;
|
|
};
|
|
|
|
/**
|
|
* UseIntensityStereo returns a boolean value indicating whether the frame uses intensity
|
|
* stereo.
|
|
*
|
|
* @returns {*}
|
|
*/
|
|
fh.useIntensityStereo = function () {
|
|
if (fh.mode() !== consts.ModeJointStereo) {
|
|
return false;
|
|
}
|
|
return (fh.modeExtension() & 0x1) !== 0;
|
|
};
|
|
|
|
/**
|
|
* Copyright returns whether or not this recording is copywritten - stored in position 3
|
|
*
|
|
* @returns {number}
|
|
*/
|
|
fh.copyright = function () {
|
|
return ((fh.value & 0x00000008) >>> 3) >>> 0;
|
|
};
|
|
|
|
/**
|
|
* OriginalOrCopy returns whether or not this is an Original recording or a copy of one -
|
|
* stored in position 2
|
|
*
|
|
* @returns {number}
|
|
*/
|
|
fh.originalOrCopy = function () {
|
|
return ((fh.value & 0x00000004) >>> 2) >>> 0;
|
|
};
|
|
|
|
/**
|
|
* Emphasis returns emphasis - the emphasis indication is here to tell the decoder that the
|
|
* file must be de-emphasized - stored in position 0,1
|
|
*
|
|
* @returns {number}
|
|
*/
|
|
fh.emphasis = function () {
|
|
return (fh.value & 0x00000003) >>> 0;
|
|
};
|
|
|
|
/**
|
|
* IsValid returns a boolean value indicating whether the header is valid or not.
|
|
*
|
|
* @returns {boolean}
|
|
*/
|
|
fh.isValid = function () {
|
|
const sync = 0xffe00000;
|
|
if ((fh.value & sync) >>> 0 !== sync) {
|
|
return false;
|
|
}
|
|
if (fh.id() === consts.VersionReserved) {
|
|
return false;
|
|
}
|
|
if (fh.bitrateIndex() === 15) {
|
|
return false;
|
|
}
|
|
if (fh.samplingFrequency().value === consts.SamplingFrequencyReserved) {
|
|
return false;
|
|
}
|
|
if (fh.layer() === consts.LayerReserved) {
|
|
return false;
|
|
}
|
|
if (fh.emphasis() === 2) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* @returns {number}
|
|
*/
|
|
fh.frameSize = function () {
|
|
var value = (144 * Frameheader.bitrate(fh.layer(), fh.bitrateIndex()))
|
|
/ fh.samplingFrequency().Int()
|
|
+ fh.paddingBit();
|
|
return Math.floor(value);
|
|
};
|
|
|
|
/**
|
|
* @returns {number}
|
|
*/
|
|
fh.numberOfChannels = function () {
|
|
if (fh.mode() === consts.ModeSingleChannel) {
|
|
return 1;
|
|
}
|
|
return 2;
|
|
};
|
|
|
|
return fh;
|
|
},
|
|
|
|
bitrate: function (layer, index) {
|
|
switch (layer) {
|
|
case consts.Layer1:
|
|
return [0, 32000, 64000, 96000, 128000, 160000, 192000, 224000,
|
|
256000, 288000, 320000, 352000, 384000, 416000, 448000][index];
|
|
case consts.Layer2:
|
|
return [0, 32000, 48000, 56000, 64000, 80000, 96000, 112000,
|
|
128000, 160000, 192000, 224000, 256000, 320000, 384000][index];
|
|
case consts.Layer3:
|
|
return [0, 32000, 40000, 48000, 56000, 64000, 80000, 96000,
|
|
112000, 128000, 160000, 192000, 224000, 256000, 320000][index];
|
|
}
|
|
throw new Error('not reached');
|
|
},
|
|
|
|
read: function (source, position) {
|
|
var pos = position;
|
|
|
|
var buf = source.readFull(4)
|
|
if (buf.err) {
|
|
return {
|
|
err: buf.err
|
|
}
|
|
}
|
|
if (buf.length < 4) {
|
|
return {
|
|
h: 0,
|
|
position: 0,
|
|
err: "UnexpectedEOF readHeader (1)"
|
|
}
|
|
}
|
|
|
|
var b1 = buf[0] >>> 0;
|
|
var b2 = buf[1] >>> 0;
|
|
var b3 = buf[2] >>> 0;
|
|
var b4 = buf[3] >>> 0;
|
|
|
|
var fh = Frameheader.createNew((((b1 << 24) >>> 0) | ((b2 << 16) >>> 0) | ((b3 << 8) >>> 0) | ((b4 << 0) >>> 0)) >>> 0);
|
|
while (!fh.isValid()) {
|
|
// stopPosition++;
|
|
|
|
buf = source.readFull(1);
|
|
|
|
if (buf.length < 1) {
|
|
return {
|
|
h: 0,
|
|
position: 0,
|
|
err: "UnexpectedEOF readHeader (2)"
|
|
}
|
|
}
|
|
|
|
b1 = b2;
|
|
b2 = b3;
|
|
b3 = b4;
|
|
b4 = buf[0] >>> 0;
|
|
|
|
fh = Frameheader.createNew((((b1 << 24) >>> 0) | ((b2 << 16) >>> 0) | ((b3 << 8) >>> 0) | ((b4 << 0) >>> 0)) >>> 0);
|
|
pos++;
|
|
}
|
|
|
|
// If we get here we've found the sync word, and can decode the header
|
|
// which is in the low 20 bits of the 32-bit sync+header word.
|
|
if (fh.bitrateIndex() === 0) {
|
|
return {
|
|
h: 0,
|
|
position: 0,
|
|
err: "mp3: free bitrate format is not supported. Header word is " + fh.value + " at position " + position
|
|
}
|
|
}
|
|
|
|
return {
|
|
h: fh,
|
|
position: pos
|
|
}
|
|
}
|
|
};
|
|
|
|
exports = Frameheader;
|