diff options
author | Dave Chapman <dave@dchapman.com> | 2005-02-16 12:56:00 +0000 |
---|---|---|
committer | Dave Chapman <dave@dchapman.com> | 2005-02-16 12:56:00 +0000 |
commit | 7b96e2daa65af18310cc998de053c5188c32cbe1 (patch) | |
tree | 1d6b6ae92cca6c21cd7347754fcccf0e05addbb9 | |
parent | fd58842b291d22ee53389614efd03173eeaeab94 (diff) | |
download | rockbox-7b96e2daa65af18310cc998de053c5188c32cbe1.tar.gz rockbox-7b96e2daa65af18310cc998de053c5188c32cbe1.zip |
Initial version of a52towav test viewer plugin for liba52 - output is hardcoded to /ac3test.wav. CUrrently restricted to Stereo AC-3 files, but easy to fix for other types of files (e.g. 5.1)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5977 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/plugins/Makefile | 2 | ||||
-rw-r--r-- | apps/plugins/SOURCES | 1 | ||||
-rw-r--r-- | apps/plugins/a52towav.c | 455 | ||||
-rw-r--r-- | apps/plugins/viewers.config | 2 |
4 files changed, 459 insertions, 1 deletions
diff --git a/apps/plugins/Makefile b/apps/plugins/Makefile index 12126a0d3d..8b7243b127 100644 --- a/apps/plugins/Makefile +++ b/apps/plugins/Makefile | |||
@@ -17,7 +17,7 @@ ifdef APPEXTRA | |||
17 | endif | 17 | endif |
18 | 18 | ||
19 | ifdef SOFTWARECODECS | 19 | ifdef SOFTWARECODECS |
20 | CODECLIBS = -lmad | 20 | CODECLIBS = -lmad -la52 |
21 | endif | 21 | endif |
22 | 22 | ||
23 | LDS := plugin.lds | 23 | LDS := plugin.lds |
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index 9dd042ff31..caa77f080f 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES | |||
@@ -65,4 +65,5 @@ alpine_cdc.c | |||
65 | 65 | ||
66 | #if CONFIG_HWCODEC == MASNONE /* software codec platforms */ | 66 | #if CONFIG_HWCODEC == MASNONE /* software codec platforms */ |
67 | mpa2wav.c | 67 | mpa2wav.c |
68 | a52towav.c | ||
68 | #endif | 69 | #endif |
diff --git a/apps/plugins/a52towav.c b/apps/plugins/a52towav.c new file mode 100644 index 0000000000..17b6c91e51 --- /dev/null +++ b/apps/plugins/a52towav.c | |||
@@ -0,0 +1,455 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Björn Stenberg | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | #include "plugin.h" | ||
21 | |||
22 | #if (CONFIG_HWCODEC == MASNONE) && !defined(SIMULATOR) | ||
23 | /* software codec platforms, not for simulator */ | ||
24 | |||
25 | #include <inttypes.h> /* Needed by a52.h */ | ||
26 | |||
27 | #include <codecs/liba52/config.h> | ||
28 | #include <codecs/liba52/a52.h> | ||
29 | |||
30 | /* Currently used for WAV output */ | ||
31 | #ifdef WORDS_BIGENDIAN | ||
32 | #warning ************************************* BIG ENDIAN | ||
33 | #define LE_S16(x) ( (uint16_t) ( ((uint16_t)(x) >> 8) | ((uint16_t)(x) << 8) ) ) | ||
34 | #else | ||
35 | #define LE_S16(x) (x) | ||
36 | #endif | ||
37 | |||
38 | typedef struct ao_sample_format { | ||
39 | int bits; /* bits per sample */ | ||
40 | int rate; /* samples per second (in a single channel) */ | ||
41 | int channels; /* number of audio channels */ | ||
42 | int byte_format; /* Byte ordering in sample, see constants below */ | ||
43 | } ao_sample_format; | ||
44 | |||
45 | #define AO_FMT_LITTLE 1 | ||
46 | #define AO_FMT_BIG 2 | ||
47 | #define AO_FMT_NATIVE 4 | ||
48 | |||
49 | /* the main data structure of the program */ | ||
50 | typedef struct { | ||
51 | int infile; | ||
52 | int outfile; | ||
53 | off_t curpos; | ||
54 | off_t filesize; | ||
55 | ao_sample_format samfmt; /* bits, rate, channels, byte_format */ | ||
56 | // ao_device *ao_dev; | ||
57 | unsigned long total_samples; | ||
58 | unsigned long current_sample; | ||
59 | float total_time; /* seconds */ | ||
60 | float elapsed_time; /* seconds */ | ||
61 | } file_info_struct; | ||
62 | |||
63 | file_info_struct file_info; | ||
64 | |||
65 | #define MALLOC_BUFSIZE (512*1024) | ||
66 | |||
67 | int mem_ptr; | ||
68 | int bufsize; | ||
69 | unsigned char* mp3buf; // The actual MP3 buffer from Rockbox | ||
70 | unsigned char* mallocbuf; // 512K from the start of MP3 buffer | ||
71 | unsigned char* filebuf; // The rest of the MP3 buffer | ||
72 | |||
73 | |||
74 | |||
75 | #define BUFFER_SIZE 4096 | ||
76 | //static uint8_t buffer[BUFFER_SIZE]; | ||
77 | static float gain = 1; | ||
78 | static a52_state_t * state; | ||
79 | |||
80 | int output; | ||
81 | |||
82 | // DAVE: I'm not sure what these are for. | ||
83 | int disable_accel=0; | ||
84 | int disable_adjust=0; | ||
85 | int disable_dynrng=0; | ||
86 | |||
87 | /* welcome to the example rockbox plugin */ | ||
88 | |||
89 | /* here is a global api struct pointer. while not strictly necessary, | ||
90 | it's nice not to have to pass the api pointer in all function calls | ||
91 | in the plugin */ | ||
92 | static struct plugin_api* rb; | ||
93 | |||
94 | void* malloc(size_t size) { | ||
95 | void* x; | ||
96 | char s[32]; | ||
97 | |||
98 | x=&mallocbuf[mem_ptr]; | ||
99 | mem_ptr+=size+(size%4); // Keep memory 32-bit aligned (if it was already?) | ||
100 | |||
101 | rb->snprintf(s,30,"Memory used: %d\r",mem_ptr); | ||
102 | rb->lcd_putsxy(0,80,s); | ||
103 | rb->lcd_update(); | ||
104 | return(x); | ||
105 | } | ||
106 | |||
107 | void* calloc(size_t nmemb, size_t size) { | ||
108 | void* x; | ||
109 | x=malloc(nmemb*size); | ||
110 | rb->memset(x,0,nmemb*size); | ||
111 | return(x); | ||
112 | } | ||
113 | |||
114 | void free(void* ptr) { | ||
115 | (void)ptr; | ||
116 | } | ||
117 | |||
118 | void* realloc(void* ptr, size_t size) { | ||
119 | void* x; | ||
120 | (void)ptr; | ||
121 | x=malloc(size); | ||
122 | return(x); | ||
123 | } | ||
124 | |||
125 | void *memcpy(void *dest, const void *src, size_t n) { | ||
126 | return(rb->memcpy(dest,src,n)); | ||
127 | } | ||
128 | |||
129 | void *memset(void *s, int c, size_t n) { | ||
130 | return(rb->memset(s,c,n)); | ||
131 | } | ||
132 | |||
133 | int memcmp(const void *s1, const void *s2, size_t n) { | ||
134 | return(rb->memcmp(s1,s2,n)); | ||
135 | } | ||
136 | |||
137 | void* memmove(const void *s1, const void *s2, size_t n) { | ||
138 | char* dest=(char*)s1; | ||
139 | char* src=(char*)s2; | ||
140 | size_t i; | ||
141 | |||
142 | for (i=0;i<n;i++) { dest[i]=src[i]; } | ||
143 | // while(n>0) { *(dest++)=*(src++); n--; } | ||
144 | return(dest); | ||
145 | } | ||
146 | |||
147 | void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *)) { | ||
148 | rb->qsort(base,nmemb,size,compar); | ||
149 | } | ||
150 | |||
151 | |||
152 | |||
153 | |||
154 | static unsigned char wav_header[44]={'R','I','F','F', // 0 - ChunkID | ||
155 | 0,0,0,0, // 4 - ChunkSize (filesize-8) | ||
156 | 'W','A','V','E', // 8 - Format | ||
157 | 'f','m','t',' ', // 12 - SubChunkID | ||
158 | 16,0,0,0, // 16 - SubChunk1ID // 16 for PCM | ||
159 | 1,0, // 20 - AudioFormat (1=16-bit) | ||
160 | 2,0, // 22 - NumChannels | ||
161 | 0,0,0,0, // 24 - SampleRate in Hz | ||
162 | 0,0,0,0, // 28 - Byte Rate (SampleRate*NumChannels*(BitsPerSample/8) | ||
163 | 4,0, // 32 - BlockAlign (== NumChannels * BitsPerSample/8) | ||
164 | 16,0, // 34 - BitsPerSample | ||
165 | 'd','a','t','a', // 36 - Subchunk2ID | ||
166 | 0,0,0,0 // 40 - Subchunk2Size | ||
167 | }; | ||
168 | |||
169 | void close_wav(file_info_struct* file_info) { | ||
170 | int x; | ||
171 | int filesize=rb->filesize(file_info->outfile); | ||
172 | |||
173 | /* We assume 16-bit, Stereo */ | ||
174 | |||
175 | rb->lseek(file_info->outfile,0,SEEK_SET); | ||
176 | |||
177 | // ChunkSize | ||
178 | x=filesize-8; | ||
179 | wav_header[4]=(x&0xff); | ||
180 | wav_header[5]=(x&0xff00)>>8; | ||
181 | wav_header[6]=(x&0xff0000)>>16; | ||
182 | wav_header[7]=(x&0xff000000)>>24; | ||
183 | |||
184 | // Samplerate | ||
185 | wav_header[24]=file_info->samfmt.rate&0xff; | ||
186 | wav_header[25]=(file_info->samfmt.rate&0xff00)>>8; | ||
187 | wav_header[26]=(file_info->samfmt.rate&0xff0000)>>16; | ||
188 | wav_header[27]=(file_info->samfmt.rate&0xff000000)>>24; | ||
189 | |||
190 | // ByteRate | ||
191 | x=file_info->samfmt.rate*4; | ||
192 | wav_header[28]=(x&0xff); | ||
193 | wav_header[29]=(x&0xff00)>>8; | ||
194 | wav_header[30]=(x&0xff0000)>>16; | ||
195 | wav_header[31]=(x&0xff000000)>>24; | ||
196 | |||
197 | // Subchunk2Size | ||
198 | x=filesize-44; | ||
199 | wav_header[40]=(x&0xff); | ||
200 | wav_header[41]=(x&0xff00)>>8; | ||
201 | wav_header[42]=(x&0xff0000)>>16; | ||
202 | wav_header[43]=(x&0xff000000)>>24; | ||
203 | |||
204 | rb->write(file_info->outfile,wav_header,sizeof(wav_header)); | ||
205 | rb->close(file_info->outfile); | ||
206 | } | ||
207 | |||
208 | static inline int16_t convert (int32_t i) | ||
209 | { | ||
210 | i >>= 15; | ||
211 | return (i > 32767) ? 32767 : ((i < -32768) ? -32768 : i); | ||
212 | } | ||
213 | |||
214 | void convert2s16_2 (sample_t * _f, int16_t * s16) | ||
215 | { | ||
216 | int i; | ||
217 | int32_t * f = (int32_t *) _f; | ||
218 | for (i = 0; i < 256; i++) { | ||
219 | s16[2*i] = LE_S16(convert (f[i])); | ||
220 | s16[2*i+1] = LE_S16(convert (f[i+256])); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | void ao_play(file_info_struct* file_info,sample_t* samples,int flags) { | ||
225 | int i; | ||
226 | static int16_t int16_samples[256*2]; | ||
227 | |||
228 | flags &= A52_CHANNEL_MASK | A52_LFE; | ||
229 | |||
230 | if (flags==A52_STEREO) { | ||
231 | // convert2s16_2(samples,int16_samples,flags); | ||
232 | for (i = 0; i < 256; i++) { | ||
233 | int16_samples[2*i] = LE_S16(convert (samples[i])); | ||
234 | int16_samples[2*i+1] = LE_S16(convert (samples[i+256])); | ||
235 | } | ||
236 | } else { | ||
237 | #ifdef SIMULATOR | ||
238 | fprintf(stderr,"ERROR: unsupported format: %d\n",flags); | ||
239 | #endif | ||
240 | } | ||
241 | |||
242 | i=rb->write(file_info->outfile,int16_samples,256*2*2); | ||
243 | |||
244 | #ifdef SIMULATOR | ||
245 | if (i!=(256*2*2)) { | ||
246 | fprintf(stderr,"Attempted to write %d bytes, wrote %d bytes\n",256*2*2,i); | ||
247 | } | ||
248 | #endif | ||
249 | } | ||
250 | |||
251 | |||
252 | void a52_decode_data (file_info_struct* file_info, uint8_t * start, uint8_t * end) | ||
253 | { | ||
254 | static uint8_t buf[3840]; | ||
255 | static uint8_t * bufptr = buf; | ||
256 | static uint8_t * bufpos = buf + 7; | ||
257 | |||
258 | /* | ||
259 | * sample_rate and flags are static because this routine could | ||
260 | * exit between the a52_syncinfo() and the ao_setup(), and we want | ||
261 | * to have the same values when we get back ! | ||
262 | */ | ||
263 | |||
264 | static int sample_rate; | ||
265 | static int flags; | ||
266 | int bit_rate; | ||
267 | int len; | ||
268 | |||
269 | while (1) { | ||
270 | len = end - start; | ||
271 | if (!len) | ||
272 | break; | ||
273 | if (len > bufpos - bufptr) | ||
274 | len = bufpos - bufptr; | ||
275 | memcpy (bufptr, start, len); | ||
276 | bufptr += len; | ||
277 | start += len; | ||
278 | if (bufptr == bufpos) { | ||
279 | if (bufpos == buf + 7) { | ||
280 | int length; | ||
281 | |||
282 | length = a52_syncinfo (buf, &flags, &sample_rate, &bit_rate); | ||
283 | if (!length) { | ||
284 | #ifdef SIMULATOR | ||
285 | fprintf (stderr, "skip\n"); | ||
286 | #endif | ||
287 | for (bufptr = buf; bufptr < buf + 6; bufptr++) | ||
288 | bufptr[0] = bufptr[1]; | ||
289 | continue; | ||
290 | } | ||
291 | bufpos = buf + length; | ||
292 | } else { | ||
293 | // The following two defaults are taken from audio_out_oss.c: | ||
294 | level_t level; | ||
295 | sample_t bias; | ||
296 | int i; | ||
297 | |||
298 | /* This is the configuration for the downmixing: */ | ||
299 | flags=A52_STEREO|A52_ADJUST_LEVEL|A52_LFE; | ||
300 | level=(1 << 26); | ||
301 | bias=0; | ||
302 | |||
303 | level = (level_t) (level * gain); | ||
304 | |||
305 | if (a52_frame (state, buf, &flags, &level, bias)) | ||
306 | goto error; | ||
307 | |||
308 | if (output==0) { | ||
309 | file_info->samfmt.bits=16; | ||
310 | file_info->samfmt.rate=sample_rate; | ||
311 | output=1; | ||
312 | // output=ao_open(&format); | ||
313 | } | ||
314 | |||
315 | // An A52 frame consists of 6 blocks of 256 samples | ||
316 | // So we decode and output them one block at a time | ||
317 | for (i = 0; i < 6; i++) { | ||
318 | if (a52_block (state)) { | ||
319 | goto error; | ||
320 | } | ||
321 | ao_play (file_info, a52_samples (state),flags); | ||
322 | file_info->current_sample+=256; | ||
323 | } | ||
324 | bufptr = buf; | ||
325 | bufpos = buf + 7; | ||
326 | // print_fps (0); | ||
327 | continue; | ||
328 | error: | ||
329 | #ifdef SIMULATOR | ||
330 | fprintf (stderr, "error\n"); | ||
331 | #endif | ||
332 | bufptr = buf; | ||
333 | bufpos = buf + 7; | ||
334 | } | ||
335 | } | ||
336 | } | ||
337 | } | ||
338 | |||
339 | /* this is the plugin entry point */ | ||
340 | enum plugin_status plugin_start(struct plugin_api* api, void* file) | ||
341 | { | ||
342 | int i,n,bytesleft; | ||
343 | char s[32]; | ||
344 | unsigned long ticks_taken; | ||
345 | unsigned long start_tick; | ||
346 | unsigned long long speed; | ||
347 | unsigned long xspeed; | ||
348 | int accel=0; // ??? This is the parameter to a52_init(). | ||
349 | |||
350 | /* this macro should be called as the first thing you do in the plugin. | ||
351 | it test that the api version and model the plugin was compiled for | ||
352 | matches the machine it is running on */ | ||
353 | TEST_PLUGIN_API(api); | ||
354 | |||
355 | /* if you are using a global api pointer, don't forget to copy it! | ||
356 | otherwise you will get lovely "I04: IllInstr" errors... :-) */ | ||
357 | rb = api; | ||
358 | |||
359 | /* now go ahead and have fun! */ | ||
360 | // rb->splash(HZ*2, true, "Hello world!"); | ||
361 | |||
362 | mem_ptr=0; | ||
363 | mp3buf=rb->plugin_get_mp3_buffer(&bufsize); | ||
364 | mallocbuf=mp3buf; | ||
365 | filebuf=&mp3buf[MALLOC_BUFSIZE]; | ||
366 | |||
367 | rb->snprintf(s,32,"mp3 bufsize: %d\r",bufsize); | ||
368 | rb->lcd_putsxy(0,100,s); | ||
369 | rb->lcd_update(); | ||
370 | |||
371 | file_info.infile=rb->open(file,O_RDONLY); | ||
372 | file_info.outfile=rb->creat("/ac3test.wav",O_WRONLY); | ||
373 | rb->write(file_info.outfile,wav_header,sizeof(wav_header)); | ||
374 | file_info.curpos=0; | ||
375 | file_info.filesize=rb->filesize(file_info.infile); | ||
376 | |||
377 | if (file_info.filesize > (bufsize-MALLOC_BUFSIZE)) { | ||
378 | rb->close(file_info.infile); | ||
379 | rb->splash(HZ*2, true, "File too large"); | ||
380 | return PLUGIN_ERROR; | ||
381 | } | ||
382 | |||
383 | rb->snprintf(s,32,"Loading file..."); | ||
384 | rb->lcd_putsxy(0,0,s); | ||
385 | rb->lcd_update(); | ||
386 | |||
387 | bytesleft=file_info.filesize; | ||
388 | i=0; | ||
389 | while (bytesleft > 0) { | ||
390 | n=rb->read(file_info.infile,&filebuf[i],bytesleft); | ||
391 | if (n < 0) { | ||
392 | rb->close(file_info.infile); | ||
393 | rb->splash(HZ*2, true, "ERROR READING FILE"); | ||
394 | return PLUGIN_ERROR; | ||
395 | } | ||
396 | i+=n; bytesleft-=n; | ||
397 | } | ||
398 | rb->close(file_info.infile); | ||
399 | |||
400 | state = a52_init (accel); | ||
401 | if (state == NULL) { | ||
402 | //fprintf (stderr, "A52 init failed\n"); | ||
403 | return PLUGIN_ERROR; | ||
404 | } | ||
405 | |||
406 | i=0; | ||
407 | start_tick=*(rb->current_tick); | ||
408 | while (file_info.curpos < file_info.filesize) { | ||
409 | i++; | ||
410 | if ((file_info.curpos+BUFFER_SIZE) < file_info.filesize) { | ||
411 | a52_decode_data (&file_info,&filebuf[file_info.curpos],&filebuf[file_info.curpos+BUFFER_SIZE]); | ||
412 | file_info.curpos+=BUFFER_SIZE; | ||
413 | } else { | ||
414 | a52_decode_data (&file_info,&filebuf[file_info.curpos],&filebuf[file_info.filesize-1]); | ||
415 | file_info.curpos=file_info.filesize; | ||
416 | } | ||
417 | |||
418 | rb->snprintf(s,32,"Bytes read: %d\r",file_info.curpos); | ||
419 | rb->lcd_putsxy(0,0,s); | ||
420 | rb->snprintf(s,32,"Samples Decoded: %d\r",file_info.current_sample); | ||
421 | rb->lcd_putsxy(0,20,s); | ||
422 | rb->snprintf(s,32,"Frames Decoded: %d\r",i); | ||
423 | rb->lcd_putsxy(0,40,s); | ||
424 | |||
425 | ticks_taken=*(rb->current_tick)-start_tick; | ||
426 | |||
427 | /* e.g.: | ||
428 | ticks_taken=500 | ||
429 | sam_fmt.rate=44,100 | ||
430 | samples_decoded=172,400 | ||
431 | (samples_decoded/sam_fmt.rate)*100=400 (time it should have taken) | ||
432 | % Speed=(400/500)*100=80% | ||
433 | |||
434 | */ | ||
435 | |||
436 | if (ticks_taken==0) { ticks_taken=1; } // Avoid fp exception. | ||
437 | |||
438 | speed=(100*file_info.current_sample)/file_info.samfmt.rate; | ||
439 | xspeed=(speed*10000)/ticks_taken; | ||
440 | rb->snprintf(s,32,"Speed %ld.%02ld %% Secs: %d",(xspeed/100),(xspeed%100),ticks_taken/100); | ||
441 | rb->lcd_putsxy(0,60,s); | ||
442 | |||
443 | rb->lcd_update(); | ||
444 | if (rb->button_get(false)!=BUTTON_NONE) { | ||
445 | close_wav(&file_info); | ||
446 | return PLUGIN_OK; | ||
447 | } | ||
448 | } | ||
449 | close_wav(&file_info); | ||
450 | |||
451 | //NO NEED: a52_free (state); | ||
452 | rb->splash(HZ*2, true, "FINISHED!"); | ||
453 | return PLUGIN_OK; | ||
454 | } | ||
455 | #endif /* CONFIG_HWCODEC == MASNONE */ | ||
diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config index bf2488fea4..bb5752c9db 100644 --- a/apps/plugins/viewers.config +++ b/apps/plugins/viewers.config | |||
@@ -8,3 +8,5 @@ m3u,search.rock,00 00 00 00 00 00 | |||
8 | txt,sort.rock, 00 00 00 00 00 00 | 8 | txt,sort.rock, 00 00 00 00 00 00 |
9 | mp2,mpa2wav.rock, 00 00 00 00 00 00 | 9 | mp2,mpa2wav.rock, 00 00 00 00 00 00 |
10 | mp3,mpa2wav.rock, 00 00 00 00 00 00 | 10 | mp3,mpa2wav.rock, 00 00 00 00 00 00 |
11 | ac3,a52towav.rock, 00 00 00 00 00 00 | ||
12 | a52,a52towav.rock, 00 00 00 00 00 00 | ||