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 |
22 | void (*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 |
26 | static s32 PsndBuffer[2*(54000+100)/50+2];\r |
cc68a136 |
27 | \r |
c9e1affc |
28 | // cdda output buffer\r |
91ea9406 |
29 | s16 cdda_out_buffer[2*1152];\r |
c9e1affc |
30 | \r |
43ef4071 |
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 |
a2f24bfa |
35 | PICO_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 |
42 | PICO_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 |
50 | PICO_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 |
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 |
110a49ed |
67 | static 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 |
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 |
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 |
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 |
864ac1d6 |
122 | static int ymclock;\r |
123 | static int ymrate;\r |
124 | static int ymopts;\r |
125 | \r |
cc68a136 |
126 | // to be called after changing sound rate or chips\r |
9d917eea |
127 | void 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 |
212 | PICO_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 |
223 | PICO_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 |
265 | PICO_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 |
293 | PICO_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 |
336 | PICO_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 |
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 |
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 |
390 | static 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 |
423 | void 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 |
447 | PICO_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 |
469 | static 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 |
555 | PICO_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 |
567 | static 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 |
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 |
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 |