Initial working version

This commit is contained in:
Samuel Kent
2022-12-22 20:22:22 +11:00
parent ce9675a1cc
commit ced7fa5092
902 changed files with 150252 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
import { ITokenizer } from 'strtok3/lib/core';
export declare class BitReader {
private tokenizer;
pos: number;
private dword;
constructor(tokenizer: ITokenizer);
/**
*
* @param bits 1..30 bits
*/
read(bits: number): Promise<number>;
ignore(bits: number): Promise<number>;
}
+54
View File
@@ -0,0 +1,54 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BitReader = void 0;
const Token = require("token-types");
class BitReader {
constructor(tokenizer) {
this.tokenizer = tokenizer;
this.pos = 0;
this.dword = undefined;
}
/**
*
* @param bits 1..30 bits
*/
async read(bits) {
while (this.dword === undefined) {
this.dword = await this.tokenizer.readToken(Token.UINT32_LE);
}
let out = this.dword;
this.pos += bits;
if (this.pos < 32) {
out >>>= (32 - this.pos);
return out & ((1 << bits) - 1);
}
else {
this.pos -= 32;
if (this.pos === 0) {
this.dword = undefined;
return out & ((1 << bits) - 1);
}
else {
this.dword = await this.tokenizer.readToken(Token.UINT32_LE);
if (this.pos) {
out <<= this.pos;
out |= this.dword >>> (32 - this.pos);
}
return out & ((1 << bits) - 1);
}
}
}
async ignore(bits) {
if (this.pos > 0) {
const remaining = 32 - this.pos;
this.dword = undefined;
bits -= remaining;
this.pos = 0;
}
const remainder = bits % 32;
const numOfWords = (bits - remainder) / 32;
await this.tokenizer.ignore(numOfWords * 4);
return this.read(remainder);
}
}
exports.BitReader = BitReader;
+8
View File
@@ -0,0 +1,8 @@
import { BasicParser } from '../../common/BasicParser';
export declare class MpcSv7Parser extends BasicParser {
private bitreader;
private audioLength;
private duration;
parse(): Promise<void>;
private skipAudioData;
}
+46
View File
@@ -0,0 +1,46 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.MpcSv7Parser = void 0;
const initDebug = require("debug");
const BasicParser_1 = require("../../common/BasicParser");
const SV7 = require("./StreamVersion7");
const APEv2Parser_1 = require("../../apev2/APEv2Parser");
const BitReader_1 = require("./BitReader");
const debug = initDebug('music-metadata:parser:musepack');
class MpcSv7Parser extends BasicParser_1.BasicParser {
constructor() {
super(...arguments);
this.audioLength = 0;
}
async parse() {
const header = await this.tokenizer.readToken(SV7.Header);
if (header.signature !== 'MP+')
throw new Error('Unexpected magic number');
debug(`stream-version=${header.streamMajorVersion}.${header.streamMinorVersion}`);
this.metadata.setFormat('container', 'Musepack, SV7');
this.metadata.setFormat('sampleRate', header.sampleFrequency);
const numberOfSamples = 1152 * (header.frameCount - 1) + header.lastFrameLength;
this.metadata.setFormat('numberOfSamples', numberOfSamples);
this.duration = numberOfSamples / header.sampleFrequency;
this.metadata.setFormat('duration', this.duration);
this.bitreader = new BitReader_1.BitReader(this.tokenizer);
this.metadata.setFormat('numberOfChannels', header.midSideStereo || header.intensityStereo ? 2 : 1);
const version = await this.bitreader.read(8);
this.metadata.setFormat('codec', (version / 100).toFixed(2));
await this.skipAudioData(header.frameCount);
debug(`End of audio stream, switching to APEv2, offset=${this.tokenizer.position}`);
return APEv2Parser_1.APEv2Parser.tryParseApeHeader(this.metadata, this.tokenizer, this.options);
}
async skipAudioData(frameCount) {
while (frameCount-- > 0) {
const frameLength = await this.bitreader.read(20);
this.audioLength += 20 + frameLength;
await this.bitreader.ignore(frameLength);
}
// last frame
const lastFrameLength = await this.bitreader.read(11);
this.audioLength += lastFrameLength;
this.metadata.setFormat('bitrate', this.audioLength / this.duration);
}
}
exports.MpcSv7Parser = MpcSv7Parser;
+28
View File
@@ -0,0 +1,28 @@
import { IGetToken } from 'strtok3/lib/core';
/**
* MusePack stream version 7 format specification
* http://trac.musepack.net/musepack/wiki/SV7Specification
*/
export interface IHeader {
signature: string;
streamMinorVersion: number;
streamMajorVersion: number;
frameCount: number;
intensityStereo: boolean;
midSideStereo: boolean;
maxBand: number;
profile: number;
link: number;
sampleFrequency: number;
maxLevel: number;
titleGain: number;
titlePeak: number;
albumGain: number;
albumPeak: number;
trueGapless: boolean;
lastFrameLength: number;
}
/**
* BASIC STRUCTURE
*/
export declare const Header: IGetToken<IHeader>;
+41
View File
@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Header = void 0;
const Token = require("token-types");
const util = require("../../common/Util");
/**
* BASIC STRUCTURE
*/
exports.Header = {
len: 6 * 4,
get: (buf, off) => {
const header = {
// word 0
signature: Buffer.from(buf).toString('latin1', off, off + 3),
// versionIndex number * 1000 (3.81 = 3810) (remember that 4-byte alignment causes this to take 4-bytes)
streamMinorVersion: util.getBitAllignedNumber(buf, off + 3, 0, 4),
streamMajorVersion: util.getBitAllignedNumber(buf, off + 3, 4, 4),
// word 1
frameCount: Token.UINT32_LE.get(buf, off + 4),
// word 2
maxLevel: Token.UINT16_LE.get(buf, off + 8),
sampleFrequency: [44100, 48000, 37800, 32000][util.getBitAllignedNumber(buf, off + 10, 0, 2)],
link: util.getBitAllignedNumber(buf, off + 10, 2, 2),
profile: util.getBitAllignedNumber(buf, off + 10, 4, 4),
maxBand: util.getBitAllignedNumber(buf, off + 11, 0, 6),
intensityStereo: util.isBitSet(buf, off + 11, 6),
midSideStereo: util.isBitSet(buf, off + 11, 7),
// word 3
titlePeak: Token.UINT16_LE.get(buf, off + 12),
titleGain: Token.UINT16_LE.get(buf, off + 14),
// word 4
albumPeak: Token.UINT16_LE.get(buf, off + 16),
albumGain: Token.UINT16_LE.get(buf, off + 18),
// word
lastFrameLength: (Token.UINT32_LE.get(buf, off + 20) >>> 20) & 0x7FF,
trueGapless: util.isBitSet(buf, off + 23, 0)
};
header.lastFrameLength = header.trueGapless ? (Token.UINT32_LE.get(buf, 20) >>> 20) & 0x7FF : 0;
return header;
}
};