455aac7b1b045f5429a6e0070a4c7e4ac0635493
[picodrive.git] / pico / sound / sound.c
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