Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / mikmod.c
1 /***************************************************************************\r
2  *             __________               __   ___.\r
3  *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___\r
4  *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /\r
5  *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <\r
6  *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \\r
7  *                     \/            \/     \/    \/            \/\r
8  *\r
9  *\r
10  * All files in this archive are subject to the GNU General Public License.\r
11  * See the file COPYING in the source tree root for full license agreement.\r
12  *\r
13  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY\r
14  * KIND, either express or implied.\r
15  *\r
16  ****************************************************************************/\r
17 #include "plugin.h"\r
18 #include "mikmod_build.h"\r
19 \r
20 PLUGIN_HEADER\r
21 \r
22 struct plugin_api *rb;\r
23 \r
24 MODULE *module;\r
25 \r
26 #define MAXPCMBUFFERS 16 // Do NOT set this to a value lower than 2 or playback will break\r
27 #define PCMBUFFERSIZE 65536\r
28 \r
29 char *pcmbufs[MAXPCMBUFFERS];\r
30 int bufferlen[MAXPCMBUFFERS];\r
31 int buffercount = 0;\r
32 \r
33 int curbuff = 0;\r
34 int lastbuff = 0;\r
35 \r
36 size_t buffrendered = 0;\r
37 size_t buffplayed = 0;\r
38 size_t buff_last = ~0;\r
39 \r
40 int vol, minvol, maxvol;\r
41 int lastpower = 0, lastvol = 0;\r
42 int lastbutton = 0;\r
43 \r
44 char sbuf[32];\r
45 \r
46 int mallocbufsize;\r
47 char *mbuf;\r
48 \r
49 \r
50 void mikmod_prepare_malloc(char *buff, int bufsize);\r
51 long mikmod_get_malloc_usage(void);\r
52 void* mikmod_malloc(size_t size);\r
53 \r
54 \r
55 \r
56 int FindLastOccurrenceChar(char *string, char chr)\r
57 {\r
58         int i = 0;\r
59         //int LastOcc = -1;\r
60         int LastOcc = 0;\r
61 \r
62         while (string[i] != 0)\r
63         {\r
64                 if (string[i] == chr)\r
65                 {\r
66                         LastOcc = i;\r
67                 }\r
68 \r
69                 i++;\r
70         }\r
71 \r
72         return LastOcc;\r
73 }\r
74 \r
75 \r
76 void get_more(unsigned char **start, size_t *size)\r
77         {\r
78                 if (curbuff >= (buffercount - 1))\r
79                 {\r
80                         curbuff = 0;\r
81                 }\r
82                 else\r
83                 {\r
84                         curbuff++;\r
85                 }\r
86                 *start = pcmbufs[curbuff];\r
87                 *size  = bufferlen[curbuff];\r
88                 buffplayed++;\r
89         }\r
90 \r
91 \r
92 void checkbutton(void)\r
93 {\r
94         lastbutton = rb->button_get(false);\r
95         if (lastbutton & BUTTON_MENU)\r
96         {\r
97                 return;\r
98         }\r
99 \r
100         switch (lastbutton)\r
101         {\r
102                 case BUTTON_SCROLL_FWD:\r
103                 case (BUTTON_SCROLL_FWD | BUTTON_REPEAT):\r
104                         vol = rb->global_settings->volume;\r
105 \r
106                         if (vol < maxvol)\r
107                         {\r
108                                 vol++;\r
109                                 rb->sound_set(SOUND_VOLUME, vol);\r
110                                 rb->global_settings->volume = vol;\r
111                         }\r
112                         break;\r
113 \r
114                 case BUTTON_SCROLL_BACK:\r
115                 case (BUTTON_SCROLL_BACK | BUTTON_REPEAT):\r
116                         vol = rb->global_settings->volume;\r
117 \r
118                         if (vol > minvol)\r
119                         {\r
120                                 vol--;\r
121                                 rb->sound_set(SOUND_VOLUME, vol);\r
122                                 rb->global_settings->volume = vol;\r
123                         }\r
124                         break;\r
125 \r
126                 case (BUTTON_PLAY | BUTTON_REL):\r
127 \r
128 #ifdef HAVE_ADJUSTABLE_CPU_FREQ\r
129         rb->cpu_boost(false); // Just in case we got called from inside the render loop\r
130 #endif\r
131                         rb->pcm_play_pause(false);\r
132                         do /* code from mpegplayer, *very* simple unpause loop */\r
133                         {\r
134                                 lastbutton = rb->button_get(true);\r
135                                 if (lastbutton == BUTTON_MENU)\r
136                                 {\r
137                                         return;\r
138                                 }\r
139                         } while (lastbutton != (BUTTON_PLAY | BUTTON_REL));\r
140                         rb->pcm_play_pause(true);\r
141                         break;\r
142 \r
143                 default:\r
144                         if(rb->default_event_handler(lastbutton) == SYS_USB_CONNECTED)\r
145                         {\r
146                                 return;\r
147                         }\r
148         } /* switch(button) */\r
149 \r
150         if ((rb->global_settings->volume != lastvol) || (rb->battery_level() != lastpower))\r
151         {\r
152                 lastpower = rb->battery_level();\r
153                 lastvol = rb->global_settings->volume;\r
154                 \r
155                 rb->snprintf(sbuf, sizeof(sbuf) - 1, "%d dB   ", rb->global_settings->volume);\r
156                 rb->lcd_puts(5, 3, sbuf);\r
157                 rb->snprintf(sbuf, sizeof(sbuf) - 1, "%d %%   ", rb->battery_level());\r
158                 rb->lcd_puts(5, 4, sbuf);\r
159 \r
160                 rb->lcd_update();\r
161         }\r
162         lastbutton = 0;\r
163 }\r
164 \r
165 \r
166 void mainloop(void)\r
167 {\r
168         int i;\r
169 \r
170         minvol = rb->sound_min(SOUND_VOLUME);\r
171         maxvol = rb->sound_max(SOUND_VOLUME);\r
172 \r
173         while (buffplayed <= buff_last && !lastbutton)\r
174         {\r
175                 while (curbuff != lastbuff && !lastbutton) // Render loop, fills up all buffers except the one being played\r
176                 {\r
177 \r
178 #ifdef HAVE_ADJUSTABLE_CPU_FREQ\r
179         rb->cpu_boost(true);\r
180 #endif\r
181 \r
182                         checkbutton();\r
183 \r
184                         if (Player_Active())\r
185                         {\r
186                                 bufferlen[lastbuff] = VC_WriteBytes(pcmbufs[lastbuff], PCMBUFFERSIZE);\r
187                                 buffrendered++;\r
188                         }\r
189                         else\r
190                         {\r
191                                 // No more data to render (songend) - clear the next buffer so get_more() can return it a last time\r
192                                 for (i = 0; i < PCMBUFFERSIZE; i++)\r
193                                         (pcmbufs[lastbuff])[i] = 0;\r
194                                 bufferlen[lastbuff] = PCMBUFFERSIZE;\r
195                                 buff_last = buffrendered;\r
196                         }\r
197 \r
198                         if (lastbuff >= (buffercount - 1))\r
199                                 lastbuff = 0;\r
200                         else\r
201                                 lastbuff++;\r
202 \r
203                         checkbutton();\r
204                         rb->reset_poweroff_timer();\r
205 \r
206 #ifdef HAVE_ADJUSTABLE_CPU_FREQ\r
207         rb->cpu_boost(false);\r
208 #endif\r
209 \r
210                 }\r
211 \r
212                 checkbutton();\r
213 \r
214                 rb->yield();\r
215 \r
216         } /* while */\r
217 \r
218 } /* mainloop */\r
219 \r
220 \r
221 \r
222 \r
223 enum plugin_status plugin_start(struct plugin_api *api, void *parameter)\r
224 {\r
225         bool talk_menu;\r
226         int i;\r
227 \r
228         rb = api;\r
229 \r
230         /* Have to shut up voice menus or it will mess up our waveform playback */\r
231         talk_menu = rb->global_settings->talk_menu;\r
232         rb->global_settings->talk_menu = false;\r
233 \r
234         rb->audio_stop();\r
235         rb->pcm_play_stop();\r
236         rb->pcm_set_frequency(44100);\r
237 \r
238         // Initialize internal semi-dynamic memory\r
239         mbuf = rb->plugin_get_audio_buffer(&mallocbufsize);\r
240         mikmod_prepare_malloc(mbuf, mallocbufsize);\r
241 \r
242 \r
243         // Allocate PCM output buffers\r
244         for (i = 0; i < MAXPCMBUFFERS; i++)\r
245         {\r
246                 if ((pcmbufs[i] = mikmod_malloc(PCMBUFFERSIZE)) == NULL)\r
247                 {\r
248                         break;\r
249                 }\r
250         }\r
251         buffercount = i;\r
252         //lastbuff = buffercount - 1;\r
253         // Prefill buffers !\r
254 \r
255 \r
256         // General output...\r
257         rb->lcd_clear_display();\r
258         rb->lcd_puts(0, 0, " MikMod for Rockbox 0.1");\r
259         rb->lcd_puts(0, 1, "========================");\r
260 \r
261         rb->lcd_puts(0, 3, "Vol: -");\r
262         rb->lcd_puts(0, 4, "Pwr: -");\r
263         rb->lcd_puts(0, 5, "---------");\r
264         rb->lcd_puts(0, 6, "File:");\r
265         rb->lcd_puts(7, 6, &((char*)parameter)[FindLastOccurrenceChar(parameter, '/') + 1]);\r
266 \r
267         rb->lcd_update();\r
268 \r
269         //initialize the library\r
270         MikMod_RegisterAllLoaders();\r
271         md_mode = DMODE_SOFT_MUSIC | DMODE_16BITS | DMODE_STEREO | DMODE_INTERP | DMODE_SOFT_SNDFX;\r
272 \r
273         if (!MikMod_Init(""))\r
274         {\r
275                 #ifdef HAVE_ADJUSTABLE_CPU_FREQ\r
276                         rb->cpu_boost(true);\r
277                 #endif\r
278                 //load module\r
279                 module = Player_Load(parameter, 32, 1);\r
280 \r
281                 #ifdef HAVE_ADJUSTABLE_CPU_FREQ\r
282                         rb->cpu_boost(false);\r
283                 #endif\r
284 \r
285                 if (module)\r
286                 {\r
287                         Player_Start(module);\r
288 #ifndef SIMULATOR\r
289                         rb->ata_sleep();\r
290 #endif\r
291 \r
292                         rb->snprintf(sbuf, sizeof(sbuf) - 1, "Memory: %d / %d", (int)(mikmod_get_malloc_usage() - (buffercount * PCMBUFFERSIZE)), mallocbufsize);\r
293                         rb->lcd_puts(0, 16, sbuf);\r
294 \r
295                         rb->lcd_puts(0, 7, "Type:");\r
296                         rb->lcd_puts(7, 7, module->modtype);\r
297 \r
298                         rb->lcd_puts(0, 9, "Title:");\r
299                         rb->lcd_puts_scroll(7, 9, module->songname);\r
300 \r
301                         rb->lcd_update();\r
302 \r
303                         // Only prefill the first buffer so we get a quick start...\r
304                         bufferlen[0] = VC_WriteBytes(pcmbufs[0], PCMBUFFERSIZE);\r
305 \r
306                         curbuff = buffercount - 1;\r
307                         lastbuff = 1; // Fixes skip during first buffers played\r
308 \r
309                         rb->pcm_play_data(get_more, NULL, 0);\r
310 \r
311                         mainloop();\r
312 \r
313                 }\r
314                 else /* module */\r
315                 {\r
316                         rb->splash(HZ*2, "Could not load module, reason:");\r
317                         rb->splash(HZ*2, MikMod_strerror(MikMod_errno));\r
318                 }\r
319         }\r
320         else /* MikMod_Init */\r
321         {\r
322                 rb->splash(HZ*2, "Could not initialize sound, reason:");\r
323                 rb->splash(HZ*2, MikMod_strerror(MikMod_errno));\r
324         }\r
325 \r
326 \r
327         #ifdef HAVE_ADJUSTABLE_CPU_FREQ\r
328                 rb->cpu_boost(false);\r
329         #endif\r
330 \r
331         rb->pcm_play_stop();\r
332 \r
333         Player_Stop();\r
334         Player_Free(module);\r
335         MikMod_Exit();\r
336 \r
337 \r
338         //restore default - user of apis is responsible for restoring\r
339         //   default state - normally playback at 44100Hz\r
340         rb->pcm_set_frequency(HW_SAMPR_DEFAULT);\r
341         rb->global_settings->talk_menu = talk_menu;\r
342 \r
343 #ifdef HAVE_ADJUSTABLE_CPU_FREQ\r
344         rb->cpu_boost(false);\r
345 #endif\r
346 \r
347         return PLUGIN_OK;\r
348 }\r