diff options
author | Linus Nielsen Feltzing <linus@haxx.se> | 2005-08-21 23:01:12 +0000 |
---|---|---|
committer | Linus Nielsen Feltzing <linus@haxx.se> | 2005-08-21 23:01:12 +0000 |
commit | 0ad617cbf0cc85dde241345194b76b590df567a8 (patch) | |
tree | 65931c5a10788981b17f08bca1ba83151bee2855 /apps/abrepeat.c | |
parent | 658c8451ead9e7d07478c903c430af9c7799f324 (diff) | |
download | rockbox-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
Diffstat (limited to 'apps/abrepeat.c')
-rw-r--r-- | apps/abrepeat.c | 251 |
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 | |||
32 | static 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 | |||
62 | void 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 | |||
73 | static unsigned int ab_A_marker = AB_MARKER_NONE; | ||
74 | static unsigned int ab_B_marker = AB_MARKER_NONE; | ||
75 | |||
76 | bool ab_repeat_mode_enabled(void) | ||
77 | { | ||
78 | extern struct user_settings global_settings; | ||
79 | return global_settings.repeat_mode == REPEAT_AB; | ||
80 | } | ||
81 | |||
82 | bool ab_A_marker_set(void) | ||
83 | { | ||
84 | return ab_A_marker != AB_MARKER_NONE; | ||
85 | } | ||
86 | |||
87 | bool ab_B_marker_set(void) | ||
88 | { | ||
89 | return ab_B_marker != AB_MARKER_NONE; | ||
90 | } | ||
91 | |||
92 | unsigned int ab_get_A_marker(void) | ||
93 | { | ||
94 | return ab_A_marker; | ||
95 | } | ||
96 | |||
97 | unsigned int ab_get_B_marker(void) | ||
98 | { | ||
99 | return ab_B_marker; | ||
100 | } | ||
101 | |||
102 | bool 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 | ||
105 | was hit; it must be larger than the frequency (in milliseconds) at which this | ||
106 | function is called otherwise detection of the B marker will be unreliable; | ||
107 | we assume that this function will be called on each system tick and derive | ||
108 | the window size from this with a generous margin of error (note: the number | ||
109 | of 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; | ||
121 | intended for use in handling the jump NEXT and PREV commands */ | ||
122 | bool 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; | ||
129 | intended for use in handling the jump PREV command */ | ||
130 | bool 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 | ||
133 | larger than a single instant in order to give the user time to hit PREV again to | ||
134 | jump back to the start of the song; it should be large enough to allow a | ||
135 | reasonable 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 | |||
141 | void 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 | |||
151 | void 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 | ||
158 | the time the user hears the passage they want to mark and the time | ||
159 | they actually press the button; the actual song position is adjusted | ||
160 | by this fudge factor when setting a mark */ | ||
161 | #define EAR_TO_HAND_LATENCY_FUDGE 200 | ||
162 | |||
163 | void 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 | |||
173 | void 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 | |||
185 | static 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 | |||
191 | static 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 | |||
200 | static 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 | |||
214 | void 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 */ | ||