A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 1041 lines 27 kB view raw
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}