A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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}