summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/cuesheet.c360
-rw-r--r--apps/cuesheet.h85
-rw-r--r--apps/filetree.c5
-rw-r--r--apps/gui/gwps-common.c64
-rw-r--r--apps/gui/gwps.c48
-rw-r--r--apps/lang/english.lang28
-rw-r--r--apps/main.c6
-rw-r--r--apps/menus/playback_menu.c18
-rw-r--r--apps/metadata.c6
-rw-r--r--apps/playback.c18
-rw-r--r--apps/settings.h1
-rw-r--r--apps/settings_list.c1
-rw-r--r--apps/tree.c1
-rw-r--r--apps/tree.h1
15 files changed, 637 insertions, 6 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index e57c882a8b..1793724665 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -32,6 +32,7 @@ settings_list.c
32settings_menu.c 32settings_menu.c
33sound_menu.c 33sound_menu.c
34status.c 34status.c
35cuesheet.c
35#if !defined(SIMULATOR) || CONFIG_CODEC == SWCODEC 36#if !defined(SIMULATOR) || CONFIG_CODEC == SWCODEC
36talk.c 37talk.c
37#endif 38#endif
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
diff --git a/apps/cuesheet.h b/apps/cuesheet.h
new file mode 100644
index 0000000000..9dff370e09
--- /dev/null
+++ b/apps/cuesheet.h
@@ -0,0 +1,85 @@
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#ifndef _CUESHEET_H_
21#define _CUESHEET_H_
22
23#include <stdbool.h>
24#include "screens.h"
25
26#define MAX_NAME 64 /* Max length of information strings */
27#define MAX_TRACKS 99 /* Max number of tracks in a cuesheet */
28
29struct cue_track_info {
30 char title[MAX_NAME];
31 char performer[MAX_NAME];
32 unsigned long offset; /* ms from start of track */
33};
34
35struct cuesheet {
36 char path[MAX_PATH];
37 char audio_filename[MAX_PATH];
38
39 char title[MAX_NAME];
40 char performer[MAX_NAME];
41
42 int track_count;
43 struct cue_track_info tracks[MAX_TRACKS];
44
45 int curr_track_idx;
46 struct cue_track_info *curr_track;
47};
48
49struct cuesheet *curr_cue;
50struct cuesheet *temp_cue;
51
52/* returns true if cuesheet support is initialised */
53bool cuesheet_is_enabled(void);
54
55/* allocates the cuesheet buffer */
56void cuesheet_init(void);
57
58/* looks if there is a cuesheet file that has a name matching "trackpath" */
59bool look_for_cuesheet_file(const char *trackpath);
60
61/* parse cuesheet "file" and store the information in "cue" */
62bool parse_cuesheet(char *file, struct cuesheet *cue);
63
64/* reads a cuesheet to find the audio track associated to it */
65bool get_trackname_from_cuesheet(char *filename, char *buf);
66
67/* displays a cuesheet to the screen (it is stored in the plugin buffer) */
68bool display_cuesheet_content(char* filename);
69
70/* finds the index of the current track played within a cuesheet */
71int cue_find_current_track(struct cuesheet *cue, unsigned long curpos);
72
73/* update the id3 info to that of the currently playing track in the cuesheet */
74void cue_spoof_id3(struct cuesheet *cue, struct mp3entry *id3);
75
76/* skip to next track in the cuesheet towards "direction" (which is 1 or -1) */
77bool curr_cuesheet_skip(int direction, unsigned long curr_pos);
78
79#ifdef HAVE_LCD_BITMAP
80/* draw track markers on the progressbar */
81void cue_draw_markers(struct screen *screen, unsigned long tracklen,
82 int x1, int x2, int y, int h);
83#endif
84
85#endif
diff --git a/apps/filetree.c b/apps/filetree.c
index 10174dbb31..df4065c91b 100644
--- a/apps/filetree.c
+++ b/apps/filetree.c
@@ -41,6 +41,7 @@
41#include "dircache.h" 41#include "dircache.h"
42#include "splash.h" 42#include "splash.h"
43#include "yesno.h" 43#include "yesno.h"
44#include "cuesheet.h"
44#ifdef HAVE_LCD_BITMAP 45#ifdef HAVE_LCD_BITMAP
45#include "keyboard.h" 46#include "keyboard.h"
46#endif 47#endif
@@ -550,6 +551,10 @@ int ft_enter(struct tree_context* c)
550 } 551 }
551 break; 552 break;
552 553
554 case TREE_ATTR_CUE:
555 display_cuesheet_content(buf);
556 break;
557
553 default: 558 default:
554 { 559 {
555 char* plugin; 560 char* plugin;
diff --git a/apps/gui/gwps-common.c b/apps/gui/gwps-common.c
index 53b12238e4..b6e64d2fcc 100644
--- a/apps/gui/gwps-common.c
+++ b/apps/gui/gwps-common.c
@@ -53,6 +53,7 @@
53#endif 53#endif
54#include "dsp.h" 54#include "dsp.h"
55#include "action.h" 55#include "action.h"
56#include "cuesheet.h"
56 57
57#ifdef HAVE_LCD_CHARCELLS 58#ifdef HAVE_LCD_CHARCELLS
58static bool draw_player_progress(struct gui_wps *gwps); 59static bool draw_player_progress(struct gui_wps *gwps);
@@ -1850,6 +1851,14 @@ bool gui_wps_refresh(struct gui_wps *gwps, int ffwd_offset,
1850 data->progress_start, data->progress_end, sb_y, 1851 data->progress_start, data->progress_end, sb_y,
1851 data->progress_height); 1852 data->progress_height);
1852#endif 1853#endif
1854
1855 if (cuesheet_is_enabled() && state->id3->cuesheet_type)
1856 {
1857 cue_draw_markers(display, state->id3->length,
1858 data->progress_start, data->progress_end,
1859 sb_y+1, data->progress_height-2);
1860 }
1861
1853 update_line = true; 1862 update_line = true;
1854 } 1863 }
1855 if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) { 1864 if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) {
@@ -2561,6 +2570,35 @@ bool update(struct gui_wps *gwps)
2561 { 2570 {
2562 gwps->display->stop_scroll(); 2571 gwps->display->stop_scroll();
2563 gwps->state->id3 = audio_current_track(); 2572 gwps->state->id3 = audio_current_track();
2573
2574 if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type
2575 && strcmp(gwps->state->id3->path, curr_cue->audio_filename))
2576 {
2577 /* the current cuesheet isn't the right one any more */
2578
2579 if (!strcmp(gwps->state->id3->path, temp_cue->audio_filename)) {
2580 /* We have the new cuesheet in memory (temp_cue),
2581 let's make it the current one ! */
2582 memcpy(curr_cue, temp_cue, sizeof(struct cuesheet));
2583 }
2584 else {
2585 /* We need to parse the new cuesheet */
2586
2587 char cuepath[MAX_PATH];
2588 strncpy(cuepath, gwps->state->id3->path, MAX_PATH);
2589 char *dot = strrchr(cuepath, '.');
2590 strcpy(dot, ".cue");
2591
2592 if (parse_cuesheet(cuepath, curr_cue))
2593 {
2594 gwps->state->id3->cuesheet_type = 1;
2595 strcpy(curr_cue->audio_filename, gwps->state->id3->path);
2596 }
2597 }
2598
2599 cue_spoof_id3(curr_cue, gwps->state->id3);
2600 }
2601
2564 if (gui_wps_display()) 2602 if (gui_wps_display())
2565 retcode = true; 2603 retcode = true;
2566 else{ 2604 else{
@@ -2572,12 +2610,34 @@ bool update(struct gui_wps *gwps)
2572 sizeof(gwps->state->current_track_path)); 2610 sizeof(gwps->state->current_track_path));
2573 } 2611 }
2574 2612
2613 if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type
2614 && (gwps->state->id3->elapsed < curr_cue->curr_track->offset
2615 || (curr_cue->curr_track_idx < curr_cue->track_count - 1
2616 && gwps->state->id3->elapsed >= (curr_cue->curr_track+1)->offset)))
2617 {
2618 /* We've changed tracks within the cuesheet :
2619 we need to update the ID3 info and refresh the WPS */
2620
2621 cue_find_current_track(curr_cue, gwps->state->id3->elapsed);
2622 cue_spoof_id3(curr_cue, gwps->state->id3);
2623
2624 gwps->display->stop_scroll();
2625 if (gui_wps_display())
2626 retcode = true;
2627 else{
2628 gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL);
2629 }
2630 gui_wps_statusbar_draw(gwps, false);
2631
2632 return retcode;
2633 }
2634
2575 if (gwps->state->id3) 2635 if (gwps->state->id3)
2576 gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC); 2636 gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);
2577 2637
2578 gui_wps_statusbar_draw(gwps, false); 2638 gui_wps_statusbar_draw(gwps, false);
2579 2639
2580 return retcode; 2640 return retcode;
2581} 2641}
2582 2642
2583 2643
diff --git a/apps/gui/gwps.c b/apps/gui/gwps.c
index 7128a958f7..00290a8871 100644
--- a/apps/gui/gwps.c
+++ b/apps/gui/gwps.c
@@ -54,6 +54,7 @@
54#include "abrepeat.h" 54#include "abrepeat.h"
55#include "playback.h" 55#include "playback.h"
56#include "splash.h" 56#include "splash.h"
57#include "cuesheet.h"
57#if LCD_DEPTH > 1 58#if LCD_DEPTH > 1
58#include "backdrop.h" 59#include "backdrop.h"
59#endif 60#endif
@@ -333,7 +334,16 @@ long gui_wps_show(void)
333 if (global_settings.party_mode) 334 if (global_settings.party_mode)
334 break; 335 break;
335 if (current_tick -last_right < HZ) 336 if (current_tick -last_right < HZ)
336 audio_next_dir(); 337 {
338 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
339 {
340 audio_next();
341 }
342 else
343 {
344 audio_next_dir();
345 }
346 }
337 else ffwd_rew(ACTION_WPS_SEEKFWD); 347 else ffwd_rew(ACTION_WPS_SEEKFWD);
338 last_right = 0; 348 last_right = 0;
339 break; 349 break;
@@ -343,7 +353,22 @@ long gui_wps_show(void)
343 if (global_settings.party_mode) 353 if (global_settings.party_mode)
344 break; 354 break;
345 if (current_tick -last_left < HZ) 355 if (current_tick -last_left < HZ)
346 audio_prev_dir(); 356 {
357 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
358 {
359 if (!wps_state.paused)
360#if (CONFIG_CODEC == SWCODEC)
361 audio_pre_ff_rewind();
362#else
363 audio_pause();
364#endif
365 audio_ff_rewind(0);
366 }
367 else
368 {
369 audio_prev_dir();
370 }
371 }
347 else ffwd_rew(ACTION_WPS_SEEKBACK); 372 else ffwd_rew(ACTION_WPS_SEEKBACK);
348 last_left = 0; 373 last_left = 0;
349 break; 374 break;
@@ -377,6 +402,13 @@ long gui_wps_show(void)
377 audio_prev(); 402 audio_prev();
378 } 403 }
379 else { 404 else {
405
406 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
407 {
408 curr_cuesheet_skip(-1, wps_state.id3->elapsed);
409 break;
410 }
411
380 if (!wps_state.paused) 412 if (!wps_state.paused)
381#if (CONFIG_CODEC == SWCODEC) 413#if (CONFIG_CODEC == SWCODEC)
382 audio_pre_ff_rewind(); 414 audio_pre_ff_rewind();
@@ -417,6 +449,18 @@ long gui_wps_show(void)
417 } 449 }
418 /* ...otherwise, do it normally */ 450 /* ...otherwise, do it normally */
419#endif 451#endif
452
453 /* take care of if we're playing a cuesheet */
454 if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
455 {
456 if (curr_cuesheet_skip(1, wps_state.id3->elapsed))
457 {
458 /* if the result was false, then we really want
459 to skip to the next track */
460 break;
461 }
462 }
463
420 audio_next(); 464 audio_next();
421 break; 465 break;
422 /* next / prev directories */ 466 /* next / prev directories */
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index f663e855ed..3085f839cf 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -10515,3 +10515,31 @@
10515 *: "" 10515 *: ""
10516 </voice> 10516 </voice>
10517</phrase> 10517</phrase>
10518<phrase>
10519 id: LANG_CUESHEET
10520 desc:
10521 user:
10522 <source>
10523 *: "Cuesheet"
10524 </source>
10525 <dest>
10526 *: "Cuesheet"
10527 </dest>
10528 <voice>
10529 *: "Cuesheet"
10530 </voice>
10531</phrase>
10532<phrase>
10533 id: LANG_CUESHEET_ENABLE
10534 desc: cuesheet support option
10535 user:
10536 <source>
10537 *: "Cuesheet Support"
10538 </source>
10539 <dest>
10540 *: "Cuesheet Support"
10541 </dest>
10542 <voice>
10543 *: "Cuesheet Support"
10544 </voice>
10545</phrase>
diff --git a/apps/main.c b/apps/main.c
index dac7ac755d..4c643c4130 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -103,6 +103,8 @@
103#include "m5636.h" 103#include "m5636.h"
104#endif 104#endif
105 105
106#include "cuesheet.h"
107
106/*#define AUTOROCK*/ /* define this to check for "autostart.rock" on boot */ 108/*#define AUTOROCK*/ /* define this to check for "autostart.rock" on boot */
107 109
108const char appsversion[]=APPSVERSION; 110const char appsversion[]=APPSVERSION;
@@ -274,7 +276,8 @@ static void init(void)
274 global_settings.superbass); 276 global_settings.superbass);
275 277
276 scrobbler_init(); 278 scrobbler_init();
277 279 cuesheet_init();
280
278 /* audio_init must to know the size of voice buffer so init voice first */ 281 /* audio_init must to know the size of voice buffer so init voice first */
279#if CONFIG_CODEC == SWCODEC 282#if CONFIG_CODEC == SWCODEC
280 talk_init(); 283 talk_init();
@@ -489,6 +492,7 @@ static void init(void)
489 playlist_init(); 492 playlist_init();
490 tree_init(); 493 tree_init();
491 scrobbler_init(); 494 scrobbler_init();
495 cuesheet_init();
492 496
493 /* No buffer allocation (see buffer.c) may take place after the call to 497 /* No buffer allocation (see buffer.c) may take place after the call to
494 audio_init() since the mpeg thread takes the rest of the buffer space */ 498 audio_init() since the mpeg thread takes the rest of the buffer space */
diff --git a/apps/menus/playback_menu.c b/apps/menus/playback_menu.c
index 7c0f75198d..4dbfb6c40d 100644
--- a/apps/menus/playback_menu.c
+++ b/apps/menus/playback_menu.c
@@ -33,6 +33,7 @@
33#include "dsp.h" 33#include "dsp.h"
34#include "scrobbler.h" 34#include "scrobbler.h"
35#include "audio.h" 35#include "audio.h"
36#include "cuesheet.h"
36 37
37#if CONFIG_CODEC == SWCODEC 38#if CONFIG_CODEC == SWCODEC
38int setcrossfadeonexit_callback(int action,const struct menu_item_ex *this_item) 39int setcrossfadeonexit_callback(int action,const struct menu_item_ex *this_item)
@@ -146,6 +147,21 @@ int audioscrobbler_callback(int action,const struct menu_item_ex *this_item)
146} 147}
147MENUITEM_SETTING(audioscrobbler, &global_settings.audioscrobbler, audioscrobbler_callback); 148MENUITEM_SETTING(audioscrobbler, &global_settings.audioscrobbler, audioscrobbler_callback);
148 149
150
151int cuesheet_callback(int action,const struct menu_item_ex *this_item)
152{
153 (void)this_item;
154 switch (action)
155 {
156 case ACTION_EXIT_MENUITEM: /* on exit */
157 if (!cuesheet_is_enabled() && global_settings.cuesheet)
158 gui_syncsplash(HZ*2, true, str(LANG_PLEASE_REBOOT));
159 break;
160 }
161 return action;
162}
163MENUITEM_SETTING(cuesheet, &global_settings.cuesheet, cuesheet_callback);
164
149#ifdef HAVE_HEADPHONE_DETECTION 165#ifdef HAVE_HEADPHONE_DETECTION
150MENUITEM_SETTING(unplug_mode, &global_settings.unplug_mode, NULL); 166MENUITEM_SETTING(unplug_mode, &global_settings.unplug_mode, NULL);
151MENUITEM_SETTING(unplug_rw, &global_settings.unplug_rw, NULL); 167MENUITEM_SETTING(unplug_rw, &global_settings.unplug_rw, NULL);
@@ -167,7 +183,7 @@ MAKE_MENU(playback_menu_item,ID2P(LANG_PLAYBACK),0,
167#ifdef HAVE_SPDIF_POWER 183#ifdef HAVE_SPDIF_POWER
168 &spdif_enable, 184 &spdif_enable,
169#endif 185#endif
170 &id3_v1_first, &next_folder, &audioscrobbler 186 &id3_v1_first, &next_folder, &audioscrobbler, &cuesheet
171#ifdef HAVE_HEADPHONE_DETECTION 187#ifdef HAVE_HEADPHONE_DETECTION
172 ,&unplug_menu 188 ,&unplug_menu
173#endif 189#endif
diff --git a/apps/metadata.c b/apps/metadata.c
index fbe7bfc233..ae2a8ecda8 100644
--- a/apps/metadata.c
+++ b/apps/metadata.c
@@ -30,6 +30,7 @@
30#include "replaygain.h" 30#include "replaygain.h"
31#include "debug.h" 31#include "debug.h"
32#include "system.h" 32#include "system.h"
33#include "cuesheet.h"
33 34
34enum tagtype { TAGTYPE_APE = 1, TAGTYPE_VORBIS }; 35enum tagtype { TAGTYPE_APE = 1, TAGTYPE_VORBIS };
35 36
@@ -2272,6 +2273,11 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
2272 2273
2273 /* We have successfully read the metadata from the file */ 2274 /* We have successfully read the metadata from the file */
2274 2275
2276 if (cuesheet_is_enabled() && look_for_cuesheet_file(trackname))
2277 {
2278 track->id3.cuesheet_type = 1;
2279 }
2280
2275 lseek(fd, 0, SEEK_SET); 2281 lseek(fd, 0, SEEK_SET);
2276 strncpy(track->id3.path, trackname, sizeof(track->id3.path)); 2282 strncpy(track->id3.path, trackname, sizeof(track->id3.path));
2277 track->taginfo_ready = true; 2283 track->taginfo_ready = true;
diff --git a/apps/playback.c b/apps/playback.c
index d256f5a4f0..b80c449c47 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -57,6 +57,7 @@
57#include "buffer.h" 57#include "buffer.h"
58#include "dsp.h" 58#include "dsp.h"
59#include "abrepeat.h" 59#include "abrepeat.h"
60#include "cuesheet.h"
60#ifdef HAVE_TAGCACHE 61#ifdef HAVE_TAGCACHE
61#include "tagcache.h" 62#include "tagcache.h"
62#endif 63#endif
@@ -2742,6 +2743,23 @@ static bool audio_load_track(int offset, bool start_play, bool rebuffer)
2742 2743
2743 } 2744 }
2744 2745
2746 if (cuesheet_is_enabled() && tracks[track_widx].id3.cuesheet_type == 1)
2747 {
2748 char cuepath[MAX_PATH];
2749 strncpy(cuepath, trackname, MAX_PATH);
2750 char *dot = strrchr(cuepath, '.');
2751 strcpy(dot, ".cue");
2752
2753 struct cuesheet *cue = start_play ? curr_cue : temp_cue;
2754
2755 if (parse_cuesheet(cuepath, cue))
2756 {
2757 strcpy((cue)->audio_filename, trackname);
2758 if (start_play)
2759 cue_spoof_id3(curr_cue, &tracks[track_widx].id3);
2760 }
2761 }
2762
2745 /* Load the codec. */ 2763 /* Load the codec. */
2746 tracks[track_widx].codecbuf = &filebuf[buf_widx]; 2764 tracks[track_widx].codecbuf = &filebuf[buf_widx];
2747 if (!audio_loadcodec(start_play)) 2765 if (!audio_loadcodec(start_play))
diff --git a/apps/settings.h b/apps/settings.h
index 30872ac04f..3605e7e777 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -683,6 +683,7 @@ struct user_settings
683#endif 683#endif
684 /* Encoder Settings End */ 684 /* Encoder Settings End */
685#endif /* CONFIG_CODEC == SWCODEC */ 685#endif /* CONFIG_CODEC == SWCODEC */
686 bool cuesheet;
686}; 687};
687 688
688/** global variables **/ 689/** global variables **/
diff --git a/apps/settings_list.c b/apps/settings_list.c
index c40cf09b2e..e847cfa23e 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -919,6 +919,7 @@ const struct settings_list settings[] = {
919 OFFON_SETTING(0,usb_charging,LANG_USB_CHARGING,false,"usb charging",NULL), 919 OFFON_SETTING(0,usb_charging,LANG_USB_CHARGING,false,"usb charging",NULL),
920#endif 920#endif
921#endif 921#endif
922 OFFON_SETTING(0,cuesheet,LANG_CUESHEET_ENABLE,false,"cuesheet support", NULL),
922}; 923};
923 924
924const int nb_settings = sizeof(settings)/sizeof(*settings); 925const int nb_settings = sizeof(settings)/sizeof(*settings);
diff --git a/apps/tree.c b/apps/tree.c
index 88492f4b65..806d1de616 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -133,6 +133,7 @@ const struct filetype filetypes[] = {
133 { "kbd", TREE_ATTR_KBD, Icon_Keyboard, VOICE_EXT_KBD }, 133 { "kbd", TREE_ATTR_KBD, Icon_Keyboard, VOICE_EXT_KBD },
134#endif 134#endif
135 { "bmark",TREE_ATTR_BMARK, Icon_Bookmark, VOICE_EXT_BMARK }, 135 { "bmark",TREE_ATTR_BMARK, Icon_Bookmark, VOICE_EXT_BMARK },
136 { "cue", TREE_ATTR_CUE, Icon_Bookmark, LANG_CUESHEET },
136#ifdef BOOTFILE_EXT 137#ifdef BOOTFILE_EXT
137 { BOOTFILE_EXT, TREE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ }, 138 { BOOTFILE_EXT, TREE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ },
138#endif /* #ifndef SIMULATOR */ 139#endif /* #ifndef SIMULATOR */
diff --git a/apps/tree.h b/apps/tree.h
index 003714252e..110fc09f0c 100644
--- a/apps/tree.h
+++ b/apps/tree.h
@@ -94,6 +94,7 @@ struct tree_context {
94#define TREE_ATTR_BMP 0x1100 /* backdrop bmp file */ 94#define TREE_ATTR_BMP 0x1100 /* backdrop bmp file */
95#define TREE_ATTR_KBD 0x1200 /* keyboard file */ 95#define TREE_ATTR_KBD 0x1200 /* keyboard file */
96#define TREE_ATTR_FMR 0x1300 /* preset file */ 96#define TREE_ATTR_FMR 0x1300 /* preset file */
97#define TREE_ATTR_CUE 0x1400 /* cuesheet file */
97#define TREE_ATTR_MASK 0xFF00 /* which bits tree.c uses for file types */ 98#define TREE_ATTR_MASK 0xFF00 /* which bits tree.c uses for file types */
98 99
99void tree_get_filetypes(const struct filetype**, int*); 100void tree_get_filetypes(const struct filetype**, int*);