summaryrefslogtreecommitdiff
path: root/apps/abrepeat.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/abrepeat.c')
-rw-r--r--apps/abrepeat.c251
1 files changed, 251 insertions, 0 deletions
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 */