| 1 | /*\r |
| 2 | * PicoDrive\r |
| 3 | * (c) Copyright Dave, 2004\r |
| 4 | * (C) notaz, 2006-2009\r |
| 5 | * (C) irixxxx, 2019-2024\r |
| 6 | *\r |
| 7 | * This work is licensed under the terms of MAME license.\r |
| 8 | * See COPYING file in the top-level directory.\r |
| 9 | */\r |
| 10 | \r |
| 11 | #include <string.h>\r |
| 12 | #include "../pico_int.h"\r |
| 13 | #include "ym2612.h"\r |
| 14 | #include "ym2413.h"\r |
| 15 | #include "sn76496.h"\r |
| 16 | #include "../cd/megasd.h"\r |
| 17 | #include "resampler.h"\r |
| 18 | #include "mix.h"\r |
| 19 | \r |
| 20 | #define YM2612_CH6PAN 0x1b6 // panning register for channel 6 (used for DAC)\r |
| 21 | \r |
| 22 | void (*PsndMix_32_to_16)(s16 *dest, s32 *src, int count) = mix_32_to_16_stereo;\r |
| 23 | \r |
| 24 | // master int buffer to mix to\r |
| 25 | // +1 for a fill triggered by an instruction overhanging into the next scanline\r |
| 26 | static s32 PsndBuffer[2*(54000+100)/50+2];\r |
| 27 | \r |
| 28 | // cdda output buffer\r |
| 29 | s16 cdda_out_buffer[2*1152];\r |
| 30 | \r |
| 31 | // FM resampling polyphase FIR\r |
| 32 | static resampler_t *fmresampler;\r |
| 33 | static int (*PsndFMUpdate)(s32 *buffer, int length, int stereo, int is_buf_empty);\r |
| 34 | \r |
| 35 | PICO_INTERNAL void PsndInit(void)\r |
| 36 | {\r |
| 37 | opll = OPLL_new(OSC_NTSC/15, OSC_NTSC/15/72);\r |
| 38 | OPLL_setChipType(opll,0);\r |
| 39 | OPLL_reset(opll);\r |
| 40 | }\r |
| 41 | \r |
| 42 | PICO_INTERNAL void PsndExit(void)\r |
| 43 | {\r |
| 44 | OPLL_delete(opll);\r |
| 45 | opll = NULL;\r |
| 46 | \r |
| 47 | resampler_free(fmresampler); fmresampler = NULL;\r |
| 48 | }\r |
| 49 | \r |
| 50 | PICO_INTERNAL void PsndReset(void)\r |
| 51 | {\r |
| 52 | // PsndRerate calls YM2612Init, which also resets\r |
| 53 | PsndRerate(0);\r |
| 54 | timers_reset();\r |
| 55 | }\r |
| 56 | \r |
| 57 | // FM polyphase FIR resampling\r |
| 58 | #define FMFIR_TAPS 8\r |
| 59 | \r |
| 60 | // resample FM from its native 53267Hz/52781Hz with polyphase FIR filter\r |
| 61 | static int ymchans;\r |
| 62 | static void YM2612Update(s32 *buffer, int length, int stereo)\r |
| 63 | {\r |
| 64 | ymchans = YM2612UpdateOne(buffer, length, stereo, 1);\r |
| 65 | }\r |
| 66 | \r |
| 67 | static int YM2612UpdateFIR(s32 *buffer, int length, int stereo, int is_buf_empty)\r |
| 68 | {\r |
| 69 | resampler_update(fmresampler, buffer, length, YM2612Update);\r |
| 70 | return ymchans;\r |
| 71 | }\r |
| 72 | \r |
| 73 | // resample SMS FM from its native 49716Hz/49262Hz with polyphase FIR filter\r |
| 74 | static void YM2413Update(s32 *buffer, int length, int stereo)\r |
| 75 | {\r |
| 76 | while (length-- > 0) {\r |
| 77 | int16_t getdata = OPLL_calc(opll) * 3;\r |
| 78 | *buffer++ = getdata;\r |
| 79 | buffer += stereo; // only left for stereo, to be mixed to right later\r |
| 80 | }\r |
| 81 | }\r |
| 82 | \r |
| 83 | static int YM2413UpdateFIR(s32 *buffer, int length, int stereo, int is_buf_empty)\r |
| 84 | {\r |
| 85 | if (!is_buf_empty) memset(buffer, 0, (length << stereo) * sizeof(*buffer));\r |
| 86 | resampler_update(fmresampler, buffer, length, YM2413Update);\r |
| 87 | return 0;\r |
| 88 | }\r |
| 89 | \r |
| 90 | // FIR setup, looks for a close enough rational number matching the ratio\r |
| 91 | static void YMFM_setup_FIR(int inrate, int outrate, int stereo)\r |
| 92 | {\r |
| 93 | int mindiff = 999;\r |
| 94 | int diff, mul, div;\r |
| 95 | int minmult = 11, maxmult = 61; // min,max interpolation factor\r |
| 96 | \r |
| 97 | // compute filter ratio with largest multiplier for smallest error\r |
| 98 | for (mul = minmult; mul <= maxmult; mul++) {\r |
| 99 | div = (inrate*mul + outrate/2) / outrate;\r |
| 100 | diff = outrate*div/mul - inrate;\r |
| 101 | if (abs(diff) < abs(mindiff)) {\r |
| 102 | mindiff = diff;\r |
| 103 | Pico.snd.fm_fir_mul = mul;\r |
| 104 | Pico.snd.fm_fir_div = div;\r |
| 105 | if (abs(mindiff)*1000 <= inrate) break; // below error limit\r |
| 106 | }\r |
| 107 | }\r |
| 108 | printf("FM polyphase FIR ratio=%d/%d error=%.3f%%\n",\r |
| 109 | Pico.snd.fm_fir_mul, Pico.snd.fm_fir_div, 100.0*mindiff/inrate);\r |
| 110 | \r |
| 111 | resampler_free(fmresampler);\r |
| 112 | fmresampler = resampler_new(FMFIR_TAPS, Pico.snd.fm_fir_mul, Pico.snd.fm_fir_div,\r |
| 113 | 0.85, 2, 2*inrate/50, stereo);\r |
| 114 | }\r |
| 115 | \r |
| 116 | // wrapper for the YM2612UpdateONE macro\r |
| 117 | static int YM2612UpdateONE(s32 *buffer, int length, int stereo, int is_buf_empty)\r |
| 118 | {\r |
| 119 | return YM2612UpdateOne(buffer, length, stereo, is_buf_empty);\r |
| 120 | }\r |
| 121 | \r |
| 122 | static int ymclock;\r |
| 123 | static int ymrate;\r |
| 124 | static int ymopts;\r |
| 125 | \r |
| 126 | // to be called after changing sound rate or chips\r |
| 127 | void PsndRerate(int preserve_state)\r |
| 128 | {\r |
| 129 | void *state = NULL;\r |
| 130 | int target_fps = Pico.m.pal ? 50 : 60;\r |
| 131 | int target_lines = Pico.m.pal ? 313 : 262;\r |
| 132 | int sms_clock = Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15;\r |
| 133 | int ym2413_rate = (sms_clock + 36) / 72;\r |
| 134 | int ym2612_clock = Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7;\r |
| 135 | int ym2612_rate = YM2612_NATIVE_RATE();\r |
| 136 | int ym2612_init = !preserve_state;\r |
| 137 | int state_size = 4096;\r |
| 138 | \r |
| 139 | // don't init YM2612 if preserve_state and no parameter changes\r |
| 140 | ym2612_init |= ymclock != ym2612_clock || ymopts != (PicoIn.opt & (POPT_DIS_FM_SSGEG|POPT_EN_FM_DAC));\r |
| 141 | ym2612_init |= ymrate != (PicoIn.opt & POPT_EN_FM_FILTER ? ym2612_rate : PicoIn.sndRate);\r |
| 142 | ymclock = ym2612_clock;\r |
| 143 | ymrate = (PicoIn.opt & POPT_EN_FM_FILTER ? ym2612_rate : PicoIn.sndRate);\r |
| 144 | ymopts = PicoIn.opt & (POPT_DIS_FM_SSGEG|POPT_EN_FM_DAC);\r |
| 145 | \r |
| 146 | if (preserve_state && ym2612_init) {\r |
| 147 | state = malloc(state_size);\r |
| 148 | if (state)\r |
| 149 | state_size = YM2612PicoStateSave3(state, state_size);\r |
| 150 | }\r |
| 151 | \r |
| 152 | if (PicoIn.AHW & PAHW_SMS) {\r |
| 153 | OPLL_setRate(opll, ym2413_rate);\r |
| 154 | if (!preserve_state)\r |
| 155 | OPLL_reset(opll);\r |
| 156 | YMFM_setup_FIR(ym2413_rate, PicoIn.sndRate, 0);\r |
| 157 | PsndFMUpdate = YM2413UpdateFIR;\r |
| 158 | } else if ((PicoIn.opt & POPT_EN_FM_FILTER) && ym2612_rate != PicoIn.sndRate) {\r |
| 159 | // polyphase FIR resampler, resampling directly from native to output rate\r |
| 160 | if (ym2612_init)\r |
| 161 | YM2612Init(ym2612_clock, ym2612_rate,\r |
| 162 | ((PicoIn.opt&POPT_DIS_FM_SSGEG) ? 0 : ST_SSG) |\r |
| 163 | ((PicoIn.opt&POPT_EN_FM_DAC) ? ST_DAC : 0));\r |
| 164 | YMFM_setup_FIR(ym2612_rate, PicoIn.sndRate, PicoIn.opt & POPT_EN_STEREO);\r |
| 165 | PsndFMUpdate = YM2612UpdateFIR;\r |
| 166 | } else {\r |
| 167 | if (ym2612_init)\r |
| 168 | YM2612Init(ym2612_clock, PicoIn.sndRate,\r |
| 169 | ((PicoIn.opt&POPT_DIS_FM_SSGEG) ? 0 : ST_SSG) |\r |
| 170 | ((PicoIn.opt&POPT_EN_FM_DAC) ? ST_DAC : 0));\r |
| 171 | PsndFMUpdate = YM2612UpdateONE;\r |
| 172 | }\r |
| 173 | \r |
| 174 | if (state) {\r |
| 175 | YM2612PicoStateLoad3(state, state_size);\r |
| 176 | free(state);\r |
| 177 | }\r |
| 178 | \r |
| 179 | if (preserve_state)\r |
| 180 | SN76496_set_clockrate(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PicoIn.sndRate);\r |
| 181 | else\r |
| 182 | SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PicoIn.sndRate);\r |
| 183 | \r |
| 184 | // calculate Pico.snd.len\r |
| 185 | Pico.snd.len = PicoIn.sndRate / target_fps;\r |
| 186 | Pico.snd.len_e_add = ((PicoIn.sndRate - Pico.snd.len * target_fps) << 16) / target_fps;\r |
| 187 | Pico.snd.len_e_cnt = 0; // Q16\r |
| 188 | \r |
| 189 | // samples per line (Q16)\r |
| 190 | Pico.snd.smpl_mult = 65536LL * PicoIn.sndRate / (target_fps*target_lines);\r |
| 191 | // samples per z80 clock (Q20)\r |
| 192 | Pico.snd.clkz_mult = 16 * Pico.snd.smpl_mult * 15/7 / 488.5;\r |
| 193 | // samples per 44.1 KHz sample (Q16)\r |
| 194 | Pico.snd.cdda_mult = 65536LL * 44100 / PicoIn.sndRate;\r |
| 195 | Pico.snd.cdda_div = 65536LL * PicoIn.sndRate / 44100;\r |
| 196 | \r |
| 197 | // clear all buffers\r |
| 198 | memset32(PsndBuffer, 0, sizeof(PsndBuffer)/4);\r |
| 199 | memset(cdda_out_buffer, 0, sizeof(cdda_out_buffer));\r |
| 200 | if (PicoIn.sndOut)\r |
| 201 | PsndClear();\r |
| 202 | \r |
| 203 | // set mixer\r |
| 204 | PsndMix_32_to_16 = (PicoIn.opt & POPT_EN_STEREO) ? mix_32_to_16_stereo : mix_32_to_16_mono;\r |
| 205 | mix_reset(PicoIn.opt & POPT_EN_SNDFILTER ? PicoIn.sndFilterAlpha : 0);\r |
| 206 | \r |
| 207 | if (PicoIn.AHW & PAHW_PICO)\r |
| 208 | PicoReratePico();\r |
| 209 | }\r |
| 210 | \r |
| 211 | \r |
| 212 | PICO_INTERNAL void PsndStartFrame(void)\r |
| 213 | {\r |
| 214 | // compensate for float part of Pico.snd.len\r |
| 215 | Pico.snd.len_use = Pico.snd.len;\r |
| 216 | Pico.snd.len_e_cnt += Pico.snd.len_e_add;\r |
| 217 | if (Pico.snd.len_e_cnt >= 0x10000) {\r |
| 218 | Pico.snd.len_e_cnt -= 0x10000;\r |
| 219 | Pico.snd.len_use++;\r |
| 220 | }\r |
| 221 | }\r |
| 222 | \r |
| 223 | PICO_INTERNAL void PsndDoDAC(int cyc_to)\r |
| 224 | {\r |
| 225 | int pos, len;\r |
| 226 | int dout = ym2612.dacout;\r |
| 227 | \r |
| 228 | // nothing to do if sound is off\r |
| 229 | if (!PicoIn.sndOut) return;\r |
| 230 | \r |
| 231 | // number of samples to fill in buffer (Q20)\r |
| 232 | len = (cyc_to * Pico.snd.clkz_mult) - Pico.snd.dac_pos;\r |
| 233 | \r |
| 234 | // update position and calculate buffer offset and length\r |
| 235 | pos = (Pico.snd.dac_pos+0x80000) >> 20;\r |
| 236 | Pico.snd.dac_pos += len;\r |
| 237 | len = ((Pico.snd.dac_pos+0x80000) >> 20) - pos;\r |
| 238 | \r |
| 239 | // avoid loss of the 1st sample of a new block (Q rounding issues)\r |
| 240 | if (pos+len == 0)\r |
| 241 | len = 1, Pico.snd.dac_pos += 0x80000;\r |
| 242 | if (len <= 0)\r |
| 243 | return;\r |
| 244 | \r |
| 245 | // fill buffer, applying a rather weak order 1 bessel IIR on the way\r |
| 246 | // y[n] = (x[n] + x[n-1])*(1/2) (3dB cutoff at 11025 Hz, no gain)\r |
| 247 | // 1 sample delay for correct IIR filtering over audio frame boundaries\r |
| 248 | if (PicoIn.opt & POPT_EN_STEREO) {\r |
| 249 | s16 *d = PicoIn.sndOut + pos*2;\r |
| 250 | int pan = ym2612.REGS[YM2612_CH6PAN];\r |
| 251 | int l = pan & 0x80 ? Pico.snd.dac_val : 0;\r |
| 252 | int r = pan & 0x40 ? Pico.snd.dac_val : 0;\r |
| 253 | *d++ += pan & 0x80 ? Pico.snd.dac_val2 : 0;\r |
| 254 | *d++ += pan & 0x40 ? Pico.snd.dac_val2 : 0;\r |
| 255 | while (--len) *d++ += l, *d++ += r;\r |
| 256 | } else {\r |
| 257 | s16 *d = PicoIn.sndOut + pos;\r |
| 258 | *d++ += Pico.snd.dac_val2;\r |
| 259 | while (--len) *d++ += Pico.snd.dac_val;\r |
| 260 | }\r |
| 261 | Pico.snd.dac_val2 = (Pico.snd.dac_val + dout) >> 1;\r |
| 262 | Pico.snd.dac_val = dout;\r |
| 263 | }\r |
| 264 | \r |
| 265 | PICO_INTERNAL void PsndDoPSG(int cyc_to)\r |
| 266 | {\r |
| 267 | int pos, len;\r |
| 268 | int stereo = 0;\r |
| 269 | \r |
| 270 | // nothing to do if sound is off\r |
| 271 | if (!PicoIn.sndOut) return;\r |
| 272 | \r |
| 273 | // number of samples to fill in buffer (Q20)\r |
| 274 | len = (cyc_to * Pico.snd.clkz_mult) - Pico.snd.psg_pos;\r |
| 275 | \r |
| 276 | // update position and calculate buffer offset and length\r |
| 277 | pos = (Pico.snd.psg_pos+0x80000) >> 20;\r |
| 278 | Pico.snd.psg_pos += len;\r |
| 279 | len = ((Pico.snd.psg_pos+0x80000) >> 20) - pos;\r |
| 280 | \r |
| 281 | if (len <= 0)\r |
| 282 | return;\r |
| 283 | if (!(PicoIn.opt & POPT_EN_PSG))\r |
| 284 | return;\r |
| 285 | \r |
| 286 | if (PicoIn.opt & POPT_EN_STEREO) {\r |
| 287 | stereo = 1;\r |
| 288 | pos <<= 1;\r |
| 289 | }\r |
| 290 | SN76496Update(PicoIn.sndOut + pos, len, stereo);\r |
| 291 | }\r |
| 292 | \r |
| 293 | PICO_INTERNAL void PsndDoSMSFM(int cyc_to)\r |
| 294 | {\r |
| 295 | int pos, len;\r |
| 296 | int stereo = 0;\r |
| 297 | s32 *buf32 = PsndBuffer;\r |
| 298 | s16 *buf = PicoIn.sndOut;\r |
| 299 | \r |
| 300 | // nothing to do if sound is off\r |
| 301 | if (!PicoIn.sndOut) return;\r |
| 302 | \r |
| 303 | // number of samples to fill in buffer (Q20)\r |
| 304 | len = (cyc_to * Pico.snd.clkz_mult) - Pico.snd.ym2413_pos;\r |
| 305 | \r |
| 306 | // update position and calculate buffer offset and length\r |
| 307 | pos = (Pico.snd.ym2413_pos+0x80000) >> 20;\r |
| 308 | Pico.snd.ym2413_pos += len;\r |
| 309 | len = ((Pico.snd.ym2413_pos+0x80000) >> 20) - pos;\r |
| 310 | \r |
| 311 | if (len <= 0)\r |
| 312 | return;\r |
| 313 | if (!(PicoIn.opt & POPT_EN_YM2413))\r |
| 314 | return;\r |
| 315 | \r |
| 316 | if (PicoIn.opt & POPT_EN_STEREO) {\r |
| 317 | stereo = 1;\r |
| 318 | pos <<= 1;\r |
| 319 | }\r |
| 320 | \r |
| 321 | if (Pico.m.hardware & PMS_HW_FMUSED) {\r |
| 322 | buf += pos;\r |
| 323 | PsndFMUpdate(buf32, len, 0, 0);\r |
| 324 | if (stereo) \r |
| 325 | while (len--) {\r |
| 326 | *buf++ += *buf32;\r |
| 327 | *buf++ += *buf32++;\r |
| 328 | }\r |
| 329 | else\r |
| 330 | while (len--) {\r |
| 331 | *buf++ += *buf32++;\r |
| 332 | }\r |
| 333 | }\r |
| 334 | }\r |
| 335 | \r |
| 336 | PICO_INTERNAL void PsndDoFM(int cyc_to)\r |
| 337 | {\r |
| 338 | int pos, len;\r |
| 339 | int stereo = 0;\r |
| 340 | \r |
| 341 | // nothing to do if sound is off\r |
| 342 | if (!PicoIn.sndOut) return;\r |
| 343 | \r |
| 344 | // Q20, number of samples since last call\r |
| 345 | len = (cyc_to * Pico.snd.clkz_mult) - Pico.snd.fm_pos;\r |
| 346 | \r |
| 347 | // update position and calculate buffer offset and length\r |
| 348 | pos = (Pico.snd.fm_pos+0x80000) >> 20;\r |
| 349 | Pico.snd.fm_pos += len;\r |
| 350 | len = ((Pico.snd.fm_pos+0x80000) >> 20) - pos;\r |
| 351 | if (len <= 0)\r |
| 352 | return;\r |
| 353 | \r |
| 354 | // fill buffer\r |
| 355 | if (PicoIn.opt & POPT_EN_STEREO) {\r |
| 356 | stereo = 1;\r |
| 357 | pos <<= 1;\r |
| 358 | }\r |
| 359 | if (PicoIn.opt & POPT_EN_FM)\r |
| 360 | PsndFMUpdate(PsndBuffer + pos, len, stereo, 1);\r |
| 361 | }\r |
| 362 | \r |
| 363 | PICO_INTERNAL void PsndDoPCM(int cyc_to)\r |
| 364 | {\r |
| 365 | int pos, len;\r |
| 366 | int stereo = 0;\r |
| 367 | \r |
| 368 | // nothing to do if sound is off\r |
| 369 | if (!PicoIn.sndOut) return;\r |
| 370 | \r |
| 371 | // Q20, number of samples since last call\r |
| 372 | len = (cyc_to * Pico.snd.clkz_mult) - Pico.snd.pcm_pos;\r |
| 373 | \r |
| 374 | // update position and calculate buffer offset and length\r |
| 375 | pos = (Pico.snd.pcm_pos+0x80000) >> 20;\r |
| 376 | Pico.snd.pcm_pos += len;\r |
| 377 | len = ((Pico.snd.pcm_pos+0x80000) >> 20) - pos;\r |
| 378 | if (len <= 0)\r |
| 379 | return;\r |
| 380 | \r |
| 381 | // fill buffer\r |
| 382 | if (PicoIn.opt & POPT_EN_STEREO) {\r |
| 383 | stereo = 1;\r |
| 384 | pos <<= 1;\r |
| 385 | }\r |
| 386 | PicoPicoPCMUpdate(PicoIn.sndOut + pos, len, stereo);\r |
| 387 | }\r |
| 388 | \r |
| 389 | // cdda\r |
| 390 | static void cdda_raw_update(s32 *buffer, int length, int stereo)\r |
| 391 | {\r |
| 392 | int ret, cdda_bytes;\r |
| 393 | \r |
| 394 | // apply start offset in frame (offset to 1st lba to play)\r |
| 395 | int offs = Pico_mcd->cdda_frame_offs * Pico.snd.cdda_div >> 16;\r |
| 396 | length -= offs;\r |
| 397 | buffer += offs * (stereo ? 2 : 1);\r |
| 398 | Pico_mcd->cdda_frame_offs = 0;\r |
| 399 | \r |
| 400 | cdda_bytes = (length * Pico.snd.cdda_mult >> 16) * 4;\r |
| 401 | \r |
| 402 | // compute offset of last played sample in this frame (need for save/load)\r |
| 403 | Pico_mcd->m.cdda_lba_offset += cdda_bytes/4;\r |
| 404 | while (Pico_mcd->m.cdda_lba_offset >= 2352/4)\r |
| 405 | Pico_mcd->m.cdda_lba_offset -= 2352/4;\r |
| 406 | \r |
| 407 | ret = pm_read_audio(cdda_out_buffer, cdda_bytes, Pico_mcd->cdda_stream);\r |
| 408 | if (ret < cdda_bytes) {\r |
| 409 | memset((char *)cdda_out_buffer + ret, 0, cdda_bytes - ret);\r |
| 410 | Pico_mcd->cdda_stream = NULL;\r |
| 411 | }\r |
| 412 | \r |
| 413 | // now mix\r |
| 414 | if (stereo) switch (Pico.snd.cdda_mult) {\r |
| 415 | case 0x10000: mix_16h_to_32(buffer, cdda_out_buffer, length*2); break;\r |
| 416 | case 0x20000: mix_16h_to_32_s1(buffer, cdda_out_buffer, length*2); break;\r |
| 417 | case 0x40000: mix_16h_to_32_s2(buffer, cdda_out_buffer, length*2); break;\r |
| 418 | default: mix_16h_to_32_resample_stereo(buffer, cdda_out_buffer, length, Pico.snd.cdda_mult);\r |
| 419 | } else\r |
| 420 | mix_16h_to_32_resample_mono(buffer, cdda_out_buffer, length, Pico.snd.cdda_mult);\r |
| 421 | }\r |
| 422 | \r |
| 423 | void cdda_start_play(int lba_base, int lba_offset, int lb_len)\r |
| 424 | {\r |
| 425 | if (Pico_mcd->cdda_type == CT_MP3)\r |
| 426 | {\r |
| 427 | int pos1024 = 0;\r |
| 428 | \r |
| 429 | if (lba_offset)\r |
| 430 | pos1024 = lba_offset * 1024 / lb_len;\r |
| 431 | \r |
| 432 | mp3_start_play(Pico_mcd->cdda_stream, pos1024);\r |
| 433 | return;\r |
| 434 | }\r |
| 435 | \r |
| 436 | // on restart after loading, consider offset of last played sample\r |
| 437 | pm_seek(Pico_mcd->cdda_stream, (lba_base + lba_offset) * 2352 +\r |
| 438 | Pico_mcd->m.cdda_lba_offset * 4, SEEK_SET);\r |
| 439 | if (Pico_mcd->cdda_type == CT_WAV)\r |
| 440 | {\r |
| 441 | // skip headers, assume it's 44kHz stereo uncompressed\r |
| 442 | pm_seek(Pico_mcd->cdda_stream, 44, SEEK_CUR);\r |
| 443 | }\r |
| 444 | }\r |
| 445 | \r |
| 446 | \r |
| 447 | PICO_INTERNAL void PsndClear(void)\r |
| 448 | {\r |
| 449 | int len = Pico.snd.len;\r |
| 450 | if (Pico.snd.len_e_add) len++;\r |
| 451 | \r |
| 452 | // drop pos remainder to avoid rounding errors (not entirely correct though)\r |
| 453 | Pico.snd.dac_pos = Pico.snd.fm_pos = Pico.snd.psg_pos = Pico.snd.ym2413_pos = Pico.snd.pcm_pos = 0;\r |
| 454 | if (!PicoIn.sndOut) return;\r |
| 455 | \r |
| 456 | if (PicoIn.opt & POPT_EN_STEREO)\r |
| 457 | memset32((int *) PicoIn.sndOut, 0, len); // assume PicoIn.sndOut to be aligned\r |
| 458 | else {\r |
| 459 | s16 *out = PicoIn.sndOut;\r |
| 460 | if ((uintptr_t)out & 2) { *out++ = 0; len--; }\r |
| 461 | memset32((int *) out, 0, len/2);\r |
| 462 | if (len & 1) out[len-1] = 0;\r |
| 463 | }\r |
| 464 | if (!(PicoIn.opt & POPT_EN_FM))\r |
| 465 | memset32(PsndBuffer, 0, PicoIn.opt & POPT_EN_STEREO ? len*2 : len);\r |
| 466 | }\r |
| 467 | \r |
| 468 | \r |
| 469 | static int PsndRender(int offset, int length)\r |
| 470 | {\r |
| 471 | s32 *buf32;\r |
| 472 | int stereo = (PicoIn.opt & 8) >> 3;\r |
| 473 | int fmlen = ((Pico.snd.fm_pos+0x80000) >> 20);\r |
| 474 | int daclen = ((Pico.snd.dac_pos+0x80000) >> 20);\r |
| 475 | int psglen = ((Pico.snd.psg_pos+0x80000) >> 20);\r |
| 476 | int pcmlen = ((Pico.snd.pcm_pos+0x80000) >> 20);\r |
| 477 | \r |
| 478 | buf32 = PsndBuffer+(offset<<stereo);\r |
| 479 | \r |
| 480 | pprof_start(sound);\r |
| 481 | \r |
| 482 | // Add in parts of the PSG output not yet done\r |
| 483 | if (length-psglen > 0 && PicoIn.sndOut) {\r |
| 484 | s16 *psgbuf = PicoIn.sndOut + (psglen << stereo);\r |
| 485 | Pico.snd.psg_pos += (length-psglen) << 20;\r |
| 486 | if (PicoIn.opt & POPT_EN_PSG)\r |
| 487 | SN76496Update(psgbuf, length-psglen, stereo);\r |
| 488 | }\r |
| 489 | \r |
| 490 | if (PicoIn.AHW & PAHW_PICO) {\r |
| 491 | // always need to render sound for interrupts\r |
| 492 | s16 *buf16 = PicoIn.sndOut ? PicoIn.sndOut + (pcmlen<<stereo) : NULL;\r |
| 493 | PicoPicoPCMUpdate(buf16, length-pcmlen, stereo);\r |
| 494 | return length;\r |
| 495 | }\r |
| 496 | \r |
| 497 | // Fill up DAC output in case of missing samples (Q rounding errors)\r |
| 498 | if (length-daclen > 0 && PicoIn.sndOut) {\r |
| 499 | Pico.snd.dac_pos += (length-daclen) << 20;\r |
| 500 | if (PicoIn.opt & POPT_EN_STEREO) {\r |
| 501 | s16 *d = PicoIn.sndOut + daclen*2;\r |
| 502 | int pan = ym2612.REGS[YM2612_CH6PAN];\r |
| 503 | int l = pan & 0x80 ? Pico.snd.dac_val : 0;\r |
| 504 | int r = pan & 0x40 ? Pico.snd.dac_val : 0;\r |
| 505 | *d++ += pan & 0x80 ? Pico.snd.dac_val2 : 0;\r |
| 506 | *d++ += pan & 0x40 ? Pico.snd.dac_val2 : 0;\r |
| 507 | if (l|r) for (daclen++; length-daclen > 0; daclen++)\r |
| 508 | *d++ += l, *d++ += r;\r |
| 509 | } else {\r |
| 510 | s16 *d = PicoIn.sndOut + daclen;\r |
| 511 | *d++ += Pico.snd.dac_val2;\r |
| 512 | if (Pico.snd.dac_val) for (daclen++; length-daclen > 0; daclen++)\r |
| 513 | *d++ += Pico.snd.dac_val;\r |
| 514 | }\r |
| 515 | Pico.snd.dac_val2 = Pico.snd.dac_val;\r |
| 516 | }\r |
| 517 | \r |
| 518 | // Add in parts of the FM buffer not yet done\r |
| 519 | if (length-fmlen > 0 && PicoIn.sndOut) {\r |
| 520 | s32 *fmbuf = buf32 + ((fmlen-offset) << stereo);\r |
| 521 | Pico.snd.fm_pos += (length-fmlen) << 20;\r |
| 522 | if (PicoIn.opt & POPT_EN_FM)\r |
| 523 | PsndFMUpdate(fmbuf, length-fmlen, stereo, 1);\r |
| 524 | }\r |
| 525 | \r |
| 526 | // CD: PCM sound\r |
| 527 | if (PicoIn.AHW & PAHW_MCD) {\r |
| 528 | pcd_pcm_update(buf32, length-offset, stereo);\r |
| 529 | }\r |
| 530 | \r |
| 531 | // CD: CDDA audio\r |
| 532 | // CD mode, cdda enabled, not data track, CDC is reading\r |
| 533 | if ((PicoIn.AHW & PAHW_MCD) && (PicoIn.opt & POPT_EN_MCD_CDDA)\r |
| 534 | && Pico_mcd->cdda_stream != NULL\r |
| 535 | && (!(Pico_mcd->s68k_regs[0x36] & 1) || Pico_msd.state == 3))\r |
| 536 | {\r |
| 537 | if (Pico_mcd->cdda_type == CT_MP3)\r |
| 538 | mp3_update(buf32, length-offset, stereo);\r |
| 539 | else\r |
| 540 | cdda_raw_update(buf32, length-offset, stereo);\r |
| 541 | }\r |
| 542 | \r |
| 543 | if ((PicoIn.AHW & PAHW_32X) && (PicoIn.opt & POPT_EN_PWM))\r |
| 544 | p32x_pwm_update(buf32, length-offset, stereo);\r |
| 545 | \r |
| 546 | // convert + limit to normal 16bit output\r |
| 547 | if (PicoIn.sndOut)\r |
| 548 | PsndMix_32_to_16(PicoIn.sndOut+(offset<<stereo), buf32, length-offset);\r |
| 549 | \r |
| 550 | pprof_end(sound);\r |
| 551 | \r |
| 552 | return length;\r |
| 553 | }\r |
| 554 | \r |
| 555 | PICO_INTERNAL void PsndGetSamples(int y)\r |
| 556 | {\r |
| 557 | static int curr_pos = 0;\r |
| 558 | \r |
| 559 | curr_pos = PsndRender(0, Pico.snd.len_use);\r |
| 560 | \r |
| 561 | if (PicoIn.writeSound && PicoIn.sndOut)\r |
| 562 | PicoIn.writeSound(curr_pos * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));\r |
| 563 | // clear sound buffer\r |
| 564 | PsndClear();\r |
| 565 | }\r |
| 566 | \r |
| 567 | static int PsndRenderMS(int offset, int length)\r |
| 568 | {\r |
| 569 | s32 *buf32 = PsndBuffer;\r |
| 570 | int stereo = (PicoIn.opt & 8) >> 3;\r |
| 571 | int psglen = ((Pico.snd.psg_pos+0x80000) >> 20);\r |
| 572 | int ym2413len = ((Pico.snd.ym2413_pos+0x80000) >> 20);\r |
| 573 | \r |
| 574 | if (!PicoIn.sndOut)\r |
| 575 | return length;\r |
| 576 | \r |
| 577 | pprof_start(sound);\r |
| 578 | \r |
| 579 | // Add in parts of the PSG output not yet done\r |
| 580 | if (length-psglen > 0) {\r |
| 581 | s16 *psgbuf = PicoIn.sndOut + (psglen << stereo);\r |
| 582 | Pico.snd.psg_pos += (length-psglen) << 20;\r |
| 583 | if (PicoIn.opt & POPT_EN_PSG)\r |
| 584 | SN76496Update(psgbuf, length-psglen, stereo);\r |
| 585 | }\r |
| 586 | \r |
| 587 | if (length-ym2413len > 0) {\r |
| 588 | s16 *ym2413buf = PicoIn.sndOut + (ym2413len << stereo);\r |
| 589 | Pico.snd.ym2413_pos += (length-ym2413len) << 20;\r |
| 590 | int len = (length-ym2413len);\r |
| 591 | if (Pico.m.hardware & PMS_HW_FMUSED) {\r |
| 592 | PsndFMUpdate(buf32, len, 0, 0);\r |
| 593 | if (stereo)\r |
| 594 | while (len--) {\r |
| 595 | *ym2413buf++ += *buf32;\r |
| 596 | *ym2413buf++ += *buf32++;\r |
| 597 | }\r |
| 598 | else\r |
| 599 | while (len--) {\r |
| 600 | *ym2413buf++ += *buf32++;\r |
| 601 | }\r |
| 602 | }\r |
| 603 | }\r |
| 604 | \r |
| 605 | pprof_end(sound);\r |
| 606 | \r |
| 607 | return length;\r |
| 608 | }\r |
| 609 | \r |
| 610 | PICO_INTERNAL void PsndGetSamplesMS(int y)\r |
| 611 | {\r |
| 612 | static int curr_pos = 0;\r |
| 613 | \r |
| 614 | curr_pos = PsndRenderMS(0, Pico.snd.len_use);\r |
| 615 | \r |
| 616 | if (PicoIn.writeSound != NULL && PicoIn.sndOut)\r |
| 617 | PicoIn.writeSound(curr_pos * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));\r |
| 618 | PsndClear();\r |
| 619 | }\r |
| 620 | \r |
| 621 | // vim:shiftwidth=2:ts=2:expandtab\r |