pandora: fix readme and pxml version
[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
4d97b0a7 14#include "ym2413.h"\r
cc68a136 15#include "sn76496.h"\r
b0be121b 16#include "../cd/megasd.h"\r
e2e2b6ad 17#include "resampler.h"\r
110a49ed 18#include "mix.h"\r
e2e2b6ad 19\r
73411ac4 20#define YM2612_CH6PAN 0x1b6 // panning register for channel 6 (used for DAC)\r
21\r
70efc52d 22void (*PsndMix_32_to_16)(s16 *dest, s32 *src, int count) = mix_32_to_16_stereo;\r
4a32f01f 23\r
4f265db7 24// master int buffer to mix to\r
1510eeaf 25// +1 for a fill triggered by an instruction overhanging into the next scanline\r
68a95087 26static s32 PsndBuffer[2*(54000+100)/50+2];\r
cc68a136 27\r
c9e1affc 28// cdda output buffer\r
91ea9406 29s16 cdda_out_buffer[2*1152];\r
c9e1affc 30\r
43ef4071 31// FM resampling polyphase FIR\r
32static resampler_t *fmresampler;\r
33static int (*PsndFMUpdate)(s32 *buffer, int length, int stereo, int is_buf_empty);\r
34\r
a2f24bfa 35PICO_INTERNAL void PsndInit(void)\r
36{\r
43ef4071 37 opll = OPLL_new(OSC_NTSC/15, OSC_NTSC/15/72);\r
a2f24bfa 38 OPLL_setChipType(opll,0);\r
39 OPLL_reset(opll);\r
40}\r
41\r
42PICO_INTERNAL void PsndExit(void)\r
43{\r
44 OPLL_delete(opll);\r
45 opll = NULL;\r
e2e2b6ad 46\r
e2e2b6ad 47 resampler_free(fmresampler); fmresampler = NULL;\r
a2f24bfa 48}\r
cc68a136 49\r
9d917eea 50PICO_INTERNAL void PsndReset(void)\r
cc68a136 51{\r
af37bca8 52 // PsndRerate calls YM2612Init, which also resets\r
9d917eea 53 PsndRerate(0);\r
af37bca8 54 timers_reset();\r
cc68a136 55}\r
56\r
e2e2b6ad 57// FM polyphase FIR resampling\r
cc5b0805 58#define FMFIR_TAPS 8\r
e2e2b6ad 59\r
60// resample FM from its native 53267Hz/52781Hz with polyphase FIR filter\r
61static int ymchans;\r
62static void YM2612Update(s32 *buffer, int length, int stereo)\r
63{\r
64 ymchans = YM2612UpdateOne(buffer, length, stereo, 1);\r
65}\r
66\r
110a49ed 67static int YM2612UpdateFIR(s32 *buffer, int length, int stereo, int is_buf_empty)\r
e2e2b6ad 68{\r
69 resampler_update(fmresampler, buffer, length, YM2612Update);\r
70 return ymchans;\r
71}\r
72\r
43ef4071 73// resample SMS FM from its native 49716Hz/49262Hz with polyphase FIR filter\r
74static 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
83static 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
91static void YMFM_setup_FIR(int inrate, int outrate, int stereo)\r
e2e2b6ad 92{\r
93 int mindiff = 999;\r
94 int diff, mul, div;\r
cc5b0805 95 int minmult = 11, maxmult = 61; // min,max interpolation factor\r
e2e2b6ad 96\r
97 // compute filter ratio with largest multiplier for smallest error\r
68a95087 98 for (mul = minmult; mul <= maxmult; mul++) {\r
e2e2b6ad 99 div = (inrate*mul + outrate/2) / outrate;\r
100 diff = outrate*div/mul - inrate;\r
68a95087 101 if (abs(diff) < abs(mindiff)) {\r
e2e2b6ad 102 mindiff = diff;\r
103 Pico.snd.fm_fir_mul = mul;\r
104 Pico.snd.fm_fir_div = div;\r
cc5b0805 105 if (abs(mindiff)*1000 <= inrate) break; // below error limit\r
e2e2b6ad 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
68a95087 113 0.85, 2, 2*inrate/50, stereo);\r
e2e2b6ad 114}\r
cc68a136 115\r
3167aa9a 116// wrapper for the YM2612UpdateONE macro\r
117static 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
864ac1d6 122static int ymclock;\r
123static int ymrate;\r
124static int ymopts;\r
125\r
cc68a136 126// to be called after changing sound rate or chips\r
9d917eea 127void PsndRerate(int preserve_state)\r
cc68a136 128{\r
5f8c85be 129 void *state = NULL;\r
7a93adeb 130 int target_fps = Pico.m.pal ? 50 : 60;\r
8ac9ab7f 131 int target_lines = Pico.m.pal ? 313 : 262;\r
43ef4071 132 int sms_clock = Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15;\r
864ac1d6 133 int ym2413_rate = (sms_clock + 36) / 72;\r
e2e2b6ad 134 int ym2612_clock = Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7;\r
68a95087 135 int ym2612_rate = YM2612_NATIVE_RATE();\r
864ac1d6 136 int ym2612_init = !preserve_state;\r
ccce7ee7 137 int state_size = 4096;\r
cc68a136 138\r
864ac1d6 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
ccce7ee7 147 state = malloc(state_size);\r
148 if (state)\r
149 state_size = YM2612PicoStateSave3(state, state_size);\r
7a93adeb 150 }\r
864ac1d6 151\r
43ef4071 152 if (PicoIn.AHW & PAHW_SMS) {\r
153 OPLL_setRate(opll, ym2413_rate);\r
864ac1d6 154 if (!preserve_state)\r
155 OPLL_reset(opll);\r
43ef4071 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
68a95087 159 // polyphase FIR resampler, resampling directly from native to output rate\r
864ac1d6 160 if (ym2612_init)\r
161 YM2612Init(ym2612_clock, ym2612_rate,\r
8794ba5c 162 ((PicoIn.opt&POPT_DIS_FM_SSGEG) ? 0 : ST_SSG) |\r
23cd73bc 163 ((PicoIn.opt&POPT_EN_FM_DAC) ? ST_DAC : 0));\r
43ef4071 164 YMFM_setup_FIR(ym2612_rate, PicoIn.sndRate, PicoIn.opt & POPT_EN_STEREO);\r
e2e2b6ad 165 PsndFMUpdate = YM2612UpdateFIR;\r
166 } else {\r
864ac1d6 167 if (ym2612_init)\r
168 YM2612Init(ym2612_clock, PicoIn.sndRate,\r
e2e2b6ad 169 ((PicoIn.opt&POPT_DIS_FM_SSGEG) ? 0 : ST_SSG) |\r
170 ((PicoIn.opt&POPT_EN_FM_DAC) ? ST_DAC : 0));\r
3167aa9a 171 PsndFMUpdate = YM2612UpdateONE;\r
e2e2b6ad 172 }\r
864ac1d6 173\r
ccce7ee7 174 if (state) {\r
175 YM2612PicoStateLoad3(state, state_size);\r
864ac1d6 176 free(state);\r
7a93adeb 177 }\r
cc68a136 178\r
864ac1d6 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
5f8c85be 183\r
6311a3ba 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
b9bc876c 187 Pico.snd.len_e_cnt = 0; // Q16\r
cc68a136 188\r
b9bc876c 189 // samples per line (Q16)\r
09b96f99 190 Pico.snd.smpl_mult = 65536LL * PicoIn.sndRate / (target_fps*target_lines);\r
43e14010 191 // samples per z80 clock (Q20)\r
588881c4 192 Pico.snd.clkz_mult = 16 * Pico.snd.smpl_mult * 15/7 / 488.5;\r
193 // samples per 44.1 KHz sample (Q16)\r
f7741cac 194 Pico.snd.cdda_mult = 65536LL * 44100 / PicoIn.sndRate;\r
195 Pico.snd.cdda_div = 65536LL * PicoIn.sndRate / 44100;\r
4f265db7 196\r
7a93adeb 197 // clear all buffers\r
198 memset32(PsndBuffer, 0, sizeof(PsndBuffer)/4);\r
c9e1affc 199 memset(cdda_out_buffer, 0, sizeof(cdda_out_buffer));\r
6311a3ba 200 if (PicoIn.sndOut)\r
9d917eea 201 PsndClear();\r
4a32f01f 202\r
203 // set mixer\r
70efc52d 204 PsndMix_32_to_16 = (PicoIn.opt & POPT_EN_STEREO) ? mix_32_to_16_stereo : mix_32_to_16_mono;\r
37631374 205 mix_reset(PicoIn.opt & POPT_EN_SNDFILTER ? PicoIn.sndFilterAlpha : 0);\r
ed367a3f 206\r
93f9619e 207 if (PicoIn.AHW & PAHW_PICO)\r
ed367a3f 208 PicoReratePico();\r
cc68a136 209}\r
210\r
211\r
4f2cdbf5 212PICO_INTERNAL void PsndStartFrame(void)\r
213{\r
6311a3ba 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
4f2cdbf5 220 }\r
4f2cdbf5 221}\r
222\r
43e14010 223PICO_INTERNAL void PsndDoDAC(int cyc_to)\r
cc68a136 224{\r
43e14010 225 int pos, len;\r
4b9c5888 226 int dout = ym2612.dacout;\r
4f265db7 227\r
9f1d5acd 228 // nothing to do if sound is off\r
229 if (!PicoIn.sndOut) return;\r
230\r
43e14010 231 // number of samples to fill in buffer (Q20)\r
588881c4 232 len = (cyc_to * Pico.snd.clkz_mult) - Pico.snd.dac_pos;\r
43e14010 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
4f2cdbf5 242 if (len <= 0)\r
243 return;\r
244\r
43e14010 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
93f9619e 248 if (PicoIn.opt & POPT_EN_STEREO) {\r
f7741cac 249 s16 *d = PicoIn.sndOut + pos*2;\r
73411ac4 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
4b9c5888 256 } else {\r
f7741cac 257 s16 *d = PicoIn.sndOut + pos;\r
43e14010 258 *d++ += Pico.snd.dac_val2;\r
259 while (--len) *d++ += Pico.snd.dac_val;\r
cc68a136 260 }\r
43e14010 261 Pico.snd.dac_val2 = (Pico.snd.dac_val + dout) >> 1;\r
262 Pico.snd.dac_val = dout;\r
4f265db7 263}\r
cc68a136 264\r
86d6fb9a 265PICO_INTERNAL void PsndDoPSG(int cyc_to)\r
5d638db0 266{\r
09b96f99 267 int pos, len;\r
5d638db0 268 int stereo = 0;\r
269\r
9f1d5acd 270 // nothing to do if sound is off\r
271 if (!PicoIn.sndOut) return;\r
272\r
86d6fb9a 273 // number of samples to fill in buffer (Q20)\r
588881c4 274 len = (cyc_to * Pico.snd.clkz_mult) - Pico.snd.psg_pos;\r
5d638db0 275\r
09b96f99 276 // update position and calculate buffer offset and length\r
86d6fb9a 277 pos = (Pico.snd.psg_pos+0x80000) >> 20;\r
09b96f99 278 Pico.snd.psg_pos += len;\r
86d6fb9a 279 len = ((Pico.snd.psg_pos+0x80000) >> 20) - pos;\r
280\r
86d6fb9a 281 if (len <= 0)\r
282 return;\r
e348af76 283 if (!(PicoIn.opt & POPT_EN_PSG))\r
5d638db0 284 return;\r
285\r
93f9619e 286 if (PicoIn.opt & POPT_EN_STEREO) {\r
5d638db0 287 stereo = 1;\r
288 pos <<= 1;\r
289 }\r
6311a3ba 290 SN76496Update(PicoIn.sndOut + pos, len, stereo);\r
5d638db0 291}\r
292\r
43ef4071 293PICO_INTERNAL void PsndDoSMSFM(int cyc_to)\r
a2f24bfa 294{\r
295 int pos, len;\r
296 int stereo = 0;\r
43ef4071 297 s32 *buf32 = PsndBuffer;\r
298 s16 *buf = PicoIn.sndOut;\r
a2f24bfa 299\r
9f1d5acd 300 // nothing to do if sound is off\r
301 if (!PicoIn.sndOut) return;\r
302\r
86d6fb9a 303 // number of samples to fill in buffer (Q20)\r
588881c4 304 len = (cyc_to * Pico.snd.clkz_mult) - Pico.snd.ym2413_pos;\r
a2f24bfa 305\r
306 // update position and calculate buffer offset and length\r
86d6fb9a 307 pos = (Pico.snd.ym2413_pos+0x80000) >> 20;\r
a2f24bfa 308 Pico.snd.ym2413_pos += len;\r
86d6fb9a 309 len = ((Pico.snd.ym2413_pos+0x80000) >> 20) - pos;\r
310\r
86d6fb9a 311 if (len <= 0)\r
312 return;\r
e348af76 313 if (!(PicoIn.opt & POPT_EN_YM2413))\r
a2f24bfa 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
43ef4071 321 if (Pico.m.hardware & PMS_HW_FMUSED) {\r
322 buf += pos;\r
323 PsndFMUpdate(buf32, len, 0, 0);\r
70efc52d 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
a2f24bfa 333 }\r
334}\r
a2f24bfa 335\r
09b96f99 336PICO_INTERNAL void PsndDoFM(int cyc_to)\r
8ac9ab7f 337{\r
338 int pos, len;\r
339 int stereo = 0;\r
340\r
9f1d5acd 341 // nothing to do if sound is off\r
342 if (!PicoIn.sndOut) return;\r
343\r
027940e1 344 // Q20, number of samples since last call\r
588881c4 345 len = (cyc_to * Pico.snd.clkz_mult) - Pico.snd.fm_pos;\r
8ac9ab7f 346\r
8ac9ab7f 347 // update position and calculate buffer offset and length\r
09b96f99 348 pos = (Pico.snd.fm_pos+0x80000) >> 20;\r
8ac9ab7f 349 Pico.snd.fm_pos += len;\r
09b96f99 350 len = ((Pico.snd.fm_pos+0x80000) >> 20) - pos;\r
15caa286 351 if (len <= 0)\r
352 return;\r
8ac9ab7f 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
e2e2b6ad 360 PsndFMUpdate(PsndBuffer + pos, len, stereo, 1);\r
8ac9ab7f 361}\r
362\r
e348af76 363PICO_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
588881c4 372 len = (cyc_to * Pico.snd.clkz_mult) - Pico.snd.pcm_pos;\r
e348af76 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
c9e1affc 389// cdda\r
f7741cac 390static void cdda_raw_update(s32 *buffer, int length, int stereo)\r
c9e1affc 391{\r
f7741cac 392 int ret, cdda_bytes;\r
c9e1affc 393\r
588881c4 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
f7741cac 400 cdda_bytes = (length * Pico.snd.cdda_mult >> 16) * 4;\r
c9e1affc 401\r
588881c4 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
15ca7152 407 ret = pm_read_audio(cdda_out_buffer, cdda_bytes, Pico_mcd->cdda_stream);\r
c9e1affc 408 if (ret < cdda_bytes) {\r
409 memset((char *)cdda_out_buffer + ret, 0, cdda_bytes - ret);\r
274fcc35 410 Pico_mcd->cdda_stream = NULL;\r
c9e1affc 411 }\r
412\r
413 // now mix\r
f7741cac 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
c9e1affc 421}\r
422\r
274fcc35 423void cdda_start_play(int lba_base, int lba_offset, int lb_len)\r
c9e1affc 424{\r
274fcc35 425 if (Pico_mcd->cdda_type == CT_MP3)\r
c9e1affc 426 {\r
427 int pos1024 = 0;\r
428\r
c9e1affc 429 if (lba_offset)\r
274fcc35 430 pos1024 = lba_offset * 1024 / lb_len;\r
c9e1affc 431\r
274fcc35 432 mp3_start_play(Pico_mcd->cdda_stream, pos1024);\r
c9e1affc 433 return;\r
434 }\r
435\r
588881c4 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
274fcc35 439 if (Pico_mcd->cdda_type == CT_WAV)\r
0bccafeb 440 {\r
441 // skip headers, assume it's 44kHz stereo uncompressed\r
274fcc35 442 pm_seek(Pico_mcd->cdda_stream, 44, SEEK_CUR);\r
0bccafeb 443 }\r
c9e1affc 444}\r
445\r
cc68a136 446\r
9d917eea 447PICO_INTERNAL void PsndClear(void)\r
4f265db7 448{\r
6311a3ba 449 int len = Pico.snd.len;\r
450 if (Pico.snd.len_e_add) len++;\r
9f1d5acd 451\r
452 // drop pos remainder to avoid rounding errors (not entirely correct though)\r
e348af76 453 Pico.snd.dac_pos = Pico.snd.fm_pos = Pico.snd.psg_pos = Pico.snd.ym2413_pos = Pico.snd.pcm_pos = 0;\r
9f1d5acd 454 if (!PicoIn.sndOut) return;\r
455\r
93f9619e 456 if (PicoIn.opt & POPT_EN_STEREO)\r
6311a3ba 457 memset32((int *) PicoIn.sndOut, 0, len); // assume PicoIn.sndOut to be aligned\r
88b3d7c1 458 else {\r
f7741cac 459 s16 *out = PicoIn.sndOut;\r
48c9e01b 460 if ((uintptr_t)out & 2) { *out++ = 0; len--; }\r
88b3d7c1 461 memset32((int *) out, 0, len/2);\r
462 if (len & 1) out[len-1] = 0;\r
463 }\r
b9bc876c 464 if (!(PicoIn.opt & POPT_EN_FM))\r
465 memset32(PsndBuffer, 0, PicoIn.opt & POPT_EN_STEREO ? len*2 : len);\r
cc68a136 466}\r
467\r
468\r
7b3f44c6 469static int PsndRender(int offset, int length)\r
cc68a136 470{\r
9f1d5acd 471 s32 *buf32;\r
93f9619e 472 int stereo = (PicoIn.opt & 8) >> 3;\r
09b96f99 473 int fmlen = ((Pico.snd.fm_pos+0x80000) >> 20);\r
84e18560 474 int daclen = ((Pico.snd.dac_pos+0x80000) >> 20);\r
15caa286 475 int psglen = ((Pico.snd.psg_pos+0x80000) >> 20);\r
e348af76 476 int pcmlen = ((Pico.snd.pcm_pos+0x80000) >> 20);\r
33be04ca 477\r
84e18560 478 buf32 = PsndBuffer+(offset<<stereo);\r
cc68a136 479\r
f6c49d38 480 pprof_start(sound);\r
481\r
e44c606f 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
93f9619e 490 if (PicoIn.AHW & PAHW_PICO) {\r
e44c606f 491 // always need to render sound for interrupts\r
e348af76 492 s16 *buf16 = PicoIn.sndOut ? PicoIn.sndOut + (pcmlen<<stereo) : NULL;\r
493 PicoPicoPCMUpdate(buf16, length-pcmlen, stereo);\r
ef4eb506 494 return length;\r
495 }\r
496\r
027940e1 497 // Fill up DAC output in case of missing samples (Q rounding errors)\r
9f1d5acd 498 if (length-daclen > 0 && PicoIn.sndOut) {\r
84e18560 499 Pico.snd.dac_pos += (length-daclen) << 20;\r
73411ac4 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
43e14010 514 }\r
09b96f99 515 Pico.snd.dac_val2 = Pico.snd.dac_val;\r
43e14010 516 }\r
517\r
8ac9ab7f 518 // Add in parts of the FM buffer not yet done\r
9f1d5acd 519 if (length-fmlen > 0 && PicoIn.sndOut) {\r
f7741cac 520 s32 *fmbuf = buf32 + ((fmlen-offset) << stereo);\r
09b96f99 521 Pico.snd.fm_pos += (length-fmlen) << 20;\r
8ac9ab7f 522 if (PicoIn.opt & POPT_EN_FM)\r
e2e2b6ad 523 PsndFMUpdate(fmbuf, length-fmlen, stereo, 1);\r
8ac9ab7f 524 }\r
4f265db7 525\r
7a93adeb 526 // CD: PCM sound\r
93f9619e 527 if (PicoIn.AHW & PAHW_MCD) {\r
84e18560 528 pcd_pcm_update(buf32, length-offset, stereo);\r
85f8e929 529 }\r
4f265db7 530\r
7a93adeb 531 // CD: CDDA audio\r
da42200b 532 // CD mode, cdda enabled, not data track, CDC is reading\r
93f9619e 533 if ((PicoIn.AHW & PAHW_MCD) && (PicoIn.opt & POPT_EN_MCD_CDDA)\r
274fcc35 534 && Pico_mcd->cdda_stream != NULL\r
b0be121b 535 && (!(Pico_mcd->s68k_regs[0x36] & 1) || Pico_msd.state == 3))\r
c9e1affc 536 {\r
274fcc35 537 if (Pico_mcd->cdda_type == CT_MP3)\r
84e18560 538 mp3_update(buf32, length-offset, stereo);\r
c9e1affc 539 else\r
f7741cac 540 cdda_raw_update(buf32, length-offset, stereo);\r
c9e1affc 541 }\r
4f265db7 542\r
93f9619e 543 if ((PicoIn.AHW & PAHW_32X) && (PicoIn.opt & POPT_EN_PWM))\r
84e18560 544 p32x_pwm_update(buf32, length-offset, stereo);\r
db1d3564 545\r
4f265db7 546 // convert + limit to normal 16bit output\r
9f1d5acd 547 if (PicoIn.sndOut)\r
70efc52d 548 PsndMix_32_to_16(PicoIn.sndOut+(offset<<stereo), buf32, length-offset);\r
cc68a136 549\r
f6c49d38 550 pprof_end(sound);\r
551\r
7a93adeb 552 return length;\r
cc68a136 553}\r
554\r
7b3f44c6 555PICO_INTERNAL void PsndGetSamples(int y)\r
556{\r
7b3f44c6 557 static int curr_pos = 0;\r
558\r
8ac9ab7f 559 curr_pos = PsndRender(0, Pico.snd.len_use);\r
560\r
9f1d5acd 561 if (PicoIn.writeSound && PicoIn.sndOut)\r
8ac9ab7f 562 PicoIn.writeSound(curr_pos * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));\r
563 // clear sound buffer\r
564 PsndClear();\r
7b3f44c6 565}\r
566\r
70aecd15 567static int PsndRenderMS(int offset, int length)\r
2ec9bec5 568{\r
43ef4071 569 s32 *buf32 = PsndBuffer;\r
70aecd15 570 int stereo = (PicoIn.opt & 8) >> 3;\r
15caa286 571 int psglen = ((Pico.snd.psg_pos+0x80000) >> 20);\r
572 int ym2413len = ((Pico.snd.ym2413_pos+0x80000) >> 20);\r
2ec9bec5 573\r
9f1d5acd 574 if (!PicoIn.sndOut)\r
575 return length;\r
576\r
70aecd15 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
f7741cac 581 s16 *psgbuf = PicoIn.sndOut + (psglen << stereo);\r
15caa286 582 Pico.snd.psg_pos += (length-psglen) << 20;\r
70aecd15 583 if (PicoIn.opt & POPT_EN_PSG)\r
584 SN76496Update(psgbuf, length-psglen, stereo);\r
585 }\r
2ec9bec5 586\r
a2f24bfa 587 if (length-ym2413len > 0) {\r
f7741cac 588 s16 *ym2413buf = PicoIn.sndOut + (ym2413len << stereo);\r
15caa286 589 Pico.snd.ym2413_pos += (length-ym2413len) << 20;\r
a2f24bfa 590 int len = (length-ym2413len);\r
43ef4071 591 if (Pico.m.hardware & PMS_HW_FMUSED) {\r
592 PsndFMUpdate(buf32, len, 0, 0);\r
70efc52d 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
a2f24bfa 602 }\r
603 }\r
604\r
70aecd15 605 pprof_end(sound);\r
606\r
607 return length;\r
608}\r
609\r
610PICO_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
9f1d5acd 616 if (PicoIn.writeSound != NULL && PicoIn.sndOut)\r
70aecd15 617 PicoIn.writeSound(curr_pos * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));\r
2ec9bec5 618 PsndClear();\r
619}\r
620\r
4f2cdbf5 621// vim:shiftwidth=2:ts=2:expandtab\r