platform ps2, handle audio similar to psp
[picodrive.git] / pico / sound / sound.c
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
17 void (*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
20 static int PsndBuffer[2*(44100+100)/50];\r
21 \r
22 // dac, psg\r
23 static unsigned short dac_info[312+4]; // pos in sample buffer\r
24 \r
25 // cdda output buffer\r
26 short cdda_out_buffer[2*1152];\r
27 \r
28 // sn76496\r
29 extern int *sn76496_regs;\r
30 \r
31 \r
32 static 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
84 PICO_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
93 void 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
140 PICO_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
155 PICO_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
184 PICO_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
214 static 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
238 void 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
260 PICO_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
275 static 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
330 PICO_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
360 PICO_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