improve 64bit portability
[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 *\r
6 * This work is licensed under the terms of MAME license.\r
7 * See COPYING file in the top-level directory.\r
8 */\r
9\r
10#include <string.h>\r
11#include "ym2612.h"\r
12#include "sn76496.h"\r
13#include "../pico_int.h"\r
14#include "../cd/cue.h"\r
15#include "mix.h"\r
16\r
17void (*PsndMix_32_to_16l)(short *dest, int *src, int count) = mix_32_to_16l_stereo;\r
18\r
19// master int buffer to mix to\r
20static int PsndBuffer[2*(44100+100)/50];\r
21\r
22// dac, psg\r
23static unsigned short dac_info[312+4]; // pos in sample buffer\r
24\r
25// cdda output buffer\r
26short cdda_out_buffer[2*1152];\r
27\r
28// sn76496\r
29extern int *sn76496_regs;\r
30\r
31\r
32static void dac_recalculate(void)\r
33{\r
34 int lines = Pico.m.pal ? 313 : 262;\r
35 int mid = Pico.m.pal ? 68 : 93;\r
36 int i, dac_cnt, pos, len;\r
37\r
38 if (Pico.snd.len <= lines)\r
39 {\r
40 // shrinking algo\r
41 dac_cnt = -Pico.snd.len;\r
42 len=1; pos=0;\r
43 dac_info[225] = 1;\r
44\r
45 for(i=226; i != 225; i++)\r
46 {\r
47 if (i >= lines) i = 0;\r
48 if(dac_cnt < 0) {\r
49 pos++;\r
50 dac_cnt += lines;\r
51 }\r
52 dac_cnt -= Pico.snd.len;\r
53 dac_info[i] = pos;\r
54 }\r
55 }\r
56 else\r
57 {\r
58 // stretching\r
59 dac_cnt = Pico.snd.len;\r
60 pos=0;\r
61 for(i = 225; i != 224; i++)\r
62 {\r
63 if (i >= lines) i = 0;\r
64 len=0;\r
65 while(dac_cnt >= 0) {\r
66 dac_cnt -= lines;\r
67 len++;\r
68 }\r
69 if (i == mid) // midpoint\r
70 while(pos+len < Pico.snd.len/2) {\r
71 dac_cnt -= lines;\r
72 len++;\r
73 }\r
74 dac_cnt += Pico.snd.len;\r
75 pos += len;\r
76 dac_info[i] = pos;\r
77 }\r
78 }\r
79 for (i = lines; i < sizeof(dac_info) / sizeof(dac_info[0]); i++)\r
80 dac_info[i] = dac_info[0];\r
81}\r
82\r
83\r
84PICO_INTERNAL void PsndReset(void)\r
85{\r
86 // PsndRerate calls YM2612Init, which also resets\r
87 PsndRerate(0);\r
88 timers_reset();\r
89}\r
90\r
91\r
92// to be called after changing sound rate or chips\r
93void PsndRerate(int preserve_state)\r
94{\r
95 void *state = NULL;\r
96 int target_fps = Pico.m.pal ? 50 : 60;\r
97\r
98 if (preserve_state) {\r
99 state = malloc(0x204);\r
100 if (state == NULL) return;\r
101 ym2612_pack_state();\r
102 memcpy(state, YM2612GetRegs(), 0x204);\r
103 }\r
104 YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PicoIn.sndRate);\r
105 if (preserve_state) {\r
106 // feed it back it's own registers, just like after loading state\r
107 memcpy(YM2612GetRegs(), state, 0x204);\r
108 ym2612_unpack_state();\r
109 }\r
110\r
111 if (preserve_state) memcpy(state, sn76496_regs, 28*4); // remember old state\r
112 SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PicoIn.sndRate);\r
113 if (preserve_state) memcpy(sn76496_regs, state, 28*4); // restore old state\r
114\r
115 if (state)\r
116 free(state);\r
117\r
118 // calculate Pico.snd.len\r
119 Pico.snd.len = PicoIn.sndRate / target_fps;\r
120 Pico.snd.len_e_add = ((PicoIn.sndRate - Pico.snd.len * target_fps) << 16) / target_fps;\r
121 Pico.snd.len_e_cnt = 0;\r
122\r
123 // recalculate dac info\r
124 dac_recalculate();\r
125\r
126 // clear all buffers\r
127 memset32(PsndBuffer, 0, sizeof(PsndBuffer)/4);\r
128 memset(cdda_out_buffer, 0, sizeof(cdda_out_buffer));\r
129 if (PicoIn.sndOut)\r
130 PsndClear();\r
131\r
132 // set mixer\r
133 PsndMix_32_to_16l = (PicoIn.opt & POPT_EN_STEREO) ? mix_32_to_16l_stereo : mix_32_to_16_mono;\r
134\r
135 if (PicoIn.AHW & PAHW_PICO)\r
136 PicoReratePico();\r
137}\r
138\r
139\r
140PICO_INTERNAL void PsndStartFrame(void)\r
141{\r
142 // compensate for float part of Pico.snd.len\r
143 Pico.snd.len_use = Pico.snd.len;\r
144 Pico.snd.len_e_cnt += Pico.snd.len_e_add;\r
145 if (Pico.snd.len_e_cnt >= 0x10000) {\r
146 Pico.snd.len_e_cnt -= 0x10000;\r
147 Pico.snd.len_use++;\r
148 }\r
149\r
150 Pico.snd.dac_line = Pico.snd.psg_line = 0;\r
151 Pico.m.status &= ~1;\r
152 dac_info[224] = Pico.snd.len_use;\r
153}\r
154\r
155PICO_INTERNAL void PsndDoDAC(int line_to)\r
156{\r
157 int pos, pos1, len;\r
158 int dout = ym2612.dacout;\r
159 int line_from = Pico.snd.dac_line;\r
160\r
161 if (line_to >= 313)\r
162 line_to = 312;\r
163\r
164 pos = dac_info[line_from];\r
165 pos1 = dac_info[line_to + 1];\r
166 len = pos1 - pos;\r
167 if (len <= 0)\r
168 return;\r
169\r
170 Pico.snd.dac_line = line_to + 1;\r
171\r
172 if (!PicoIn.sndOut)\r
173 return;\r
174\r
175 if (PicoIn.opt & POPT_EN_STEREO) {\r
176 short *d = PicoIn.sndOut + pos*2;\r
177 for (; len > 0; len--, d+=2) *d += dout;\r
178 } else {\r
179 short *d = PicoIn.sndOut + pos;\r
180 for (; len > 0; len--, d++) *d += dout;\r
181 }\r
182}\r
183\r
184PICO_INTERNAL void PsndDoPSG(int line_to)\r
185{\r
186 int line_from = Pico.snd.psg_line;\r
187 int pos, pos1, len;\r
188 int stereo = 0;\r
189\r
190 if (line_to >= 313)\r
191 line_to = 312;\r
192\r
193 pos = dac_info[line_from];\r
194 pos1 = dac_info[line_to + 1];\r
195 len = pos1 - pos;\r
196 //elprintf(EL_STATUS, "%3d %3d %3d %3d %3d",\r
197 // pos, pos1, len, line_from, line_to);\r
198 if (len <= 0)\r
199 return;\r
200\r
201 Pico.snd.psg_line = line_to + 1;\r
202\r
203 if (!PicoIn.sndOut || !(PicoIn.opt & POPT_EN_PSG))\r
204 return;\r
205\r
206 if (PicoIn.opt & POPT_EN_STEREO) {\r
207 stereo = 1;\r
208 pos <<= 1;\r
209 }\r
210 SN76496Update(PicoIn.sndOut + pos, len, stereo);\r
211}\r
212\r
213// cdda\r
214static void cdda_raw_update(int *buffer, int length)\r
215{\r
216 int ret, cdda_bytes, mult = 1;\r
217\r
218 cdda_bytes = length*4;\r
219 if (PicoIn.sndRate <= 22050 + 100) mult = 2;\r
220 if (PicoIn.sndRate < 22050 - 100) mult = 4;\r
221 cdda_bytes *= mult;\r
222\r
223 ret = pm_read(cdda_out_buffer, cdda_bytes, Pico_mcd->cdda_stream);\r
224 if (ret < cdda_bytes) {\r
225 memset((char *)cdda_out_buffer + ret, 0, cdda_bytes - ret);\r
226 Pico_mcd->cdda_stream = NULL;\r
227 return;\r
228 }\r
229\r
230 // now mix\r
231 switch (mult) {\r
232 case 1: mix_16h_to_32(buffer, cdda_out_buffer, length*2); break;\r
233 case 2: mix_16h_to_32_s1(buffer, cdda_out_buffer, length*2); break;\r
234 case 4: mix_16h_to_32_s2(buffer, cdda_out_buffer, length*2); break;\r
235 }\r
236}\r
237\r
238void cdda_start_play(int lba_base, int lba_offset, int lb_len)\r
239{\r
240 if (Pico_mcd->cdda_type == CT_MP3)\r
241 {\r
242 int pos1024 = 0;\r
243\r
244 if (lba_offset)\r
245 pos1024 = lba_offset * 1024 / lb_len;\r
246\r
247 mp3_start_play(Pico_mcd->cdda_stream, pos1024);\r
248 return;\r
249 }\r
250\r
251 pm_seek(Pico_mcd->cdda_stream, (lba_base + lba_offset) * 2352, SEEK_SET);\r
252 if (Pico_mcd->cdda_type == CT_WAV)\r
253 {\r
254 // skip headers, assume it's 44kHz stereo uncompressed\r
255 pm_seek(Pico_mcd->cdda_stream, 44, SEEK_CUR);\r
256 }\r
257}\r
258\r
259\r
260PICO_INTERNAL void PsndClear(void)\r
261{\r
262 int len = Pico.snd.len;\r
263 if (Pico.snd.len_e_add) len++;\r
264 if (PicoIn.opt & POPT_EN_STEREO)\r
265 memset32((int *) PicoIn.sndOut, 0, len); // assume PicoIn.sndOut to be aligned\r
266 else {\r
267 short *out = PicoIn.sndOut;\r
268 if ((uintptr_t)out & 2) { *out++ = 0; len--; }\r
269 memset32((int *) out, 0, len/2);\r
270 if (len & 1) out[len-1] = 0;\r
271 }\r
272}\r
273\r
274\r
275static int PsndRender(int offset, int length)\r
276{\r
277 int buf32_updated = 0;\r
278 int *buf32 = PsndBuffer+offset;\r
279 int stereo = (PicoIn.opt & 8) >> 3;\r
280\r
281 offset <<= stereo;\r
282\r
283 pprof_start(sound);\r
284\r
285 if (PicoIn.AHW & PAHW_PICO) {\r
286 PicoPicoPCMUpdate(PicoIn.sndOut+offset, length, stereo);\r
287 return length;\r
288 }\r
289\r
290 // Add in the stereo FM buffer\r
291 if (PicoIn.opt & POPT_EN_FM) {\r
292 buf32_updated = YM2612UpdateOne(buf32, length, stereo, 1);\r
293 } else\r
294 memset32(buf32, 0, length<<stereo);\r
295\r
296//printf("active_chs: %02x\n", buf32_updated);\r
297 (void)buf32_updated;\r
298\r
299 // CD: PCM sound\r
300 if (PicoIn.AHW & PAHW_MCD) {\r
301 pcd_pcm_update(buf32, length, stereo);\r
302 //buf32_updated = 1;\r
303 }\r
304\r
305 // CD: CDDA audio\r
306 // CD mode, cdda enabled, not data track, CDC is reading\r
307 if ((PicoIn.AHW & PAHW_MCD) && (PicoIn.opt & POPT_EN_MCD_CDDA)\r
308 && Pico_mcd->cdda_stream != NULL\r
309 && !(Pico_mcd->s68k_regs[0x36] & 1))\r
310 {\r
311 // note: only 44, 22 and 11 kHz supported, with forced stereo\r
312 if (Pico_mcd->cdda_type == CT_MP3)\r
313 mp3_update(buf32, length, stereo);\r
314 else\r
315 cdda_raw_update(buf32, length);\r
316 }\r
317\r
318 if ((PicoIn.AHW & PAHW_32X) && (PicoIn.opt & POPT_EN_PWM))\r
319 p32x_pwm_update(buf32, length, stereo);\r
320\r
321 // convert + limit to normal 16bit output\r
322 PsndMix_32_to_16l(PicoIn.sndOut+offset, buf32, length);\r
323\r
324 pprof_end(sound);\r
325\r
326 return length;\r
327}\r
328\r
329// to be called on 224 or line_sample scanlines only\r
330PICO_INTERNAL void PsndGetSamples(int y)\r
331{\r
332 static int curr_pos = 0;\r
333\r
334 if (ym2612.dacen && Pico.snd.dac_line < y)\r
335 PsndDoDAC(y - 1);\r
336 PsndDoPSG(y - 1);\r
337\r
338 if (y == 224)\r
339 {\r
340 if (Pico.m.status & 2)\r
341 curr_pos += PsndRender(curr_pos, Pico.snd.len-Pico.snd.len/2);\r
342 else curr_pos = PsndRender(0, Pico.snd.len_use);\r
343 if (Pico.m.status & 1)\r
344 Pico.m.status |= 2;\r
345 else Pico.m.status &= ~2;\r
346 if (PicoIn.writeSound)\r
347 PicoIn.writeSound(curr_pos * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));\r
348 // clear sound buffer\r
349 PsndClear();\r
350 Pico.snd.dac_line = 224;\r
351 dac_info[224] = 0;\r
352 }\r
353 else if (Pico.m.status & 3) {\r
354 Pico.m.status |= 2;\r
355 Pico.m.status &= ~1;\r
356 curr_pos = PsndRender(0, Pico.snd.len/2);\r
357 }\r
358}\r
359\r
360PICO_INTERNAL void PsndGetSamplesMS(void)\r
361{\r
362 int length = Pico.snd.len_use;\r
363\r
364 PsndDoPSG(223);\r
365\r
366 // upmix to "stereo" if needed\r
367 if (PicoIn.opt & POPT_EN_STEREO) {\r
368 int i, *p;\r
369 for (i = length, p = (void *)PicoIn.sndOut; i > 0; i--, p++)\r
370 *p |= *p << 16;\r
371 }\r
372\r
373 if (PicoIn.writeSound != NULL)\r
374 PicoIn.writeSound(length * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));\r
375 PsndClear();\r
376\r
377 dac_info[224] = 0;\r
378}\r
379\r
380// vim:shiftwidth=2:ts=2:expandtab\r