diff --git a/ym3438.c b/ym3438.c index c86567a..9391b37 100644 --- a/ym3438.c +++ b/ym3438.c @@ -27,8 +27,13 @@ */ #include +#include #include "ym3438.h" +// superctr's MegaDrive model 1 filter +#define FILTER_CUTOFF 0.512331301282628 // 5894Hz single pole IIR low pass +#define FILTER_CUTOFF_I (1-FILTER_CUTOFF) + enum { eg_num_attack = 0, eg_num_decay = 1, @@ -216,8 +221,6 @@ static const Bit32u fm_algorithm[4][6][8] = { } }; -static Bit32u chip_type = ym3438_mode_readmode; - static void OPN2_DoIO(ym3438_t *chip) { /* Write signal check */ @@ -981,7 +984,7 @@ static void OPN2_ChOutput(ym3438_t *chip) chip->mol = 0; chip->mor = 0; - if (chip_type & ym3438_mode_ym2612) + if (chip->chip_type & ym3438_mode_ym2612) { out_en = ((cycles & 3) == 3) || test_dac; /* YM2612 DAC emulation(not verified) */ @@ -1201,12 +1204,21 @@ void OPN2_Reset(ym3438_t *chip) } } -void OPN2_SetChipType(Bit32u type) +void OPN2_SetClockRate(ym3438_t *chip, Bit32u clock, Bit32u rate) { + chip->clock = clock; + chip->smplRate = rate; + + chip->rateratio = (Bit32s)((((Bit64u)144 * chip->smplRate) << RSM_FRAC) / chip->clock); + if (abs(chip->rateratio - (1 << RSM_FRAC)) <= 1) + chip->rateratio = (1 << RSM_FRAC); +} + +void OPN2_SetChipType(ym3438_t *chip, Bit32u type) { - chip_type = type; + chip->chip_type = type; } -void OPN2_Clock(ym3438_t *chip, Bit16s *buffer) +void OPN2_Clock(ym3438_t *chip, Bit32s *buffer) { Bit32u slot = chip->cycles; chip->lfo_inc = chip->mode_test_21[1]; @@ -1380,7 +1392,7 @@ Bit32u OPN2_ReadIRQPin(ym3438_t *chip) Bit8u OPN2_Read(ym3438_t *chip, Bit32u port) { - if ((port & 3) == 0 || (chip_type & ym3438_mode_readmode)) + if ((port & 3) == 0 || (chip->chip_type & ym3438_mode_readmode)) { if (chip->mode_test_21[6]) { @@ -1410,7 +1422,7 @@ Bit8u OPN2_Read(ym3438_t *chip, Bit32u port) chip->status = (chip->busy << 7) | (chip->timer_b_overflow_flag << 1) | chip->timer_a_overflow_flag; } - if (chip_type & ym3438_mode_ym2612) + if (chip->chip_type & ym3438_mode_ym2612) { chip->status_time = 300000; } @@ -1425,3 +1437,123 @@ Bit8u OPN2_Read(ym3438_t *chip, Bit32u port) } return 0; } + +void OPN2_WriteBuffered(ym3438_t *chip, Bit8u port, Bit8u data) +{ + Bit64u time1, time2; + Bit32s buffer[2]; + Bit64u skip; + + if (chip->writebuf[chip->writebuf_last].port & 0x04) + { + OPN2_Write(chip, chip->writebuf[chip->writebuf_last].port & 0X03, + chip->writebuf[chip->writebuf_last].data); + + chip->writebuf_cur = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; + skip = chip->writebuf[chip->writebuf_last].time - chip->writebuf_samplecnt; + chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; + while (skip--) + { + OPN2_Clock(chip, buffer); + } + } + + chip->writebuf[chip->writebuf_last].port = (port & 0x03) | 0x04; + chip->writebuf[chip->writebuf_last].data = data; + time1 = chip->writebuf_lasttime + OPN_WRITEBUF_DELAY; + time2 = chip->writebuf_samplecnt; + + if (time1 < time2) + { + time1 = time2; + } + + chip->writebuf[chip->writebuf_last].time = time1; + chip->writebuf_lasttime = time1; + chip->writebuf_last = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; +} + +void OPN2_GenerateResampled(ym3438_t *chip, Bit32s *buf) +{ + Bit32u i; + Bit32s buffer[2]; + Bit32u mute; + + while (chip->samplecnt >= chip->rateratio) + { + chip->oldsamples[0] = chip->samples[0]; + chip->oldsamples[1] = chip->samples[1]; + chip->samples[0] = chip->samples[1] = 0; + for (i = 0; i < 24; i++) + { + switch (chip->cycles >> 2) + { + case 0: // Ch 2 + mute = chip->mute[1]; + break; + case 1: // Ch 6, DAC + mute = chip->mute[5 + chip->dacen]; + break; + case 2: // Ch 4 + mute = chip->mute[3]; + break; + case 3: // Ch 1 + mute = chip->mute[0]; + break; + case 4: // Ch 5 + mute = chip->mute[4]; + break; + case 5: // Ch 3 + mute = chip->mute[2]; + break; + default: + mute = 0; + break; + } + OPN2_Clock(chip, buffer); + if (!mute) + { + chip->samples[0] += buffer[0]; + chip->samples[1] += buffer[1]; + } + + while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) + { + if (!(chip->writebuf[chip->writebuf_cur].port & 0x04)) + { + break; + } + chip->writebuf[chip->writebuf_cur].port &= 0x03; + OPN2_Write(chip, chip->writebuf[chip->writebuf_cur].port, + chip->writebuf[chip->writebuf_cur].data); + chip->writebuf_cur = (chip->writebuf_cur + 1) % OPN_WRITEBUF_SIZE; + } + chip->writebuf_samplecnt++; + } + if(!chip->use_filter) + { + chip->samples[0] *= 11; + chip->samples[1] *= 11; + } + else + { + chip->samples[0] = chip->oldsamples[0] + FILTER_CUTOFF_I * (chip->samples[0]*(11+1) - chip->oldsamples[0]); + chip->samples[1] = chip->oldsamples[1] + FILTER_CUTOFF_I * (chip->samples[1]*(11+1) - chip->oldsamples[1]); + } + chip->samplecnt -= chip->rateratio; + } + buf[0] = (Bit32s)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + + chip->samples[0] * chip->samplecnt) / chip->rateratio); + buf[1] = (Bit32s)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + + chip->samples[1] * chip->samplecnt) / chip->rateratio); + chip->samplecnt += 1 << RSM_FRAC; +} + +void OPN2_SetMutemask(ym3438_t *chip, Bit8u mute) +{ + Bit8u i; + for (i = 0; i < 7; i++) + { + chip->mute[i] = (mute >> i) & 0x01; + } +} diff --git a/ym3438.h b/ym3438.h index 090a57c..31c67b8 100644 --- a/ym3438.h +++ b/ym3438.h @@ -33,12 +33,18 @@ extern "C" { #endif +#define RSM_FRAC 10 +#define OPN_WRITEBUF_SIZE 2048 +#define OPN_WRITEBUF_DELAY 15 + +#include + enum { ym3438_mode_ym2612 = 0x01, /* Enables YM2612 emulation (MD1, MD2 VA2) */ - ym3438_mode_readmode = 0x02 /* Enables status read on any port (TeraDrive, MD1 VA7, MD2, etc) */ -}; + ym3438_mode_readmode = 0x02, /* Enables status read on any port (TeraDrive, MD1 VA7, MD2, etc) */ -#include + ym3438_mode_max = UINT32_MAX +}; typedef uintptr_t Bitu; typedef intptr_t Bits; @@ -51,8 +57,17 @@ typedef int16_t Bit16s; typedef uint8_t Bit8u; typedef int8_t Bit8s; +typedef struct _opn2_writebuf { + Bit64u time; + Bit8u port; + Bit8u data; +} opn2_writebuf; + typedef struct { + Bit32u clock; + Bit32u smplRate; + Bit32u cycles; Bit32u channel; Bit16s mol, mor; @@ -193,17 +208,36 @@ typedef struct Bit8u pms[6]; Bit8u status; Bit32u status_time; + + Bit32u chip_type; + Bit32u use_filter; + Bit32u mute[7]; + Bit32s rateratio; + Bit32s samplecnt; + Bit32s oldsamples[2]; + Bit32s samples[2]; + + Bit64u writebuf_samplecnt; + Bit32u writebuf_cur; + Bit32u writebuf_last; + Bit64u writebuf_lasttime; + opn2_writebuf writebuf[OPN_WRITEBUF_SIZE]; } ym3438_t; void OPN2_Reset(ym3438_t *chip); -void OPN2_SetChipType(Bit32u type); -void OPN2_Clock(ym3438_t *chip, Bit16s *buffer); +void OPN2_SetChipType(ym3438_t *chip, Bit32u type); +void OPN2_Clock(ym3438_t *chip, Bit32s *buffer); void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data); void OPN2_SetTestPin(ym3438_t *chip, Bit32u value); Bit32u OPN2_ReadTestPin(ym3438_t *chip); Bit32u OPN2_ReadIRQPin(ym3438_t *chip); Bit8u OPN2_Read(ym3438_t *chip, Bit32u port); +void OPN2_SetClockRate(ym3438_t *chip, Bit32u clock, Bit32u rate); +void OPN2_WriteBuffered(ym3438_t *chip, Bit8u port, Bit8u data); +void OPN2_GenerateResampled(ym3438_t *chip, Bit32s *buf); +void OPN2_SetMutemask(ym3438_t *chip, Bit8u mute); + #ifdef __cplusplus } #endif