/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2004 Matthias Wientapper * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "pluginlib_actions.h" #include "metronome.h" PLUGIN_HEADER #define METRONOME_QUIT PLA_QUIT #define METRONOME_VOL_UP PLA_INC #define METRONOME_VOL_DOWN PLA_DEC #define METRONOME_VOL_UP_REP PLA_INC_REPEAT #define METRONOME_VOL_DOWN_REP PLA_DEC_REPEAT #define METRONOME_LEFT PLA_LEFT #define METRONOME_RIGHT PLA_RIGHT #define METRONOME_LEFT_REP PLA_LEFT_REPEAT #define METRONOME_RIGHT_REP PLA_RIGHT_REPEAT enum { METRONOME_PLAY_TAP = LAST_PLUGINLIB_ACTION+1, #if CONFIG_KEYPAD == ONDIO_PAD METRONOME_PAUSE, #endif /* ONDIO_PAD */ #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD) METRONOME_SYNC #endif /* IRIVER_H100_PAD||IRIVER_H300_PAD */ }; #if CONFIG_KEYPAD == ONDIO_PAD #define METRONOME_TAP PLA_START #define METRONOME_MSG_START "start: mode" #define METRONOME_MSG_STOP "pause: hold mode" static const struct button_mapping ondio_action[] = { {METRONOME_PLAY_TAP, BUTTON_MENU|BUTTON_REL, BUTTON_MENU }, {METRONOME_PAUSE, BUTTON_MENU|BUTTON_REPEAT, BUTTON_NONE }, {CONTEXT_CUSTOM,BUTTON_NONE,BUTTON_NONE} }; #else /* !ONDIO_PAD */ #define METRONOME_TAP PLA_FIRE #define METRONOME_PLAYPAUSE PLA_START #define METRONOME_MSG_START "press play" #define METRONOME_MSG_STOP "press pause" #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD) #define MET_SYNC static const struct button_mapping iriver_syncaction[] = { {METRONOME_SYNC, BUTTON_REC, BUTTON_NONE }, {CONTEXT_CUSTOM,BUTTON_NONE,BUTTON_NONE} }; #endif /* IRIVER_H100_PAD||IRIVER_H300_PAD */ #endif /* #if CONFIG_KEYPAD == ONDIO_PAD */ const struct button_mapping *plugin_contexts[]={ generic_increase_decrease, generic_directions, #if CONFIG_KEYPAD == ONDIO_PAD ondio_action, #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD) iriver_syncaction, #endif generic_actions }; #define PLA_ARRAY_COUNT sizeof(plugin_contexts)/sizeof(plugin_contexts[0]) static struct plugin_api* rb; MEM_FUNCTION_WRAPPERS(rb); static int bpm = 120; static int period = 0; static int minitick = 0; static bool sound_active = false; static bool sound_paused = true; static char buffer[30]; static bool reset_tap = false; static int tap_count = 0; static int tap_time = 0; static int tap_timeout = 0; int bpm_step_counter = 0; #if CONFIG_CODEC != SWCODEC #define MET_IS_PLAYING rb->mp3_is_playing() #define MET_PLAY_STOP rb->mp3_play_stop() void callback(unsigned char** start, size_t* size){ (void)start; /* unused parameter, avoid warning */ *size = 0; /* end of data */ sound_active = false; rb->led(0); } void play_tock(void){ sound_active = true; rb->led(1); rb->mp3_play_data(sound, sizeof(sound), callback); rb->mp3_play_pause(true); /* kickoff audio */ } #else /* CONFIG_CODEC == SWCODEC */ #define MET_IS_PLAYING rb->pcm_is_playing() #define MET_PLAY_STOP rb->audio_stop() int tock; bool need_to_play = false; short sndbuf[sizeof(sound)*2]; /* Convert the mono "tock" sample to interleaved stereo */ void prepare_tock(void) { int i; for(i = 0;i < (int)sizeof(sound)/2;i++) { sndbuf[i*2] = sound[i]; sndbuf[i*2+1] = sound[i]; } } void play_tock(void) { rb->pcm_play_data(NULL,(unsigned char *)sndbuf,sizeof(sndbuf)); tock++; } #endif /* CONFIG_CODEC != SWCODEC */ void calc_period(void) { period = 61440/bpm-1; /* (60*1024)/bpm; */ } void metronome_draw(struct screen* display) { display->clear_display(); #ifdef HAVE_LCD_BITMAP display->setfont(FONT_SYSFIXED); display->puts(0, 0, "Metronome"); if(display->screen_type==SCREEN_MAIN) { display->puts(0, 5, "Select to TAP"); display->puts(0, 6, "Rec to SYNC"); } #ifdef HAVE_REMOTE_LCD else { display->puts(0, 5, "Rec to TAP"); display->puts(0, 6, "Mode to SYNC"); } #endif #endif /* HAVE_LCD_BITMAP */ rb->snprintf(buffer, sizeof(buffer), "BPM: %d ",bpm); #ifdef HAVE_LCD_BITMAP display->puts(0,3, buffer); #else display->puts(0,0, buffer); #endif /* HAVE_LCD_BITMAP */ rb->snprintf(buffer, sizeof(buffer), "Vol: %d", rb->global_settings->volume); #ifdef HAVE_LCD_BITMAP display->puts(10, 3, buffer); #else display->puts(0,1, buffer); #endif /* HAVE_LCD_BITMAP */ #ifdef HAVE_LCD_BITMAP display->drawline(0, 12, 111, 12); if(sound_paused) display->puts(0,2,METRONOME_MSG_START); else display->puts(0,2,METRONOME_MSG_STOP); #endif /* HAVE_LCD_BITMAP */ display->update(); } void draw_display(void) { int i; FOR_NB_SCREENS(i) metronome_draw(rb->screens[i]); } /* helper function to change the volume by a certain amount, +/- ripped from video.c */ void change_volume(int delta){ int minvol = rb->sound_min(SOUND_VOLUME); int maxvol = rb->sound_max(SOUND_VOLUME); int vol = rb->global_settings->volume + delta; if (vol > maxvol) vol = maxvol; else if (vol < minvol) vol = minvol; if (vol != rb->global_settings->volume) { rb->sound_set(SOUND_VOLUME, vol); rb->global_settings->volume = vol; draw_display(); } } /*function to accelerate bpm change*/ void change_bpm(int direction){ if((bpm_step_counter < 20) || (bpm > 389) || (bpm < 10)) bpm = bpm + direction; else if (bpm_step_counter < 60) bpm = bpm + direction * 2; else bpm = bpm + direction * 9; if (bpm > 400) bpm = 400; if (bpm < 1) bpm = 1; calc_period(); draw_display(); bpm_step_counter++; } void timer_callback(void) { if(minitick >= period){ minitick = 0; if(!sound_active && !sound_paused && !tap_count) { #if CONFIG_CODEC == SWCODEC /* On SWCODEC we can't call play_tock() directly from an ISR. */ need_to_play = true; #else play_tock(); #endif rb->reset_poweroff_timer(); } } else { minitick++; } if (tap_count) { tap_time++; if (tap_count > 1 && tap_time > tap_timeout) tap_count = 0; } } void cleanup(void *parameter) { (void)parameter; rb->timer_unregister(); MET_PLAY_STOP; /* stop audio ISR */ rb->led(0); } void tap(void) { if (tap_count == 0 || tap_time < tap_count) { tap_time = 0; } else { if (tap_time > 0) { bpm = 61440/(tap_time/tap_count); if (bpm > 400) bpm = 400; } calc_period(); draw_display(); tap_timeout = (tap_count+2)*tap_time/tap_count; } tap_count++; minitick = 0; /* sync tock to tapping */ play_tock(); reset_tap = false; } enum plugin_status plugin_start(struct plugin_api* api, void* parameter){ int button; enum plugin_status status; (void)parameter; rb = api; if (MET_IS_PLAYING) MET_PLAY_STOP; /* stop audio IS */ #if CONFIG_CODEC != SWCODEC rb->bitswap(sound, sizeof(sound)); #else prepare_tock(); #if INPUT_SRC_CAPS != 0 /* Select playback */ rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); #endif rb->pcm_set_frequency(SAMPR_44); #endif /* CONFIG_CODEC != SWCODEC */ calc_period(); rb->timer_register(1, NULL, TIMER_FREQ/1024, 1, timer_callback); draw_display(); /* main loop */ while (true){ reset_tap = true; #if CONFIG_CODEC == SWCODEC button = pluginlib_getaction(rb,1,plugin_contexts,PLA_ARRAY_COUNT); if (need_to_play) { need_to_play = false; play_tock(); } #else button = pluginlib_getaction(rb,TIMEOUT_BLOCK, plugin_contexts,PLA_ARRAY_COUNT); #endif /* SWCODEC */ switch (button) { case METRONOME_QUIT: /* get out of here */ cleanup(NULL); status = PLUGIN_OK; goto metronome_exit; #if CONFIG_KEYPAD == ONDIO_PAD case METRONOME_PLAY_TAP: if(sound_paused) { sound_paused = false; calc_period(); draw_display(); } else tap(); break; case METRONOME_PAUSE: if(!sound_paused) { sound_paused = true; draw_display(); } break; #else case METRONOME_PLAYPAUSE: if(sound_paused) sound_paused = false; else sound_paused = true; calc_period(); draw_display(); break; #endif /* ONDIO_PAD */ case METRONOME_VOL_UP: case METRONOME_VOL_UP_REP: change_volume(1); calc_period(); break; case METRONOME_VOL_DOWN: case METRONOME_VOL_DOWN_REP: change_volume(-1); calc_period(); break; case METRONOME_LEFT: bpm_step_counter = 0; case METRONOME_LEFT_REP: change_bpm(-1); break; case METRONOME_RIGHT: bpm_step_counter = 0; case METRONOME_RIGHT_REP: change_bpm(1); break; #ifdef METRONOME_TAP case METRONOME_TAP: tap(); break; #endif #ifdef MET_SYNC case METRONOME_SYNC: minitick = period; break; #endif default: if (rb->default_event_handler_ex(button, cleanup, NULL) == SYS_USB_CONNECTED) { status = PLUGIN_USB_CONNECTED; goto metronome_exit; } reset_tap = false; break; } if (reset_tap) { tap_count = 0; } } metronome_exit: #if CONFIG_CODEC == SWCODEC rb->pcm_set_frequency(HW_SAMPR_DEFAULT); #endif return status; }