vgm-conv
Version:
Chip-type and clock converter for VGM.
123 lines (122 loc) • 4.97 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.OPLToOPLLConverter = 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");
class OPLToOPLLConverter extends vgm_converter_1.VGMConverter {
_regs = new Uint8Array(256);
_buf = new vgm_write_data_buffer_1.default(256, 1);
_voices = [];
constructor(from, to, opts) {
super(from, { chip: to.chip, index: from.index, clock: 1, relativeClock: true });
for (let i = 0; i < 16; i++) {
this._voices.push({ program: 3, volumeOffset: 0, octaveOffset: 0 });
}
}
_setupRhythm() {
this._y(0x16, 0x20);
this._y(0x17, 0x50);
this._y(0x18, 0xC0);
this._y(0x26, 0x05);
this._y(0x27, 0x05);
this._y(0x28, 0x01);
const v_bd = (this._regs[0x53] & 0x1f) >> 2;
const v_hh = (this._regs[0x51] & 0x1f) >> 2;
const v_sd = (this._regs[0x54] & 0x1f) >> 2;
const v_tom = (this._regs[0x52] & 0x1f) >> 2;
const v_cym = (this._regs[0x55] & 0x1f) >> 2;
this._y(0x38, v_bd);
this._y(0x39, v_hh << 4 | v_sd);
this._y(0x40, v_tom << 4 | v_cym);
}
_y(addr, data, optimize = true) {
const index = this.from.index;
this._buf.push(new vgm_parser_1.VGMWriteDataCommand({ target: vgm_parser_1.VGMWriteDataTargetId.ym2413, index, addr, data }), optimize);
}
_getVoiceArr(ch) {
const off = Math.floor(ch / 3) * 8 + (ch % 3);
return [
this._regs[0x20 + off],
this._regs[0x23 + off],
this._regs[0x40 + off],
this._regs[0x43 + off],
this._regs[0x60 + off],
this._regs[0x63 + off],
this._regs[0x80 + off],
this._regs[0x83 + off],
0x00,
this._regs[0xc0 + ch],
];
}
_applyVoice(ch) {
const opl = ym_voice_1.OPLVoice.decode(this._getVoiceArr(ch));
const enc = opl.toOPLLVoice().encode();
for (let i = 0; i < 8; i++) {
this._y(i, enc[i]);
}
}
_estimateVoice(ch) {
const opl = ym_voice_1.OPLVoice.decode(this._getVoiceArr(ch));
const inst = opl.toOPLLROMVoice();
this._voices[ch] = inst;
}
_key(ch, kon, blk, fnum) {
const vdef = this._voices[ch];
const _blk = Math.min(7, Math.max(0, blk + vdef.octaveOffset));
this._y(0x10 + ch, fnum & 0xff);
this._y(0x20 + ch, (kon << 4) | (_blk << 1) | (fnum >> 8));
}
convertCommand(cmd) {
if (cmd instanceof vgm_parser_1.VGMWriteDataCommand &&
(cmd.chip === "y8950" || cmd.chip === "ym3526" || cmd.chip === "ym3812") &&
cmd.index === this.from.index) {
this._regs[cmd.addr] = cmd.data;
if (cmd.addr == 0xbd) {
this._y(0x0e, cmd.data);
if (cmd.data & 32) {
this._setupRhythm();
}
}
else if ((0x40 <= cmd.addr && cmd.addr <= 0x55)) {
const ch = ((cmd.addr & 0x1f) >> 3) * 3 + ((cmd.addr & 0x7) % 3);
if (ch < 9) {
// TOTAL LEVEL
if ((cmd.addr & 1) === 1) {
this._estimateVoice(ch);
const vdef = this._voices[ch];
const inst = vdef.program;
const vol = Math.min(15, Math.max(0, (cmd.data & 0x3f) >> 2));
this._y(0x30 + ch, (inst << 4 | vol));
}
}
}
else if (0xc0 <= cmd.addr && cmd.addr <= 0xc8) {
const ch = cmd.addr & 0xf;
if (ch < 9) {
this._estimateVoice(ch);
const vdef = this._voices[ch];
const inst = vdef.program;
const vol = Math.min(15, Math.max(0, (cmd.data & 0x3f) >> 2));
this._y(0x30 + ch, (inst << 4 | vol));
}
}
else if ((0xa0 <= cmd.addr && cmd.addr <= 0xa8) || (0xb0 <= cmd.addr && cmd.addr <= 0xb8)) {
// KEYON - BLOCK - FNUM
const rflag = (this._regs[0xbd] & 32);
const ch = cmd.addr & 0xf;
if (ch < 9) {
let blkfnum = (this._regs[0xb0 + ch] << 8 | this._regs[0xa0 + ch]) >> 1;
this._key(ch, (blkfnum >> 12) & 1, (blkfnum >> 9) & 7, blkfnum & 0x1ff);
}
}
return this._buf.commit();
}
return [cmd];
}
}
exports.OPLToOPLLConverter = OPLToOPLLConverter;