summaryrefslogtreecommitdiff
path: root/apps/plugins/mikmod/mikmod.c
diff options
context:
space:
mode:
authorFrank Gevaerts <frank@gevaerts.be>2010-12-12 15:03:30 +0000
committerFrank Gevaerts <frank@gevaerts.be>2010-12-12 15:03:30 +0000
commit26f2bfde03420edad4de1f22cb3d515dc063b20d (patch)
tree4a8c4abaf4795f38da70a4657c1a0fb3ba9debeb /apps/plugins/mikmod/mikmod.c
parentd192bdf11e06e50645ecb5726658d4b691480a9a (diff)
downloadrockbox-26f2bfde03420edad4de1f22cb3d515dc063b20d.zip
rockbox-26f2bfde03420edad4de1f22cb3d515dc063b20d.tar.gz
rockbox-26f2bfde03420edad4de1f22cb3d515dc063b20d.tar.bz2
rockbox-26f2bfde03420edad4de1f22cb3d515dc063b20d.tar.xz
Add MikMod plugin, ported by Jason Yu, with some minor work by Craig Mann and William Peters (FS#8806)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28810 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/mikmod/mikmod.c')
-rw-r--r--apps/plugins/mikmod/mikmod.c943
1 files changed, 943 insertions, 0 deletions
diff --git a/apps/plugins/mikmod/mikmod.c b/apps/plugins/mikmod/mikmod.c
new file mode 100644
index 0000000..fe17683
--- /dev/null
+++ b/apps/plugins/mikmod/mikmod.c
@@ -0,0 +1,943 @@
+#define NO_MMSUPP_DEFINES
+
+#include "plugin.h"
+#include "lib/configfile.h"
+#include "mikmod.h"
+
+
+#undef SYNC
+#ifdef SIMULATOR
+#define SYNC
+#elif NUM_CORES > 1
+#define USETHREADS
+#endif
+
+#define MAX_CHARS LCD_WIDTH/6
+#define MAX_LINES LCD_HEIGHT/8
+#define LINE_LENGTH 80
+
+#define DIR_PREV 1
+#define DIR_NEXT -1
+#define DIR_NONE 0
+
+#define PLUGIN_NEWSONG 10
+
+/* Persistent configuration */
+#define MIKMOD_CONFIGFILE "mikmod.cfg"
+#define MIKMOD_SETTINGS_MINVERSION 1
+#define MIKMOD_SETTINGS_VERSION 1
+
+#ifdef USETHREADS
+#define EV_EXIT 9999
+#define THREAD_STACK_SIZE DEFAULT_STACK_SIZE + 0x200
+static unsigned int thread_id;
+static struct event_queue thread_q;
+/* use long for aligning */
+unsigned long thread_stack[THREAD_STACK_SIZE/sizeof(long)];
+#endif
+
+/* the current full file name */
+static char np_file[MAX_PATH];
+static int curfile = 0, direction = DIR_NEXT, entries = 0;
+
+/* list of the mod files */
+static char **file_pt;
+
+
+/* The MP3 audio buffer which we will use as heap memory */
+static unsigned char* audio_buffer;
+/* amount of bytes left in audio_buffer */
+static size_t audio_buffer_free;
+
+
+/* The rockbox plugin interface */
+MEM_FUNCTION_WRAPPERS;
+
+bool quit;
+int playingtime IBSS_ATTR;
+MODULE *module IBSS_ATTR;
+char gmbuf[BUF_SIZE*NBUF];
+
+
+int textlines;
+int vscroll = 0;
+int hscroll = 0;
+bool screenupdated = false;
+
+enum {
+ DISPLAY_INFO = 0,
+ DISPLAY_SAMPLE,
+ DISPLAY_INST,
+ DISPLAY_COMMENTS,
+} display;
+
+
+/*
+* strncat wrapper
+*/
+char* mmsupp_strncat(char *s1, const char *s2, size_t n)
+{
+ char *s = s1;
+ /* Loop over the data in s1. */
+ while (*s != '\0')
+ s++;
+ /* s now points to s1's trailing null character, now copy
+ up to n bytes from s2 into s1 stopping if a null character
+ is encountered in s2.
+ It is not safe to use strncpy here since it copies EXACTLY n
+ characters, NULL padding if necessary. */
+ while (n != 0 && (*s = *s2++) != '\0')
+ {
+ n--;
+ s++;
+ }
+ if (*s != '\0')
+ *s = '\0';
+ return s1;
+}
+
+/*
+* sprintf wrapper
+*/
+int mmsupp_sprintf(char *buf, const char *fmt, ... )
+{
+ bool ok;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ok = rb->vsnprintf(buf, LINE_LENGTH, fmt, ap);
+ va_end(ap);
+
+ return ok;
+}
+
+/*
+* printf wrapper
+*/
+void mmsupp_printf(const char *fmt, ...)
+{
+ static int p_xtpt = 0;
+ char p_buf[LINE_LENGTH];
+ bool ok;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ok = rb->vsnprintf(p_buf, sizeof(p_buf), fmt, ap);
+ va_end(ap);
+
+ int i=0;
+
+ /* Device LCDs display newlines funny. */
+ for(i=0; p_buf[i]!=0; i++)
+ if(p_buf[i] == '\n')
+ p_buf[i] = ' ';
+
+ rb->lcd_putsxy(1, p_xtpt, (unsigned char *)p_buf);
+ rb->lcd_update();
+
+ p_xtpt += 8;
+ if(p_xtpt > LCD_HEIGHT-8)
+ {
+ p_xtpt = 0;
+ rb->lcd_clear_display();
+ }
+}
+
+
+/************************* File Access ***************************/
+
+/* support function for qsort() */
+static int compare(const void* p1, const void* p2)
+{
+ return rb->strcasecmp(*((char **)p1), *((char **)p2));
+}
+
+bool mod_ext(const char ext[])
+{
+ if(!ext)
+ return false;
+ if(!rb->strcasecmp(ext,".669") ||
+ !rb->strcasecmp(ext,".amf") ||
+ !rb->strcasecmp(ext,".asy") ||
+ !rb->strcasecmp(ext,".dsm") ||
+ !rb->strcasecmp(ext,".far") ||
+ !rb->strcasecmp(ext,".gdm") ||
+ !rb->strcasecmp(ext,".gt2") ||
+ !rb->strcasecmp(ext,".imf") ||
+ !rb->strcasecmp(ext,".it") ||
+ !rb->strcasecmp(ext,".m15") ||
+ !rb->strcasecmp(ext,".med") ||
+ !rb->strcasecmp(ext,".mod") ||
+ !rb->strcasecmp(ext,".mtm") ||
+ !rb->strcasecmp(ext,".okt") ||
+ !rb->strcasecmp(ext,".s3m") ||
+ !rb->strcasecmp(ext,".stm") ||
+ !rb->strcasecmp(ext,".stx") ||
+ !rb->strcasecmp(ext,".ult") ||
+ !rb->strcasecmp(ext,".uni") ||
+ !rb->strcasecmp(ext,".xm") )
+ return true;
+ else
+ return false;
+}
+
+/*Read directory contents for scrolling. */
+void get_mod_list(void)
+{
+ struct tree_context *tree = rb->tree_get_context();
+ struct entry *dircache = tree->dircache;
+ int i;
+ char *pname;
+
+ file_pt = (char **) audio_buffer;
+
+ /* Remove path and leave only the name.*/
+ pname = rb->strrchr(np_file,'/');
+ pname++;
+
+ for (i = 0; i < tree->filesindir && audio_buffer_free > sizeof(char**); i++)
+ {
+ if (!(dircache[i].attr & ATTR_DIRECTORY)
+ && mod_ext(rb->strrchr(dircache[i].name,'.')))
+ {
+ file_pt[entries] = dircache[i].name;
+ /* Set Selected File. */
+ if (!rb->strcmp(file_pt[entries], pname))
+ curfile = entries;
+ entries++;
+
+ audio_buffer += (sizeof(char**));
+ audio_buffer_free -= (sizeof(char**));
+ }
+ }
+}
+
+int change_filename(int direct)
+{
+ bool file_erased = (file_pt[curfile] == NULL);
+ direction = direct;
+
+ curfile += (direct == DIR_PREV? entries - 1: 1);
+ if (curfile >= entries)
+ curfile -= entries;
+
+ if (file_erased)
+ {
+ /* remove 'erased' file names from list. */
+ int count, i;
+ for (count = i = 0; i < entries; i++)
+ {
+ if (curfile == i)
+ curfile = count;
+ if (file_pt[i] != NULL)
+ file_pt[count++] = file_pt[i];
+ }
+ entries = count;
+ }
+
+ if (entries == 0)
+ {
+ rb->splash(HZ, "No supported files");
+ return PLUGIN_ERROR;
+ }
+
+ rb->strcpy(rb->strrchr(np_file, '/')+1, file_pt[curfile]);
+
+ return PLUGIN_NEWSONG;
+}
+
+/*****************************************************************************
+* Playback
+*/
+
+bool swap = false;
+bool lastswap = true;
+
+static inline void synthbuf(void)
+{
+ char *outptr;
+
+#ifndef SYNC
+ if (lastswap == swap) return;
+ lastswap = swap;
+
+ outptr = (swap ? gmbuf : gmbuf + BUF_SIZE);
+#else
+ outptr = gmbuf;
+#endif
+
+ VC_WriteBytes(outptr, BUF_SIZE);
+}
+
+void get_more(unsigned char** start, size_t* size)
+{
+#ifndef SYNC
+ if (lastswap != swap)
+ {
+ //printf("Buffer miss!");
+ }
+
+#else
+ synthbuf();
+#endif
+
+ *size = BUF_SIZE;
+#ifndef SYNC
+ *start = (unsigned char*)((swap ? gmbuf : gmbuf + BUF_SIZE));
+ swap = !swap;
+#else
+ *start = (unsigned char*)(gmbuf);
+#endif
+}
+
+void showinfo()
+{
+ char statustext[LINE_LENGTH];
+
+ if (!module)
+ {
+ return;
+ }
+
+ rb->lcd_clear_display();
+
+ playingtime = (int)(module->sngtime >> 10);
+ sprintf(statustext, "Name: %s", module->songname);
+ rb->lcd_putsxy(1, 1, statustext);
+ sprintf(statustext, "Type: %s", module->modtype);
+ rb->lcd_putsxy(1, 11, statustext);
+
+ sprintf(statustext, "Samples: %d", module->numsmp);
+ rb->lcd_putsxy(1, 21, statustext);
+
+ if ( module->flags & UF_INST )
+ {
+ sprintf(statustext, "Instruments: %d", module->numins);
+ rb->lcd_putsxy(1, 31, statustext);
+ }
+
+ sprintf(statustext, "pat: %03d/%03d %2.2X",
+ module->sngpos, module->numpos - 1, module->patpos);
+ rb->lcd_putsxy(1, 51, statustext);
+
+ sprintf(statustext, "spd: %d/%d",
+ module->sngspd, module->bpm);
+ rb->lcd_putsxy(1, 61, statustext);
+
+ sprintf(statustext, "vol: %ddB", rb->global_settings->volume);
+ rb->lcd_putsxy(1, 71, statustext);
+
+ sprintf(statustext, "time: %d:%02d",
+ (playingtime / 60) % 60, playingtime % 60);
+ rb->lcd_putsxy(1, 81, statustext);
+
+ if (module->flags & UF_NNA)
+ {
+ sprintf(statustext, "chn: %d/%d+%d->%d",
+ module->realchn, module->numchn,
+ module->totalchn - module->realchn,
+ module->totalchn);
+ }
+ else
+ {
+ sprintf(statustext, "chn: %d/%d",
+ module->realchn, module->numchn);
+ }
+ rb->lcd_putsxy(0, 91, statustext);
+
+ rb->lcd_update();
+}
+
+void showsamples()
+{
+ int i, j;
+ char statustext[LINE_LENGTH];
+
+ if ( screenupdated )
+ {
+ return;
+ }
+ rb->lcd_clear_display();
+ for( i=0; i<MAX_LINES && i+vscroll<module->numsmp; i++ )
+ {
+ sprintf(statustext, "%02d %s", i+vscroll+1, module->samples[i+vscroll].samplename);
+ rb->lcd_putsxy(1, 1+(8*i), statustext);
+ }
+ rb->lcd_update();
+ screenupdated = true;
+}
+
+void showinstruments()
+{
+ int i, j;
+ char statustext[LINE_LENGTH];
+
+ if ( screenupdated )
+ {
+ return;
+ }
+ rb->lcd_clear_display();
+ for( i=0; i<MAX_LINES && i+vscroll<module->numins; i++ )
+ {
+ sprintf(statustext, "%02d %s", i+vscroll+1, module->instruments[i+vscroll].insname);
+ rb->lcd_putsxy(1, 1+(8*i), statustext);
+ }
+ rb->lcd_update();
+ screenupdated = true;
+}
+
+void showcomments()
+{
+ int i, j=0, k=0, l;
+ char statustext[LINE_LENGTH];
+
+ if ( screenupdated )
+ {
+ return;
+ }
+ rb->lcd_clear_display();
+
+ for(i=0; module->comment[i]!='\0'; i++)
+ {
+ if(module->comment[i] != '\n')
+ {
+ statustext[j] = module->comment[i];
+ j++;
+ }
+
+ if(module->comment[i] == '\n' || j>LINE_LENGTH-1)
+ {
+ rb->lcd_putsxy(1-(6*hscroll), 1+(8*k)-(8*vscroll), statustext);
+ for( l=0; l<LINE_LENGTH; l++ )
+ {
+ statustext[l] = 0;
+ }
+ k++;
+ j=0;
+ }
+ }
+ if (j>0)
+ {
+ rb->lcd_putsxy(1-(6*hscroll), 1+(8*k)-(8*vscroll), statustext);
+ }
+
+ rb->lcd_update();
+ screenupdated = true;
+}
+
+int changedisplay()
+{
+ display = (display+1) % 4;
+
+ if (display == DISPLAY_SAMPLE)
+ {
+ textlines = module->numsmp;
+ }
+
+ if (display == DISPLAY_INST)
+ {
+ if ( module->flags & UF_INST )
+ {
+ textlines = module->numins;
+ }
+ else
+ {
+ display = DISPLAY_COMMENTS;
+ }
+ }
+
+ if (display == DISPLAY_COMMENTS)
+ {
+ if (module->comment)
+ {
+ textlines = 100;
+ }
+ else
+ {
+ display = DISPLAY_INFO;
+ }
+ }
+ screenupdated = false;
+ vscroll = 0;
+ hscroll = 0;
+}
+
+struct mikmod_settings
+{
+ int pansep;
+ int reverb;
+ bool interp;
+ bool reverse;
+ bool surround;
+ bool boost;
+};
+
+static struct mikmod_settings settings =
+{
+ 128,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1
+};
+
+static struct mikmod_settings old_settings;
+
+static struct configdata config[] =
+{
+ { TYPE_INT, 0, 128, { .int_p = &settings.pansep }, "Panning Separation", NULL},
+ { TYPE_INT, 0, 15, { .int_p = &settings.reverb }, "Reverberation", NULL},
+ { TYPE_BOOL, 0, 1, { .bool_p = &settings.interp }, "Interpolation", NULL},
+ { TYPE_BOOL, 0, 1, { .bool_p = &settings.reverse }, "Reverse Channels", NULL},
+ { TYPE_BOOL, 0, 1, { .bool_p = &settings.surround }, "Surround", NULL},
+ { TYPE_BOOL, 0, 1, { .bool_p = &settings.boost }, "CPU Boost", NULL},
+};
+
+void applysettings()
+{
+ md_pansep = settings.pansep;
+ md_reverb = settings.reverb;
+ md_mode = DMODE_STEREO | DMODE_16BITS | DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX;
+ if ( settings.interp )
+ {
+ md_mode |= DMODE_INTERP;
+ }
+ if ( settings.reverse )
+ {
+ md_mode |= DMODE_REVERSE;
+ }
+ if ( settings.surround )
+ {
+ md_mode |= DMODE_SURROUND;
+ }
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ if ( Player_Active() )
+ {
+ rb->cpu_boost(settings.boost);
+ }
+#endif
+}
+
+/**
+ Shows the settings menu
+ */
+int settings_menu(void)
+{
+ int selection = 0;
+ bool old_val;
+
+ MENUITEM_STRINGLIST(settings_menu, "Mikmod Settings", NULL, "Panning Separation",
+ "Reverberation", "Interpolation", "Reverse Channels", "Surround",
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ "CPU Boost"
+#endif
+ );
+
+ do
+ {
+ selection=rb->do_menu(&settings_menu,&selection, NULL, false);
+ switch(selection)
+ {
+ case 0:
+ rb->set_int("Panning Separation", "", 1,
+ &(settings.pansep),
+ NULL, 8, 0, 128, NULL );
+ applysettings();
+ break;
+
+ case 1:
+ rb->set_int("Reverberation", "", 1,
+ &(settings.reverb),
+ NULL, 1, 0, 15, NULL );
+ applysettings();
+ break;
+
+ case 2:
+ rb->set_bool("Interpolation", &(settings.interp));
+ applysettings();
+ break;
+
+ case 3:
+ rb->set_bool("Reverse Channels", &(settings.reverse));
+ applysettings();
+ break;
+
+ case 4:
+ rb->set_bool("Surround", &(settings.surround));
+ applysettings();
+ break;
+
+ case 5:
+ rb->set_bool("CPU Boost", &(settings.boost));
+ applysettings();
+ break;
+
+ case MENU_ATTACHED_USB:
+ return PLUGIN_USB_CONNECTED;
+ }
+ } while ( selection >= 0 );
+ return 0;
+}
+
+/**
+ Show the main menu
+ */
+int main_menu(void)
+{
+ int selection = 0;
+ int result;
+
+ MENUITEM_STRINGLIST(main_menu,"Mikmod Main Menu",NULL,
+ "Settings", "Return", "Quit");
+ while (1)
+ {
+ switch (rb->do_menu(&main_menu,&selection, NULL, false))
+ {
+ case 0:
+ result = settings_menu();
+ if ( result != 0 ) return result;
+ break;
+
+ case 1:
+ return 0;
+
+ case 2:
+ return -1;
+
+ case MENU_ATTACHED_USB:
+ return PLUGIN_USB_CONNECTED;
+
+ default:
+ return 0;
+ }
+ }
+}
+
+#ifdef USETHREADS
+/* double buffering thread */
+void thread(void)
+{
+ struct queue_event ev;
+
+ while (1)
+ {
+ synthbuf();
+ rb->queue_wait_w_tmo(&thread_q, &ev, HZ/20);
+ switch (ev.id) {
+ case EV_EXIT:
+ return;
+ }
+ }
+}
+#endif
+
+void mm_errorhandler(void)
+{
+ rb->splashf(HZ, "%s", MikMod_strerror(MikMod_errno));
+ quit = true;
+}
+
+int playfile(char* filename)
+{
+ int vol = 0;
+ int button;
+ int retval = PLUGIN_OK;
+ bool changingpos = false;
+ int menureturn;
+
+ playingtime = 0;
+
+ rb->splashf(HZ, "Loading %s", filename);
+
+ module = Player_Load(filename, 64, 0);
+
+ if (!module)
+ {
+ rb->splashf(HZ, "%s", MikMod_strerror(MikMod_errno));
+ retval = PLUGIN_ERROR;
+ quit = true;
+ }
+ else
+ {
+ display = DISPLAY_INFO;
+ Player_Start(module);
+ rb->pcm_play_data(&get_more, NULL, 0);
+ }
+
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ if ( settings.boost )
+ rb->cpu_boost(true);
+#endif
+#ifdef USETHREADS
+ rb->queue_init(&thread_q, true);
+ if ((thread_id = rb->create_thread(thread, thread_stack,
+ sizeof(thread_stack), 0, "render buffering thread"
+ IF_PRIO(, PRIORITY_PLAYBACK)
+ IF_COP(, CPU))) == 0)
+ {
+ rb->splash(HZ, "Cannot create thread!");
+ return PLUGIN_ERROR;
+ }
+#endif
+
+ while (!quit && Player_Active() && retval == PLUGIN_OK)
+ {
+#if !defined(SYNC) && !defined(USETHREADS)
+ synthbuf();
+#endif
+ switch (display)
+ {
+ case DISPLAY_SAMPLE:
+ showsamples();
+ break;
+ case DISPLAY_INST:
+ showinstruments();
+ break;
+ case DISPLAY_COMMENTS:
+ showcomments();
+ break;
+ default:
+ showinfo();
+ }
+
+ rb->yield();
+
+ /* Prevent idle poweroff */
+ rb->reset_poweroff_timer();
+
+ button = rb->get_action(CONTEXT_WPS, TIMEOUT_NOBLOCK);
+ switch (button)
+ {
+ case ACTION_WPS_VOLUP:
+ if ( display != DISPLAY_INFO )
+ {
+ if ( textlines-vscroll >= MAX_LINES )
+ {
+ vscroll++;
+ screenupdated = false;
+ }
+ break;
+ }
+ vol = rb->global_settings->volume;
+ if (vol < rb->sound_max(SOUND_VOLUME))
+ {
+ vol++;
+ rb->sound_set(SOUND_VOLUME, vol);
+ rb->global_settings->volume = vol;
+ }
+ break;
+
+ case ACTION_WPS_VOLDOWN:
+ if ( display != DISPLAY_INFO )
+ {
+ if ( vscroll > 0 )
+ {
+ vscroll--;
+ screenupdated = false;
+ }
+ break;
+ }
+ vol = rb->global_settings->volume;
+ if (vol > rb->sound_min(SOUND_VOLUME))
+ {
+ vol--;
+ rb->sound_set(SOUND_VOLUME, vol);
+ rb->global_settings->volume = vol;
+ }
+ break;
+
+ case ACTION_WPS_SKIPPREV:
+ if(entries>1 && !changingpos)
+ {
+ if ((int)(module->sngtime >> 10) > 2)
+ {
+ Player_SetPosition(0);
+ module->sngtime = 0;
+ }
+ else {
+ retval = change_filename(DIR_PREV);
+ }
+ }
+ else
+ {
+ changingpos = false;
+ }
+ break;
+ case ACTION_WPS_SEEKBACK:
+ if ( display != DISPLAY_INFO )
+ {
+ if ( hscroll > 0 )
+ {
+ hscroll--;
+ screenupdated = false;
+ }
+ break;
+ }
+ Player_PrevPosition();
+ changingpos = true;
+ break;
+
+ case ACTION_WPS_SKIPNEXT:
+ if(entries>1 && !changingpos)
+ {
+ retval = change_filename(DIR_NEXT);
+ }
+ else
+ {
+ changingpos = false;
+ }
+ break;
+ case ACTION_WPS_SEEKFWD:
+ if ( display != DISPLAY_INFO )
+ {
+ hscroll++;
+ screenupdated = false;
+ break;
+ }
+ Player_NextPosition();
+ changingpos = true;
+ break;
+
+ case ACTION_WPS_PLAY:
+ if(!Player_Paused())
+ {
+ rb->pcm_play_stop();
+ }
+ else
+ {
+ rb->pcm_play_data(&get_more, NULL, 0);
+ }
+ Player_TogglePause();
+ break;
+
+ case ACTION_WPS_BROWSE:
+ changedisplay();
+ break;
+
+ case ACTION_WPS_MENU:
+ menureturn = main_menu();
+ if ( menureturn != 0 )
+ {
+ quit = true;
+ if ( menureturn == PLUGIN_USB_CONNECTED )
+ {
+ retval = menureturn;
+ }
+ }
+ rb->lcd_setfont(0);
+ screenupdated = false;
+ break;
+
+ case ACTION_WPS_STOP:
+ quit = true;
+ break;
+
+ default:
+ if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
+ {
+ quit = true;
+ retval = PLUGIN_USB_CONNECTED;
+ }
+ }
+ }
+
+#ifdef USETHREADS
+ rb->queue_post(&thread_q, EV_EXIT, 0);
+ rb->thread_wait(thread_id);
+ rb->queue_delete(&thread_q);
+#endif
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ if ( settings.boost )
+ rb->cpu_boost(false);
+#endif
+
+ Player_Stop();
+ Player_Free(module);
+
+ memset(gmbuf, '\0', sizeof(gmbuf));
+
+ if ( retval == PLUGIN_OK && entries > 1 && !quit )
+ {
+ retval = change_filename(DIR_NEXT);
+ }
+
+ return retval;
+}
+
+/*
+* Plugin entry point
+*
+*/
+enum plugin_status plugin_start(const void* parameter)
+{
+ enum plugin_status retval;
+
+ if (parameter == NULL)
+ {
+ rb->splash(HZ*2, " Play .mod, .it, .s3m, .xm file ");
+ return PLUGIN_OK;
+ }
+
+ rb->lcd_setfont(0);
+
+ rb->pcm_play_stop();
+#if INPUT_SRC_CAPS != 0
+ /* Select playback */
+ rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
+ rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
+#endif
+ rb->pcm_set_frequency(SAMPLE_RATE);
+
+ audio_buffer = rb->plugin_get_audio_buffer((size_t *)&audio_buffer_free);
+
+ rb->strcpy(np_file, parameter);
+ get_mod_list();
+ if(!entries) {
+ return PLUGIN_ERROR;
+ }
+
+ //add_pool(audio_buffer, audio_buffer_free);
+ init_memory_pool(audio_buffer_free, audio_buffer);
+
+ MikMod_RegisterDriver(&drv_nos);
+ MikMod_RegisterAllLoaders();
+ MikMod_RegisterErrorHandler(mm_errorhandler);
+
+ md_mixfreq = SAMPLE_RATE;
+
+ configfile_load(MIKMOD_CONFIGFILE, config,
+ ARRAYLEN(config), MIKMOD_SETTINGS_MINVERSION);
+ rb->memcpy(&old_settings, &settings, sizeof (settings));
+ applysettings();
+
+ if (MikMod_Init(""))
+ {
+ rb->splashf(HZ, "%s", MikMod_strerror(MikMod_errno));
+ return PLUGIN_ERROR;
+ }
+
+ do
+ {
+ retval = playfile(np_file);
+ } while (retval == PLUGIN_NEWSONG);
+
+ MikMod_Exit();
+
+ rb->pcm_play_stop();
+ rb->pcm_set_frequency(HW_SAMPR_DEFAULT);
+
+ if (retval == PLUGIN_OK)
+ {
+ rb->splash(0, "Saving Settings");
+ if (rb->memcmp(&settings, &old_settings, sizeof (settings)))
+ {
+ configfile_save(MIKMOD_CONFIGFILE, config,
+ ARRAYLEN(config), MIKMOD_SETTINGS_MINVERSION);
+ }
+ }
+
+ destroy_memory_pool(audio_buffer);
+
+ return retval;
+}