Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / mikmod.c
diff --git a/apps/plugins/mikmod/mikmod.c b/apps/plugins/mikmod/mikmod.c
new file mode 100644 (file)
index 0000000..3ec91bd
--- /dev/null
@@ -0,0 +1,348 @@
+/***************************************************************************\r
+ *             __________               __   ___.\r
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___\r
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /\r
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <\r
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \\r
+ *                     \/            \/     \/    \/            \/\r
+ *\r
+ *\r
+ * All files in this archive are subject to the GNU General Public License.\r
+ * See the file COPYING in the source tree root for full license agreement.\r
+ *\r
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY\r
+ * KIND, either express or implied.\r
+ *\r
+ ****************************************************************************/\r
+#include "plugin.h"\r
+#include "mikmod_build.h"\r
+\r
+PLUGIN_HEADER\r
+\r
+struct plugin_api *rb;\r
+\r
+MODULE *module;\r
+\r
+#define MAXPCMBUFFERS 16 // Do NOT set this to a value lower than 2 or playback will break\r
+#define PCMBUFFERSIZE 65536\r
+\r
+char *pcmbufs[MAXPCMBUFFERS];\r
+int bufferlen[MAXPCMBUFFERS];\r
+int buffercount = 0;\r
+\r
+int curbuff = 0;\r
+int lastbuff = 0;\r
+\r
+size_t buffrendered = 0;\r
+size_t buffplayed = 0;\r
+size_t buff_last = ~0;\r
+\r
+int vol, minvol, maxvol;\r
+int lastpower = 0, lastvol = 0;\r
+int lastbutton = 0;\r
+\r
+char sbuf[32];\r
+\r
+int mallocbufsize;\r
+char *mbuf;\r
+\r
+\r
+void mikmod_prepare_malloc(char *buff, int bufsize);\r
+long mikmod_get_malloc_usage(void);\r
+void* mikmod_malloc(size_t size);\r
+\r
+\r
+\r
+int FindLastOccurrenceChar(char *string, char chr)\r
+{\r
+       int i = 0;\r
+       //int LastOcc = -1;\r
+       int LastOcc = 0;\r
+\r
+       while (string[i] != 0)\r
+       {\r
+               if (string[i] == chr)\r
+               {\r
+                       LastOcc = i;\r
+               }\r
+\r
+               i++;\r
+       }\r
+\r
+       return LastOcc;\r
+}\r
+\r
+\r
+void get_more(unsigned char **start, size_t *size)\r
+       {\r
+               if (curbuff >= (buffercount - 1))\r
+               {\r
+                       curbuff = 0;\r
+               }\r
+               else\r
+               {\r
+                       curbuff++;\r
+               }\r
+               *start = pcmbufs[curbuff];\r
+               *size  = bufferlen[curbuff];\r
+               buffplayed++;\r
+       }\r
+\r
+\r
+void checkbutton(void)\r
+{\r
+       lastbutton = rb->button_get(false);\r
+       if (lastbutton & BUTTON_MENU)\r
+       {\r
+               return;\r
+       }\r
+\r
+       switch (lastbutton)\r
+       {\r
+               case BUTTON_SCROLL_FWD:\r
+               case (BUTTON_SCROLL_FWD | BUTTON_REPEAT):\r
+                       vol = rb->global_settings->volume;\r
+\r
+                       if (vol < maxvol)\r
+                       {\r
+                               vol++;\r
+                               rb->sound_set(SOUND_VOLUME, vol);\r
+                               rb->global_settings->volume = vol;\r
+                       }\r
+                       break;\r
+\r
+               case BUTTON_SCROLL_BACK:\r
+               case (BUTTON_SCROLL_BACK | BUTTON_REPEAT):\r
+                       vol = rb->global_settings->volume;\r
+\r
+                       if (vol > minvol)\r
+                       {\r
+                               vol--;\r
+                               rb->sound_set(SOUND_VOLUME, vol);\r
+                               rb->global_settings->volume = vol;\r
+                       }\r
+                       break;\r
+\r
+               case (BUTTON_PLAY | BUTTON_REL):\r
+\r
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ\r
+       rb->cpu_boost(false); // Just in case we got called from inside the render loop\r
+#endif\r
+                       rb->pcm_play_pause(false);\r
+                       do /* code from mpegplayer, *very* simple unpause loop */\r
+                       {\r
+                               lastbutton = rb->button_get(true);\r
+                               if (lastbutton == BUTTON_MENU)\r
+                               {\r
+                                       return;\r
+                               }\r
+                       } while (lastbutton != (BUTTON_PLAY | BUTTON_REL));\r
+                       rb->pcm_play_pause(true);\r
+                       break;\r
+\r
+               default:\r
+                       if(rb->default_event_handler(lastbutton) == SYS_USB_CONNECTED)\r
+                       {\r
+                               return;\r
+                       }\r
+       } /* switch(button) */\r
+\r
+       if ((rb->global_settings->volume != lastvol) || (rb->battery_level() != lastpower))\r
+       {\r
+               lastpower = rb->battery_level();\r
+               lastvol = rb->global_settings->volume;\r
+               \r
+               rb->snprintf(sbuf, sizeof(sbuf) - 1, "%d dB   ", rb->global_settings->volume);\r
+               rb->lcd_puts(5, 3, sbuf);\r
+               rb->snprintf(sbuf, sizeof(sbuf) - 1, "%d %%   ", rb->battery_level());\r
+               rb->lcd_puts(5, 4, sbuf);\r
+\r
+               rb->lcd_update();\r
+       }\r
+       lastbutton = 0;\r
+}\r
+\r
+\r
+void mainloop(void)\r
+{\r
+       int i;\r
+\r
+       minvol = rb->sound_min(SOUND_VOLUME);\r
+       maxvol = rb->sound_max(SOUND_VOLUME);\r
+\r
+       while (buffplayed <= buff_last && !lastbutton)\r
+       {\r
+               while (curbuff != lastbuff && !lastbutton) // Render loop, fills up all buffers except the one being played\r
+               {\r
+\r
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ\r
+       rb->cpu_boost(true);\r
+#endif\r
+\r
+                       checkbutton();\r
+\r
+                       if (Player_Active())\r
+                       {\r
+                               bufferlen[lastbuff] = VC_WriteBytes(pcmbufs[lastbuff], PCMBUFFERSIZE);\r
+                               buffrendered++;\r
+                       }\r
+                       else\r
+                       {\r
+                               // No more data to render (songend) - clear the next buffer so get_more() can return it a last time\r
+                               for (i = 0; i < PCMBUFFERSIZE; i++)\r
+                                       (pcmbufs[lastbuff])[i] = 0;\r
+                               bufferlen[lastbuff] = PCMBUFFERSIZE;\r
+                               buff_last = buffrendered;\r
+                       }\r
+\r
+                       if (lastbuff >= (buffercount - 1))\r
+                               lastbuff = 0;\r
+                       else\r
+                               lastbuff++;\r
+\r
+                       checkbutton();\r
+                       rb->reset_poweroff_timer();\r
+\r
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ\r
+       rb->cpu_boost(false);\r
+#endif\r
+\r
+               }\r
+\r
+               checkbutton();\r
+\r
+               rb->yield();\r
+\r
+       } /* while */\r
+\r
+} /* mainloop */\r
+\r
+\r
+\r
+\r
+enum plugin_status plugin_start(struct plugin_api *api, void *parameter)\r
+{\r
+       bool talk_menu;\r
+       int i;\r
+\r
+       rb = api;\r
+\r
+       /* Have to shut up voice menus or it will mess up our waveform playback */\r
+       talk_menu = rb->global_settings->talk_menu;\r
+       rb->global_settings->talk_menu = false;\r
+\r
+       rb->audio_stop();\r
+       rb->pcm_play_stop();\r
+       rb->pcm_set_frequency(44100);\r
+\r
+       // Initialize internal semi-dynamic memory\r
+       mbuf = rb->plugin_get_audio_buffer(&mallocbufsize);\r
+       mikmod_prepare_malloc(mbuf, mallocbufsize);\r
+\r
+\r
+       // Allocate PCM output buffers\r
+       for (i = 0; i < MAXPCMBUFFERS; i++)\r
+       {\r
+               if ((pcmbufs[i] = mikmod_malloc(PCMBUFFERSIZE)) == NULL)\r
+               {\r
+                       break;\r
+               }\r
+       }\r
+       buffercount = i;\r
+       //lastbuff = buffercount - 1;\r
+       // Prefill buffers !\r
+\r
+\r
+       // General output...\r
+       rb->lcd_clear_display();\r
+       rb->lcd_puts(0, 0, " MikMod for Rockbox 0.1");\r
+       rb->lcd_puts(0, 1, "========================");\r
+\r
+       rb->lcd_puts(0, 3, "Vol: -");\r
+       rb->lcd_puts(0, 4, "Pwr: -");\r
+       rb->lcd_puts(0, 5, "---------");\r
+       rb->lcd_puts(0, 6, "File:");\r
+       rb->lcd_puts(7, 6, &((char*)parameter)[FindLastOccurrenceChar(parameter, '/') + 1]);\r
+\r
+       rb->lcd_update();\r
+\r
+       //initialize the library\r
+       MikMod_RegisterAllLoaders();\r
+       md_mode = DMODE_SOFT_MUSIC | DMODE_16BITS | DMODE_STEREO | DMODE_INTERP | DMODE_SOFT_SNDFX;\r
+\r
+       if (!MikMod_Init(""))\r
+       {\r
+               #ifdef HAVE_ADJUSTABLE_CPU_FREQ\r
+                       rb->cpu_boost(true);\r
+               #endif\r
+               //load module\r
+               module = Player_Load(parameter, 32, 1);\r
+\r
+               #ifdef HAVE_ADJUSTABLE_CPU_FREQ\r
+                       rb->cpu_boost(false);\r
+               #endif\r
+\r
+               if (module)\r
+               {\r
+                       Player_Start(module);\r
+#ifndef SIMULATOR\r
+                       rb->ata_sleep();\r
+#endif\r
+\r
+                       rb->snprintf(sbuf, sizeof(sbuf) - 1, "Memory: %d / %d", (int)(mikmod_get_malloc_usage() - (buffercount * PCMBUFFERSIZE)), mallocbufsize);\r
+                       rb->lcd_puts(0, 16, sbuf);\r
+\r
+                       rb->lcd_puts(0, 7, "Type:");\r
+                       rb->lcd_puts(7, 7, module->modtype);\r
+\r
+                       rb->lcd_puts(0, 9, "Title:");\r
+                       rb->lcd_puts_scroll(7, 9, module->songname);\r
+\r
+                       rb->lcd_update();\r
+\r
+                       // Only prefill the first buffer so we get a quick start...\r
+                       bufferlen[0] = VC_WriteBytes(pcmbufs[0], PCMBUFFERSIZE);\r
+\r
+                       curbuff = buffercount - 1;\r
+                       lastbuff = 1; // Fixes skip during first buffers played\r
+\r
+                       rb->pcm_play_data(get_more, NULL, 0);\r
+\r
+                       mainloop();\r
+\r
+               }\r
+               else /* module */\r
+               {\r
+                       rb->splash(HZ*2, "Could not load module, reason:");\r
+                       rb->splash(HZ*2, MikMod_strerror(MikMod_errno));\r
+               }\r
+       }\r
+       else /* MikMod_Init */\r
+       {\r
+               rb->splash(HZ*2, "Could not initialize sound, reason:");\r
+               rb->splash(HZ*2, MikMod_strerror(MikMod_errno));\r
+       }\r
+\r
+\r
+       #ifdef HAVE_ADJUSTABLE_CPU_FREQ\r
+               rb->cpu_boost(false);\r
+       #endif\r
+\r
+       rb->pcm_play_stop();\r
+\r
+       Player_Stop();\r
+       Player_Free(module);\r
+       MikMod_Exit();\r
+\r
+\r
+       //restore default - user of apis is responsible for restoring\r
+       //   default state - normally playback at 44100Hz\r
+       rb->pcm_set_frequency(HW_SAMPR_DEFAULT);\r
+       rb->global_settings->talk_menu = talk_menu;\r
+\r
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ\r
+       rb->cpu_boost(false);\r
+#endif\r
+\r
+       return PLUGIN_OK;\r
+}\r