vgm-conv
Version:
Chip-type and clock converter for VGM.
152 lines (151 loc) • 5.97 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.YM2612ToYM2413Converter = void 0;
const opn_to_ym2413_converter_1 = require("./opn-to-ym2413-converter");
const vgm_parser_1 = require("vgm-parser");
const ym2413_dac_table_1 = require("./ym2413-dac-table");
class YM2612ToYM2413Converter extends opn_to_ym2413_converter_1.OPNToYM2413Converter {
rootPcmChannel = 6;
_dacEmulation = "fmpcm";
constructor(from, to, opts) {
super(from, { chip: "ym2413", index: from.index, clock: 1 / 2, relativeClock: true }, opts);
this._dacEmulation = this.opts.dacEmulation ||
this.opts.voiceTable?.opn2opll?.dacEmulation ||
this._dacEmulation;
}
getInitialCommands() {
if (this._dacEmulation === "test") {
// select FM 9-ch mode
this._y(14, 0);
this._y(15, 4);
// Set saw wave to user-defined voice.
this._y(1, 0x2c);
this._y(0, 0x2c);
this._y(2, 0x28);
this._y(3, 0x07);
this._y(4, 0xf0);
this._y(5, 0xf0);
this._y(6, 0x0f);
this._y(7, 0x0f);
this._y(48 + this.rootPcmChannel, 0, false);
this._y(49 + this.rootPcmChannel, 0, false);
this._y(50 + this.rootPcmChannel, 0, false);
this._y(32 + this.rootPcmChannel, 0x1e, false);
this._y(33 + this.rootPcmChannel, 0x1e, false);
this._y(34 + this.rootPcmChannel, 0x1e, false);
return this._buf.commit();
}
else if (this._dacEmulation === "fmpcm") {
// select FM 9-ch mode
this._y(14, 0);
// make sure all channels key-off
this._y(37, 0, false);
this._y(38, 0, false);
this._y(39, 0, false);
this._y(40, 0, false);
// select violin tone whose modulator AR is 15.
this._y(53, (1 << 4) | 15, false);
this._y(54, (1 << 4) | 15, false);
this._y(55, (1 << 4) | 15, false);
this._y(56, (1 << 4) | 15, false);
// set f-number fnum=1 is the most accurate but need longer wait time.
const fnum = 32;
this._y(21, fnum, false);
this._y(22, fnum, false);
this._y(23, fnum, false);
this._y(24, fnum, false);
// start phase generator
this._y(37, 16, false);
this._y(38, 16, false);
this._y(39, 16, false);
this._y(40, 16, false);
const goalClock = this.to.relativeClock ? (this.from.clock * this.to.clock) : (this.to.clock || 3579545);
// wait until 1/4 cycle of phase generator
const freq = (fnum * goalClock) / 72 / (1 << 19);
const cycleInSeconds = 1.0 / freq;
const nnnn = Math.round(44100 * (cycleInSeconds / 4));
this._buf.push(new vgm_parser_1.VGMWaitWordCommand({ count: nnnn }), false);
// stop phase generator
this._y(21, 0, false);
this._y(22, 0, false);
this._y(23, 0, false);
this._y(24, 0, false);
return this._buf.commit();
}
else {
return super.getInitialCommands();
}
}
_pcmData = new Uint8Array();
_pcmIndex = 0;
convert2A(cmd) {
const v = this._pcmData[this._pcmIndex];
this._pcmIndex++;
if (this.opts.decimation <= 1 || this._div % this.opts.decimation == 0) {
if (this._dacEmulation === "test") {
const vv = 47 + Math.round((208 * v) / 255);
this._y(16 + this.rootPcmChannel, vv, false);
this._y(17 + this.rootPcmChannel, vv, false);
this._y(18 + this.rootPcmChannel, vv, false);
}
else if (this._dacEmulation === "fmpcm") {
const idx = Math.min(768, v * 4) & 0x3f8;
const vs = ym2413_dac_table_1.YM2413DACTable[idx];
for (let i = 0; i < 3; i++) {
this._y(56 - i, (8 << 4) | vs[2 - i], false);
}
}
}
if (1 <= cmd.count) {
this._buf.push(new vgm_parser_1.VGMWaitNibbleCommand({ count: cmd.count }));
}
this._div++;
return this._buf.commit();
}
convertSeekPCM(cmd) {
this._pcmIndex = cmd.offset;
return [];
}
convertDataBlock(cmd) {
if (cmd.chip === "ym2612") {
this._pcmData = cmd.blockData;
return [];
}
return [cmd];
}
convertCommand(cmd) {
const convertFM = this.from.subModule == null || this.from.subModule === "fm";
const convertDAC = this.from.subModule == null || this.from.subModule === "dac";
if (cmd instanceof vgm_parser_1.VGMWriteDataCommand && cmd.chip === "ym2612" && cmd.index === this.from.index) {
if (convertFM) {
if (this.from.subModule == "fm" && cmd.addr == 0x2b) {
return [cmd];
}
if (this._dacEmulation != "test") {
return this.convertFM(cmd);
}
return [];
}
}
else if (cmd instanceof vgm_parser_1.VGMDataBlockCommand && cmd.chip === "ym2612") {
if (convertDAC) {
return this.convertDataBlock(cmd);
}
}
else if (cmd instanceof vgm_parser_1.VGMWrite2ACommand) {
if (convertDAC) {
return this.convert2A(cmd);
}
}
else if (cmd instanceof vgm_parser_1.VGMSeekPCMCommand) {
if (convertDAC) {
return this.convertSeekPCM(cmd);
}
}
else {
return super.convertCommand(cmd);
}
return [cmd];
}
}
exports.YM2612ToYM2413Converter = YM2612ToYM2413Converter;