Update the discussion of themeing in the manual, and put a note in the wps tags appen...
[kugel-rb.git] / apps / cuesheet.c
blobc75ea6533497151a547f64a3abddae6d3cfcd34d
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stdbool.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include "system.h"
28 #include "audio.h"
29 #include "kernel.h"
30 #include "logf.h"
31 #include "misc.h"
32 #include "screens.h"
33 #include "list.h"
34 #include "action.h"
35 #include "lang.h"
36 #include "debug.h"
37 #include "settings.h"
38 #include "plugin.h"
39 #include "playback.h"
40 #include "cuesheet.h"
41 #include "gui/wps.h"
43 #define CUE_DIR ROCKBOX_DIR "/cue"
45 bool look_for_cuesheet_file(const char *trackpath, char *found_cue_path)
47 /* DEBUGF("look for cue file\n"); */
49 char cuepath[MAX_PATH];
50 char *dot, *slash;
52 slash = strrchr(trackpath, '/');
53 if (!slash)
55 found_cue_path = NULL;
56 return false;
59 strlcpy(cuepath, trackpath, MAX_PATH);
60 dot = strrchr(cuepath, '.');
61 strcpy(dot, ".cue");
63 if (!file_exists(cuepath))
65 strcpy(cuepath, CUE_DIR);
66 strcat(cuepath, slash);
67 char *dot = strrchr(cuepath, '.');
68 strcpy(dot, ".cue");
69 if (!file_exists(cuepath))
71 if (found_cue_path)
72 found_cue_path = NULL;
73 return false;
77 if (found_cue_path)
78 strlcpy(found_cue_path, cuepath, MAX_PATH);
79 return true;
82 static char *get_string(const char *line)
84 char *start, *end;
86 start = strchr(line, '"');
87 if (!start)
89 start = strchr(line, ' ');
91 if (!start)
92 return NULL;
95 end = strchr(++start, '"');
96 if (end)
97 *end = '\0';
99 return start;
102 /* parse cuesheet "file" and store the information in "cue" */
103 bool parse_cuesheet(char *file, struct cuesheet *cue)
105 char line[MAX_PATH];
106 char *s;
107 bool utf8 = false;
109 int fd = open_utf8(file,O_RDONLY);
110 if (fd < 0)
112 /* couln't open the file */
113 return false;
115 if(lseek(fd, 0, SEEK_CUR) > 0)
116 utf8 = true;
118 /* Initialization */
119 memset(cue, 0, sizeof(struct cuesheet));
120 strcpy(cue->path, file);
121 cue->curr_track = cue->tracks;
123 while ( read_line(fd,line,MAX_PATH) && cue->track_count < MAX_TRACKS )
125 s = skip_whitespace(line);
127 if (!strncmp(s, "TRACK", 5))
129 cue->track_count++;
131 else if (!strncmp(s, "INDEX 01", 8))
133 s = strchr(s,' ');
134 s = skip_whitespace(s);
135 s = strchr(s,' ');
136 s = skip_whitespace(s);
137 cue->tracks[cue->track_count-1].offset = 60*1000 * atoi(s);
138 s = strchr(s,':') + 1;
139 cue->tracks[cue->track_count-1].offset += 1000 * atoi(s);
140 s = strchr(s,':') + 1;
141 cue->tracks[cue->track_count-1].offset += 13 * atoi(s);
143 else if (!strncmp(s, "TITLE", 5)
144 || !strncmp(s, "PERFORMER", 9)
145 || !strncmp(s, "SONGWRITER", 10))
147 char *dest = NULL;
148 char *string = get_string(s);
149 if (!string)
150 break;
152 switch (*s)
154 case 'T': /* TITLE */
155 dest = (cue->track_count <= 0) ? cue->title :
156 cue->tracks[cue->track_count-1].title;
157 break;
159 case 'P': /* PERFORMER */
160 dest = (cue->track_count <= 0) ? cue->performer :
161 cue->tracks[cue->track_count-1].performer;
162 break;
164 case 'S': /* SONGWRITER */
165 dest = (cue->track_count <= 0) ? cue->songwriter :
166 cue->tracks[cue->track_count-1].songwriter;
167 break;
170 if (dest)
172 if (!utf8)
174 dest = iso_decode(string, dest, -1, MIN(strlen(string), MAX_NAME));
175 *dest = '\0';
177 else
179 strlcpy(dest, string, MAX_NAME*3 + 1);
184 close(fd);
186 /* If some songs don't have performer info, we copy the cuesheet performer */
187 int i;
188 for (i = 0; i < cue->track_count; i++)
190 if (*(cue->tracks[i].performer) == '\0')
191 strlcpy(cue->tracks[i].performer, cue->performer, MAX_NAME*3);
193 if (*(cue->tracks[i].songwriter) == '\0')
194 strlcpy(cue->tracks[i].songwriter, cue->songwriter, MAX_NAME*3);
197 return true;
200 /* takes care of seeking to a track in a playlist
201 * returns false if audio isn't playing */
202 static bool seek(unsigned long pos)
204 if (!(audio_status() & AUDIO_STATUS_PLAY))
206 return false;
208 else
210 #if (CONFIG_CODEC == SWCODEC)
211 audio_pre_ff_rewind();
212 audio_ff_rewind(pos);
213 #else
214 audio_pause();
215 audio_ff_rewind(pos);
216 audio_resume();
217 #endif
218 return true;
222 /* returns the index of the track currently being played
223 and updates the information about the current track. */
224 int cue_find_current_track(struct cuesheet *cue, unsigned long curpos)
226 int i=0;
227 while (i < cue->track_count-1 && cue->tracks[i+1].offset < curpos)
228 i++;
230 cue->curr_track_idx = i;
231 cue->curr_track = cue->tracks + i;
232 return i;
235 /* callback that gives list item titles for the cuesheet browser */
236 static const char* list_get_name_cb(int selected_item,
237 void *data,
238 char *buffer,
239 size_t buffer_len)
241 struct cuesheet *cue = (struct cuesheet *)data;
243 if (selected_item & 1)
244 strlcpy(buffer, cue->tracks[selected_item/2].title, buffer_len);
245 else
246 snprintf(buffer, buffer_len, "%02d. %s", selected_item/2+1,
247 cue->tracks[selected_item/2].performer);
249 return buffer;
252 void browse_cuesheet(struct cuesheet *cue)
254 struct gui_synclist lists;
255 int action;
256 bool done = false;
257 int sel;
258 char title[MAX_PATH];
259 char cuepath[MAX_PATH];
260 struct mp3entry *id3 = audio_current_track();
262 snprintf(title, MAX_PATH, "%s: %s", cue->performer, cue->title);
263 gui_synclist_init(&lists, list_get_name_cb, cue, false, 2, NULL);
264 gui_synclist_set_nb_items(&lists, 2*cue->track_count);
265 gui_synclist_set_title(&lists, title, 0);
268 if (id3)
270 gui_synclist_select_item(&lists,
271 2*cue_find_current_track(cue, id3->elapsed));
274 while (!done)
276 gui_synclist_draw(&lists);
277 action = get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
278 if (gui_synclist_do_button(&lists, &action, LIST_WRAP_UNLESS_HELD))
279 continue;
280 switch (action)
282 case ACTION_STD_OK:
283 id3 = audio_current_track();
284 if (id3 && *id3->path && strcmp(id3->path, "No file!"))
286 look_for_cuesheet_file(id3->path, cuepath);
287 if (id3->cuesheet && !strcmp(cue->path, cuepath))
289 sel = gui_synclist_get_sel_pos(&lists);
290 seek(cue->tracks[sel/2].offset);
293 break;
294 case ACTION_STD_CANCEL:
295 done = true;
300 bool display_cuesheet_content(char* filename)
302 size_t bufsize = 0;
303 struct cuesheet *cue = (struct cuesheet *)plugin_get_buffer(&bufsize);
304 if (!cue || bufsize < sizeof(struct cuesheet))
305 return false;
307 if (!parse_cuesheet(filename, cue))
308 return false;
310 browse_cuesheet(cue);
311 return true;
314 /* skips backwards or forward in the current cuesheet
315 * the return value indicates whether we're still in a cusheet after skipping
316 * it also returns false if we weren't in a cuesheet.
317 * direction should be 1 or -1.
319 bool curr_cuesheet_skip(struct cuesheet *cue, int direction, unsigned long curr_pos)
321 int track = cue_find_current_track(cue, curr_pos);
323 if (direction >= 0 && track == cue->track_count - 1)
325 /* we want to get out of the cuesheet */
326 return false;
328 else
330 if (!(direction <= 0 && track == 0))
332 /* If skipping forward, skip to next cuesheet segment. If skipping
333 backward before DEFAULT_SKIP_TRESH milliseconds have elapsed, skip
334 to previous cuesheet segment. If skipping backward after
335 DEFAULT_SKIP_TRESH seconds have elapsed, skip to the start of the
336 current cuesheet segment */
337 if (direction == 1 ||
338 ((curr_pos - cue->tracks[track].offset) < DEFAULT_SKIP_TRESH))
340 track += direction;
344 seek(cue->tracks[track].offset);
345 return true;
350 void cue_spoof_id3(struct cuesheet *cue, struct mp3entry *id3)
352 if (!cue || !cue->curr_track)
353 return;
355 struct cue_track_info *track = cue->curr_track;
357 id3->title = *track->title ? track->title : NULL;
358 id3->artist = *track->performer ? track->performer : NULL;
359 id3->composer = *track->songwriter ? track->songwriter : NULL;
360 id3->album = *cue->title ? cue->title : NULL;
362 /* if the album artist is the same as the track artist, we hide it. */
363 if (strcmp(cue->performer, track->performer))
364 id3->albumartist = *cue->performer ? cue->performer : NULL;
365 else
366 id3->albumartist = NULL;
368 int i = cue->curr_track_idx;
369 id3->tracknum = i+1;
370 if (id3->track_string)
371 snprintf(id3->track_string, 10, "%d/%d", i+1, cue->track_count);
374 #ifdef HAVE_LCD_BITMAP
375 static inline void draw_veritcal_line_mark(struct screen * screen,
376 int x, int y, int h)
378 screen->set_drawmode(DRMODE_COMPLEMENT);
379 screen->vline(x, y, y+h-1);
382 /* draw the cuesheet markers for a track of length "tracklen",
383 between (x,y) and (x+w,y) */
384 void cue_draw_markers(struct screen *screen, struct cuesheet *cue,
385 unsigned long tracklen,
386 int x, int y, int w, int h)
388 int i,xi;
389 for (i=1; i < cue->track_count; i++)
391 xi = x + (w * cue->tracks[i].offset)/tracklen;
392 draw_veritcal_line_mark(screen, xi, y, h);
395 #endif
397 bool cuesheet_subtrack_changed(struct mp3entry *id3)
399 struct cuesheet *cue = id3->cuesheet;
400 if (cue && (id3->elapsed < cue->curr_track->offset
401 || (cue->curr_track_idx < cue->track_count - 1
402 && id3->elapsed >= (cue->curr_track+1)->offset)))
404 cue_find_current_track(cue, id3->elapsed);
405 cue_spoof_id3(cue, id3);
406 return true;
408 return false;