diff options
Diffstat (limited to 'apps/plugins/a52towav.c')
-rw-r--r-- | apps/plugins/a52towav.c | 312 |
1 files changed, 38 insertions, 274 deletions
diff --git a/apps/plugins/a52towav.c b/apps/plugins/a52towav.c index 17b6c91e51..552479b0b0 100644 --- a/apps/plugins/a52towav.c +++ b/apps/plugins/a52towav.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * \/ \/ \/ \/ \/ | 7 | * \/ \/ \/ \/ \/ |
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2002 Björn Stenberg | 10 | * Copyright (C) 2005 Dave Chapman |
11 | * | 11 | * |
12 | * All files in this archive are subject to the GNU General Public License. | 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. | 13 | * See the file COPYING in the source tree root for full license agreement. |
@@ -27,7 +27,18 @@ | |||
27 | #include <codecs/liba52/config.h> | 27 | #include <codecs/liba52/config.h> |
28 | #include <codecs/liba52/a52.h> | 28 | #include <codecs/liba52/a52.h> |
29 | 29 | ||
30 | /* Currently used for WAV output */ | 30 | static struct plugin_api* rb; |
31 | |||
32 | /* Helper functions common to all decoder test viewers (uses rb) */ | ||
33 | |||
34 | #include "xxx2wav.h" | ||
35 | |||
36 | /* FIX: We can remove this warning when the build system has a | ||
37 | mechanism for auto-detecting the endianness of the target CPU - | ||
38 | WORDS_BIGENDIAN is defined in liba52/config.h and is also used | ||
39 | internally by liba52. | ||
40 | */ | ||
41 | |||
31 | #ifdef WORDS_BIGENDIAN | 42 | #ifdef WORDS_BIGENDIAN |
32 | #warning ************************************* BIG ENDIAN | 43 | #warning ************************************* BIG ENDIAN |
33 | #define LE_S16(x) ( (uint16_t) ( ((uint16_t)(x) >> 8) | ((uint16_t)(x) << 8) ) ) | 44 | #define LE_S16(x) ( (uint16_t) ( ((uint16_t)(x) >> 8) | ((uint16_t)(x) << 8) ) ) |
@@ -35,192 +46,16 @@ | |||
35 | #define LE_S16(x) (x) | 46 | #define LE_S16(x) (x) |
36 | #endif | 47 | #endif |
37 | 48 | ||
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 | 49 | ||
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; | 50 | static float gain = 1; |
78 | static a52_state_t * state; | 51 | static a52_state_t * state; |
79 | 52 | ||
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) | 53 | static inline int16_t convert (int32_t i) |
209 | { | 54 | { |
210 | i >>= 15; | 55 | i >>= 15; |
211 | return (i > 32767) ? 32767 : ((i < -32768) ? -32768 : i); | 56 | return (i > 32767) ? 32767 : ((i < -32768) ? -32768 : i); |
212 | } | 57 | } |
213 | 58 | ||
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) { | 59 | void ao_play(file_info_struct* file_info,sample_t* samples,int flags) { |
225 | int i; | 60 | int i; |
226 | static int16_t int16_samples[256*2]; | 61 | static int16_t int16_samples[256*2]; |
@@ -228,7 +63,6 @@ void ao_play(file_info_struct* file_info,sample_t* samples,int flags) { | |||
228 | flags &= A52_CHANNEL_MASK | A52_LFE; | 63 | flags &= A52_CHANNEL_MASK | A52_LFE; |
229 | 64 | ||
230 | if (flags==A52_STEREO) { | 65 | if (flags==A52_STEREO) { |
231 | // convert2s16_2(samples,int16_samples,flags); | ||
232 | for (i = 0; i < 256; i++) { | 66 | for (i = 0; i < 256; i++) { |
233 | int16_samples[2*i] = LE_S16(convert (samples[i])); | 67 | int16_samples[2*i] = LE_S16(convert (samples[i])); |
234 | int16_samples[2*i+1] = LE_S16(convert (samples[i+256])); | 68 | int16_samples[2*i+1] = LE_S16(convert (samples[i+256])); |
@@ -239,13 +73,8 @@ void ao_play(file_info_struct* file_info,sample_t* samples,int flags) { | |||
239 | #endif | 73 | #endif |
240 | } | 74 | } |
241 | 75 | ||
76 | /* FIX: Buffer the disk write to write larger amounts at one */ | ||
242 | i=rb->write(file_info->outfile,int16_samples,256*2*2); | 77 | 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 | } | 78 | } |
250 | 79 | ||
251 | 80 | ||
@@ -304,13 +133,10 @@ void a52_decode_data (file_info_struct* file_info, uint8_t * start, uint8_t * en | |||
304 | 133 | ||
305 | if (a52_frame (state, buf, &flags, &level, bias)) | 134 | if (a52_frame (state, buf, &flags, &level, bias)) |
306 | goto error; | 135 | goto error; |
136 | file_info->frames_decoded++; | ||
307 | 137 | ||
308 | if (output==0) { | 138 | /* We assume this never changes */ |
309 | file_info->samfmt.bits=16; | 139 | file_info->samplerate=sample_rate; |
310 | file_info->samfmt.rate=sample_rate; | ||
311 | output=1; | ||
312 | // output=ao_open(&format); | ||
313 | } | ||
314 | 140 | ||
315 | // An A52 frame consists of 6 blocks of 256 samples | 141 | // An A52 frame consists of 6 blocks of 256 samples |
316 | // So we decode and output them one block at a time | 142 | // So we decode and output them one block at a time |
@@ -323,7 +149,6 @@ void a52_decode_data (file_info_struct* file_info, uint8_t * start, uint8_t * en | |||
323 | } | 149 | } |
324 | bufptr = buf; | 150 | bufptr = buf; |
325 | bufpos = buf + 7; | 151 | bufpos = buf + 7; |
326 | // print_fps (0); | ||
327 | continue; | 152 | continue; |
328 | error: | 153 | error: |
329 | #ifdef SIMULATOR | 154 | #ifdef SIMULATOR |
@@ -336,111 +161,47 @@ void a52_decode_data (file_info_struct* file_info, uint8_t * start, uint8_t * en | |||
336 | } | 161 | } |
337 | } | 162 | } |
338 | 163 | ||
164 | |||
165 | #define BUFFER_SIZE 4096 | ||
166 | |||
339 | /* this is the plugin entry point */ | 167 | /* this is the plugin entry point */ |
340 | enum plugin_status plugin_start(struct plugin_api* api, void* file) | 168 | enum plugin_status plugin_start(struct plugin_api* api, void* file) |
341 | { | 169 | { |
342 | int i,n,bytesleft; | 170 | /* Generic plugin initialisation */ |
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 | 171 | ||
355 | /* if you are using a global api pointer, don't forget to copy it! | 172 | TEST_PLUGIN_API(api); |
356 | otherwise you will get lovely "I04: IllInstr" errors... :-) */ | ||
357 | rb = api; | 173 | rb = api; |
358 | 174 | ||
359 | /* now go ahead and have fun! */ | ||
360 | // rb->splash(HZ*2, true, "Hello world!"); | ||
361 | 175 | ||
362 | mem_ptr=0; | 176 | /* This function sets up the buffers and reads the file into RAM */ |
363 | mp3buf=rb->plugin_get_mp3_buffer(&bufsize); | ||
364 | mallocbuf=mp3buf; | ||
365 | filebuf=&mp3buf[MALLOC_BUFSIZE]; | ||
366 | 177 | ||
367 | rb->snprintf(s,32,"mp3 bufsize: %d\r",bufsize); | 178 | if (local_init(file,&file_info)) { |
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; | 179 | return PLUGIN_ERROR; |
381 | } | 180 | } |
382 | 181 | ||
383 | rb->snprintf(s,32,"Loading file..."); | 182 | /* Intialise the A52 decoder and check for success */ |
384 | rb->lcd_putsxy(0,0,s); | 183 | state = a52_init (0); // Parameter is "accel" |
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 | 184 | ||
400 | state = a52_init (accel); | ||
401 | if (state == NULL) { | 185 | if (state == NULL) { |
402 | //fprintf (stderr, "A52 init failed\n"); | 186 | rb->splash(HZ*2, true, "a52_init failed"); |
403 | return PLUGIN_ERROR; | 187 | return PLUGIN_ERROR; |
404 | } | 188 | } |
405 | 189 | ||
406 | i=0; | 190 | /* The main decoding loop */ |
407 | start_tick=*(rb->current_tick); | 191 | |
192 | file_info.start_tick=*(rb->current_tick); | ||
408 | while (file_info.curpos < file_info.filesize) { | 193 | while (file_info.curpos < file_info.filesize) { |
409 | i++; | 194 | |
410 | if ((file_info.curpos+BUFFER_SIZE) < file_info.filesize) { | 195 | 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]); | 196 | a52_decode_data(&file_info,&filebuf[file_info.curpos],&filebuf[file_info.curpos+BUFFER_SIZE]); |
412 | file_info.curpos+=BUFFER_SIZE; | 197 | file_info.curpos+=BUFFER_SIZE; |
413 | } else { | 198 | } else { |
414 | a52_decode_data (&file_info,&filebuf[file_info.curpos],&filebuf[file_info.filesize-1]); | 199 | a52_decode_data(&file_info,&filebuf[file_info.curpos],&filebuf[file_info.filesize-1]); |
415 | file_info.curpos=file_info.filesize; | 200 | file_info.curpos=file_info.filesize; |
416 | } | 201 | } |
417 | 202 | ||
418 | rb->snprintf(s,32,"Bytes read: %d\r",file_info.curpos); | 203 | display_status(&file_info); |
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 | 204 | ||
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) { | 205 | if (rb->button_get(false)!=BUTTON_NONE) { |
445 | close_wav(&file_info); | 206 | close_wav(&file_info); |
446 | return PLUGIN_OK; | 207 | return PLUGIN_OK; |
@@ -448,7 +209,10 @@ enum plugin_status plugin_start(struct plugin_api* api, void* file) | |||
448 | } | 209 | } |
449 | close_wav(&file_info); | 210 | close_wav(&file_info); |
450 | 211 | ||
451 | //NO NEED: a52_free (state); | 212 | /* Cleanly close and exit */ |
213 | |||
214 | //NOT NEEDED: a52_free (state); | ||
215 | |||
452 | rb->splash(HZ*2, true, "FINISHED!"); | 216 | rb->splash(HZ*2, true, "FINISHED!"); |
453 | return PLUGIN_OK; | 217 | return PLUGIN_OK; |
454 | } | 218 | } |