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
+6
View File
@@ -0,0 +1,6 @@
import { BasicParser } from '../../common/BasicParser';
export declare class MpcSv8Parser extends BasicParser {
private audioLength;
parse(): Promise<void>;
private parsePacket;
}
+55
View File
@@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MpcSv8Parser = void 0;
const initDebug = require("debug");
const BasicParser_1 = require("../../common/BasicParser");
const SV8 = require("./StreamVersion8");
const APEv2Parser_1 = require("../../apev2/APEv2Parser");
const FourCC_1 = require("../../common/FourCC");
const debug = initDebug('music-metadata:parser:musepack');
class MpcSv8Parser extends BasicParser_1.BasicParser {
constructor() {
super(...arguments);
this.audioLength = 0;
}
async parse() {
const signature = await this.tokenizer.readToken(FourCC_1.FourCcToken);
if (signature !== 'MPCK')
throw new Error('Invalid Magic number');
this.metadata.setFormat('container', 'Musepack, SV8');
return this.parsePacket();
}
async parsePacket() {
const sv8reader = new SV8.StreamReader(this.tokenizer);
do {
const header = await sv8reader.readPacketHeader();
debug(`packet-header key=${header.key}, payloadLength=${header.payloadLength}`);
switch (header.key) {
case 'SH': // Stream Header
const sh = await sv8reader.readStreamHeader(header.payloadLength);
this.metadata.setFormat('numberOfSamples', sh.sampleCount);
this.metadata.setFormat('sampleRate', sh.sampleFrequency);
this.metadata.setFormat('duration', sh.sampleCount / sh.sampleFrequency);
this.metadata.setFormat('numberOfChannels', sh.channelCount);
break;
case 'AP': // Audio Packet
this.audioLength += header.payloadLength;
await this.tokenizer.ignore(header.payloadLength);
break;
case 'RG': // Replaygain
case 'EI': // Encoder Info
case 'SO': // Seek Table Offset
case 'ST': // Seek Table
case 'CT': // Chapter-Tag
await this.tokenizer.ignore(header.payloadLength);
break;
case 'SE': // Stream End
this.metadata.setFormat('bitrate', this.audioLength * 8 / this.metadata.format.duration);
return APEv2Parser_1.APEv2Parser.tryParseApeHeader(this.metadata, this.tokenizer, this.options);
default:
throw new Error(`Unexpected header: ${header.key}`);
}
} while (true);
}
}
exports.MpcSv8Parser = MpcSv8Parser;
+40
View File
@@ -0,0 +1,40 @@
import { ITokenizer } from 'strtok3/lib/core';
export interface IPacketHeader {
key: string;
payloadLength: number;
}
/**
* Stream Header Packet
* Ref: http://trac.musepack.net/musepack/wiki/SV8Specification#StreamHeaderPacket
*/
interface IStreamHeader1 {
crc: number;
streamVersion: number;
}
/**
* Stream Header Packet
* Ref: http://trac.musepack.net/musepack/wiki/SV8Specification#StreamHeaderPacket
*/
interface IStreamHeader3 {
sampleFrequency: number;
maxUsedBands: number;
channelCount: number;
msUsed: boolean;
audioBlockFrames: number;
}
/**
* Stream Header Packet
* Ref: http://trac.musepack.net/musepack/wiki/SV8Specification#StreamHeaderPacket
*/
interface IStreamHeader extends IStreamHeader1, IStreamHeader3 {
sampleCount: number;
beginningOfSilence: number;
}
export declare class StreamReader {
private tokenizer;
constructor(tokenizer: ITokenizer);
readPacketHeader(): Promise<IPacketHeader>;
readStreamHeader(size: number): Promise<IStreamHeader>;
private readVariableSizeField;
}
export {};
+80
View File
@@ -0,0 +1,80 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StreamReader = void 0;
const Token = require("token-types");
const util = require("../../common/Util");
const initDebug = require("debug");
const debug = initDebug('music-metadata:parser:musepack:sv8');
const PacketKey = new Token.StringType(2, 'binary');
/**
* Stream Header Packet part 1
* Ref: http://trac.musepack.net/musepack/wiki/SV8Specification#StreamHeaderPacket
*/
const SH_part1 = {
len: 5,
get: (buf, off) => {
return {
crc: Token.UINT32_LE.get(buf, off),
streamVersion: Token.UINT8.get(buf, off + 4)
};
}
};
/**
* Stream Header Packet part 3
* Ref: http://trac.musepack.net/musepack/wiki/SV8Specification#StreamHeaderPacket
*/
const SH_part3 = {
len: 2,
get: (buf, off) => {
return {
sampleFrequency: [44100, 48000, 37800, 32000][util.getBitAllignedNumber(buf, off, 0, 3)],
maxUsedBands: util.getBitAllignedNumber(buf, off, 3, 5),
channelCount: util.getBitAllignedNumber(buf, off + 1, 0, 4) + 1,
msUsed: util.isBitSet(buf, off + 1, 4),
audioBlockFrames: util.getBitAllignedNumber(buf, off + 1, 5, 3)
};
}
};
class StreamReader {
constructor(tokenizer) {
this.tokenizer = tokenizer;
}
async readPacketHeader() {
const key = await this.tokenizer.readToken(PacketKey);
const size = await this.readVariableSizeField();
return {
key,
payloadLength: size.value - 2 - size.len
};
}
async readStreamHeader(size) {
const streamHeader = {};
debug(`Reading SH at offset=${this.tokenizer.position}`);
const part1 = await this.tokenizer.readToken(SH_part1);
size -= SH_part1.len;
Object.assign(streamHeader, part1);
debug(`SH.streamVersion = ${part1.streamVersion}`);
const sampleCount = await this.readVariableSizeField();
size -= sampleCount.len;
streamHeader.sampleCount = sampleCount.value;
const bs = await this.readVariableSizeField();
size -= bs.len;
streamHeader.beginningOfSilence = bs.value;
const part3 = await this.tokenizer.readToken(SH_part3);
size -= SH_part3.len;
Object.assign(streamHeader, part3);
// assert.equal(size, 0);
await this.tokenizer.ignore(size);
return streamHeader;
}
async readVariableSizeField(len = 1, hb = 0) {
let n = await this.tokenizer.readNumber(Token.UINT8);
if ((n & 0x80) === 0) {
return { len, value: hb + n };
}
n &= 0x7F;
n += hb;
return this.readVariableSizeField(len + 1, n << 7);
}
}
exports.StreamReader = StreamReader;