summaryrefslogtreecommitdiff
path: root/apps/cuesheet.c
diff options
context:
space:
mode:
authorNicolas Pennequin <nicolas.pennequin@free.fr>2007-02-14 14:40:24 +0000
committerNicolas Pennequin <nicolas.pennequin@free.fr>2007-02-14 14:40:24 +0000
commit9f4bd8712fc122f61ec162c544d613a95c3ca66e (patch)
tree4e652a1e7c19ac8a6bb789ee79304744c133d029 /apps/cuesheet.c
parent0403c2a572154667f3f2bd671d7d5a7cc08c64af (diff)
downloadrockbox-9f4bd8712fc122f61ec162c544d613a95c3ca66e.tar.gz
rockbox-9f4bd8712fc122f61ec162c544d613a95c3ca66e.zip
Cuesheet support by Jonathan Gordon and me (FS #6460).
Everytime an audio file is loaded, a cue file with the same name is searched for. A setting allows to disable this (default is off). Cuesheet files can also be viewed in the file browser. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12304 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/cuesheet.c')
-rw-r--r--apps/cuesheet.c360
1 files changed, 360 insertions, 0 deletions
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
44void 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
55bool cuesheet_is_enabled(void)
56{
57 return (curr_cue != NULL);
58}
59
60bool 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
79char *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" */
88bool 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 */
174bool 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. */
194int 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 */
207char *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
227void 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
284bool 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 */
303bool 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
323void 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
340static 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) */
349void 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