diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/SOURCES | 1 | ||||
-rw-r--r-- | apps/cuesheet.c | 360 | ||||
-rw-r--r-- | apps/cuesheet.h | 85 | ||||
-rw-r--r-- | apps/filetree.c | 5 | ||||
-rw-r--r-- | apps/gui/gwps-common.c | 64 | ||||
-rw-r--r-- | apps/gui/gwps.c | 48 | ||||
-rw-r--r-- | apps/lang/english.lang | 28 | ||||
-rw-r--r-- | apps/main.c | 6 | ||||
-rw-r--r-- | apps/menus/playback_menu.c | 18 | ||||
-rw-r--r-- | apps/metadata.c | 6 | ||||
-rw-r--r-- | apps/playback.c | 18 | ||||
-rw-r--r-- | apps/settings.h | 1 | ||||
-rw-r--r-- | apps/settings_list.c | 1 | ||||
-rw-r--r-- | apps/tree.c | 1 | ||||
-rw-r--r-- | apps/tree.h | 1 |
15 files changed, 637 insertions, 6 deletions
diff --git a/apps/SOURCES b/apps/SOURCES index e57c882a8b..1793724665 100644 --- a/apps/SOURCES +++ b/apps/SOURCES | |||
@@ -32,6 +32,7 @@ settings_list.c | |||
32 | settings_menu.c | 32 | settings_menu.c |
33 | sound_menu.c | 33 | sound_menu.c |
34 | status.c | 34 | status.c |
35 | cuesheet.c | ||
35 | #if !defined(SIMULATOR) || CONFIG_CODEC == SWCODEC | 36 | #if !defined(SIMULATOR) || CONFIG_CODEC == SWCODEC |
36 | talk.c | 37 | talk.c |
37 | #endif | 38 | #endif |
diff --git a/apps/cuesheet.c b/apps/cuesheet.c new file mode 100644 index 0000000000..6db3528cad --- /dev/null +++ b/apps/cuesheet.c | |||
@@ -0,0 +1,360 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $$ | ||
9 | * | ||
10 | * Copyright (C) 2007 Nicolas Pennequin, Jonathan Gordon | ||
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 <stdio.h> | ||
21 | #include <stdlib.h> | ||
22 | #include <stdbool.h> | ||
23 | #include <atoi.h> | ||
24 | #include <string.h> | ||
25 | #include "system.h" | ||
26 | #include "audio.h" | ||
27 | #include "kernel.h" | ||
28 | #include "logf.h" | ||
29 | #include "sprintf.h" | ||
30 | #include "misc.h" | ||
31 | #include "screens.h" | ||
32 | #include "splash.h" | ||
33 | #include "list.h" | ||
34 | #include "action.h" | ||
35 | #include "lang.h" | ||
36 | #include "debug.h" | ||
37 | #include "settings.h" | ||
38 | #include "buffer.h" | ||
39 | #include "plugin.h" | ||
40 | #include "playback.h" | ||
41 | #include "cuesheet.h" | ||
42 | |||
43 | |||
44 | void cuesheet_init(void) | ||
45 | { | ||
46 | if (global_settings.cuesheet) { | ||
47 | curr_cue = (struct cuesheet *)buffer_alloc(MAX_TRACKS * sizeof(struct cuesheet)); | ||
48 | temp_cue = (struct cuesheet *)buffer_alloc(MAX_TRACKS * sizeof(struct cuesheet)); | ||
49 | } else { | ||
50 | curr_cue = NULL; | ||
51 | temp_cue = NULL; | ||
52 | } | ||
53 | } | ||
54 | |||
55 | bool cuesheet_is_enabled(void) | ||
56 | { | ||
57 | return (curr_cue != NULL); | ||
58 | } | ||
59 | |||
60 | bool look_for_cuesheet_file(const char *trackpath) | ||
61 | { | ||
62 | char cuepath[MAX_PATH]; | ||
63 | strncpy(cuepath, trackpath, MAX_PATH); | ||
64 | char *dot = strrchr(cuepath, '.'); | ||
65 | strcpy(dot, ".cue"); | ||
66 | |||
67 | int fd = open(cuepath,O_RDONLY); | ||
68 | if (fd < 0) | ||
69 | { | ||
70 | return false; | ||
71 | } | ||
72 | else | ||
73 | { | ||
74 | close(fd); | ||
75 | return true; | ||
76 | } | ||
77 | } | ||
78 | |||
79 | char *skip_whitespace(char* buf) | ||
80 | { | ||
81 | char *r = buf; | ||
82 | while (*r && (*r < 33)) | ||
83 | r++; | ||
84 | return r; | ||
85 | } | ||
86 | |||
87 | /* parse cuesheet "file" and store the information in "cue" */ | ||
88 | bool parse_cuesheet(char *file, struct cuesheet *cue) | ||
89 | { | ||
90 | char line[MAX_PATH]; | ||
91 | char *s, *start, *end; | ||
92 | int fd = open(file,O_RDONLY); | ||
93 | if (fd < 0) | ||
94 | { | ||
95 | /* couln't open the file */ | ||
96 | return false; | ||
97 | } | ||
98 | |||
99 | memset(cue, 0, sizeof(struct cuesheet)); | ||
100 | |||
101 | strcpy(cue->path, file); | ||
102 | |||
103 | cue->curr_track_idx = 0; | ||
104 | cue->curr_track = cue->tracks; | ||
105 | |||
106 | cue->track_count = 0; | ||
107 | while (read_line(fd,line,MAX_PATH)) | ||
108 | { | ||
109 | s = skip_whitespace(line); | ||
110 | if (!strncmp(s, "TITLE", 5)) | ||
111 | { | ||
112 | start = strchr(s,'"'); | ||
113 | if (!start) | ||
114 | break; | ||
115 | end = strchr(++start,'"'); | ||
116 | if (!end) | ||
117 | break; | ||
118 | *end = '\0'; | ||
119 | if (cue->track_count <= 0) | ||
120 | strncpy(cue->title,start,MAX_NAME); | ||
121 | else strncpy(cue->tracks[cue->track_count-1].title, | ||
122 | start,MAX_NAME); | ||
123 | } | ||
124 | else if (!strncmp(s, "PERFORMER", 9)) | ||
125 | { | ||
126 | start = strchr(s,'"'); | ||
127 | if (!start) | ||
128 | break; | ||
129 | end = strchr(++start,'"'); | ||
130 | if (!end) | ||
131 | break; | ||
132 | *end = '\0'; | ||
133 | if (cue->track_count <= 0) | ||
134 | strncpy(cue->performer,start,MAX_NAME); | ||
135 | else strncpy(cue->tracks[cue->track_count-1].performer, | ||
136 | start,MAX_NAME); | ||
137 | } | ||
138 | else if (!strncmp(s, "TRACK", 5)) | ||
139 | { | ||
140 | if (cue->track_count >= MAX_TRACKS) | ||
141 | break; /* out of memeory! stop parsing */ | ||
142 | cue->track_count++; | ||
143 | } | ||
144 | else if (!strncmp(s, "INDEX", 5)) | ||
145 | { | ||
146 | s = strchr(s,' '); | ||
147 | s = skip_whitespace(s); | ||
148 | s = strchr(s,' '); | ||
149 | s = skip_whitespace(s); | ||
150 | cue->tracks[cue->track_count-1].offset = 60*1000 * atoi(s); | ||
151 | s = strchr(s,':') + 1; | ||
152 | cue->tracks[cue->track_count-1].offset += 1000 * atoi(s); | ||
153 | s = strchr(s,':') + 1; | ||
154 | cue->tracks[cue->track_count-1].offset += 13 * atoi(s); | ||
155 | } | ||
156 | } | ||
157 | close(fd); | ||
158 | |||
159 | /* If some songs don't have performer info, we copy the cuesheet performer */ | ||
160 | int i; | ||
161 | for (i = 0; i < cue->track_count; i++) | ||
162 | { | ||
163 | if (*(cue->tracks[i].performer) == '\0') | ||
164 | { | ||
165 | strncpy(cue->tracks[i].performer, cue->performer, MAX_NAME); | ||
166 | } | ||
167 | } | ||
168 | |||
169 | return true; | ||
170 | } | ||
171 | |||
172 | /* takes care of seeking to a track in a playlist | ||
173 | * returns false if audio isn't playing */ | ||
174 | bool seek(unsigned long pos) | ||
175 | { | ||
176 | if (!(audio_status() & AUDIO_STATUS_PLAY)) | ||
177 | { | ||
178 | return false; | ||
179 | } | ||
180 | else | ||
181 | { | ||
182 | #if (CONFIG_CODEC == SWCODEC) | ||
183 | audio_pre_ff_rewind(); | ||
184 | #else | ||
185 | audio_pause(); | ||
186 | #endif | ||
187 | audio_ff_rewind(pos); | ||
188 | return true; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | /* returns the index of the track currently being played | ||
193 | and updates the information about the current track. */ | ||
194 | int cue_find_current_track(struct cuesheet *cue, unsigned long curpos) | ||
195 | { | ||
196 | int i=0; | ||
197 | while (i < cue->track_count-1 && cue->tracks[i+1].offset < curpos) | ||
198 | { | ||
199 | i++; | ||
200 | } | ||
201 | cue->curr_track_idx = i; | ||
202 | cue->curr_track = cue->tracks + i; | ||
203 | return i; | ||
204 | } | ||
205 | |||
206 | /* callback that gives list item titles for the cuesheet browser */ | ||
207 | char *list_get_name_cb(int selected_item, | ||
208 | void *data, | ||
209 | char *buffer) | ||
210 | { | ||
211 | struct cuesheet *cue = (struct cuesheet *)data; | ||
212 | |||
213 | if (selected_item & 1) | ||
214 | { | ||
215 | snprintf(buffer, MAX_PATH, | ||
216 | (selected_item+1)/2 > 9 ? " %s" : " %s", | ||
217 | cue->tracks[selected_item/2].title); | ||
218 | } | ||
219 | else | ||
220 | { | ||
221 | snprintf(buffer, MAX_PATH, "%d %s", selected_item/2+1, | ||
222 | cue->tracks[selected_item/2].performer); | ||
223 | } | ||
224 | return buffer; | ||
225 | } | ||
226 | |||
227 | void browse_cuesheet(struct cuesheet *cue) | ||
228 | { | ||
229 | struct gui_synclist lists; | ||
230 | int action; | ||
231 | bool done = false; | ||
232 | int sel; | ||
233 | char title[MAX_PATH]; | ||
234 | char cuepath[MAX_PATH]; | ||
235 | char *dot; | ||
236 | struct mp3entry *id3 = audio_current_track(); | ||
237 | |||
238 | snprintf(title, MAX_PATH, "%s: %s", cue->performer, cue->title); | ||
239 | gui_synclist_init(&lists, list_get_name_cb, cue, false, 2); | ||
240 | gui_synclist_set_nb_items(&lists, 2*cue->track_count); | ||
241 | gui_synclist_set_title(&lists, title, 0); | ||
242 | |||
243 | if (strcmp(id3->path, "No file!")) | ||
244 | { | ||
245 | strncpy(cuepath, id3->path, MAX_PATH); | ||
246 | dot = strrchr(cuepath, '.'); | ||
247 | strcpy(dot, ".cue"); | ||
248 | } | ||
249 | |||
250 | if (id3->cuesheet_type && !strcmp(cue->path, cuepath)) | ||
251 | { | ||
252 | gui_synclist_select_item(&lists, | ||
253 | 2*cue_find_current_track(cue, id3->elapsed)); | ||
254 | } | ||
255 | |||
256 | while (!done) | ||
257 | { | ||
258 | gui_synclist_draw(&lists); | ||
259 | action = get_action(CONTEXT_LIST,TIMEOUT_BLOCK); | ||
260 | if (gui_synclist_do_button(&lists,action,LIST_WRAP_UNLESS_HELD)) | ||
261 | continue; | ||
262 | switch (action) | ||
263 | { | ||
264 | case ACTION_STD_OK: | ||
265 | id3 = audio_current_track(); | ||
266 | if (strcmp(id3->path, "No file!")) | ||
267 | { | ||
268 | strncpy(cuepath, id3->path, MAX_PATH); | ||
269 | dot = strrchr(cuepath, '.'); | ||
270 | strcpy(dot, ".cue"); | ||
271 | if (id3->cuesheet_type && !strcmp(cue->path, cuepath)) | ||
272 | { | ||
273 | sel = gui_synclist_get_sel_pos(&lists); | ||
274 | seek(cue->tracks[sel/2].offset); | ||
275 | } | ||
276 | } | ||
277 | break; | ||
278 | case ACTION_STD_CANCEL: | ||
279 | done = true; | ||
280 | } | ||
281 | } | ||
282 | } | ||
283 | |||
284 | bool display_cuesheet_content(char* filename) | ||
285 | { | ||
286 | int bufsize = 0; | ||
287 | struct cuesheet *cue = (struct cuesheet *)plugin_get_buffer(&bufsize); | ||
288 | if (!cue) | ||
289 | return false; | ||
290 | |||
291 | if (!parse_cuesheet(filename, cue)) | ||
292 | return false; | ||
293 | |||
294 | browse_cuesheet(cue); | ||
295 | return true; | ||
296 | } | ||
297 | |||
298 | /* skips backwards or forward in the current cuesheet | ||
299 | * the return value indicates whether we're still in a cusheet after skipping | ||
300 | * it also returns false if we weren't in a cuesheet. | ||
301 | * direction should be 1 or -1. | ||
302 | */ | ||
303 | bool curr_cuesheet_skip(int direction, unsigned long curr_pos) | ||
304 | { | ||
305 | int track = cue_find_current_track(curr_cue, curr_pos); | ||
306 | |||
307 | if (direction >= 0 && track == curr_cue->track_count - 1) | ||
308 | { | ||
309 | /* we want to get out of the cuesheet */ | ||
310 | return false; | ||
311 | } | ||
312 | else | ||
313 | { | ||
314 | if (!(direction <= 0 && track == 0)) | ||
315 | track += direction; | ||
316 | |||
317 | seek(curr_cue->tracks[track].offset); | ||
318 | return true; | ||
319 | } | ||
320 | |||
321 | } | ||
322 | |||
323 | void cue_spoof_id3(struct cuesheet *cue, struct mp3entry *id3) | ||
324 | { | ||
325 | if (!cue) | ||
326 | return; | ||
327 | |||
328 | int i = cue->curr_track_idx; | ||
329 | |||
330 | id3->title = cue->tracks[i].title; | ||
331 | id3->artist = cue->tracks[i].performer; | ||
332 | id3->tracknum = i+1; | ||
333 | id3->album = cue->title; | ||
334 | id3->composer = cue->performer; | ||
335 | if (id3->track_string) | ||
336 | snprintf(id3->track_string, 10, "%d/%d", i+1, cue->track_count); | ||
337 | } | ||
338 | |||
339 | #ifdef HAVE_LCD_BITMAP | ||
340 | static inline void draw_veritcal_line_mark(struct screen * screen, | ||
341 | int x, int y, int h) | ||
342 | { | ||
343 | screen->set_drawmode(DRMODE_COMPLEMENT); | ||
344 | screen->vline(x, y, y+h-1); | ||
345 | } | ||
346 | |||
347 | /* draw the cuesheet markers for a track of length "tracklen", | ||
348 | between (x1,y) and (x2,y) */ | ||
349 | void cue_draw_markers(struct screen *screen, unsigned long tracklen, | ||
350 | int x1, int x2, int y, int h) | ||
351 | { | ||
352 | int i,xi; | ||
353 | int w = x2 - x1; | ||
354 | for (i=1; i < curr_cue->track_count; i++) | ||
355 | { | ||
356 | xi = x1 + (w * curr_cue->tracks[i].offset)/tracklen; | ||
357 | draw_veritcal_line_mark(screen, xi, y, h); | ||
358 | } | ||
359 | } | ||
360 | #endif | ||
diff --git a/apps/cuesheet.h b/apps/cuesheet.h new file mode 100644 index 0000000000..9dff370e09 --- /dev/null +++ b/apps/cuesheet.h | |||
@@ -0,0 +1,85 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $$ | ||
9 | * | ||
10 | * Copyright (C) 2007 Nicolas Pennequin, Jonathan Gordon | ||
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 | #ifndef _CUESHEET_H_ | ||
21 | #define _CUESHEET_H_ | ||
22 | |||
23 | #include <stdbool.h> | ||
24 | #include "screens.h" | ||
25 | |||
26 | #define MAX_NAME 64 /* Max length of information strings */ | ||
27 | #define MAX_TRACKS 99 /* Max number of tracks in a cuesheet */ | ||
28 | |||
29 | struct cue_track_info { | ||
30 | char title[MAX_NAME]; | ||
31 | char performer[MAX_NAME]; | ||
32 | unsigned long offset; /* ms from start of track */ | ||
33 | }; | ||
34 | |||
35 | struct cuesheet { | ||
36 | char path[MAX_PATH]; | ||
37 | char audio_filename[MAX_PATH]; | ||
38 | |||
39 | char title[MAX_NAME]; | ||
40 | char performer[MAX_NAME]; | ||
41 | |||
42 | int track_count; | ||
43 | struct cue_track_info tracks[MAX_TRACKS]; | ||
44 | |||
45 | int curr_track_idx; | ||
46 | struct cue_track_info *curr_track; | ||
47 | }; | ||
48 | |||
49 | struct cuesheet *curr_cue; | ||
50 | struct cuesheet *temp_cue; | ||
51 | |||
52 | /* returns true if cuesheet support is initialised */ | ||
53 | bool cuesheet_is_enabled(void); | ||
54 | |||
55 | /* allocates the cuesheet buffer */ | ||
56 | void cuesheet_init(void); | ||
57 | |||
58 | /* looks if there is a cuesheet file that has a name matching "trackpath" */ | ||
59 | bool look_for_cuesheet_file(const char *trackpath); | ||
60 | |||
61 | /* parse cuesheet "file" and store the information in "cue" */ | ||
62 | bool parse_cuesheet(char *file, struct cuesheet *cue); | ||
63 | |||
64 | /* reads a cuesheet to find the audio track associated to it */ | ||
65 | bool get_trackname_from_cuesheet(char *filename, char *buf); | ||
66 | |||
67 | /* displays a cuesheet to the screen (it is stored in the plugin buffer) */ | ||
68 | bool display_cuesheet_content(char* filename); | ||
69 | |||
70 | /* finds the index of the current track played within a cuesheet */ | ||
71 | int cue_find_current_track(struct cuesheet *cue, unsigned long curpos); | ||
72 | |||
73 | /* update the id3 info to that of the currently playing track in the cuesheet */ | ||
74 | void cue_spoof_id3(struct cuesheet *cue, struct mp3entry *id3); | ||
75 | |||
76 | /* skip to next track in the cuesheet towards "direction" (which is 1 or -1) */ | ||
77 | bool curr_cuesheet_skip(int direction, unsigned long curr_pos); | ||
78 | |||
79 | #ifdef HAVE_LCD_BITMAP | ||
80 | /* draw track markers on the progressbar */ | ||
81 | void cue_draw_markers(struct screen *screen, unsigned long tracklen, | ||
82 | int x1, int x2, int y, int h); | ||
83 | #endif | ||
84 | |||
85 | #endif | ||
diff --git a/apps/filetree.c b/apps/filetree.c index 10174dbb31..df4065c91b 100644 --- a/apps/filetree.c +++ b/apps/filetree.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include "dircache.h" | 41 | #include "dircache.h" |
42 | #include "splash.h" | 42 | #include "splash.h" |
43 | #include "yesno.h" | 43 | #include "yesno.h" |
44 | #include "cuesheet.h" | ||
44 | #ifdef HAVE_LCD_BITMAP | 45 | #ifdef HAVE_LCD_BITMAP |
45 | #include "keyboard.h" | 46 | #include "keyboard.h" |
46 | #endif | 47 | #endif |
@@ -550,6 +551,10 @@ int ft_enter(struct tree_context* c) | |||
550 | } | 551 | } |
551 | break; | 552 | break; |
552 | 553 | ||
554 | case TREE_ATTR_CUE: | ||
555 | display_cuesheet_content(buf); | ||
556 | break; | ||
557 | |||
553 | default: | 558 | default: |
554 | { | 559 | { |
555 | char* plugin; | 560 | char* plugin; |
diff --git a/apps/gui/gwps-common.c b/apps/gui/gwps-common.c index 53b12238e4..b6e64d2fcc 100644 --- a/apps/gui/gwps-common.c +++ b/apps/gui/gwps-common.c | |||
@@ -53,6 +53,7 @@ | |||
53 | #endif | 53 | #endif |
54 | #include "dsp.h" | 54 | #include "dsp.h" |
55 | #include "action.h" | 55 | #include "action.h" |
56 | #include "cuesheet.h" | ||
56 | 57 | ||
57 | #ifdef HAVE_LCD_CHARCELLS | 58 | #ifdef HAVE_LCD_CHARCELLS |
58 | static bool draw_player_progress(struct gui_wps *gwps); | 59 | static bool draw_player_progress(struct gui_wps *gwps); |
@@ -1850,6 +1851,14 @@ bool gui_wps_refresh(struct gui_wps *gwps, int ffwd_offset, | |||
1850 | data->progress_start, data->progress_end, sb_y, | 1851 | data->progress_start, data->progress_end, sb_y, |
1851 | data->progress_height); | 1852 | data->progress_height); |
1852 | #endif | 1853 | #endif |
1854 | |||
1855 | if (cuesheet_is_enabled() && state->id3->cuesheet_type) | ||
1856 | { | ||
1857 | cue_draw_markers(display, state->id3->length, | ||
1858 | data->progress_start, data->progress_end, | ||
1859 | sb_y+1, data->progress_height-2); | ||
1860 | } | ||
1861 | |||
1853 | update_line = true; | 1862 | update_line = true; |
1854 | } | 1863 | } |
1855 | if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) { | 1864 | if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) { |
@@ -2561,6 +2570,35 @@ bool update(struct gui_wps *gwps) | |||
2561 | { | 2570 | { |
2562 | gwps->display->stop_scroll(); | 2571 | gwps->display->stop_scroll(); |
2563 | gwps->state->id3 = audio_current_track(); | 2572 | gwps->state->id3 = audio_current_track(); |
2573 | |||
2574 | if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type | ||
2575 | && strcmp(gwps->state->id3->path, curr_cue->audio_filename)) | ||
2576 | { | ||
2577 | /* the current cuesheet isn't the right one any more */ | ||
2578 | |||
2579 | if (!strcmp(gwps->state->id3->path, temp_cue->audio_filename)) { | ||
2580 | /* We have the new cuesheet in memory (temp_cue), | ||
2581 | let's make it the current one ! */ | ||
2582 | memcpy(curr_cue, temp_cue, sizeof(struct cuesheet)); | ||
2583 | } | ||
2584 | else { | ||
2585 | /* We need to parse the new cuesheet */ | ||
2586 | |||
2587 | char cuepath[MAX_PATH]; | ||
2588 | strncpy(cuepath, gwps->state->id3->path, MAX_PATH); | ||
2589 | char *dot = strrchr(cuepath, '.'); | ||
2590 | strcpy(dot, ".cue"); | ||
2591 | |||
2592 | if (parse_cuesheet(cuepath, curr_cue)) | ||
2593 | { | ||
2594 | gwps->state->id3->cuesheet_type = 1; | ||
2595 | strcpy(curr_cue->audio_filename, gwps->state->id3->path); | ||
2596 | } | ||
2597 | } | ||
2598 | |||
2599 | cue_spoof_id3(curr_cue, gwps->state->id3); | ||
2600 | } | ||
2601 | |||
2564 | if (gui_wps_display()) | 2602 | if (gui_wps_display()) |
2565 | retcode = true; | 2603 | retcode = true; |
2566 | else{ | 2604 | else{ |
@@ -2572,12 +2610,34 @@ bool update(struct gui_wps *gwps) | |||
2572 | sizeof(gwps->state->current_track_path)); | 2610 | sizeof(gwps->state->current_track_path)); |
2573 | } | 2611 | } |
2574 | 2612 | ||
2613 | if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type | ||
2614 | && (gwps->state->id3->elapsed < curr_cue->curr_track->offset | ||
2615 | || (curr_cue->curr_track_idx < curr_cue->track_count - 1 | ||
2616 | && gwps->state->id3->elapsed >= (curr_cue->curr_track+1)->offset))) | ||
2617 | { | ||
2618 | /* We've changed tracks within the cuesheet : | ||
2619 | we need to update the ID3 info and refresh the WPS */ | ||
2620 | |||
2621 | cue_find_current_track(curr_cue, gwps->state->id3->elapsed); | ||
2622 | cue_spoof_id3(curr_cue, gwps->state->id3); | ||
2623 | |||
2624 | gwps->display->stop_scroll(); | ||
2625 | if (gui_wps_display()) | ||
2626 | retcode = true; | ||
2627 | else{ | ||
2628 | gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL); | ||
2629 | } | ||
2630 | gui_wps_statusbar_draw(gwps, false); | ||
2631 | |||
2632 | return retcode; | ||
2633 | } | ||
2634 | |||
2575 | if (gwps->state->id3) | 2635 | if (gwps->state->id3) |
2576 | gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC); | 2636 | gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC); |
2577 | 2637 | ||
2578 | gui_wps_statusbar_draw(gwps, false); | 2638 | gui_wps_statusbar_draw(gwps, false); |
2579 | 2639 | ||
2580 | return retcode; | 2640 | return retcode; |
2581 | } | 2641 | } |
2582 | 2642 | ||
2583 | 2643 | ||
diff --git a/apps/gui/gwps.c b/apps/gui/gwps.c index 7128a958f7..00290a8871 100644 --- a/apps/gui/gwps.c +++ b/apps/gui/gwps.c | |||
@@ -54,6 +54,7 @@ | |||
54 | #include "abrepeat.h" | 54 | #include "abrepeat.h" |
55 | #include "playback.h" | 55 | #include "playback.h" |
56 | #include "splash.h" | 56 | #include "splash.h" |
57 | #include "cuesheet.h" | ||
57 | #if LCD_DEPTH > 1 | 58 | #if LCD_DEPTH > 1 |
58 | #include "backdrop.h" | 59 | #include "backdrop.h" |
59 | #endif | 60 | #endif |
@@ -333,7 +334,16 @@ long gui_wps_show(void) | |||
333 | if (global_settings.party_mode) | 334 | if (global_settings.party_mode) |
334 | break; | 335 | break; |
335 | if (current_tick -last_right < HZ) | 336 | if (current_tick -last_right < HZ) |
336 | audio_next_dir(); | 337 | { |
338 | if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type) | ||
339 | { | ||
340 | audio_next(); | ||
341 | } | ||
342 | else | ||
343 | { | ||
344 | audio_next_dir(); | ||
345 | } | ||
346 | } | ||
337 | else ffwd_rew(ACTION_WPS_SEEKFWD); | 347 | else ffwd_rew(ACTION_WPS_SEEKFWD); |
338 | last_right = 0; | 348 | last_right = 0; |
339 | break; | 349 | break; |
@@ -343,7 +353,22 @@ long gui_wps_show(void) | |||
343 | if (global_settings.party_mode) | 353 | if (global_settings.party_mode) |
344 | break; | 354 | break; |
345 | if (current_tick -last_left < HZ) | 355 | if (current_tick -last_left < HZ) |
346 | audio_prev_dir(); | 356 | { |
357 | if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type) | ||
358 | { | ||
359 | if (!wps_state.paused) | ||
360 | #if (CONFIG_CODEC == SWCODEC) | ||
361 | audio_pre_ff_rewind(); | ||
362 | #else | ||
363 | audio_pause(); | ||
364 | #endif | ||
365 | audio_ff_rewind(0); | ||
366 | } | ||
367 | else | ||
368 | { | ||
369 | audio_prev_dir(); | ||
370 | } | ||
371 | } | ||
347 | else ffwd_rew(ACTION_WPS_SEEKBACK); | 372 | else ffwd_rew(ACTION_WPS_SEEKBACK); |
348 | last_left = 0; | 373 | last_left = 0; |
349 | break; | 374 | break; |
@@ -377,6 +402,13 @@ long gui_wps_show(void) | |||
377 | audio_prev(); | 402 | audio_prev(); |
378 | } | 403 | } |
379 | else { | 404 | else { |
405 | |||
406 | if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type) | ||
407 | { | ||
408 | curr_cuesheet_skip(-1, wps_state.id3->elapsed); | ||
409 | break; | ||
410 | } | ||
411 | |||
380 | if (!wps_state.paused) | 412 | if (!wps_state.paused) |
381 | #if (CONFIG_CODEC == SWCODEC) | 413 | #if (CONFIG_CODEC == SWCODEC) |
382 | audio_pre_ff_rewind(); | 414 | audio_pre_ff_rewind(); |
@@ -417,6 +449,18 @@ long gui_wps_show(void) | |||
417 | } | 449 | } |
418 | /* ...otherwise, do it normally */ | 450 | /* ...otherwise, do it normally */ |
419 | #endif | 451 | #endif |
452 | |||
453 | /* take care of if we're playing a cuesheet */ | ||
454 | if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type) | ||
455 | { | ||
456 | if (curr_cuesheet_skip(1, wps_state.id3->elapsed)) | ||
457 | { | ||
458 | /* if the result was false, then we really want | ||
459 | to skip to the next track */ | ||
460 | break; | ||
461 | } | ||
462 | } | ||
463 | |||
420 | audio_next(); | 464 | audio_next(); |
421 | break; | 465 | break; |
422 | /* next / prev directories */ | 466 | /* next / prev directories */ |
diff --git a/apps/lang/english.lang b/apps/lang/english.lang index f663e855ed..3085f839cf 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang | |||
@@ -10515,3 +10515,31 @@ | |||
10515 | *: "" | 10515 | *: "" |
10516 | </voice> | 10516 | </voice> |
10517 | </phrase> | 10517 | </phrase> |
10518 | <phrase> | ||
10519 | id: LANG_CUESHEET | ||
10520 | desc: | ||
10521 | user: | ||
10522 | <source> | ||
10523 | *: "Cuesheet" | ||
10524 | </source> | ||
10525 | <dest> | ||
10526 | *: "Cuesheet" | ||
10527 | </dest> | ||
10528 | <voice> | ||
10529 | *: "Cuesheet" | ||
10530 | </voice> | ||
10531 | </phrase> | ||
10532 | <phrase> | ||
10533 | id: LANG_CUESHEET_ENABLE | ||
10534 | desc: cuesheet support option | ||
10535 | user: | ||
10536 | <source> | ||
10537 | *: "Cuesheet Support" | ||
10538 | </source> | ||
10539 | <dest> | ||
10540 | *: "Cuesheet Support" | ||
10541 | </dest> | ||
10542 | <voice> | ||
10543 | *: "Cuesheet Support" | ||
10544 | </voice> | ||
10545 | </phrase> | ||
diff --git a/apps/main.c b/apps/main.c index dac7ac755d..4c643c4130 100644 --- a/apps/main.c +++ b/apps/main.c | |||
@@ -103,6 +103,8 @@ | |||
103 | #include "m5636.h" | 103 | #include "m5636.h" |
104 | #endif | 104 | #endif |
105 | 105 | ||
106 | #include "cuesheet.h" | ||
107 | |||
106 | /*#define AUTOROCK*/ /* define this to check for "autostart.rock" on boot */ | 108 | /*#define AUTOROCK*/ /* define this to check for "autostart.rock" on boot */ |
107 | 109 | ||
108 | const char appsversion[]=APPSVERSION; | 110 | const char appsversion[]=APPSVERSION; |
@@ -274,7 +276,8 @@ static void init(void) | |||
274 | global_settings.superbass); | 276 | global_settings.superbass); |
275 | 277 | ||
276 | scrobbler_init(); | 278 | scrobbler_init(); |
277 | 279 | cuesheet_init(); | |
280 | |||
278 | /* audio_init must to know the size of voice buffer so init voice first */ | 281 | /* audio_init must to know the size of voice buffer so init voice first */ |
279 | #if CONFIG_CODEC == SWCODEC | 282 | #if CONFIG_CODEC == SWCODEC |
280 | talk_init(); | 283 | talk_init(); |
@@ -489,6 +492,7 @@ static void init(void) | |||
489 | playlist_init(); | 492 | playlist_init(); |
490 | tree_init(); | 493 | tree_init(); |
491 | scrobbler_init(); | 494 | scrobbler_init(); |
495 | cuesheet_init(); | ||
492 | 496 | ||
493 | /* No buffer allocation (see buffer.c) may take place after the call to | 497 | /* No buffer allocation (see buffer.c) may take place after the call to |
494 | audio_init() since the mpeg thread takes the rest of the buffer space */ | 498 | audio_init() since the mpeg thread takes the rest of the buffer space */ |
diff --git a/apps/menus/playback_menu.c b/apps/menus/playback_menu.c index 7c0f75198d..4dbfb6c40d 100644 --- a/apps/menus/playback_menu.c +++ b/apps/menus/playback_menu.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include "dsp.h" | 33 | #include "dsp.h" |
34 | #include "scrobbler.h" | 34 | #include "scrobbler.h" |
35 | #include "audio.h" | 35 | #include "audio.h" |
36 | #include "cuesheet.h" | ||
36 | 37 | ||
37 | #if CONFIG_CODEC == SWCODEC | 38 | #if CONFIG_CODEC == SWCODEC |
38 | int setcrossfadeonexit_callback(int action,const struct menu_item_ex *this_item) | 39 | int setcrossfadeonexit_callback(int action,const struct menu_item_ex *this_item) |
@@ -146,6 +147,21 @@ int audioscrobbler_callback(int action,const struct menu_item_ex *this_item) | |||
146 | } | 147 | } |
147 | MENUITEM_SETTING(audioscrobbler, &global_settings.audioscrobbler, audioscrobbler_callback); | 148 | MENUITEM_SETTING(audioscrobbler, &global_settings.audioscrobbler, audioscrobbler_callback); |
148 | 149 | ||
150 | |||
151 | int cuesheet_callback(int action,const struct menu_item_ex *this_item) | ||
152 | { | ||
153 | (void)this_item; | ||
154 | switch (action) | ||
155 | { | ||
156 | case ACTION_EXIT_MENUITEM: /* on exit */ | ||
157 | if (!cuesheet_is_enabled() && global_settings.cuesheet) | ||
158 | gui_syncsplash(HZ*2, true, str(LANG_PLEASE_REBOOT)); | ||
159 | break; | ||
160 | } | ||
161 | return action; | ||
162 | } | ||
163 | MENUITEM_SETTING(cuesheet, &global_settings.cuesheet, cuesheet_callback); | ||
164 | |||
149 | #ifdef HAVE_HEADPHONE_DETECTION | 165 | #ifdef HAVE_HEADPHONE_DETECTION |
150 | MENUITEM_SETTING(unplug_mode, &global_settings.unplug_mode, NULL); | 166 | MENUITEM_SETTING(unplug_mode, &global_settings.unplug_mode, NULL); |
151 | MENUITEM_SETTING(unplug_rw, &global_settings.unplug_rw, NULL); | 167 | MENUITEM_SETTING(unplug_rw, &global_settings.unplug_rw, NULL); |
@@ -167,7 +183,7 @@ MAKE_MENU(playback_menu_item,ID2P(LANG_PLAYBACK),0, | |||
167 | #ifdef HAVE_SPDIF_POWER | 183 | #ifdef HAVE_SPDIF_POWER |
168 | &spdif_enable, | 184 | &spdif_enable, |
169 | #endif | 185 | #endif |
170 | &id3_v1_first, &next_folder, &audioscrobbler | 186 | &id3_v1_first, &next_folder, &audioscrobbler, &cuesheet |
171 | #ifdef HAVE_HEADPHONE_DETECTION | 187 | #ifdef HAVE_HEADPHONE_DETECTION |
172 | ,&unplug_menu | 188 | ,&unplug_menu |
173 | #endif | 189 | #endif |
diff --git a/apps/metadata.c b/apps/metadata.c index fbe7bfc233..ae2a8ecda8 100644 --- a/apps/metadata.c +++ b/apps/metadata.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include "replaygain.h" | 30 | #include "replaygain.h" |
31 | #include "debug.h" | 31 | #include "debug.h" |
32 | #include "system.h" | 32 | #include "system.h" |
33 | #include "cuesheet.h" | ||
33 | 34 | ||
34 | enum tagtype { TAGTYPE_APE = 1, TAGTYPE_VORBIS }; | 35 | enum tagtype { TAGTYPE_APE = 1, TAGTYPE_VORBIS }; |
35 | 36 | ||
@@ -2272,6 +2273,11 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname, | |||
2272 | 2273 | ||
2273 | /* We have successfully read the metadata from the file */ | 2274 | /* We have successfully read the metadata from the file */ |
2274 | 2275 | ||
2276 | if (cuesheet_is_enabled() && look_for_cuesheet_file(trackname)) | ||
2277 | { | ||
2278 | track->id3.cuesheet_type = 1; | ||
2279 | } | ||
2280 | |||
2275 | lseek(fd, 0, SEEK_SET); | 2281 | lseek(fd, 0, SEEK_SET); |
2276 | strncpy(track->id3.path, trackname, sizeof(track->id3.path)); | 2282 | strncpy(track->id3.path, trackname, sizeof(track->id3.path)); |
2277 | track->taginfo_ready = true; | 2283 | track->taginfo_ready = true; |
diff --git a/apps/playback.c b/apps/playback.c index d256f5a4f0..b80c449c47 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -57,6 +57,7 @@ | |||
57 | #include "buffer.h" | 57 | #include "buffer.h" |
58 | #include "dsp.h" | 58 | #include "dsp.h" |
59 | #include "abrepeat.h" | 59 | #include "abrepeat.h" |
60 | #include "cuesheet.h" | ||
60 | #ifdef HAVE_TAGCACHE | 61 | #ifdef HAVE_TAGCACHE |
61 | #include "tagcache.h" | 62 | #include "tagcache.h" |
62 | #endif | 63 | #endif |
@@ -2742,6 +2743,23 @@ static bool audio_load_track(int offset, bool start_play, bool rebuffer) | |||
2742 | 2743 | ||
2743 | } | 2744 | } |
2744 | 2745 | ||
2746 | if (cuesheet_is_enabled() && tracks[track_widx].id3.cuesheet_type == 1) | ||
2747 | { | ||
2748 | char cuepath[MAX_PATH]; | ||
2749 | strncpy(cuepath, trackname, MAX_PATH); | ||
2750 | char *dot = strrchr(cuepath, '.'); | ||
2751 | strcpy(dot, ".cue"); | ||
2752 | |||
2753 | struct cuesheet *cue = start_play ? curr_cue : temp_cue; | ||
2754 | |||
2755 | if (parse_cuesheet(cuepath, cue)) | ||
2756 | { | ||
2757 | strcpy((cue)->audio_filename, trackname); | ||
2758 | if (start_play) | ||
2759 | cue_spoof_id3(curr_cue, &tracks[track_widx].id3); | ||
2760 | } | ||
2761 | } | ||
2762 | |||
2745 | /* Load the codec. */ | 2763 | /* Load the codec. */ |
2746 | tracks[track_widx].codecbuf = &filebuf[buf_widx]; | 2764 | tracks[track_widx].codecbuf = &filebuf[buf_widx]; |
2747 | if (!audio_loadcodec(start_play)) | 2765 | if (!audio_loadcodec(start_play)) |
diff --git a/apps/settings.h b/apps/settings.h index 30872ac04f..3605e7e777 100644 --- a/apps/settings.h +++ b/apps/settings.h | |||
@@ -683,6 +683,7 @@ struct user_settings | |||
683 | #endif | 683 | #endif |
684 | /* Encoder Settings End */ | 684 | /* Encoder Settings End */ |
685 | #endif /* CONFIG_CODEC == SWCODEC */ | 685 | #endif /* CONFIG_CODEC == SWCODEC */ |
686 | bool cuesheet; | ||
686 | }; | 687 | }; |
687 | 688 | ||
688 | /** global variables **/ | 689 | /** global variables **/ |
diff --git a/apps/settings_list.c b/apps/settings_list.c index c40cf09b2e..e847cfa23e 100644 --- a/apps/settings_list.c +++ b/apps/settings_list.c | |||
@@ -919,6 +919,7 @@ const struct settings_list settings[] = { | |||
919 | OFFON_SETTING(0,usb_charging,LANG_USB_CHARGING,false,"usb charging",NULL), | 919 | OFFON_SETTING(0,usb_charging,LANG_USB_CHARGING,false,"usb charging",NULL), |
920 | #endif | 920 | #endif |
921 | #endif | 921 | #endif |
922 | OFFON_SETTING(0,cuesheet,LANG_CUESHEET_ENABLE,false,"cuesheet support", NULL), | ||
922 | }; | 923 | }; |
923 | 924 | ||
924 | const int nb_settings = sizeof(settings)/sizeof(*settings); | 925 | const int nb_settings = sizeof(settings)/sizeof(*settings); |
diff --git a/apps/tree.c b/apps/tree.c index 88492f4b65..806d1de616 100644 --- a/apps/tree.c +++ b/apps/tree.c | |||
@@ -133,6 +133,7 @@ const struct filetype filetypes[] = { | |||
133 | { "kbd", TREE_ATTR_KBD, Icon_Keyboard, VOICE_EXT_KBD }, | 133 | { "kbd", TREE_ATTR_KBD, Icon_Keyboard, VOICE_EXT_KBD }, |
134 | #endif | 134 | #endif |
135 | { "bmark",TREE_ATTR_BMARK, Icon_Bookmark, VOICE_EXT_BMARK }, | 135 | { "bmark",TREE_ATTR_BMARK, Icon_Bookmark, VOICE_EXT_BMARK }, |
136 | { "cue", TREE_ATTR_CUE, Icon_Bookmark, LANG_CUESHEET }, | ||
136 | #ifdef BOOTFILE_EXT | 137 | #ifdef BOOTFILE_EXT |
137 | { BOOTFILE_EXT, TREE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ }, | 138 | { BOOTFILE_EXT, TREE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ }, |
138 | #endif /* #ifndef SIMULATOR */ | 139 | #endif /* #ifndef SIMULATOR */ |
diff --git a/apps/tree.h b/apps/tree.h index 003714252e..110fc09f0c 100644 --- a/apps/tree.h +++ b/apps/tree.h | |||
@@ -94,6 +94,7 @@ struct tree_context { | |||
94 | #define TREE_ATTR_BMP 0x1100 /* backdrop bmp file */ | 94 | #define TREE_ATTR_BMP 0x1100 /* backdrop bmp file */ |
95 | #define TREE_ATTR_KBD 0x1200 /* keyboard file */ | 95 | #define TREE_ATTR_KBD 0x1200 /* keyboard file */ |
96 | #define TREE_ATTR_FMR 0x1300 /* preset file */ | 96 | #define TREE_ATTR_FMR 0x1300 /* preset file */ |
97 | #define TREE_ATTR_CUE 0x1400 /* cuesheet file */ | ||
97 | #define TREE_ATTR_MASK 0xFF00 /* which bits tree.c uses for file types */ | 98 | #define TREE_ATTR_MASK 0xFF00 /* which bits tree.c uses for file types */ |
98 | 99 | ||
99 | void tree_get_filetypes(const struct filetype**, int*); | 100 | void tree_get_filetypes(const struct filetype**, int*); |