Updated our source code header to explicitly mention that we are GPL v2 or
[Rockbox.git] / apps / plugins / metronome.c
blob6e3b9515f23fa73a6cf45536acf1a092336905be
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2004 Matthias Wientapper
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "pluginlib_actions.h"
22 #include "metronome.h"
24 PLUGIN_HEADER
25 #define METRONOME_QUIT PLA_QUIT
26 #define METRONOME_VOL_UP PLA_INC
27 #define METRONOME_VOL_DOWN PLA_DEC
28 #define METRONOME_VOL_UP_REP PLA_INC_REPEAT
29 #define METRONOME_VOL_DOWN_REP PLA_DEC_REPEAT
30 #define METRONOME_LEFT PLA_LEFT
31 #define METRONOME_RIGHT PLA_RIGHT
32 #define METRONOME_LEFT_REP PLA_LEFT_REPEAT
33 #define METRONOME_RIGHT_REP PLA_RIGHT_REPEAT
34 enum {
35 METRONOME_PLAY_TAP = LAST_PLUGINLIB_ACTION+1,
36 #if CONFIG_KEYPAD == ONDIO_PAD
37 METRONOME_PAUSE,
38 #endif /* ONDIO_PAD */
39 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
40 METRONOME_SYNC
41 #endif /* IRIVER_H100_PAD||IRIVER_H300_PAD */
45 #if CONFIG_KEYPAD == ONDIO_PAD
46 #define METRONOME_TAP PLA_START
47 #define METRONOME_MSG_START "start: mode"
48 #define METRONOME_MSG_STOP "pause: hold mode"
49 static const struct button_mapping ondio_action[] =
51 {METRONOME_PLAY_TAP, BUTTON_MENU|BUTTON_REL, BUTTON_MENU },
52 {METRONOME_PAUSE, BUTTON_MENU|BUTTON_REPEAT, BUTTON_NONE },
53 {CONTEXT_CUSTOM,BUTTON_NONE,BUTTON_NONE}
55 #else /* !ONDIO_PAD */
56 #define METRONOME_TAP PLA_FIRE
57 #define METRONOME_PLAYPAUSE PLA_START
58 #define METRONOME_MSG_START "press play"
59 #define METRONOME_MSG_STOP "press pause"
61 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
62 #define MET_SYNC
63 static const struct button_mapping iriver_syncaction[] =
65 {METRONOME_SYNC, BUTTON_REC, BUTTON_NONE },
66 {CONTEXT_CUSTOM,BUTTON_NONE,BUTTON_NONE}
68 #endif /* IRIVER_H100_PAD||IRIVER_H300_PAD */
69 #endif /* #if CONFIG_KEYPAD == ONDIO_PAD */
71 const struct button_mapping *plugin_contexts[]={
72 generic_increase_decrease,
73 generic_directions,
74 #if CONFIG_KEYPAD == ONDIO_PAD
75 ondio_action,
76 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
77 iriver_syncaction,
78 #endif
79 generic_actions
81 #define PLA_ARRAY_COUNT sizeof(plugin_contexts)/sizeof(plugin_contexts[0])
83 static const struct plugin_api* rb;
85 MEM_FUNCTION_WRAPPERS(rb);
87 static int bpm = 120;
88 static int period = 0;
89 static int minitick = 0;
91 static bool sound_active = false;
92 static bool sound_paused = true;
94 static char buffer[30];
96 static bool reset_tap = false;
97 static int tap_count = 0;
98 static int tap_time = 0;
99 static int tap_timeout = 0;
101 int bpm_step_counter = 0;
103 #if CONFIG_CODEC != SWCODEC
105 #define MET_IS_PLAYING rb->mp3_is_playing()
106 #define MET_PLAY_STOP rb->mp3_play_stop()
108 void callback(unsigned char** start, size_t* size){
109 (void)start; /* unused parameter, avoid warning */
110 *size = 0; /* end of data */
111 sound_active = false;
112 rb->led(0);
115 void play_tock(void){
116 sound_active = true;
117 rb->led(1);
118 rb->mp3_play_data(sound, sizeof(sound), callback);
119 rb->mp3_play_pause(true); /* kickoff audio */
122 #else /* CONFIG_CODEC == SWCODEC */
124 #define MET_IS_PLAYING rb->pcm_is_playing()
125 #define MET_PLAY_STOP rb->audio_stop()
128 int tock;
129 bool need_to_play = false;
131 short sndbuf[sizeof(sound)*2];
133 /* Convert the mono "tock" sample to interleaved stereo */
134 void prepare_tock(void)
136 int i;
137 for(i = 0;i < (int)sizeof(sound)/2;i++) {
138 sndbuf[i*2] = sound[i];
139 sndbuf[i*2+1] = sound[i];
143 void play_tock(void) {
144 rb->pcm_play_data(NULL,(unsigned char *)sndbuf,sizeof(sndbuf));
145 tock++;
148 #endif /* CONFIG_CODEC != SWCODEC */
150 void calc_period(void)
152 period = 61440/bpm-1; /* (60*1024)/bpm; */
156 void metronome_draw(struct screen* display)
158 display->clear_display();
160 #ifdef HAVE_LCD_BITMAP
161 display->setfont(FONT_SYSFIXED);
162 display->puts(0, 0, "Metronome");
163 if(display->screen_type==SCREEN_MAIN)
165 display->puts(0, 5, "Select to TAP");
166 display->puts(0, 6, "Rec to SYNC");
168 #ifdef HAVE_REMOTE_LCD
169 else
171 display->puts(0, 5, "Rec to TAP");
172 display->puts(0, 6, "Mode to SYNC");
174 #endif
175 #endif /* HAVE_LCD_BITMAP */
177 rb->snprintf(buffer, sizeof(buffer), "BPM: %d ",bpm);
178 #ifdef HAVE_LCD_BITMAP
179 display->puts(0,3, buffer);
180 #else
181 display->puts(0,0, buffer);
182 #endif /* HAVE_LCD_BITMAP */
184 rb->snprintf(buffer, sizeof(buffer), "Vol: %d",
185 rb->global_settings->volume);
186 #ifdef HAVE_LCD_BITMAP
187 display->puts(10, 3, buffer);
188 #else
189 display->puts(0,1, buffer);
190 #endif /* HAVE_LCD_BITMAP */
192 #ifdef HAVE_LCD_BITMAP
193 display->hline(0, 111, 12);
194 if(sound_paused)
195 display->puts(0,2,METRONOME_MSG_START);
196 else
197 display->puts(0,2,METRONOME_MSG_STOP);
198 display->setfont(FONT_UI);
199 #endif /* HAVE_LCD_BITMAP */
200 display->update();
203 void draw_display(void)
205 int i;
206 FOR_NB_SCREENS(i)
207 metronome_draw(rb->screens[i]);
210 /* helper function to change the volume by a certain amount, +/-
211 ripped from video.c */
212 void change_volume(int delta){
213 int minvol = rb->sound_min(SOUND_VOLUME);
214 int maxvol = rb->sound_max(SOUND_VOLUME);
215 int vol = rb->global_settings->volume + delta;
217 if (vol > maxvol) vol = maxvol;
218 else if (vol < minvol) vol = minvol;
219 if (vol != rb->global_settings->volume) {
220 rb->sound_set(SOUND_VOLUME, vol);
221 rb->global_settings->volume = vol;
222 draw_display();
226 /*function to accelerate bpm change*/
227 void change_bpm(int direction){
228 if((bpm_step_counter < 20)
229 || (bpm > 389)
230 || (bpm < 10))
231 bpm = bpm + direction;
232 else if (bpm_step_counter < 60)
233 bpm = bpm + direction * 2;
234 else
235 bpm = bpm + direction * 9;
237 if (bpm > 400) bpm = 400;
238 if (bpm < 1) bpm = 1;
239 calc_period();
240 draw_display();
241 bpm_step_counter++;
244 void timer_callback(void)
246 if(minitick >= period){
247 minitick = 0;
248 if(!sound_active && !sound_paused && !tap_count) {
249 #if CONFIG_CODEC == SWCODEC
250 /* On SWCODEC we can't call play_tock() directly from an ISR. */
251 need_to_play = true;
252 #else
253 play_tock();
254 #endif
255 rb->reset_poweroff_timer();
258 else {
259 minitick++;
262 if (tap_count) {
263 tap_time++;
264 if (tap_count > 1 && tap_time > tap_timeout)
265 tap_count = 0;
269 void cleanup(void *parameter)
271 (void)parameter;
273 rb->timer_unregister();
274 MET_PLAY_STOP; /* stop audio ISR */
275 rb->led(0);
278 void tap(void)
280 if (tap_count == 0 || tap_time < tap_count) {
281 tap_time = 0;
283 else {
284 if (tap_time > 0) {
285 bpm = 61440/(tap_time/tap_count);
287 if (bpm > 400)
288 bpm = 400;
291 calc_period();
292 draw_display();
294 tap_timeout = (tap_count+2)*tap_time/tap_count;
297 tap_count++;
298 minitick = 0; /* sync tock to tapping */
299 play_tock();
301 reset_tap = false;
304 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter){
305 int button;
306 enum plugin_status status;
308 (void)parameter;
309 rb = api;
311 if (MET_IS_PLAYING)
312 MET_PLAY_STOP; /* stop audio IS */
314 #if CONFIG_CODEC != SWCODEC
315 rb->bitswap(sound, sizeof(sound));
316 #else
317 prepare_tock();
318 #if INPUT_SRC_CAPS != 0
319 /* Select playback */
320 rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
321 rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
322 #endif
323 rb->pcm_set_frequency(SAMPR_44);
324 #endif /* CONFIG_CODEC != SWCODEC */
326 calc_period();
327 rb->timer_register(1, NULL, TIMER_FREQ/1024, 1, timer_callback IF_COP(, CPU));
329 draw_display();
331 /* main loop */
332 while (true){
333 reset_tap = true;
334 #if CONFIG_CODEC == SWCODEC
335 button = pluginlib_getaction(rb,1,plugin_contexts,PLA_ARRAY_COUNT);
336 if (need_to_play)
338 need_to_play = false;
339 play_tock();
341 #else
342 button = pluginlib_getaction(rb,TIMEOUT_BLOCK,
343 plugin_contexts,PLA_ARRAY_COUNT);
344 #endif /* SWCODEC */
345 switch (button) {
347 case METRONOME_QUIT:
348 /* get out of here */
349 cleanup(NULL);
350 status = PLUGIN_OK;
351 goto metronome_exit;
353 #if CONFIG_KEYPAD == ONDIO_PAD
354 case METRONOME_PLAY_TAP:
355 if(sound_paused) {
356 sound_paused = false;
357 calc_period();
358 draw_display();
360 else
361 tap();
362 break;
364 case METRONOME_PAUSE:
365 if(!sound_paused) {
366 sound_paused = true;
367 draw_display();
369 break;
371 #else
372 case METRONOME_PLAYPAUSE:
373 if(sound_paused)
374 sound_paused = false;
375 else
376 sound_paused = true;
377 calc_period();
378 draw_display();
379 break;
380 #endif /* ONDIO_PAD */
382 case METRONOME_VOL_UP:
383 case METRONOME_VOL_UP_REP:
384 change_volume(1);
385 calc_period();
386 break;
388 case METRONOME_VOL_DOWN:
389 case METRONOME_VOL_DOWN_REP:
390 change_volume(-1);
391 calc_period();
392 break;
394 case METRONOME_LEFT:
395 bpm_step_counter = 0;
396 case METRONOME_LEFT_REP:
397 change_bpm(-1);
398 break;
400 case METRONOME_RIGHT:
401 bpm_step_counter = 0;
402 case METRONOME_RIGHT_REP:
403 change_bpm(1);
404 break;
406 #ifdef METRONOME_TAP
407 case METRONOME_TAP:
408 tap();
409 break;
410 #endif
412 #ifdef MET_SYNC
413 case METRONOME_SYNC:
414 minitick = period;
415 break;
416 #endif
418 default:
419 if (rb->default_event_handler_ex(button, cleanup, NULL)
420 == SYS_USB_CONNECTED)
422 status = PLUGIN_USB_CONNECTED;
423 goto metronome_exit;
425 reset_tap = false;
426 break;
429 if (reset_tap) {
430 tap_count = 0;
434 metronome_exit:
435 #if CONFIG_CODEC == SWCODEC
436 rb->pcm_set_frequency(HW_SAMPR_DEFAULT);
437 #endif
438 return status;