diff --git a/apps/abrepeat.c b/apps/abrepeat.c index 3a3d5b394d..b83a7b6aa3 100644 --- a/apps/abrepeat.c +++ b/apps/abrepeat.c @@ -19,16 +19,20 @@ #include "abrepeat.h" -#include "settings.h" -#include "audio.h" -#include "kernel.h" - -#ifdef HAVE_LCD_BITMAP -#include "lcd.h" -#endif - #ifdef AB_REPEAT_ENABLE +unsigned int ab_A_marker IDATA_ATTR = AB_MARKER_NONE; +unsigned int ab_B_marker IDATA_ATTR = AB_MARKER_NONE; + +#if (CONFIG_CODEC == SWCODEC) +void ab_end_of_track_report(void) +{ + if ( ab_A_marker_set() && ! ab_B_marker_set() ) + { + ab_jump_to_A_marker(); + } +} +#else static int ab_audio_event_handler(unsigned short event, unsigned long data) { int rc = AUDIO_EVENT_RC_IGNORED; @@ -38,7 +42,8 @@ static int ab_audio_event_handler(unsigned short event, unsigned long data) { case AUDIO_EVENT_POS_REPORT: { - if ( ! (audio_status() & AUDIO_STATUS_PAUSE) && ab_reached_B_marker(data) ) + if ( ! (audio_status() & AUDIO_STATUS_PAUSE) && + ab_reached_B_marker(data) ) { ab_jump_to_A_marker(); rc = AUDIO_EVENT_RC_HANDLED; @@ -58,6 +63,7 @@ static int ab_audio_event_handler(unsigned short event, unsigned long data) } return rc; } +#endif void ab_repeat_init(void) { @@ -65,30 +71,13 @@ void ab_repeat_init(void) if ( ! ab_initialized ) { ab_initialized = true; +#if (CONFIG_CODEC != SWCODEC) audio_register_event_handler(ab_audio_event_handler, AUDIO_EVENT_POS_REPORT | AUDIO_EVENT_END_OF_TRACK ); +#endif } } -static unsigned int ab_A_marker = AB_MARKER_NONE; -static unsigned int ab_B_marker = AB_MARKER_NONE; - -bool ab_repeat_mode_enabled(void) -{ - extern struct user_settings global_settings; - return global_settings.repeat_mode == REPEAT_AB; -} - -bool ab_A_marker_set(void) -{ - return ab_A_marker != AB_MARKER_NONE; -} - -bool ab_B_marker_set(void) -{ - return ab_B_marker != AB_MARKER_NONE; -} - unsigned int ab_get_A_marker(void) { return ab_A_marker; @@ -99,24 +88,6 @@ unsigned int ab_get_B_marker(void) return ab_B_marker; } -bool ab_reached_B_marker(unsigned int song_position) -{ -/* following is the size of the window in which we'll detect that the B marker -was hit; it must be larger than the frequency (in milliseconds) at which this -function is called otherwise detection of the B marker will be unreliable; -we assume that this function will be called on each system tick and derive -the window size from this with a generous margin of error (note: the number -of ticks per second is given by HZ) */ -#define B_MARKER_DETECT_WINDOW ((1000/HZ)*10) - if (ab_B_marker != AB_MARKER_NONE) - { - if ( (song_position >= ab_B_marker) - && (song_position <= (ab_B_marker+B_MARKER_DETECT_WINDOW)) ) - return true; - } - return false; -} - /* determines if the given song position is earlier than the A mark; intended for use in handling the jump NEXT and PREV commands */ bool ab_before_A_marker(unsigned int song_position) @@ -129,9 +100,9 @@ bool ab_before_A_marker(unsigned int song_position) intended for use in handling the jump PREV command */ bool ab_after_A_marker(unsigned int song_position) { -/* following is the size of the virtual A marker; we pretend that the A marker is -larger than a single instant in order to give the user time to hit PREV again to -jump back to the start of the song; it should be large enough to allow a +/* following is the size of the virtual A marker; we pretend that the A marker +is larger than a single instant in order to give the user time to hit PREV again +to jump back to the start of the song; it should be large enough to allow a reasonable amount of time for the typical user to react */ #define A_MARKER_VIRTUAL_SIZE 1000 return (ab_A_marker != AB_MARKER_NONE) @@ -140,12 +111,16 @@ reasonable amount of time for the typical user to react */ void ab_jump_to_A_marker(void) { +#if (CONFIG_CODEC == SWCODEC) + audio_seamless_seek(ab_A_marker); +#else bool paused = (audio_status() & AUDIO_STATUS_PAUSE) != 0; if ( ! paused ) audio_pause(); audio_ff_rewind(ab_A_marker); if ( ! paused ) audio_resume(); +#endif } void ab_reset_markers(void) @@ -182,37 +157,42 @@ void ab_set_B_marker(unsigned int song_position) #ifdef HAVE_LCD_BITMAP -static int ab_calc_mark_x_pos(int mark, int capacity, int offset, int size) +static inline int ab_calc_mark_x_pos(int mark, int capacity, + int offset, int size) { int w = size - offset; return offset + ( (w * mark) / capacity ); } -static void ab_draw_veritcal_line_mark(int x, int y, int h) +static inline void ab_draw_veritcal_line_mark(struct screen * screen, + int x, int y, int h) { - lcd_set_drawmode(DRMODE_COMPLEMENT); - lcd_vline(x, y, y+h-1); + screen->set_drawmode(DRMODE_COMPLEMENT); + screen->vline(x, y, y+h-1); } #define DIRECTION_RIGHT 1 #define DIRECTION_LEFT -1 -static void ab_draw_arrow_mark(int x, int y, int h, int direction) +static inline void ab_draw_arrow_mark(struct screen * screen, + int x, int y, int h, int direction) { /* draw lines in decreasing size until a height of zero is reached */ - lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); + screen->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); while( h > 0 ) { - lcd_vline(x, y, y+h-1); + screen->vline(x, y, y+h-1); h -= 2; y++; x += direction; - lcd_set_drawmode(DRMODE_COMPLEMENT); + screen->set_drawmode(DRMODE_COMPLEMENT); } } -void ab_draw_markers(int capacity, int x, int y, int w, int h) +void ab_draw_markers(struct screen * screen, int capacity, + int x, int y, int h) { + int w = screen->width; /* if both markers are set, determine if they're far enough apart to draw arrows */ if ( ab_A_marker_set() && ab_B_marker_set() ) @@ -222,13 +202,13 @@ void ab_draw_markers(int capacity, int x, int y, int w, int h) int arrow_width = (h+1) / 2; if ( (xb-xa) < (arrow_width*2) ) { - ab_draw_veritcal_line_mark(xa, y, h); - ab_draw_veritcal_line_mark(xb, y, h); + ab_draw_veritcal_line_mark(screen, xa, y, h); + ab_draw_veritcal_line_mark(screen, xb, y, h); } else { - ab_draw_arrow_mark(xa, y, h, DIRECTION_RIGHT); - ab_draw_arrow_mark(xb, y, h, DIRECTION_LEFT); + ab_draw_arrow_mark(screen, xa, y, h, DIRECTION_RIGHT); + ab_draw_arrow_mark(screen, xb, y, h, DIRECTION_LEFT); } } else @@ -236,12 +216,12 @@ void ab_draw_markers(int capacity, int x, int y, int w, int h) if (ab_A_marker_set()) { int xa = ab_calc_mark_x_pos(ab_A_marker, capacity, x, w); - ab_draw_arrow_mark(xa, y, h, DIRECTION_RIGHT); + ab_draw_arrow_mark(screen, xa, y, h, DIRECTION_RIGHT); } if (ab_B_marker_set()) { int xb = ab_calc_mark_x_pos(ab_B_marker, capacity, x, w); - ab_draw_arrow_mark(xb, y, h, DIRECTION_LEFT); + ab_draw_arrow_mark(screen, xb, y, h, DIRECTION_LEFT); } } } diff --git a/apps/abrepeat.h b/apps/abrepeat.h index 113a1f5ee6..143e57b371 100644 --- a/apps/abrepeat.h +++ b/apps/abrepeat.h @@ -20,27 +20,95 @@ #define _ABREPEAT_H_ #include "system.h" -#include #ifdef AB_REPEAT_ENABLE +#include "audio.h" +#include "kernel.h" +#include #define AB_MARKER_NONE 0 +#if (AB_REPEAT_ENABLE == 1) +#include "settings.h" +#endif + void ab_repeat_init(void); -bool ab_repeat_mode_enabled(void); // test if a/b repeat is enabled -bool ab_A_marker_set(void); -bool ab_B_marker_set(void); unsigned int ab_get_A_marker(void); unsigned int ab_get_B_marker(void); -bool ab_reached_B_marker(unsigned int song_position); bool ab_before_A_marker(unsigned int song_position); bool ab_after_A_marker(unsigned int song_position); void ab_jump_to_A_marker(void); void ab_reset_markers(void); void ab_set_A_marker(unsigned int song_position); void ab_set_B_marker(unsigned int song_position); +#if (CONFIG_CODEC == SWCODEC) +void ab_end_of_track_report(void); +#endif #ifdef HAVE_LCD_BITMAP -void ab_draw_markers(int capacity, int x, int y, int w, int h); +#include "screen_access.h" +void ab_draw_markers(struct screen * screen, int capacity, + int x, int y, int h); +#endif + +/* These functions really need to be inlined for speed */ +extern unsigned int ab_A_marker; +extern unsigned int ab_B_marker; + +static inline bool ab_A_marker_set(void) +{ + return ab_A_marker != AB_MARKER_NONE; +} + +static inline bool ab_B_marker_set(void) +{ + return ab_B_marker != AB_MARKER_NONE; +} + +static inline bool ab_repeat_mode_enabled(void) +{ +#if (AB_REPEAT_ENABLE == 2) + return ab_A_marker_set() || ab_B_marker_set(); +#else + return global_settings.repeat_mode == REPEAT_AB; +#endif +} + +static inline bool ab_reached_B_marker(unsigned int song_position) +{ +/* following is the size of the window in which we'll detect that the B marker +was hit; it must be larger than the frequency (in milliseconds) at which this +function is called otherwise detection of the B marker will be unreliable */ +#if (CONFIG_CODEC == SWCODEC) +/* On swcodec, the worst case seems to be 9600kHz with 1024 samples between + * calls, meaning ~9 calls per second, look within 1/5 of a second */ +#define B_MARKER_DETECT_WINDOW 200 +#else +/* we assume that this function will be called on each system tick and derive +the window size from this with a generous margin of error (note: the number +of ticks per second is given by HZ) */ +#define B_MARKER_DETECT_WINDOW ((1000/HZ)*10) +#endif + if (ab_B_marker != AB_MARKER_NONE) + { + if ( (song_position >= ab_B_marker) + && (song_position <= (ab_B_marker+B_MARKER_DETECT_WINDOW)) ) + return true; + } + return false; +} + +#if (CONFIG_CODEC == SWCODEC) +static inline void ab_position_report(unsigned long position) +{ + if (ab_repeat_mode_enabled()) + { + if ( !(audio_status() & AUDIO_STATUS_PAUSE) && + ab_reached_B_marker(position) ) + { + ab_jump_to_A_marker(); + } + } +} #endif #endif diff --git a/apps/bookmark.c b/apps/bookmark.c index 2eb4fc9d02..c8c6f941f2 100644 --- a/apps/bookmark.c +++ b/apps/bookmark.c @@ -739,7 +739,7 @@ static void display_bookmark(const char* bookmark, /* bookmark shuffle and repeat states*/ switch (repeat_mode) { -#ifdef AB_REPEAT_ENABLE +#if (AB_REPEAT_ENABLE == 1) case REPEAT_AB: statusbar_icon_play_mode(Icon_RepeatAB); break; diff --git a/apps/gui/gwps-common.c b/apps/gui/gwps-common.c index b9b3e9bcd0..31b4de239f 100644 --- a/apps/gui/gwps-common.c +++ b/apps/gui/gwps-common.c @@ -31,6 +31,7 @@ #ifdef HAVE_LCD_CHARCELLS #include "hwcompat.h" #endif +#include "abrepeat.h" #include "mp3_playback.h" #include "backlight.h" #include "lang.h" @@ -1431,8 +1432,8 @@ bool gui_wps_refresh(struct gui_wps *gwps, int ffwd_offset, HORIZONTAL); #ifdef AB_REPEAT_ENABLE if ( ab_repeat_mode_enabled() ) - ab_draw_markers(state->id3->length, 0, sby, LCD_WIDTH, - PROGRESS_BAR_HEIGHT); + ab_draw_markers(display, state->id3->length, 0, sby, + PROGRESS_BAR_HEIGHT); #endif update_line = true; } diff --git a/apps/gui/gwps.c b/apps/gui/gwps.c index efe207f94a..f75dd80d0f 100644 --- a/apps/gui/gwps.c +++ b/apps/gui/gwps.c @@ -392,11 +392,17 @@ long gui_wps_show(void) #ifdef AB_REPEAT_ENABLE /* if we're in A/B repeat mode and the current position is past the A marker, jump back to the A marker... */ - if ( ab_repeat_mode_enabled() - && ab_after_A_marker(wps_state.id3->elapsed) ) + if ( ab_repeat_mode_enabled() ) { - ab_jump_to_A_marker(); - break; + if ( ab_after_A_marker(wps_state.id3->elapsed) ) + { + ab_jump_to_A_marker(); + break; +#if (AB_REPEAT_ENABLE == 2) + } else { + ab_reset_markers(); +#endif + } } /* ...otherwise, do it normally */ #endif @@ -451,11 +457,17 @@ long gui_wps_show(void) #ifdef AB_REPEAT_ENABLE /* if we're in A/B repeat mode and the current position is before the A marker, jump to the A marker... */ - if ( ab_repeat_mode_enabled() - && ab_before_A_marker(wps_state.id3->elapsed) ) + if ( ab_repeat_mode_enabled() ) { - ab_jump_to_A_marker(); - break; + if ( ab_before_A_marker(wps_state.id3->elapsed) ) + { + ab_jump_to_A_marker(); + break; +#if (AB_REPEAT_ENABLE == 2) + } else { + ab_reset_markers(); +#endif + } } /* ...otherwise, do it normally */ #endif @@ -545,6 +557,33 @@ long gui_wps_show(void) #ifdef AB_REPEAT_ENABLE +#ifdef WPS_AB_SINGLE + case WPS_AB_SINGLE: +#ifdef WPS_AB_SINGLE_PRE + if (lastbutton != WPS_AB_SINGLE_PRE) + break; +#endif +/* If we are using the menu option to enable ab_repeat mode, don't do anything + * when it's disabled */ +#if (AB_REPEAT_ENABLE == 1) + if (!ab_repeat_mode_enabled()) + break; +#endif + if (ab_A_marker_set()) { + update_track = true; + if (ab_B_marker_set()) { + ab_reset_markers(); + break; + } + ab_set_B_marker(wps_state.id3->elapsed); + ab_jump_to_A_marker(); + break; + } + ab_set_A_marker(wps_state.id3->elapsed); + break; +#endif + + #ifdef WPS_AB_SET_A_MARKER /* set A marker for A-B repeat */ case WPS_AB_SET_A_MARKER: diff --git a/apps/gui/gwps.h b/apps/gui/gwps.h index 1017181a21..e00f6288ef 100644 --- a/apps/gui/gwps.h +++ b/apps/gui/gwps.h @@ -68,6 +68,12 @@ #define WPS_RC_CONTEXT (BUTTON_RC_MENU | BUTTON_REPEAT) #define WPS_RC_QUICK (BUTTON_RC_MODE | BUTTON_REPEAT) +#ifdef AB_REPEAT_ENABLE +#define WPS_AB_SET_A_MARKER (BUTTON_ON | BUTTON_LEFT) +#define WPS_AB_SET_B_MARKER (BUTTON_ON | BUTTON_RIGHT) +#define WPS_AB_RESET_AB_MARKERS (BUTTON_ON | BUTTON_OFF) +#endif + #elif CONFIG_KEYPAD == RECORDER_PAD #define WPS_NEXT (BUTTON_RIGHT | BUTTON_REL) #define WPS_NEXT_PRE BUTTON_RIGHT diff --git a/apps/gui/quickscreen.c b/apps/gui/quickscreen.c index 07d4e2eee6..6aac24f81e 100644 --- a/apps/gui/quickscreen.c +++ b/apps/gui/quickscreen.c @@ -20,6 +20,7 @@ #include "quickscreen.h" #ifdef HAS_QUICKSCREEN +#include "system.h" #include "icons.h" #include "textarea.h" #include "font.h" diff --git a/apps/gui/statusbar.c b/apps/gui/statusbar.c index d8e611af85..34ac5283af 100644 --- a/apps/gui/statusbar.c +++ b/apps/gui/statusbar.c @@ -247,11 +247,11 @@ void gui_statusbar_draw(struct gui_statusbar * bar, bool force_redraw) Icon_Play); switch (bar->info.repeat) { -#ifdef AB_REPEAT_ENABLE +#if (AB_REPEAT_ENABLE == 1) case REPEAT_AB: gui_statusbar_icon_play_mode(display, Icon_RepeatAB); break; -#endif /* AB_REPEAT_ENABLE */ +#endif /* AB_REPEAT_ENABLE == 1 */ case REPEAT_ONE: gui_statusbar_icon_play_mode(display, Icon_RepeatOne); diff --git a/apps/gui/yesno.c b/apps/gui/yesno.c index 3345a691ca..225f6d3f43 100644 --- a/apps/gui/yesno.c +++ b/apps/gui/yesno.c @@ -1,4 +1,5 @@ #include "yesno.h" +#include "system.h" #include "kernel.h" #include "misc.h" #include "lang.h" diff --git a/apps/playback.c b/apps/playback.c index 932bf115f9..0331405846 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -49,6 +49,7 @@ #include "pcm_record.h" #include "buffer.h" #include "dsp.h" +#include "abrepeat.h" #ifdef HAVE_LCD_BITMAP #include "icons.h" #include "peakmeter.h" @@ -418,6 +419,9 @@ void codec_set_elapsed_callback(unsigned int value) if (ci.stop_codec || current_codec == CODEC_IDX_VOICE) return ; +#ifdef AB_REPEAT_ENABLE + ab_position_report(value); +#endif latency = pcmbuf_get_latency(); if (value < latency) { @@ -1663,7 +1667,10 @@ bool codec_request_next_track_callback(void) the core has been requested the codec to be terminated. */ return !ci_voice.stop_codec && queue_empty(&voice_codec_queue); } - +#ifdef AB_REPEAT_ENABLE + ab_end_of_track_report(); +#endif + pcmbuf_set_position_callback(pcmbuf_position_callback); if (ci.stop_codec || !playing) diff --git a/apps/playlist.c b/apps/playlist.c index e8239b21f2..d760b2450d 100644 --- a/apps/playlist.c +++ b/apps/playlist.c @@ -994,7 +994,7 @@ static int get_next_index(const struct playlist_info* playlist, int steps, } case REPEAT_ONE: -#ifdef AB_REPEAT_ENABLE +#if (AB_REPEAT_ENABLE == 1) case REPEAT_AB: #endif next_index = current_index; @@ -2205,7 +2205,7 @@ int playlist_next(int steps) int index; if ( (steps > 0) -#ifdef AB_REPEAT_ENABLE +#if (AB_REPEAT_ENABLE == 1) && (global_settings.repeat_mode != REPEAT_AB) #endif && (global_settings.repeat_mode != REPEAT_ONE) ) diff --git a/apps/screens.c b/apps/screens.c index 2cb0fb576a..9897e2541d 100644 --- a/apps/screens.c +++ b/apps/screens.c @@ -46,7 +46,6 @@ #include "debug.h" #include "led.h" #include "sound.h" -#include "abrepeat.h" #include "gwps-common.h" #include "splash.h" #include "statusbar.h" @@ -515,7 +514,7 @@ bool quick_screen_quick(int button_enter) [REPEAT_ALL]={ STR(LANG_REPEAT_ALL) }, [REPEAT_ONE]={ STR(LANG_REPEAT_ONE) }, [REPEAT_SHUFFLE]={ STR(LANG_SHUFFLE) }, -#ifdef AB_REPEAT_ENABLE +#if (AB_REPEAT_ENABLE == 1) [REPEAT_AB]={ STR(LANG_REPEAT_AB) } #endif }; diff --git a/apps/settings.h b/apps/settings.h index 4d7d4fb707..d60c39f6e0 100644 --- a/apps/settings.h +++ b/apps/settings.h @@ -25,7 +25,6 @@ #include "file.h" #include "dircache.h" #include "timefuncs.h" -#include "abrepeat.h" #ifdef HAVE_BACKLIGHT_BRIGHTNESS #include "backlight.h" /* for [MIN|MAX]_BRIGHTNESS_SETTING */ @@ -478,7 +477,7 @@ enum REPEAT_ALL, REPEAT_ONE, REPEAT_SHUFFLE, -#ifdef AB_REPEAT_ENABLE +#if (AB_REPEAT_ENABLE == 1) REPEAT_AB, #endif NUM_REPEAT_MODES diff --git a/apps/settings_menu.c b/apps/settings_menu.c index 5338be34a3..35b4d8772d 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c @@ -580,7 +580,7 @@ static bool repeat_mode(void) { STR(LANG_REPEAT_ALL) }, { STR(LANG_REPEAT_ONE) }, { STR(LANG_SHUFFLE) }, -#ifdef AB_REPEAT_ENABLE +#if (AB_REPEAT_ENABLE == 1) { STR(LANG_REPEAT_AB) } #endif }; diff --git a/firmware/export/config-h100.h b/firmware/export/config-h100.h index 34c0cc4f70..ea49dd8e2c 100644 --- a/firmware/export/config-h100.h +++ b/firmware/export/config-h100.h @@ -47,6 +47,8 @@ /* The number of bytes reserved for loadable plugins */ #define PLUGIN_BUFFER_SIZE 0x80000 +#define AB_REPEAT_ENABLE 1 + #define CONFIG_TUNER TEA5767 #define CONFIG_TUNER_XTAL 32768 diff --git a/firmware/export/config-h120.h b/firmware/export/config-h120.h index 2790063c64..fee5f4862c 100644 --- a/firmware/export/config-h120.h +++ b/firmware/export/config-h120.h @@ -43,6 +43,8 @@ /* The number of bytes reserved for loadable plugins */ #define PLUGIN_BUFFER_SIZE 0x80000 +#define AB_REPEAT_ENABLE 1 + #define CONFIG_TUNER TEA5767 #define CONFIG_TUNER_XTAL 32768 diff --git a/firmware/export/config-h300.h b/firmware/export/config-h300.h index ded20c9129..d57975d45d 100644 --- a/firmware/export/config-h300.h +++ b/firmware/export/config-h300.h @@ -48,6 +48,8 @@ /* The number of bytes reserved for loadable plugins */ #define PLUGIN_BUFFER_SIZE 0x80000 +#define AB_REPEAT_ENABLE 1 + #define CONFIG_TUNER TEA5767 #define CONFIG_TUNER_XTAL 32768