vgm-conv
Version:
Chip-type and clock converter for VGM.
256 lines (255 loc) • 9.36 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.YM2203ToOPLConverter = void 0;
const vgm_converter_1 = require("./vgm-converter");
const vgm_write_data_buffer_1 = __importDefault(require("./vgm-write-data-buffer"));
const vgm_parser_1 = require("vgm-parser");
const ym_voice_1 = require("ym-voice");
function type2target(type) {
switch (type) {
case "ym3526":
return vgm_parser_1.VGMWriteDataTargetId.ym3526; // 0x5b;
case "y8950":
return vgm_parser_1.VGMWriteDataTargetId.y8950; // 0x5c;
case "ymf262":
return vgm_parser_1.VGMWriteDataTargetId.ymf262_p0; // 0x5e;
case "ym3812":
default:
return vgm_parser_1.VGMWriteDataTargetId.ym3812; // 0x5a;
}
}
// prettier-ignore
const muteVoice = new ym_voice_1.OPLVoice({
fb: 7, con: 0,
slots: [
{
am: 0, pm: 0, eg: 1, kr: 0, ml: 2,
kl: 0, tl: 26,
ar: 15, dr: 0, sl: 0, rr: 0,
ws: 0
},
{
am: 0, pm: 0, eg: 0, kr: 0, ml: 2,
kl: 0, tl: 63,
ar: 0, dr: 0, sl: 0, rr: 15,
ws: 0
}
]
});
class YM2203ToOPLConverter extends vgm_converter_1.VGMConverter {
_regs = new Uint8Array(256);
_buf = new vgm_write_data_buffer_1.default(256, 1);
_type;
_targetId;
_keyStatus = [false, false, false, false, false, false]; // 0..2: FM1-3, 3..5: CH-3 SLOT
_toClock;
_ssgAttenuation = 0;
constructor(from, to, opts) {
super(from, { chip: to.chip, index: from.index, clock: to.chip === "ymf262" ? 4 : 1, relativeClock: true });
this._type = to.chip;
this._targetId = type2target(this._type);
this._toClock = this.convertedChipInfo.clock;
if (opts.ssgAttenuation != null) {
this._ssgAttenuation = opts.ssgAttenuation;
}
}
_y(addr, data, optimize = true) {
const index = this.from.index;
this._buf.push(new vgm_parser_1.VGMWriteDataCommand({ target: this._targetId, index, addr, data }), optimize);
}
getInitialCommands() {
this._y(0x01, 0x20); // YM3812 mode
this._y(0x0e, 0x00); // no rhythm
return this._buf.commit();
}
_setVoice(opl_ch, v) {
const b = v.encode();
const o = Math.floor(opl_ch / 3) * 8 + (opl_ch % 3);
this._y(0x20 + o, b[0]);
this._y(0x23 + o, b[1]);
this._y(0x40 + o, b[2]);
this._y(0x43 + o, b[3]);
this._y(0x60 + o, b[4]);
this._y(0x63 + o, b[5]);
this._y(0x80 + o, b[6]);
this._y(0x83 + o, b[7]);
this._y(0xe0 + o, b[8]);
this._y(0xe3 + o, b[9]);
this._y(0xc0 + opl_ch, b[10]);
}
_updateVoice(ch) {
const key = this._keyStatus[ch];
const regs = this._regs;
// prettier-ignore
const opnVoice = ym_voice_1.OPNVoice.decode([
regs[0x30 + ch], regs[0x38 + ch], regs[0x34 + ch], regs[0x3c + ch],
regs[0x40 + ch], regs[0x48 + ch], regs[0x44 + ch], regs[0x4c + ch],
regs[0x50 + ch], regs[0x58 + ch], regs[0x54 + ch], regs[0x5c + ch],
regs[0x60 + ch], regs[0x68 + ch], regs[0x64 + ch], regs[0x6c + ch],
regs[0x70 + ch], regs[0x78 + ch], regs[0x74 + ch], regs[0x7c + ch],
regs[0x80 + ch], regs[0x88 + ch], regs[0x84 + ch], regs[0x8c + ch],
regs[0x90 + ch], regs[0x98 + ch], regs[0x94 + ch], regs[0x9c + ch],
regs[0xb0 + ch], regs[0xb4 + ch]
]);
const oplVoices = opnVoice.toOPL(key);
this._setVoice(ch * 2, oplVoices[0]);
this._setVoice(ch * 2 + 1, oplVoices[1]);
}
_updateKeyBlkFnum(ch) {
const key = this._keyStatus[ch];
this._updateVoice(ch);
const blk_fnum = ((this._regs[0xa4 + ch] << 8) | this._regs[0xa0 + ch]) >> 1;
const dl = blk_fnum & 0xff;
const dh = ((blk_fnum >> 8) & 0x1f) | (key ? 0x20 : 0);
this._y(0xa0 + ch * 2, dl);
this._y(0xa0 + ch * 2 + 1, dl);
this._y(0xb0 + ch * 2, dh);
this._y(0xb0 + ch * 2 + 1, dh);
}
_updateSSGTone(ch) {
const t = ((1 << ch) & this._regs[0x7]) === 0;
const n = ((8 << ch) & this._regs[0x7]) === 0;
const v = this._regs[0x08 + ch];
const vol = v & 0x10 ? 0 : v & 0xf;
const tl = Math.max(0, Math.min(63, [63, 62, 56, 52, 46, 42, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0][vol & 0xf] + this._ssgAttenuation));
const np = this._regs[0x06] & 0x1f;
let ssgVoice;
if (t && !n) {
ssgVoice = new ym_voice_1.OPLVoice({
fb: 7,
con: 0,
slots: [
{ am: 0, pm: 0, eg: 1, kr: 0, ml: 2, kl: 0, tl: 27, ar: 15, dr: 0, sl: 0, rr: 15, ws: 0 },
{ am: 0, pm: 0, eg: 1, kr: 0, ml: 1, kl: 0, tl: tl, ar: 15, dr: 0, sl: 0, rr: 15, ws: 0 },
],
});
}
else if (!t && n) {
ssgVoice = new ym_voice_1.OPLVoice({
fb: 7,
con: 0,
slots: [
{ am: 0, pm: 0, eg: 1, kr: 0, ml: np >> 1, kl: 0, tl: np >> 4, ar: 15, dr: 0, sl: 0, rr: 15, ws: 0 },
{ am: 0, pm: 0, eg: 1, kr: 0, ml: np >> 1, kl: 0, tl: tl, ar: 15, dr: 0, sl: 0, rr: 15, ws: 0 },
],
});
}
else if (t && n) {
ssgVoice = new ym_voice_1.OPLVoice({
fb: 7,
con: 0,
slots: [
{ am: 0, pm: 0, eg: 1, kr: 0, ml: 2, kl: 0, tl: 27, ar: 15, dr: 0, sl: 0, rr: 15, ws: 0 },
{ am: 0, pm: 0, eg: 1, kr: 0, ml: 1, kl: 0, tl: tl, ar: 15, dr: 0, sl: 0, rr: 15, ws: 0 },
],
});
}
else {
ssgVoice = new ym_voice_1.OPLVoice({
fb: 7,
con: 0,
slots: [
{ am: 0, pm: 0, eg: 1, kr: 0, ml: 2, kl: 0, tl: 27, ar: 15, dr: 0, sl: 0, rr: 15, ws: 0 },
{ am: 0, pm: 0, eg: 1, kr: 0, ml: 1, kl: 0, tl: 63, ar: 15, dr: 0, sl: 0, rr: 15, ws: 0 },
],
});
}
this._setVoice(ch + 6, ssgVoice);
}
_convertSSG(cmd) {
const a = cmd.addr;
const d = cmd.data;
this._regs[a & 0xff] = d & 0xff;
if (a <= 0x05) {
const ch = a >> 1;
const tp = (this._regs[ch * 2 + 1] << 8) | this._regs[ch * 2];
const freq = this.from.clock / (32 * tp);
let fnum = Math.floor((freq << 19) / (this.from.clock / 72));
let blk = 1;
while (fnum > 1023) {
fnum >>= 1;
blk++;
}
if (blk > 7)
blk = 7;
const dl = fnum & 0xff;
const dh = 0x20 | (blk << 2) | (fnum >> 8);
this._y(0xa6 + ch, dl);
this._y(0xb6 + ch, dh);
}
if (a === 0x06 || a === 0x07) {
this._updateSSGTone(0);
this._updateSSGTone(1);
this._updateSSGTone(2);
}
if (0x08 <= a && a <= 0x0a) {
this._updateSSGTone(a - 0x08);
}
return this._buf.commit();
}
_convertFM(cmd) {
const a = cmd.addr;
const d = cmd.data;
this._regs[a & 0xff] = d & 0xff;
if (a === 0x28) {
const ch = d & 3;
if (ch < 3) {
const key = d & 0xf0 ? true : false;
this._keyStatus[ch] = key;
this._updateKeyBlkFnum(ch);
}
}
else if (0x30 <= a && a < 0xa0) {
const ch = a & 3;
if (ch < 3) {
this._updateVoice(ch);
}
}
else if (0xa0 <= a && a < 0xa4) {
// F-Num 1
const ch = a - 0xa0;
if (ch < 3) {
this._updateKeyBlkFnum(ch);
}
}
else if (0xa4 <= a && a < 0xa8) {
// F-Num 2
const ch = a - 0xa4;
if (ch < 3) {
this._updateKeyBlkFnum(ch);
}
}
else if (0xb0 <= a && a < 0xb4) {
// FB/CON
const ch = a - 0xb0;
if (ch < 3) {
this._updateVoice(ch);
}
}
else if (0xb4 <= a && a < 0xb8) {
// LR/AMS/PMS
}
return this._buf.commit();
}
convertCommand(cmd) {
const convertFM = this.from.subModule == null || this.from.subModule === "fm";
const convertSSG = this.from.subModule == null || this.from.subModule === "ssg";
if (cmd instanceof vgm_parser_1.VGMWriteDataCommand && cmd.chip === "ym2203" && cmd.index === this.from.index) {
if (cmd.addr < 0x10) {
if (convertSSG) {
return this._convertSSG(cmd);
}
}
else {
if (convertFM) {
return this._convertFM(cmd);
}
}
}
return [cmd];
}
}
exports.YM2203ToOPLConverter = YM2203ToOPLConverter;