summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/plugins/Makefile2
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/a52towav.c455
-rw-r--r--apps/plugins/viewers.config2
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
17endif 17endif
18 18
19ifdef SOFTWARECODECS 19ifdef SOFTWARECODECS
20 CODECLIBS = -lmad 20 CODECLIBS = -lmad -la52
21endif 21endif
22 22
23LDS := plugin.lds 23LDS := 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 */
67mpa2wav.c 67mpa2wav.c
68a52towav.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
38typedef 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 */
50typedef 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
63file_info_struct file_info;
64
65#define MALLOC_BUFSIZE (512*1024)
66
67int mem_ptr;
68int bufsize;
69unsigned char* mp3buf; // The actual MP3 buffer from Rockbox
70unsigned char* mallocbuf; // 512K from the start of MP3 buffer
71unsigned char* filebuf; // The rest of the MP3 buffer
72
73
74
75#define BUFFER_SIZE 4096
76//static uint8_t buffer[BUFFER_SIZE];
77static float gain = 1;
78static a52_state_t * state;
79
80int output;
81
82// DAVE: I'm not sure what these are for.
83int disable_accel=0;
84int disable_adjust=0;
85int 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 */
92static struct plugin_api* rb;
93
94void* 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
107void* 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
114void free(void* ptr) {
115 (void)ptr;
116}
117
118void* realloc(void* ptr, size_t size) {
119 void* x;
120 (void)ptr;
121 x=malloc(size);
122 return(x);
123}
124
125void *memcpy(void *dest, const void *src, size_t n) {
126 return(rb->memcpy(dest,src,n));
127}
128
129void *memset(void *s, int c, size_t n) {
130 return(rb->memset(s,c,n));
131}
132
133int memcmp(const void *s1, const void *s2, size_t n) {
134 return(rb->memcmp(s1,s2,n));
135}
136
137void* 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
147void 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
154static 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
169void 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
208static inline int16_t convert (int32_t i)
209{
210 i >>= 15;
211 return (i > 32767) ? 32767 : ((i < -32768) ? -32768 : i);
212}
213
214void 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
224void 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
252void 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 */
340enum 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
8txt,sort.rock, 00 00 00 00 00 00 8txt,sort.rock, 00 00 00 00 00 00
9mp2,mpa2wav.rock, 00 00 00 00 00 00 9mp2,mpa2wav.rock, 00 00 00 00 00 00
10mp3,mpa2wav.rock, 00 00 00 00 00 00 10mp3,mpa2wav.rock, 00 00 00 00 00 00
11ac3,a52towav.rock, 00 00 00 00 00 00
12a52,a52towav.rock, 00 00 00 00 00 00