// sn76496\r
extern int *sn76496_regs;\r
\r
+// FM resampling polyphase FIR\r
+static resampler_t *fmresampler;\r
+static int (*PsndFMUpdate)(s32 *buffer, int length, int stereo, int is_buf_empty);\r
+\r
// ym2413\r
-#define YM2413_CLK 3579545\r
static OPLL *opll = NULL;\r
static OPLL old_opll;\r
static struct {\r
uint8_t reg[sizeof(opll->reg)];\r
} opll_buf;\r
\r
+\r
+void YM2413_regWrite(unsigned data){\r
+ OPLL_writeIO(opll,0,data);\r
+}\r
+void YM2413_dataWrite(unsigned data){\r
+ OPLL_writeIO(opll,1,data);\r
+}\r
+\r
PICO_INTERNAL void *YM2413GetRegs(void)\r
{\r
memcpy(opll_buf.reg, opll->reg, sizeof(opll->reg));\r
opll->adr = opll_buf.adr;\r
}\r
\r
-static resampler_t *fmresampler;\r
-static int (*PsndFMUpdate)(s32 *buffer, int length, int stereo, int is_buf_empty);\r
-\r
PICO_INTERNAL void PsndInit(void)\r
{\r
- opll = OPLL_new(YM2413_CLK, PicoIn.sndRate);\r
+ opll = OPLL_new(OSC_NTSC/15, OSC_NTSC/15/72);\r
OPLL_setChipType(opll,0);\r
OPLL_reset(opll);\r
}\r
return ymchans;\r
}\r
\r
-static void YM2612_setup_FIR(int inrate, int outrate, int stereo)\r
+// resample SMS FM from its native 49716Hz/49262Hz with polyphase FIR filter\r
+static void YM2413Update(s32 *buffer, int length, int stereo)\r
+{\r
+ while (length-- > 0) {\r
+ int16_t getdata = OPLL_calc(opll) * 3;\r
+ *buffer++ = getdata;\r
+ buffer += stereo; // only left for stereo, to be mixed to right later\r
+ }\r
+}\r
+\r
+static int YM2413UpdateFIR(s32 *buffer, int length, int stereo, int is_buf_empty)\r
+{\r
+ if (!is_buf_empty) memset(buffer, 0, (length << stereo) * sizeof(*buffer));\r
+ resampler_update(fmresampler, buffer, length, YM2413Update);\r
+ return 0;\r
+}\r
+\r
+// FIR setup, looks for a close enough rational number matching the ratio\r
+static void YMFM_setup_FIR(int inrate, int outrate, int stereo)\r
{\r
int mindiff = 999;\r
int diff, mul, div;\r
void *state = NULL;\r
int target_fps = Pico.m.pal ? 50 : 60;\r
int target_lines = Pico.m.pal ? 313 : 262;\r
+ int sms_clock = Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15;\r
int ym2612_clock = Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7;\r
int ym2612_rate = YM2612_NATIVE_RATE();\r
+ int ym2413_rate = (sms_clock + 36) / 72;\r
\r
if (preserve_state) {\r
state = malloc(0x204);\r
if (state == NULL) return;\r
ym2612_pack_state();\r
memcpy(state, YM2612GetRegs(), 0x204);\r
+\r
+ if (opll != NULL)\r
+ memcpy(&old_opll, opll, sizeof(OPLL)); // remember old state\r
}\r
- if ((PicoIn.opt & POPT_EN_FM_FILTER) && ym2612_rate != PicoIn.sndRate) {\r
+ if (PicoIn.AHW & PAHW_SMS) {\r
+ OPLL_setRate(opll, ym2413_rate);\r
+ OPLL_reset(opll);\r
+ YMFM_setup_FIR(ym2413_rate, PicoIn.sndRate, 0);\r
+ PsndFMUpdate = YM2413UpdateFIR;\r
+ } else if ((PicoIn.opt & POPT_EN_FM_FILTER) && ym2612_rate != PicoIn.sndRate) {\r
// polyphase FIR resampler, resampling directly from native to output rate\r
YM2612Init(ym2612_clock, ym2612_rate,\r
((PicoIn.opt&POPT_DIS_FM_SSGEG) ? 0 : ST_SSG) |\r
((PicoIn.opt&POPT_EN_FM_DAC) ? ST_DAC : 0));\r
- YM2612_setup_FIR(ym2612_rate, PicoIn.sndRate, PicoIn.opt & POPT_EN_STEREO);\r
+ YMFM_setup_FIR(ym2612_rate, PicoIn.sndRate, PicoIn.opt & POPT_EN_STEREO);\r
PsndFMUpdate = YM2612UpdateFIR;\r
} else {\r
YM2612Init(ym2612_clock, PicoIn.sndRate,\r
// feed it back it's own registers, just like after loading state\r
memcpy(YM2612GetRegs(), state, 0x204);\r
ym2612_unpack_state();\r
+\r
+ if (opll != NULL) {\r
+ memcpy(&opll->adr, &old_opll.adr, sizeof(OPLL)-20); // restore old state\r
+ OPLL_forceRefresh(opll);\r
+ }\r
}\r
\r
if (preserve_state) memcpy(state, sn76496_regs, 28*4); // remember old state\r
SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PicoIn.sndRate);\r
if (preserve_state) memcpy(sn76496_regs, state, 28*4); // restore old state\r
\r
- if(opll != NULL){\r
- if (preserve_state) memcpy(&old_opll, opll, sizeof(OPLL)); // remember old state\r
- OPLL_setRate(opll, PicoIn.sndRate);\r
- OPLL_reset(opll);\r
- if (preserve_state) memcpy(&opll->adr, &old_opll.adr, sizeof(OPLL)-20); // restore old state\r
- OPLL_forceRefresh(opll);\r
- }\r
-\r
if (state)\r
free(state);\r
\r
SN76496Update(PicoIn.sndOut + pos, len, stereo);\r
}\r
\r
-#if 0\r
-PICO_INTERNAL void PsndDoYM2413(int cyc_to)\r
+PICO_INTERNAL void PsndDoSMSFM(int cyc_to)\r
{\r
int pos, len;\r
int stereo = 0;\r
- s16 *buf;\r
+ s32 *buf32 = PsndBuffer;\r
+ s16 *buf = PicoIn.sndOut;\r
\r
// nothing to do if sound is off\r
if (!PicoIn.sndOut) return;\r
pos <<= 1;\r
}\r
\r
- buf = PicoIn.sndOut + pos;\r
- while (len-- > 0) {\r
- int16_t getdata = OPLL_calc(opll) * 3;\r
- *buf++ += getdata;\r
- buf += stereo; // only left for stereo, to be mixed to right later\r
+ if (Pico.m.hardware & PMS_HW_FMUSED) {\r
+ buf += pos;\r
+ PsndFMUpdate(buf32, len, 0, 0);\r
+ while (len--) {\r
+ *buf++ += *buf32++;\r
+ buf += stereo;\r
+ }\r
}\r
}\r
-#endif\r
-\r
-void YM2413_regWrite(unsigned data){\r
- OPLL_writeIO(opll,0,data);\r
-}\r
-void YM2413_dataWrite(unsigned data){\r
- OPLL_writeIO(opll,1,data);\r
-}\r
-\r
\r
PICO_INTERNAL void PsndDoFM(int cyc_to)\r
{\r
\r
static int PsndRenderMS(int offset, int length)\r
{\r
+ s32 *buf32 = PsndBuffer;\r
int stereo = (PicoIn.opt & 8) >> 3;\r
int psglen = ((Pico.snd.psg_pos+0x80000) >> 20);\r
int ym2413len = ((Pico.snd.ym2413_pos+0x80000) >> 20);\r
s16 *ym2413buf = PicoIn.sndOut + (ym2413len << stereo);\r
Pico.snd.ym2413_pos += (length-ym2413len) << 20;\r
int len = (length-ym2413len);\r
- if (PicoIn.opt & POPT_EN_YM2413){\r
- while (len-- > 0) {\r
- int16_t getdata = OPLL_calc(opll) * 3;\r
- *ym2413buf += getdata;\r
- ym2413buf += 1<<stereo;\r
+ if (Pico.m.hardware & PMS_HW_FMUSED) {\r
+ PsndFMUpdate(buf32, len, 0, 0);\r
+ while (len--) {\r
+ *ym2413buf++ += *buf32++;\r
+ ym2413buf += stereo;\r
}\r
}\r
}\r