summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/plugins/test_codec.c326
1 files changed, 289 insertions, 37 deletions
diff --git a/apps/plugins/test_codec.c b/apps/plugins/test_codec.c
index 3d8da48341..4d9bff7a45 100644
--- a/apps/plugins/test_codec.c
+++ b/apps/plugins/test_codec.c
@@ -22,6 +22,16 @@ PLUGIN_HEADER
22 22
23static struct plugin_api* rb; 23static struct plugin_api* rb;
24 24
25struct wavinfo_t
26{
27 int fd;
28 int samplerate;
29 int channels;
30 int sampledepth;
31 int stereomode;
32 int totalsamples;
33};
34
25static void* audiobuf; 35static void* audiobuf;
26static void* codec_mallocbuf; 36static void* codec_mallocbuf;
27static size_t audiosize; 37static size_t audiosize;
@@ -35,6 +45,85 @@ static bool taginfo_ready = true;
35 45
36static volatile unsigned int elapsed; 46static volatile unsigned int elapsed;
37static volatile bool codec_playing; 47static volatile bool codec_playing;
48struct wavinfo_t wavinfo;
49
50static unsigned char wav_header[44] =
51{
52 'R','I','F','F', // 0 - ChunkID
53 0,0,0,0, // 4 - ChunkSize (filesize-8)
54 'W','A','V','E', // 8 - Format
55 'f','m','t',' ', // 12 - SubChunkID
56 16,0,0,0, // 16 - SubChunk1ID // 16 for PCM
57 1,0, // 20 - AudioFormat (1=16-bit)
58 0,0, // 22 - NumChannels
59 0,0,0,0, // 24 - SampleRate in Hz
60 0,0,0,0, // 28 - Byte Rate (SampleRate*NumChannels*(BitsPerSample/8)
61 0,0, // 32 - BlockAlign (== NumChannels * BitsPerSample/8)
62 16,0, // 34 - BitsPerSample
63 'd','a','t','a', // 36 - Subchunk2ID
64 0,0,0,0 // 40 - Subchunk2Size
65};
66
67static inline void int2le32(unsigned char* buf, int32_t x)
68{
69 buf[0] = (x & 0xff);
70 buf[1] = (x & 0xff00) >> 8;
71 buf[2] = (x & 0xff0000) >> 16;
72 buf[3] = (x & 0xff000000) >>24;
73}
74
75static inline void int2le24(unsigned char* buf, int32_t x)
76{
77 buf[0] = (x & 0xff);
78 buf[1] = (x & 0xff00) >> 8;
79 buf[2] = (x & 0xff0000) >> 16;
80}
81
82static inline void int2le16(unsigned char* buf, int16_t x)
83{
84 buf[0] = (x & 0xff);
85 buf[1] = (x & 0xff00) >> 8;
86}
87
88void init_wav(char* filename)
89{
90 wavinfo.totalsamples = 0;
91
92 wavinfo.fd = rb->creat(filename);
93
94 if (wavinfo.fd >= 0)
95 {
96 /* Write WAV header - we go back and fill in the details at the end */
97 rb->write(wavinfo.fd, wav_header, sizeof(wav_header));
98 }
99}
100
101
102void close_wav(void) {
103 int filesize = rb->filesize(wavinfo.fd);
104 int channels = (wavinfo.stereomode == STEREO_MONO) ? 1 : 2;
105 int bps = 16; /* TODO */
106
107 /* We assume 16-bit, Stereo */
108
109 rb->lseek(wavinfo.fd,0,SEEK_SET);
110
111 int2le32(wav_header+4, filesize-8); /* ChunkSize */
112
113 int2le16(wav_header+22, channels);
114
115 int2le32(wav_header+24, wavinfo.samplerate);
116
117 int2le32(wav_header+28, wavinfo.samplerate * channels * (bps / 8)); /* ByteRate */
118
119 int2le16(wav_header+32, channels * (bps / 8));
120
121 int2le32(wav_header+40, filesize - 44); /* Subchunk2Size */
122
123 rb->write(wavinfo.fd, wav_header, sizeof(wav_header));
124
125 rb->close(wavinfo.fd);
126}
38 127
39/* Returns buffer to malloc array. Only codeclib should need this. */ 128/* Returns buffer to malloc array. Only codeclib should need this. */
40static void* get_codec_memory(size_t *size) 129static void* get_codec_memory(size_t *size)
@@ -44,9 +133,8 @@ static void* get_codec_memory(size_t *size)
44 return codec_mallocbuf; 133 return codec_mallocbuf;
45} 134}
46 135
47/* Insert PCM data into audio buffer for playback. Playback will start 136/* Null output */
48 automatically. */ 137static bool pcmbuf_insert_null(const void *ch1, const void *ch2, int count)
49static bool pcmbuf_insert(const void *ch1, const void *ch2, int count)
50{ 138{
51 /* Always successful - just discard data */ 139 /* Always successful - just discard data */
52 (void)ch1; 140 (void)ch1;
@@ -56,6 +144,100 @@ static bool pcmbuf_insert(const void *ch1, const void *ch2, int count)
56 return true; 144 return true;
57} 145}
58 146
147/* 64KB should be enough */
148static unsigned char wavbuffer[64*1024];
149
150static inline int32_t clip_sample(int32_t sample)
151{
152 if ((int16_t)sample != sample)
153 sample = 0x7fff ^ (sample >> 31);
154
155 return sample;
156}
157
158
159/* WAV output */
160static bool pcmbuf_insert_wav(const void *ch1, const void *ch2, int count)
161{
162 const int16_t* data1_16;
163 const int16_t* data2_16;
164 const int32_t* data1_32;
165 const int32_t* data2_32;
166 unsigned char* p = wavbuffer;
167 int scale = wavinfo.sampledepth - 15;
168
169 if (wavinfo.sampledepth <= 16) {
170 data1_16 = ch1;
171 data2_16 = ch2;
172
173 switch(wavinfo.stereomode)
174 {
175 case STEREO_INTERLEAVED:
176 while (count--) {
177 int2le16(p,*data1_16++);
178 p += 2;
179 int2le16(p,*data1_16++);
180 p += 2;
181 }
182 break;
183
184 case STEREO_NONINTERLEAVED:
185 while (count--) {
186 int2le16(p,*data1_16++);
187 p += 2;
188 int2le16(p,*data2_16++);
189 p += 2;
190 }
191
192 break;
193
194 case STEREO_MONO:
195 while (count--) {
196 int2le16(p,*data1_16++);
197 p += 2;
198 }
199 break;
200 }
201 } else {
202 data1_32 = ch1;
203 data2_32 = ch2;
204
205 switch(wavinfo.stereomode)
206 {
207 case STEREO_INTERLEAVED:
208 while (count--) {
209 int2le16(p, clip_sample((*data1_32++) >> scale));
210 p += 2;
211 int2le16(p, clip_sample((*data1_32++) >> scale));
212 p += 2;
213 }
214 break;
215
216 case STEREO_NONINTERLEAVED:
217 while (count--) {
218 int2le16(p, clip_sample((*data1_32++) >> scale));
219 p += 2;
220 int2le16(p, clip_sample((*data2_32++) >> scale));
221 p += 2;
222 }
223
224 break;
225
226 case STEREO_MONO:
227 while (count--) {
228 int2le16(p, clip_sample((*data1_32++) >> scale));
229 p += 2;
230 }
231 break;
232 }
233 }
234
235 wavinfo.totalsamples += count;
236 rb->write(wavinfo.fd, wavbuffer, p - wavbuffer);
237
238 return true;
239}
240
59 241
60/* Set song position in WPS (value in ms). */ 242/* Set song position in WPS (value in ms). */
61static void set_elapsed(unsigned int value) 243static void set_elapsed(unsigned int value)
@@ -68,7 +250,6 @@ static void set_elapsed(unsigned int value)
68 Will return number of bytes read or 0 if end of file. */ 250 Will return number of bytes read or 0 if end of file. */
69static size_t read_filebuf(void *ptr, size_t size) 251static size_t read_filebuf(void *ptr, size_t size)
70{ 252{
71 DEBUGF("read_filebuf(_,%d)\n",(int)size);
72 if (ci.curpos > (off_t)track.filesize) 253 if (ci.curpos > (off_t)track.filesize)
73 { 254 {
74 return 0; 255 return 0;
@@ -76,7 +257,6 @@ static size_t read_filebuf(void *ptr, size_t size)
76 /* TODO: Don't read beyond end of buffer */ 257 /* TODO: Don't read beyond end of buffer */
77 rb->memcpy(ptr, audiobuf + ci.curpos, size); 258 rb->memcpy(ptr, audiobuf + ci.curpos, size);
78 ci.curpos += size; 259 ci.curpos += size;
79 DEBUGF("New ci.curpos = %d\n",ci.curpos);
80 return size; 260 return size;
81 } 261 }
82} 262}
@@ -98,7 +278,6 @@ static void* request_buffer(size_t *realsize, size_t reqsize)
98static void advance_buffer(size_t amount) 278static void advance_buffer(size_t amount)
99{ 279{
100 ci.curpos += amount; 280 ci.curpos += amount;
101 DEBUGF("advance_buffer(%d) - new ci.curpos=%d\n",(int)amount,(int)ci.curpos);
102} 281}
103 282
104 283
@@ -152,7 +331,6 @@ static void discard_codec(void)
152 331
153static void set_offset(size_t value) 332static void set_offset(size_t value)
154{ 333{
155 DEBUGF("set_offset(%d)\n",(int)value);
156 /* ??? */ 334 /* ??? */
157 (void)value; 335 (void)value;
158} 336}
@@ -161,9 +339,25 @@ static void set_offset(size_t value)
161/* Configure different codec buffer parameters. */ 339/* Configure different codec buffer parameters. */
162static void configure(int setting, intptr_t value) 340static void configure(int setting, intptr_t value)
163{ 341{
164 (void)setting; 342 switch(setting)
165 (void)value; 343 {
166 DEBUGF("setting %d = %d\n",setting,(int)value); 344 case DSP_SWITCH_FREQUENCY:
345 case DSP_SET_FREQUENCY:
346 DEBUGF("samplerate=%d\n",(int)value);
347 wavinfo.samplerate = (int)value;
348 break;
349
350 case DSP_SET_SAMPLE_DEPTH:
351 DEBUGF("sampledepth = %d\n",(int)value);
352 wavinfo.sampledepth=(int)value;
353 break;
354
355 case DSP_SET_STEREO_MODE:
356 DEBUGF("Stereo mode = %d\n",(int)value);
357 wavinfo.stereomode=(int)value;
358 break;
359 }
360
167} 361}
168 362
169static void init_ci(void) 363static void init_ci(void)
@@ -171,7 +365,12 @@ static void init_ci(void)
171 /* --- Our "fake" implementations of the codec API functions. --- */ 365 /* --- Our "fake" implementations of the codec API functions. --- */
172 366
173 ci.get_codec_memory = get_codec_memory; 367 ci.get_codec_memory = get_codec_memory;
174 ci.pcmbuf_insert = pcmbuf_insert; 368
369 if (wavinfo.fd >= 0) {
370 ci.pcmbuf_insert = pcmbuf_insert_wav;
371 } else {
372 ci.pcmbuf_insert = pcmbuf_insert_null;
373 }
175 ci.set_elapsed = set_elapsed; 374 ci.set_elapsed = set_elapsed;
176 ci.read_filebuf = read_filebuf; 375 ci.read_filebuf = read_filebuf;
177 ci.request_buffer = request_buffer; 376 ci.request_buffer = request_buffer;
@@ -244,6 +443,7 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
244 size_t n; 443 size_t n;
245 int fd; 444 int fd;
246 int i; 445 int i;
446 enum plugin_status res = PLUGIN_OK;
247 unsigned long starttick; 447 unsigned long starttick;
248 unsigned long ticks; 448 unsigned long ticks;
249 unsigned long speed; 449 unsigned long speed;
@@ -252,6 +452,9 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
252 unsigned char* codec_stack_copy; 452 unsigned char* codec_stack_copy;
253 size_t codec_stack_size; 453 size_t codec_stack_size;
254 struct thread_entry* codecthread_id; 454 struct thread_entry* codecthread_id;
455 int result;
456 char* ch;
457 int line = 0;
255 458
256 rb = api; 459 rb = api;
257 460
@@ -261,6 +464,10 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
261 return PLUGIN_ERROR; 464 return PLUGIN_ERROR;
262 } 465 }
263 466
467#ifdef SIMULATOR
468 /* The simulator thread implementation doesn't have stack buffers */
469 (void)i;
470#else
264 /* Borrow the codec thread's stack (in IRAM on most targets) */ 471 /* Borrow the codec thread's stack (in IRAM on most targets) */
265 codec_stack = NULL; 472 codec_stack = NULL;
266 for (i = 0; i < MAXTHREADS; i++) 473 for (i = 0; i < MAXTHREADS; i++)
@@ -278,14 +485,17 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
278 rb->splash(HZ*2, "No codec thread!"); 485 rb->splash(HZ*2, "No codec thread!");
279 return PLUGIN_ERROR; 486 return PLUGIN_ERROR;
280 } 487 }
488#endif
281 489
282 codec_mallocbuf = rb->plugin_get_audio_buffer(&audiosize); 490 codec_mallocbuf = rb->plugin_get_audio_buffer(&audiosize);
283 codec_stack_copy = codec_mallocbuf + 512*1024; 491 codec_stack_copy = codec_mallocbuf + 512*1024;
284 audiobuf = codec_stack_copy + codec_stack_size; 492 audiobuf = codec_stack_copy + codec_stack_size;
285 audiosize -= 512*1024 + codec_stack_size; 493 audiosize -= 512*1024 + codec_stack_size;
286 494
495#ifndef SIMULATOR
287 /* Backup the codec thread's stack */ 496 /* Backup the codec thread's stack */
288 rb->memcpy(codec_stack_copy,codec_stack,codec_stack_size); 497 rb->memcpy(codec_stack_copy,codec_stack,codec_stack_size);
498#endif
289 499
290 fd = rb->open(parameter,O_RDONLY); 500 fd = rb->open(parameter,O_RDONLY);
291 if (fd < 0) 501 if (fd < 0)
@@ -309,14 +519,46 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
309 return PLUGIN_ERROR; 519 return PLUGIN_ERROR;
310 } 520 }
311 521
522#ifdef HAVE_ADJUSTABLE_CPU_FREQ
523 rb->cpu_boost(true);
524#endif
525 rb->lcd_clear_display();
526 rb->lcd_update();
527
528 MENUITEM_STRINGLIST(menu,"test_codec",NULL,"Speed test","Write WAV");
529
530 rb->lcd_clear_display();
531
532 DEBUGF("Calling menu\n");
533 result=rb->do_menu(&menu,&result);
534 DEBUGF("Done\n");
535
536 if (result==1) {
537 init_wav("/test.wav");
538 if (wavinfo.fd < 0) {
539 rb->splash(HZ*2, "Cannot create /test.wav");
540 res = PLUGIN_ERROR;
541 goto exit;
542 }
543 } else if (result == MENU_ATTACHED_USB) {
544 res = PLUGIN_USB_CONNECTED;
545 goto exit;
546 } else if (result < 0) {
547 res = PLUGIN_OK;
548 goto exit;
549 }
550
551 rb->lcd_clear_display();
312 rb->splash(0, "Loading..."); 552 rb->splash(0, "Loading...");
553 rb->lcd_clear_display();
313 554
314 n = rb->read(fd, audiobuf, track.filesize); 555 n = rb->read(fd, audiobuf, track.filesize);
315 556
316 if (n != track.filesize) 557 if (n != track.filesize)
317 { 558 {
318 rb->splash(HZ*2, "Read failed."); 559 rb->splash(HZ*2, "Read failed.");
319 return PLUGIN_ERROR; 560 res = PLUGIN_ERROR;
561 goto exit;
320 } 562 }
321 563
322 /* Initialise the function pointers in the codec API */ 564 /* Initialise the function pointers in the codec API */
@@ -331,15 +573,6 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
331 ci.new_track = 0; 573 ci.new_track = 0;
332 ci.seek_time = 0; 574 ci.seek_time = 0;
333 575
334#ifdef HAVE_ADJUSTABLE_CPU_FREQ
335 rb->cpu_boost(true);
336#endif
337 rb->lcd_set_backdrop(NULL);
338 rb->lcd_set_foreground(LCD_WHITE);
339 rb->lcd_set_background(LCD_BLACK);
340 rb->lcd_clear_display();
341 rb->lcd_update();
342
343 starttick = *rb->current_tick; 576 starttick = *rb->current_tick;
344 577
345 codec_playing = true; 578 codec_playing = true;
@@ -352,44 +585,63 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
352 goto exit; 585 goto exit;
353 } 586 }
354 587
588 /* Display filename (excluding any path)*/
589 ch = rb->strrchr(parameter, '/');
590 if (ch==NULL)
591 ch = parameter;
592 else
593 ch++;
594
595 rb->snprintf(str,sizeof(str),"%s",ch);
596 rb->lcd_puts(0,line++,str);
597
355 /* Wait for codec thread to die */ 598 /* Wait for codec thread to die */
356 while (codec_playing) 599 while (codec_playing)
357 { 600 {
358 rb->sleep(HZ); 601 rb->sleep(HZ);
359 rb->snprintf(str,sizeof(str),"%d of %d",elapsed,(int)track.id3.length); 602 rb->snprintf(str,sizeof(str),"%d of %d",elapsed,(int)track.id3.length);
360 rb->lcd_puts(0,0,str); 603 rb->lcd_puts(0,line,str);
361 rb->lcd_update(); 604 rb->lcd_update();
362 } 605 }
606 line++;
607
608 /* Close WAV file (if there was one) */
609 if (wavinfo.fd >= 0) {
610 close_wav();
611 rb->lcd_puts(0,line++,"Wrote /test.wav");
612 } else {
613 /* Display benchmark information */
363 614
364 /* Display benchmark information */ 615 ticks = *rb->current_tick - starttick;
365 616 rb->snprintf(str,sizeof(str),"Decode time - %d.%02ds",(int)ticks/100,(int)ticks%100);
366 ticks = *rb->current_tick - starttick; 617 rb->lcd_puts(0,line++,str);
367 rb->snprintf(str,sizeof(str),"Decode time - %d.%02ds",(int)ticks/100,(int)ticks%100);
368 rb->lcd_puts(0,1,str);
369 618
370 duration = track.id3.length / 10; 619 duration = track.id3.length / 10;
371 rb->snprintf(str,sizeof(str),"File duration - %d.%02ds",(int)duration/100,(int)duration%100); 620 rb->snprintf(str,sizeof(str),"File duration - %d.%02ds",(int)duration/100,(int)duration%100);
372 rb->lcd_puts(0,2,str); 621 rb->lcd_puts(0,line++,str);
373 622
374 if (ticks > 0) 623 if (ticks > 0)
375 speed = duration * 10000 / ticks; 624 speed = duration * 10000 / ticks;
376 else 625 else
377 speed = 0; 626 speed = 0;
378 627
379 rb->snprintf(str,sizeof(str),"%d.%02d%% realtime",(int)speed/100,(int)speed%100); 628 rb->snprintf(str,sizeof(str),"%d.%02d%% realtime",(int)speed/100,(int)speed%100);
380 rb->lcd_puts(0,3,str); 629 rb->lcd_puts(0,line++,str);
381 630
631 }
382 rb->lcd_update(); 632 rb->lcd_update();
383 633
384 while (rb->button_get(true) != BUTTON_SELECT); 634 while (rb->button_get(true) != BUTTON_SELECT);
385 635
386exit: 636exit:
637#ifndef SIMULATOR
387 /* Restore the codec thread's stack */ 638 /* Restore the codec thread's stack */
388 rb->memcpy(codec_stack, codec_stack_copy, codec_stack_size); 639 rb->memcpy(codec_stack, codec_stack_copy, codec_stack_size);
640#endif
389 641
390#ifdef HAVE_ADJUSTABLE_CPU_FREQ 642#ifdef HAVE_ADJUSTABLE_CPU_FREQ
391 rb->cpu_boost(false); 643 rb->cpu_boost(false);
392#endif 644#endif
393 645
394 return PLUGIN_OK; 646 return res;
395} 647}