UNPKG

vgm-conv

Version:
196 lines (195 loc) 8.31 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.YM2413ToYM2608Converter = 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 ym_voice_1 = require("ym-voice"); /* level key scaling table */ const KSLTable = [0, 24, 32, 37, 40, 43, 45, 47, 48, 50, 51, 52, 53, 54, 55, 56]; class YM2413ToYM2608Converter extends vgm_converter_1.VGMConverter { constructor(from, to, opts) { super(from, { chip: "ym2608", index: from.index, clock: 2, relativeClock: true }); this._romVoices = (0, ym_voice_1.getOPLLRomVoices)(opts?.opllVariant).map((e) => { return { opll: e, opn: e.toOPN() }; }); this._userVoice = this._romVoices[0]; this._voiceMap = [ this._romVoices[0], this._romVoices[0], this._romVoices[0], this._romVoices[0], this._romVoices[0], this._romVoices[0], ]; } _regs = new Uint8Array(256).fill(0); _buf = new vgm_write_data_buffer_1.default(256, 2); _userVoice; _romVoices; _voiceMap; _y(port, addr, data, optimize = true) { let target; if (port == 0) { target = this.from.index == 0 ? vgm_parser_1.VGMWriteDataTargetId.ym2608_p0 : vgm_parser_1.VGMWriteDataTargetId.ym2608_2_p0; } else { target = this.from.index == 0 ? vgm_parser_1.VGMWriteDataTargetId.ym2608_p1 : vgm_parser_1.VGMWriteDataTargetId.ym2608_2_p1; } const index = this.from.index; this._buf.push(new vgm_parser_1.VGMWriteDataCommand({ target, index, port, addr, data }), optimize); } getInitialCommands() { this._y(0, 0x22, 0x0b); // LFO ON 6.02Hz this._y(0, 0x29, 0x80); // enable FM 4-6ch this._y(0, 0x11, 0x38); // Rhythm TL this._y(0, 0x18, 0xdf); // bd TL this._y(0, 0x19, 0xdf); // sd TL this._y(0, 0x1a, 0xdf); // top TL this._y(0, 0x1b, 0xdf); // hh TL this._y(0, 0x1c, 0xdf); // tom TL this._y(0, 0x1d, 0xdf); // rym TL return this._buf.commit(); } _setSlotVolume(port, nch, blk_fnum, volume) { const ch = port * 3 + nch; const fnum_h = (blk_fnum >> 5) & 15; const oct = (blk_fnum >> 9) & 7; const voice = this._voiceMap[ch]; /* level key scale emulation */ const kll = Math.max(0, KSLTable[fnum_h] - 8 * (7 - oct)); const tll = (vl, slot, kl) => { return Math.min(127, vl + slot.tl + (kl ? kll >> (3 - kl) : 0)); }; this._y(port, 0x40 + nch, tll(0, voice.opn.slots[0], voice.opll.slots[0].kl)); this._y(port, 0x44 + nch, tll(0, voice.opn.slots[1], 0)); this._y(port, 0x48 + nch, tll(0, voice.opn.slots[2], 0)); this._y(port, 0x4c + nch, tll(volume << 2, voice.opn.slots[3], voice.opll.slots[1].kl)); } _setInstVolume(port, nch, iv) { const inst = iv >> 4; const volume = iv & 15; const ch = port * 3 + nch; const voice = inst === 0 ? this._userVoice : this._romVoices[inst]; this._voiceMap[ch] = voice; const opnVoice = voice.opn; this._y(port, 0xb0 + nch, (opnVoice.fb << 3) | opnVoice.con); // YM2413's amplitude LFO depth is 4.5dB. // Set YM2608's AMS to 2 here for LFO depth to 5.9dB. const ams = 2; this._y(port, 0xb4 + nch, 0xc0 | (ams << 4) | opnVoice.pms); for (let i = 0; i < 4; i++) { this._y(port, 0x30 + i * 4 + nch, (opnVoice.slots[i].dt << 4) | opnVoice.slots[i].ml); this._y(port, 0x50 + i * 4 + nch, (opnVoice.slots[i].ks << 6) | opnVoice.slots[i].ar); this._y(port, 0x60 + i * 4 + nch, (opnVoice.slots[i].am << 7) | opnVoice.slots[i].dr); this._y(port, 0x70 + i * 4 + nch, opnVoice.slots[i].sr); this._y(port, 0x80 + i * 4 + nch, (opnVoice.slots[i].sl << 4) | opnVoice.slots[i].rr); } const blk_fnum = ((this._regs[0x20 + ch] & 0xf) << 8) | this._regs[0x10 + ch]; this._setSlotVolume(port, nch, blk_fnum, volume); } _convert(cmd) { const a = cmd.addr; const d = cmd.data; if (a < 0x08) { this._regs[a] = d; const opllVoice = ym_voice_1.OPLLVoice.decode(this._regs); this._userVoice = { opll: opllVoice, opn: opllVoice.toOPN() }; for (let ch = 0; ch < 6; ch++) { const iv = this._regs[0x30 + ch]; if (iv >> 4 === 0) { const port = ch < 3 ? 0 : 1; const nch = (3 <= ch ? ch + 1 : ch) & 3; this._setInstVolume(port, nch, iv); } } } else if (a === 0x0e) { if (d & 32) { const prev = this._regs[0x0e]; const hh = ~prev & d & 1; const top = (~prev & d & 2) >> 1; const tom = (~prev & d & 4) >> 2; const sd = (~prev & d & 8) >> 3; const bd = (~prev & d & 16) >> 4; this._y(0, 0x10, (tom << 4) | (hh << 3) | (top << 2) | (sd << 1) | bd, false); } else { this._y(0, 0x10, 0xff, false); } this._regs[a] = d; } else if (0x10 <= a && a < 0x16) { // F-Num 1 const ch = a - 0x10; const port = ch < 3 ? 0 : 1; const nch = (3 <= ch ? ch + 1 : ch) & 3; const al = 0xa0 + nch; const ah = 0xa4 + nch; const blk_fnum = ((this._regs[0x20 + ch] & 0xf) << 8) | d; this._y(port, ah, blk_fnum >> 6, false); this._y(port, al, (blk_fnum << 2) & 0xff, false); this._regs[a] = d; this._setSlotVolume(port, nch, blk_fnum, this._regs[0x30 + ch] & 0xf); } else if (0x20 <= a && a < 0x26) { // BLOCK & F-Num 2 const ch = a - 0x20; const port = ch < 3 ? 0 : 1; const nch = (3 <= ch ? ch + 1 : ch) & 3; const al = 0xa0 + nch; const blk_fnum = ((d & 0xf) << 8) | this._regs[0x10 + ch]; const ah = 0xa4 + nch; const prevKey = (this._regs[0x20 + ch] >> 4) & 1; const nextKey = (d >> 4) & 1; this._y(port, ah, blk_fnum >> 6, false); this._y(port, al, (blk_fnum << 2) & 0xff, false); this._setSlotVolume(port, nch, blk_fnum, this._regs[0x30 + ch] & 0xf); if (prevKey != nextKey) { this._y(0, 0x28, (nextKey ? 0xf0 : 0) | (port * 4 + nch), false); } this._regs[a] = d; } else if (0x30 <= a && a < 0x36) { // INST & VOLUME const ch = a - 0x30; const port = ch < 3 ? 0 : 1; const nch = (3 <= ch ? ch + 1 : ch) & 3; this._setInstVolume(port, nch, d); this._regs[a] = d; } else if (a === 0x36) { const bd_vol = 15 - (d & 0xf); this._y(0, 0x18, 0xc0 | (bd_vol << 1)); this._regs[a] = d; } else if (a === 0x37) { const sd_vol = 15 - (d & 0xf); const hh_vol = 15 - (d >> 4); this._y(0, 0x19, 0xc0 | (sd_vol << 1)); this._y(0, 0x1b, 0xc0 | hh_vol); this._regs[a] = d; } else if (a === 0x38) { const top_vol = 15 - (d & 0xf); const tom_vol = 15 - (d >> 4); this._y(0, 0x1a, 0xc0 | (top_vol << 1)); this._y(0, 0x1c, 0xc0 | Math.round(tom_vol * 1.5)); this._regs[a] = d; } else { this._regs[a] = d; } return this._buf.commit(); } convertCommand(cmd) { if (cmd instanceof vgm_parser_1.VGMWriteDataCommand && cmd.chip === "ym2413" && cmd.index === this.from.index) { return this._convert(cmd); } return [cmd]; } } exports.YM2413ToYM2608Converter = YM2413ToYM2608Converter;