Better endianness handling, removed some leftovers
[kugel-rb.git] / apps / cuesheet.c
blob0d96eafe0c65a820876b1a0b8358f57769be5cd3
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $$
10 * Copyright (C) 2007 Nicolas Pennequin, Jonathan Gordon
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
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"
43 #if CONFIG_CODEC != SWCODEC
44 /* special trickery because the hwcodec playback engine is in firmware/ */
45 static bool cuesheet_handler(const char *filename)
47 return cuesheet_is_enabled() && look_for_cuesheet_file(filename);
49 #endif
51 void cuesheet_init(void)
53 if (global_settings.cuesheet) {
54 curr_cue = (struct cuesheet *)buffer_alloc(sizeof(struct cuesheet));
55 temp_cue = (struct cuesheet *)buffer_alloc(sizeof(struct cuesheet));
56 #if CONFIG_CODEC != SWCODEC
57 audio_set_cuesheet_callback(cuesheet_handler);
58 #endif
59 } else {
60 curr_cue = NULL;
61 temp_cue = NULL;
65 bool cuesheet_is_enabled(void)
67 return (curr_cue != NULL);
70 bool look_for_cuesheet_file(const char *trackpath)
72 char cuepath[MAX_PATH];
73 strncpy(cuepath, trackpath, MAX_PATH);
74 char *dot = strrchr(cuepath, '.');
75 strcpy(dot, ".cue");
77 int fd = open(cuepath,O_RDONLY);
78 if (fd < 0)
80 return false;
82 else
84 close(fd);
85 return true;
89 static char *skip_whitespace(char* buf)
91 char *r = buf;
92 while (*r && isspace(*r))
93 r++;
94 return r;
98 static char *get_string(const char *line)
100 char *start, *end;
102 start = strchr(line, '"');
103 if (!start)
105 start = strchr(line, ' ');
107 if (!start)
108 return NULL;
111 end = strchr(++start, '"');
112 if (end)
113 *end = '\0';
115 return start;
118 /* parse cuesheet "file" and store the information in "cue" */
119 bool parse_cuesheet(char *file, struct cuesheet *cue)
121 char line[MAX_PATH];
122 char *s;
124 int fd = open(file,O_RDONLY);
125 if (fd < 0)
127 /* couln't open the file */
128 return false;
131 /* Initialization */
132 memset(cue, 0, sizeof(struct cuesheet));
133 strcpy(cue->path, file);
134 cue->curr_track = cue->tracks;
136 while ( read_line(fd,line,MAX_PATH) && cue->track_count < MAX_TRACKS )
138 s = skip_whitespace(line);
140 if (!strncmp(s, "TRACK", 5))
142 cue->track_count++;
144 else if (!strncmp(s, "INDEX 01", 8))
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);
156 else if (!strncmp(s, "TITLE", 5)
157 || !strncmp(s, "PERFORMER", 9)
158 || !strncmp(s, "SONGWRITER", 10))
160 char *dest = NULL;
161 char *string = get_string(s);
162 if (!string)
163 break;
165 switch (*s)
167 case 'T': /* TITLE */
168 dest = (cue->track_count <= 0) ? cue->title :
169 cue->tracks[cue->track_count-1].title;
170 break;
172 case 'P': /* PERFORMER */
173 dest = (cue->track_count <= 0) ? cue->performer :
174 cue->tracks[cue->track_count-1].performer;
175 break;
177 case 'S': /* SONGWRITER */
178 dest = (cue->track_count <= 0) ? cue->songwriter :
179 cue->tracks[cue->track_count-1].songwriter;
180 break;
183 if (dest)
184 strncpy(dest, string, MAX_NAME);
187 close(fd);
189 /* If some songs don't have performer info, we copy the cuesheet performer */
190 int i;
191 for (i = 0; i < cue->track_count; i++)
193 if (*(cue->tracks[i].performer) == '\0')
194 strncpy(cue->tracks[i].performer, cue->performer, MAX_NAME);
196 if (*(cue->tracks[i].songwriter) == '\0')
197 strncpy(cue->tracks[i].songwriter, cue->songwriter, MAX_NAME);
200 return true;
203 /* takes care of seeking to a track in a playlist
204 * returns false if audio isn't playing */
205 static bool seek(unsigned long pos)
207 if (!(audio_status() & AUDIO_STATUS_PLAY))
209 return false;
211 else
213 #if (CONFIG_CODEC == SWCODEC)
214 audio_pre_ff_rewind();
215 audio_ff_rewind(pos);
216 #else
217 audio_pause();
218 audio_ff_rewind(pos);
219 audio_resume();
220 #endif
221 return true;
225 /* returns the index of the track currently being played
226 and updates the information about the current track. */
227 int cue_find_current_track(struct cuesheet *cue, unsigned long curpos)
229 int i=0;
230 while (i < cue->track_count-1 && cue->tracks[i+1].offset < curpos)
231 i++;
233 cue->curr_track_idx = i;
234 cue->curr_track = cue->tracks + i;
235 return i;
238 /* callback that gives list item titles for the cuesheet browser */
239 static char *list_get_name_cb(int selected_item,
240 void *data,
241 char *buffer)
243 struct cuesheet *cue = (struct cuesheet *)data;
245 if (selected_item & 1)
246 snprintf(buffer, MAX_PATH, "%s",
247 cue->tracks[selected_item/2].title);
248 else
249 snprintf(buffer, MAX_PATH, "%02d. %s", selected_item/2+1,
250 cue->tracks[selected_item/2].performer);
252 return buffer;
255 static void browse_cuesheet(struct cuesheet *cue)
257 struct gui_synclist lists;
258 int action;
259 bool done = false;
260 int sel;
261 char title[MAX_PATH];
262 char cuepath[MAX_PATH];
263 char *dot;
264 struct mp3entry *id3 = audio_current_track();
266 snprintf(title, MAX_PATH, "%s: %s", cue->performer, cue->title);
267 gui_synclist_init(&lists, list_get_name_cb, cue, false, 2);
268 gui_synclist_set_nb_items(&lists, 2*cue->track_count);
269 gui_synclist_set_title(&lists, title, 0);
271 if (id3 && *id3->path && strcmp(id3->path, "No file!"))
273 strncpy(cuepath, id3->path, MAX_PATH);
274 dot = strrchr(cuepath, '.');
275 strcpy(dot, ".cue");
278 if (id3 && id3->cuesheet_type && !strcmp(cue->path, cuepath))
280 gui_synclist_select_item(&lists,
281 2*cue_find_current_track(cue, id3->elapsed));
284 while (!done)
286 gui_synclist_draw(&lists);
287 action = get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
288 if (gui_synclist_do_button(&lists,action,LIST_WRAP_UNLESS_HELD))
289 continue;
290 switch (action)
292 case ACTION_STD_OK:
293 id3 = audio_current_track();
294 if (id3 && *id3->path && strcmp(id3->path, "No file!"))
296 strncpy(cuepath, id3->path, MAX_PATH);
297 dot = strrchr(cuepath, '.');
298 strcpy(dot, ".cue");
299 if (id3->cuesheet_type && !strcmp(cue->path, cuepath))
301 sel = gui_synclist_get_sel_pos(&lists);
302 seek(cue->tracks[sel/2].offset);
305 break;
306 case ACTION_STD_CANCEL:
307 done = true;
310 action_signalscreenchange();
313 bool display_cuesheet_content(char* filename)
315 unsigned int bufsize = 0;
316 struct cuesheet *cue = (struct cuesheet *)plugin_get_buffer(&bufsize);
317 if (!cue || bufsize < sizeof(struct cuesheet))
318 return false;
320 if (!parse_cuesheet(filename, cue))
321 return false;
323 browse_cuesheet(cue);
324 return true;
327 /* skips backwards or forward in the current cuesheet
328 * the return value indicates whether we're still in a cusheet after skipping
329 * it also returns false if we weren't in a cuesheet.
330 * direction should be 1 or -1.
332 bool curr_cuesheet_skip(int direction, unsigned long curr_pos)
334 int track = cue_find_current_track(curr_cue, curr_pos);
336 if (direction >= 0 && track == curr_cue->track_count - 1)
338 /* we want to get out of the cuesheet */
339 return false;
341 else
343 if (!(direction <= 0 && track == 0))
344 track += direction;
346 seek(curr_cue->tracks[track].offset);
347 return true;
352 void cue_spoof_id3(struct cuesheet *cue, struct mp3entry *id3)
354 if (!cue || !cue->curr_track)
355 return;
357 struct cue_track_info *track = cue->curr_track;
359 id3->title = *track->title ? track->title : NULL;
360 id3->artist = *track->performer ? track->performer : NULL;
361 id3->composer = *track->songwriter ? track->songwriter : NULL;
362 id3->album = *cue->title ? cue->title : NULL;
364 /* if the album artist is the same as the track artist, we hide it. */
365 if (strcmp(cue->performer, track->performer))
366 id3->albumartist = *cue->performer ? cue->performer : NULL;
367 else
368 id3->albumartist = NULL;
370 int i = cue->curr_track_idx;
371 id3->tracknum = i+1;
372 if (id3->track_string)
373 snprintf(id3->track_string, 10, "%d/%d", i+1, cue->track_count);
376 #ifdef HAVE_LCD_BITMAP
377 static inline void draw_veritcal_line_mark(struct screen * screen,
378 int x, int y, int h)
380 screen->set_drawmode(DRMODE_COMPLEMENT);
381 screen->vline(x, y, y+h-1);
384 /* draw the cuesheet markers for a track of length "tracklen",
385 between (x1,y) and (x2,y) */
386 void cue_draw_markers(struct screen *screen, unsigned long tracklen,
387 int x1, int x2, int y, int h)
389 int i,xi;
390 int w = x2 - x1;
391 for (i=1; i < curr_cue->track_count; i++)
393 xi = x1 + (w * curr_cue->tracks[i].offset)/tracklen;
394 draw_veritcal_line_mark(screen, xi, y, h);
397 #endif