| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (C) 2004 Matthias Wientapper |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| * |
| * 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 const 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->hline(0, 111, 12); |
| if(sound_paused) |
| display->puts(0,2,METRONOME_MSG_START); |
| else |
| display->puts(0,2,METRONOME_MSG_STOP); |
| display->setfont(FONT_UI); |
| #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(const struct plugin_api* api, const 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 IF_COP(, CPU)); |
| |
| 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; |
| } |