-
Notifications
You must be signed in to change notification settings - Fork 7
Description
YM2151 (FM): Dumping of FM presets from VGMs with YM2151 data to the OPM instrument collection format is slated for the next major release of VGM2PRE
On vgmrips wiki it's stated that YM2151/OPM is going to be added to the next release, but that was four years ago! It's not that hard to implement, so sharing some notes:
The MAME source code shows how to handle the YM2151 register opcodes (the stuff VGM logs):
https://github.com/mamedev/mame/blob/master/src/devices/sound/ym2151.cpp#L650
It decodes opcodes with bit masks (not unlike other supported formats in vgm2pre), so that can be adapted to parse and collect all the YM2151 opcodes in a VGM. However, the decoded value isn't necessarily compatible with VOPM format.
And for that, VOPM's source is available here:
http://picopicose.com/files/vopmex_src_131110.zip
VOPM uses raw decoded values in .OPM format and shifts them all way to the LSB (e.g. 0b01100000 becomes 0b00000011)
Here is a comparison of the decoding differences and similarities in MAME and VOPM. Each line shows how a specific value is extracted from a register.
Column 1: MAME decoding register for use in YM2151 emulator (e.g. add 34 to RR for some reason)
Column 2: VOPM decoding register for use in YM2151 emulator (slight differences, other calculations handled elsewhere maybe)
Column 3: VOPM decoding .OPM file values to produce the same registers later decoded by Column 2 method.
//============================================================================================================================
//====[ MAME ]======================================[ VOPM: bytes ]==================================[ VOPM: .opm > bytes ]===
// RL enable, Feedback, Connection // RL enable, Feedback, Connection // RL enable, Feedback, Connection
op->fb_shift = ((v>>3)&7) ? ((v>>3)&7)+6:0; n = (n>>3) & 7; ((ChData.FL&0x7)<<3)
connect[r&7] = v&7; SetConnection(ch, data&7); |(ChData.CON&0x07)
pan[ (r&7)*2 ] = (v & 0x40) ? ~0 : 0; /*pan[0][ch] = ((data&0x40) ? -1 : 0);*/ if(F_PAN<32)tmp|=0x40;
pan[ (r&7)*2 +1 ] = (v & 0x80) ? ~0 : 0; /*pan[1][ch] = ((data&0x80) ? -1 : 0);*/ else if(F_PAN>96)tmp|=0x80;else tmp|=0xc0;
// PMS, AMS // PMS, AMS // PMS, AMS
op->pms = (v>>4) & 7; int pms = (n>>4)&7; (ChData.PMS<<4)
op->ams = (v & 3); Ams[ch] = ((n&3)-1) & 0x1f; |(ChData.AMS&0x3)
// DT1, MUL // DT1, MUL // DT1, MUL
op->dt1_i = (v&0x70)<<1; Dt1 = (n>>4)&7; (pOp->DT1<<4)
op->mul = (v&0x0f) ? (v&0x0f)<<1: 1; Mul = (n&0x0f)<<1; |(pOp->MUL&0x0f)
// TL // TL // TL
op->tl = (v&0x7f)<<(10-7); /* 7bit TL */ Tl = (128-(n&0x7f))<<3; pOp->TL
// KS, AR // KS, AR // KS, AR
op->ks = 5-(v>>6); Ks = (n&255)>>6; (pOp->KS<<6)
op->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; Ar = n & 0x1f; |(pOp->AR&0x1f)
// LFO AM enable, D1R // LFO AM enable, D1R // LFO AM enable, D1R
op->AMmask = (v&0x80) ? ~0 : 0; n & 0x80; (pOp->F_AME)
op->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; D1r = n & 0x1f; |(pOp->D1R&0x1f)
// DT2, D2R // DT2, D2R // DT2, D2R
op->dt2 = dt2_tab[ v>>6 ]; Dt2 = DT2TBL[(n&255)>>6]; (pOp->DT2<<6)
op->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; D2r = n & 0x1f; |(pOp->D2R&0x1f)
// D1L, RR // D1L, RR // D1L, RR
op->d1l = d1l_tab[ v>>4 ]; StatTbl[DECAY].limit = D1LTBL[(n&255)>>4]; (pOp->D1L<<4)
op->rr = 34 + ((v&0x0f)<<2); Rr = n & 0x0f; |(pOp->RR&0x0f)
//===============================================================================================================================This covers all operator parameters, Connection, Feedback, PMS and AMS, but not LFO or Noise related parameters (was completely absent in the VGM i was targeting for this).
Cheers