pandora: fix readme and pxml version
[picodrive.git] / pico / sound / sound.c
... / ...
CommitLineData
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
22void (*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
26static s32 PsndBuffer[2*(54000+100)/50+2];\r
27\r
28// cdda output buffer\r
29s16 cdda_out_buffer[2*1152];\r
30\r
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
35PICO_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
42PICO_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
50PICO_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
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
67static 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
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
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
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
122static int ymclock;\r
123static int ymrate;\r
124static int ymopts;\r
125\r
126// to be called after changing sound rate or chips\r
127void 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
212PICO_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
223PICO_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
265PICO_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
293PICO_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
336PICO_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
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
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
390static 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
423void 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
447PICO_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
469static 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
555PICO_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
567static 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
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
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