UNPKG

vgm-conv

Version:
136 lines (135 loc) 5.44 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AY8910ToOPMConverter = 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 opm_freq_1 = require("./opm_freq"); const VOL_TO_TL = [127, 62, 56, 52, 46, 42, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0]; const N_VOL_TO_TL = [127, 126, 125, 124, 123, 122, 121, 120, 116, 112, 105, 96, 82, 64, 37, 0]; const OPM_CH_BASE = 4; function toOpmCh(psgCh) { return psgCh + OPM_CH_BASE; } class AY8910ToOPMConverter extends vgm_converter_1.VGMConverter { _regs = new Uint8Array(256).fill(0); _buf = new vgm_write_data_buffer_1.default(256, 1); _fdiv; _clockRatio; _whiteNoiseAttenuation; _squareWaveAttenuation; constructor(from, to, opts) { super(from, to); this._fdiv = from.chip == "ay8910" ? 2 : 4; this._clockRatio = 3579545 / this.convertedChipInfo.clock; const mainAttenuation = opts.mainAttenuation ?? 0; this._whiteNoiseAttenuation = mainAttenuation + (opts.whiteNoiseAttenuation ?? 0); this._squareWaveAttenuation = mainAttenuation + (opts.squareWaveAttenuation ?? 8); } _y(addr, data, optimize = true) { const index = this.from.index; this._buf.push(new vgm_parser_1.VGMWriteDataCommand({ target: vgm_parser_1.VGMWriteDataTargetId.ym2151, index, addr, data }), optimize); } getInitialCommands() { // PSG TONE for (let opmCh = OPM_CH_BASE; opmCh < OPM_CH_BASE + 3; opmCh++) { this._y(0x20 + opmCh, 0xfc); // RL=ON FB=7 CON=4 this._y(0x40 + opmCh, 0x02); // M1: DT=0 ML=2 this._y(0x50 + opmCh, 0x01); // C1: DT=0 ML=1 this._y(0x60 + opmCh, 0x1b); // M1: TL=27 this._y(0x70 + opmCh, 0x7f); // C1: TL=127 (mute) this._y(0x80 + opmCh, 0x1f); // M1: AR=31 this._y(0x90 + opmCh, 0x1f); // C1: AR=31 this._y(0xa0 + opmCh, 0); // M1: DR=0 this._y(0xb0 + opmCh, 0); // C1: DR=0 this._y(0xc0 + opmCh, 0); // M1: DT2=0 SR=0 this._y(0xd0 + opmCh, 0); // C1: DT2=0 SR=0 this._y(0xe0 + opmCh, 0); // M1: SL=0 RR=0 this._y(0xf0 + opmCh, 0); // C1: SL=0 RR=0 // KEY ON this._y(0x08, (0xf << 3) | opmCh, false); } // for Noise Slot (slot#32) { const opmCh = 7; this._y(0x20 + opmCh, 0xfc); // RL=ON FB=7 CON=4 this._y(0x58 + opmCh, 0x00); // C2: DT=0 ML=0 this._y(0x78 + opmCh, 0x7f); // C2: TL=127 (mute) this._y(0x98 + opmCh, 0x1f); // C2: AR=31 this._y(0xb8 + opmCh, 0); // C2: DR=0 this._y(0xd8 + opmCh, 0); // C2: DT2=0 SR=0 this._y(0xf8 + opmCh, 0); // C2: SL=0 RR=0 this._y(0x08, (0x8 << 3) | opmCh, false); // slot32 only } return this._buf.commit(); } _updateFreq(ch, freq) { const note = (0, opm_freq_1.freqToOPMNote)(freq, this._clockRatio / this._fdiv); const opmCh = toOpmCh(ch); this._y(0x28 + opmCh, note.kc); this._y(0x30 + opmCh, note.kf << 2); } _nfreq = 0; _updateNoise() { let nVol = 0; for (let i = 0; i < 3; i++) { if ((this._regs[0x7] & (0x8 << i)) == 0) { nVol = Math.max(nVol, this._regs[0x8 + i] & 0xf); } } this._y(0x0f, 0x80 | (0x1f - this._nfreq)); this._y(0x7f, Math.min(127, N_VOL_TO_TL[nVol] + this._whiteNoiseAttenuation)); // SLOT32 } _updateTone(ch) { const t = ((1 << ch) & this._regs[0x7]) == 0; const opmCh = toOpmCh(ch); if (t) { const v = this._regs[0x08 + ch]; const tVol = v & 0x10 ? 0 : v & 0xf; this._y(0x70 + opmCh, Math.min(127, VOL_TO_TL[tVol & 0xf] + this._squareWaveAttenuation)); } else { this._y(0x70 + opmCh, 0x7f); } } _convert(cmd) { const { addr, data } = cmd; this._regs[addr & 0xff] = data & 0xff; if (addr <= 0x05) { const ch = addr >> 1; const tp = (this._regs[ch * 2 + 1] << 8) | this._regs[ch * 2]; if (tp == 0) { this._updateFreq(ch, 0); } else { const freq = this.from.clock / (16 * tp); this._updateFreq(ch, freq); } } else if (0x08 <= addr && addr <= 0x0a) { this._updateTone(addr - 0x08); this._updateNoise(); } else if (addr === 0x06) { this._nfreq = this._regs[0x06] & 0x1f; this._updateNoise(); } else if (addr === 0x07) { this._updateTone(0); this._updateTone(1); this._updateTone(2); this._updateNoise(); } return this._buf.commit(); } convertCommand(cmd) { if (cmd instanceof vgm_parser_1.VGMWriteDataCommand && cmd.chip === this.from.chip && cmd.index === this.from.index) { return this._convert(cmd); } return [cmd]; } } exports.AY8910ToOPMConverter = AY8910ToOPMConverter;