1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 Nicolas Pennequin, Jonathan Gordon
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
45 #define CUE_DIR ROCKBOX_DIR "/cue"
47 struct cuesheet
*curr_cue
;
48 struct cuesheet
*temp_cue
;
50 #if CONFIG_CODEC != SWCODEC
51 /* special trickery because the hwcodec playback engine is in firmware/ */
52 static bool cuesheet_handler(const char *filename
)
54 return cuesheet_is_enabled() && look_for_cuesheet_file(filename
, NULL
);
58 void cuesheet_init(void)
60 if (global_settings
.cuesheet
) {
61 curr_cue
= (struct cuesheet
*)buffer_alloc(sizeof(struct cuesheet
));
62 temp_cue
= (struct cuesheet
*)buffer_alloc(sizeof(struct cuesheet
));
63 #if CONFIG_CODEC != SWCODEC
64 audio_set_cuesheet_callback(cuesheet_handler
);
72 bool cuesheet_is_enabled(void)
74 return (curr_cue
!= NULL
);
77 bool look_for_cuesheet_file(const char *trackpath
, char *found_cue_path
)
79 /* DEBUGF("look for cue file\n"); */
81 char cuepath
[MAX_PATH
];
84 slash
= strrchr(trackpath
, '/');
87 found_cue_path
= NULL
;
91 strncpy(cuepath
, trackpath
, MAX_PATH
);
92 dot
= strrchr(cuepath
, '.');
95 if (!file_exists(cuepath
))
97 strcpy(cuepath
, CUE_DIR
);
98 strcat(cuepath
, slash
);
99 char *dot
= strrchr(cuepath
, '.');
101 if (!file_exists(cuepath
))
104 found_cue_path
= NULL
;
110 strncpy(found_cue_path
, cuepath
, MAX_PATH
);
114 static char *skip_whitespace(char* buf
)
117 while (*r
&& isspace(*r
))
123 static char *get_string(const char *line
)
127 start
= strchr(line
, '"');
130 start
= strchr(line
, ' ');
136 end
= strchr(++start
, '"');
143 /* parse cuesheet "file" and store the information in "cue" */
144 bool parse_cuesheet(char *file
, struct cuesheet
*cue
)
149 DEBUGF("cue parse\n");
150 int fd
= open(file
,O_RDONLY
);
153 /* couln't open the file */
158 memset(cue
, 0, sizeof(struct cuesheet
));
159 strcpy(cue
->path
, file
);
160 cue
->curr_track
= cue
->tracks
;
162 while ( read_line(fd
,line
,MAX_PATH
) && cue
->track_count
< MAX_TRACKS
)
164 s
= skip_whitespace(line
);
166 if (!strncmp(s
, "TRACK", 5))
170 else if (!strncmp(s
, "INDEX 01", 8))
173 s
= skip_whitespace(s
);
175 s
= skip_whitespace(s
);
176 cue
->tracks
[cue
->track_count
-1].offset
= 60*1000 * atoi(s
);
177 s
= strchr(s
,':') + 1;
178 cue
->tracks
[cue
->track_count
-1].offset
+= 1000 * atoi(s
);
179 s
= strchr(s
,':') + 1;
180 cue
->tracks
[cue
->track_count
-1].offset
+= 13 * atoi(s
);
182 else if (!strncmp(s
, "TITLE", 5)
183 || !strncmp(s
, "PERFORMER", 9)
184 || !strncmp(s
, "SONGWRITER", 10))
187 char *string
= get_string(s
);
193 case 'T': /* TITLE */
194 dest
= (cue
->track_count
<= 0) ? cue
->title
:
195 cue
->tracks
[cue
->track_count
-1].title
;
198 case 'P': /* PERFORMER */
199 dest
= (cue
->track_count
<= 0) ? cue
->performer
:
200 cue
->tracks
[cue
->track_count
-1].performer
;
203 case 'S': /* SONGWRITER */
204 dest
= (cue
->track_count
<= 0) ? cue
->songwriter
:
205 cue
->tracks
[cue
->track_count
-1].songwriter
;
210 dest
= iso_decode(string
, dest
, -1, MIN(strlen(string
), MAX_NAME
));
217 /* If some songs don't have performer info, we copy the cuesheet performer */
219 for (i
= 0; i
< cue
->track_count
; i
++)
221 if (*(cue
->tracks
[i
].performer
) == '\0')
222 strncpy(cue
->tracks
[i
].performer
, cue
->performer
, MAX_NAME
);
224 if (*(cue
->tracks
[i
].songwriter
) == '\0')
225 strncpy(cue
->tracks
[i
].songwriter
, cue
->songwriter
, MAX_NAME
);
231 /* takes care of seeking to a track in a playlist
232 * returns false if audio isn't playing */
233 static bool seek(unsigned long pos
)
235 if (!(audio_status() & AUDIO_STATUS_PLAY
))
241 #if (CONFIG_CODEC == SWCODEC)
242 audio_pre_ff_rewind();
243 audio_ff_rewind(pos
);
246 audio_ff_rewind(pos
);
253 /* returns the index of the track currently being played
254 and updates the information about the current track. */
255 int cue_find_current_track(struct cuesheet
*cue
, unsigned long curpos
)
258 while (i
< cue
->track_count
-1 && cue
->tracks
[i
+1].offset
< curpos
)
261 cue
->curr_track_idx
= i
;
262 cue
->curr_track
= cue
->tracks
+ i
;
266 /* callback that gives list item titles for the cuesheet browser */
267 static char *list_get_name_cb(int selected_item
,
272 struct cuesheet
*cue
= (struct cuesheet
*)data
;
274 if (selected_item
& 1)
275 snprintf(buffer
, buffer_len
, "%s",
276 cue
->tracks
[selected_item
/2].title
);
278 snprintf(buffer
, buffer_len
, "%02d. %s", selected_item
/2+1,
279 cue
->tracks
[selected_item
/2].performer
);
284 void browse_cuesheet(struct cuesheet
*cue
)
286 struct gui_synclist lists
;
290 char title
[MAX_PATH
];
291 char cuepath
[MAX_PATH
];
292 struct mp3entry
*id3
= audio_current_track();
294 snprintf(title
, MAX_PATH
, "%s: %s", cue
->performer
, cue
->title
);
295 gui_synclist_init(&lists
, list_get_name_cb
, cue
, false, 2, NULL
);
296 gui_synclist_set_nb_items(&lists
, 2*cue
->track_count
);
297 gui_synclist_set_title(&lists
, title
, 0);
299 if (id3
&& *id3
->path
&& strcmp(id3
->path
, "No file!"))
301 look_for_cuesheet_file(id3
->path
, cuepath
);
304 if (id3
&& id3
->cuesheet_type
&& !strcmp(cue
->path
, cuepath
))
306 gui_synclist_select_item(&lists
,
307 2*cue_find_current_track(cue
, id3
->elapsed
));
312 gui_synclist_draw(&lists
);
313 action
= get_action(CONTEXT_LIST
,TIMEOUT_BLOCK
);
314 if (gui_synclist_do_button(&lists
, &action
, LIST_WRAP_UNLESS_HELD
))
319 id3
= audio_current_track();
320 if (id3
&& *id3
->path
&& strcmp(id3
->path
, "No file!"))
322 look_for_cuesheet_file(id3
->path
, cuepath
);
323 if (id3
->cuesheet_type
&& !strcmp(cue
->path
, cuepath
))
325 sel
= gui_synclist_get_sel_pos(&lists
);
326 seek(cue
->tracks
[sel
/2].offset
);
330 case ACTION_STD_CANCEL
:
336 bool display_cuesheet_content(char* filename
)
339 struct cuesheet
*cue
= (struct cuesheet
*)plugin_get_buffer(&bufsize
);
340 if (!cue
|| bufsize
< sizeof(struct cuesheet
))
343 if (!parse_cuesheet(filename
, cue
))
346 browse_cuesheet(cue
);
350 /* skips backwards or forward in the current cuesheet
351 * the return value indicates whether we're still in a cusheet after skipping
352 * it also returns false if we weren't in a cuesheet.
353 * direction should be 1 or -1.
355 bool curr_cuesheet_skip(int direction
, unsigned long curr_pos
)
357 int track
= cue_find_current_track(curr_cue
, curr_pos
);
359 if (direction
>= 0 && track
== curr_cue
->track_count
- 1)
361 /* we want to get out of the cuesheet */
366 if (!(direction
<= 0 && track
== 0))
369 seek(curr_cue
->tracks
[track
].offset
);
375 void cue_spoof_id3(struct cuesheet
*cue
, struct mp3entry
*id3
)
377 if (!cue
|| !cue
->curr_track
)
380 struct cue_track_info
*track
= cue
->curr_track
;
382 id3
->title
= *track
->title
? track
->title
: NULL
;
383 id3
->artist
= *track
->performer
? track
->performer
: NULL
;
384 id3
->composer
= *track
->songwriter
? track
->songwriter
: NULL
;
385 id3
->album
= *cue
->title
? cue
->title
: NULL
;
387 /* if the album artist is the same as the track artist, we hide it. */
388 if (strcmp(cue
->performer
, track
->performer
))
389 id3
->albumartist
= *cue
->performer
? cue
->performer
: NULL
;
391 id3
->albumartist
= NULL
;
393 int i
= cue
->curr_track_idx
;
395 if (id3
->track_string
)
396 snprintf(id3
->track_string
, 10, "%d/%d", i
+1, cue
->track_count
);
399 #ifdef HAVE_LCD_BITMAP
400 static inline void draw_veritcal_line_mark(struct screen
* screen
,
403 screen
->set_drawmode(DRMODE_COMPLEMENT
);
404 screen
->vline(x
, y
, y
+h
-1);
407 /* draw the cuesheet markers for a track of length "tracklen",
408 between (x1,y) and (x2,y) */
409 void cue_draw_markers(struct screen
*screen
, unsigned long tracklen
,
410 int x1
, int x2
, int y
, int h
)
414 for (i
=1; i
< curr_cue
->track_count
; i
++)
416 xi
= x1
+ (w
* curr_cue
->tracks
[i
].offset
)/tracklen
;
417 draw_veritcal_line_mark(screen
, xi
, y
, h
);