vgm-conv
Version:
Chip-type and clock converter for VGM.
173 lines (172 loc) • 6.42 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SN76489ToOPNConverter = void 0;
const vgm_converter_1 = require("./vgm-converter");
const vgm_parser_1 = require("vgm-parser");
const vgm_write_data_buffer_1 = __importDefault(require("./vgm-write-data-buffer"));
const voltbl = [15, 14, 14, 13, 12, 12, 11, 10, 10, 9, 8, 8, 7, 6, 6, 0];
function fdiv2fnum(fdiv) {
if (fdiv == 0)
return 0;
const clk = 1.0;
const freq = clk / (2 * 16 * fdiv);
let fnum = Math.round((144 * freq * (1 << 20)) / clk);
let blk = 0;
while (fnum > 0x7ff) {
fnum >>= 1;
blk++;
}
blk = Math.min(7, blk);
return (blk << 11) | fnum;
}
class SN76489ToOPNConverter extends vgm_converter_1.VGMConverter {
_buf = new vgm_write_data_buffer_1.default(256, 1);
_fdiv = new Uint16Array(4);
_ch = 0;
_type = 0;
_target;
constructor(from, to, opts) {
super(from, { chip: to.chip, index: from.index, clock: to.chip === "ym2203" ? 1 : 2, relativeClock: true });
switch (to.chip) {
case "ym2612":
this._target = from.index === 0 ? 0x52 : 0xa2;
break;
case "ym2203":
this._target = from.index === 0 ? 0x55 : 0xa5;
break;
case "ym2608":
this._target = from.index === 0 ? 0x56 : 0xa6;
break;
default:
throw new Error(`Invalid chip: ${to.chip}.`);
}
}
_y(a, d) {
this._buf.push(new vgm_parser_1.VGMWriteDataCommand({
target: this._target,
index: this.from.index,
port: 0,
addr: a,
data: d
}), false);
}
_makeSQRVoice(ch) {
this._y(0x30 + ch, 0x02); // FM 1_1 DT/MULTI
this._y(0x38 + ch, 0x02); // FM 1_2 DT/MULTI
this._y(0x34 + ch, 0x02); // FM 1_3 DT/MULTI
this._y(0x3c + ch, 0x01); // FM 1_4 DT/MULTI
this._y(0x40 + ch, 0x1e); // FM 1_1 TL
this._y(0x48 + ch, 0x20); // FM 1_2 TL
this._y(0x44 + ch, 0x30); // FM 1_3 TL
this._y(0x4c + ch, 0x7f); // FM 1_4 TL
this._y(0x50 + ch, 0x1f); // FM 1_1 KS/AR
this._y(0x58 + ch, 0x1f); // FM 1_2 KS/AR
this._y(0x54 + ch, 0x1f); // FM 1_3 KS/AR
this._y(0x5c + ch, 0x1f); // FM 1_4 KS/AR
this._y(0x60 + ch, 0x00); // FM 1_1 AM/DR
this._y(0x68 + ch, 0x00); // FM 1_2 AM/DR
this._y(0x64 + ch, 0x00); // FM 1_3 AM/DR
this._y(0x6c + ch, 0x00); // FM 1_4 AM/DR
this._y(0x70 + ch, 0x00); // FM 1_1 SR
this._y(0x78 + ch, 0x00); // FM 1_2 SR
this._y(0x74 + ch, 0x00); // FM 1_3 SR
this._y(0x7c + ch, 0x00); // FM 1_4 SR
this._y(0x80 + ch, 0x00); // FM 1_1 SL/RR
this._y(0x88 + ch, 0x00); // FM 1_2 SL/RR
this._y(0x84 + ch, 0x00); // FM 1_3 SL/RR
this._y(0x8c + ch, 0x0f); // FM 1_4 SL/RR
this._y(0xb0 + ch, 0x3b); // FM 1 FB:7/ALG:3
this._y(0xb4 + ch, 0xc0); // FM 1 LR/AMS/PMS
}
getInitialCommands() {
this._y(7, 0x01c);
this._makeSQRVoice(0);
this._makeSQRVoice(1);
this._makeSQRVoice(2);
this._y(0x28, 0xf0); // KEY-ON FM 1
this._y(0x28, 0xf1); // KEY-ON FM 2
this._y(0x28, 0xf2); // KEY-ON FM 3
return this._buf.commit();
}
_convert(cmd) {
const { data } = cmd;
if (data & 0x80) {
const ch = (data >> 5) & 3;
const type = (data >> 4) & 1;
this._ch = ch;
this._type = type;
if (type) {
const v = data & 0xf;
if (ch < 3) {
const tl = v == 0xf ? 0x7f : Math.round(((data & 0xf) * 2) / 0.75 + 4);
this._y(0x4c + ch, tl);
}
else {
this._y(10, voltbl[v]);
}
}
else {
if (ch < 3) {
const new_fdiv = (this._fdiv[ch] & 0x3f0) | (data & 0xf);
const blk_fnum = fdiv2fnum(new_fdiv);
this._y(0xa4 + ch, blk_fnum >> 8);
this._y(0xa0 + ch, blk_fnum & 0xff);
this._fdiv[ch] = new_fdiv;
}
else {
const n = data & 0x3;
switch (n) {
case 0:
case 1:
case 2:
this._y(0x6, Math.min(31, 16 << n));
break;
case 3:
const fdiv = Math.min(31, Math.round((this._fdiv[2] + 1) << 2));
this._y(0x6, fdiv);
break;
}
this._fdiv[ch] = n;
}
}
}
else {
const ch = this._ch;
const type = this._type;
if (type) {
const v = data & 0xf;
if (ch < 3) {
const tl = v == 0xf ? 0x7f : Math.round(((data & 0xf) * 2) / 0.75 + 4);
this._y(0x4c + ch, tl);
}
else {
this._y(10, voltbl[v]);
}
}
else {
if (ch < 3) {
const new_fdiv = ((data & 0x3f) << 4) | (this._fdiv[ch] & 0xf);
const blk_fnum = fdiv2fnum(new_fdiv);
this._y(0xa4 + ch, blk_fnum >> 8);
this._y(0xa0 + ch, blk_fnum & 0xff);
this._fdiv[ch] = new_fdiv;
if (ch === 2 && this._fdiv[3] == 3) {
const freq = Math.min(31, Math.round((this._fdiv[2] + 1) << 2));
this._y(0x6, freq);
}
}
}
}
return this._buf.commit();
}
convertCommand(cmd) {
if (cmd instanceof vgm_parser_1.VGMWriteDataCommand && cmd.chip === "sn76489" && cmd.index == this.from.index) {
return this._convert(cmd);
}
return [cmd];
}
}
exports.SN76489ToOPNConverter = SN76489ToOPNConverter;