diff options
Diffstat (limited to 'plugins/gme/game-music-emu-0.6pre/gme/s_opl.c')
-rw-r--r-- | plugins/gme/game-music-emu-0.6pre/gme/s_opl.c | 1244 |
1 files changed, 1244 insertions, 0 deletions
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/s_opl.c b/plugins/gme/game-music-emu-0.6pre/gme/s_opl.c new file mode 100644 index 00000000..b2da8056 --- /dev/null +++ b/plugins/gme/game-music-emu-0.6pre/gme/s_opl.c @@ -0,0 +1,1244 @@ +/* + s_opl.c -- YM2413/Y8950/YM3526/YM3812 emulator by Mamiya, 2001. + + References: + fmopl.c -- 1999,2000 written by Tatsuyuki Satoh (MAME development). + fmopl.c(fixed) -- 2000 modified by mamiya (NEZplug development). + fmgen.cpp -- 1999-2001 written by cisc. + emu2413.c -- a YM2413 emulator : written by Mitsutaka Okazaki 2001 + fmpac.ill -- 2000 created by sama. + fmpac.ill -- 2000 created by NARUTO. + fmpac.ill -- 2001 created by Okazaki. + YM2413 application manual +*/ + +#include "kmsnddev.h" +#include "divfix.h" +#include "s_logtbl.h" +#include "s_opltbl.h" +#include "s_opl.h" +#include "s_deltat.h" +#include <string.h> + +#define PG_SHIFT 10 /* fix */ +#define CPS_SHIFTE 20 +#define CPS_SHIFTP 14 +#define LFO_SHIFT 16 + +#define OPLL_INST_WORK 0x40 +#define OPLL_INST_WORK2 (OPLL_INST_WORK + 8 * 0x13) + +#define AR_BITS 6 /* fix */ +#define AR_SHIFT 14 /* fix */ +#define EG_SHIFT 15 /* fix */ + +#define AR_PHASEMAX (((1 << AR_BITS) - 1) << AR_SHIFT) +#define EG_PHASEMAX (127 << EG_SHIFT) +#define EG_KEYOFF (128 << EG_SHIFT) +#define LOG_KEYOFF (31 << (LOG_BITS + 1)) + +#if 0 + 0-48dB + AR_BITS=6 + AR_SHIFT=14 + x = FC000h(20.7618(sec) * 3579545 / 72)cycles 63 * 4000h + x / 3 * 4 1730.15(ms) + x / 3 * 256 27.03(ms) + + EG_BITS=7 + EG_SHIFT=15 + x = 3F8000h(83.7064(sec) * 3579545 / 72)cycles 127 * 8000h + x / 4 = 20926.6(ms) +#endif + + +#define TESTING_OPTIMIZE_AME 1 +#define USE_FBBUF 1 + +typedef struct +{ + Uint32 phase; + Uint32 spd; + Uint32 rng; +} OPL_PG; + +enum { + EG_MODE_ATTACK, + EG_MODE_DECAY, + EG_MODE_SUSTINE, + EG_MODE_RELEASE, + EG_MODE_NUM, + EG_MODE_SUSHOLD, + EG_MODE_OFF = EG_MODE_NUM +}; + +typedef struct +{ + Uint32 phasear; + Uint32 phase; + Uint32 spd [EG_MODE_NUM]; + Uint32 dr_phasemax; + Uint8 mode; + Uint8 pad4_1; + Uint8 pad4_2; + Uint8 pad4_3; +} OPL_EG; + +enum +{ + FLAG_AME = (1 << 0), + FLAG_PME = (1 << 1), + FLAG_EGT = (1 << 2), + FLAG_KSR = (1 << 3) +}; + +typedef struct +{ + OPL_PG pg; + OPL_EG eg; + Int32 input; +#if USE_FBBUF + Int32 fbbuf; +#endif +#if TESTING_OPTIMIZE_AME + Uint32* amin; +#endif + Uint32 tl_ofs; + Uint32* sintable; + + Uint8 modcar; /* 1:m 0:c */ + Uint8 fb; + Uint8 lvl; + Uint8 nst; + + Uint8 tll; + Uint8 key; + Uint8 rkey; + Uint8 prevkey; + + Uint8 enable; + Uint8 __enablehold__; + Uint8 flag; + Uint8 ksr; + + Uint8 mul; + Uint8 ksl; + Uint8 ar; + Uint8 dr; + Uint8 sl; + Uint8 rr; + Uint8 tl; + Uint8 wf; +} OPL_OP; + +typedef struct +{ + OPL_OP op [2]; + Uint8 con; + Uint8 freql; + Uint8 freqh; + Uint8 blk; + Uint8 kcode; + Uint8 sus; + Uint8 ksb; + Uint8 pad4_3; +} OPL_CH; + +enum +{ + LFO_UNIT_AM, + LFO_UNIT_PM, + LFO_UNIT_NUM +}; + +typedef struct +{ + Uint32 output; + Uint32 cnt; + Uint32 sps; /* step per sample */ + Uint32 adr; + Uint32 adrmask; + Uint32* table; +} OPL_LFO; + +typedef struct +{ + KMIF_SOUND_DEVICE kmif; + KMIF_SOUND_DEVICE* deltatpcm; + KMIF_LOGTABLE* logtbl; + KMIF_OPLTABLE* opltbl; + OPL_CH ch [9]; + OPL_LFO lfo [LFO_UNIT_NUM]; + struct OPLSOUND_COMMON_TAG + { + Uint32 cpsp; + Uint32 cnt; + Uint32* ar_table; + Uint32* tll2logtbl; +#if TESTING_OPTIMIZE_AME + Uint32 amzero; +#endif + Int32 mastervolume; + Uint32 sintablemask; + Uint32 ratetbla [4]; + Uint32 ratetbl [4]; + Uint8 adr; + Uint8 wfe; + Uint8 rc; + Uint8 rmode; + Uint8 enable; + } common; + Uint8 regs [0x100]; + Uint8 opl_type; +} OPLSOUND; + +static Uint8 romtone [3] [16 * 19] = +{ + { +#include "i_fmpac.h" + }, + { +#include "i_fmunit.h" + }, + { +#include "i_vrc7.h" + }, +}; + +static void SetOpOff(OPL_OP* opp ) +{ + opp->eg.mode = EG_MODE_OFF; + opp->eg.phase = EG_KEYOFF; + opp->enable = 0; +} + +inline static void EgStep( OPLSOUND* sndp, OPL_OP* opp ) +{ + switch ( opp->eg.mode ) + { + default: + NEVER_REACH + + case EG_MODE_ATTACK: + opp->eg.phase = sndp->common.ar_table [opp->eg.phasear >> (AR_SHIFT + AR_BITS - ARTBL_BITS)] >> (ARTBL_SHIFT - EG_SHIFT); + opp->eg.phasear += opp->eg.spd [EG_MODE_ATTACK]; + if ( opp->eg.phasear >= AR_PHASEMAX ) + { + opp->eg.mode = EG_MODE_DECAY; + opp->eg.phase = 0; + } + break; + + case EG_MODE_DECAY: + opp->eg.phase += opp->eg.spd [EG_MODE_DECAY]; + if ( opp->eg.phase >= opp->eg.dr_phasemax ) + { + opp->eg.phase = opp->eg.dr_phasemax; + opp->eg.mode = (opp->flag & FLAG_EGT) ? EG_MODE_SUSHOLD : EG_MODE_SUSTINE; + } + break; + + case EG_MODE_SUSTINE: + case EG_MODE_RELEASE: + opp->eg.phase += opp->eg.spd [opp->eg.mode]; + if ( opp->eg.phase >= EG_PHASEMAX ) + SetOpOff(opp); + break; + + case EG_MODE_SUSHOLD: + case EG_MODE_OFF: + break; + } +} + +static void OpStep( OPLSOUND* sndp, OPL_OP* opp ) +{ + int step; + EgStep(sndp, opp); + step = opp->pg.spd; + if ( opp->flag & FLAG_PME ) + step = (step * sndp->lfo [LFO_UNIT_PM].output) >> PM_SHIFT; + opp->pg.phase += step; +} + +__inline static void OpStepNG( OPLSOUND* sndp, OPL_OP* opp ) +{ + Uint32 step; + EgStep(sndp, opp); + opp->pg.phase += opp->pg.spd; + step = opp->pg.phase >> opp->nst/*(PG_SHIFT + 5)*/; + opp->pg.phase &= (1 << opp->nst/*(PG_SHIFT + 5)*/) - 1; + while ( step-- ) + { + opp->pg.rng ^= ((opp->pg.rng & 1) << 16) + ((opp->pg.rng & 1) << 13); + opp->pg.rng >>= 1; + } +} + +#if -1 >> 1 == -1 +/* RIGHT SHIFT IS SIGNED */ +#define SSR(x, y) ((Int32)(x) >> (y)) +#else +/* RIGHT SHIFT IS UNSIGNED */ +#define SSR(x, y) (((x) >= 0) ? ((x) >> (y)) : (-((-(x) - 1) >> (y)) - 1)) +#endif + + +inline static void OpSynthMod( OPLSOUND* sndp, OPL_OP* opp ) +{ + if ( opp->enable ) + { + Uint32 tll; + Int32 output; + OpStep(sndp, opp); + tll = opp->tll + (opp->eg.phase >> EG_SHIFT); + tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll]; + tll += opp->tl_ofs; +#if TESTING_OPTIMIZE_AME + tll += *opp->amin; +#else + if ( opp->flag & FLAG_AME ) + tll += sndp->lfo [LFO_UNIT_AM].output; +#endif + tll += opp->sintable [sndp->common.sintablemask & (opp->input + (opp->pg.phase >> PG_SHIFT))]; + output = LogToLin(sndp->logtbl, tll, -8 + ((LOG_LIN_BITS + 1) - (SINTBL_BITS + 2))); + if ( opp->fb ) + { +#if USE_FBBUF + Int32 fbtmp; + fbtmp = opp->fbbuf + output; + opp->fbbuf = output; + opp->input = SSR(fbtmp, (9 - opp->fb)); +#else + opp->input = SSR(output, (8 - opp->fb)); +#endif + } + opp [1].input = output; + } +} + +inline static Int32 OpSynthCarFb( OPLSOUND* sndp, OPL_OP* opp ) +{ + if ( opp->enable ) + { + Uint32 tll; + OpStep(sndp, opp); + tll = opp->tll + (opp->eg.phase >> EG_SHIFT); + tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll]; + tll += opp->tl_ofs; +#if TESTING_OPTIMIZE_AME + tll +=* opp->amin; +#else + if ( opp->flag & FLAG_AME) tll += sndp->lfo [LFO_UNIT_AM].output; +#endif + tll += opp->sintable [sndp->common.sintablemask & (opp->input + (opp->pg.phase >> PG_SHIFT))]; + if ( opp->fb ) + { +#if USE_FBBUF + Int32 output, fbtmp; + output = LogToLin(sndp->logtbl, tll, -8 + ((LOG_LIN_BITS + 1) - (SINTBL_BITS + 2))); + fbtmp = opp->fbbuf + output; + opp->fbbuf = output; + opp->input = SSR(fbtmp, (9 - opp->fb)); +#else + Int32 output; + output = LogToLin(sndp->logtbl, tll, -8 + ((LOG_LIN_BITS + 1) - (SINTBL_BITS + 2))); + opp->input = SSR(output, (8 - opp->fb)); +#endif + } + return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl); + } + return 0; +} + +inline static Int32 OpSynthCar( OPLSOUND* sndp, OPL_OP* opp ) +{ + if ( opp->enable ) + { + Uint32 tll; + OpStep(sndp, opp); + tll = opp->tll + (opp->eg.phase >> EG_SHIFT); + tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll]; + tll += opp->tl_ofs; +#if TESTING_OPTIMIZE_AME + tll += *opp->amin; +#else + if ( opp->flag & FLAG_AME ) + tll += sndp->lfo [LFO_UNIT_AM].output; +#endif + tll += opp->sintable [sndp->common.sintablemask & (opp->input + (opp->pg.phase >> PG_SHIFT))]; + return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl); + } + return 0; +} + +inline static Int32 OpSynthTom( OPLSOUND* sndp, OPL_OP* opp ) +{ + if ( opp->enable ) + { + Uint32 tll; + OpStep(sndp, opp); + tll = opp->tll + (opp->eg.phase >> EG_SHIFT); + tll = (tll >= 128 - 16) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll]; + tll += opp->tl_ofs; + tll += opp->sintable [sndp->common.sintablemask & (opp->pg.phase >> PG_SHIFT)]; + return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl); + } + return 0; +} + + +static Int32 OpSynthRym( OPLSOUND* sndp, OPL_OP* opp ) +{ + if ( opp->enable ) + { + Uint32 tll; + OpStepNG(sndp, opp); + tll = opp->tll + (opp->eg.phase >> EG_SHIFT) + 0x10/* +6dB */; + tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll]; + tll += opp->tl_ofs; + tll += (opp->pg.rng & 1); + return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl); + } + return 0; +} + +inline static void LfoStep(OPL_LFO* lfop ) +{ + lfop->cnt += lfop->sps; + lfop->adr += lfop->cnt >> LFO_SHIFT; + lfop->cnt &= (1 << LFO_SHIFT) - 1; + lfop->output = lfop->table [lfop->adr & lfop->adrmask]; +} + +static int sndsynth( OPLSOUND* sndp ) +{ + Int32 accum = 0; + if ( sndp->common.enable ) + { + Uint32 i, rch; + for ( i = 0; i < LFO_UNIT_NUM; i++ ) + LfoStep(&sndp->lfo [i]); + + rch = sndp->common.rmode ? 7 : 9; + for ( i = 0; i < rch; i++ ) + { + if ( sndp->ch [i].op [0].modcar ) + OpSynthMod(sndp, &sndp->ch [i].op [0]); + else + accum += OpSynthCarFb(sndp, &sndp->ch [i].op [0]); + accum += OpSynthCar(sndp, &sndp->ch [i].op [1]); + } + if ( sndp->common.rmode ) + { + accum += OpSynthRym(sndp, &sndp->ch [7].op [0]); + accum += OpSynthRym(sndp, &sndp->ch [7].op [1]); + accum += OpSynthTom(sndp, &sndp->ch [8].op [0]); + accum += OpSynthRym(sndp, &sndp->ch [8].op [1]); + } + } + if ( sndp->deltatpcm ) + { + accum += sndp->deltatpcm->synth(sndp->deltatpcm->ctx); + } +#if 0 + /* NISE DAC */ + if ( accum >= 0 ) + accum = (Int32)(((Uint32) accum) & (((1 << 8) - 1) << (23 - 8))); + else + accum = -(Int32)(((Uint32)-accum) & (((1 << 8) - 1) << (23 - 8))); +#endif + return accum; +} + +static void sndvolume( OPLSOUND* sndp, Int32 volume ) +{ + if ( sndp->deltatpcm) sndp->deltatpcm->volume(sndp->deltatpcm->ctx, volume); + volume = (volume << (LOG_BITS - 8)) << 1; + sndp->common.mastervolume = volume; +} + +const static Uint8 op_table [0x20]= +{ + 0, 2, 4, 1, 3, 5,0xFF,0xFF, + 6, 8, 10, 7, 9, 11,0xFF,0xFF, + 12, 14, 16, 13, 15, 17,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +}; + +const static Uint8 mul_table [0x10]= +{ + 1+0, 2+0, 4+0, 2+4, 8+0, 8+2, 8+4,16-2, + 16+0,16+2,16+4,16+4,16+8,16+8,32-2,32-2, +}; + +#define DB2TLL(x) (x * 2 / 375 ) +const static Uint8 ksl_table [8] [16]= +{ + { + DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), + DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), + DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), + DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), + },{ + DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), + DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), + DB2TLL( 0), DB2TLL( 750), DB2TLL( 1125), DB2TLL( 1500), + DB2TLL( 1875), DB2TLL( 2250), DB2TLL( 2625), DB2TLL( 3000), + },{ + DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), + DB2TLL( 0), DB2TLL( 1125), DB2TLL( 1875), DB2TLL( 2625), + DB2TLL( 3000), DB2TLL( 3750), DB2TLL( 4125), DB2TLL( 4500), + DB2TLL( 4875), DB2TLL( 5250), DB2TLL( 5625), DB2TLL( 6000), + },{ + DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 1875), + DB2TLL( 3000), DB2TLL( 4125), DB2TLL( 4875), DB2TLL( 5625), + DB2TLL( 6000), DB2TLL( 6750), DB2TLL( 7125), DB2TLL( 7500), + DB2TLL( 7875), DB2TLL( 8250), DB2TLL( 8625), DB2TLL( 9000), + },{ + DB2TLL( 0), DB2TLL( 0), DB2TLL( 3000), DB2TLL( 4875), + DB2TLL( 6000), DB2TLL( 7125), DB2TLL( 7875), DB2TLL( 8625), + DB2TLL( 9000), DB2TLL( 9750), DB2TLL(10125), DB2TLL(10500), + DB2TLL(10875), DB2TLL(11250), DB2TLL(11625), DB2TLL(12000), + },{ + DB2TLL( 0), DB2TLL( 3000), DB2TLL( 6000), DB2TLL( 7875), + DB2TLL( 9000), DB2TLL(10125), DB2TLL(10875), DB2TLL(11625), + DB2TLL(12000), DB2TLL(12750), DB2TLL(13125), DB2TLL(13500), + DB2TLL(13875), DB2TLL(14250), DB2TLL(14625), DB2TLL(15000), + },{ + DB2TLL( 0), DB2TLL( 6000), DB2TLL( 9000), DB2TLL(10875), + DB2TLL(12000), DB2TLL(13125), DB2TLL(13875), DB2TLL(14625), + DB2TLL(15000), DB2TLL(15750), DB2TLL(16125), DB2TLL(16500), + DB2TLL(16875), DB2TLL(17250), DB2TLL(17625), DB2TLL(18000), + },{ + DB2TLL( 0), DB2TLL( 9000), DB2TLL(12000), DB2TLL(13875), + DB2TLL(15000), DB2TLL(16125), DB2TLL(16875), DB2TLL(17625), + DB2TLL(18000), DB2TLL(18750), DB2TLL(19125), DB2TLL(19500), + DB2TLL(19875), DB2TLL(20250), DB2TLL(20625), DB2TLL(21000), + } +}; +#undef DB2TLL + +static Uint32 rateconvAR( OPLSOUND* sndp, Uint32 rrr, Uint32 ksr ) +{ + if ( !rrr ) + return 0; + rrr = rrr + (ksr >> 2); + if ( rrr >= 15) + return AR_PHASEMAX; + return sndp->common.ratetbla [ksr & 3] >> (CPS_SHIFTE + 1 - rrr); +} + +static Uint32 rateconv( OPLSOUND* sndp, Uint32 rrr, Uint32 ksr ) +{ + if ( !rrr ) + return 0; + rrr = rrr + (ksr >> 2); + if ( rrr > 15 ) + rrr = 15; + return sndp->common.ratetbl [ksr & 3] >> (CPS_SHIFTE + 1 - rrr); +} + +static void OpUpdateWF( OPLSOUND* sndp, OPL_OP* opp ) +{ + opp->sintable = sndp->opltbl->sin_table [opp->wf & sndp->common.wfe]; +} + +static void OpUpdatePG( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp ) +{ + opp->pg.spd = (((chp->freqh << 8) + chp->freql) * opp->mul * sndp->common.cpsp) >> (CPS_SHIFTP - chp->blk); +} + +static void OpUpdateEG( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp ) +{ + Uint32 sr, rr; + opp->ksr = chp->kcode >> ((opp->flag & FLAG_KSR) ? 0 : 2); + opp->eg.dr_phasemax = opp->sl << (1 + 2 + EG_SHIFT); /* 3dB->eg */ + opp->eg.spd [EG_MODE_ATTACK] = rateconvAR(sndp, opp->ar, opp->ksr); + opp->eg.spd [EG_MODE_DECAY] = rateconv(sndp, opp->dr, opp->ksr); + if ( opp->flag & FLAG_EGT ) + { + if ( opp->eg.mode == EG_MODE_SUSTINE ) + opp->eg.mode = EG_MODE_SUSHOLD; + sr = 0; + rr = opp->rr; + } + else + { + if ( opp->eg.mode == EG_MODE_SUSHOLD ) + opp->eg.mode = EG_MODE_SUSTINE; + sr = opp->rr; + rr = 7; + } + if ( chp->sus ) + { + rr = 5; + } + opp->eg.spd [EG_MODE_SUSTINE] = rateconv(sndp, sr, opp->ksr); + opp->eg.spd [EG_MODE_RELEASE] = rateconv(sndp, rr, opp->ksr); +} + +static void OpUpdateTLL( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp ) +{ + opp->tll = (opp->tl + (chp->ksb >> opp->ksl)) << 1; +} + + + +static void oplsetopmul( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v ) +{ + opp->flag &= ~(FLAG_AME | FLAG_PME | FLAG_EGT | FLAG_KSR); +#if TESTING_OPTIMIZE_AME + if ( v & 0x80 ) + opp->amin = &sndp->lfo [LFO_UNIT_AM].output; else opp->amin = &sndp->common.amzero; +#else + if ( v & 0x80 ) + opp->flag |= FLAG_AME; +#endif + if ( v & 0x40 ) opp->flag |= FLAG_PME; + if ( v & 0x20 ) opp->flag |= FLAG_EGT; + if ( v & 0x10 ) opp->flag |= FLAG_KSR; + opp->mul = mul_table [v & 0x0F]; + OpUpdateEG(sndp, chp, opp); + OpUpdatePG(sndp, chp, opp); +} + +static void oplsetopkstl( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v ) +{ + opp->ksl = (v >> 6) ? (3 - (v >> 6)) : 15; /* 0 / 1.5 / 3 / 6 db/OCT */ + opp->tl = v & 0x3F; /* 0.75 db */ + OpUpdateTLL(sndp, chp, opp); +} + +static void oplsetopardr( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v ) +{ + opp->ar = v >> 4; + opp->dr = v & 0xF; + OpUpdateEG(sndp, chp, opp); +} + +static void oplsetopslrr( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v ) +{ + opp->sl = v >> 4; + opp->rr = v & 0xF; + OpUpdateEG(sndp, chp, opp); +} + +static void oplsetopwf( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v ) +{ + opp->wf = v & 0x3; + OpUpdateWF(sndp, opp); +} + +static void oplsetopkey( OPLSOUND* sndp, OPL_OP* opp ) +{ + Uint8 nextkey = ((sndp->common.rmode) && opp->rkey) || opp->key; + if ( opp->prevkey ^ nextkey ) + { + opp->prevkey = nextkey; + if ( nextkey ) + { + sndp->common.enable = 1; + opp->eg.mode = EG_MODE_ATTACK; + opp->eg.phase = EG_KEYOFF; + opp->enable = 1; + opp->eg.phasear = 0; + } + else if ( !opp->modcar && opp->eg.mode != EG_MODE_OFF ) + opp->eg.mode = EG_MODE_RELEASE; + } +} + +static void oplsetchfreql( OPLSOUND* sndp, OPL_CH* chp, Uint32 v ) +{ + chp->freql = v & 0xFF; + chp->ksb = ksl_table [chp->blk] [(chp->freqh << 2) + (chp->freql >> 6)]; + OpUpdatePG(sndp, chp, &chp->op [0]); + OpUpdatePG(sndp, chp, &chp->op [1]); + OpUpdateTLL(sndp, chp, &chp->op [0]); + OpUpdateTLL(sndp, chp, &chp->op [1]); +} + +static void oplsetchfreqh( OPLSOUND* sndp, OPL_CH* chp, Uint32 v ) +{ + Uint32 key = v & 0x20; + chp->kcode = (v >> 1) & 15; + chp->freqh = v & 3; + chp->blk = (v >> 2) & 7; + chp->op [0].key = chp->op [1].key = key; + oplsetopkey(sndp, &chp->op [0]); + oplsetopkey(sndp, &chp->op [1]); + chp->sus = 0; + chp->ksb = ksl_table [chp->blk] [(chp->freqh << 2) + (chp->freql >> 6)]; + OpUpdateEG(sndp, chp, &chp->op [0]); + OpUpdateEG(sndp, chp, &chp->op [1]); + OpUpdatePG(sndp, chp, &chp->op [0]); + OpUpdatePG(sndp, chp, &chp->op [1]); + OpUpdateTLL(sndp, chp, &chp->op [0]); + OpUpdateTLL(sndp, chp, &chp->op [1]); +} + +static void oplsetchfbcon( OPLSOUND* sndp, OPL_CH* chp, Uint32 v ) +{ + chp->op [0].fb = (v >> 1) & 7; +#if USE_FBBUF + chp->op [0].fbbuf = 0; +#endif + chp->con = v & 1; + chp->op [0].modcar = (chp->con) ? 0 : 1; + OpUpdateEG(sndp, chp, &chp->op [0]); + chp->op [1].input = 0; +} + +static void opllsetopvolume( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v ) +{ + opp->tl = v; + OpUpdateTLL(sndp, chp, opp); +} + +static void opllsetchfreql( OPLSOUND* sndp, OPL_CH* chp, Uint32 v ) +{ + chp->freql = v & 0xFF; + chp->ksb = ksl_table [chp->blk] [(chp->freqh << 3) + (chp->freql >> 5)]; + OpUpdatePG(sndp, chp, &chp->op [0]); + OpUpdatePG(sndp, chp, &chp->op [1]); + OpUpdateTLL(sndp, chp, &chp->op [0]); + OpUpdateTLL(sndp, chp, &chp->op [1]); +} + +static void opllsetchfreqh( OPLSOUND* sndp, OPL_CH* chp, Uint32 v ) +{ + Uint32 key = v & 0x10; + chp->kcode = v & 15; + chp->freqh = v & 1; + chp->blk = (v >> 1) & 7; + chp->op [0].key = chp->op [1].key = key; + oplsetopkey(sndp, &chp->op [0]); + oplsetopkey(sndp, &chp->op [1]); + chp->sus = v & 0x20; + chp->ksb = ksl_table [chp->blk] [(chp->freqh << 3) + (chp->freql >> 5)]; + OpUpdateEG(sndp, chp, &chp->op [0]); + OpUpdateEG(sndp, chp, &chp->op [1]); + OpUpdatePG(sndp, chp, &chp->op [0]); + OpUpdatePG(sndp, chp, &chp->op [1]); + OpUpdateTLL(sndp, chp, &chp->op [0]); + OpUpdateTLL(sndp, chp, &chp->op [1]); +} + +static void SetOpTone( OPLSOUND* sndp, OPL_OP* opp, Uint8* tonep ) +{ + opp->flag &= ~(FLAG_AME | FLAG_PME | FLAG_EGT | FLAG_KSR); +#if TESTING_OPTIMIZE_AME + if ( tonep [0] & 0x80 ) opp->amin = &sndp->lfo [LFO_UNIT_AM].output; else opp->amin = &sndp->common.amzero; +#else + if ( tonep [0] & 0x80 ) opp->flag |= FLAG_AME; +#endif + if ( tonep [0] & 0x40 ) opp->flag |= FLAG_PME; + if ( tonep [0] & 0x20 ) opp->flag |= FLAG_EGT; + if ( tonep [0] & 0x10 ) opp->flag |= FLAG_KSR; + opp->mul = mul_table [tonep [0] & 0x0F] << 1; + opp->ksl = (tonep [2] >> 6) ? (3 - (tonep [2] >> 6)) : 15; + opp->ar = tonep [4] >> 4; + opp->dr = tonep [4] & 0xF; + opp->sl = tonep [6] >> 4; + opp->rr = tonep [6] & 0xF; +} +static void SetChTone( OPLSOUND* sndp, OPL_CH* chp, Uint8* tonep, Uint8* tlofsp ) +{ + Uint32 op; + for ( op = 0; op < 2; op++ ) + SetOpTone(sndp, &chp->op [op], &tonep [op]); + chp->op [0].tl_ofs = (tlofsp [0] ^ 0x80) << (LOG_BITS - 4 + 1); + chp->op [1].tl_ofs = (tlofsp [1] ^ 0x80) << (LOG_BITS - 4 + 1); + chp->op [0].tl = tonep [2] & 0x3F; + chp->op [0].fb = tonep [3] & 0x7; + chp->op [0].wf = (tonep [3] >> 3) & 1; + chp->op [1].wf = (tonep [3] >> 4) & 1; +#if USE_FBBUF + chp->op [0].fbbuf = 0; +#endif + chp->op [1].input = 0; + OpUpdateWF(sndp, &chp->op [0]); + OpUpdateWF(sndp, &chp->op [1]); + OpUpdateEG(sndp, chp, &chp->op [0]); + OpUpdateEG(sndp, chp, &chp->op [1]); + OpUpdatePG(sndp, chp, &chp->op [0]); + OpUpdatePG(sndp, chp, &chp->op [1]); + OpUpdateTLL(sndp, chp, &chp->op [0]); + OpUpdateTLL(sndp, chp, &chp->op [1]); +} + +static void opllsetchtone( OPLSOUND* sndp, OPL_CH* chp, Uint32 tone ) +{ + SetChTone(sndp, chp, &sndp->regs [OPLL_INST_WORK + (tone << 3)], &sndp->regs [OPLL_INST_WORK2 + (tone << 1) + 0]); +} + +static void recovercon( OPLSOUND* sndp, OPL_CH* chp ) +{ + chp->op [0].modcar = (chp->con) ? 0 : 1; + chp->op [0].lvl = chp->con ? 1 : 0; + chp->op [1].lvl = 1; + OpUpdateEG(sndp, chp, &chp->op [0]); + chp->op [1].input = 0; +} + +static void initrc_common( OPLSOUND* sndp, Uint32 rmode ) +{ + if ( rmode ) + { + /* BD */ + sndp->ch [6].op [0].modcar = 1; + sndp->ch [6].op [0].lvl = 0; + OpUpdateEG(sndp, &sndp->ch [6], &sndp->ch [6].op [0]); + sndp->ch [6].op [1].input = 0; + sndp->ch [6].op [1].lvl = 2; + /* CYM */ + sndp->ch [7].op [0].modcar = 0; + sndp->ch [7].op [0].lvl = 1; + OpUpdateEG(sndp, &sndp->ch [7], &sndp->ch [7].op [0]); + /* SD */ + sndp->ch [7].op [1].input = 0; + sndp->ch [7].op [1].lvl = 2; + /* TOM */ + sndp->ch [8].op [0].modcar = 0; + sndp->ch [8].op [0].lvl = 2; + OpUpdateEG(sndp, &sndp->ch [8], &sndp->ch [8].op [0]); + /* HH */ + sndp->ch [8].op [1].input = 0; + sndp->ch [8].op [1].lvl = 1; + } + else + { + recovercon(sndp, &sndp->ch [6]); + if ( !sndp->ch [6].op [0].key ) SetOpOff(&sndp->ch [6].op [0]); + if ( !sndp->ch [6].op [1].key ) SetOpOff(&sndp->ch [6].op [1]); + recovercon(sndp, &sndp->ch [7]); + if ( !sndp->ch [7].op [0].key ) SetOpOff(&sndp->ch [7].op [0]); + if ( !sndp->ch [7].op [1].key ) SetOpOff(&sndp->ch [7].op [1]); + recovercon(sndp, &sndp->ch [8]); + if ( !sndp->ch [8].op [0].key ) SetOpOff(&sndp->ch [8].op [0]); + if ( !sndp->ch [8].op [1].key ) SetOpOff(&sndp->ch [8].op [1]); + } +} + +static void oplsetrc( OPLSOUND* sndp, Uint32 rc ) +{ + sndp->lfo [LFO_UNIT_AM].table = (rc & 0x80) ? sndp->opltbl->am_table1 : sndp->opltbl->am_table2; + sndp->lfo [LFO_UNIT_PM].table = (rc & 0x40) ? sndp->opltbl->pm_table1 : sndp->opltbl->pm_table2; + if ( (sndp->common.rmode ^ rc) & 0x20 ) + { + if ( rc & 0x20 ) + { +#if 0 + static Uint8 volini [2] = { 0, 0 }; + static Uint8 bdtone [8] = { 0x04, 0x20, 0x28, 0x00, 0xDF, 0xF8, 0xFF, 0xF8 }; + SetChTone(sndp, &sndp->ch [6], bdtone, volini); + SetChTone(sndp, &sndp->ch [7], &romtone [0] [0x11 << 4], volini); + SetChTone(sndp, &sndp->ch [8], &romtone [0] [0x12 << 4], volini); +#endif + sndp->ch [7].op [0].nst = PG_SHIFT + 4; + sndp->ch [7].op [1].nst = PG_SHIFT + 6; + sndp->ch [8].op [1].nst = PG_SHIFT + 5; + } + initrc_common(sndp, rc & 0x20); + } + sndp->common.rmode = rc & 0x20; + sndp->common.rc = rc & 0x1F; + /* BD */ + sndp->ch [6].op [0].rkey = sndp->ch [6].op [1].rkey = rc & 0x10; + oplsetopkey(sndp, &sndp->ch [6].op [0]); + oplsetopkey(sndp, &sndp->ch [6].op [1]); + /* CYM */ + sndp->ch [7].op [0].rkey = rc & 0x01; + oplsetopkey(sndp, &sndp->ch [7].op [0]); + /* SD */ + sndp->ch [7].op [1].rkey = rc & 0x08; + oplsetopkey(sndp, &sndp->ch [7].op [1]); + /* TOM */ + sndp->ch [8].op [0].rkey = rc & 0x04; + oplsetopkey(sndp, &sndp->ch [8].op [0]); + /* HH */ + sndp->ch [8].op [1].rkey = rc & 0x02; + oplsetopkey(sndp, &sndp->ch [8].op [1]); +} + +static void opllsetrc( OPLSOUND* sndp, Uint32 rc ) +{ + if ( (sndp->common.rmode ^ rc) & 0x20 ) + { + if ( rc & 0x20 ) + { + opllsetchtone(sndp, &sndp->ch [6], 0x10); + opllsetchtone(sndp, &sndp->ch [7], 0x11); + opllsetchtone(sndp, &sndp->ch [8], 0x12); + opllsetopvolume(sndp, &sndp->ch [7], &sndp->ch [7].op [0], (sndp->regs [0x37] & 0xF0) >> 2); + opllsetopvolume(sndp, &sndp->ch [8], &sndp->ch [8].op [0], (sndp->regs [0x38] & 0xF0) >> 2); + sndp->ch [7].op [0].nst = PG_SHIFT + 5; + sndp->ch [7].op [1].nst = PG_SHIFT + 5; + sndp->ch [8].op [1].nst = PG_SHIFT + 5; + } + else + { + opllsetchtone(sndp, &sndp->ch [6], sndp->regs [0x36]>>4); + opllsetchtone(sndp, &sndp->ch [7], sndp->regs [0x37]>>4); + opllsetchtone(sndp, &sndp->ch [8], sndp->regs [0x38]>>4); + } + initrc_common(sndp, rc & 0x20); + } + sndp->common.rmode = rc & 0x20; + sndp->common.rc = rc & 0x1F; + /* BD */ + sndp->ch [6].op [0].rkey = sndp->ch [6].op [1].rkey = rc & 0x10; + oplsetopkey(sndp, &sndp->ch [6].op [0]); + oplsetopkey(sndp, &sndp->ch [6].op [1]); + /* CYM */ + sndp->ch [7].op [0].rkey = rc & 0x01; + oplsetopkey(sndp, &sndp->ch [7].op [0]); + /* SD */ + sndp->ch [7].op [1].rkey = rc & 0x08; + oplsetopkey(sndp, &sndp->ch [7].op [1]); + /* TOM */ + sndp->ch [8].op [0].rkey = rc & 0x04; + oplsetopkey(sndp, &sndp->ch [8].op [0]); + /* HH */ + sndp->ch [8].op [1].rkey = rc & 0x02; + oplsetopkey(sndp, &sndp->ch [8].op [1]); +} + +#define OPLSETOP(func) { \ + Uint32 op = op_table [a & 0x1F]; \ + if ( op != 0xFF) func(sndp, &sndp->ch [op >> 1], &sndp->ch [op >> 1].op [op & 1], v); \ +} + +__inline static void oplwritereg( OPLSOUND* sndp, Uint32 a, Uint32 v ) +{ + switch ( a >> 5 ) + { + default: + NEVER_REACH + + case 0: + switch ( a & 0x1F ) + { + case 0x01: + if ( sndp->opl_type == OPL_TYPE_OPL2 ) + { + Uint32 i; + sndp->common.wfe = (v & 0x20) ? 3 : 0; + for ( i = 0; i < 9; i++ ) + { + OpUpdateWF(sndp, &sndp->ch [i].op [0]); + OpUpdateWF(sndp, &sndp->ch [i].op [1]); + } + } + break; + + case 0x08: + /* CSM mode */ + case 0x07: case 0x09: case 0x0A: case 0x0B: case 0x0C: + case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: case 0x12: + if ( sndp->deltatpcm ) + sndp->deltatpcm->write(sndp->deltatpcm->ctx, a - 0x07, v); + break; + } + break; + + case 1: OPLSETOP(oplsetopmul); break; + case 2: OPLSETOP(oplsetopkstl); break; + case 3: OPLSETOP(oplsetopardr); break; + case 4: OPLSETOP(oplsetopslrr); break; + case 7: OPLSETOP(oplsetopwf); break; + case 5: + if ( (a & 0x1F) == (0xBD & 0x1F) ) + oplsetrc(sndp, v); + else if ( (a & 0x1F) < 9 ) + oplsetchfreql(sndp, &sndp->ch [a & 0xF], v); + else if ( (a & 0xF) < 9 ) + oplsetchfreqh(sndp, &sndp->ch [a & 0xF], v); + break; + + case 6: + if ( (a & 0x1F) < 9) oplsetchfbcon(sndp, &sndp->ch [a & 0xF], v); + break; + } +} + +static void oplwrite( OPLSOUND* sndp, Uint32 a, Uint32 v ) +{ + if ( a & 1 ) + { + sndp->regs [sndp->common.adr] = v; + oplwritereg(sndp, sndp->common.adr, v); + } + else + sndp->common.adr = v; +} + +static Uint32 oplread( OPLSOUND* sndp, Uint32 a ) +{ + if ( a & 1 ) + return sndp->regs [sndp->common.adr]; + else + return 0x80; +} + +__inline static void opllwritereg( OPLSOUND* sndp, Uint32 a, Uint32 v ) +{ + switch ( a >> 3 ) + { + default: + NEVER_REACH + case 0: + sndp->regs [OPLL_INST_WORK + (a & 7)] = v; + break; + + case 1: + if ( a == 0xE) opllsetrc(sndp, v & 0x3F); + break; + + case 2: + case 3: + a &= 0xF; + if ( a < 9) opllsetchfreql(sndp, &sndp->ch [a], v); + break; + + case 4: + case 5: + a &= 0xF; + if ( a < 9) opllsetchfreqh(sndp, &sndp->ch [a], v); + break; + + case 6: + case 7: + a &= 0xF; + if ( a < 9 ) + { + if ( (sndp->common.rmode) && (a >= 6) ) + { + if ( a != 6) opllsetopvolume(sndp, &sndp->ch [a], &sndp->ch [a].op [0], (v & 0xF0) >> 2); + } + else + { + opllsetchtone(sndp, &sndp->ch [a], (v & 0xF0) >> 4); + } + opllsetopvolume(sndp, &sndp->ch [a], &sndp->ch [a].op [1], (v & 0xF) << 2); + } + break; + } +} + +static void opllwrite( OPLSOUND* sndp, Uint32 a, Uint32 v ) +{ + if ( a & 1 ) + { + if ( sndp->common.adr < 0x40 ) + { + sndp->regs [sndp->common.adr] = v; + opllwritereg(sndp, sndp->common.adr, v); + } + } + else + sndp->common.adr = v; +} + +static Uint32 opllread( OPLSOUND* sndp, Uint32 a ) +{ + return 0xFF; +} + +static void opreset( OPLSOUND* sndp, OPL_OP* opp ) +{ + /* XMEMSET(opp, 0, sizeof(OPL_OP)); */ + SetOpOff(opp); + opp->tl_ofs = 0x80 << (LOG_BITS - 4 + 1); +#if TESTING_OPTIMIZE_AME + opp->amin = &sndp->common.amzero; +#endif + opp->pg.rng = 0xFFFF; +} + +static void chreset( OPLSOUND* sndp, OPL_CH* chp, Uint32 clock, Uint32 freq ) +{ + Uint32 op; + XMEMSET(chp, 0, sizeof(OPL_CH)); + for ( op = 0; op < 2; op++ ) + { + opreset(sndp, &chp->op [op]); + } + recovercon(sndp, chp); +} + +static void sndreset( OPLSOUND* sndp, Uint32 clock, Uint32 freq ) +{ + Uint32 i, cpse; + XMEMSET(&sndp->common, 0, sizeof(sndp->common)); + XMEMSET(&sndp->lfo [LFO_UNIT_AM], 0, sizeof(OPL_LFO)); + sndp->lfo [LFO_UNIT_AM].sps = DivFix(37 * (1 << AMTBL_BITS), freq * 10, LFO_SHIFT); + sndp->lfo [LFO_UNIT_AM].adrmask = (1 << AMTBL_BITS) - 1; + sndp->lfo [LFO_UNIT_AM].table = sndp->opltbl->am_table1; + XMEMSET(&sndp->lfo [LFO_UNIT_PM], 0, sizeof(OPL_LFO)); + sndp->lfo [LFO_UNIT_PM].sps = DivFix(64 * (1 << PMTBL_BITS), freq * 10, LFO_SHIFT); + sndp->lfo [LFO_UNIT_PM].adrmask = (1 << PMTBL_BITS) - 1; + sndp->lfo [LFO_UNIT_PM].table = sndp->opltbl->pm_table1; + sndp->common.cpsp = DivFix(clock, 72 * freq, CPS_SHIFTP); + cpse = DivFix(clock, 72 * freq, CPS_SHIFTE); + for ( i = 0; i < 4; i++ ) + { + sndp->common.ratetbl [i] = (i + 4) * cpse; + sndp->common.ratetbla [i] = 3 * sndp->common.ratetbl [i]; + } + sndp->common.tll2logtbl = sndp->opltbl->tll2log_table; + sndp->common.sintablemask = (1 << SINTBL_BITS) - 1; + + for ( i = 0; i < 9; i++ ) + chreset(sndp, &sndp->ch [i], clock, freq); + + if ( sndp->deltatpcm ) + sndp->deltatpcm->reset(sndp->deltatpcm->ctx, clock, freq); + + if ( sndp->opl_type & OPL_TYPE_OPL ) + { + XMEMSET(&sndp->regs, 0, 0x100); + sndp->common.ar_table = sndp->opltbl->ar_tablepow; + sndp->common.sintablemask -= (1 << (SINTBL_BITS - 11)) - 1; + for ( i = 0x0; i < 0x100; i++ ) + { + oplwrite(sndp, 0, i); + oplwrite(sndp, 1, 0x00); + } + for ( i = 0xA0; i < 0xA9; i++ ) + { + oplwrite(sndp, 0, 0xA0 + i); + oplwrite(sndp, 1, 0x40); + oplwrite(sndp, 0, 0xB0 + i); + oplwrite(sndp, 1, 0x0E); + } + } + else + { + const static Uint8 fmbios_initdata [9] = "\x30\x10\x20\x20\xfb\xb2\xf3\xf3"; + XMEMSET(&sndp->regs, 0, 0x40); + sndp->common.ar_table = sndp->opltbl->ar_tablelog; + sndp->common.wfe = 1; + sndp->common.sintablemask -= (1 << (SINTBL_BITS - 8)) - 1; + for ( i = 0; i < sizeof(fmbios_initdata)-1; i++ ) + { + opllwrite(sndp, 0, i); + opllwrite(sndp, 1, fmbios_initdata [i]); + } + opllwrite(sndp, 0, 0x0E); + opllwrite(sndp, 1, 0x00); + opllwrite(sndp, 0, 0x0F); + opllwrite(sndp, 1, 0x00); + for ( i = 0; i < 9; i++ ) + { + opllwrite(sndp, 0, 0x10 + i); + opllwrite(sndp, 1, 0x20); + opllwrite(sndp, 0, 0x20 + i); + opllwrite(sndp, 1, 0x07); + opllwrite(sndp, 0, 0x30 + i); + opllwrite(sndp, 1, 0xB3); + } + } +} + +static void oplsetinst( OPLSOUND* sndp, Uint32 n, void* p, Uint32 l ) +{ + if ( sndp->deltatpcm) sndp->deltatpcm->setinst(sndp->deltatpcm->ctx, n, p, l); +} + +__inline static Uint32 GetDwordLE(Uint8* p ) +{ + return p [0] | (p [1] << 8) | (p [2] << 16) | (p [3] << 24); +} +#define GetDwordLEM(p) (Uint32)((((Uint8* )p) [0] | (((Uint8* )p) [1] << 8) | (((Uint8* )p) [2] << 16) | (((Uint8* )p) [3] << 24)) ) + +static void opllsetinst( OPLSOUND* sndp, Uint32 n, Uint8* p, Uint32 l ) +{ + Int32 i, j, sb = 9; + if ( n ) + return; + if ( (GetDwordLE(p) & 0xF0FFFFFF) == GetDwordLEM("ILL0") ) + { + if ( 0 < p [4] && p [4] <= SINTBL_BITS) sb = p [4]; + for ( j = 1; j < 16 + 3; j++ ) + for ( i = 0; i < 8; i++ ) + sndp->regs [OPLL_INST_WORK + (j << 3) + i] = p [(j << 4) + i]; + for ( j = 0; j < 16 + 3; j++ ) + { + sndp->regs [OPLL_INST_WORK2 + (j << 1) + 0] = p [(j << 4) + 8]; + sndp->regs [OPLL_INST_WORK2 + (j << 1) + 1] = p [(j << 4) + 9]; + } + } + else + { + for ( j = 1; j < 16; j++ ) + for ( i = 0; i < 8; i++ ) + sndp->regs [OPLL_INST_WORK + (j << 3) + i] = p [((j - 1) << 3) + i]; + } + sndp->common.sintablemask = (1 << SINTBL_BITS) - 1; + sndp->common.sintablemask -= (1 << (SINTBL_BITS - sb)) - 1; +} + +static void sndrelease( OPLSOUND* sndp ) +{ + if ( sndp->logtbl) sndp->logtbl->release(sndp->logtbl->ctx); + if ( sndp->opltbl) sndp->opltbl->release(sndp->opltbl->ctx); + if ( sndp->deltatpcm) sndp->deltatpcm->release(sndp->deltatpcm->ctx); + XFREE(sndp); +} + +KMIF_SOUND_DEVICE* OPLSoundAlloc(Uint32 opl_type ) +{ + OPLSOUND* sndp; + sndp = (OPLSOUND*) XMALLOC(sizeof(OPLSOUND)); + if ( !sndp) return 0; + sndp->opl_type = opl_type; + sndp->kmif.ctx = sndp; + sndp->kmif.release = (void (*)( void* )) sndrelease; + sndp->kmif.volume = (void (*)( void*, int )) sndvolume; + sndp->kmif.reset = (void (*)( void*, Uint32, Uint32 )) sndreset; + sndp->kmif.synth = (int (*)( void* )) sndsynth; + if ( sndp->opl_type == OPL_TYPE_MSXAUDIO ) + { + sndp->deltatpcm = YMDELTATPCMSoundAlloc(YMDELTATPCM_TYPE_Y8950); + } + else + sndp->deltatpcm = 0; + if ( sndp->opl_type & OPL_TYPE_OPL ) + { + sndp->kmif.write = (void (*)( void*, Uint32, Uint32 )) oplwrite; + sndp->kmif.read = (Uint32 (*)( void*, Uint32 )) oplread; + sndp->kmif.setinst = (void (*)( void*, Uint32, void*, Uint32 )) oplsetinst; + } + else + { + sndp->kmif.write = (void (*)( void*, Uint32, Uint32 )) opllwrite; + sndp->kmif.read = (Uint32 (*)( void*, Uint32 )) opllread; + sndp->kmif.setinst = (void (*)( void*, Uint32, void*, Uint32 )) opllsetinst; + switch ( sndp->opl_type ) + { + case OPL_TYPE_OPLL: + case OPL_TYPE_MSXMUSIC: + opllsetinst(sndp, 0, romtone [0], 16 * 19); + break; + + case OPL_TYPE_SMSFMUNIT: + opllsetinst(sndp, 0, romtone [1], 16 * 19); + break; + + case OPL_TYPE_VRC7: + opllsetinst(sndp, 0, romtone [2], 16 * 19); + break; + } + } + sndp->logtbl = LogTableAddRef(); + sndp->opltbl = OplTableAddRef(); + if ( !sndp->logtbl || !sndp->opltbl ) + { + sndrelease(sndp); + return 0; + } + + return &sndp->kmif; +} |