UNPKG

soundfont2

Version:

A SoundFont2 parser for Node.js and web browsers

110 lines (95 loc) 3.38 kB
import { ParseError, RIFFChunk } from './riff'; import { MetaData, PresetData } from './types'; import { SF_INFO_CHUNKS, SF_VERSION_LENGTH } from './constants'; import { getGenerators, getInstrumentHeaders, getModulators, getPresetHeaders, getSampleHeaders, getZones } from './chunks'; export class SF2Chunk extends RIFFChunk { /** * All sub-chunks of this `SF2Chunk` as `SF2Chunk`. */ public readonly subChunks: SF2Chunk[]; public constructor(chunk: RIFFChunk) { super(chunk.id, chunk.length, chunk.buffer, chunk.subChunks); this.subChunks = chunk.subChunks.map(subChunk => new SF2Chunk(subChunk)); } /** * Get meta data from the chunk. This assumes the chunk is a LIST chunk, containing INFO * sub-chunks. */ public getMetaData(): MetaData { if (this.id !== 'LIST') { throw new ParseError('Unexpected chunk ID', `'LIST'`, `'${this.id}'`); } const info = this.subChunks.reduce<{ [key in SF_INFO_CHUNKS]?: string }>((target, chunk) => { if (chunk.id === 'ifil' || chunk.id === 'iver') { // ifil and iver length must be 4 bytes if (chunk.length !== SF_VERSION_LENGTH) { throw new ParseError(`Invalid size for the '${chunk.id}' sub-chunk`); } target[chunk.id as SF_INFO_CHUNKS] = `${chunk.getInt16()}.${chunk.getInt16(2)}`; } else { target[chunk.id as SF_INFO_CHUNKS] = chunk.getString(); } return target; }, {}); if (!info.ifil) { throw new ParseError(`Missing required 'ifil' sub-chunk`); } if (!info.INAM) { throw new ParseError(`Missing required 'INAM' sub-chunk`); } return { version: info.ifil, soundEngine: info.isng || 'EMU8000', name: info.INAM, rom: info.irom, romVersion: info.iver, creationDate: info.ICRD, author: info.IENG, product: info.IPRD, copyright: info.ICOP, comments: info.ICMT, createdBy: info.ISFT }; } /** * Get the sample data as a unsigned 8-bit buffer from the chunk. This assumes the chunk is a * LIST chunk containing a smpl sub-chunk. */ public getSampleData(): Uint8Array { if (this.id !== 'LIST') { throw new ParseError('Unexpected chunk ID', `'LIST'`, `'${this.id}'`); } const sampleChunk = this.subChunks[0]; if (sampleChunk.id !== 'smpl') { throw new ParseError('Invalid chunk signature', `'smpl'`, `'${sampleChunk.id}'`); } return sampleChunk.buffer; } /** * Get the preset data from the chunk. This assumes the chunk is a LIST chunk containing the * preset data sub-chunks. */ public getPresetData(): PresetData { if (this.id !== 'LIST') { throw new ParseError('Unexpected chunk ID', `'LIST'`, `'${this.id}'`); } return { presetHeaders: getPresetHeaders(this.subChunks[0]), presetZones: getZones(this.subChunks[1], 'pbag'), presetModulators: getModulators(this.subChunks[2], 'pmod'), presetGenerators: getGenerators(this.subChunks[3], 'pgen'), instrumentHeaders: getInstrumentHeaders(this.subChunks[4]), instrumentZones: getZones(this.subChunks[5], 'ibag'), instrumentModulators: getModulators(this.subChunks[6], 'imod'), instrumentGenerators: getGenerators(this.subChunks[7], 'igen'), sampleHeaders: getSampleHeaders(this.subChunks[8]) }; } }