fix FS#8701 - metronome doesnt change back to ui font after drawing
[Rockbox.git] / apps / plugins / metronome.c
blob571e085b51fd58422c87c4642a67650393564d66
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2004 Matthias Wientapper
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include "pluginlib_actions.h"
20 #include "metronome.h"
22 PLUGIN_HEADER
23 #define METRONOME_QUIT PLA_QUIT
24 #define METRONOME_VOL_UP PLA_INC
25 #define METRONOME_VOL_DOWN PLA_DEC
26 #define METRONOME_VOL_UP_REP PLA_INC_REPEAT
27 #define METRONOME_VOL_DOWN_REP PLA_DEC_REPEAT
28 #define METRONOME_LEFT PLA_LEFT
29 #define METRONOME_RIGHT PLA_RIGHT
30 #define METRONOME_LEFT_REP PLA_LEFT_REPEAT
31 #define METRONOME_RIGHT_REP PLA_RIGHT_REPEAT
32 enum {
33 METRONOME_PLAY_TAP = LAST_PLUGINLIB_ACTION+1,
34 #if CONFIG_KEYPAD == ONDIO_PAD
35 METRONOME_PAUSE,
36 #endif /* ONDIO_PAD */
37 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
38 METRONOME_SYNC
39 #endif /* IRIVER_H100_PAD||IRIVER_H300_PAD */
43 #if CONFIG_KEYPAD == ONDIO_PAD
44 #define METRONOME_TAP PLA_START
45 #define METRONOME_MSG_START "start: mode"
46 #define METRONOME_MSG_STOP "pause: hold mode"
47 static const struct button_mapping ondio_action[] =
49 {METRONOME_PLAY_TAP, BUTTON_MENU|BUTTON_REL, BUTTON_MENU },
50 {METRONOME_PAUSE, BUTTON_MENU|BUTTON_REPEAT, BUTTON_NONE },
51 {CONTEXT_CUSTOM,BUTTON_NONE,BUTTON_NONE}
53 #else /* !ONDIO_PAD */
54 #define METRONOME_TAP PLA_FIRE
55 #define METRONOME_PLAYPAUSE PLA_START
56 #define METRONOME_MSG_START "press play"
57 #define METRONOME_MSG_STOP "press pause"
59 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
60 #define MET_SYNC
61 static const struct button_mapping iriver_syncaction[] =
63 {METRONOME_SYNC, BUTTON_REC, BUTTON_NONE },
64 {CONTEXT_CUSTOM,BUTTON_NONE,BUTTON_NONE}
66 #endif /* IRIVER_H100_PAD||IRIVER_H300_PAD */
67 #endif /* #if CONFIG_KEYPAD == ONDIO_PAD */
69 const struct button_mapping *plugin_contexts[]={
70 generic_increase_decrease,
71 generic_directions,
72 #if CONFIG_KEYPAD == ONDIO_PAD
73 ondio_action,
74 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
75 iriver_syncaction,
76 #endif
77 generic_actions
79 #define PLA_ARRAY_COUNT sizeof(plugin_contexts)/sizeof(plugin_contexts[0])
81 static struct plugin_api* rb;
83 MEM_FUNCTION_WRAPPERS(rb);
85 static int bpm = 120;
86 static int period = 0;
87 static int minitick = 0;
89 static bool sound_active = false;
90 static bool sound_paused = true;
92 static char buffer[30];
94 static bool reset_tap = false;
95 static int tap_count = 0;
96 static int tap_time = 0;
97 static int tap_timeout = 0;
99 int bpm_step_counter = 0;
101 #if CONFIG_CODEC != SWCODEC
103 #define MET_IS_PLAYING rb->mp3_is_playing()
104 #define MET_PLAY_STOP rb->mp3_play_stop()
106 void callback(unsigned char** start, size_t* size){
107 (void)start; /* unused parameter, avoid warning */
108 *size = 0; /* end of data */
109 sound_active = false;
110 rb->led(0);
113 void play_tock(void){
114 sound_active = true;
115 rb->led(1);
116 rb->mp3_play_data(sound, sizeof(sound), callback);
117 rb->mp3_play_pause(true); /* kickoff audio */
120 #else /* CONFIG_CODEC == SWCODEC */
122 #define MET_IS_PLAYING rb->pcm_is_playing()
123 #define MET_PLAY_STOP rb->audio_stop()
126 int tock;
127 bool need_to_play = false;
129 short sndbuf[sizeof(sound)*2];
131 /* Convert the mono "tock" sample to interleaved stereo */
132 void prepare_tock(void)
134 int i;
135 for(i = 0;i < (int)sizeof(sound)/2;i++) {
136 sndbuf[i*2] = sound[i];
137 sndbuf[i*2+1] = sound[i];
141 void play_tock(void) {
142 rb->pcm_play_data(NULL,(unsigned char *)sndbuf,sizeof(sndbuf));
143 tock++;
146 #endif /* CONFIG_CODEC != SWCODEC */
148 void calc_period(void)
150 period = 61440/bpm-1; /* (60*1024)/bpm; */
154 void metronome_draw(struct screen* display)
156 display->clear_display();
158 #ifdef HAVE_LCD_BITMAP
159 display->setfont(FONT_SYSFIXED);
160 display->puts(0, 0, "Metronome");
161 if(display->screen_type==SCREEN_MAIN)
163 display->puts(0, 5, "Select to TAP");
164 display->puts(0, 6, "Rec to SYNC");
166 #ifdef HAVE_REMOTE_LCD
167 else
169 display->puts(0, 5, "Rec to TAP");
170 display->puts(0, 6, "Mode to SYNC");
172 #endif
173 #endif /* HAVE_LCD_BITMAP */
175 rb->snprintf(buffer, sizeof(buffer), "BPM: %d ",bpm);
176 #ifdef HAVE_LCD_BITMAP
177 display->puts(0,3, buffer);
178 #else
179 display->puts(0,0, buffer);
180 #endif /* HAVE_LCD_BITMAP */
182 rb->snprintf(buffer, sizeof(buffer), "Vol: %d",
183 rb->global_settings->volume);
184 #ifdef HAVE_LCD_BITMAP
185 display->puts(10, 3, buffer);
186 #else
187 display->puts(0,1, buffer);
188 #endif /* HAVE_LCD_BITMAP */
190 #ifdef HAVE_LCD_BITMAP
191 display->drawline(0, 12, 111, 12);
192 if(sound_paused)
193 display->puts(0,2,METRONOME_MSG_START);
194 else
195 display->puts(0,2,METRONOME_MSG_STOP);
196 display->setfont(FONT_UI);
197 #endif /* HAVE_LCD_BITMAP */
198 display->update();
201 void draw_display(void)
203 int i;
204 FOR_NB_SCREENS(i)
205 metronome_draw(rb->screens[i]);
208 /* helper function to change the volume by a certain amount, +/-
209 ripped from video.c */
210 void change_volume(int delta){
211 int minvol = rb->sound_min(SOUND_VOLUME);
212 int maxvol = rb->sound_max(SOUND_VOLUME);
213 int vol = rb->global_settings->volume + delta;
215 if (vol > maxvol) vol = maxvol;
216 else if (vol < minvol) vol = minvol;
217 if (vol != rb->global_settings->volume) {
218 rb->sound_set(SOUND_VOLUME, vol);
219 rb->global_settings->volume = vol;
220 draw_display();
224 /*function to accelerate bpm change*/
225 void change_bpm(int direction){
226 if((bpm_step_counter < 20)
227 || (bpm > 389)
228 || (bpm < 10))
229 bpm = bpm + direction;
230 else if (bpm_step_counter < 60)
231 bpm = bpm + direction * 2;
232 else
233 bpm = bpm + direction * 9;
235 if (bpm > 400) bpm = 400;
236 if (bpm < 1) bpm = 1;
237 calc_period();
238 draw_display();
239 bpm_step_counter++;
242 void timer_callback(void)
244 if(minitick >= period){
245 minitick = 0;
246 if(!sound_active && !sound_paused && !tap_count) {
247 #if CONFIG_CODEC == SWCODEC
248 /* On SWCODEC we can't call play_tock() directly from an ISR. */
249 need_to_play = true;
250 #else
251 play_tock();
252 #endif
253 rb->reset_poweroff_timer();
256 else {
257 minitick++;
260 if (tap_count) {
261 tap_time++;
262 if (tap_count > 1 && tap_time > tap_timeout)
263 tap_count = 0;
267 void cleanup(void *parameter)
269 (void)parameter;
271 rb->timer_unregister();
272 MET_PLAY_STOP; /* stop audio ISR */
273 rb->led(0);
276 void tap(void)
278 if (tap_count == 0 || tap_time < tap_count) {
279 tap_time = 0;
281 else {
282 if (tap_time > 0) {
283 bpm = 61440/(tap_time/tap_count);
285 if (bpm > 400)
286 bpm = 400;
289 calc_period();
290 draw_display();
292 tap_timeout = (tap_count+2)*tap_time/tap_count;
295 tap_count++;
296 minitick = 0; /* sync tock to tapping */
297 play_tock();
299 reset_tap = false;
302 enum plugin_status plugin_start(struct plugin_api* api, void* parameter){
303 int button;
304 enum plugin_status status;
306 (void)parameter;
307 rb = api;
309 if (MET_IS_PLAYING)
310 MET_PLAY_STOP; /* stop audio IS */
312 #if CONFIG_CODEC != SWCODEC
313 rb->bitswap(sound, sizeof(sound));
314 #else
315 prepare_tock();
316 #if INPUT_SRC_CAPS != 0
317 /* Select playback */
318 rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
319 rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
320 #endif
321 rb->pcm_set_frequency(SAMPR_44);
322 #endif /* CONFIG_CODEC != SWCODEC */
324 calc_period();
325 rb->timer_register(1, NULL, TIMER_FREQ/1024, 1, timer_callback);
327 draw_display();
329 /* main loop */
330 while (true){
331 reset_tap = true;
332 #if CONFIG_CODEC == SWCODEC
333 button = pluginlib_getaction(rb,1,plugin_contexts,PLA_ARRAY_COUNT);
334 if (need_to_play)
336 need_to_play = false;
337 play_tock();
339 #else
340 button = pluginlib_getaction(rb,TIMEOUT_BLOCK,
341 plugin_contexts,PLA_ARRAY_COUNT);
342 #endif /* SWCODEC */
343 switch (button) {
345 case METRONOME_QUIT:
346 /* get out of here */
347 cleanup(NULL);
348 status = PLUGIN_OK;
349 goto metronome_exit;
351 #if CONFIG_KEYPAD == ONDIO_PAD
352 case METRONOME_PLAY_TAP:
353 if(sound_paused) {
354 sound_paused = false;
355 calc_period();
356 draw_display();
358 else
359 tap();
360 break;
362 case METRONOME_PAUSE:
363 if(!sound_paused) {
364 sound_paused = true;
365 draw_display();
367 break;
369 #else
370 case METRONOME_PLAYPAUSE:
371 if(sound_paused)
372 sound_paused = false;
373 else
374 sound_paused = true;
375 calc_period();
376 draw_display();
377 break;
378 #endif /* ONDIO_PAD */
380 case METRONOME_VOL_UP:
381 case METRONOME_VOL_UP_REP:
382 change_volume(1);
383 calc_period();
384 break;
386 case METRONOME_VOL_DOWN:
387 case METRONOME_VOL_DOWN_REP:
388 change_volume(-1);
389 calc_period();
390 break;
392 case METRONOME_LEFT:
393 bpm_step_counter = 0;
394 case METRONOME_LEFT_REP:
395 change_bpm(-1);
396 break;
398 case METRONOME_RIGHT:
399 bpm_step_counter = 0;
400 case METRONOME_RIGHT_REP:
401 change_bpm(1);
402 break;
404 #ifdef METRONOME_TAP
405 case METRONOME_TAP:
406 tap();
407 break;
408 #endif
410 #ifdef MET_SYNC
411 case METRONOME_SYNC:
412 minitick = period;
413 break;
414 #endif
416 default:
417 if (rb->default_event_handler_ex(button, cleanup, NULL)
418 == SYS_USB_CONNECTED)
420 status = PLUGIN_USB_CONNECTED;
421 goto metronome_exit;
423 reset_tap = false;
424 break;
427 if (reset_tap) {
428 tap_count = 0;
432 metronome_exit:
433 #if CONFIG_CODEC == SWCODEC
434 rb->pcm_set_frequency(HW_SAMPR_DEFAULT);
435 #endif
436 return status;