A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Dave Chapman
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "plugin.h"
22#include "lib/pluginlib_touchscreen.h"
23#include "lib/pluginlib_exit.h"
24#include "lib/pluginlib_actions.h"
25
26/* this set the context to use with PLA */
27static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
28
29#define TESTCODEC_EXITBUTTON PLA_EXIT
30#define TESTCODEC_EXITBUTTON2 PLA_CANCEL
31
32
33#ifdef HAVE_ADJUSTABLE_CPU_FREQ
34static unsigned int boost =1;
35
36static const struct opt_items boost_settings[2] = {
37 { "No", -1 },
38 { "Yes", -1 },
39};
40#endif
41
42/* Log functions copied from test_disk.c */
43static int line = 0;
44static int max_line = 0;
45static int log_fd = -1;
46
47static void log_close(void)
48{
49 if (log_fd >= 0)
50 rb->close(log_fd);
51}
52
53static bool log_init(bool use_logfile)
54{
55 int h;
56 char logfilename[MAX_PATH];
57
58 rb->lcd_getstringsize("A", NULL, &h);
59 max_line = LCD_HEIGHT / h;
60 line = 0;
61 rb->lcd_clear_display();
62 rb->lcd_update();
63
64 if (use_logfile) {
65 log_close();
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);
69 return log_fd >= 0;
70 }
71
72 return true;
73}
74
75static void log_text(char *text, bool advance)
76{
77 rb->lcd_puts(0, line, text);
78 rb->lcd_update();
79 if (advance)
80 {
81 if (++line >= max_line)
82 line = 0;
83 if (log_fd >= 0)
84 rb->fdprintf(log_fd, "%s\n", text);
85 }
86}
87
88struct wavinfo_t
89{
90 int fd;
91 int samplerate;
92 int channels;
93 int sampledepth;
94 int stereomode;
95 int totalsamples;
96};
97
98static void* audiobuf;
99static void* codec_mallocbuf;
100static size_t audiosize;
101static size_t audiobufsize;
102static int offset;
103static int fd;
104
105/* Our local implementation of the codec API */
106static struct codec_api ci;
107
108struct test_track_info {
109 struct mp3entry id3; /* TAG metadata */
110 size_t filesize; /* File total length */
111};
112
113static struct test_track_info track;
114
115static bool use_dsp;
116
117static bool checksum;
118static uint32_t crc32;
119
120static volatile unsigned int elapsed;
121static volatile bool codec_playing;
122static volatile long codec_action;
123static volatile long endtick;
124static volatile long rebuffertick;
125struct wavinfo_t wavinfo;
126
127static unsigned char wav_header[44] =
128{
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
142};
143
144static inline void int2le32(unsigned char* buf, int32_t x)
145{
146 buf[0] = (x & 0xff);
147 buf[1] = (x & 0xff00) >> 8;
148 buf[2] = (x & 0xff0000) >> 16;
149 buf[3] = (x & 0xff000000) >>24;
150}
151
152static inline void int2le24(unsigned char* buf, int32_t x)
153{
154 buf[0] = (x & 0xff);
155 buf[1] = (x & 0xff00) >> 8;
156 buf[2] = (x & 0xff0000) >> 16;
157}
158
159static inline void int2le16(unsigned char* buf, int16_t x)
160{
161 buf[0] = (x & 0xff);
162 buf[1] = (x & 0xff00) >> 8;
163}
164
165static unsigned char *wavbuffer;
166static unsigned char *dspbuffer;
167static int dspbuffer_count;
168
169void init_wav(char* filename)
170{
171 wavinfo.totalsamples = 0;
172
173 wavinfo.fd = rb->creat(filename, 0666);
174
175 if (wavinfo.fd >= 0)
176 {
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));
179 }
180}
181
182
183void close_wav(void)
184{
185 int filesize = rb->filesize(wavinfo.fd);
186 int channels = (wavinfo.stereomode == STEREO_MONO) ? 1 : 2;
187 int bps = 16; /* TODO */
188
189 /* We assume 16-bit, Stereo */
190
191 rb->lseek(wavinfo.fd,0,SEEK_SET);
192
193 int2le32(wav_header+4, filesize-8); /* ChunkSize */
194
195 int2le16(wav_header+22, channels);
196
197 int2le32(wav_header+24, wavinfo.samplerate);
198
199 int2le32(wav_header+28, wavinfo.samplerate * channels * (bps / 8)); /* ByteRate */
200
201 int2le16(wav_header+32, channels * (bps / 8));
202
203 int2le32(wav_header+40, filesize - 44); /* Subchunk2Size */
204
205 rb->write(wavinfo.fd, wav_header, sizeof(wav_header));
206
207 rb->close(wavinfo.fd);
208}
209
210/* Returns buffer to malloc array. Only codeclib should need this. */
211static void* codec_get_buffer(size_t *size)
212{
213 *size = CODEC_SIZE;
214 return codec_mallocbuf;
215}
216
217static int process_dsp(const void *ch1, const void *ch2, int count)
218{
219 struct dsp_buffer src;
220 src.remcount = count;
221 src.pin[0] = ch1;
222 src.pin[1] = ch2;
223 src.proc_mask = 0;
224
225 struct dsp_buffer dst;
226 dst.remcount = 0;
227 dst.p16out = (int16_t *)dspbuffer;
228 dst.bufcount = dspbuffer_count;
229
230 while (1)
231 {
232 int old_remcount = dst.remcount;
233 rb->dsp_process(ci.dsp, &src, &dst, true);
234
235 if (dst.bufcount <= 0 ||
236 (src.remcount <= 0 && dst.remcount <= old_remcount))
237 {
238 /* Dest is full or no input left and DSP purged */
239 break;
240 }
241 }
242
243 return dst.remcount;
244}
245
246/* Null output */
247static void pcmbuf_insert_null(const void *ch1, const void *ch2, int count)
248{
249 if (use_dsp)
250 process_dsp(ch1, ch2, count);
251
252 /* Prevent idle poweroff */
253 rb->reset_poweroff_timer();
254}
255
256/*
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.
260 */
261static 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);
265
266 if(new_offset + audiobufsize <= track.filesize)
267 bytestoread = audiobufsize;
268 else
269 bytestoread = track.filesize-new_offset;
270
271 n = rb->read(fd, audiobuf,bytestoread);
272
273 if (n != bytestoread)
274 {
275 log_text("Read failed.",true);
276 DEBUGF("read fail: got %d bytes, expected %d\n", (int)n, (int)audiobufsize);
277#ifdef HAVE_BACKLIGHT
278 rb->backlight_on();
279#endif
280 if (fd >= 0)
281 {
282 rb->close(fd);
283 }
284
285 return -1;
286 }
287 offset = new_offset;
288
289 /*keep track of how much time we spent buffering*/
290 rebuffertick += *rb->current_tick-temp;
291
292 return 0;
293}
294
295/* WAV output or calculate crc32 of output*/
296static void pcmbuf_insert_wav_checksum(const void *ch1, const void *ch2, int count)
297{
298 /* Prevent idle poweroff */
299 rb->reset_poweroff_timer();
300
301 if (use_dsp) {
302 count = process_dsp(ch1, ch2, count);
303 wavinfo.totalsamples += count;
304
305#ifdef ROCKBOX_BIG_ENDIAN
306 unsigned char* p = dspbuffer;
307 int i;
308 for (i = 0; i < count; i++) {
309 int2le16(p,*(int16_t *)p);
310 p += 2;
311 int2le16(p,*(int16_t *)p);
312 p += 2;
313 }
314#endif
315 if (checksum) {
316 crc32 = rb->crc_32(dspbuffer, count * 2 * sizeof (int16_t), crc32);
317 } else {
318 rb->write(wavinfo.fd, dspbuffer, count * 2 * sizeof (int16_t));
319 }
320 }
321 else
322 {
323 const int16_t* data1_16;
324 const int16_t* data2_16;
325 const int32_t* data1_32;
326 const int32_t* data2_32;
327 unsigned char* p = wavbuffer;
328 const int scale = wavinfo.sampledepth - 15;
329 const int dc_bias = 1 << (scale - 1);
330
331 if (wavinfo.sampledepth <= 16) {
332 data1_16 = ch1;
333 data2_16 = ch2;
334
335 switch(wavinfo.stereomode)
336 {
337 case STEREO_INTERLEAVED:
338 while (count--) {
339 int2le16(p,*data1_16++);
340 p += 2;
341 int2le16(p,*data1_16++);
342 p += 2;
343 }
344 break;
345
346 case STEREO_NONINTERLEAVED:
347 while (count--) {
348 int2le16(p,*data1_16++);
349 p += 2;
350 int2le16(p,*data2_16++);
351 p += 2;
352 }
353
354 break;
355
356 case STEREO_MONO:
357 while (count--) {
358 int2le16(p,*data1_16++);
359 p += 2;
360 }
361 break;
362 }
363 } else {
364 data1_32 = ch1;
365 data2_32 = ch2;
366
367 switch(wavinfo.stereomode)
368 {
369 case STEREO_INTERLEAVED:
370 while (count--) {
371 int2le16(p, clip_sample_16((*data1_32++ + dc_bias) >> scale));
372 p += 2;
373 int2le16(p, clip_sample_16((*data1_32++ + dc_bias) >> scale));
374 p += 2;
375 }
376 break;
377
378 case STEREO_NONINTERLEAVED:
379 while (count--) {
380 int2le16(p, clip_sample_16((*data1_32++ + dc_bias) >> scale));
381 p += 2;
382 int2le16(p, clip_sample_16((*data2_32++ + dc_bias) >> scale));
383 p += 2;
384 }
385
386 break;
387
388 case STEREO_MONO:
389 while (count--) {
390 int2le16(p, clip_sample_16((*data1_32++ + dc_bias) >> scale));
391 p += 2;
392 }
393 break;
394 }
395 }
396
397 wavinfo.totalsamples += count;
398 if (checksum)
399 crc32 = rb->crc_32(wavbuffer, p - wavbuffer, crc32);
400 else
401 rb->write(wavinfo.fd, wavbuffer, p - wavbuffer);
402 } /* else */
403}
404
405/* Set song position in WPS (value in ms). */
406static void set_elapsed(unsigned long value)
407{
408 elapsed = value;
409 ci.id3->elapsed = value;
410}
411
412
413/* Read next <size> amount bytes from file buffer to <ptr>.
414 Will return number of bytes read or 0 if end of file. */
415static size_t read_filebuf(void *ptr, size_t size)
416{
417 if (ci.curpos > (off_t)track.filesize)
418 {
419 return 0;
420 } else {
421 size_t realsize = MIN(track.filesize-ci.curpos,size);
422
423 /* check if we have enough bytes ready*/
424 if(realsize >(audiobufsize - (ci.curpos-offset)))
425 {
426 /*rebuffer so that we start at ci.curpos*/
427 fill_buffer(ci.curpos);
428 }
429
430 rb->memcpy(ptr, audiobuf + (ci.curpos-offset), realsize);
431 ci.curpos += realsize;
432 return realsize;
433 }
434}
435
436
437/* Request pointer to file buffer which can be used to read
438 <realsize> amount of data. <reqsize> tells the buffer system
439 how much data it should try to allocate. If <realsize> is 0,
440 end of file is reached. */
441static void* request_buffer(size_t *realsize, size_t reqsize)
442{
443 *realsize = MIN(track.filesize-ci.curpos,reqsize);
444
445 /*check if we have enough bytes ready - requested > bufsize-currentbufpos*/
446 if(*realsize>(audiobufsize - (ci.curpos-offset)))
447 {
448 /*rebuffer so that we start at ci.curpos*/
449 fill_buffer(ci.curpos);
450 }
451
452 return (audiobuf + (ci.curpos-offset));
453}
454
455/* Advance file buffer position by <amount> amount of bytes. */
456static void advance_buffer(size_t amount)
457{
458 ci.curpos += amount;
459 ci.id3->offset = ci.curpos;
460}
461
462
463/* Seek file buffer to position <newpos> beginning of file. */
464static bool seek_buffer(size_t newpos)
465{
466 ci.curpos = newpos;
467 return true;
468}
469
470
471/* Codec should call this function when it has done the seeking. */
472static void seek_complete(void)
473{
474 /* Do nothing */
475}
476
477/* Codec calls this to know what it should do next. */
478static long get_command(intptr_t *param)
479{
480 rb->yield();
481 return codec_action;
482 (void)param;
483}
484
485/* Some codecs call this to determine whether they should loop. */
486static bool loop_track(void)
487{
488 return false;
489}
490
491static void set_offset(size_t value)
492{
493 ci.id3->offset = value;
494}
495
496
497/* Configure different codec buffer parameters. */
498static void configure(int setting, intptr_t value)
499{
500 if (use_dsp)
501 rb->dsp_configure(ci.dsp, setting, value);
502 switch(setting)
503 {
504 case DSP_SET_FREQUENCY:
505 DEBUGF("samplerate=%d\n",(int)value);
506 if (use_dsp) {
507 wavinfo.samplerate = rb->dsp_configure(
508 ci.dsp, DSP_GET_OUT_FREQUENCY, 0);
509 } else {
510 wavinfo.samplerate = (int)value;
511 }
512 break;
513
514 case DSP_SET_SAMPLE_DEPTH:
515 DEBUGF("sampledepth = %d\n",(int)value);
516 wavinfo.sampledepth = use_dsp ? 16 : (int)value;
517 break;
518
519 case DSP_SET_STEREO_MODE:
520 DEBUGF("Stereo mode = %d\n",(int)value);
521 wavinfo.stereomode = use_dsp ? STEREO_INTERLEAVED : (int)value;
522 break;
523 }
524
525}
526
527static void strip_filesize(off_t size)
528{
529 ci.filesize = size;
530}
531
532static void init_ci(void)
533{
534 /* --- Our "fake" implementations of the codec API functions. --- */
535
536 ci.dsp = rb->dsp_get_config(CODEC_IDX_AUDIO);
537 ci.codec_get_buffer = codec_get_buffer;
538
539 if (wavinfo.fd >= 0 || checksum) {
540 ci.pcmbuf_insert = pcmbuf_insert_wav_checksum;
541 } else {
542 ci.pcmbuf_insert = pcmbuf_insert_null;
543 }
544
545 ci.set_elapsed = set_elapsed;
546 ci.read_filebuf = read_filebuf;
547 ci.request_buffer = request_buffer;
548 ci.advance_buffer = advance_buffer;
549 ci.seek_buffer = seek_buffer;
550 ci.seek_complete = seek_complete;
551 ci.set_offset = set_offset;
552 ci.configure = configure;
553 ci.get_command = get_command;
554 ci.loop_track = loop_track;
555 ci.strip_filesize = strip_filesize;
556
557 /* --- "Core" functions --- */
558
559 /* kernel/ system */
560 ci.sleep = rb->sleep;
561 ci.yield = rb->yield;
562
563 /* strings and memory */
564 ci.strcpy = rb->strcpy;
565 ci.strlen = rb->strlen;
566 ci.strcmp = rb->strcmp;
567 ci.strcat = rb->strcat;
568 ci.memset = rb->memset;
569 ci.memcpy = rb->memcpy;
570 ci.memmove = rb->memmove;
571 ci.memcmp = rb->memcmp;
572 ci.memchr = rb->memchr;
573#if defined(DEBUG) || defined(SIMULATOR)
574 ci.debugf = rb->debugf;
575#endif
576#ifdef ROCKBOX_HAS_LOGF
577 ci.logf = rb->logf;
578#endif
579
580 ci.qsort = rb->qsort;
581
582#ifdef RB_PROFILE
583 ci.profile_thread = rb->profile_thread;
584 ci.profstop = rb->profstop;
585 ci.profile_func_enter = rb->profile_func_enter;
586 ci.profile_func_exit = rb->profile_func_exit;
587#endif
588
589 ci.commit_dcache = rb->commit_dcache;
590 ci.commit_discard_dcache = rb->commit_discard_dcache;
591 ci.commit_discard_idcache = rb->commit_discard_idcache;
592
593#if NUM_CORES > 1
594 ci.create_thread = rb->create_thread;
595 ci.thread_thaw = rb->thread_thaw;
596 ci.thread_wait = rb->thread_wait;
597 ci.semaphore_init = rb->semaphore_init;
598 ci.semaphore_wait = rb->semaphore_wait;
599 ci.semaphore_release = rb->semaphore_release;
600#endif
601
602#if defined(ARM_NEED_DIV0)
603 ci.__div0 = rb->__div0;
604#endif
605}
606
607static void codec_thread(void)
608{
609 const char* codecname;
610 int res;
611
612 codecname = rb->get_codec_filename(track.id3.codectype);
613
614 /* Load the codec */
615 res = rb->codec_load_file(codecname, &ci);
616
617 if (res >= 0)
618 {
619 /* Decode the file */
620 res = rb->codec_run_proc();
621 }
622
623 /* Clean up */
624 rb->codec_close();
625
626 /* Signal to the main thread that we are done */
627 endtick = *rb->current_tick - rebuffertick;
628 codec_playing = false;
629}
630
631static enum plugin_status test_track(const char* filename)
632{
633 size_t n;
634 enum plugin_status res = PLUGIN_ERROR;
635 long starttick;
636 long ticks;
637 unsigned long speed;
638 unsigned long duration;
639 const char* ch;
640 char str[MAX_PATH];
641 offset=0;
642
643 /* Display filename (excluding any path)*/
644 ch = rb->strrchr(filename, '/');
645 if (ch==NULL)
646 ch = filename;
647 else
648 ch++;
649
650 rb->snprintf(str,sizeof(str),"%s",ch);
651 log_text(str,true);
652
653 log_text("Loading...",false);
654
655 fd = rb->open(filename,O_RDONLY);
656 if (fd < 0)
657 {
658 log_text("Cannot open file",true);
659 goto exit;
660 }
661
662 track.filesize = rb->filesize(fd);
663
664 /* Clear the id3 struct */
665 rb->memset(&track.id3, 0, sizeof(struct mp3entry));
666
667 if (!rb->get_metadata(&(track.id3), fd, filename))
668 {
669 log_text("Cannot read metadata",true);
670 goto exit;
671 }
672
673 if (track.filesize > audiosize)
674 {
675 audiobufsize=audiosize;
676
677 } else
678 {
679 audiobufsize=track.filesize;
680 }
681
682 n = rb->read(fd, audiobuf, audiobufsize);
683
684 if (n != audiobufsize)
685 {
686 log_text("Read failed.",true);
687 goto exit;
688 }
689
690
691 /* Initialise the function pointers in the codec API */
692 init_ci();
693
694 /* Prepare the codec struct for playing the whole file */
695 ci.filesize = track.filesize;
696 ci.id3 = &track.id3;
697 ci.curpos = 0;
698
699 if (use_dsp) {
700 rb->dsp_configure(ci.dsp, DSP_RESET, 0);
701 rb->dsp_configure(ci.dsp, DSP_FLUSH, 0);
702 }
703
704 if (checksum)
705 crc32 = 0xffffffff;
706
707 rebuffertick=0;
708 starttick = *rb->current_tick;
709
710 codec_playing = true;
711 codec_action = CODEC_ACTION_NULL;
712
713 rb->codec_thread_do_callback(codec_thread, NULL);
714
715 /* Wait for codec thread to die */
716 while (codec_playing)
717 {
718 int button = pluginlib_getaction(HZ, plugin_contexts,
719 ARRAYLEN(plugin_contexts));
720 if ((button == TESTCODEC_EXITBUTTON) || (button == TESTCODEC_EXITBUTTON2))
721 {
722 codec_action = CODEC_ACTION_HALT;
723 break;
724 }
725
726 rb->snprintf(str,sizeof(str),"%d of %d",elapsed,(int)track.id3.length);
727 log_text(str,false);
728 }
729 ticks = endtick - starttick;
730
731 /* Be sure it is done */
732 rb->codec_thread_do_callback(NULL, NULL);
733#ifdef HAVE_BACKLIGHT
734 rb->backlight_on();
735#endif
736 log_text(str,true);
737
738 if (codec_action == CODEC_ACTION_HALT)
739 {
740 /* User aborted test */
741 }
742 else if (checksum)
743 {
744 rb->snprintf(str, sizeof(str), "CRC32 - %08x", (unsigned)crc32);
745 log_text(str,true);
746 }
747 else if (wavinfo.fd < 0)
748 {
749 /* Display benchmark information */
750 rb->snprintf(str,sizeof(str),"Decode time - %d.%02ds",(int)ticks/100,(int)ticks%100);
751 log_text(str,true);
752
753 duration = track.id3.length / 10;
754 rb->snprintf(str,sizeof(str),"File duration - %d.%02ds",(int)duration/100,(int)duration%100);
755 log_text(str,true);
756
757 if (ticks > 0)
758 speed = duration * 10000 / ticks;
759 else
760 speed = 0;
761
762 rb->snprintf(str,sizeof(str),"%d.%02d%% realtime",(int)speed/100,(int)speed%100);
763 log_text(str,true);
764
765#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
766 /* show effective clockrate in MHz needed for realtime decoding */
767 if (speed > 0)
768 {
769 int freq;
770 freq = *rb->cpu_frequency;
771
772 speed = freq / speed;
773 rb->snprintf(str,sizeof(str),"%d.%02dMHz needed for realtime",
774 (int)speed/100,(int)speed%100);
775 log_text(str,true);
776 }
777#endif
778 }
779
780 res = PLUGIN_OK;
781
782exit:
783#ifdef HAVE_BACKLIGHT
784 rb->backlight_on();
785#endif
786 if (fd >= 0)
787 {
788 rb->close(fd);
789 }
790
791 return res;
792}
793
794#ifdef HAVE_TOUCHSCREEN
795void cleanup(void)
796{
797 rb->screens[0]->set_viewport(NULL);
798}
799#endif
800
801void plugin_quit(void)
802{
803 int btn;
804#ifdef HAVE_TOUCHSCREEN
805 static struct touchbutton button[] = {{
806 .action = ACTION_STD_OK,
807 .title = "OK",
808 /* viewport runtime initialized, rest false/NULL */
809 }};
810 struct viewport *vp = &button[0].vp;
811 struct screen *lcd = rb->screens[SCREEN_MAIN];
812 rb->viewport_set_defaults(vp, SCREEN_MAIN);
813 const int border = 10;
814 const int height = 50;
815
816 lcd->set_viewport(vp);
817 /* button matches the bottom center in the grid */
818 vp->x = lcd->lcdwidth/3;
819 vp->width = lcd->lcdwidth/3;
820 vp->height = height;
821 vp->y = lcd->lcdheight - height - border;
822
823 touchbutton_draw(button, ARRAYLEN(button));
824 lcd->update_viewport();
825 if (rb->touchscreen_get_mode() == TOUCHSCREEN_POINT)
826 {
827 while (codec_action != CODEC_ACTION_HALT &&
828 touchbutton_get(button, ARRAYLEN(button)) != ACTION_STD_OK);
829 }
830 else
831#endif
832 do {
833 btn = pluginlib_getaction(TIMEOUT_BLOCK, plugin_contexts,
834 ARRAYLEN(plugin_contexts));
835 exit_on_usb(btn);
836 } while ((codec_action != CODEC_ACTION_HALT)
837 && (btn != TESTCODEC_EXITBUTTON)
838 && (btn != TESTCODEC_EXITBUTTON2));
839}
840
841/* plugin entry point */
842enum plugin_status plugin_start(const void* parameter)
843{
844 int result, selection = 0;
845 enum plugin_status res = PLUGIN_OK;
846 int scandir;
847 struct dirent *entry;
848 DIR* dir;
849 char* ch;
850 char dirpath[MAX_PATH];
851 char filename[MAX_PATH];
852 size_t buffer_size;
853
854 if (parameter == NULL)
855 {
856 rb->splash(HZ*2, "No File");
857 return PLUGIN_ERROR;
858 }
859
860 wavbuffer = rb->plugin_get_buffer(&buffer_size);
861 dspbuffer = wavbuffer + buffer_size / 2;
862 dspbuffer_count = (buffer_size - (dspbuffer - wavbuffer)) /
863 (2 * sizeof (int16_t));
864
865 codec_mallocbuf = rb->plugin_get_audio_buffer(&audiosize);
866 /* Align codec_mallocbuf to pointer size, tlsf wants that */
867 codec_mallocbuf = (void*)(((intptr_t)codec_mallocbuf +
868 sizeof(intptr_t)-1) & ~(sizeof(intptr_t)-1));
869 audiobuf = SKIPBYTES(codec_mallocbuf, CODEC_SIZE);
870 audiosize -= CODEC_SIZE;
871
872 rb->lcd_clear_display();
873 rb->lcd_update();
874
875#ifdef HAVE_TOUCHSCREEN
876 rb->touchscreen_set_mode(rb->global_settings->touch_mode);
877#endif
878
879 enum
880 {
881 SPEED_TEST = 0,
882 SPEED_TEST_DIR,
883 WRITE_WAV,
884 SPEED_TEST_WITH_DSP,
885 SPEED_TEST_DIR_WITH_DSP,
886 WRITE_WAV_WITH_DSP,
887 CHECKSUM,
888 CHECKSUM_DIR,
889 QUIT,
890#ifdef HAVE_ADJUSTABLE_CPU_FREQ
891 BOOST,
892#endif
893 };
894
895 MENUITEM_STRINGLIST(
896 menu, "test_codec", NULL,
897 "Speed test",
898 "Speed test folder",
899 "Write WAV",
900 "Speed test with DSP",
901 "Speed test folder with DSP",
902 "Write WAV with DSP",
903 "Checksum",
904 "Checksum folder",
905 "Quit",
906#ifdef HAVE_ADJUSTABLE_CPU_FREQ
907 "Boosting",
908#endif
909 );
910
911
912show_menu:
913 rb->lcd_clear_display();
914
915#ifdef HAVE_ADJUSTABLE_CPU_FREQ
916menu:
917#endif
918
919 result = rb->do_menu(&menu, &selection, NULL, false);
920
921#ifdef HAVE_ADJUSTABLE_CPU_FREQ
922 if (result == BOOST)
923 {
924 rb->set_option("Boosting", &boost, RB_INT,
925 boost_settings, 2, NULL);
926 goto menu;
927 }
928#endif
929
930 if (result == QUIT)
931 {
932 res = PLUGIN_OK;
933 goto exit;
934 }
935
936 scandir = 0;
937
938 /* Map test runs with checksum calcualtion to standard runs
939 * SPEED_TEST and SPEED_TEST_DIR and set the 'checksum' flag. */
940 if ((checksum = (result == CHECKSUM ||
941 result == CHECKSUM_DIR)))
942 result -= 6;
943
944 /* Map test runs with DSP to standard runs SPEED_TEST,
945 * SPEED_TEST_DIR and WRITE_WAV and set the 'use_dsp' flag. */
946 if ((use_dsp = (result >= SPEED_TEST_WITH_DSP &&
947 result <= WRITE_WAV_WITH_DSP)))
948 result -= 3;
949
950 if (result == SPEED_TEST) {
951 wavinfo.fd = -1;
952 log_init(false);
953 } else if (result == SPEED_TEST_DIR) {
954 wavinfo.fd = -1;
955 scandir = 1;
956
957 /* Only create a log file when we are testing a folder */
958 if (!log_init(true)) {
959 rb->splash(HZ*2, "Cannot create logfile");
960 res = PLUGIN_ERROR;
961 goto exit;
962 }
963 } else if (result == WRITE_WAV) {
964 log_init(false);
965 init_wav("/test.wav");
966 if (wavinfo.fd < 0) {
967 rb->splash(HZ*2, "Cannot create /test.wav");
968 res = PLUGIN_ERROR;
969 goto exit;
970 }
971 } else if (result == MENU_ATTACHED_USB) {
972 res = PLUGIN_USB_CONNECTED;
973 goto exit;
974 } else if (result < 0) {
975 res = PLUGIN_OK;
976 goto exit;
977 }
978
979#ifdef HAVE_ADJUSTABLE_CPU_FREQ
980 if (boost)
981 rb->cpu_boost(true);
982 else
983 rb->cpu_boost(false); /*force unboost at start to be safe*/
984#endif
985
986 if (scandir) {
987 /* Test all files in the same directory as the file selected by the
988 user */
989
990 rb->strlcpy(dirpath,parameter,sizeof(dirpath));
991 ch = rb->strrchr(dirpath,'/');
992 ch[1]=0;
993
994 DEBUGF("Scanning directory \"%s\"\n",dirpath);
995 dir = rb->opendir(dirpath);
996 if (dir) {
997 entry = rb->readdir(dir);
998 while (entry) {
999 struct dirinfo info = rb->dir_get_info(dir, entry);
1000 if (!(info.attribute & ATTR_DIRECTORY)) {
1001 rb->snprintf(filename,sizeof(filename),"%s%s",dirpath,entry->d_name);
1002 test_track(filename);
1003
1004 if (codec_action == CODEC_ACTION_HALT)
1005 break;
1006
1007 log_text("", true);
1008 }
1009
1010 /* Read next entry */
1011 entry = rb->readdir(dir);
1012 }
1013
1014 rb->closedir(dir);
1015 }
1016 } else {
1017 /* Just test the file */
1018 res = test_track(parameter);
1019
1020 /* Close WAV file (if there was one) */
1021 if (wavinfo.fd >= 0) {
1022 close_wav();
1023 log_text("Wrote /test.wav",true);
1024 }
1025 }
1026
1027#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1028 if (boost)
1029 rb->cpu_boost(false);
1030#endif
1031
1032 plugin_quit();
1033
1034 rb->button_clear_queue();
1035 goto show_menu;
1036
1037exit:
1038 log_close();
1039
1040 return res;
1041}