recognize the MED ssf2 header
[picodrive.git] / platform / gp2x / code940 / mp3test.c
1 #include <stdio.h>\r
2 #include <stdlib.h>\r
3 #include <string.h>\r
4 #include <unistd.h>\r
5 #include <sys/mman.h>\r
6 #include <sys/ioctl.h>\r
7 #include <sys/time.h>\r
8 #include <fcntl.h>\r
9 #include <errno.h>\r
10 \r
11 #include "940shared.h"\r
12 #include "../gp2x.h"\r
13 //#include "emu.h"\r
14 //#include "menu.h"\r
15 #include "../asmutils.h"\r
16 #include "../helix/pub/mp3dec.h"\r
17 \r
18 /* we will need some gp2x internals here */\r
19 extern volatile unsigned short *gp2x_memregs; /* from minimal library rlyeh */\r
20 extern volatile unsigned long  *gp2x_memregl;\r
21 \r
22 static unsigned char *shared_mem = 0;\r
23 static _940_data_t *shared_data = 0;\r
24 static _940_ctl_t *shared_ctl = 0;\r
25 static unsigned char *mp3_mem = 0;\r
26 \r
27 #define MP3_SIZE_MAX (0x1000000 - 4*640*480)\r
28 \r
29 int crashed_940 = 0;\r
30 \r
31 \r
32 /***********************************************************/\r
33 \r
34 #define MAXOUT          (+32767)\r
35 #define MINOUT          (-32768)\r
36 \r
37 /* limitter */\r
38 #define Limit(val, max,min) { \\r
39         if ( val > max )      val = max; \\r
40         else if ( val < min ) val = min; \\r
41 }\r
42 \r
43 \r
44 void wait_busy_940(void)\r
45 {\r
46         int i;\r
47 #if 0\r
48         printf("940 busy, entering wait loop.. (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc);\r
49         for (i = 0; i < 8; i++)\r
50                 printf("%i ", shared_ctl->vstarts[i]);\r
51         printf(")\n");\r
52 \r
53         for (i = 0; shared_ctl->busy; i++)\r
54         {\r
55                 spend_cycles(1024); /* needs tuning */\r
56         }\r
57         printf("wait iterations: %i\n", i);\r
58 #else\r
59         for (i = 0; shared_ctl->busy && i < 0x10000; i++)\r
60                 spend_cycles(8*1024);\r
61         if (i < 0x10000) return;\r
62 \r
63         /* 940 crashed */\r
64         printf("940 crashed (cnt: %i, ve: ", shared_ctl->loopc);\r
65         for (i = 0; i < 8; i++)\r
66                 printf("%i ", shared_ctl->vstarts[i]);\r
67         printf(")\n");\r
68         crashed_940 = 1;\r
69 #endif\r
70 }\r
71 \r
72 \r
73 void add_job_940(int job0, int job1)\r
74 {\r
75         shared_ctl->jobs[0] = job0;\r
76         shared_ctl->jobs[1] = job1;\r
77         shared_ctl->busy = 1;\r
78         gp2x_memregs[0x3B3E>>1] = 0xffff; // cause an IRQ for 940\r
79 }\r
80 \r
81 \r
82 static int read_to_upper(void *dest, void *tmpbuf, int tmpsize, FILE *f)\r
83 {\r
84         int nRead, nLen = 0;\r
85 \r
86         while(1)\r
87         {\r
88                 nRead = fread(tmpbuf, 1, tmpsize, f);\r
89                 if(nRead <= 0)\r
90                         break;\r
91                 memcpy((unsigned char *)dest + nLen, tmpbuf, nRead);\r
92                 nLen += nRead;\r
93         }\r
94 \r
95         return nLen;\r
96 }\r
97 \r
98 static void simpleWait(int thissec, int lim_time)\r
99 {\r
100         struct timeval tval;\r
101 \r
102         spend_cycles(1024);\r
103         gettimeofday(&tval, 0);\r
104         if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
105 \r
106         while(tval.tv_usec < lim_time)\r
107         {\r
108                 spend_cycles(1024);\r
109                 gettimeofday(&tval, 0);\r
110                 if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
111         }\r
112 }\r
113 \r
114 \r
115 char **g_argv;\r
116 \r
117 /* none of the functions in this file should be called before this one */\r
118 void YM2612Init_940(int baseclock, int rate)\r
119 {\r
120         printf("YM2612Init_940()\n");\r
121         printf("Mem usage: shared_data: %i, shared_ctl: %i\n", sizeof(*shared_data), sizeof(*shared_ctl));\r
122 \r
123         Reset940(1, 2);\r
124         Pause940(1);\r
125 \r
126         gp2x_memregs[0x3B46>>1] = 0xffff; // clear pending DUALCPU interrupts for 940\r
127         gp2x_memregs[0x3B42>>1] = 0xffff; // enable DUALCPU interrupts for 940\r
128 \r
129         gp2x_memregl[0x4508>>2] = ~(1<<26); // unmask DUALCPU ints in the undocumented 940's interrupt controller\r
130 \r
131         if (shared_mem == NULL)\r
132         {\r
133                 shared_mem = (unsigned char *) mmap(0, 0x210000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0x2000000);\r
134                 if(shared_mem == MAP_FAILED)\r
135                 {\r
136                         printf("mmap(shared_data) failed with %i\n", errno);\r
137                         exit(1);\r
138                 }\r
139                 shared_data = (_940_data_t *) (shared_mem+0x100000);\r
140                 /* this area must not get buffered on either side */\r
141                 shared_ctl =  (_940_ctl_t *)  (shared_mem+0x200000);\r
142                 mp3_mem = (unsigned char *) mmap(0, MP3_SIZE_MAX, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0x3000000);\r
143                 if (mp3_mem == MAP_FAILED)\r
144                 {\r
145                         printf("mmap(mp3_mem) failed with %i\n", errno);\r
146                         exit(1);\r
147                 }\r
148                 crashed_940 = 1;\r
149         }\r
150 \r
151         if (crashed_940)\r
152         {\r
153                 unsigned char ucData[1024];\r
154                 int i;\r
155                 char binpath[1024];\r
156                 FILE *fp;\r
157 \r
158                 strncpy(binpath, g_argv[0], 1023);\r
159                 binpath[1023] = 0;\r
160                 for (i = strlen(binpath); i > 0; i--)\r
161                         if (binpath[i] == '/') { binpath[i] = 0; break; }\r
162                 strcat(binpath, "/code940.bin");\r
163 \r
164                 fp = fopen(binpath, "rb");\r
165                 if(!fp)\r
166                 {\r
167                         printf("failed to open %s\n", binpath);\r
168                         exit(1);\r
169                 }\r
170 \r
171                 read_to_upper(shared_mem, ucData, sizeof(ucData), fp);\r
172                 fclose(fp);\r
173                 crashed_940 = 0;\r
174         }\r
175 \r
176         memset(shared_data, 0, sizeof(*shared_data));\r
177         memset(shared_ctl,  0, sizeof(*shared_ctl));\r
178 \r
179         /* now cause 940 to init it's ym2612 stuff */\r
180         shared_ctl->baseclock = baseclock;\r
181         shared_ctl->rate = rate;\r
182         shared_ctl->jobs[0] = JOB940_INITALL;\r
183         shared_ctl->jobs[1] = 0;\r
184         shared_ctl->busy = 1;\r
185 \r
186         /* start the 940 */\r
187         Reset940(0, 2);\r
188         Pause940(0);\r
189 }\r
190 \r
191 \r
192 unsigned char *mp3_data = 0;\r
193 \r
194 void local_decode(void)\r
195 {\r
196         int mp3_offs = shared_ctl->mp3_offs;\r
197         unsigned char *readPtr = mp3_data + mp3_offs;\r
198         int bytesLeft = shared_ctl->mp3_len - mp3_offs;\r
199         int offset; // frame offset from readPtr\r
200         int err = 0;\r
201 \r
202         if (bytesLeft <= 0) return; // EOF, nothing to do\r
203 \r
204         offset = MP3FindSyncWord(readPtr, bytesLeft);\r
205         if (offset < 0) {\r
206                 shared_ctl->mp3_offs = shared_ctl->mp3_len;\r
207                 return; // EOF\r
208         }\r
209         readPtr += offset;\r
210         bytesLeft -= offset;\r
211 \r
212         err = MP3Decode(shared_data->mp3dec, &readPtr, &bytesLeft,\r
213                         shared_data->mp3_buffer[shared_ctl->mp3_buffsel], 0);\r
214         if (err) {\r
215                 if (err == ERR_MP3_INDATA_UNDERFLOW) {\r
216                         shared_ctl->mp3_offs = shared_ctl->mp3_len; // EOF\r
217                         return;\r
218                 } else if (err <= -6 && err >= -12) {\r
219                         // ERR_MP3_INVALID_FRAMEHEADER, ERR_MP3_INVALID_*\r
220                         // just try to skip the offending frame..\r
221                         readPtr++;\r
222                 }\r
223                 shared_ctl->mp3_errors++;\r
224                 shared_ctl->mp3_lasterr = err;\r
225         }\r
226         shared_ctl->mp3_offs = readPtr - mp3_data;\r
227 }\r
228 \r
229 \r
230 void gp2x_sound_sync(void);\r
231 \r
232 #define USE_LOCAL 0\r
233 #define BENCHMARK 0\r
234 \r
235 int main(int argc, char *argv[])\r
236 {\r
237         FILE *f;\r
238         int size;\r
239         struct timeval tval; // timing\r
240         int thissec = 0, fps = 0;\r
241         int target_frametime, frame_samples, samples_ready, mp3_buffer_offs, play_bufsel;\r
242         unsigned char play_buffer[44100/50*2*2];\r
243 \r
244         if (argc != 2) {\r
245                 printf("usage: %s <mp3file>\n", argv[0]);\r
246                 return 1;\r
247         }\r
248 \r
249         g_argv = argv;\r
250 \r
251         gp2x_init();\r
252         YM2612Init_940(123, 44100);\r
253 \r
254         // load a mp3\r
255         f = fopen(argv[1], "rb");\r
256         if (!f) {\r
257                 printf("can't open %s\n", argv[1]);\r
258                 return 1;\r
259         }\r
260 \r
261         fseek(f, 0, SEEK_END);\r
262         size = (int) ftell(f);\r
263         if (size > MP3_SIZE_MAX) {\r
264                 printf("size %i > %i\n", size, MP3_SIZE_MAX);\r
265                 size = MP3_SIZE_MAX;\r
266         }\r
267 \r
268         fseek(f, 0, SEEK_SET);\r
269         if (fread(mp3_mem, 1, size, f) != size) {\r
270                 printf("read failed, errno=%i\n", errno);\r
271                 fclose(f);\r
272                 exit(1);\r
273         }\r
274         fclose(f);\r
275         shared_ctl->mp3_len = size;\r
276 \r
277 #if USE_LOCAL\r
278         shared_data->mp3dec = MP3InitDecoder();\r
279         mp3_data = malloc(size);\r
280         printf("init: dec: %p ptr: %p\n", shared_data->mp3dec, mp3_data);\r
281         if (!mp3_data) {\r
282                 printf("low mem\n");\r
283                 exit(1);\r
284         }\r
285         memcpy(mp3_data, mp3_mem, size);\r
286 #else\r
287         //printf("YM2612UpdateOne_940()\n");\r
288         if (shared_ctl->busy) wait_busy_940();\r
289 #endif\r
290 \r
291         gp2x_start_sound(44100, 16, 1);\r
292 \r
293         #define DESIRED_FPS 50\r
294         target_frametime = 1000000/DESIRED_FPS;\r
295         frame_samples = 44100/DESIRED_FPS;\r
296         samples_ready = mp3_buffer_offs = 0;\r
297         play_bufsel = 1;\r
298 \r
299         for (;; fps++)\r
300         {\r
301                 int lim_time;\r
302 \r
303                 gettimeofday(&tval, 0);\r
304                 if (tval.tv_sec != thissec)\r
305                 {\r
306                         printf("fps: %i\n", fps);\r
307                         thissec = tval.tv_sec;\r
308                         fps = 0;\r
309 #if BENCHMARK\r
310                         shared_ctl->mp3_offs = 0;\r
311 #endif\r
312                 }\r
313 \r
314 #if 0\r
315                 // decode\r
316 #if USE_LOCAL\r
317                 shared_ctl->mp3_buffsel ^= 1;\r
318                 local_decode();\r
319 #else\r
320                 wait_busy_940();\r
321                 shared_ctl->mp3_buffsel ^= 1;\r
322                 add_job_940(JOB940_MP3DECODE, 0);\r
323 #endif\r
324 \r
325                 if (shared_ctl->mp3_lasterr) {\r
326                         printf("mp3_lasterr #%i: %i size: %i offs: %i\n", shared_ctl->mp3_errors, shared_ctl->mp3_lasterr,\r
327                                 shared_ctl->mp3_len, shared_ctl->mp3_offs);\r
328                         printf("loopc: %i bytes: %08x\n",\r
329                                 shared_ctl->loopc, *(int *)(mp3_mem+shared_ctl->mp3_offs));\r
330                         shared_ctl->mp3_lasterr = 0;\r
331                 }\r
332 \r
333 #if !BENCHMARK\r
334                 // play\r
335                 gp2x_sound_sync();\r
336                 gp2x_sound_write(shared_data->mp3_buffer[shared_ctl->mp3_buffsel^1], 1152*2*2);\r
337 #endif\r
338 #else\r
339                 lim_time = (fps+1) * target_frametime;\r
340 \r
341                 wait_busy_940();\r
342 \r
343                 // decode, play\r
344                 if (samples_ready >= frame_samples) {\r
345                         if (1152 - mp3_buffer_offs >= frame_samples) {\r
346                                 memcpy(play_buffer, shared_data->mp3_buffer[play_bufsel] + mp3_buffer_offs*2,\r
347                                         frame_samples*2*2);\r
348                                 mp3_buffer_offs += frame_samples;\r
349                         } else {\r
350                                 // collect from both buffers..\r
351                                 int left = 1152 - mp3_buffer_offs;\r
352                                 memcpy(play_buffer, shared_data->mp3_buffer[play_bufsel] + mp3_buffer_offs*2,\r
353                                         left*2*2);\r
354                                 play_bufsel ^= 1;\r
355                                 mp3_buffer_offs = frame_samples - left;\r
356                                 memcpy(play_buffer + left*2*2, shared_data->mp3_buffer[play_bufsel],\r
357                                         mp3_buffer_offs*2*2);\r
358                         }\r
359                         gp2x_sound_write(play_buffer, frame_samples*2*2);\r
360                         samples_ready -= frame_samples;\r
361                 }\r
362 \r
363                 // make sure we will have enough samples next frame\r
364                 if (samples_ready < frame_samples) {\r
365 //                      wait_busy_940();\r
366                         shared_ctl->mp3_buffsel ^= 1;\r
367                         add_job_940(JOB940_MP3DECODE, 0);\r
368                         samples_ready += 1152;\r
369                 }\r
370 \r
371                 gettimeofday(&tval, 0);\r
372                 if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
373                 if(tval.tv_usec < lim_time)\r
374                 {\r
375                         // we are too fast\r
376                         simpleWait(thissec, lim_time);\r
377                 }\r
378 #endif\r
379         }\r
380 \r
381         return 0;\r
382 }\r
383 \r