--- /dev/null
+/***************************************************************************\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