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