From 0ad617cbf0cc85dde241345194b76b590df567a8 Mon Sep 17 00:00:00 2001 From: Linus Nielsen Feltzing Date: Sun, 21 Aug 2005 23:01:12 +0000 Subject: [PATCH] Patch #1105616 by Ray Lambert - A-B Repeat for Archos studio/recorder, still not 100% complete, but I wanted to commit it before the 2.5 feature freeze git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7380 a1c6a512-1295-4272-9138-f99709370657 --- apps/SOURCES | 1 + apps/abrepeat.c | 251 ++++++++++++++++++++++++++++ apps/abrepeat.h | 48 ++++++ apps/bookmark.c | 7 + apps/lang/english.lang | 6 + apps/playlist.c | 10 +- apps/recorder/icons.c | 1 + apps/recorder/icons.h | 1 + apps/screens.c | 9 +- apps/settings.c | 4 +- apps/settings.h | 16 +- apps/settings_menu.c | 6 +- apps/status.c | 7 + apps/wps-display.c | 20 ++- apps/wps.c | 74 +++++++- apps/wps.h | 12 ++ firmware/export/audio.h | 40 +++++ firmware/export/config-fmrecorder.h | 2 + firmware/export/config-player.h | 2 + firmware/export/config-recorder.h | 2 + firmware/export/config-recorderv2.h | 2 + firmware/mpeg.c | 68 +++++++- 22 files changed, 566 insertions(+), 23 deletions(-) create mode 100644 apps/abrepeat.c create mode 100644 apps/abrepeat.h diff --git a/apps/SOURCES b/apps/SOURCES index e8fd2d2ddb..3c933d22b6 100644 --- a/apps/SOURCES +++ b/apps/SOURCES @@ -2,6 +2,7 @@ logfdisp.c #endif alarm_menu.c +abrepeat.c bookmark.c credits.c debug_menu.c diff --git a/apps/abrepeat.c b/apps/abrepeat.c new file mode 100644 index 0000000000..3a3d5b394d --- /dev/null +++ b/apps/abrepeat.c @@ -0,0 +1,251 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $$ + * + * Copyright (C) 2005 Ray Lambert + * + * 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 "abrepeat.h" + +#include "settings.h" +#include "audio.h" +#include "kernel.h" + +#ifdef HAVE_LCD_BITMAP +#include "lcd.h" +#endif + +#ifdef AB_REPEAT_ENABLE + +static int ab_audio_event_handler(unsigned short event, unsigned long data) +{ + int rc = AUDIO_EVENT_RC_IGNORED; + if ( ab_repeat_mode_enabled() ) + { + switch(event) + { + case AUDIO_EVENT_POS_REPORT: + { + if ( ! (audio_status() & AUDIO_STATUS_PAUSE) && ab_reached_B_marker(data) ) + { + ab_jump_to_A_marker(); + rc = AUDIO_EVENT_RC_HANDLED; + } + break; + } + case AUDIO_EVENT_END_OF_TRACK: + { + if ( ab_A_marker_set() && ! ab_B_marker_set() ) + { + ab_jump_to_A_marker(); + rc = AUDIO_EVENT_RC_HANDLED; + } + break; + } + } + } + return rc; +} + +void ab_repeat_init(void) +{ + static bool ab_initialized = false; + if ( ! ab_initialized ) + { + ab_initialized = true; + audio_register_event_handler(ab_audio_event_handler, + AUDIO_EVENT_POS_REPORT | AUDIO_EVENT_END_OF_TRACK ); + } +} + +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; +} + +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) +{ + return (ab_A_marker != AB_MARKER_NONE) + && (song_position < ab_A_marker); +} + +/* determines if the given song position is later than the A mark; +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 +reasonable amount of time for the typical user to react */ +#define A_MARKER_VIRTUAL_SIZE 1000 + return (ab_A_marker != AB_MARKER_NONE) + && (song_position > (ab_A_marker+A_MARKER_VIRTUAL_SIZE)); +} + +void ab_jump_to_A_marker(void) +{ + bool paused = (audio_status() & AUDIO_STATUS_PAUSE) != 0; + if ( ! paused ) + audio_pause(); + audio_ff_rewind(ab_A_marker); + if ( ! paused ) + audio_resume(); +} + +void ab_reset_markers(void) +{ + ab_A_marker = AB_MARKER_NONE; + ab_B_marker = AB_MARKER_NONE; +} + +/* following is a fudge factor to help overcome the latency between +the time the user hears the passage they want to mark and the time +they actually press the button; the actual song position is adjusted +by this fudge factor when setting a mark */ +#define EAR_TO_HAND_LATENCY_FUDGE 200 + +void ab_set_A_marker(unsigned int song_position) +{ + ab_A_marker = song_position; + ab_A_marker = (ab_A_marker >= EAR_TO_HAND_LATENCY_FUDGE) + ? (ab_A_marker - EAR_TO_HAND_LATENCY_FUDGE) : 0; + /* check if markers are out of order */ + if ( (ab_B_marker != AB_MARKER_NONE) && (ab_A_marker > ab_B_marker) ) + ab_B_marker = AB_MARKER_NONE; +} + +void ab_set_B_marker(unsigned int song_position) +{ + ab_B_marker = song_position; + ab_B_marker = (ab_B_marker >= EAR_TO_HAND_LATENCY_FUDGE) + ? (ab_B_marker - EAR_TO_HAND_LATENCY_FUDGE) : 0; + /* check if markers are out of order */ + if ( (ab_A_marker != AB_MARKER_NONE) && (ab_B_marker < ab_A_marker) ) + ab_A_marker = AB_MARKER_NONE; +} + +#ifdef HAVE_LCD_BITMAP + +static 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) +{ + lcd_set_drawmode(DRMODE_COMPLEMENT); + lcd_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) +{ + /* draw lines in decreasing size until a height of zero is reached */ + lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); + while( h > 0 ) + { + lcd_vline(x, y, y+h-1); + h -= 2; + y++; + x += direction; + lcd_set_drawmode(DRMODE_COMPLEMENT); + } +} + +void ab_draw_markers(int capacity, int x, int y, int w, int h) +{ + /* if both markers are set, determine if they're far enough apart + to draw arrows */ + if ( ab_A_marker_set() && ab_B_marker_set() ) + { + int xa = ab_calc_mark_x_pos(ab_A_marker, capacity, x, w); + int xb = ab_calc_mark_x_pos(ab_B_marker, capacity, x, w); + 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); + } + else + { + ab_draw_arrow_mark(xa, y, h, DIRECTION_RIGHT); + ab_draw_arrow_mark(xb, y, h, DIRECTION_LEFT); + } + } + else + { + 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); + } + 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); + } + } +} + +#endif /* HAVE_LCD_BITMAP */ + +#endif /* AB_REPEAT_ENABLE */ diff --git a/apps/abrepeat.h b/apps/abrepeat.h new file mode 100644 index 0000000000..113a1f5ee6 --- /dev/null +++ b/apps/abrepeat.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $$ + * + * Copyright (C) 2005 Ray Lambert + * + * 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. + * + ****************************************************************************/ +#ifndef _ABREPEAT_H_ +#define _ABREPEAT_H_ + +#include "system.h" +#include + +#ifdef AB_REPEAT_ENABLE + +#define AB_MARKER_NONE 0 + +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); +#ifdef HAVE_LCD_BITMAP +void ab_draw_markers(int capacity, int x, int y, int w, int h); +#endif + +#endif + +#endif /* _ABREPEAT_H_ */ diff --git a/apps/bookmark.c b/apps/bookmark.c index 4afba78d14..c157d01662 100644 --- a/apps/bookmark.c +++ b/apps/bookmark.c @@ -47,6 +47,7 @@ #include "sprintf.h" #include "talk.h" #include "misc.h" +#include "abrepeat.h" #define MAX_BOOKMARKS 10 #define MAX_BOOKMARK_SIZE 350 @@ -778,6 +779,12 @@ static void display_bookmark(const char* bookmark, /* bookmark shuffle and repeat states*/ switch (repeat_mode) { +#ifdef AB_REPEAT_ENABLE + case REPEAT_AB: + statusbar_icon_play_mode(Icon_RepeatAB); + break; +#endif + case REPEAT_ONE: statusbar_icon_play_mode(Icon_RepeatOne); break; diff --git a/apps/lang/english.lang b/apps/lang/english.lang index eb1c887fae..75601c858b 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -3286,3 +3286,9 @@ desc: in beep volume in playback settings eng: "Strong" voice "Strong" new: + +id: LANG_REPEAT_AB +desc: repeat one song +eng: "A-B" +voice: "A-B" +new: diff --git a/apps/playlist.c b/apps/playlist.c index bd443e4f38..bc2bd05905 100644 --- a/apps/playlist.c +++ b/apps/playlist.c @@ -85,6 +85,7 @@ #include "misc.h" #include "button.h" #include "filetree.h" +#include "abrepeat.h" #ifdef HAVE_LCD_BITMAP #include "icons.h" #include "widgets.h" @@ -867,6 +868,9 @@ static int get_next_index(const struct playlist_info* playlist, int steps, } case REPEAT_ONE: +#ifdef AB_REPEAT_ENABLE + case REPEAT_AB: +#endif next_index = current_index; break; @@ -1916,7 +1920,11 @@ int playlist_next(int steps) struct playlist_info* playlist = ¤t_playlist; int index; - if (steps > 0 && global_settings.repeat_mode != REPEAT_ONE) + if ( (steps > 0) +#ifdef AB_REPEAT_ENABLE + && (global_settings.repeat_mode != REPEAT_AB) +#endif + && (global_settings.repeat_mode != REPEAT_ONE) ) { int i, j; diff --git a/apps/recorder/icons.c b/apps/recorder/icons.c index 6e60905478..78f3039df2 100644 --- a/apps/recorder/icons.c +++ b/apps/recorder/icons.c @@ -73,6 +73,7 @@ const unsigned char bitmap_icons_7x8[][7] = {0x3e,0x41,0x51,0x41,0x45,0x41,0x3e}, /* Shuffle playmode (dice) */ {0x04,0x0c,0x1c,0x3c,0x1c,0x0c,0x04}, /* Down-arrow */ {0x20,0x30,0x38,0x3c,0x38,0x30,0x20}, /* Up-arrow */ + {0x7f,0x04,0x4e,0x5f,0x44,0x38,0x7f}, /* Repeat-AB playmode */ }; #if CONFIG_LED == LED_VIRTUAL diff --git a/apps/recorder/icons.h b/apps/recorder/icons.h index c0b870fbb6..0ea29bb324 100644 --- a/apps/recorder/icons.h +++ b/apps/recorder/icons.h @@ -58,6 +58,7 @@ enum icons_7x8 { Icon_Shuffle, Icon_DownArrow, Icon_UpArrow, + Icon_RepeatAB, Icon_Last }; diff --git a/apps/screens.c b/apps/screens.c index e259c03407..c9acfa6a05 100644 --- a/apps/screens.c +++ b/apps/screens.c @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2002 Björn Stenberg + * Copyright (C) 2002 Bj�n Stenberg * * 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. @@ -45,6 +45,7 @@ #include "debug.h" #include "led.h" #include "sound.h" +#include "abrepeat.h" #include "wps-display.h" #if defined(HAVE_LCD_BITMAP) #include "widgets.h" @@ -593,6 +594,12 @@ bool quick_screen(int context, int button) case REPEAT_SHUFFLE: ptr = str(LANG_SHUFFLE); break; + +#ifdef AB_REPEAT_ENABLE + case REPEAT_AB: + ptr = str(LANG_REPEAT_AB); + break; +#endif } lcd_getstringsize(str(LANG_REPEAT),&w,&h); diff --git a/apps/settings.c b/apps/settings.c index 9bcdc558a2..4811ad2320 100644 --- a/apps/settings.c +++ b/apps/settings.c @@ -78,7 +78,7 @@ const char rec_base_directory[] = REC_BASE_DIR; #include "pcm_playback.h" #endif -#define CONFIG_BLOCK_VERSION 24 +#define CONFIG_BLOCK_VERSION 25 #define CONFIG_BLOCK_SIZE 512 #define RTC_BLOCK_SIZE 44 @@ -205,7 +205,7 @@ static const struct bit_entry rtc_bits[] = {16 | SIGNED, S_O(resume_first_index), 0, NULL, NULL }, {32 | SIGNED, S_O(resume_offset), -1, NULL, NULL }, {32 | SIGNED, S_O(resume_seed), -1, NULL, NULL }, - {2, S_O(repeat_mode), REPEAT_ALL, "repeat", "off,all,one,shuffle" }, + {3, S_O(repeat_mode), REPEAT_ALL, "repeat", "off,all,one,shuffle,ab" }, /* LCD */ {6, S_O(contrast), 40, "contrast", NULL }, #ifdef CONFIG_BACKLIGHT diff --git a/apps/settings.h b/apps/settings.h index 767fa49463..d0041d2e99 100644 --- a/apps/settings.h +++ b/apps/settings.h @@ -24,6 +24,7 @@ #include "config.h" #include "file.h" #include "timefuncs.h" +#include "abrepeat.h" #define ROCKBOX_DIR "/.rockbox" #define FONT_DIR "/fonts" @@ -229,7 +230,7 @@ struct user_settings /* misc options */ - int repeat_mode; /* 0=off 1=repeat all 2=repeat one 3=shuffle */ + int repeat_mode; /* 0=off 1=repeat all 2=repeat one 3=shuffle 4=ab */ int dirfilter; /* 0=display all, 1=only supported, 2=only music, 3=dirs+playlists, 4=ID3 database */ bool sort_case; /* dir sort order: 0=case insensitive, 1=sensitive */ @@ -403,8 +404,17 @@ extern const char rec_base_directory[]; #define SETTINGS_ALL 3 /* both */ /* repeat mode options */ -enum { REPEAT_OFF, REPEAT_ALL, REPEAT_ONE, REPEAT_SHUFFLE, -NUM_REPEAT_MODES }; +enum +{ + REPEAT_OFF, + REPEAT_ALL, + REPEAT_ONE, + REPEAT_SHUFFLE, +#ifdef AB_REPEAT_ENABLE + REPEAT_AB, +#endif + NUM_REPEAT_MODES +}; /* dir filter options */ /* Note: Any new filter modes need to be added before NUM_FILTER_MODES. diff --git a/apps/settings_menu.c b/apps/settings_menu.c index 780c2940b4..9f7259620e 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c @@ -46,6 +46,7 @@ #include "talk.h" #include "timefuncs.h" #include "misc.h" +#include "abrepeat.h" #include "power.h" #include "database.h" @@ -604,11 +605,14 @@ static bool repeat_mode(void) { STR(LANG_REPEAT_ALL) }, { STR(LANG_REPEAT_ONE) }, { STR(LANG_SHUFFLE) }, +#ifdef AB_REPEAT_ENABLE + { STR(LANG_REPEAT_AB) } +#endif }; int old_repeat = global_settings.repeat_mode; result = set_option( str(LANG_REPEAT), &global_settings.repeat_mode, - INT, names, 4, NULL ); + INT, names, NUM_REPEAT_MODES, NULL ); if (old_repeat != global_settings.repeat_mode && (audio_status() & AUDIO_STATUS_PLAY)) diff --git a/apps/status.c b/apps/status.c index c7f46db6fb..923b549bf0 100644 --- a/apps/status.c +++ b/apps/status.c @@ -27,6 +27,7 @@ #include "mp3_playback.h" #include "audio.h" #include "wps.h" +#include "abrepeat.h" #ifdef HAVE_RTC #include "timefuncs.h" #endif @@ -262,6 +263,12 @@ void status_draw(bool force_redraw) info.redraw_volume = statusbar_icon_volume(info.volume); statusbar_icon_play_state(current_playmode() + Icon_Play); switch (info.repeat) { +#ifdef AB_REPEAT_ENABLE + case REPEAT_AB: + statusbar_icon_play_mode(Icon_RepeatAB); + break; +#endif + case REPEAT_ONE: statusbar_icon_play_mode(Icon_RepeatOne); break; diff --git a/apps/wps-display.c b/apps/wps-display.c index f6fda70377..892db8ce1a 100644 --- a/apps/wps-display.c +++ b/apps/wps-display.c @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2002 Björn Stenberg + * Copyright (C) 2002 Bj�n Stenberg * * 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. @@ -44,6 +44,7 @@ #include "sprintf.h" #include "backlight.h" #include "button.h" +#include "abrepeat.h" #ifdef HAVE_LCD_BITMAP #include "icons.h" @@ -1214,11 +1215,18 @@ bool wps_refresh(struct mp3entry* id3, #ifdef HAVE_LCD_BITMAP /* progress */ - if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) { - scrollbar(0, i*h + offset + (h > 7 ? (h - 6) / 2 : 1), LCD_WIDTH, 6, - id3->length?id3->length:1, 0, - id3->length?id3->elapsed + ff_rewind_count:0, - HORIZONTAL); + if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) + { +#define PROGRESS_BAR_HEIGHT 6 /* this should probably be defined elsewhere; config-*.h perhaps? */ + int sby = i*h + offset + (h > 7 ? (h - 6) / 2 : 1); + scrollbar(0, sby, LCD_WIDTH, PROGRESS_BAR_HEIGHT, + id3->length?id3->length:1, 0, + id3->length?id3->elapsed + ff_rewind_count:0, + HORIZONTAL); +#ifdef AB_REPEAT_ENABLE + if ( ab_repeat_mode_enabled() ) + ab_draw_markers(id3->length, 0, sby, LCD_WIDTH, PROGRESS_BAR_HEIGHT); +#endif update_line = true; } if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) { diff --git a/apps/wps.c b/apps/wps.c index 6302626004..bf0283015f 100644 --- a/apps/wps.c +++ b/apps/wps.c @@ -51,6 +51,7 @@ #include "misc.h" #include "sound.h" #include "onplay.h" +#include "abrepeat.h" #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */ /* 3% of 30min file == 54s step size */ @@ -347,6 +348,11 @@ long wps_show(void) lcd_setmargins(0, 0); #endif +#ifdef AB_REPEAT_ENABLE + ab_repeat_init(); + ab_reset_markers(); +#endif + ff_rewind = false; if(audio_status() & AUDIO_STATUS_PLAY) @@ -585,6 +591,19 @@ long wps_show(void) break; #endif #endif + +#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(id3->elapsed) ) + { + ab_jump_to_A_marker(); + update_track = true; + break; + } + /* ...otherwise, do it normally */ +#endif + if (!id3 || (id3->elapsed < 3*1000)) { audio_prev(); } @@ -612,6 +631,19 @@ long wps_show(void) break; #endif #endif + +#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(id3->elapsed) ) + { + ab_jump_to_A_marker(); + update_track = true; + break; + } + /* ...otherwise, do it normally */ +#endif + audio_next(); break; @@ -674,7 +706,8 @@ long wps_show(void) /* pitch screen */ #if CONFIG_KEYPAD == RECORDER_PAD - case BUTTON_ON | BUTTON_REPEAT: + case BUTTON_ON | BUTTON_UP: + case BUTTON_ON | BUTTON_DOWN: if (2 == pitch_screen()) return SYS_USB_CONNECTED; restore = true; @@ -682,6 +715,41 @@ long wps_show(void) #endif #endif +#ifdef AB_REPEAT_ENABLE + +#ifdef WPS_AB_SET_A_MARKER + /* set A marker for A-B repeat */ + case WPS_AB_SET_A_MARKER: + if (ab_repeat_mode_enabled()) + ab_set_A_marker(id3->elapsed); + break; +#endif + +#ifdef WPS_AB_SET_B_MARKER + /* set B marker for A-B repeat and jump to A */ + case WPS_AB_SET_B_MARKER: + if (ab_repeat_mode_enabled()) + { + ab_set_B_marker(id3->elapsed); + ab_jump_to_A_marker(); + update_track = true; + } + break; +#endif + +#ifdef WPS_AB_RESET_AB_MARKERS + /* reset A&B markers */ + case WPS_AB_RESET_AB_MARKERS: + if (ab_repeat_mode_enabled()) + { + ab_reset_markers(); + update_track = true; + } + break; +#endif + +#endif /* AB_REPEAT_ENABLE */ + /* stop and exit wps */ #ifdef WPS_EXIT case WPS_EXIT: @@ -706,6 +774,7 @@ long wps_show(void) default: if(default_event_handler(button) == SYS_USB_CONNECTED) return SYS_USB_CONNECTED; + update_track = true; break; } @@ -734,6 +803,9 @@ long wps_show(void) lcd_stop_scroll(); bookmark_autobookmark(); audio_stop(); +#ifdef AB_REPEAT_ENABLE + ab_reset_markers(); +#endif /* Keys can be locked when exiting, so either unlock here or implement key locking in tree.c too */ diff --git a/apps/wps.h b/apps/wps.h index 1779ce425d..6f0d601876 100644 --- a/apps/wps.h +++ b/apps/wps.h @@ -79,6 +79,12 @@ #define WPS_CONTEXT (BUTTON_PLAY | BUTTON_REPEAT) #define WPS_QUICK BUTTON_F2 +#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 + #define WPS_RC_NEXT BUTTON_RC_RIGHT #define WPS_RC_PREV BUTTON_RC_LEFT #define WPS_RC_PAUSE BUTTON_RC_PLAY @@ -106,6 +112,12 @@ #define WPS_ID3 (BUTTON_MENU | BUTTON_ON) #define WPS_CONTEXT (BUTTON_PLAY | 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_STOP) +#endif + #define WPS_RC_NEXT BUTTON_RC_RIGHT #define WPS_RC_PREV BUTTON_RC_LEFT #define WPS_RC_PAUSE BUTTON_RC_PLAY diff --git a/firmware/export/audio.h b/firmware/export/audio.h index 17de7f077d..67ed052f2b 100644 --- a/firmware/export/audio.h +++ b/firmware/export/audio.h @@ -79,4 +79,44 @@ int audio_get_file_pos(void); void audio_beep(int duration); void audio_init_playback(void); +/***********************************************************************/ +/* audio event handling */ + +/* subscribe to one or more audio event(s) by OR'ing together the desired */ +/* event IDs (defined below); a handler is called with a solitary event ID */ +/* (so switch() is okay) and possibly some useful data (depending on the */ +/* event); a handler must return one of the return codes defined below */ + +typedef int (*AUDIO_EVENT_HANDLER)(unsigned short event, unsigned long data); + +void audio_register_event_handler(AUDIO_EVENT_HANDLER handler, unsigned short mask); + +/***********************************************************************/ +/* handler return codes */ + +#define AUDIO_EVENT_RC_IGNORED 200 + /* indicates that no action was taken or the event was not recognized */ + +#define AUDIO_EVENT_RC_HANDLED 201 + /* indicates that the event was handled and some action was taken which renders + the original event invalid; USE WITH CARE!; this return code aborts all further + processing of the given event */ + +/***********************************************************************/ +/* audio event IDs */ + +#define AUDIO_EVENT_POS_REPORT (1<<0) + /* sends a periodic song position report to handlers; a report is sent on + each kernal tick; the number of ticks per second is defined by HZ; on each + report the current song position is passed in 'data'; if a handler takes an + action that changes the song or the song position it must return + AUDIO_EVENT_RC_HANDLED which suppresses the event for any remaining handlers */ + +#define AUDIO_EVENT_END_OF_TRACK (1<<1) + /* generated when the end of the currently playing track is reached; no + data is passed; if the handler implements some alternate end-of-track + processing it should return AUDIO_EVENT_RC_HANDLED which suppresses the + event for any remaining handlers as well as the normal end-of-track + processing */ + #endif diff --git a/firmware/export/config-fmrecorder.h b/firmware/export/config-fmrecorder.h index 40ba404a82..c53388984f 100644 --- a/firmware/export/config-fmrecorder.h +++ b/firmware/export/config-fmrecorder.h @@ -24,6 +24,8 @@ /* Define this if you have an FM Radio */ #define CONFIG_TUNER S1A0903X01 +#define AB_REPEAT_ENABLE 1 + #ifndef SIMULATOR /* Define this if you have a MAS3587F */ diff --git a/firmware/export/config-player.h b/firmware/export/config-player.h index 57fd7d8d92..70858a1636 100644 --- a/firmware/export/config-player.h +++ b/firmware/export/config-player.h @@ -12,6 +12,8 @@ /* The number of bytes reserved for loadable plugins */ #define PLUGIN_BUFFER_SIZE 0x8000 +#define AB_REPEAT_ENABLE 1 + #ifndef SIMULATOR /* Define this if you have a SH7034 */ diff --git a/firmware/export/config-recorder.h b/firmware/export/config-recorder.h index bab6cd17c0..51cdb79321 100644 --- a/firmware/export/config-recorder.h +++ b/firmware/export/config-recorder.h @@ -18,6 +18,8 @@ /* The number of bytes reserved for loadable plugins */ #define PLUGIN_BUFFER_SIZE 0x8000 +#define AB_REPEAT_ENABLE 1 + #ifndef SIMULATOR /* Define this if you have a MAS3587F */ diff --git a/firmware/export/config-recorderv2.h b/firmware/export/config-recorderv2.h index 058495e894..b2d6d01669 100644 --- a/firmware/export/config-recorderv2.h +++ b/firmware/export/config-recorderv2.h @@ -21,6 +21,8 @@ /* The number of bytes reserved for loadable plugins */ #define PLUGIN_BUFFER_SIZE 0x8000 +#define AB_REPEAT_ENABLE 1 + #ifndef SIMULATOR /* Define this if you have a SH7034 */ diff --git a/firmware/mpeg.c b/firmware/mpeg.c index 575ba29fe3..721a4acbcc 100644 --- a/firmware/mpeg.c +++ b/firmware/mpeg.c @@ -280,6 +280,52 @@ static struct trackdata *get_trackdata(int offset) } #endif /* !SIMULATOR */ +/***********************************************************************/ +/* audio event handling */ + +#define MAX_EVENT_HANDLERS 10 +struct event_handlers_table +{ + AUDIO_EVENT_HANDLER handler; + unsigned short mask; +}; +static struct event_handlers_table event_handlers[MAX_EVENT_HANDLERS]; +static int event_handlers_count = 0; + +void audio_register_event_handler(AUDIO_EVENT_HANDLER handler, unsigned short mask) +{ + if (event_handlers_count < MAX_EVENT_HANDLERS) + { + event_handlers[event_handlers_count].handler = handler; + event_handlers[event_handlers_count].mask = mask; + event_handlers_count++; + } +} + +/* dispatch calls each handler in the order registered and returns after some + handler actually handles the event (the event is assumed to no longer be valid + after this, due to the handler changing some condition); returns true if someone + handled the event, which is expected to cause the caller to skip its own handling + of the event */ +#ifndef SIMULATOR +static bool audio_dispatch_event(unsigned short event, unsigned long data) +{ + int i = 0; + for(i=0; i < event_handlers_count; i++) + { + if ( event_handlers[i].mask & event ) + { + int rc = event_handlers[i].handler(event, data); + if ( rc == AUDIO_EVENT_RC_HANDLED ) + return true; + } + } + return false; +} +#endif + +/***********************************************************************/ + static void set_elapsed(struct mp3entry* id3) { if ( id3->vbr ) { @@ -730,9 +776,10 @@ void rec_tick(void) void playback_tick(void) { - get_trackdata(0)->id3.elapsed += - (current_tick - last_dma_tick) * 1000 / HZ; + struct trackdata *ptd = get_trackdata(0); + ptd->id3.elapsed += (current_tick - last_dma_tick) * 1000 / HZ; last_dma_tick = current_tick; + audio_dispatch_event(AUDIO_EVENT_POS_REPORT, (unsigned long)ptd->id3.elapsed); } static void reset_mp3_buffer(void) @@ -762,8 +809,11 @@ static void transfer_end(unsigned char** ppbuf, int* psize) { if (audiobuf_read == get_trackdata(track_offset)->mempos) { - queue_post(&mpeg_queue, MPEG_TRACK_CHANGE, 0); - track_offset++; + if ( ! audio_dispatch_event(AUDIO_EVENT_END_OF_TRACK, 0) ) + { + queue_post(&mpeg_queue, MPEG_TRACK_CHANGE, 0); + track_offset++; + } } } @@ -823,10 +873,12 @@ static void transfer_end(unsigned char** ppbuf, int* psize) } else { - DEBUGF("No more MP3 data. Stopping.\n"); - - queue_post(&mpeg_queue, MPEG_TRACK_CHANGE, 0); - playing = false; + if ( ! audio_dispatch_event(AUDIO_EVENT_END_OF_TRACK, 0) ) + { + DEBUGF("No more MP3 data. Stopping.\n"); + queue_post(&mpeg_queue, MPEG_TRACK_CHANGE, 0); + playing = false; + } } *psize = 0; /* no more transfer */ }