FS#9638 - temp_cue is unused and wasting memory.
[kugel-rb.git] / apps / cuesheet.c
blob0a7521bbc5d843abeb00764d5034e44ade70f677
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 "sprintf.h"
32 #include "misc.h"
33 #include "screens.h"
34 #include "splash.h"
35 #include "list.h"
36 #include "action.h"
37 #include "lang.h"
38 #include "debug.h"
39 #include "settings.h"
40 #include "buffer.h"
41 #include "plugin.h"
42 #include "playback.h"
43 #include "cuesheet.h"
45 #define CUE_DIR ROCKBOX_DIR "/cue"
47 struct cuesheet *curr_cue;
49 #if CONFIG_CODEC != SWCODEC
50 /* special trickery because the hwcodec playback engine is in firmware/ */
51 static bool cuesheet_handler(const char *filename)
53 return cuesheet_is_enabled() && look_for_cuesheet_file(filename, NULL);
55 #endif
57 void cuesheet_init(void)
59 if (global_settings.cuesheet) {
60 curr_cue = (struct cuesheet *)buffer_alloc(sizeof(struct cuesheet));
61 #if CONFIG_CODEC != SWCODEC
62 audio_set_cuesheet_callback(cuesheet_handler);
63 #endif
64 } else {
65 curr_cue = NULL;
69 bool cuesheet_is_enabled(void)
71 return (curr_cue != NULL);
74 bool look_for_cuesheet_file(const char *trackpath, char *found_cue_path)
76 /* DEBUGF("look for cue file\n"); */
78 char cuepath[MAX_PATH];
79 char *dot, *slash;
81 slash = strrchr(trackpath, '/');
82 if (!slash)
84 found_cue_path = NULL;
85 return false;
88 strncpy(cuepath, trackpath, MAX_PATH);
89 dot = strrchr(cuepath, '.');
90 strcpy(dot, ".cue");
92 if (!file_exists(cuepath))
94 strcpy(cuepath, CUE_DIR);
95 strcat(cuepath, slash);
96 char *dot = strrchr(cuepath, '.');
97 strcpy(dot, ".cue");
98 if (!file_exists(cuepath))
100 if (found_cue_path)
101 found_cue_path = NULL;
102 return false;
106 if (found_cue_path)
107 strncpy(found_cue_path, cuepath, MAX_PATH);
108 return true;
111 static char *skip_whitespace(char* buf)
113 char *r = buf;
114 while (*r && isspace(*r))
115 r++;
116 return r;
120 static char *get_string(const char *line)
122 char *start, *end;
124 start = strchr(line, '"');
125 if (!start)
127 start = strchr(line, ' ');
129 if (!start)
130 return NULL;
133 end = strchr(++start, '"');
134 if (end)
135 *end = '\0';
137 return start;
140 /* parse cuesheet "file" and store the information in "cue" */
141 bool parse_cuesheet(char *file, struct cuesheet *cue)
143 char line[MAX_PATH];
144 char *s;
145 bool utf8 = false;
147 DEBUGF("cue parse\n");
148 int fd = open_utf8(file,O_RDONLY);
149 if (fd < 0)
151 /* couln't open the file */
152 return false;
154 if(lseek(fd, 0, SEEK_CUR) > 0)
155 utf8 = true;
157 /* Initialization */
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))
168 cue->track_count++;
170 else if (!strncmp(s, "INDEX 01", 8))
172 s = strchr(s,' ');
173 s = skip_whitespace(s);
174 s = strchr(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))
186 char *dest = NULL;
187 char *string = get_string(s);
188 if (!string)
189 break;
191 switch (*s)
193 case 'T': /* TITLE */
194 dest = (cue->track_count <= 0) ? cue->title :
195 cue->tracks[cue->track_count-1].title;
196 break;
198 case 'P': /* PERFORMER */
199 dest = (cue->track_count <= 0) ? cue->performer :
200 cue->tracks[cue->track_count-1].performer;
201 break;
203 case 'S': /* SONGWRITER */
204 dest = (cue->track_count <= 0) ? cue->songwriter :
205 cue->tracks[cue->track_count-1].songwriter;
206 break;
209 if (dest)
211 if (!utf8)
213 dest = iso_decode(string, dest, -1, MIN(strlen(string), MAX_NAME));
214 *dest = '\0';
216 else
218 strncpy(dest, string, MAX_NAME*3);
219 dest[MAX_NAME*3] = '\0';
224 close(fd);
226 /* If some songs don't have performer info, we copy the cuesheet performer */
227 int i;
228 for (i = 0; i < cue->track_count; i++)
230 if (*(cue->tracks[i].performer) == '\0')
231 strncpy(cue->tracks[i].performer, cue->performer, MAX_NAME*3);
233 if (*(cue->tracks[i].songwriter) == '\0')
234 strncpy(cue->tracks[i].songwriter, cue->songwriter, MAX_NAME*3);
237 return true;
240 /* takes care of seeking to a track in a playlist
241 * returns false if audio isn't playing */
242 static bool seek(unsigned long pos)
244 if (!(audio_status() & AUDIO_STATUS_PLAY))
246 return false;
248 else
250 #if (CONFIG_CODEC == SWCODEC)
251 audio_pre_ff_rewind();
252 audio_ff_rewind(pos);
253 #else
254 audio_pause();
255 audio_ff_rewind(pos);
256 audio_resume();
257 #endif
258 return true;
262 /* returns the index of the track currently being played
263 and updates the information about the current track. */
264 int cue_find_current_track(struct cuesheet *cue, unsigned long curpos)
266 int i=0;
267 while (i < cue->track_count-1 && cue->tracks[i+1].offset < curpos)
268 i++;
270 cue->curr_track_idx = i;
271 cue->curr_track = cue->tracks + i;
272 return i;
275 /* callback that gives list item titles for the cuesheet browser */
276 static char *list_get_name_cb(int selected_item,
277 void *data,
278 char *buffer,
279 size_t buffer_len)
281 struct cuesheet *cue = (struct cuesheet *)data;
283 if (selected_item & 1)
284 strncpy(buffer, cue->tracks[selected_item/2].title, buffer_len);
285 else
286 snprintf(buffer, buffer_len, "%02d. %s", selected_item/2+1,
287 cue->tracks[selected_item/2].performer);
289 return buffer;
292 void browse_cuesheet(struct cuesheet *cue)
294 struct gui_synclist lists;
295 int action;
296 bool done = false;
297 int sel;
298 char title[MAX_PATH];
299 char cuepath[MAX_PATH];
300 struct mp3entry *id3 = audio_current_track();
302 snprintf(title, MAX_PATH, "%s: %s", cue->performer, cue->title);
303 gui_synclist_init(&lists, list_get_name_cb, cue, false, 2, NULL);
304 gui_synclist_set_nb_items(&lists, 2*cue->track_count);
305 gui_synclist_set_title(&lists, title, 0);
307 if (id3 && *id3->path && strcmp(id3->path, "No file!"))
309 look_for_cuesheet_file(id3->path, cuepath);
312 if (id3 && id3->cuesheet_type && !strcmp(cue->path, cuepath))
314 gui_synclist_select_item(&lists,
315 2*cue_find_current_track(cue, id3->elapsed));
318 while (!done)
320 gui_synclist_draw(&lists);
321 action = get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
322 if (gui_synclist_do_button(&lists, &action, LIST_WRAP_UNLESS_HELD))
323 continue;
324 switch (action)
326 case ACTION_STD_OK:
327 id3 = audio_current_track();
328 if (id3 && *id3->path && strcmp(id3->path, "No file!"))
330 look_for_cuesheet_file(id3->path, cuepath);
331 if (id3->cuesheet_type && !strcmp(cue->path, cuepath))
333 sel = gui_synclist_get_sel_pos(&lists);
334 seek(cue->tracks[sel/2].offset);
337 break;
338 case ACTION_STD_CANCEL:
339 done = true;
344 bool display_cuesheet_content(char* filename)
346 size_t bufsize = 0;
347 struct cuesheet *cue = (struct cuesheet *)plugin_get_buffer(&bufsize);
348 if (!cue || bufsize < sizeof(struct cuesheet))
349 return false;
351 if (!parse_cuesheet(filename, cue))
352 return false;
354 browse_cuesheet(cue);
355 return true;
358 /* skips backwards or forward in the current cuesheet
359 * the return value indicates whether we're still in a cusheet after skipping
360 * it also returns false if we weren't in a cuesheet.
361 * direction should be 1 or -1.
363 bool curr_cuesheet_skip(int direction, unsigned long curr_pos)
365 int track = cue_find_current_track(curr_cue, curr_pos);
367 if (direction >= 0 && track == curr_cue->track_count - 1)
369 /* we want to get out of the cuesheet */
370 return false;
372 else
374 if (!(direction <= 0 && track == 0))
375 track += direction;
377 seek(curr_cue->tracks[track].offset);
378 return true;
383 void cue_spoof_id3(struct cuesheet *cue, struct mp3entry *id3)
385 if (!cue || !cue->curr_track)
386 return;
388 struct cue_track_info *track = cue->curr_track;
390 id3->title = *track->title ? track->title : NULL;
391 id3->artist = *track->performer ? track->performer : NULL;
392 id3->composer = *track->songwriter ? track->songwriter : NULL;
393 id3->album = *cue->title ? cue->title : NULL;
395 /* if the album artist is the same as the track artist, we hide it. */
396 if (strcmp(cue->performer, track->performer))
397 id3->albumartist = *cue->performer ? cue->performer : NULL;
398 else
399 id3->albumartist = NULL;
401 int i = cue->curr_track_idx;
402 id3->tracknum = i+1;
403 if (id3->track_string)
404 snprintf(id3->track_string, 10, "%d/%d", i+1, cue->track_count);
407 #ifdef HAVE_LCD_BITMAP
408 static inline void draw_veritcal_line_mark(struct screen * screen,
409 int x, int y, int h)
411 screen->set_drawmode(DRMODE_COMPLEMENT);
412 screen->vline(x, y, y+h-1);
415 /* draw the cuesheet markers for a track of length "tracklen",
416 between (x1,y) and (x2,y) */
417 void cue_draw_markers(struct screen *screen, unsigned long tracklen,
418 int x1, int x2, int y, int h)
420 int i,xi;
421 int w = x2 - x1;
422 for (i=1; i < curr_cue->track_count; i++)
424 xi = x1 + (w * curr_cue->tracks[i].offset)/tracklen;
425 draw_veritcal_line_mark(screen, xi, y, h);
428 #endif