Add 2008 to the copyright notice.
[Rockbox.git] / apps / plugins / metronome.c
blob402f4ac3fc1425e018384e147ad6aca9f35a9d82
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 #endif /* HAVE_LCD_BITMAP */
197 display->update();
200 void draw_display(void)
202 int i;
203 FOR_NB_SCREENS(i)
204 metronome_draw(rb->screens[i]);
207 /* helper function to change the volume by a certain amount, +/-
208 ripped from video.c */
209 void change_volume(int delta){
210 int minvol = rb->sound_min(SOUND_VOLUME);
211 int maxvol = rb->sound_max(SOUND_VOLUME);
212 int vol = rb->global_settings->volume + delta;
214 if (vol > maxvol) vol = maxvol;
215 else if (vol < minvol) vol = minvol;
216 if (vol != rb->global_settings->volume) {
217 rb->sound_set(SOUND_VOLUME, vol);
218 rb->global_settings->volume = vol;
219 draw_display();
223 /*function to accelerate bpm change*/
224 void change_bpm(int direction){
225 if((bpm_step_counter < 20)
226 || (bpm > 389)
227 || (bpm < 10))
228 bpm = bpm + direction;
229 else if (bpm_step_counter < 60)
230 bpm = bpm + direction * 2;
231 else
232 bpm = bpm + direction * 9;
234 if (bpm > 400) bpm = 400;
235 if (bpm < 1) bpm = 1;
236 calc_period();
237 draw_display();
238 bpm_step_counter++;
241 void timer_callback(void)
243 if(minitick >= period){
244 minitick = 0;
245 if(!sound_active && !sound_paused && !tap_count) {
246 #if CONFIG_CODEC == SWCODEC
247 /* On SWCODEC we can't call play_tock() directly from an ISR. */
248 need_to_play = true;
249 #else
250 play_tock();
251 #endif
252 rb->reset_poweroff_timer();
255 else {
256 minitick++;
259 if (tap_count) {
260 tap_time++;
261 if (tap_count > 1 && tap_time > tap_timeout)
262 tap_count = 0;
266 void cleanup(void *parameter)
268 (void)parameter;
270 rb->timer_unregister();
271 MET_PLAY_STOP; /* stop audio ISR */
272 rb->led(0);
275 void tap(void)
277 if (tap_count == 0 || tap_time < tap_count) {
278 tap_time = 0;
280 else {
281 if (tap_time > 0) {
282 bpm = 61440/(tap_time/tap_count);
284 if (bpm > 400)
285 bpm = 400;
288 calc_period();
289 draw_display();
291 tap_timeout = (tap_count+2)*tap_time/tap_count;
294 tap_count++;
295 minitick = 0; /* sync tock to tapping */
296 play_tock();
298 reset_tap = false;
301 enum plugin_status plugin_start(struct plugin_api* api, void* parameter){
302 int button;
303 enum plugin_status status;
305 (void)parameter;
306 rb = api;
308 if (MET_IS_PLAYING)
309 MET_PLAY_STOP; /* stop audio IS */
311 #if CONFIG_CODEC != SWCODEC
312 rb->bitswap(sound, sizeof(sound));
313 #else
314 prepare_tock();
315 #if INPUT_SRC_CAPS != 0
316 /* Select playback */
317 rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
318 rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
319 #endif
320 rb->pcm_set_frequency(SAMPR_44);
321 #endif /* CONFIG_CODEC != SWCODEC */
323 calc_period();
324 rb->timer_register(1, NULL, TIMER_FREQ/1024, 1, timer_callback);
326 draw_display();
328 /* main loop */
329 while (true){
330 reset_tap = true;
331 #if CONFIG_CODEC == SWCODEC
332 button = pluginlib_getaction(rb,1,plugin_contexts,PLA_ARRAY_COUNT);
333 if (need_to_play)
335 need_to_play = false;
336 play_tock();
338 #else
339 button = pluginlib_getaction(rb,TIMEOUT_BLOCK,
340 plugin_contexts,PLA_ARRAY_COUNT);
341 #endif /* SWCODEC */
342 switch (button) {
344 case METRONOME_QUIT:
345 /* get out of here */
346 cleanup(NULL);
347 status = PLUGIN_OK;
348 goto metronome_exit;
350 #if CONFIG_KEYPAD == ONDIO_PAD
351 case METRONOME_PLAY_TAP:
352 if(sound_paused) {
353 sound_paused = false;
354 calc_period();
355 draw_display();
357 else
358 tap();
359 break;
361 case METRONOME_PAUSE:
362 if(!sound_paused) {
363 sound_paused = true;
364 draw_display();
366 break;
368 #else
369 case METRONOME_PLAYPAUSE:
370 if(sound_paused)
371 sound_paused = false;
372 else
373 sound_paused = true;
374 calc_period();
375 draw_display();
376 break;
377 #endif /* ONDIO_PAD */
379 case METRONOME_VOL_UP:
380 case METRONOME_VOL_UP_REP:
381 change_volume(1);
382 calc_period();
383 break;
385 case METRONOME_VOL_DOWN:
386 case METRONOME_VOL_DOWN_REP:
387 change_volume(-1);
388 calc_period();
389 break;
391 case METRONOME_LEFT:
392 bpm_step_counter = 0;
393 case METRONOME_LEFT_REP:
394 change_bpm(-1);
395 break;
397 case METRONOME_RIGHT:
398 bpm_step_counter = 0;
399 case METRONOME_RIGHT_REP:
400 change_bpm(1);
401 break;
403 #ifdef METRONOME_TAP
404 case METRONOME_TAP:
405 tap();
406 break;
407 #endif
409 #ifdef MET_SYNC
410 case METRONOME_SYNC:
411 minitick = period;
412 break;
413 #endif
415 default:
416 if (rb->default_event_handler_ex(button, cleanup, NULL)
417 == SYS_USB_CONNECTED)
419 status = PLUGIN_USB_CONNECTED;
420 goto metronome_exit;
422 reset_tap = false;
423 break;
426 if (reset_tap) {
427 tap_count = 0;
431 metronome_exit:
432 #if CONFIG_CODEC == SWCODEC
433 rb->pcm_set_frequency(HW_SAMPR_DEFAULT);
434 #endif
435 return status;