vgmtool / vgm2pre

VGM preset dumper standalone app; code will be integrated into new VGMTool
MIT License
27 stars 5 forks source link

Add YM2151 and OPM support #2

Open bryc opened 6 years ago

bryc commented 6 years ago

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

Epictyphlosion commented 2 years ago

I think you might wanna make a fork and add support yourself, bud. Original dev seems to be dead.

jadasse commented 2 years ago

I think you might wanna make a fork and add support yourself, bud. Original dev seems to be dead.

and hopefully as promised, samples would be added as a feature