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 |
21 | void (*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 |
25 | static s32 PsndBuffer[2*(54000+100)/50+2];\r |
cc68a136 |
26 | \r |
c9e1affc |
27 | // cdda output buffer\r |
91ea9406 |
28 | s16 cdda_out_buffer[2*1152];\r |
c9e1affc |
29 | \r |
cc68a136 |
30 | // sn76496\r |
31 | extern int *sn76496_regs;\r |
32 | \r |
43ef4071 |
33 | // FM resampling polyphase FIR\r |
34 | static resampler_t *fmresampler;\r |
35 | static int (*PsndFMUpdate)(s32 *buffer, int length, int stereo, int is_buf_empty);\r |
36 | \r |
a2f24bfa |
37 | // ym2413\r |
a2f24bfa |
38 | static OPLL *opll = NULL;\r |
2ec448a8 |
39 | static struct {\r |
40 | uint32_t adr;\r |
41 | uint8_t reg[sizeof(opll->reg)];\r |
42 | } opll_buf;\r |
43 | \r |
43ef4071 |
44 | \r |
45 | void YM2413_regWrite(unsigned data){\r |
46 | OPLL_writeIO(opll,0,data);\r |
47 | }\r |
48 | void YM2413_dataWrite(unsigned data){\r |
49 | OPLL_writeIO(opll,1,data);\r |
50 | }\r |
51 | \r |
2ec448a8 |
52 | PICO_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 |
59 | PICO_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 |
70 | PICO_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 |
77 | PICO_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 |
85 | PICO_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 |
96 | static int ymchans;\r |
97 | static void YM2612Update(s32 *buffer, int length, int stereo)\r |
98 | {\r |
99 | ymchans = YM2612UpdateOne(buffer, length, stereo, 1);\r |
100 | }\r |
101 | \r |
110a49ed |
102 | static 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 |
109 | static 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 |
118 | static 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 |
126 | static 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 |
152 | static 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 |
157 | static int ymclock;\r |
158 | static int ymrate;\r |
159 | static int ymopts;\r |
160 | \r |
cc68a136 |
161 | // to be called after changing sound rate or chips\r |
9d917eea |
162 | void 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 |
249 | PICO_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 |
260 | PICO_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 |
302 | PICO_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 |
330 | PICO_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 |
373 | PICO_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 |
400 | PICO_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 |
427 | static 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 |
450 | void 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 |
472 | PICO_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 |
494 | static 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 |
580 | PICO_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 |
592 | static 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 |
635 | PICO_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 |