lcd-m6sp.c: remove \r
[kugel-rb.git] / apps / plugins / test_codec.c
blobaf3fff7016f4495af4365a37b8f53dcd0f5be410
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
21 #include "plugin.h"
23 PLUGIN_HEADER
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 == COWON_D2_PAD || CONFIG_KEYPAD == ONDAVX747_PAD
34 #define TESTCODEC_EXITBUTTON BUTTON_POWER
35 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
36 #define TESTCODEC_EXITBUTTON BUTTON_REC
37 #elif defined(HAVE_TOUCHSCREEN)
38 #define TESTCODEC_EXITBUTTON BUTTON_TOPLEFT
39 #else
40 #define TESTCODEC_EXITBUTTON BUTTON_SELECT
41 #endif
43 /* Log functions copied from test_disk.c */
44 static int line = 0;
45 static int max_line = 0;
46 static int log_fd = -1;
48 static void log_close(void)
50 if (log_fd >= 0)
51 rb->close(log_fd);
54 static bool log_init(bool use_logfile)
56 int h;
57 char logfilename[MAX_PATH];
59 rb->lcd_getstringsize("A", NULL, &h);
60 max_line = LCD_HEIGHT / h;
61 line = 0;
62 rb->lcd_clear_display();
63 rb->lcd_update();
65 if (use_logfile) {
66 log_close();
67 rb->create_numbered_filename(logfilename, "/", "test_codec_log_", ".txt",
68 2 IF_CNFN_NUM_(, NULL));
69 log_fd = rb->open(logfilename, O_RDWR|O_CREAT|O_TRUNC, 0666);
70 return log_fd >= 0;
73 return true;
76 static void log_text(char *text, bool advance)
78 rb->lcd_puts(0, line, text);
79 rb->lcd_update();
80 if (advance)
82 if (++line >= max_line)
83 line = 0;
84 if (log_fd >= 0)
85 rb->fdprintf(log_fd, "%s\n", text);
89 struct wavinfo_t
91 int fd;
92 int samplerate;
93 int channels;
94 int sampledepth;
95 int stereomode;
96 int totalsamples;
99 static void* audiobuf;
100 static void* codec_mallocbuf;
101 static size_t audiosize;
103 /* Our local implementation of the codec API */
104 static struct codec_api ci;
106 struct test_track_info {
107 struct mp3entry id3; /* TAG metadata */
108 size_t filesize; /* File total length */
111 static struct test_track_info track;
112 static bool taginfo_ready = true;
114 static bool use_dsp;
116 static bool checksum;
117 static uint32_t crc32;
119 static volatile unsigned int elapsed;
120 static volatile bool codec_playing;
121 static volatile long endtick;
122 struct wavinfo_t wavinfo;
124 static unsigned char wav_header[44] =
126 'R','I','F','F', // 0 - ChunkID
127 0,0,0,0, // 4 - ChunkSize (filesize-8)
128 'W','A','V','E', // 8 - Format
129 'f','m','t',' ', // 12 - SubChunkID
130 16,0,0,0, // 16 - SubChunk1ID // 16 for PCM
131 1,0, // 20 - AudioFormat (1=16-bit)
132 0,0, // 22 - NumChannels
133 0,0,0,0, // 24 - SampleRate in Hz
134 0,0,0,0, // 28 - Byte Rate (SampleRate*NumChannels*(BitsPerSample/8)
135 0,0, // 32 - BlockAlign (== NumChannels * BitsPerSample/8)
136 16,0, // 34 - BitsPerSample
137 'd','a','t','a', // 36 - Subchunk2ID
138 0,0,0,0 // 40 - Subchunk2Size
141 static inline void int2le32(unsigned char* buf, int32_t x)
143 buf[0] = (x & 0xff);
144 buf[1] = (x & 0xff00) >> 8;
145 buf[2] = (x & 0xff0000) >> 16;
146 buf[3] = (x & 0xff000000) >>24;
149 static inline void int2le24(unsigned char* buf, int32_t x)
151 buf[0] = (x & 0xff);
152 buf[1] = (x & 0xff00) >> 8;
153 buf[2] = (x & 0xff0000) >> 16;
156 static inline void int2le16(unsigned char* buf, int16_t x)
158 buf[0] = (x & 0xff);
159 buf[1] = (x & 0xff00) >> 8;
162 static unsigned char *wavbuffer;
163 static unsigned char *dspbuffer;
165 void init_wav(char* filename)
167 wavinfo.totalsamples = 0;
169 wavinfo.fd = rb->creat(filename, 0666);
171 if (wavinfo.fd >= 0)
173 /* Write WAV header - we go back and fill in the details at the end */
174 rb->write(wavinfo.fd, wav_header, sizeof(wav_header));
179 void close_wav(void)
181 int filesize = rb->filesize(wavinfo.fd);
182 int channels = (wavinfo.stereomode == STEREO_MONO) ? 1 : 2;
183 int bps = 16; /* TODO */
185 /* We assume 16-bit, Stereo */
187 rb->lseek(wavinfo.fd,0,SEEK_SET);
189 int2le32(wav_header+4, filesize-8); /* ChunkSize */
191 int2le16(wav_header+22, channels);
193 int2le32(wav_header+24, wavinfo.samplerate);
195 int2le32(wav_header+28, wavinfo.samplerate * channels * (bps / 8)); /* ByteRate */
197 int2le16(wav_header+32, channels * (bps / 8));
199 int2le32(wav_header+40, filesize - 44); /* Subchunk2Size */
201 rb->write(wavinfo.fd, wav_header, sizeof(wav_header));
203 rb->close(wavinfo.fd);
206 /* Returns buffer to malloc array. Only codeclib should need this. */
207 static void* codec_get_buffer(size_t *size)
209 DEBUGF("codec_get_buffer(%"PRIuPTR")\n",(uintptr_t)size);
210 *size = CODEC_SIZE;
211 return codec_mallocbuf;
214 static int process_dsp(const void *ch1, const void *ch2, int count)
216 const char *src[2] = { ch1, ch2 };
217 int written_count = 0;
218 char *dest = dspbuffer;
220 while (count > 0)
222 int out_count = rb->dsp_output_count(ci.dsp, count);
224 int inp_count = rb->dsp_input_count(ci.dsp, out_count);
226 if (inp_count <= 0)
227 break;
229 if (inp_count > count)
230 inp_count = count;
232 out_count = rb->dsp_process(ci.dsp, dest, src, inp_count);
234 if (out_count <= 0)
235 break;
237 written_count += out_count;
238 dest += out_count * 4;
240 count -= inp_count;
243 return written_count;
246 static inline int32_t clip_sample(int32_t sample)
248 if ((int16_t)sample != sample)
249 sample = 0x7fff ^ (sample >> 31);
251 return sample;
254 /* Null output */
255 static void pcmbuf_insert_null(const void *ch1, const void *ch2, int count)
257 if (use_dsp)
258 process_dsp(ch1, ch2, count);
260 /* Prevent idle poweroff */
261 rb->reset_poweroff_timer();
264 /* WAV output or calculate crc32 of output*/
265 static void pcmbuf_insert_wav_checksum(const void *ch1, const void *ch2, int count)
267 const int16_t* data1_16;
268 const int16_t* data2_16;
269 const int32_t* data1_32;
270 const int32_t* data2_32;
271 unsigned char* p = wavbuffer;
272 const int scale = wavinfo.sampledepth - 15;
273 const int dc_bias = 1 << (scale - 1);
274 int channels = (wavinfo.stereomode == STEREO_MONO) ? 1 : 2;
276 /* Prevent idle poweroff */
277 rb->reset_poweroff_timer();
279 if (use_dsp) {
280 count = process_dsp(ch1, ch2, count);
281 wavinfo.totalsamples += count;
282 if (channels == 1)
284 unsigned char *s = dspbuffer, *d = dspbuffer;
285 int c = count;
286 while (c-- > 0)
288 *d++ = *s++;
289 *d++ = *s++;
290 s++;
291 s++;
294 if (checksum)
295 crc32 = rb->crc_32(dspbuffer, count * 2 * channels, crc32);
296 else
297 rb->write(wavinfo.fd, dspbuffer, count * 2 * channels);
299 else
301 if (wavinfo.sampledepth <= 16) {
302 data1_16 = ch1;
303 data2_16 = ch2;
305 switch(wavinfo.stereomode)
307 case STEREO_INTERLEAVED:
308 while (count--) {
309 int2le16(p,*data1_16++);
310 p += 2;
311 int2le16(p,*data1_16++);
312 p += 2;
314 break;
316 case STEREO_NONINTERLEAVED:
317 while (count--) {
318 int2le16(p,*data1_16++);
319 p += 2;
320 int2le16(p,*data2_16++);
321 p += 2;
324 break;
326 case STEREO_MONO:
327 while (count--) {
328 int2le16(p,*data1_16++);
329 p += 2;
331 break;
333 } else {
334 data1_32 = ch1;
335 data2_32 = ch2;
337 switch(wavinfo.stereomode)
339 case STEREO_INTERLEAVED:
340 while (count--) {
341 int2le16(p, clip_sample((*data1_32++ + dc_bias) >> scale));
342 p += 2;
343 int2le16(p, clip_sample((*data1_32++ + dc_bias) >> scale));
344 p += 2;
346 break;
348 case STEREO_NONINTERLEAVED:
349 while (count--) {
350 int2le16(p, clip_sample((*data1_32++ + dc_bias) >> scale));
351 p += 2;
352 int2le16(p, clip_sample((*data2_32++ + dc_bias) >> scale));
353 p += 2;
356 break;
358 case STEREO_MONO:
359 while (count--) {
360 int2le16(p, clip_sample((*data1_32++ + dc_bias) >> scale));
361 p += 2;
363 break;
367 wavinfo.totalsamples += count;
368 if (checksum)
369 crc32 = rb->crc_32(wavbuffer, p - wavbuffer, crc32);
370 else
371 rb->write(wavinfo.fd, wavbuffer, p - wavbuffer);
372 } /* else */
375 /* Set song position in WPS (value in ms). */
376 static void set_elapsed(unsigned long value)
378 elapsed = value;
382 /* Read next <size> amount bytes from file buffer to <ptr>.
383 Will return number of bytes read or 0 if end of file. */
384 static size_t read_filebuf(void *ptr, size_t size)
386 if (ci.curpos > (off_t)track.filesize)
388 return 0;
389 } else {
390 /* TODO: Don't read beyond end of buffer */
391 rb->memcpy(ptr, audiobuf + ci.curpos, size);
392 ci.curpos += size;
393 return size;
398 /* Request pointer to file buffer which can be used to read
399 <realsize> amount of data. <reqsize> tells the buffer system
400 how much data it should try to allocate. If <realsize> is 0,
401 end of file is reached. */
402 static void* request_buffer(size_t *realsize, size_t reqsize)
404 *realsize = MIN(track.filesize-ci.curpos,reqsize);
406 return (audiobuf + ci.curpos);
410 /* Advance file buffer position by <amount> amount of bytes. */
411 static void advance_buffer(size_t amount)
413 ci.curpos += amount;
417 /* Advance file buffer to a pointer location inside file buffer. */
418 static void advance_buffer_loc(void *ptr)
420 ci.curpos = ptr - audiobuf;
424 /* Seek file buffer to position <newpos> beginning of file. */
425 static bool seek_buffer(size_t newpos)
427 ci.curpos = newpos;
428 return true;
432 /* Codec should call this function when it has done the seeking. */
433 static void seek_complete(void)
435 /* Do nothing */
438 /* Request file change from file buffer. Returns true is next
439 track is available and changed. If return value is false,
440 codec should exit immediately with PLUGIN_OK status. */
441 static bool request_next_track(void)
443 /* We are only decoding a single track */
444 return false;
448 /* Free the buffer area of the current codec after its loaded */
449 static void discard_codec(void)
451 /* ??? */
455 static void set_offset(size_t value)
457 /* ??? */
458 (void)value;
462 /* Configure different codec buffer parameters. */
463 static void configure(int setting, intptr_t value)
465 if (use_dsp)
466 rb->dsp_configure(ci.dsp, setting, value);
467 switch(setting)
469 case DSP_SWITCH_FREQUENCY:
470 case DSP_SET_FREQUENCY:
471 DEBUGF("samplerate=%d\n",(int)value);
472 wavinfo.samplerate = (int)value;
473 break;
475 case DSP_SET_SAMPLE_DEPTH:
476 DEBUGF("sampledepth = %d\n",(int)value);
477 wavinfo.sampledepth=(int)value;
478 break;
480 case DSP_SET_STEREO_MODE:
481 DEBUGF("Stereo mode = %d\n",(int)value);
482 wavinfo.stereomode=(int)value;
483 break;
488 static void init_ci(void)
490 /* --- Our "fake" implementations of the codec API functions. --- */
492 ci.codec_get_buffer = codec_get_buffer;
494 if (wavinfo.fd >= 0 || checksum) {
495 ci.pcmbuf_insert = pcmbuf_insert_wav_checksum;
496 } else {
497 ci.pcmbuf_insert = pcmbuf_insert_null;
500 ci.set_elapsed = set_elapsed;
501 ci.read_filebuf = read_filebuf;
502 ci.request_buffer = request_buffer;
503 ci.advance_buffer = advance_buffer;
504 ci.advance_buffer_loc = advance_buffer_loc;
505 ci.seek_buffer = seek_buffer;
506 ci.seek_complete = seek_complete;
507 ci.request_next_track = request_next_track;
508 ci.discard_codec = discard_codec;
509 ci.set_offset = set_offset;
510 ci.configure = configure;
511 ci.dsp = (struct dsp_config *)rb->dsp_configure(NULL, DSP_MYDSP,
512 CODEC_IDX_AUDIO);
514 /* --- "Core" functions --- */
516 /* kernel/ system */
517 ci.sleep = rb->sleep;
518 ci.yield = rb->yield;
520 /* strings and memory */
521 ci.strcpy = rb->strcpy;
522 ci.strlen = rb->strlen;
523 ci.strcmp = rb->strcmp;
524 ci.strcat = rb->strcat;
525 ci.memset = rb->memset;
526 ci.memcpy = rb->memcpy;
527 ci.memmove = rb->memmove;
528 ci.memcmp = rb->memcmp;
529 ci.memchr = rb->memchr;
530 ci.strcasestr = rb->strcasestr;
531 #if defined(DEBUG) || defined(SIMULATOR)
532 ci.debugf = rb->debugf;
533 #endif
534 #ifdef ROCKBOX_HAS_LOGF
535 ci.logf = rb->logf;
536 #endif
538 ci.qsort = rb->qsort;
539 ci.global_settings = rb->global_settings;
541 #ifdef RB_PROFILE
542 ci.profile_thread = rb->profile_thread;
543 ci.profstop = rb->profstop;
544 ci.profile_func_enter = rb->profile_func_enter;
545 ci.profile_func_exit = rb->profile_func_exit;
546 #endif
548 #if NUM_CORES > 1
549 ci.cpucache_invalidate = rb->cpucache_invalidate;
550 ci.cpucache_flush = rb->cpucache_flush;
551 #endif
553 #if NUM_CORES > 1
554 ci.create_thread = rb->create_thread;
555 ci.thread_thaw = rb->thread_thaw;
556 ci.thread_wait = rb->thread_wait;
557 ci.semaphore_init = rb->semaphore_init;
558 ci.semaphore_wait = rb->semaphore_wait;
559 ci.semaphore_release = rb->semaphore_release;
560 #endif
562 #ifdef CPU_ARM
563 ci.__div0 = rb->__div0;
564 #endif
567 static void codec_thread(void)
569 const char* codecname;
570 int res;
572 codecname = rb->get_codec_filename(track.id3.codectype);
574 /* Load the codec and start decoding. */
575 res = rb->codec_load_file(codecname,&ci);
577 /* Signal to the main thread that we are done */
578 endtick = *rb->current_tick;
579 codec_playing = false;
582 static enum plugin_status test_track(const char* filename)
584 size_t n;
585 int fd;
586 enum plugin_status res = PLUGIN_ERROR;
587 long starttick;
588 long ticks;
589 unsigned long speed;
590 unsigned long duration;
591 const char* ch;
592 char str[MAX_PATH];
594 /* Display filename (excluding any path)*/
595 ch = rb->strrchr(filename, '/');
596 if (ch==NULL)
597 ch = filename;
598 else
599 ch++;
601 rb->snprintf(str,sizeof(str),"%s",ch);
602 log_text(str,true);
604 log_text("Loading...",false);
606 fd = rb->open(filename,O_RDONLY);
607 if (fd < 0)
609 log_text("Cannot open file",true);
610 goto exit;
613 track.filesize = rb->filesize(fd);
615 /* Clear the id3 struct */
616 rb->memset(&track.id3, 0, sizeof(struct mp3entry));
618 if (!rb->get_metadata(&(track.id3), fd, filename))
620 log_text("Cannot read metadata",true);
621 goto exit;
624 if (track.filesize > audiosize)
626 log_text("File too large",true);
627 goto exit;
630 n = rb->read(fd, audiobuf, track.filesize);
632 if (n != track.filesize)
634 log_text("Read failed.",true);
635 goto exit;
638 /* Initialise the function pointers in the codec API */
639 init_ci();
641 /* Prepare the codec struct for playing the whole file */
642 ci.filesize = track.filesize;
643 ci.id3 = &track.id3;
644 ci.taginfo_ready = &taginfo_ready;
645 ci.curpos = 0;
646 ci.stop_codec = false;
647 ci.new_track = 0;
648 ci.seek_time = 0;
650 if (use_dsp)
651 rb->dsp_configure(ci.dsp, DSP_RESET, 0);
653 if (checksum)
654 crc32 = 0xffffffff;
656 starttick = *rb->current_tick;
658 codec_playing = true;
660 rb->codec_thread_do_callback(codec_thread, NULL);
662 /* Wait for codec thread to die */
663 while (codec_playing)
665 rb->sleep(HZ);
666 rb->snprintf(str,sizeof(str),"%d of %d",elapsed,(int)track.id3.length);
667 log_text(str,false);
669 ticks = endtick - starttick;
671 /* Be sure it is done */
672 rb->codec_thread_do_callback(NULL, NULL);
674 log_text(str,true);
676 if (checksum)
678 rb->snprintf(str, sizeof(str), "CRC32 - %08x", (unsigned)crc32);
679 log_text(str,true);
681 else if (wavinfo.fd < 0)
683 /* Display benchmark information */
684 rb->snprintf(str,sizeof(str),"Decode time - %d.%02ds",(int)ticks/100,(int)ticks%100);
685 log_text(str,true);
687 duration = track.id3.length / 10;
688 rb->snprintf(str,sizeof(str),"File duration - %d.%02ds",(int)duration/100,(int)duration%100);
689 log_text(str,true);
691 if (ticks > 0)
692 speed = duration * 10000 / ticks;
693 else
694 speed = 0;
696 rb->snprintf(str,sizeof(str),"%d.%02d%% realtime",(int)speed/100,(int)speed%100);
697 log_text(str,true);
699 #if (CONFIG_PLATFORM & PLATFORM_NATIVE)
700 /* show effective clockrate in MHz needed for realtime decoding */
701 if (speed > 0)
703 speed = CPUFREQ_MAX / speed;
704 rb->snprintf(str,sizeof(str),"%d.%02dMHz needed for realtime",
705 (int)speed/100,(int)speed%100);
706 log_text(str,true);
708 #endif
711 res = PLUGIN_OK;
713 exit:
714 rb->backlight_on();
716 if (fd >= 0)
718 rb->close(fd);
721 return res;
724 /* plugin entry point */
725 enum plugin_status plugin_start(const void* parameter)
727 int result, selection = 0;
728 enum plugin_status res = PLUGIN_OK;
729 int scandir;
730 struct dirent *entry;
731 DIR* dir;
732 char* ch;
733 char dirpath[MAX_PATH];
734 char filename[MAX_PATH];
735 size_t buffer_size;
737 if (parameter == NULL)
739 rb->splash(HZ*2, "No File");
740 return PLUGIN_ERROR;
743 wavbuffer = rb->plugin_get_buffer(&buffer_size);
744 dspbuffer = wavbuffer + buffer_size / 2;
746 codec_mallocbuf = rb->plugin_get_audio_buffer(&audiosize);
747 audiobuf = SKIPBYTES(codec_mallocbuf, CODEC_SIZE);
748 audiosize -= CODEC_SIZE;
750 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
751 rb->cpu_boost(true);
752 #endif
753 rb->lcd_clear_display();
754 rb->lcd_update();
756 enum
758 SPEED_TEST = 0,
759 SPEED_TEST_DIR,
760 WRITE_WAV,
761 SPEED_TEST_WITH_DSP,
762 SPEED_TEST_DIR_WITH_DSP,
763 WRITE_WAV_WITH_DSP,
764 CHECKSUM,
765 CHECKSUM_DIR,
766 QUIT,
769 MENUITEM_STRINGLIST(
770 menu, "test_codec", NULL,
771 "Speed test",
772 "Speed test folder",
773 "Write WAV",
774 "Speed test with DSP",
775 "Speed test folder with DSP",
776 "Write WAV with DSP",
777 "Checksum",
778 "Checksum folder",
779 "Quit",
782 show_menu:
783 rb->lcd_clear_display();
785 result = rb->do_menu(&menu, &selection, NULL, false);
787 if (result == QUIT)
789 res = PLUGIN_OK;
790 goto exit;
793 scandir = 0;
795 if ((checksum = (result == CHECKSUM || result == CHECKSUM_DIR)))
796 result -= 6;
798 if ((use_dsp = ((result >= SPEED_TEST_WITH_DSP)
799 && (result <= WRITE_WAV_WITH_DSP)))) {
800 result -= 3;
802 if (result == SPEED_TEST) {
803 wavinfo.fd = -1;
804 log_init(false);
805 } else if (result == SPEED_TEST_DIR) {
806 wavinfo.fd = -1;
807 scandir = 1;
809 /* Only create a log file when we are testing a folder */
810 if (!log_init(true)) {
811 rb->splash(HZ*2, "Cannot create logfile");
812 res = PLUGIN_ERROR;
813 goto exit;
815 } else if (result == WRITE_WAV) {
816 log_init(false);
817 init_wav("/test.wav");
818 if (wavinfo.fd < 0) {
819 rb->splash(HZ*2, "Cannot create /test.wav");
820 res = PLUGIN_ERROR;
821 goto exit;
823 } else if (result == MENU_ATTACHED_USB) {
824 res = PLUGIN_USB_CONNECTED;
825 goto exit;
826 } else if (result < 0) {
827 res = PLUGIN_OK;
828 goto exit;
831 if (scandir) {
832 /* Test all files in the same directory as the file selected by the
833 user */
835 rb->strlcpy(dirpath,parameter,sizeof(dirpath));
836 ch = rb->strrchr(dirpath,'/');
837 ch[1]=0;
839 DEBUGF("Scanning directory \"%s\"\n",dirpath);
840 dir = rb->opendir(dirpath);
841 if (dir) {
842 entry = rb->readdir(dir);
843 while (entry) {
844 if (!(entry->attribute & ATTR_DIRECTORY)) {
845 rb->snprintf(filename,sizeof(filename),"%s%s",dirpath,entry->d_name);
846 test_track(filename);
847 log_text("", true);
850 /* Read next entry */
851 entry = rb->readdir(dir);
854 rb->closedir(dir);
856 } else {
857 /* Just test the file */
858 res = test_track(parameter);
860 /* Close WAV file (if there was one) */
861 if (wavinfo.fd >= 0) {
862 close_wav();
863 log_text("Wrote /test.wav",true);
865 while (rb->button_get(true) != TESTCODEC_EXITBUTTON);
867 rb->button_clear_queue();
868 goto show_menu;
870 exit:
871 log_close();
873 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
874 rb->cpu_boost(false);
875 #endif
877 return res;