A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 1040 lines 25 kB view raw
1#define NO_MMSUPP_DEFINES 2 3#include "plugin.h" 4#include "lib/configfile.h" 5#include "mikmod.h" 6 7 8#undef SYNC 9#ifdef SIMULATOR 10#define SYNC 11#else 12#define USETHREADS 13#endif 14 15#define MAX_CHARS LCD_WIDTH/6 16#define MAX_LINES LCD_HEIGHT/8 17#define LINE_LENGTH 80 18 19#define DIR_PREV 1 20#define DIR_NEXT -1 21#define DIR_NONE 0 22 23#define PLUGIN_NEWSONG 10 24 25/* Persistent configuration */ 26#define MIKMOD_CONFIGFILE "mikmod.cfg" 27#define MIKMOD_SETTINGS_MINVERSION 1 28#define MIKMOD_SETTINGS_VERSION 2 29 30#ifdef USETHREADS 31#define EV_EXIT 9999 32#define THREAD_STACK_SIZE DEFAULT_STACK_SIZE + 0x200 33static unsigned int thread_id; 34static struct event_queue thread_q SHAREDBSS_ATTR; 35/* use long for aligning */ 36unsigned long thread_stack[THREAD_STACK_SIZE/sizeof(long)]; 37#endif 38 39static int font_h, font_w; 40 41/* the current full file name */ 42static char np_file[MAX_PATH]; 43static int curfile = 0, direction = DIR_NEXT, entries = 0; 44 45/* list of the mod files */ 46static char **file_pt; 47 48static int inited = 0; 49 50/* The MP3 audio buffer which we will use as heap memory */ 51static unsigned char* audio_buffer; 52/* amount of bytes left in audio_buffer */ 53static size_t audio_buffer_free; 54 55 56bool quit; 57int playingtime IBSS_ATTR; 58MODULE *module IBSS_ATTR; 59char gmbuf[BUF_SIZE*NBUF]; 60 61 62int textlines; 63int vscroll = 0; 64int hscroll = 0; 65bool screenupdated = false; 66 67enum { 68 DISPLAY_INFO = 0, 69 DISPLAY_SAMPLE, 70 DISPLAY_INST, 71 DISPLAY_COMMENTS, 72} display; 73 74 75/* 76* strncat wrapper 77*/ 78char* mmsupp_strncat(char *s1, const char *s2, size_t n) 79{ 80 char *s = s1; 81 /* Loop over the data in s1. */ 82 while (*s != '\0') 83 s++; 84 /* s now points to s1's trailing null character, now copy 85 up to n bytes from s2 into s1 stopping if a null character 86 is encountered in s2. 87 It is not safe to use strncpy here since it copies EXACTLY n 88 characters, NULL padding if necessary. */ 89 while (n != 0 && (*s = *s2++) != '\0') 90 { 91 n--; 92 s++; 93 } 94 if (*s != '\0') 95 *s = '\0'; 96 return s1; 97} 98 99/* 100* sprintf wrapper 101*/ 102int mmsupp_sprintf(char *buf, const char *fmt, ... ) 103{ 104 bool ok; 105 va_list ap; 106 107 va_start(ap, fmt); 108 ok = rb->vsnprintf(buf, LINE_LENGTH, fmt, ap); 109 va_end(ap); 110 111 return ok; 112} 113 114/* 115* printf wrapper 116*/ 117void mmsupp_printf(const char *fmt, ...) 118{ 119 static int p_xtpt = 0; 120 char p_buf[LINE_LENGTH]; 121 /* bool ok; */ 122 va_list ap; 123 124 va_start(ap, fmt); 125 /* ok = */ (void)rb->vsnprintf(p_buf, sizeof(p_buf), fmt, ap); 126 va_end(ap); 127 128 int i=0; 129 130 /* Device LCDs display newlines funny. */ 131 for(i=0; p_buf[i]!=0; i++) 132 if(p_buf[i] == '\n') 133 p_buf[i] = ' '; 134 135 rb->lcd_putsxy(1, p_xtpt, (unsigned char *)p_buf); 136 rb->lcd_update(); 137 138 p_xtpt += 8; 139 if(p_xtpt > LCD_HEIGHT-8) 140 { 141 p_xtpt = 0; 142 rb->lcd_clear_display(); 143 } 144} 145 146 147/************************* File Access ***************************/ 148 149/* support function for qsort() */ 150/* not used 151static int compare(const void* p1, const void* p2) 152{ 153 return rb->strcasecmp(*((char **)p1), *((char **)p2)); 154} 155*/ 156 157static bool mod_ext(const char ext[]) 158{ 159 if(!ext) 160 return false; 161 if(!rb->strcasecmp(ext,".669") || 162 !rb->strcasecmp(ext,".amf") || 163 !rb->strcasecmp(ext,".asy") || 164 !rb->strcasecmp(ext,".dsm") || 165 !rb->strcasecmp(ext,".far") || 166 !rb->strcasecmp(ext,".gdm") || 167 !rb->strcasecmp(ext,".imf") || 168 !rb->strcasecmp(ext,".it") || 169 !rb->strcasecmp(ext,".m15") || 170 !rb->strcasecmp(ext,".med") || 171 !rb->strcasecmp(ext,".mod") || 172 !rb->strcasecmp(ext,".mtm") || 173 !rb->strcasecmp(ext,".okt") || 174 !rb->strcasecmp(ext,".s3m") || 175 !rb->strcasecmp(ext,".stm") || 176 !rb->strcasecmp(ext,".stx") || 177 !rb->strcasecmp(ext,".ult") || 178 !rb->strcasecmp(ext,".uni") || 179 !rb->strcasecmp(ext,".umx") || 180 !rb->strcasecmp(ext,".xm") ) 181 return true; 182 else 183 return false; 184} 185 186/*Read directory contents for scrolling. */ 187static void get_mod_list(void) 188{ 189 struct tree_context *tree = rb->tree_get_context(); 190 struct entry *dircache = rb->tree_get_entries(tree); 191 int i; 192 char *pname; 193 194 file_pt = (char **) audio_buffer; 195 196 /* Remove path and leave only the name.*/ 197 pname = rb->strrchr(np_file,'/'); 198 pname++; 199 200 for (i = 0; i < tree->filesindir && audio_buffer_free > sizeof(char**); i++) 201 { 202 if (!(dircache[i].attr & ATTR_DIRECTORY) 203 && mod_ext(rb->strrchr(dircache[i].name,'.'))) 204 { 205 file_pt[entries] = dircache[i].name; 206 /* Set Selected File. */ 207 if (!rb->strcmp(file_pt[entries], pname)) 208 curfile = entries; 209 entries++; 210 211 audio_buffer += (sizeof(char**)); 212 audio_buffer_free -= (sizeof(char**)); 213 } 214 } 215} 216 217static int change_filename(int direct) 218{ 219 bool file_erased = (file_pt[curfile] == NULL); 220 direction = direct; 221 222 curfile += (direct == DIR_PREV? entries - 1: 1); 223 if (curfile >= entries) 224 curfile -= entries; 225 226 if (file_erased) 227 { 228 /* remove 'erased' file names from list. */ 229 int count, i; 230 for (count = i = 0; i < entries; i++) 231 { 232 if (curfile == i) 233 curfile = count; 234 if (file_pt[i] != NULL) 235 file_pt[count++] = file_pt[i]; 236 } 237 entries = count; 238 } 239 240 if (entries == 0) 241 { 242 rb->splash(HZ, ID2P(LANG_NO_FILES)); 243 return PLUGIN_ERROR; 244 } 245 246 rb->strcpy(rb->strrchr(np_file, '/')+1, file_pt[curfile]); 247 248 return PLUGIN_NEWSONG; 249} 250 251/***************************************************************************** 252* Playback 253*/ 254 255bool swap = false; 256bool lastswap = true; 257 258static inline void synthbuf(void) 259{ 260 char *outptr; 261 262#ifndef SYNC 263 if (lastswap == swap) return; 264 lastswap = swap; 265 266 outptr = (swap ? gmbuf : gmbuf + BUF_SIZE); 267#else 268 outptr = gmbuf; 269#endif 270 271 VC_WriteBytes(outptr, BUF_SIZE); 272} 273 274static void get_more(const void** start, size_t* size) 275{ 276#ifndef SYNC 277 if (lastswap != swap) 278 { 279 //printf("Buffer miss!"); 280 } 281 282#else 283 synthbuf(); 284#endif 285 286 *size = BUF_SIZE; 287#ifndef SYNC 288 *start = swap ? gmbuf : gmbuf + BUF_SIZE; 289 swap = !swap; 290#else 291 *start = gmbuf; 292#endif 293} 294 295static void showinfo(void) 296{ 297 char statustext[LINE_LENGTH]; 298 299 if (!module) 300 { 301 return; 302 } 303 304 rb->lcd_clear_display(); 305 306 playingtime = (int)(module->sngtime >> 10); 307 sprintf(statustext, "Name: %s", module->songname); 308 rb->lcd_putsxy(1, 1, statustext); 309 sprintf(statustext, "Type: %s", module->modtype); 310 rb->lcd_putsxy(1, 1 + 1 * font_h, statustext); 311 312 sprintf(statustext, "Samples: %d", module->numsmp); 313 rb->lcd_putsxy(1, 1 + 2 * font_h, statustext); 314 315 if ( module->flags & UF_INST ) 316 { 317 sprintf(statustext, "Instruments: %d", module->numins); 318 rb->lcd_putsxy(1, 1 + 3 * font_h, statustext); 319 } 320 321 sprintf(statustext, "pat: %03d/%03d %2.2X", 322 module->sngpos, module->numpos - 1, module->patpos); 323 rb->lcd_putsxy(1, 1 + 5 * font_h, statustext); 324 325 sprintf(statustext, "spd: %d/%d", 326 module->sngspd, module->bpm); 327 rb->lcd_putsxy(1, 1 + 6 * font_h, statustext); 328 329 sprintf(statustext, "vol: %ddB", rb->global_status->volume); 330 rb->lcd_putsxy(1, 1 + 7 * font_h, statustext); 331 332 sprintf(statustext, "time: %d:%02d", 333 (playingtime / 60) % 60, playingtime % 60); 334 rb->lcd_putsxy(1, 1 + 8 * font_h, statustext); 335 336 if (module->flags & UF_NNA) 337 { 338 sprintf(statustext, "chn: %d/%d+%d->%d", 339 module->realchn, module->numchn, 340 module->totalchn - module->realchn, 341 module->totalchn); 342 } 343 else 344 { 345 sprintf(statustext, "chn: %d/%d", 346 module->realchn, module->numchn); 347 } 348 rb->lcd_putsxy(0, 1 + 9 * font_h, statustext); 349 350 rb->lcd_update(); 351} 352 353static void showsamples(void) 354{ 355 int i; 356 char statustext[LINE_LENGTH]; 357 358 if ( screenupdated ) 359 { 360 return; 361 } 362 rb->lcd_clear_display(); 363 for( i=0; i<MAX_LINES && i+vscroll<module->numsmp; i++ ) 364 { 365 sprintf(statustext, "%02d %s", i+vscroll+1, module->samples[i+vscroll].samplename); 366 rb->lcd_putsxy(1, 1+(font_h*i), statustext); 367 } 368 rb->lcd_update(); 369 screenupdated = true; 370} 371 372static void showinstruments(void) 373{ 374 int i; 375 char statustext[LINE_LENGTH]; 376 377 if ( screenupdated ) 378 { 379 return; 380 } 381 rb->lcd_clear_display(); 382 for( i=0; i<MAX_LINES && i+vscroll<module->numins; i++ ) 383 { 384 sprintf(statustext, "%02d %s", i+vscroll+1, module->instruments[i+vscroll].insname ? module->instruments[i+vscroll].insname : "[n/a]"); 385 rb->lcd_putsxy(1, 1+(font_h*i), statustext); 386 } 387 rb->lcd_update(); 388 screenupdated = true; 389} 390 391static void showcomments(void) 392{ 393 int i, j=0, k=0, l; 394 char statustext[LINE_LENGTH]; 395 396 if ( screenupdated ) 397 { 398 return; 399 } 400 rb->lcd_clear_display(); 401 402 for(i=0; module->comment[i]!='\0'; i++) 403 { 404 if(module->comment[i] != '\n') 405 { 406 statustext[j] = module->comment[i]; 407 j++; 408 } 409 410 if(module->comment[i] == '\n' || j>LINE_LENGTH-1) 411 { 412 rb->lcd_putsxy(1-(font_w*hscroll), 1+(font_h*k)-(font_h*vscroll), statustext); 413 for( l=0; l<LINE_LENGTH; l++ ) 414 { 415 statustext[l] = 0; 416 } 417 k++; 418 j=0; 419 } 420 } 421 if (j>0) 422 { 423 rb->lcd_putsxy(1-(font_w*hscroll), 1+(font_h*k)-(font_h*vscroll), statustext); 424 } 425 426 rb->lcd_update(); 427 screenupdated = true; 428} 429 430static void changedisplay(void) 431{ 432 display = (display+1) % 4; 433 434 if (display == DISPLAY_SAMPLE) 435 { 436 textlines = module->numsmp; 437 } 438 439 if (display == DISPLAY_INST) 440 { 441 if ( module->flags & UF_INST ) 442 { 443 textlines = module->numins; 444 } 445 else 446 { 447 display = DISPLAY_COMMENTS; 448 } 449 } 450 451 if (display == DISPLAY_COMMENTS) 452 { 453 if (module->comment) 454 { 455 textlines = 100; 456 } 457 else 458 { 459 display = DISPLAY_INFO; 460 } 461 } 462 screenupdated = false; 463 vscroll = 0; 464 hscroll = 0; 465} 466 467struct mikmod_settings 468{ 469 int pansep; 470 int reverb; 471 int sample_rate; 472 bool interp; 473 bool reverse; 474 bool surround; 475 bool hqmixer; 476#ifdef HAVE_ADJUSTABLE_CPU_FREQ 477 bool boost; 478#endif 479}; 480 481static struct mikmod_settings settings = 482{ 483 .pansep = 128, 484 .reverb = 0, 485 .sample_rate = -1, 486 .interp = 0, 487 .reverse = 0, 488 .surround = 1, 489 .hqmixer = 0, 490#ifdef HAVE_ADJUSTABLE_CPU_FREQ 491 .boost = 1, 492#endif 493}; 494 495static struct mikmod_settings old_settings; 496 497static const struct configdata config[] = 498{ 499 { TYPE_INT, 0, 128, { .int_p = &settings.pansep }, "Panning Separation", NULL}, 500 { TYPE_INT, 0, 15, { .int_p = &settings.reverb }, "Reverberation", NULL}, 501 { TYPE_BOOL, 0, 1, { .bool_p = &settings.interp }, "Interpolation", NULL}, 502 { TYPE_BOOL, 0, 1, { .bool_p = &settings.reverse }, "Reverse Channels", NULL}, 503 { TYPE_BOOL, 0, 1, { .bool_p = &settings.surround }, "Surround", NULL}, 504 { TYPE_BOOL, 0, 1, { .bool_p = &settings.hqmixer }, "HQ Mixer", NULL}, 505 { TYPE_INT, 0, HW_NUM_FREQ-1, { .int_p = &settings.sample_rate }, "Sample Rate", NULL}, 506#ifdef HAVE_ADJUSTABLE_CPU_FREQ 507 { TYPE_BOOL, 0, 1, { .bool_p = &settings.boost }, "CPU Boost", NULL}, 508#endif 509}; 510 511static void applysettings(void) 512{ 513 md_pansep = settings.pansep; 514 md_reverb = settings.reverb; 515 md_mode = DMODE_STEREO | DMODE_16BITS | DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX; 516 517 if ( settings.interp ) 518 { 519 md_mode |= DMODE_INTERP; 520 } 521 if ( settings.reverse ) 522 { 523 md_mode |= DMODE_REVERSE; 524 } 525 if ( settings.surround ) 526 { 527 md_mode |= DMODE_SURROUND; 528 } 529#ifndef NO_HQMIXER 530 if ( settings.hqmixer ) 531 { 532 md_mode |= DMODE_HQMIXER; 533 } 534#endif 535 536 if (inited && (md_mixfreq != rb->hw_freq_sampr[settings.sample_rate])) { 537 md_mixfreq = rb->hw_freq_sampr[settings.sample_rate]; 538// MikMod_Reset(""); BROKEN! 539 rb->pcm_play_stop(); 540 rb->mixer_set_frequency(md_mixfreq); 541 rb->mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, get_more, NULL, 0); 542 } 543 544#ifdef HAVE_ADJUSTABLE_CPU_FREQ 545 if ( Player_Active() ) 546 { 547 rb->cpu_boost(settings.boost); 548 } 549#endif 550} 551 552static const struct opt_items sr_names[HW_NUM_FREQ] = { 553 HW_HAVE_192_([HW_FREQ_192] = { "192kHz", TALK_ID(192, UNIT_KHZ) },) 554 HW_HAVE_176_([HW_FREQ_176] = { "176.4kHz", TALK_ID(176, UNIT_KHZ) },) 555 HW_HAVE_96_([HW_FREQ_96] = { "96kHz", TALK_ID(96, UNIT_KHZ) },) 556 HW_HAVE_88_([HW_FREQ_88] = { "88.2kHz", TALK_ID(88, UNIT_KHZ) },) 557 HW_HAVE_64_([HW_FREQ_64] = { "64kHz", TALK_ID(64, UNIT_KHZ) },) 558 HW_HAVE_48_([HW_FREQ_48] = { "48kHz", TALK_ID(48, UNIT_KHZ) },) 559 HW_HAVE_44_([HW_FREQ_44] = { "44.1kHz", TALK_ID(44, UNIT_KHZ) },) 560 HW_HAVE_32_([HW_FREQ_32] = { "32kHz", TALK_ID(32, UNIT_KHZ) },) 561 HW_HAVE_24_([HW_FREQ_24] = { "24kHz", TALK_ID(24, UNIT_KHZ) },) 562 HW_HAVE_22_([HW_FREQ_22] = { "22.05kHz", TALK_ID(22, UNIT_KHZ) },) 563 HW_HAVE_16_([HW_FREQ_16] = { "16kHz", TALK_ID(16, UNIT_KHZ) },) 564 HW_HAVE_12_([HW_FREQ_12] = { "12kHz", TALK_ID(12, UNIT_KHZ) },) 565 HW_HAVE_11_([HW_FREQ_11] = { "11.025kHz", TALK_ID(11, UNIT_KHZ) },) 566 HW_HAVE_8_( [HW_FREQ_8 ] = { "8kHz", TALK_ID( 8, UNIT_KHZ) },) 567}; 568 569/** 570 Shows the settings menu 571 */ 572static int settings_menu(void) 573{ 574 int selection = 0; 575 576 MENUITEM_STRINGLIST(settings_menu, ID2P(LANG_MIKMOD_SETTINGS), NULL, 577 ID2P(LANG_PANNING_SEPARATION), 578 ID2P(LANG_REVERBERATION), 579 ID2P(LANG_INTERPOLATION), 580 ID2P(LANG_SWAP_CHANNELS), 581 ID2P(LANG_MIKMOD_SURROUND), 582 ID2P(LANG_MIKMOD_HQMIXER), 583 ID2P(LANG_MIKMOD_SAMPLERATE), 584#ifdef HAVE_ADJUSTABLE_CPU_FREQ 585 ID2P(LANG_CPU_BOOST) 586#endif 587 ); 588 do 589 { 590 selection=rb->do_menu(&settings_menu,&selection, NULL, false); 591 switch(selection) 592 { 593 case 0: 594 rb->set_int("Panning Separation", "", 1, 595 &(settings.pansep), 596 NULL, 8, 0, 128, NULL ); 597 applysettings(); 598 break; 599 600 case 1: 601 rb->set_int("Reverberation", "", 1, 602 &(settings.reverb), 603 NULL, 1, 0, 15, NULL ); 604 applysettings(); 605 break; 606 607 case 2: 608 rb->set_bool("Interpolation", &(settings.interp)); 609 applysettings(); 610 break; 611 612 case 3: 613 rb->set_bool("Reverse Channels", &(settings.reverse)); 614 applysettings(); 615 break; 616 617 case 4: 618 rb->set_bool("Surround", &(settings.surround)); 619 applysettings(); 620 break; 621 622 case 5: 623 rb->set_bool("HQ Mixer", &(settings.hqmixer)); 624 applysettings(); 625 break; 626 627 case 6: 628 rb->set_option("Sample Rate", &(settings.sample_rate), RB_INT, sr_names, 629 HW_NUM_FREQ, NULL); 630 applysettings(); 631 break; 632 633#ifdef HAVE_ADJUSTABLE_CPU_FREQ 634 case 7: 635 rb->set_bool("CPU Boost", &(settings.boost)); 636 applysettings(); 637 break; 638#endif 639 640 case MENU_ATTACHED_USB: 641 return PLUGIN_USB_CONNECTED; 642 } 643 } while ( selection >= 0 ); 644 return 0; 645} 646 647/** 648 Show the main menu 649 */ 650static int main_menu(void) 651{ 652 int selection = 0; 653 int result; 654 655 MENUITEM_STRINGLIST(main_menu,ID2P(LANG_MIKMOD_MENU), NULL, 656 ID2P(LANG_SETTINGS), 657 ID2P(LANG_RETURN), 658 ID2P(LANG_MENU_QUIT)); 659 while (1) 660 { 661 switch (rb->do_menu(&main_menu,&selection, NULL, false)) 662 { 663 case 0: 664 result = settings_menu(); 665 if ( result != 0 ) return result; 666 break; 667 668 case 1: 669 return 0; 670 671 case 2: 672 return -1; 673 674 case MENU_ATTACHED_USB: 675 return PLUGIN_USB_CONNECTED; 676 677 default: 678 return 0; 679 } 680 } 681} 682 683#ifdef USETHREADS 684/* double buffering thread */ 685static void thread(void) 686{ 687 struct queue_event ev = { 688 .id = 0, 689 }; 690 691 while (1) 692 { 693 if (rb->queue_empty(&thread_q)) 694 { 695 synthbuf(); 696 rb->yield(); 697 } 698 else rb->queue_wait(&thread_q, &ev); 699 switch (ev.id) { 700 case EV_EXIT: 701 return; 702 } 703 } 704} 705#endif 706 707static void mm_errorhandler(void) 708{ 709 if (rb->global_settings->talk_menu) { 710 rb->talk_id(LANG_ERROR_FORMATSTR, true); 711 rb->talk_value_decimal(MikMod_errno, UNIT_INT, 0, true); 712 rb->talk_force_enqueue_next(); 713 } 714 /* (voiced above) */ 715 rb->splashf(HZ, rb->str(LANG_ERROR_FORMATSTR), MikMod_strerror(MikMod_errno)); 716 quit = true; 717} 718 719static void do_vscroll(int up) 720{ 721 if (up) { 722 if ( textlines-vscroll >= MAX_LINES ) 723 { 724 vscroll++; 725 screenupdated = false; 726 } 727 } else { 728 if ( vscroll > 0 ) 729 { 730 vscroll--; 731 screenupdated = false; 732 } 733 } 734} 735 736static int playfile(char* filename) 737{ 738 int button; 739 int retval = PLUGIN_OK; 740 bool changingpos = false; 741 int menureturn; 742 743 playingtime = 0; 744 745 if (rb->global_settings->talk_menu) { 746 rb->talk_id(LANG_WAIT, true); 747 rb->talk_file_or_spell(NULL, filename, NULL, true); 748 rb->talk_force_enqueue_next(); 749 } 750 rb->splashf(HZ, "%s %s", rb->str(LANG_WAIT), filename); 751 752 module = Player_Load(filename, 64, 0); 753 754 if (!module) 755 { 756 mm_errorhandler(); 757 retval = PLUGIN_ERROR; 758 quit = true; 759 } 760 else 761 { 762 display = DISPLAY_INFO; 763 Player_Start(module); 764 rb->pcmbuf_fade(false, true); 765 rb->mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, get_more, NULL, 0); 766 } 767 768#ifdef HAVE_ADJUSTABLE_CPU_FREQ 769 rb->cpu_boost(settings.boost); 770#endif 771#ifdef USETHREADS 772 rb->queue_init(&thread_q, true); 773 if ((thread_id = rb->create_thread(thread, thread_stack, 774 sizeof(thread_stack), 0, "render buffering thread" 775 IF_PRIO(, PRIORITY_PLAYBACK) 776 IF_COP(, CPU))) == 0) 777 { 778 rb->splashf(HZ, ID2P(LANG_ERROR_FORMATSTR), "Cannot create thread!"); 779 return PLUGIN_ERROR; 780 } 781#endif 782 783 while (!quit && Player_Active() && retval == PLUGIN_OK) 784 { 785#if !defined(SYNC) && !defined(USETHREADS) 786 synthbuf(); 787#endif 788 switch (display) 789 { 790 case DISPLAY_SAMPLE: 791 showsamples(); 792 break; 793 case DISPLAY_INST: 794 showinstruments(); 795 break; 796 case DISPLAY_COMMENTS: 797 showcomments(); 798 break; 799 default: 800 showinfo(); 801 } 802 803 rb->yield(); 804 805 /* Prevent idle poweroff */ 806 rb->reset_poweroff_timer(); 807 808 button = rb->get_action(CONTEXT_WPS, TIMEOUT_NOBLOCK); 809 switch (button) 810 { 811 case ACTION_WPS_VOLUP: 812 if ( display != DISPLAY_INFO ) 813 { 814#ifdef HAVE_SCROLLWHEEL 815 do_vscroll(1); 816#else 817 do_vscroll(0); 818#endif 819 break; 820 } 821 822 rb->adjust_volume(1); 823 break; 824 825 case ACTION_WPS_VOLDOWN: 826 if ( display != DISPLAY_INFO ) 827 { 828#ifdef HAVE_SCROLLWHEEL 829 do_vscroll(0); 830#else 831 do_vscroll(1); 832#endif 833 break; 834 } 835 836 rb->adjust_volume(-1); 837 break; 838 839 case ACTION_WPS_SKIPPREV: 840 if(entries>1 && !changingpos) 841 { 842 if ((int)(module->sngtime >> 10) > 2) 843 { 844 Player_SetPosition(0); 845 module->sngtime = 0; 846 } 847 else { 848 retval = change_filename(DIR_PREV); 849 } 850 } 851 else 852 { 853 changingpos = false; 854 } 855 break; 856 case ACTION_WPS_SEEKBACK: 857 if ( display != DISPLAY_INFO ) 858 { 859 if ( hscroll > 0 ) 860 { 861 hscroll--; 862 screenupdated = false; 863 } 864 break; 865 } 866 Player_PrevPosition(); 867 changingpos = true; 868 break; 869 870 case ACTION_WPS_SKIPNEXT: 871 if(entries>1 && !changingpos) 872 { 873 retval = change_filename(DIR_NEXT); 874 } 875 else 876 { 877 changingpos = false; 878 } 879 break; 880 case ACTION_WPS_SEEKFWD: 881 if ( display != DISPLAY_INFO ) 882 { 883 hscroll++; 884 screenupdated = false; 885 break; 886 } 887 Player_NextPosition(); 888 changingpos = true; 889 break; 890 891 case ACTION_WPS_PLAY: 892 rb->mixer_channel_play_pause(PCM_MIXER_CHAN_PLAYBACK, Player_Paused()); 893 Player_TogglePause(); 894 break; 895 896 case ACTION_WPS_BROWSE: 897 changedisplay(); 898 break; 899 900 case ACTION_WPS_MENU: 901 menureturn = main_menu(); 902 if ( menureturn != 0 ) 903 { 904 quit = true; 905 if ( menureturn == PLUGIN_USB_CONNECTED ) 906 { 907 retval = menureturn; 908 } 909 } 910 rb->lcd_setfont(FONT_UI); 911 screenupdated = false; 912 break; 913 914 case ACTION_WPS_STOP: 915 quit = true; 916 break; 917 918 default: 919 if (rb->default_event_handler(button) == SYS_USB_CONNECTED) 920 { 921 quit = true; 922 retval = PLUGIN_USB_CONNECTED; 923 } 924 } 925 } 926 927#ifdef USETHREADS 928 rb->queue_post(&thread_q, EV_EXIT, 0); 929 rb->thread_wait(thread_id); 930 rb->queue_delete(&thread_q); 931#endif 932#ifdef HAVE_ADJUSTABLE_CPU_FREQ 933 rb->cpu_boost(false); 934#endif 935 936 Player_Stop(); 937 Player_Free(module); 938 939 memset(gmbuf, '\0', sizeof(gmbuf)); 940 941 if ( retval == PLUGIN_OK && entries > 1 && !quit ) 942 { 943 retval = change_filename(DIR_NEXT); 944 } 945 946 return retval; 947} 948 949/* 950* Plugin entry point 951* 952*/ 953enum plugin_status plugin_start(const void* parameter) 954{ 955 enum plugin_status retval; 956 int orig_samplerate = rb->mixer_get_frequency(); 957 958 if (parameter == NULL) 959 { 960 rb->splash(HZ*2, ID2P(LANG_NO_FILES)); 961 return PLUGIN_OK; 962 } 963 964 rb->lcd_getstringsize("A", NULL, &font_h); 965 966 rb->talk_force_shutup(); 967 rb->pcm_play_stop(); 968#if INPUT_SRC_CAPS != 0 969 /* Select playback */ 970 rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); 971 rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); 972#endif 973 974 audio_buffer = rb->plugin_get_audio_buffer((size_t *)&audio_buffer_free); 975 976 rb->strcpy(np_file, parameter); 977 get_mod_list(); 978 if(!entries) { 979 return PLUGIN_ERROR; 980 } 981 982 //add_pool(audio_buffer, audio_buffer_free); 983 init_memory_pool(audio_buffer_free, audio_buffer); 984 985 MikMod_RegisterDriver(&drv_nos); 986 MikMod_RegisterAllLoaders(); 987 MikMod_RegisterErrorHandler(mm_errorhandler); 988 989 configfile_load(MIKMOD_CONFIGFILE, config, 990 ARRAYLEN(config), MIKMOD_SETTINGS_MINVERSION); 991 rb->memcpy(&old_settings, &settings, sizeof (settings)); 992 993 /* If there's no configured rate, use the default */ 994 if (settings.sample_rate == -1) { 995 int i; 996 for (i = 0 ; i < HW_NUM_FREQ ; i++) { 997 if (rb->hw_freq_sampr[i] == SAMPLE_RATE) { 998 settings.sample_rate = i; 999 break; 1000 } 1001 } 1002 if (settings.sample_rate == -1) { 1003 settings.sample_rate = HW_NUM_FREQ -1; 1004 } 1005 } 1006 1007 applysettings(); 1008 1009 if (MikMod_Init("")) 1010 { 1011 mm_errorhandler(); 1012 return PLUGIN_ERROR; 1013 } 1014 1015 inited = 1; 1016 1017 do 1018 { 1019 retval = playfile(np_file); 1020 } while (retval == PLUGIN_NEWSONG); 1021 1022 MikMod_Exit(); 1023 1024 rb->pcmbuf_fade(false, false); 1025 rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); 1026 rb->mixer_set_frequency(orig_samplerate); 1027 1028 if (retval == PLUGIN_OK) 1029 { 1030 if (rb->memcmp(&settings, &old_settings, sizeof (settings))) 1031 { 1032 configfile_save(MIKMOD_CONFIGFILE, config, 1033 ARRAYLEN(config), MIKMOD_SETTINGS_MINVERSION); 1034 } 1035 } 1036 1037 destroy_memory_pool(audio_buffer); 1038 1039 return retval; 1040}