1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 Dave Chapman
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
25 /* All swcodec targets have BUTTON_SELECT apart from the H10 and M3 */
27 #if CONFIG_KEYPAD == IRIVER_H10_PAD
28 #define TESTCODEC_EXITBUTTON BUTTON_RIGHT
29 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
30 #define TESTCODEC_EXITBUTTON BUTTON_RC_PLAY
31 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
32 #define TESTCODEC_EXITBUTTON BUTTON_PLAY
33 #elif CONFIG_KEYPAD == COWOND2_PAD || CONFIG_KEYPAD == ONDAVX747_PAD
34 #define TESTCODEC_EXITBUTTON BUTTON_POWER
35 #elif defined(HAVE_TOUCHSCREEN)
36 #define TESTCODEC_EXITBUTTON BUTTON_TOPLEFT
38 #define TESTCODEC_EXITBUTTON BUTTON_SELECT
41 /* Log functions copied from test_disk.c */
43 static int max_line
= 0;
44 static int log_fd
= -1;
45 static char logfilename
[MAX_PATH
];
47 static bool log_init(bool use_logfile
)
51 rb
->lcd_getstringsize("A", NULL
, &h
);
52 max_line
= LCD_HEIGHT
/ h
;
54 rb
->lcd_clear_display();
58 rb
->create_numbered_filename(logfilename
, "/", "test_codec_log_", ".txt",
59 2 IF_CNFN_NUM_(, NULL
));
60 log_fd
= rb
->open(logfilename
, O_RDWR
|O_CREAT
|O_TRUNC
);
67 static void log_text(char *text
, bool advance
)
69 rb
->lcd_puts(0, line
, text
);
73 if (++line
>= max_line
)
76 rb
->fdprintf(log_fd
, "%s\n", text
);
80 static void log_close(void)
96 static void* audiobuf
;
97 static void* codec_mallocbuf
;
98 static size_t audiosize
;
99 static char str
[MAX_PATH
];
101 /* Our local implementation of the codec API */
102 static struct codec_api ci
;
104 struct test_track_info
{
105 struct mp3entry id3
; /* TAG metadata */
106 size_t filesize
; /* File total length */
109 static struct test_track_info track
;
110 static bool taginfo_ready
= true;
114 static volatile unsigned int elapsed
;
115 static volatile bool codec_playing
;
116 static volatile long endtick
;
117 struct wavinfo_t wavinfo
;
119 static unsigned char wav_header
[44] =
121 'R','I','F','F', // 0 - ChunkID
122 0,0,0,0, // 4 - ChunkSize (filesize-8)
123 'W','A','V','E', // 8 - Format
124 'f','m','t',' ', // 12 - SubChunkID
125 16,0,0,0, // 16 - SubChunk1ID // 16 for PCM
126 1,0, // 20 - AudioFormat (1=16-bit)
127 0,0, // 22 - NumChannels
128 0,0,0,0, // 24 - SampleRate in Hz
129 0,0,0,0, // 28 - Byte Rate (SampleRate*NumChannels*(BitsPerSample/8)
130 0,0, // 32 - BlockAlign (== NumChannels * BitsPerSample/8)
131 16,0, // 34 - BitsPerSample
132 'd','a','t','a', // 36 - Subchunk2ID
133 0,0,0,0 // 40 - Subchunk2Size
136 static inline void int2le32(unsigned char* buf
, int32_t x
)
139 buf
[1] = (x
& 0xff00) >> 8;
140 buf
[2] = (x
& 0xff0000) >> 16;
141 buf
[3] = (x
& 0xff000000) >>24;
144 static inline void int2le24(unsigned char* buf
, int32_t x
)
147 buf
[1] = (x
& 0xff00) >> 8;
148 buf
[2] = (x
& 0xff0000) >> 16;
151 static inline void int2le16(unsigned char* buf
, int16_t x
)
154 buf
[1] = (x
& 0xff00) >> 8;
157 /* 32KB should be enough */
158 static unsigned char wavbuffer
[32*1024];
159 static unsigned char dspbuffer
[32*1024];
161 void init_wav(char* filename
)
163 wavinfo
.totalsamples
= 0;
165 wavinfo
.fd
= rb
->creat(filename
);
169 /* Write WAV header - we go back and fill in the details at the end */
170 rb
->write(wavinfo
.fd
, wav_header
, sizeof(wav_header
));
175 void close_wav(void) {
176 int filesize
= rb
->filesize(wavinfo
.fd
);
177 int channels
= (wavinfo
.stereomode
== STEREO_MONO
) ? 1 : 2;
178 int bps
= 16; /* TODO */
180 /* We assume 16-bit, Stereo */
182 rb
->lseek(wavinfo
.fd
,0,SEEK_SET
);
184 int2le32(wav_header
+4, filesize
-8); /* ChunkSize */
186 int2le16(wav_header
+22, channels
);
188 int2le32(wav_header
+24, wavinfo
.samplerate
);
190 int2le32(wav_header
+28, wavinfo
.samplerate
* channels
* (bps
/ 8)); /* ByteRate */
192 int2le16(wav_header
+32, channels
* (bps
/ 8));
194 int2le32(wav_header
+40, filesize
- 44); /* Subchunk2Size */
196 rb
->write(wavinfo
.fd
, wav_header
, sizeof(wav_header
));
198 rb
->close(wavinfo
.fd
);
201 /* Returns buffer to malloc array. Only codeclib should need this. */
202 static void* codec_get_buffer(size_t *size
)
204 DEBUGF("codec_get_buffer(%d)\n",(int)size
);
206 return codec_mallocbuf
;
209 static int process_dsp(const void *ch1
, const void *ch2
, int count
)
211 const char *src
[2] = { ch1
, ch2
};
212 int written_count
= 0;
213 char *dest
= dspbuffer
;
217 int out_count
= rb
->dsp_output_count(ci
.dsp
, count
);
219 int inp_count
= rb
->dsp_input_count(ci
.dsp
, out_count
);
224 if (inp_count
> count
)
227 out_count
= rb
->dsp_process(ci
.dsp
, dest
, src
, inp_count
);
232 written_count
+= out_count
;
233 dest
+= out_count
* 4;
238 return written_count
;
242 static bool pcmbuf_insert_null(const void *ch1
, const void *ch2
, int count
)
244 if (use_dsp
) process_dsp(ch1
, ch2
, count
);
246 /* Prevent idle poweroff */
247 rb
->reset_poweroff_timer();
252 static inline int32_t clip_sample(int32_t sample
)
254 if ((int16_t)sample
!= sample
)
255 sample
= 0x7fff ^ (sample
>> 31);
262 static bool pcmbuf_insert_wav(const void *ch1
, const void *ch2
, int count
)
264 const int16_t* data1_16
;
265 const int16_t* data2_16
;
266 const int32_t* data1_32
;
267 const int32_t* data2_32
;
268 unsigned char* p
= wavbuffer
;
269 const int scale
= wavinfo
.sampledepth
- 15;
270 const int dc_bias
= 1 << (scale
- 1);
271 int channels
= (wavinfo
.stereomode
== STEREO_MONO
) ? 1 : 2;
273 /* Prevent idle poweroff */
274 rb
->reset_poweroff_timer();
277 count
= process_dsp(ch1
, ch2
, count
);
278 wavinfo
.totalsamples
+= count
;
281 unsigned char *s
= dspbuffer
, *d
= dspbuffer
;
291 rb
->write(wavinfo
.fd
, dspbuffer
, count
* 2 * channels
);
294 if (wavinfo
.sampledepth
<= 16) {
298 switch(wavinfo
.stereomode
)
300 case STEREO_INTERLEAVED
:
302 int2le16(p
,*data1_16
++);
304 int2le16(p
,*data1_16
++);
309 case STEREO_NONINTERLEAVED
:
311 int2le16(p
,*data1_16
++);
313 int2le16(p
,*data2_16
++);
321 int2le16(p
,*data1_16
++);
330 switch(wavinfo
.stereomode
)
332 case STEREO_INTERLEAVED
:
334 int2le16(p
, clip_sample((*data1_32
++ + dc_bias
) >> scale
));
336 int2le16(p
, clip_sample((*data1_32
++ + dc_bias
) >> scale
));
341 case STEREO_NONINTERLEAVED
:
343 int2le16(p
, clip_sample((*data1_32
++ + dc_bias
) >> scale
));
345 int2le16(p
, clip_sample((*data2_32
++ + dc_bias
) >> scale
));
353 int2le16(p
, clip_sample((*data1_32
++ + dc_bias
) >> scale
));
360 wavinfo
.totalsamples
+= count
;
361 rb
->write(wavinfo
.fd
, wavbuffer
, p
- wavbuffer
);
367 /* Set song position in WPS (value in ms). */
368 static void set_elapsed(unsigned long value
)
374 /* Read next <size> amount bytes from file buffer to <ptr>.
375 Will return number of bytes read or 0 if end of file. */
376 static size_t read_filebuf(void *ptr
, size_t size
)
378 if (ci
.curpos
> (off_t
)track
.filesize
)
382 /* TODO: Don't read beyond end of buffer */
383 rb
->memcpy(ptr
, audiobuf
+ ci
.curpos
, size
);
390 /* Request pointer to file buffer which can be used to read
391 <realsize> amount of data. <reqsize> tells the buffer system
392 how much data it should try to allocate. If <realsize> is 0,
393 end of file is reached. */
394 static void* request_buffer(size_t *realsize
, size_t reqsize
)
396 *realsize
= MIN(track
.filesize
-ci
.curpos
,reqsize
);
398 return (audiobuf
+ ci
.curpos
);
402 /* Advance file buffer position by <amount> amount of bytes. */
403 static void advance_buffer(size_t amount
)
409 /* Advance file buffer to a pointer location inside file buffer. */
410 static void advance_buffer_loc(void *ptr
)
412 ci
.curpos
= ptr
- audiobuf
;
416 /* Seek file buffer to position <newpos> beginning of file. */
417 static bool seek_buffer(size_t newpos
)
424 /* Codec should call this function when it has done the seeking. */
425 static void seek_complete(void)
430 /* Request file change from file buffer. Returns true is next
431 track is available and changed. If return value is false,
432 codec should exit immediately with PLUGIN_OK status. */
433 static bool request_next_track(void)
435 /* We are only decoding a single track */
440 /* Free the buffer area of the current codec after its loaded */
441 static void discard_codec(void)
447 static void set_offset(size_t value
)
454 /* Configure different codec buffer parameters. */
455 static void configure(int setting
, intptr_t value
)
458 rb
->dsp_configure(ci
.dsp
, setting
, value
);
461 case DSP_SWITCH_FREQUENCY
:
462 case DSP_SET_FREQUENCY
:
463 DEBUGF("samplerate=%d\n",(int)value
);
464 wavinfo
.samplerate
= (int)value
;
467 case DSP_SET_SAMPLE_DEPTH
:
468 DEBUGF("sampledepth = %d\n",(int)value
);
469 wavinfo
.sampledepth
=(int)value
;
472 case DSP_SET_STEREO_MODE
:
473 DEBUGF("Stereo mode = %d\n",(int)value
);
474 wavinfo
.stereomode
=(int)value
;
480 static void init_ci(void)
482 /* --- Our "fake" implementations of the codec API functions. --- */
484 ci
.codec_get_buffer
= codec_get_buffer
;
486 if (wavinfo
.fd
>= 0) {
487 ci
.pcmbuf_insert
= pcmbuf_insert_wav
;
489 ci
.pcmbuf_insert
= pcmbuf_insert_null
;
491 ci
.set_elapsed
= set_elapsed
;
492 ci
.read_filebuf
= read_filebuf
;
493 ci
.request_buffer
= request_buffer
;
494 ci
.advance_buffer
= advance_buffer
;
495 ci
.advance_buffer_loc
= advance_buffer_loc
;
496 ci
.seek_buffer
= seek_buffer
;
497 ci
.seek_complete
= seek_complete
;
498 ci
.request_next_track
= request_next_track
;
499 ci
.discard_codec
= discard_codec
;
500 ci
.set_offset
= set_offset
;
501 ci
.configure
= configure
;
502 ci
.dsp
= (struct dsp_config
*)rb
->dsp_configure(NULL
, DSP_MYDSP
,
505 /* --- "Core" functions --- */
508 ci
.sleep
= rb
->sleep
;
509 ci
.yield
= rb
->yield
;
511 /* strings and memory */
512 ci
.strcpy
= rb
->strcpy
;
513 ci
.strlen
= rb
->strlen
;
514 ci
.strcmp
= rb
->strcmp
;
515 ci
.strcat
= rb
->strcat
;
516 ci
.memset
= rb
->memset
;
517 ci
.memcpy
= rb
->memcpy
;
518 ci
.memmove
= rb
->memmove
;
519 ci
.memcmp
= rb
->memcmp
;
520 ci
.memchr
= rb
->memchr
;
521 ci
.strcasestr
= rb
->strcasestr
;
522 #if defined(DEBUG) || defined(SIMULATOR)
523 ci
.debugf
= rb
->debugf
;
525 #ifdef ROCKBOX_HAS_LOGF
529 ci
.qsort
= rb
->qsort
;
530 ci
.global_settings
= rb
->global_settings
;
533 ci
.profile_thread
= rb
->profile_thread
;
534 ci
.profstop
= rb
->profstop
;
535 ci
.profile_func_enter
= rb
->profile_func_enter
;
536 ci
.profile_func_exit
= rb
->profile_func_exit
;
540 ci
.cpucache_invalidate
= rb
->cpucache_invalidate
;
541 ci
.cpucache_flush
= rb
->cpucache_flush
;
545 ci
.create_thread
= rb
->create_thread
;
546 ci
.thread_thaw
= rb
->thread_thaw
;
547 ci
.thread_wait
= rb
->thread_wait
;
548 ci
.semaphore_init
= rb
->semaphore_init
;
549 ci
.semaphore_wait
= rb
->semaphore_wait
;
550 ci
.semaphore_release
= rb
->semaphore_release
;
554 ci
.__div0
= rb
->__div0
;
558 static void codec_thread(void)
560 const char* codecname
;
563 codecname
= rb
->get_codec_filename(track
.id3
.codectype
);
565 /* Load the codec and start decoding. */
566 res
= rb
->codec_load_file(codecname
,&ci
);
568 /* Signal to the main thread that we are done */
569 endtick
= *rb
->current_tick
;
570 codec_playing
= false;
573 static enum plugin_status
test_track(const char* filename
)
577 enum plugin_status res
= PLUGIN_ERROR
;
581 unsigned long duration
;
584 /* Display filename (excluding any path)*/
585 ch
= rb
->strrchr(filename
, '/');
591 rb
->snprintf(str
,sizeof(str
),"%s",ch
);
594 log_text("Loading...",false);
596 fd
= rb
->open(filename
,O_RDONLY
);
599 log_text("Cannot open file",true);
603 track
.filesize
= rb
->filesize(fd
);
605 /* Clear the id3 struct */
606 rb
->memset(&track
.id3
, 0, sizeof(struct mp3entry
));
608 if (!rb
->get_metadata(&(track
.id3
), fd
, filename
))
610 log_text("Cannot read metadata",true);
614 if (track
.filesize
> audiosize
)
616 log_text("File too large",true);
620 n
= rb
->read(fd
, audiobuf
, track
.filesize
);
622 if (n
!= track
.filesize
)
624 log_text("Read failed.",true);
628 /* Initialise the function pointers in the codec API */
631 /* Prepare the codec struct for playing the whole file */
632 ci
.filesize
= track
.filesize
;
634 ci
.taginfo_ready
= &taginfo_ready
;
636 ci
.stop_codec
= false;
641 rb
->dsp_configure(ci
.dsp
, DSP_RESET
, 0);
643 starttick
= *rb
->current_tick
;
645 codec_playing
= true;
647 rb
->codec_thread_do_callback(codec_thread
, NULL
);
649 /* Wait for codec thread to die */
650 while (codec_playing
)
653 rb
->snprintf(str
,sizeof(str
),"%d of %d",elapsed
,(int)track
.id3
.length
);
656 ticks
= endtick
- starttick
;
658 /* Be sure it is done */
659 rb
->codec_thread_do_callback(NULL
, NULL
);
665 /* Display benchmark information */
666 rb
->snprintf(str
,sizeof(str
),"Decode time - %d.%02ds",(int)ticks
/100,(int)ticks
%100);
669 duration
= track
.id3
.length
/ 10;
670 rb
->snprintf(str
,sizeof(str
),"File duration - %d.%02ds",(int)duration
/100,(int)duration
%100);
674 speed
= duration
* 10000 / ticks
;
678 rb
->snprintf(str
,sizeof(str
),"%d.%02d%% realtime",(int)speed
/100,(int)speed
%100);
682 /* show effective clockrate in MHz needed for realtime decoding */
685 speed
= CPUFREQ_MAX
/ speed
;
686 rb
->snprintf(str
,sizeof(str
),"%d.%02dMHz needed for realtime",
687 (int)speed
/100,(int)speed
%100);
706 /* plugin entry point */
707 enum plugin_status
plugin_start(const void* parameter
)
709 int result
, selection
= 0;
710 enum plugin_status res
= PLUGIN_OK
;
712 struct dirent
*entry
;
715 char dirpath
[MAX_PATH
];
716 char filename
[MAX_PATH
];
718 if (parameter
== NULL
)
720 rb
->splash(HZ
*2, "No File");
724 codec_mallocbuf
= rb
->plugin_get_audio_buffer(&audiosize
);
725 audiobuf
= SKIPBYTES(codec_mallocbuf
, CODEC_SIZE
);
726 audiosize
-= CODEC_SIZE
;
728 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
731 rb
->lcd_clear_display();
735 menu
, "test_codec", NULL
,
739 "Speed test with DSP",
740 "Speed test folder with DSP",
741 "Write WAV with DSP",
746 rb
->lcd_clear_display();
748 result
=rb
->do_menu(&menu
,&selection
, NULL
, false);
758 if ((use_dsp
= ((result
>= 3) && (result
<=5)))) {
764 } else if (result
==1) {
768 /* Only create a log file when we are testing a folder */
769 if (!log_init(true)) {
770 rb
->splash(HZ
*2, "Cannot create logfile");
774 } else if (result
==2) {
776 init_wav("/test.wav");
777 if (wavinfo
.fd
< 0) {
778 rb
->splash(HZ
*2, "Cannot create /test.wav");
782 } else if (result
== MENU_ATTACHED_USB
) {
783 res
= PLUGIN_USB_CONNECTED
;
785 } else if (result
< 0) {
791 /* Test all files in the same directory as the file selected by the
794 rb
->strlcpy(dirpath
,parameter
,sizeof(dirpath
));
795 ch
= rb
->strrchr(dirpath
,'/');
798 DEBUGF("Scanning directory \"%s\"\n",dirpath
);
799 dir
= rb
->opendir(dirpath
);
801 entry
= rb
->readdir(dir
);
803 if (!(entry
->attribute
& ATTR_DIRECTORY
)) {
804 rb
->snprintf(filename
,sizeof(filename
),"%s%s",dirpath
,entry
->d_name
);
805 test_track(filename
);
809 /* Read next entry */
810 entry
= rb
->readdir(dir
);
816 /* Just test the file */
817 res
= test_track(parameter
);
819 /* Close WAV file (if there was one) */
820 if (wavinfo
.fd
>= 0) {
822 log_text("Wrote /test.wav",true);
825 while (rb
->button_get(true) != TESTCODEC_EXITBUTTON
);
832 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
833 rb
->cpu_boost(false);