vgm-conv
Version:
Chip-type and clock converter for VGM.
185 lines (184 loc) • 6.52 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.YM2413ToOPLConverter = 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");
function getModOffset(ch) {
return 8 * Math.floor(ch / 3) + (ch % 3);
}
function _R(rate) {
return rate;
}
function _KLFix(kl) {
switch (kl) {
case 0:
return 0;
case 1:
return 2;
case 2:
return 1;
default:
return 3;
}
}
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;
}
}
class YM2413ToOPLConverter extends vgm_converter_1.VGMConverter {
_regs = new Uint8Array(256).fill(0);
_buf = new vgm_write_data_buffer_1.default(256, 1);
_type;
_targetId;
_romVoices;
_lfoDepth = 3;
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._romVoices = (0, ym_voice_1.getOPLLRomVoices)(opts?.opllVariant);
}
_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(0xbd, this._lfoDepth << 6); // LFO depth
return this._buf.commit();
}
_writeVoice(ch, v, modVolume, carVolume, key) {
const modOffset = getModOffset(ch);
const carOffset = modOffset + 3;
const mod = v.slots[0];
const car = v.slots[1];
[
{
a: 0x20 + modOffset,
d: (mod.am << 7) | (mod.pm << 6) | (mod.eg << 5) | (mod.kr << 4) | mod.ml,
},
{
a: 0x20 + carOffset,
d: (car.am << 7) | (car.pm << 6) | (car.eg << 5) | (car.kr << 4) | car.ml,
},
{
a: 0x40 + modOffset,
d: (_KLFix(mod.kl) << 6) | (modVolume ? modVolume : mod.tl),
},
{
a: 0x40 + carOffset,
d: (_KLFix(car.kl) << 6) | (carVolume ? carVolume : car.tl),
},
{
a: 0x60 + modOffset,
d: (_R(mod.ar) << 4) | _R(mod.dr),
},
{
a: 0x60 + carOffset,
d: (_R(car.ar) << 4) | _R(car.dr),
},
{
a: 0x80 + modOffset,
d: (mod.sl << 4) | (!mod.eg ? _R(mod.rr) : 0),
},
{
a: 0x80 + carOffset,
d: (car.sl << 4) | _R(car.eg || key ? _R(car.rr) : _R(6)),
},
{
a: 0xc0 + ch,
d: (this._type === "ymf262" ? 0xf0 : 0) | (v.fb << 1),
},
{ a: 0xe0 + modOffset, d: mod.ws ? 1 : 0 },
{ a: 0xe0 + carOffset, d: car.ws ? 1 : 0 },
].forEach(({ a, d }) => {
this._y(a, d);
});
}
_rflag = false;
_updateVoice(ch) {
const d = this._regs[0x30 + ch];
const inst = (d & 0xf0) >> 4;
const volume = d & 0xf;
const voice = inst === 0 ? ym_voice_1.OPLLVoice.decode(this._regs) : this._romVoices[inst];
const key = this._regs[0x20 + ch] & 0x10 ? true : false;
const toTL = (vol, off) => Math.max((vol << 2) - off, 0);
if (this._rflag && 6 <= ch) {
switch (ch) {
case 6:
this._writeVoice(6, this._romVoices[16], null, toTL(volume, 0), key);
break;
case 7:
this._writeVoice(7, this._romVoices[17], toTL(inst, 0), toTL(volume, 0), key);
break;
case 8:
this._writeVoice(8, this._romVoices[18], toTL(inst, 0), toTL(volume, 0), key);
break;
}
}
else {
this._writeVoice(ch, voice, null, toTL(volume, 0), key);
}
}
_convert(cmd) {
const a = cmd.addr;
const d = cmd.data;
this._regs[a & 0xff] = d & 0xff;
if (a == 0x0e) {
if (d & 0x20 && !this._rflag) {
this._rflag = true;
this._updateVoice(6);
this._updateVoice(7);
this._updateVoice(8);
}
else if (!(d & 0x20) && this._rflag) {
this._rflag = false;
this._updateVoice(6);
this._updateVoice(7);
this._updateVoice(8);
this._y(0xbd, 0xc0 | (d & 0x3f));
}
else {
this._rflag = d & 0x20 ? true : false;
}
this._y(0xbd, (this._lfoDepth << 6) | (d & 0x3f));
}
else if (0x10 <= a && a <= 0x18) {
const ch = a & 0xf;
this._y(0xb0 + ch, ((this._regs[0x20 + ch] & 0x1f) << 1) | ((d & 0x80) >> 7));
this._y(0xa0 + ch, (d & 0x7f) << 1);
}
else if (0x20 <= a && a <= 0x28) {
const ch = a & 0xf;
this._updateVoice(ch);
this._y(0xb0 + ch, ((d & 0x1f) << 1) | ((this._regs[0x10 + ch] & 0x80) >> 7));
this._y(0xa0 + ch, (this._regs[0x10 + ch] & 0x7f) << 1);
}
else if (0x30 <= a && a <= 0x38) {
const ch = a & 0xf;
this._updateVoice(ch);
}
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.YM2413ToOPLConverter = YM2413ToOPLConverter;