summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Nielsen Feltzing <linus@haxx.se>2005-08-21 23:01:12 +0000
committerLinus Nielsen Feltzing <linus@haxx.se>2005-08-21 23:01:12 +0000
commit0ad617cbf0cc85dde241345194b76b590df567a8 (patch)
tree65931c5a10788981b17f08bca1ba83151bee2855
parent658c8451ead9e7d07478c903c430af9c7799f324 (diff)
downloadrockbox-0ad617cbf0cc85dde241345194b76b590df567a8.tar.gz
rockbox-0ad617cbf0cc85dde241345194b76b590df567a8.zip
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
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/abrepeat.c251
-rw-r--r--apps/abrepeat.h48
-rw-r--r--apps/bookmark.c7
-rw-r--r--apps/lang/english.lang6
-rw-r--r--apps/playlist.c10
-rw-r--r--apps/recorder/icons.c1
-rw-r--r--apps/recorder/icons.h1
-rw-r--r--apps/screens.c9
-rw-r--r--apps/settings.c4
-rw-r--r--apps/settings.h16
-rw-r--r--apps/settings_menu.c6
-rw-r--r--apps/status.c7
-rw-r--r--apps/wps-display.c20
-rw-r--r--apps/wps.c74
-rw-r--r--apps/wps.h12
-rw-r--r--firmware/export/audio.h40
-rw-r--r--firmware/export/config-fmrecorder.h2
-rw-r--r--firmware/export/config-player.h2
-rw-r--r--firmware/export/config-recorder.h2
-rw-r--r--firmware/export/config-recorderv2.h2
-rw-r--r--firmware/mpeg.c68
22 files changed, 566 insertions, 23 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index e8fd2d2ddb..3c933d22b6 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -2,6 +2,7 @@
2logfdisp.c 2logfdisp.c
3#endif 3#endif
4alarm_menu.c 4alarm_menu.c
5abrepeat.c
5bookmark.c 6bookmark.c
6credits.c 7credits.c
7debug_menu.c 8debug_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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $$
9 *
10 * Copyright (C) 2005 Ray Lambert
11 *
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.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include "abrepeat.h"
21
22#include "settings.h"
23#include "audio.h"
24#include "kernel.h"
25
26#ifdef HAVE_LCD_BITMAP
27#include "lcd.h"
28#endif
29
30#ifdef AB_REPEAT_ENABLE
31
32static int ab_audio_event_handler(unsigned short event, unsigned long data)
33{
34 int rc = AUDIO_EVENT_RC_IGNORED;
35 if ( ab_repeat_mode_enabled() )
36 {
37 switch(event)
38 {
39 case AUDIO_EVENT_POS_REPORT:
40 {
41 if ( ! (audio_status() & AUDIO_STATUS_PAUSE) && ab_reached_B_marker(data) )
42 {
43 ab_jump_to_A_marker();
44 rc = AUDIO_EVENT_RC_HANDLED;
45 }
46 break;
47 }
48 case AUDIO_EVENT_END_OF_TRACK:
49 {
50 if ( ab_A_marker_set() && ! ab_B_marker_set() )
51 {
52 ab_jump_to_A_marker();
53 rc = AUDIO_EVENT_RC_HANDLED;
54 }
55 break;
56 }
57 }
58 }
59 return rc;
60}
61
62void ab_repeat_init(void)
63{
64 static bool ab_initialized = false;
65 if ( ! ab_initialized )
66 {
67 ab_initialized = true;
68 audio_register_event_handler(ab_audio_event_handler,
69 AUDIO_EVENT_POS_REPORT | AUDIO_EVENT_END_OF_TRACK );
70 }
71}
72
73static unsigned int ab_A_marker = AB_MARKER_NONE;
74static unsigned int ab_B_marker = AB_MARKER_NONE;
75
76bool ab_repeat_mode_enabled(void)
77{
78 extern struct user_settings global_settings;
79 return global_settings.repeat_mode == REPEAT_AB;
80}
81
82bool ab_A_marker_set(void)
83{
84 return ab_A_marker != AB_MARKER_NONE;
85}
86
87bool ab_B_marker_set(void)
88{
89 return ab_B_marker != AB_MARKER_NONE;
90}
91
92unsigned int ab_get_A_marker(void)
93{
94 return ab_A_marker;
95}
96
97unsigned int ab_get_B_marker(void)
98{
99 return ab_B_marker;
100}
101
102bool ab_reached_B_marker(unsigned int song_position)
103{
104/* following is the size of the window in which we'll detect that the B marker
105was hit; it must be larger than the frequency (in milliseconds) at which this
106function is called otherwise detection of the B marker will be unreliable;
107we assume that this function will be called on each system tick and derive
108the window size from this with a generous margin of error (note: the number
109of ticks per second is given by HZ) */
110#define B_MARKER_DETECT_WINDOW ((1000/HZ)*10)
111 if (ab_B_marker != AB_MARKER_NONE)
112 {
113 if ( (song_position >= ab_B_marker)
114 && (song_position <= (ab_B_marker+B_MARKER_DETECT_WINDOW)) )
115 return true;
116 }
117 return false;
118}
119
120/* determines if the given song position is earlier than the A mark;
121intended for use in handling the jump NEXT and PREV commands */
122bool ab_before_A_marker(unsigned int song_position)
123{
124 return (ab_A_marker != AB_MARKER_NONE)
125 && (song_position < ab_A_marker);
126}
127
128/* determines if the given song position is later than the A mark;
129intended for use in handling the jump PREV command */
130bool ab_after_A_marker(unsigned int song_position)
131{
132/* following is the size of the virtual A marker; we pretend that the A marker is
133larger than a single instant in order to give the user time to hit PREV again to
134jump back to the start of the song; it should be large enough to allow a
135reasonable amount of time for the typical user to react */
136#define A_MARKER_VIRTUAL_SIZE 1000
137 return (ab_A_marker != AB_MARKER_NONE)
138 && (song_position > (ab_A_marker+A_MARKER_VIRTUAL_SIZE));
139}
140
141void ab_jump_to_A_marker(void)
142{
143 bool paused = (audio_status() & AUDIO_STATUS_PAUSE) != 0;
144 if ( ! paused )
145 audio_pause();
146 audio_ff_rewind(ab_A_marker);
147 if ( ! paused )
148 audio_resume();
149}
150
151void ab_reset_markers(void)
152{
153 ab_A_marker = AB_MARKER_NONE;
154 ab_B_marker = AB_MARKER_NONE;
155}
156
157/* following is a fudge factor to help overcome the latency between
158the time the user hears the passage they want to mark and the time
159they actually press the button; the actual song position is adjusted
160by this fudge factor when setting a mark */
161#define EAR_TO_HAND_LATENCY_FUDGE 200
162
163void ab_set_A_marker(unsigned int song_position)
164{
165 ab_A_marker = song_position;
166 ab_A_marker = (ab_A_marker >= EAR_TO_HAND_LATENCY_FUDGE)
167 ? (ab_A_marker - EAR_TO_HAND_LATENCY_FUDGE) : 0;
168 /* check if markers are out of order */
169 if ( (ab_B_marker != AB_MARKER_NONE) && (ab_A_marker > ab_B_marker) )
170 ab_B_marker = AB_MARKER_NONE;
171}
172
173void ab_set_B_marker(unsigned int song_position)
174{
175 ab_B_marker = song_position;
176 ab_B_marker = (ab_B_marker >= EAR_TO_HAND_LATENCY_FUDGE)
177 ? (ab_B_marker - EAR_TO_HAND_LATENCY_FUDGE) : 0;
178 /* check if markers are out of order */
179 if ( (ab_A_marker != AB_MARKER_NONE) && (ab_B_marker < ab_A_marker) )
180 ab_A_marker = AB_MARKER_NONE;
181}
182
183#ifdef HAVE_LCD_BITMAP
184
185static int ab_calc_mark_x_pos(int mark, int capacity, int offset, int size)
186{
187 int w = size - offset;
188 return offset + ( (w * mark) / capacity );
189}
190
191static void ab_draw_veritcal_line_mark(int x, int y, int h)
192{
193 lcd_set_drawmode(DRMODE_COMPLEMENT);
194 lcd_vline(x, y, y+h-1);
195}
196
197#define DIRECTION_RIGHT 1
198#define DIRECTION_LEFT -1
199
200static void ab_draw_arrow_mark(int x, int y, int h, int direction)
201{
202 /* draw lines in decreasing size until a height of zero is reached */
203 lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
204 while( h > 0 )
205 {
206 lcd_vline(x, y, y+h-1);
207 h -= 2;
208 y++;
209 x += direction;
210 lcd_set_drawmode(DRMODE_COMPLEMENT);
211 }
212}
213
214void ab_draw_markers(int capacity, int x, int y, int w, int h)
215{
216 /* if both markers are set, determine if they're far enough apart
217 to draw arrows */
218 if ( ab_A_marker_set() && ab_B_marker_set() )
219 {
220 int xa = ab_calc_mark_x_pos(ab_A_marker, capacity, x, w);
221 int xb = ab_calc_mark_x_pos(ab_B_marker, capacity, x, w);
222 int arrow_width = (h+1) / 2;
223 if ( (xb-xa) < (arrow_width*2) )
224 {
225 ab_draw_veritcal_line_mark(xa, y, h);
226 ab_draw_veritcal_line_mark(xb, y, h);
227 }
228 else
229 {
230 ab_draw_arrow_mark(xa, y, h, DIRECTION_RIGHT);
231 ab_draw_arrow_mark(xb, y, h, DIRECTION_LEFT);
232 }
233 }
234 else
235 {
236 if (ab_A_marker_set())
237 {
238 int xa = ab_calc_mark_x_pos(ab_A_marker, capacity, x, w);
239 ab_draw_arrow_mark(xa, y, h, DIRECTION_RIGHT);
240 }
241 if (ab_B_marker_set())
242 {
243 int xb = ab_calc_mark_x_pos(ab_B_marker, capacity, x, w);
244 ab_draw_arrow_mark(xb, y, h, DIRECTION_LEFT);
245 }
246 }
247}
248
249#endif /* HAVE_LCD_BITMAP */
250
251#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $$
9 *
10 * Copyright (C) 2005 Ray Lambert
11 *
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.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#ifndef _ABREPEAT_H_
20#define _ABREPEAT_H_
21
22#include "system.h"
23#include <stdbool.h>
24
25#ifdef AB_REPEAT_ENABLE
26
27#define AB_MARKER_NONE 0
28
29void ab_repeat_init(void);
30bool ab_repeat_mode_enabled(void); // test if a/b repeat is enabled
31bool ab_A_marker_set(void);
32bool ab_B_marker_set(void);
33unsigned int ab_get_A_marker(void);
34unsigned int ab_get_B_marker(void);
35bool ab_reached_B_marker(unsigned int song_position);
36bool ab_before_A_marker(unsigned int song_position);
37bool ab_after_A_marker(unsigned int song_position);
38void ab_jump_to_A_marker(void);
39void ab_reset_markers(void);
40void ab_set_A_marker(unsigned int song_position);
41void ab_set_B_marker(unsigned int song_position);
42#ifdef HAVE_LCD_BITMAP
43void ab_draw_markers(int capacity, int x, int y, int w, int h);
44#endif
45
46#endif
47
48#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 @@
47#include "sprintf.h" 47#include "sprintf.h"
48#include "talk.h" 48#include "talk.h"
49#include "misc.h" 49#include "misc.h"
50#include "abrepeat.h"
50 51
51#define MAX_BOOKMARKS 10 52#define MAX_BOOKMARKS 10
52#define MAX_BOOKMARK_SIZE 350 53#define MAX_BOOKMARK_SIZE 350
@@ -778,6 +779,12 @@ static void display_bookmark(const char* bookmark,
778 /* bookmark shuffle and repeat states*/ 779 /* bookmark shuffle and repeat states*/
779 switch (repeat_mode) 780 switch (repeat_mode)
780 { 781 {
782#ifdef AB_REPEAT_ENABLE
783 case REPEAT_AB:
784 statusbar_icon_play_mode(Icon_RepeatAB);
785 break;
786#endif
787
781 case REPEAT_ONE: 788 case REPEAT_ONE:
782 statusbar_icon_play_mode(Icon_RepeatOne); 789 statusbar_icon_play_mode(Icon_RepeatOne);
783 break; 790 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
3286eng: "Strong" 3286eng: "Strong"
3287voice "Strong" 3287voice "Strong"
3288new: 3288new:
3289
3290id: LANG_REPEAT_AB
3291desc: repeat one song
3292eng: "A-B"
3293voice: "A-B"
3294new:
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 @@
85#include "misc.h" 85#include "misc.h"
86#include "button.h" 86#include "button.h"
87#include "filetree.h" 87#include "filetree.h"
88#include "abrepeat.h"
88#ifdef HAVE_LCD_BITMAP 89#ifdef HAVE_LCD_BITMAP
89#include "icons.h" 90#include "icons.h"
90#include "widgets.h" 91#include "widgets.h"
@@ -867,6 +868,9 @@ static int get_next_index(const struct playlist_info* playlist, int steps,
867 } 868 }
868 869
869 case REPEAT_ONE: 870 case REPEAT_ONE:
871#ifdef AB_REPEAT_ENABLE
872 case REPEAT_AB:
873#endif
870 next_index = current_index; 874 next_index = current_index;
871 break; 875 break;
872 876
@@ -1916,7 +1920,11 @@ int playlist_next(int steps)
1916 struct playlist_info* playlist = &current_playlist; 1920 struct playlist_info* playlist = &current_playlist;
1917 int index; 1921 int index;
1918 1922
1919 if (steps > 0 && global_settings.repeat_mode != REPEAT_ONE) 1923 if ( (steps > 0)
1924#ifdef AB_REPEAT_ENABLE
1925 && (global_settings.repeat_mode != REPEAT_AB)
1926#endif
1927 && (global_settings.repeat_mode != REPEAT_ONE) )
1920 { 1928 {
1921 int i, j; 1929 int i, j;
1922 1930
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] =
73 {0x3e,0x41,0x51,0x41,0x45,0x41,0x3e}, /* Shuffle playmode (dice) */ 73 {0x3e,0x41,0x51,0x41,0x45,0x41,0x3e}, /* Shuffle playmode (dice) */
74 {0x04,0x0c,0x1c,0x3c,0x1c,0x0c,0x04}, /* Down-arrow */ 74 {0x04,0x0c,0x1c,0x3c,0x1c,0x0c,0x04}, /* Down-arrow */
75 {0x20,0x30,0x38,0x3c,0x38,0x30,0x20}, /* Up-arrow */ 75 {0x20,0x30,0x38,0x3c,0x38,0x30,0x20}, /* Up-arrow */
76 {0x7f,0x04,0x4e,0x5f,0x44,0x38,0x7f}, /* Repeat-AB playmode */
76}; 77};
77 78
78#if CONFIG_LED == LED_VIRTUAL 79#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 {
58 Icon_Shuffle, 58 Icon_Shuffle,
59 Icon_DownArrow, 59 Icon_DownArrow,
60 Icon_UpArrow, 60 Icon_UpArrow,
61 Icon_RepeatAB,
61 Icon_Last 62 Icon_Last
62}; 63};
63 64
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 @@
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2002 Björn Stenberg 10 * Copyright (C) 2002 Bj�n Stenberg
11 * 11 *
12 * All files in this archive are subject to the GNU General Public License. 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. 13 * See the file COPYING in the source tree root for full license agreement.
@@ -45,6 +45,7 @@
45#include "debug.h" 45#include "debug.h"
46#include "led.h" 46#include "led.h"
47#include "sound.h" 47#include "sound.h"
48#include "abrepeat.h"
48#include "wps-display.h" 49#include "wps-display.h"
49#if defined(HAVE_LCD_BITMAP) 50#if defined(HAVE_LCD_BITMAP)
50#include "widgets.h" 51#include "widgets.h"
@@ -593,6 +594,12 @@ bool quick_screen(int context, int button)
593 case REPEAT_SHUFFLE: 594 case REPEAT_SHUFFLE:
594 ptr = str(LANG_SHUFFLE); 595 ptr = str(LANG_SHUFFLE);
595 break; 596 break;
597
598#ifdef AB_REPEAT_ENABLE
599 case REPEAT_AB:
600 ptr = str(LANG_REPEAT_AB);
601 break;
602#endif
596 } 603 }
597 604
598 lcd_getstringsize(str(LANG_REPEAT),&w,&h); 605 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;
78#include "pcm_playback.h" 78#include "pcm_playback.h"
79#endif 79#endif
80 80
81#define CONFIG_BLOCK_VERSION 24 81#define CONFIG_BLOCK_VERSION 25
82#define CONFIG_BLOCK_SIZE 512 82#define CONFIG_BLOCK_SIZE 512
83#define RTC_BLOCK_SIZE 44 83#define RTC_BLOCK_SIZE 44
84 84
@@ -205,7 +205,7 @@ static const struct bit_entry rtc_bits[] =
205 {16 | SIGNED, S_O(resume_first_index), 0, NULL, NULL }, 205 {16 | SIGNED, S_O(resume_first_index), 0, NULL, NULL },
206 {32 | SIGNED, S_O(resume_offset), -1, NULL, NULL }, 206 {32 | SIGNED, S_O(resume_offset), -1, NULL, NULL },
207 {32 | SIGNED, S_O(resume_seed), -1, NULL, NULL }, 207 {32 | SIGNED, S_O(resume_seed), -1, NULL, NULL },
208 {2, S_O(repeat_mode), REPEAT_ALL, "repeat", "off,all,one,shuffle" }, 208 {3, S_O(repeat_mode), REPEAT_ALL, "repeat", "off,all,one,shuffle,ab" },
209 /* LCD */ 209 /* LCD */
210 {6, S_O(contrast), 40, "contrast", NULL }, 210 {6, S_O(contrast), 40, "contrast", NULL },
211#ifdef CONFIG_BACKLIGHT 211#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 @@
24#include "config.h" 24#include "config.h"
25#include "file.h" 25#include "file.h"
26#include "timefuncs.h" 26#include "timefuncs.h"
27#include "abrepeat.h"
27 28
28#define ROCKBOX_DIR "/.rockbox" 29#define ROCKBOX_DIR "/.rockbox"
29#define FONT_DIR "/fonts" 30#define FONT_DIR "/fonts"
@@ -229,7 +230,7 @@ struct user_settings
229 230
230 /* misc options */ 231 /* misc options */
231 232
232 int repeat_mode; /* 0=off 1=repeat all 2=repeat one 3=shuffle */ 233 int repeat_mode; /* 0=off 1=repeat all 2=repeat one 3=shuffle 4=ab */
233 int dirfilter; /* 0=display all, 1=only supported, 2=only music, 234 int dirfilter; /* 0=display all, 1=only supported, 2=only music,
234 3=dirs+playlists, 4=ID3 database */ 235 3=dirs+playlists, 4=ID3 database */
235 bool sort_case; /* dir sort order: 0=case insensitive, 1=sensitive */ 236 bool sort_case; /* dir sort order: 0=case insensitive, 1=sensitive */
@@ -403,8 +404,17 @@ extern const char rec_base_directory[];
403#define SETTINGS_ALL 3 /* both */ 404#define SETTINGS_ALL 3 /* both */
404 405
405/* repeat mode options */ 406/* repeat mode options */
406enum { REPEAT_OFF, REPEAT_ALL, REPEAT_ONE, REPEAT_SHUFFLE, 407enum
407NUM_REPEAT_MODES }; 408{
409 REPEAT_OFF,
410 REPEAT_ALL,
411 REPEAT_ONE,
412 REPEAT_SHUFFLE,
413#ifdef AB_REPEAT_ENABLE
414 REPEAT_AB,
415#endif
416 NUM_REPEAT_MODES
417};
408 418
409/* dir filter options */ 419/* dir filter options */
410/* Note: Any new filter modes need to be added before NUM_FILTER_MODES. 420/* 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 @@
46#include "talk.h" 46#include "talk.h"
47#include "timefuncs.h" 47#include "timefuncs.h"
48#include "misc.h" 48#include "misc.h"
49#include "abrepeat.h"
49#include "power.h" 50#include "power.h"
50#include "database.h" 51#include "database.h"
51 52
@@ -604,11 +605,14 @@ static bool repeat_mode(void)
604 { STR(LANG_REPEAT_ALL) }, 605 { STR(LANG_REPEAT_ALL) },
605 { STR(LANG_REPEAT_ONE) }, 606 { STR(LANG_REPEAT_ONE) },
606 { STR(LANG_SHUFFLE) }, 607 { STR(LANG_SHUFFLE) },
608#ifdef AB_REPEAT_ENABLE
609 { STR(LANG_REPEAT_AB) }
610#endif
607 }; 611 };
608 int old_repeat = global_settings.repeat_mode; 612 int old_repeat = global_settings.repeat_mode;
609 613
610 result = set_option( str(LANG_REPEAT), &global_settings.repeat_mode, 614 result = set_option( str(LANG_REPEAT), &global_settings.repeat_mode,
611 INT, names, 4, NULL ); 615 INT, names, NUM_REPEAT_MODES, NULL );
612 616
613 if (old_repeat != global_settings.repeat_mode && 617 if (old_repeat != global_settings.repeat_mode &&
614 (audio_status() & AUDIO_STATUS_PLAY)) 618 (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 @@
27#include "mp3_playback.h" 27#include "mp3_playback.h"
28#include "audio.h" 28#include "audio.h"
29#include "wps.h" 29#include "wps.h"
30#include "abrepeat.h"
30#ifdef HAVE_RTC 31#ifdef HAVE_RTC
31#include "timefuncs.h" 32#include "timefuncs.h"
32#endif 33#endif
@@ -262,6 +263,12 @@ void status_draw(bool force_redraw)
262 info.redraw_volume = statusbar_icon_volume(info.volume); 263 info.redraw_volume = statusbar_icon_volume(info.volume);
263 statusbar_icon_play_state(current_playmode() + Icon_Play); 264 statusbar_icon_play_state(current_playmode() + Icon_Play);
264 switch (info.repeat) { 265 switch (info.repeat) {
266#ifdef AB_REPEAT_ENABLE
267 case REPEAT_AB:
268 statusbar_icon_play_mode(Icon_RepeatAB);
269 break;
270#endif
271
265 case REPEAT_ONE: 272 case REPEAT_ONE:
266 statusbar_icon_play_mode(Icon_RepeatOne); 273 statusbar_icon_play_mode(Icon_RepeatOne);
267 break; 274 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 @@
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2002 Björn Stenberg 10 * Copyright (C) 2002 Bj�n Stenberg
11 * 11 *
12 * All files in this archive are subject to the GNU General Public License. 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. 13 * See the file COPYING in the source tree root for full license agreement.
@@ -44,6 +44,7 @@
44#include "sprintf.h" 44#include "sprintf.h"
45#include "backlight.h" 45#include "backlight.h"
46#include "button.h" 46#include "button.h"
47#include "abrepeat.h"
47 48
48#ifdef HAVE_LCD_BITMAP 49#ifdef HAVE_LCD_BITMAP
49#include "icons.h" 50#include "icons.h"
@@ -1214,11 +1215,18 @@ bool wps_refresh(struct mp3entry* id3,
1214 1215
1215#ifdef HAVE_LCD_BITMAP 1216#ifdef HAVE_LCD_BITMAP
1216 /* progress */ 1217 /* progress */
1217 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) { 1218 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1218 scrollbar(0, i*h + offset + (h > 7 ? (h - 6) / 2 : 1), LCD_WIDTH, 6, 1219 {
1219 id3->length?id3->length:1, 0, 1220#define PROGRESS_BAR_HEIGHT 6 /* this should probably be defined elsewhere; config-*.h perhaps? */
1220 id3->length?id3->elapsed + ff_rewind_count:0, 1221 int sby = i*h + offset + (h > 7 ? (h - 6) / 2 : 1);
1221 HORIZONTAL); 1222 scrollbar(0, sby, LCD_WIDTH, PROGRESS_BAR_HEIGHT,
1223 id3->length?id3->length:1, 0,
1224 id3->length?id3->elapsed + ff_rewind_count:0,
1225 HORIZONTAL);
1226#ifdef AB_REPEAT_ENABLE
1227 if ( ab_repeat_mode_enabled() )
1228 ab_draw_markers(id3->length, 0, sby, LCD_WIDTH, PROGRESS_BAR_HEIGHT);
1229#endif
1222 update_line = true; 1230 update_line = true;
1223 } 1231 }
1224 if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) { 1232 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 @@
51#include "misc.h" 51#include "misc.h"
52#include "sound.h" 52#include "sound.h"
53#include "onplay.h" 53#include "onplay.h"
54#include "abrepeat.h"
54 55
55#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */ 56#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
56 /* 3% of 30min file == 54s step size */ 57 /* 3% of 30min file == 54s step size */
@@ -347,6 +348,11 @@ long wps_show(void)
347 lcd_setmargins(0, 0); 348 lcd_setmargins(0, 0);
348#endif 349#endif
349 350
351#ifdef AB_REPEAT_ENABLE
352 ab_repeat_init();
353 ab_reset_markers();
354#endif
355
350 ff_rewind = false; 356 ff_rewind = false;
351 357
352 if(audio_status() & AUDIO_STATUS_PLAY) 358 if(audio_status() & AUDIO_STATUS_PLAY)
@@ -585,6 +591,19 @@ long wps_show(void)
585 break; 591 break;
586#endif 592#endif
587#endif 593#endif
594
595#ifdef AB_REPEAT_ENABLE
596 /* if we're in A/B repeat mode and the current position
597 is past the A marker, jump back to the A marker... */
598 if ( ab_repeat_mode_enabled() && ab_after_A_marker(id3->elapsed) )
599 {
600 ab_jump_to_A_marker();
601 update_track = true;
602 break;
603 }
604 /* ...otherwise, do it normally */
605#endif
606
588 if (!id3 || (id3->elapsed < 3*1000)) { 607 if (!id3 || (id3->elapsed < 3*1000)) {
589 audio_prev(); 608 audio_prev();
590 } 609 }
@@ -612,6 +631,19 @@ long wps_show(void)
612 break; 631 break;
613#endif 632#endif
614#endif 633#endif
634
635#ifdef AB_REPEAT_ENABLE
636 /* if we're in A/B repeat mode and the current position is
637 before the A marker, jump to the A marker... */
638 if ( ab_repeat_mode_enabled() && ab_before_A_marker(id3->elapsed) )
639 {
640 ab_jump_to_A_marker();
641 update_track = true;
642 break;
643 }
644 /* ...otherwise, do it normally */
645#endif
646
615 audio_next(); 647 audio_next();
616 break; 648 break;
617 649
@@ -674,7 +706,8 @@ long wps_show(void)
674 706
675 /* pitch screen */ 707 /* pitch screen */
676#if CONFIG_KEYPAD == RECORDER_PAD 708#if CONFIG_KEYPAD == RECORDER_PAD
677 case BUTTON_ON | BUTTON_REPEAT: 709 case BUTTON_ON | BUTTON_UP:
710 case BUTTON_ON | BUTTON_DOWN:
678 if (2 == pitch_screen()) 711 if (2 == pitch_screen())
679 return SYS_USB_CONNECTED; 712 return SYS_USB_CONNECTED;
680 restore = true; 713 restore = true;
@@ -682,6 +715,41 @@ long wps_show(void)
682#endif 715#endif
683#endif 716#endif
684 717
718#ifdef AB_REPEAT_ENABLE
719
720#ifdef WPS_AB_SET_A_MARKER
721 /* set A marker for A-B repeat */
722 case WPS_AB_SET_A_MARKER:
723 if (ab_repeat_mode_enabled())
724 ab_set_A_marker(id3->elapsed);
725 break;
726#endif
727
728#ifdef WPS_AB_SET_B_MARKER
729 /* set B marker for A-B repeat and jump to A */
730 case WPS_AB_SET_B_MARKER:
731 if (ab_repeat_mode_enabled())
732 {
733 ab_set_B_marker(id3->elapsed);
734 ab_jump_to_A_marker();
735 update_track = true;
736 }
737 break;
738#endif
739
740#ifdef WPS_AB_RESET_AB_MARKERS
741 /* reset A&B markers */
742 case WPS_AB_RESET_AB_MARKERS:
743 if (ab_repeat_mode_enabled())
744 {
745 ab_reset_markers();
746 update_track = true;
747 }
748 break;
749#endif
750
751#endif /* AB_REPEAT_ENABLE */
752
685 /* stop and exit wps */ 753 /* stop and exit wps */
686#ifdef WPS_EXIT 754#ifdef WPS_EXIT
687 case WPS_EXIT: 755 case WPS_EXIT:
@@ -706,6 +774,7 @@ long wps_show(void)
706 default: 774 default:
707 if(default_event_handler(button) == SYS_USB_CONNECTED) 775 if(default_event_handler(button) == SYS_USB_CONNECTED)
708 return SYS_USB_CONNECTED; 776 return SYS_USB_CONNECTED;
777 update_track = true;
709 break; 778 break;
710 } 779 }
711 780
@@ -734,6 +803,9 @@ long wps_show(void)
734 lcd_stop_scroll(); 803 lcd_stop_scroll();
735 bookmark_autobookmark(); 804 bookmark_autobookmark();
736 audio_stop(); 805 audio_stop();
806#ifdef AB_REPEAT_ENABLE
807 ab_reset_markers();
808#endif
737 809
738 /* Keys can be locked when exiting, so either unlock here 810 /* Keys can be locked when exiting, so either unlock here
739 or implement key locking in tree.c too */ 811 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 @@
79#define WPS_CONTEXT (BUTTON_PLAY | BUTTON_REPEAT) 79#define WPS_CONTEXT (BUTTON_PLAY | BUTTON_REPEAT)
80#define WPS_QUICK BUTTON_F2 80#define WPS_QUICK BUTTON_F2
81 81
82#ifdef AB_REPEAT_ENABLE
83#define WPS_AB_SET_A_MARKER (BUTTON_ON | BUTTON_LEFT)
84#define WPS_AB_SET_B_MARKER (BUTTON_ON | BUTTON_RIGHT)
85#define WPS_AB_RESET_AB_MARKERS (BUTTON_ON | BUTTON_OFF)
86#endif
87
82#define WPS_RC_NEXT BUTTON_RC_RIGHT 88#define WPS_RC_NEXT BUTTON_RC_RIGHT
83#define WPS_RC_PREV BUTTON_RC_LEFT 89#define WPS_RC_PREV BUTTON_RC_LEFT
84#define WPS_RC_PAUSE BUTTON_RC_PLAY 90#define WPS_RC_PAUSE BUTTON_RC_PLAY
@@ -106,6 +112,12 @@
106#define WPS_ID3 (BUTTON_MENU | BUTTON_ON) 112#define WPS_ID3 (BUTTON_MENU | BUTTON_ON)
107#define WPS_CONTEXT (BUTTON_PLAY | BUTTON_REPEAT) 113#define WPS_CONTEXT (BUTTON_PLAY | BUTTON_REPEAT)
108 114
115#ifdef AB_REPEAT_ENABLE
116#define WPS_AB_SET_A_MARKER (BUTTON_ON | BUTTON_LEFT)
117#define WPS_AB_SET_B_MARKER (BUTTON_ON | BUTTON_RIGHT)
118#define WPS_AB_RESET_AB_MARKERS (BUTTON_ON | BUTTON_STOP)
119#endif
120
109#define WPS_RC_NEXT BUTTON_RC_RIGHT 121#define WPS_RC_NEXT BUTTON_RC_RIGHT
110#define WPS_RC_PREV BUTTON_RC_LEFT 122#define WPS_RC_PREV BUTTON_RC_LEFT
111#define WPS_RC_PAUSE BUTTON_RC_PLAY 123#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);
79void audio_beep(int duration); 79void audio_beep(int duration);
80void audio_init_playback(void); 80void audio_init_playback(void);
81 81
82/***********************************************************************/
83/* audio event handling */
84
85/* subscribe to one or more audio event(s) by OR'ing together the desired */
86/* event IDs (defined below); a handler is called with a solitary event ID */
87/* (so switch() is okay) and possibly some useful data (depending on the */
88/* event); a handler must return one of the return codes defined below */
89
90typedef int (*AUDIO_EVENT_HANDLER)(unsigned short event, unsigned long data);
91
92void audio_register_event_handler(AUDIO_EVENT_HANDLER handler, unsigned short mask);
93
94/***********************************************************************/
95/* handler return codes */
96
97#define AUDIO_EVENT_RC_IGNORED 200
98 /* indicates that no action was taken or the event was not recognized */
99
100#define AUDIO_EVENT_RC_HANDLED 201
101 /* indicates that the event was handled and some action was taken which renders
102 the original event invalid; USE WITH CARE!; this return code aborts all further
103 processing of the given event */
104
105/***********************************************************************/
106/* audio event IDs */
107
108#define AUDIO_EVENT_POS_REPORT (1<<0)
109 /* sends a periodic song position report to handlers; a report is sent on
110 each kernal tick; the number of ticks per second is defined by HZ; on each
111 report the current song position is passed in 'data'; if a handler takes an
112 action that changes the song or the song position it must return
113 AUDIO_EVENT_RC_HANDLED which suppresses the event for any remaining handlers */
114
115#define AUDIO_EVENT_END_OF_TRACK (1<<1)
116 /* generated when the end of the currently playing track is reached; no
117 data is passed; if the handler implements some alternate end-of-track
118 processing it should return AUDIO_EVENT_RC_HANDLED which suppresses the
119 event for any remaining handlers as well as the normal end-of-track
120 processing */
121
82#endif 122#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 @@
24/* Define this if you have an FM Radio */ 24/* Define this if you have an FM Radio */
25#define CONFIG_TUNER S1A0903X01 25#define CONFIG_TUNER S1A0903X01
26 26
27#define AB_REPEAT_ENABLE 1
28
27#ifndef SIMULATOR 29#ifndef SIMULATOR
28 30
29/* Define this if you have a MAS3587F */ 31/* 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 @@
12/* The number of bytes reserved for loadable plugins */ 12/* The number of bytes reserved for loadable plugins */
13#define PLUGIN_BUFFER_SIZE 0x8000 13#define PLUGIN_BUFFER_SIZE 0x8000
14 14
15#define AB_REPEAT_ENABLE 1
16
15#ifndef SIMULATOR 17#ifndef SIMULATOR
16 18
17/* Define this if you have a SH7034 */ 19/* 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 @@
18/* The number of bytes reserved for loadable plugins */ 18/* The number of bytes reserved for loadable plugins */
19#define PLUGIN_BUFFER_SIZE 0x8000 19#define PLUGIN_BUFFER_SIZE 0x8000
20 20
21#define AB_REPEAT_ENABLE 1
22
21#ifndef SIMULATOR 23#ifndef SIMULATOR
22 24
23/* Define this if you have a MAS3587F */ 25/* 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 @@
21/* The number of bytes reserved for loadable plugins */ 21/* The number of bytes reserved for loadable plugins */
22#define PLUGIN_BUFFER_SIZE 0x8000 22#define PLUGIN_BUFFER_SIZE 0x8000
23 23
24#define AB_REPEAT_ENABLE 1
25
24#ifndef SIMULATOR 26#ifndef SIMULATOR
25 27
26/* Define this if you have a SH7034 */ 28/* 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)
280} 280}
281#endif /* !SIMULATOR */ 281#endif /* !SIMULATOR */
282 282
283/***********************************************************************/
284/* audio event handling */
285
286#define MAX_EVENT_HANDLERS 10
287struct event_handlers_table
288{
289 AUDIO_EVENT_HANDLER handler;
290 unsigned short mask;
291};
292static struct event_handlers_table event_handlers[MAX_EVENT_HANDLERS];
293static int event_handlers_count = 0;
294
295void audio_register_event_handler(AUDIO_EVENT_HANDLER handler, unsigned short mask)
296{
297 if (event_handlers_count < MAX_EVENT_HANDLERS)
298 {
299 event_handlers[event_handlers_count].handler = handler;
300 event_handlers[event_handlers_count].mask = mask;
301 event_handlers_count++;
302 }
303}
304
305/* dispatch calls each handler in the order registered and returns after some
306 handler actually handles the event (the event is assumed to no longer be valid
307 after this, due to the handler changing some condition); returns true if someone
308 handled the event, which is expected to cause the caller to skip its own handling
309 of the event */
310#ifndef SIMULATOR
311static bool audio_dispatch_event(unsigned short event, unsigned long data)
312{
313 int i = 0;
314 for(i=0; i < event_handlers_count; i++)
315 {
316 if ( event_handlers[i].mask & event )
317 {
318 int rc = event_handlers[i].handler(event, data);
319 if ( rc == AUDIO_EVENT_RC_HANDLED )
320 return true;
321 }
322 }
323 return false;
324}
325#endif
326
327/***********************************************************************/
328
283static void set_elapsed(struct mp3entry* id3) 329static void set_elapsed(struct mp3entry* id3)
284{ 330{
285 if ( id3->vbr ) { 331 if ( id3->vbr ) {
@@ -730,9 +776,10 @@ void rec_tick(void)
730 776
731void playback_tick(void) 777void playback_tick(void)
732{ 778{
733 get_trackdata(0)->id3.elapsed += 779 struct trackdata *ptd = get_trackdata(0);
734 (current_tick - last_dma_tick) * 1000 / HZ; 780 ptd->id3.elapsed += (current_tick - last_dma_tick) * 1000 / HZ;
735 last_dma_tick = current_tick; 781 last_dma_tick = current_tick;
782 audio_dispatch_event(AUDIO_EVENT_POS_REPORT, (unsigned long)ptd->id3.elapsed);
736} 783}
737 784
738static void reset_mp3_buffer(void) 785static void reset_mp3_buffer(void)
@@ -762,8 +809,11 @@ static void transfer_end(unsigned char** ppbuf, int* psize)
762 { 809 {
763 if (audiobuf_read == get_trackdata(track_offset)->mempos) 810 if (audiobuf_read == get_trackdata(track_offset)->mempos)
764 { 811 {
765 queue_post(&mpeg_queue, MPEG_TRACK_CHANGE, 0); 812 if ( ! audio_dispatch_event(AUDIO_EVENT_END_OF_TRACK, 0) )
766 track_offset++; 813 {
814 queue_post(&mpeg_queue, MPEG_TRACK_CHANGE, 0);
815 track_offset++;
816 }
767 } 817 }
768 } 818 }
769 819
@@ -823,10 +873,12 @@ static void transfer_end(unsigned char** ppbuf, int* psize)
823 } 873 }
824 else 874 else
825 { 875 {
826 DEBUGF("No more MP3 data. Stopping.\n"); 876 if ( ! audio_dispatch_event(AUDIO_EVENT_END_OF_TRACK, 0) )
827 877 {
828 queue_post(&mpeg_queue, MPEG_TRACK_CHANGE, 0); 878 DEBUGF("No more MP3 data. Stopping.\n");
829 playing = false; 879 queue_post(&mpeg_queue, MPEG_TRACK_CHANGE, 0);
880 playing = false;
881 }
830 } 882 }
831 *psize = 0; /* no more transfer */ 883 *psize = 0; /* no more transfer */
832 } 884 }