UNPKG

opll2opl

Version:
267 lines (233 loc) 7.17 kB
import VGM from "./vgm"; import { InputBuffer, OutputBuffer } from "./buffer"; import PSG2OPL from "./psg2opl"; import OPLL2OPL from "./opll2opl"; import OPLType from "./opl-type"; function toOPLType(type: string): OPLType | null { switch (type) { case "opl2": case "ym3812": return "ym3812"; case "opl": case "ym3526": return "ym3526"; case "y8950": return "y8950"; case "opl3": case "ymf262": return "ymf262"; default: return null; } } export default class Converter { private _vgm: VGM; private _data: InputBuffer; private _output: OutputBuffer; private _orgLoopOffset: number; // original loop offset from top of the data. private _newLoopOffset: number; // new loop offset from top of the data. private _psg2opl?: PSG2OPL; private _opll2opl?: OPLL2OPL; private _opllTo: string; private _psgTo: string; private _oplClock: number; constructor(vgm: VGM, opllTo: string, psgTo: string) { this._vgm = vgm; this._orgLoopOffset = this._vgm.header.offsets.loop - this._vgm.header.offsets.data; this._newLoopOffset = 0; this._data = new InputBuffer(vgm.data.buffer); this._output = new OutputBuffer(new ArrayBuffer(8)); this._opllTo = opllTo; this._psgTo = psgTo; this._oplClock = vgm.header.chips.ym2413 ? vgm.header.chips.ym2413.clock : 3579545; const psgToType = toOPLType(this._psgTo); if (psgToType) { this._psg2opl = new PSG2OPL( psgToType, vgm.header.chips.ay8910 ? vgm.header.chips.ay8910.clock : 3579545 / 2, this._oplClock ); } const opllToType = toOPLType(this._opllTo); if (opllToType) { this._opll2opl = new OPLL2OPL( opllToType, vgm.header.chips.ym2413 ? vgm.header.chips.ym2413.clock : 3579545, this._oplClock ); } } private _processGameGearPsg() { const d = this._data.readByte(); this._output.writeByte(d); } private _processSn76489() { const d = this._data.readByte(); this._output.writeByte(d); } private _processYm2413() { if (!this._opll2opl) return; this._initializeOPL3(); const a = this._data.readByte(); const d = this._data.readByte(); const data = this._opll2opl.interpret(a, d); for (let i = 0; i < data.length; i++) { const { a, d } = data[i]; this._output.writeByte(this._opll2opl.command); this._output.writeByte(a); this._output.writeByte(d); } } _opl3initialized = false; private _initializeOPL3() { if (this._opllTo === "ymf262") { if (!this._opl3initialized) { this._output.writeByte(0x5f); this._output.writeByte(0x05); this._output.writeByte(0x01); this._opl3initialized = true; } } } private _processAY8910() { if (!this._psg2opl) return; this._initializeOPL3(); const a = this._data.readByte(); const d = this._data.readByte(); const data = this._psg2opl.interpret(a, d); for (let i = 0; i < data.length; i++) { const { a, d } = data[i]; this._output.writeByte(this._psg2opl.command); this._output.writeByte(a); this._output.writeByte(d); } } private _processCommon() { const a = this._data.readByte(); const d = this._data.readByte(); this._output.writeByte(a); this._output.writeByte(d); } private _processCommon2() { const p = this._data.readByte(); const a = this._data.readByte(); const d = this._data.readByte(); this._output.writeByte(p); this._output.writeByte(a); this._output.writeByte(d); } private _processSeekPcmDataBank() { this._output.writeDword(this._data.readDword()); } private _processDataBlock() { if (this._data.readByte() != 0x66) { throw new Error(); } this._output.writeByte(0x66); const type = this._data.readByte(); const size = this._data.readDword(); this._output.writeByte(type); this._output.writeDword(size); for (let i = 0; i < size; i++) { this._output.writeByte(this._data.readByte()); } } private _detectNewLoopOffset() { if (0 <= this._orgLoopOffset && this._newLoopOffset === 0) { if (this._orgLoopOffset <= this._data.readOffset) { this._newLoopOffset = this._output.writeOffset; } } } private _buildVGM() { const dataLength = this._output.writeOffset; const vgm = new VGM(this._vgm.header, this._output.buffer); vgm.header.version = 0x171; vgm.header.offsets.data = 0x100; if (this._newLoopOffset) { vgm.header.offsets.loop = vgm.header.offsets.data + this._newLoopOffset; } else { vgm.header.offsets.loop = 0; } vgm.header.offsets.eof = vgm.header.offsets.data + dataLength; if (this._opllTo === "none") { vgm.header.chips.ym2413 = undefined; } else if (this._opll2opl) { vgm.header.chips[this._opll2opl.type] = { clock: this._opll2opl.clock, }; vgm.header.chips.ym2413 = undefined; } if (this._psgTo === "none") { vgm.header.chips.ay8910 = undefined; } else if (this._psg2opl) { vgm.header.chips[this._psg2opl.type] = { clock: this._psg2opl.clock, }; vgm.header.chips.ay8910 = undefined; } return vgm.build(); } convert() { while (!this._data.eod) { const d = this._data.readByte(); if (d == 0x67) { this._output.writeByte(d); this._processDataBlock(); } else if (d == 0x61) { this._output.writeByte(d); this._output.writeWord(this._data.readWord()); } else if (d == 0x62) { this._output.writeByte(d); } else if (d == 0x63) { this._output.writeByte(d); } else if (d == 0x4f) { this._output.writeByte(d); this._processGameGearPsg(); } else if (d == 0x50) { this._output.writeByte(d); this._processSn76489(); } else if (d == 0x51) { if (this._opll2opl) { this._processYm2413(); } else { this._output.writeByte(d); this._processCommon(); } } else if (0x52 <= d && d <= 0x5f) { this._output.writeByte(d); this._processCommon(); } else if (d == 0xa0) { if (this._psg2opl) { this._processAY8910(); } else { this._output.writeByte(d); this._processCommon(); } } else if (0xb0 <= d && d <= 0xbf) { this._output.writeByte(d); this._processCommon(); } else if (0xd0 <= d && d <= 0xd6) { this._output.writeByte(d); this._processCommon2(); } else if (d == 0xe0) { this._output.writeByte(d); this._processSeekPcmDataBank(); } else if (0x70 <= d && d <= 0x7f) { this._output.writeByte(d); } else if (0x80 <= d && d <= 0x8f) { this._output.writeByte(d); } else if (d == 0x66) { this._output.writeByte(d); break; } else { throw new Error("Unsupported command: 0x" + d.toString(16)); } this._detectNewLoopOffset(); } return this._buildVGM(); } }