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 ****************************************************************************/
22 #include "lib/pluginlib_touchscreen.h"
23 #include "lib/pluginlib_exit.h"
24 #include "lib/pluginlib_actions.h"
26 /* this set the context to use with PLA */
27 static const struct button_mapping
*plugin_contexts
[] = { pla_main_ctx
};
29 #define TESTCODEC_EXITBUTTON PLA_EXIT
30 #define TESTCODEC_EXITBUTTON2 PLA_CANCEL
33 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
34 static unsigned int boost
=1;
36 static const struct opt_items boost_settings
[2] = {
42 /* Log functions copied from test_disk.c */
44 static int max_line
= 0;
45 static int log_fd
= -1;
47 static void log_close(void)
53 static bool log_init(bool use_logfile
)
56 char logfilename
[MAX_PATH
];
58 rb
->lcd_getstringsize("A", NULL
, &h
);
59 max_line
= LCD_HEIGHT
/ h
;
61 rb
->lcd_clear_display();
66 rb
->create_numbered_filename(logfilename
, HOME_DIR
, "test_codec_log_", ".txt",
67 2 IF_CNFN_NUM_(, NULL
));
68 log_fd
= rb
->open(logfilename
, O_RDWR
|O_CREAT
|O_TRUNC
, 0666);
75 static void log_text(char *text
, bool advance
)
77 rb
->lcd_puts(0, line
, text
);
81 if (++line
>= max_line
)
84 rb
->fdprintf(log_fd
, "%s\n", text
);
98 static void* audiobuf
;
99 static void* codec_mallocbuf
;
100 static size_t audiosize
;
101 static size_t audiobufsize
;
105 /* Our local implementation of the codec API */
106 static struct codec_api ci
;
108 struct test_track_info
{
109 struct mp3entry id3
; /* TAG metadata */
110 size_t filesize
; /* File total length */
113 static struct test_track_info track
;
117 static bool checksum
;
118 static uint32_t crc32
;
120 static volatile unsigned int elapsed
;
121 static volatile bool codec_playing
;
122 static volatile enum codec_command_action codec_action
;
123 static volatile long endtick
;
124 static volatile long rebuffertick
;
125 struct wavinfo_t wavinfo
;
127 static unsigned char wav_header
[44] =
129 'R','I','F','F', // 0 - ChunkID
130 0,0,0,0, // 4 - ChunkSize (filesize-8)
131 'W','A','V','E', // 8 - Format
132 'f','m','t',' ', // 12 - SubChunkID
133 16,0,0,0, // 16 - SubChunk1ID // 16 for PCM
134 1,0, // 20 - AudioFormat (1=16-bit)
135 0,0, // 22 - NumChannels
136 0,0,0,0, // 24 - SampleRate in Hz
137 0,0,0,0, // 28 - Byte Rate (SampleRate*NumChannels*(BitsPerSample/8)
138 0,0, // 32 - BlockAlign (== NumChannels * BitsPerSample/8)
139 16,0, // 34 - BitsPerSample
140 'd','a','t','a', // 36 - Subchunk2ID
141 0,0,0,0 // 40 - Subchunk2Size
144 static inline void int2le32(unsigned char* buf
, int32_t x
)
147 buf
[1] = (x
& 0xff00) >> 8;
148 buf
[2] = (x
& 0xff0000) >> 16;
149 buf
[3] = (x
& 0xff000000) >>24;
152 static inline void int2le24(unsigned char* buf
, int32_t x
)
155 buf
[1] = (x
& 0xff00) >> 8;
156 buf
[2] = (x
& 0xff0000) >> 16;
159 static inline void int2le16(unsigned char* buf
, int16_t x
)
162 buf
[1] = (x
& 0xff00) >> 8;
165 static unsigned char *wavbuffer
;
166 static unsigned char *dspbuffer
;
167 static int dspbuffer_count
;
169 void init_wav(char* filename
)
171 wavinfo
.totalsamples
= 0;
173 wavinfo
.fd
= rb
->creat(filename
, 0666);
177 /* Write WAV header - we go back and fill in the details at the end */
178 rb
->write(wavinfo
.fd
, wav_header
, sizeof(wav_header
));
185 int filesize
= rb
->filesize(wavinfo
.fd
);
186 int channels
= (wavinfo
.stereomode
== STEREO_MONO
) ? 1 : 2;
187 int bps
= 16; /* TODO */
189 /* We assume 16-bit, Stereo */
191 rb
->lseek(wavinfo
.fd
,0,SEEK_SET
);
193 int2le32(wav_header
+4, filesize
-8); /* ChunkSize */
195 int2le16(wav_header
+22, channels
);
197 int2le32(wav_header
+24, wavinfo
.samplerate
);
199 int2le32(wav_header
+28, wavinfo
.samplerate
* channels
* (bps
/ 8)); /* ByteRate */
201 int2le16(wav_header
+32, channels
* (bps
/ 8));
203 int2le32(wav_header
+40, filesize
- 44); /* Subchunk2Size */
205 rb
->write(wavinfo
.fd
, wav_header
, sizeof(wav_header
));
207 rb
->close(wavinfo
.fd
);
210 /* Returns buffer to malloc array. Only codeclib should need this. */
211 static void* codec_get_buffer(size_t *size
)
214 return codec_mallocbuf
;
217 static int process_dsp(const void *ch1
, const void *ch2
, int count
)
219 struct dsp_buffer src
;
220 src
.remcount
= count
;
225 struct dsp_buffer dst
;
227 dst
.p16out
= (int16_t *)dspbuffer
;
228 dst
.bufcount
= dspbuffer_count
;
232 int old_remcount
= dst
.remcount
;
233 rb
->dsp_process(ci
.dsp
, &src
, &dst
);
235 if (dst
.bufcount
<= 0 ||
236 (src
.remcount
<= 0 && dst
.remcount
<= old_remcount
))
238 /* Dest is full or no input left and DSP purged */
247 static void pcmbuf_insert_null(const void *ch1
, const void *ch2
, int count
)
250 process_dsp(ch1
, ch2
, count
);
252 /* Prevent idle poweroff */
253 rb
->reset_poweroff_timer();
257 * Helper function used when the file is larger then the available memory.
258 * Rebuffers the file by setting the start of the audio buffer to be
259 * new_offset and filling from there.
261 static int fill_buffer(int new_offset
){
262 size_t n
, bytestoread
;
263 long temp
= *rb
->current_tick
;
264 rb
->lseek(fd
,new_offset
,SEEK_SET
);
266 if(new_offset
+ audiobufsize
<= track
.filesize
)
267 bytestoread
= audiobufsize
;
269 bytestoread
= track
.filesize
-new_offset
;
271 n
= rb
->read(fd
, audiobuf
,bytestoread
);
273 if (n
!= bytestoread
)
275 log_text("Read failed.",true);
276 DEBUGF("read fail: got %d bytes, expected %d\n", (int)n
, (int)audiobufsize
);
288 /*keep track of how much time we spent buffering*/
289 rebuffertick
+= *rb
->current_tick
-temp
;
294 /* WAV output or calculate crc32 of output*/
295 static void pcmbuf_insert_wav_checksum(const void *ch1
, const void *ch2
, int count
)
297 /* Prevent idle poweroff */
298 rb
->reset_poweroff_timer();
301 count
= process_dsp(ch1
, ch2
, count
);
302 wavinfo
.totalsamples
+= count
;
304 #ifdef ROCKBOX_BIG_ENDIAN
305 unsigned char* p
= dspbuffer
;
307 for (i
= 0; i
< count
; i
++) {
308 int2le16(p
,*(int16_t *)p
);
310 int2le16(p
,*(int16_t *)p
);
315 crc32
= rb
->crc_32(dspbuffer
, count
* 2 * sizeof (int16_t), crc32
);
317 rb
->write(wavinfo
.fd
, dspbuffer
, count
* 2 * sizeof (int16_t));
322 const int16_t* data1_16
;
323 const int16_t* data2_16
;
324 const int32_t* data1_32
;
325 const int32_t* data2_32
;
326 unsigned char* p
= wavbuffer
;
327 const int scale
= wavinfo
.sampledepth
- 15;
328 const int dc_bias
= 1 << (scale
- 1);
330 if (wavinfo
.sampledepth
<= 16) {
334 switch(wavinfo
.stereomode
)
336 case STEREO_INTERLEAVED
:
338 int2le16(p
,*data1_16
++);
340 int2le16(p
,*data1_16
++);
345 case STEREO_NONINTERLEAVED
:
347 int2le16(p
,*data1_16
++);
349 int2le16(p
,*data2_16
++);
357 int2le16(p
,*data1_16
++);
366 switch(wavinfo
.stereomode
)
368 case STEREO_INTERLEAVED
:
370 int2le16(p
, clip_sample_16((*data1_32
++ + dc_bias
) >> scale
));
372 int2le16(p
, clip_sample_16((*data1_32
++ + dc_bias
) >> scale
));
377 case STEREO_NONINTERLEAVED
:
379 int2le16(p
, clip_sample_16((*data1_32
++ + dc_bias
) >> scale
));
381 int2le16(p
, clip_sample_16((*data2_32
++ + dc_bias
) >> scale
));
389 int2le16(p
, clip_sample_16((*data1_32
++ + dc_bias
) >> scale
));
396 wavinfo
.totalsamples
+= count
;
398 crc32
= rb
->crc_32(wavbuffer
, p
- wavbuffer
, crc32
);
400 rb
->write(wavinfo
.fd
, wavbuffer
, p
- wavbuffer
);
404 /* Set song position in WPS (value in ms). */
405 static void set_elapsed(unsigned long value
)
408 ci
.id3
->elapsed
= value
;
412 /* Read next <size> amount bytes from file buffer to <ptr>.
413 Will return number of bytes read or 0 if end of file. */
414 static size_t read_filebuf(void *ptr
, size_t size
)
416 if (ci
.curpos
> (off_t
)track
.filesize
)
420 size_t realsize
= MIN(track
.filesize
-ci
.curpos
,size
);
422 /* check if we have enough bytes ready*/
423 if(realsize
>(audiobufsize
- (ci
.curpos
-offset
)))
425 /*rebuffer so that we start at ci.curpos*/
426 fill_buffer(ci
.curpos
);
429 rb
->memcpy(ptr
, audiobuf
+ (ci
.curpos
-offset
), realsize
);
430 ci
.curpos
+= realsize
;
436 /* Request pointer to file buffer which can be used to read
437 <realsize> amount of data. <reqsize> tells the buffer system
438 how much data it should try to allocate. If <realsize> is 0,
439 end of file is reached. */
440 static void* request_buffer(size_t *realsize
, size_t reqsize
)
442 *realsize
= MIN(track
.filesize
-ci
.curpos
,reqsize
);
444 /*check if we have enough bytes ready - requested > bufsize-currentbufpos*/
445 if(*realsize
>(audiobufsize
- (ci
.curpos
-offset
)))
447 /*rebuffer so that we start at ci.curpos*/
448 fill_buffer(ci
.curpos
);
451 return (audiobuf
+ (ci
.curpos
-offset
));
454 /* Advance file buffer position by <amount> amount of bytes. */
455 static void advance_buffer(size_t amount
)
458 ci
.id3
->offset
= ci
.curpos
;
462 /* Seek file buffer to position <newpos> beginning of file. */
463 static bool seek_buffer(size_t newpos
)
470 /* Codec should call this function when it has done the seeking. */
471 static void seek_complete(void)
476 /* Codec calls this to know what it should do next. */
477 static enum codec_command_action
get_command(intptr_t *param
)
484 /* Some codecs call this to determine whether they should loop. */
485 static bool loop_track(void)
490 static void set_offset(size_t value
)
492 ci
.id3
->offset
= value
;
496 /* Configure different codec buffer parameters. */
497 static void configure(int setting
, intptr_t value
)
500 rb
->dsp_configure(ci
.dsp
, setting
, value
);
503 case DSP_SET_FREQUENCY
:
504 DEBUGF("samplerate=%d\n",(int)value
);
505 wavinfo
.samplerate
= use_dsp
? NATIVE_FREQUENCY
: (int)value
;
508 case DSP_SET_SAMPLE_DEPTH
:
509 DEBUGF("sampledepth = %d\n",(int)value
);
510 wavinfo
.sampledepth
= use_dsp
? 16 : (int)value
;
513 case DSP_SET_STEREO_MODE
:
514 DEBUGF("Stereo mode = %d\n",(int)value
);
515 wavinfo
.stereomode
= use_dsp
? STEREO_INTERLEAVED
: (int)value
;
521 static void init_ci(void)
523 /* --- Our "fake" implementations of the codec API functions. --- */
525 ci
.dsp
= rb
->dsp_get_config(CODEC_IDX_AUDIO
);
526 ci
.codec_get_buffer
= codec_get_buffer
;
528 if (wavinfo
.fd
>= 0 || checksum
) {
529 ci
.pcmbuf_insert
= pcmbuf_insert_wav_checksum
;
531 ci
.pcmbuf_insert
= pcmbuf_insert_null
;
534 ci
.set_elapsed
= set_elapsed
;
535 ci
.read_filebuf
= read_filebuf
;
536 ci
.request_buffer
= request_buffer
;
537 ci
.advance_buffer
= advance_buffer
;
538 ci
.seek_buffer
= seek_buffer
;
539 ci
.seek_complete
= seek_complete
;
540 ci
.set_offset
= set_offset
;
541 ci
.configure
= configure
;
542 ci
.get_command
= get_command
;
543 ci
.loop_track
= loop_track
;
545 /* --- "Core" functions --- */
548 ci
.sleep
= rb
->sleep
;
549 ci
.yield
= rb
->yield
;
551 /* strings and memory */
552 ci
.strcpy
= rb
->strcpy
;
553 ci
.strlen
= rb
->strlen
;
554 ci
.strcmp
= rb
->strcmp
;
555 ci
.strcat
= rb
->strcat
;
556 ci
.memset
= rb
->memset
;
557 ci
.memcpy
= rb
->memcpy
;
558 ci
.memmove
= rb
->memmove
;
559 ci
.memcmp
= rb
->memcmp
;
560 ci
.memchr
= rb
->memchr
;
561 #if defined(DEBUG) || defined(SIMULATOR)
562 ci
.debugf
= rb
->debugf
;
564 #ifdef ROCKBOX_HAS_LOGF
568 ci
.qsort
= rb
->qsort
;
571 ci
.profile_thread
= rb
->profile_thread
;
572 ci
.profstop
= rb
->profstop
;
573 ci
.profile_func_enter
= rb
->profile_func_enter
;
574 ci
.profile_func_exit
= rb
->profile_func_exit
;
577 ci
.commit_dcache
= rb
->commit_dcache
;
578 ci
.commit_discard_dcache
= rb
->commit_discard_dcache
;
579 ci
.commit_discard_idcache
= rb
->commit_discard_idcache
;
582 ci
.create_thread
= rb
->create_thread
;
583 ci
.thread_thaw
= rb
->thread_thaw
;
584 ci
.thread_wait
= rb
->thread_wait
;
585 ci
.semaphore_init
= rb
->semaphore_init
;
586 ci
.semaphore_wait
= rb
->semaphore_wait
;
587 ci
.semaphore_release
= rb
->semaphore_release
;
590 #if defined(CPU_ARM) && (CONFIG_PLATFORM & PLATFORM_NATIVE)
591 ci
.__div0
= rb
->__div0
;
595 static void codec_thread(void)
597 const char* codecname
;
600 codecname
= rb
->get_codec_filename(track
.id3
.codectype
);
603 res
= rb
->codec_load_file(codecname
, &ci
);
607 /* Decode the file */
608 res
= rb
->codec_run_proc();
614 /* Signal to the main thread that we are done */
615 endtick
= *rb
->current_tick
- rebuffertick
;
616 codec_playing
= false;
619 static enum plugin_status
test_track(const char* filename
)
622 enum plugin_status res
= PLUGIN_ERROR
;
626 unsigned long duration
;
631 /* Display filename (excluding any path)*/
632 ch
= rb
->strrchr(filename
, '/');
638 rb
->snprintf(str
,sizeof(str
),"%s",ch
);
641 log_text("Loading...",false);
643 fd
= rb
->open(filename
,O_RDONLY
);
646 log_text("Cannot open file",true);
650 track
.filesize
= rb
->filesize(fd
);
652 /* Clear the id3 struct */
653 rb
->memset(&track
.id3
, 0, sizeof(struct mp3entry
));
655 if (!rb
->get_metadata(&(track
.id3
), fd
, filename
))
657 log_text("Cannot read metadata",true);
661 if (track
.filesize
> audiosize
)
663 audiobufsize
=audiosize
;
667 audiobufsize
=track
.filesize
;
670 n
= rb
->read(fd
, audiobuf
, audiobufsize
);
672 if (n
!= audiobufsize
)
674 log_text("Read failed.",true);
679 /* Initialise the function pointers in the codec API */
682 /* Prepare the codec struct for playing the whole file */
683 ci
.filesize
= track
.filesize
;
688 rb
->dsp_configure(ci
.dsp
, DSP_RESET
, 0);
689 rb
->dsp_configure(ci
.dsp
, DSP_FLUSH
, 0);
696 starttick
= *rb
->current_tick
;
698 codec_playing
= true;
699 codec_action
= CODEC_ACTION_NULL
;
701 rb
->codec_thread_do_callback(codec_thread
, NULL
);
703 /* Wait for codec thread to die */
704 while (codec_playing
)
706 int button
= pluginlib_getaction(HZ
, plugin_contexts
,
707 ARRAYLEN(plugin_contexts
));
708 if ((button
== TESTCODEC_EXITBUTTON
) || (button
== TESTCODEC_EXITBUTTON2
))
710 codec_action
= CODEC_ACTION_HALT
;
714 rb
->snprintf(str
,sizeof(str
),"%d of %d",elapsed
,(int)track
.id3
.length
);
717 ticks
= endtick
- starttick
;
719 /* Be sure it is done */
720 rb
->codec_thread_do_callback(NULL
, NULL
);
724 if (codec_action
== CODEC_ACTION_HALT
)
726 /* User aborted test */
730 rb
->snprintf(str
, sizeof(str
), "CRC32 - %08x", (unsigned)crc32
);
733 else if (wavinfo
.fd
< 0)
735 /* Display benchmark information */
736 rb
->snprintf(str
,sizeof(str
),"Decode time - %d.%02ds",(int)ticks
/100,(int)ticks
%100);
739 duration
= track
.id3
.length
/ 10;
740 rb
->snprintf(str
,sizeof(str
),"File duration - %d.%02ds",(int)duration
/100,(int)duration
%100);
744 speed
= duration
* 10000 / ticks
;
748 rb
->snprintf(str
,sizeof(str
),"%d.%02d%% realtime",(int)speed
/100,(int)speed
%100);
751 #if (CONFIG_PLATFORM & PLATFORM_NATIVE)
752 /* show effective clockrate in MHz needed for realtime decoding */
756 freq
= *rb
->cpu_frequency
;
758 speed
= freq
/ speed
;
759 rb
->snprintf(str
,sizeof(str
),"%d.%02dMHz needed for realtime",
760 (int)speed
/100,(int)speed
%100);
779 #ifdef HAVE_TOUCHSCREEN
782 rb
->screens
[0]->set_viewport(NULL
);
786 void plugin_quit(void)
789 #ifdef HAVE_TOUCHSCREEN
790 static struct touchbutton button
[] = {{
791 .action
= ACTION_STD_OK
,
793 /* viewport runtime initialized, rest false/NULL */
795 struct viewport
*vp
= &button
[0].vp
;
796 struct screen
*lcd
= rb
->screens
[SCREEN_MAIN
];
797 rb
->viewport_set_defaults(vp
, SCREEN_MAIN
);
798 const int border
= 10;
799 const int height
= 50;
801 lcd
->set_viewport(vp
);
802 /* button matches the bottom center in the grid */
803 vp
->x
= lcd
->lcdwidth
/3;
804 vp
->width
= lcd
->lcdwidth
/3;
806 vp
->y
= lcd
->lcdheight
- height
- border
;
808 touchbutton_draw(button
, ARRAYLEN(button
));
809 lcd
->update_viewport();
810 if (rb
->touchscreen_get_mode() == TOUCHSCREEN_POINT
)
812 while (codec_action
!= CODEC_ACTION_HALT
&&
813 touchbutton_get(button
, ARRAYLEN(button
)) != ACTION_STD_OK
);
818 btn
= pluginlib_getaction(TIMEOUT_BLOCK
, plugin_contexts
,
819 ARRAYLEN(plugin_contexts
));
821 } while ((codec_action
!= CODEC_ACTION_HALT
)
822 && (btn
!= TESTCODEC_EXITBUTTON
)
823 && (btn
!= TESTCODEC_EXITBUTTON2
));
826 /* plugin entry point */
827 enum plugin_status
plugin_start(const void* parameter
)
829 int result
, selection
= 0;
830 enum plugin_status res
= PLUGIN_OK
;
832 struct dirent
*entry
;
835 char dirpath
[MAX_PATH
];
836 char filename
[MAX_PATH
];
839 if (parameter
== NULL
)
841 rb
->splash(HZ
*2, "No File");
845 wavbuffer
= rb
->plugin_get_buffer(&buffer_size
);
846 dspbuffer
= wavbuffer
+ buffer_size
/ 2;
847 dspbuffer_count
= (buffer_size
- (dspbuffer
- wavbuffer
)) /
848 (2 * sizeof (int16_t));
850 codec_mallocbuf
= rb
->plugin_get_audio_buffer(&audiosize
);
851 /* Align codec_mallocbuf to pointer size, tlsf wants that */
852 codec_mallocbuf
= (void*)(((intptr_t)codec_mallocbuf
+
853 sizeof(intptr_t)-1) & ~(sizeof(intptr_t)-1));
854 audiobuf
= SKIPBYTES(codec_mallocbuf
, CODEC_SIZE
);
855 audiosize
-= CODEC_SIZE
;
857 rb
->lcd_clear_display();
860 #ifdef HAVE_TOUCHSCREEN
861 rb
->touchscreen_set_mode(rb
->global_settings
->touch_mode
);
870 SPEED_TEST_DIR_WITH_DSP
,
875 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
881 menu
, "test_codec", NULL
,
885 "Speed test with DSP",
886 "Speed test folder with DSP",
887 "Write WAV with DSP",
891 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
898 rb
->lcd_clear_display();
900 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
904 result
= rb
->do_menu(&menu
, &selection
, NULL
, false);
906 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
909 rb
->set_option("Boosting", &boost
, INT
,
910 boost_settings
, 2, NULL
);
925 /* Map test runs with checksum calcualtion to standard runs
926 * SPEED_TEST and SPEED_TEST_DIR and set the 'checksum' flag. */
927 if ((checksum
= (result
== CHECKSUM
||
928 result
== CHECKSUM_DIR
)))
931 /* Map test runs with DSP to standard runs SPEED_TEST,
932 * SPEED_TEST_DIR and WRITE_WAV and set the 'use_dsp' flag. */
933 if ((use_dsp
= (result
>= SPEED_TEST_WITH_DSP
&&
934 result
<= WRITE_WAV_WITH_DSP
)))
937 if (result
== SPEED_TEST
) {
940 } else if (result
== SPEED_TEST_DIR
) {
944 /* Only create a log file when we are testing a folder */
945 if (!log_init(true)) {
946 rb
->splash(HZ
*2, "Cannot create logfile");
950 } else if (result
== WRITE_WAV
) {
952 init_wav("/test.wav");
953 if (wavinfo
.fd
< 0) {
954 rb
->splash(HZ
*2, "Cannot create /test.wav");
958 } else if (result
== MENU_ATTACHED_USB
) {
959 res
= PLUGIN_USB_CONNECTED
;
961 } else if (result
< 0) {
967 /* Test all files in the same directory as the file selected by the
970 rb
->strlcpy(dirpath
,parameter
,sizeof(dirpath
));
971 ch
= rb
->strrchr(dirpath
,'/');
974 DEBUGF("Scanning directory \"%s\"\n",dirpath
);
975 dir
= rb
->opendir(dirpath
);
977 entry
= rb
->readdir(dir
);
979 struct dirinfo info
= rb
->dir_get_info(dir
, entry
);
980 if (!(info
.attribute
& ATTR_DIRECTORY
)) {
981 rb
->snprintf(filename
,sizeof(filename
),"%s%s",dirpath
,entry
->d_name
);
982 test_track(filename
);
984 if (codec_action
== CODEC_ACTION_HALT
)
990 /* Read next entry */
991 entry
= rb
->readdir(dir
);
997 /* Just test the file */
998 res
= test_track(parameter
);
1000 /* Close WAV file (if there was one) */
1001 if (wavinfo
.fd
>= 0) {
1003 log_text("Wrote /test.wav",true);
1008 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1010 rb
->cpu_boost(false);
1013 rb
->button_clear_queue();