Skip to content

Commit

Permalink
#196 Move common IFF (EA-IFF 85) to definition to shared 'iff' library.
Browse files Browse the repository at this point in the history
  • Loading branch information
Borewit committed Apr 7, 2019
1 parent 327e8e5 commit 19284f5
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 77 deletions.
25 changes: 13 additions & 12 deletions src/aiff/AiffParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { ID3v2Parser } from '../id3v2/ID3v2Parser';
import { FourCcToken } from '../common/FourCC';
import { BasicParser } from '../common/BasicParser';

import * as Chunk from './Chunk';
import * as AiffToken from './AiffToken';
import * as iff from '../iff';

const debug = initDebug('music-metadata:parser:aiff');

Expand All @@ -26,7 +27,7 @@ export class AIFFParser extends BasicParser {

public async parse(): Promise<void> {

const header = await this.tokenizer.readToken<Chunk.IChunkHeader>(Chunk.Header);
const header = await this.tokenizer.readToken<iff.IChunkHeader>(iff.Header);
if (header.chunkID !== 'FORM')
throw new Error('Invalid Chunk-ID, expected \'FORM\''); // Not AIFF format

Expand All @@ -50,12 +51,12 @@ export class AIFFParser extends BasicParser {

try {
do {
const chunkHeader = await this.tokenizer.readToken<Chunk.IChunkHeader>(Chunk.Header);
const chunkHeader = await this.tokenizer.readToken<iff.IChunkHeader>(iff.Header);

debug(`Chunk id=${chunkHeader.chunkID}`);
const nextChunk = 2 * Math.round(chunkHeader.size / 2);
const bytesread = await this.readData(chunkHeader);
await this.tokenizer.ignore(nextChunk - bytesread);
const nextChunk = 2 * Math.round(chunkHeader.chunkSize / 2);
const bytesRead = await this.readData(chunkHeader);
await this.tokenizer.ignore(nextChunk - bytesRead);
} while (true);
} catch (err) {
if (err.message !== endOfFile) {
Expand All @@ -64,29 +65,29 @@ export class AIFFParser extends BasicParser {
}
}

public async readData(header: Chunk.IChunkHeader): Promise<number> {
public async readData(header: iff.IChunkHeader): Promise<number> {
switch (header.chunkID) {

case 'COMM': // The Common Chunk
const common = await this.tokenizer.readToken<Chunk.ICommon>(new Chunk.Common(header, this.isCompressed));
const common = await this.tokenizer.readToken<AiffToken.ICommon>(new AiffToken.Common(header, this.isCompressed));
this.metadata.setFormat('bitsPerSample', common.sampleSize);
this.metadata.setFormat('sampleRate', common.sampleRate);
this.metadata.setFormat('numberOfChannels', common.numChannels);
this.metadata.setFormat('numberOfSamples', common.numSampleFrames);
this.metadata.setFormat('duration', common.numSampleFrames / common.sampleRate);
this.metadata.setFormat('encoder', common.compressionName);
return header.size;
return header.chunkSize;

case 'ID3 ': // ID3-meta-data
const id3_data = await this.tokenizer.readToken<Buffer>(new Token.BufferType(header.size));
const id3_data = await this.tokenizer.readToken<Buffer>(new Token.BufferType(header.chunkSize));
const id3stream = new ID3Stream(id3_data);
const rst = strtok3.fromStream(id3stream);
await ID3v2Parser.getInstance().parse(this.metadata, rst, this.options);
return header.size;
return header.chunkSize;

case 'SSND': // Sound Data Chunk
if (this.metadata.format.duration) {
this.metadata.setFormat('bitrate', 8 * header.size / this.metadata.format.duration);
this.metadata.setFormat('bitrate', 8 * header.chunkSize / this.metadata.format.duration);
}
return 0;

Expand Down
35 changes: 4 additions & 31 deletions src/aiff/Chunk.ts → src/aiff/AiffToken.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,7 @@
import * as Token from "token-types";
import * as assert from "assert";
import {FourCcToken} from "../common/FourCC";

export interface IChunkHeader {

/**
* A chunk ID (ie, 4 ASCII bytes)
*/
chunkID: string,
/**
* Number of data bytes following this data header
*/
size: number
}

/**
* Common AIFF chunk header
*/
export const Header: Token.IGetToken<IChunkHeader> = {
len: 8,

get: (buf, off): IChunkHeader => {
return {
// Group-ID
chunkID: FourCcToken.get(buf, off),
// Size
size: buf.readUInt32BE(off + 4)
};
}
};
import * as iff from '../iff';

/**
* The Common Chunk.
Expand All @@ -48,10 +21,10 @@ export class Common implements Token.IGetToken<ICommon> {

public len: number;

public constructor(header: IChunkHeader, private isAifc: boolean) {
public constructor(header: iff.IChunkHeader, private isAifc: boolean) {
const minimumChunkSize = isAifc ? 22 : 18;
assert.ok(header.size >= minimumChunkSize, `COMMON CHUNK size should always be at least ${minimumChunkSize}`);
this.len = header.size;
assert.ok(header.chunkSize >= minimumChunkSize, `COMMON CHUNK size should always be at least ${minimumChunkSize}`);
this.len = header.chunkSize;
}

public get(buf: Buffer, off: number): ICommon {
Expand Down
35 changes: 35 additions & 0 deletions src/iff/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as Token from "token-types";
import * as assert from "assert";
import {FourCcToken} from "../common/FourCC";

/**
* "EA IFF 85" Standard for Interchange Format Files
* Ref: http://www.martinreddy.net/gfx/2d/IFF.txt
*/
export interface IChunkHeader {

/**
* A chunk ID (ie, 4 ASCII bytes)
*/
chunkID: string,
/**
* Number of data bytes following this data header
*/
chunkSize: number
}

/**
* Common AIFF chunk header
*/
export const Header: Token.IGetToken<IChunkHeader> = {
len: 8,

get: (buf, off): IChunkHeader => {
return {
// Chunk type ID
chunkID: FourCcToken.get(buf, off),
// Chunk size
chunkSize: buf.readUInt32BE(off + 4)
};
}
};
19 changes: 5 additions & 14 deletions src/riff/RiffChunk.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
import * as Token from 'token-types';
import {FourCcToken} from '../common/FourCC';
import {IChunkHeader} from '../iff';

export interface IChunkHeader {

/**
* A chunk ID (ie, 4 ASCII bytes)
*/
chunkID: string,
/**
* Number of data bytes following this data header
*/
size: number
}
export {IChunkHeader} from '../iff';

/**
* Common RIFF chunk header
Expand All @@ -24,7 +15,7 @@ export const Header: Token.IGetToken<IChunkHeader> = {
// Group-ID
chunkID: FourCcToken.get(buf, off),
// Size
size: buf.readUInt32LE(off + 4)
chunkSize: buf.readUInt32LE(off + 4)
};
}
};
Expand All @@ -37,11 +28,11 @@ export class ListInfoTagValue implements Token.IGetToken<string> {
public len: number;

public constructor(private tagHeader: IChunkHeader) {
this.len = tagHeader.size;
this.len = tagHeader.chunkSize;
this.len += this.len & 1; // if it is an odd length, round up to even
}

public get(buf, off): string {
return new Token.StringType(this.tagHeader.size, 'ascii').get(buf, off);
return new Token.StringType(this.tagHeader.chunkSize, 'ascii').get(buf, off);
}
}
32 changes: 16 additions & 16 deletions src/riff/WaveParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import * as Token from 'token-types';
import * as initDebug from 'debug';
import { Readable } from 'stream';

import * as RiffChunk from './RiffChunk';
import * as riff from './RiffChunk';
import * as WaveChunk from './../wav/WaveChunk';
import { ID3v2Parser } from '../id3v2/ID3v2Parser';
import { IChunkHeader } from '../aiff/Chunk';

import Common from '../common/Util';
import { FourCcToken } from '../common/FourCC';
import { BasicParser } from '../common/BasicParser';
Expand All @@ -30,11 +30,11 @@ export class WaveParser extends BasicParser {
private fact: WaveChunk.IFactChunk;

private blockAlign: number;
private header: RiffChunk.IChunkHeader;
private header: riff.IChunkHeader;

public async parse(): Promise<void> {

const riffHeader = await this.tokenizer.readToken<RiffChunk.IChunkHeader>(RiffChunk.Header);
const riffHeader = await this.tokenizer.readToken<riff.IChunkHeader>(riff.Header);
debug(`pos=${this.tokenizer.position}, parse: chunkID=${riffHeader.chunkID}`);
if (riffHeader.chunkID !== 'RIFF')
return; // Not RIFF format
Expand All @@ -59,7 +59,7 @@ export class WaveParser extends BasicParser {
public async readWaveChunk(): Promise<void> {

do {
const header = await this.tokenizer.readToken<RiffChunk.IChunkHeader>(RiffChunk.Header);
const header = await this.tokenizer.readToken<riff.IChunkHeader>(riff.Header);

this.header = header;
debug(`pos=${this.tokenizer.position}, readChunk: chunkID=RIFF/WAVE/${header.chunkID}`);
Expand Down Expand Up @@ -92,7 +92,7 @@ export class WaveParser extends BasicParser {

case 'id3 ': // The way Picard, FooBar currently stores, ID3 meta-data
case 'ID3 ': // The way Mp3Tags stores ID3 meta-data
const id3_data = await this.tokenizer.readToken<Buffer>(new Token.BufferType(header.size));
const id3_data = await this.tokenizer.readToken<Buffer>(new Token.BufferType(header.chunkSize));
const id3stream = new ID3Stream(id3_data);
const rst = strtok3.fromStream(id3stream);
await ID3v2Parser.getInstance().parse(this.metadata, rst, this.options);
Expand All @@ -102,46 +102,46 @@ export class WaveParser extends BasicParser {
if (this.metadata.format.lossless !== false) {
this.metadata.setFormat('lossless', true);
}
const numberOfSamples = this.fact ? this.fact.dwSampleLength : (header.size / this.blockAlign);
const numberOfSamples = this.fact ? this.fact.dwSampleLength : (header.chunkSize / this.blockAlign);
this.metadata.setFormat('numberOfSamples', numberOfSamples);

this.metadata.setFormat('duration', numberOfSamples / this.metadata.format.sampleRate);
this.metadata.setFormat('bitrate', this.metadata.format.numberOfChannels * this.blockAlign * this.metadata.format.sampleRate); // ToDo: check me
await this.tokenizer.ignore(header.size);
await this.tokenizer.ignore(header.chunkSize);
break;

default:
debug(`Ignore chunk: RIFF/${header.chunkID} of ${header.size} bytes`);
debug(`Ignore chunk: RIFF/${header.chunkID} of ${header.chunkSize} bytes`);
this.warnings.push('Ignore chunk: RIFF/' + header.chunkID);
await this.tokenizer.ignore(header.size);
await this.tokenizer.ignore(header.chunkSize);
}

if (this.header.size % 2 === 1) {
if (this.header.chunkSize % 2 === 1) {
debug('Read odd padding byte'); // https://wiki.multimedia.cx/index.php/RIFF
await this.tokenizer.ignore(1);
}
} while (true);
}

public async parseListTag(listHeader: IChunkHeader): Promise<void> {
public async parseListTag(listHeader: riff.IChunkHeader): Promise<void> {
const listType = await this.tokenizer.readToken<string>(FourCcToken);
debug('pos=%s, parseListTag: chunkID=RIFF/WAVE/LIST/%s', this.tokenizer.position, listType);
switch (listType) {
case 'INFO':
return this.parseRiffInfoTags(listHeader.size - 4);
return this.parseRiffInfoTags(listHeader.chunkSize - 4);

case 'adtl':
default:
this.warnings.push('Ignore chunk: RIFF/WAVE/LIST/' + listType);
debug('Ignoring chunkID=RIFF/WAVE/LIST/' + listType);
return this.tokenizer.ignore(listHeader.size - 4);
return this.tokenizer.ignore(listHeader.chunkSize - 4);
}
}

private async parseRiffInfoTags(chunkSize): Promise<void> {
while (chunkSize >= 8) {
const header = await this.tokenizer.readToken<RiffChunk.IChunkHeader>(RiffChunk.Header);
const valueToken = new RiffChunk.ListInfoTagValue(header);
const header = await this.tokenizer.readToken<riff.IChunkHeader>(riff.Header);
const valueToken = new riff.ListInfoTagValue(header);
const value = await this.tokenizer.readToken(valueToken);
this.addTag(header.chunkID, Common.stripNulls(value));
chunkSize -= (8 + valueToken.len);
Expand Down
8 changes: 4 additions & 4 deletions src/wav/WaveChunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ export class Format implements Token.IGetToken<IWaveFormat> {
public len: number;

public constructor(header: IChunkHeader) {
assert.ok(header.size >= 16, "16 for PCM.");
this.len = header.size;
assert.ok(header.chunkSize >= 16, "16 for PCM.");
this.len = header.chunkSize;
}

public get(buf: Buffer, off: number): IWaveFormat {
Expand Down Expand Up @@ -85,8 +85,8 @@ export class FactChunk implements Token.IGetToken<IFactChunk> {
public len: number;

public constructor(header: IChunkHeader) {
assert.ok(header.size >= 4, "minimum fact chunk size.");
this.len = header.size;
assert.ok(header.chunkSize >= 4, "minimum fact chunk size.");
this.len = header.chunkSize;
}

public get(buf: Buffer, off: number): IFactChunk {
Expand Down

0 comments on commit 19284f5

Please sign in to comment.