summaryrefslogtreecommitdiffstats
path: root/apps
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
parent0403c2a572154667f3f2bd671d7d5a7cc08c64af (diff)
downloadrockbox-9f4bd8712fc122f61ec162c544d613a95c3ca66e.tar.gz
rockbox-9f4bd8712fc122f61ec162c544d613a95c3ca66e.tar.bz2
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')
-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
settings_menu.c
sound_menu.c
status.c
+cuesheet.c
#if !defined(SIMULATOR) || CONFIG_CODEC == SWCODEC
talk.c
#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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $$
+ *
+ * Copyright (C) 2007 Nicolas Pennequin, Jonathan Gordon
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <atoi.h>
+#include <string.h>
+#include "system.h"
+#include "audio.h"
+#include "kernel.h"
+#include "logf.h"
+#include "sprintf.h"
+#include "misc.h"
+#include "screens.h"
+#include "splash.h"
+#include "list.h"
+#include "action.h"
+#include "lang.h"
+#include "debug.h"
+#include "settings.h"
+#include "buffer.h"
+#include "plugin.h"
+#include "playback.h"
+#include "cuesheet.h"
+
+
+void cuesheet_init(void)
+{
+ if (global_settings.cuesheet) {
+ curr_cue = (struct cuesheet *)buffer_alloc(MAX_TRACKS * sizeof(struct cuesheet));
+ temp_cue = (struct cuesheet *)buffer_alloc(MAX_TRACKS * sizeof(struct cuesheet));
+ } else {
+ curr_cue = NULL;
+ temp_cue = NULL;
+ }
+}
+
+bool cuesheet_is_enabled(void)
+{
+ return (curr_cue != NULL);
+}
+
+bool look_for_cuesheet_file(const char *trackpath)
+{
+ char cuepath[MAX_PATH];
+ strncpy(cuepath, trackpath, MAX_PATH);
+ char *dot = strrchr(cuepath, '.');
+ strcpy(dot, ".cue");
+
+ int fd = open(cuepath,O_RDONLY);
+ if (fd < 0)
+ {
+ return false;
+ }
+ else
+ {
+ close(fd);
+ return true;
+ }
+}
+
+char *skip_whitespace(char* buf)
+{
+ char *r = buf;
+ while (*r && (*r < 33))
+ r++;
+ return r;
+}
+
+/* parse cuesheet "file" and store the information in "cue" */
+bool parse_cuesheet(char *file, struct cuesheet *cue)
+{
+ char line[MAX_PATH];
+ char *s, *start, *end;
+ int fd = open(file,O_RDONLY);
+ if (fd < 0)
+ {
+ /* couln't open the file */
+ return false;
+ }
+
+ memset(cue, 0, sizeof(struct cuesheet));
+
+ strcpy(cue->path, file);
+
+ cue->curr_track_idx = 0;
+ cue->curr_track = cue->tracks;
+
+ cue->track_count = 0;
+ while (read_line(fd,line,MAX_PATH))
+ {
+ s = skip_whitespace(line);
+ if (!strncmp(s, "TITLE", 5))
+ {
+ start = strchr(s,'"');
+ if (!start)
+ break;
+ end = strchr(++start,'"');
+ if (!end)
+ break;
+ *end = '\0';
+ if (cue->track_count <= 0)
+ strncpy(cue->title,start,MAX_NAME);
+ else strncpy(cue->tracks[cue->track_count-1].title,
+ start,MAX_NAME);
+ }
+ else if (!strncmp(s, "PERFORMER", 9))
+ {
+ start = strchr(s,'"');
+ if (!start)
+ break;
+ end = strchr(++start,'"');
+ if (!end)
+ break;
+ *end = '\0';
+ if (cue->track_count <= 0)
+ strncpy(cue->performer,start,MAX_NAME);
+ else strncpy(cue->tracks[cue->track_count-1].performer,
+ start,MAX_NAME);
+ }
+ else if (!strncmp(s, "TRACK", 5))
+ {
+ if (cue->track_count >= MAX_TRACKS)
+ break; /* out of memeory! stop parsing */
+ cue->track_count++;
+ }
+ else if (!strncmp(s, "INDEX", 5))
+ {
+ s = strchr(s,' ');
+ s = skip_whitespace(s);
+ s = strchr(s,' ');
+ s = skip_whitespace(s);
+ cue->tracks[cue->track_count-1].offset = 60*1000 * atoi(s);
+ s = strchr(s,':') + 1;
+ cue->tracks[cue->track_count-1].offset += 1000 * atoi(s);
+ s = strchr(s,':') + 1;
+ cue->tracks[cue->track_count-1].offset += 13 * atoi(s);
+ }
+ }
+ close(fd);
+
+ /* If some songs don't have performer info, we copy the cuesheet performer */
+ int i;
+ for (i = 0; i < cue->track_count; i++)
+ {
+ if (*(cue->tracks[i].performer) == '\0')
+ {
+ strncpy(cue->tracks[i].performer, cue->performer, MAX_NAME);
+ }
+ }
+
+ return true;
+}
+
+/* takes care of seeking to a track in a playlist
+ * returns false if audio isn't playing */
+bool seek(unsigned long pos)
+{
+ if (!(audio_status() & AUDIO_STATUS_PLAY))
+ {
+ return false;
+ }
+ else
+ {
+#if (CONFIG_CODEC == SWCODEC)
+ audio_pre_ff_rewind();
+#else
+ audio_pause();
+#endif
+ audio_ff_rewind(pos);
+ return true;
+ }
+}
+
+/* returns the index of the track currently being played
+ and updates the information about the current track. */
+int cue_find_current_track(struct cuesheet *cue, unsigned long curpos)
+{
+ int i=0;
+ while (i < cue->track_count-1 && cue->tracks[i+1].offset < curpos)
+ {
+ i++;
+ }
+ cue->curr_track_idx = i;
+ cue->curr_track = cue->tracks + i;
+ return i;
+}
+
+/* callback that gives list item titles for the cuesheet browser */
+char *list_get_name_cb(int selected_item,
+ void *data,
+ char *buffer)
+{
+ struct cuesheet *cue = (struct cuesheet *)data;
+
+ if (selected_item & 1)
+ {
+ snprintf(buffer, MAX_PATH,
+ (selected_item+1)/2 > 9 ? " %s" : " %s",
+ cue->tracks[selected_item/2].title);
+ }
+ else
+ {
+ snprintf(buffer, MAX_PATH, "%d %s", selected_item/2+1,
+ cue->tracks[selected_item/2].performer);
+ }
+ return buffer;
+}
+
+void browse_cuesheet(struct cuesheet *cue)
+{
+ struct gui_synclist lists;
+ int action;
+ bool done = false;
+ int sel;
+ char title[MAX_PATH];
+ char cuepath[MAX_PATH];
+ char *dot;
+ struct mp3entry *id3 = audio_current_track();
+
+ snprintf(title, MAX_PATH, "%s: %s", cue->performer, cue->title);
+ gui_synclist_init(&lists, list_get_name_cb, cue, false, 2);
+ gui_synclist_set_nb_items(&lists, 2*cue->track_count);
+ gui_synclist_set_title(&lists, title, 0);
+
+ if (strcmp(id3->path, "No file!"))
+ {
+ strncpy(cuepath, id3->path, MAX_PATH);
+ dot = strrchr(cuepath, '.');
+ strcpy(dot, ".cue");
+ }
+
+ if (id3->cuesheet_type && !strcmp(cue->path, cuepath))
+ {
+ gui_synclist_select_item(&lists,
+ 2*cue_find_current_track(cue, id3->elapsed));
+ }
+
+ while (!done)
+ {
+ gui_synclist_draw(&lists);
+ action = get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
+ if (gui_synclist_do_button(&lists,action,LIST_WRAP_UNLESS_HELD))
+ continue;
+ switch (action)
+ {
+ case ACTION_STD_OK:
+ id3 = audio_current_track();
+ if (strcmp(id3->path, "No file!"))
+ {
+ strncpy(cuepath, id3->path, MAX_PATH);
+ dot = strrchr(cuepath, '.');
+ strcpy(dot, ".cue");
+ if (id3->cuesheet_type && !strcmp(cue->path, cuepath))
+ {
+ sel = gui_synclist_get_sel_pos(&lists);
+ seek(cue->tracks[sel/2].offset);
+ }
+ }
+ break;
+ case ACTION_STD_CANCEL:
+ done = true;
+ }
+ }
+}
+
+bool display_cuesheet_content(char* filename)
+{
+ int bufsize = 0;
+ struct cuesheet *cue = (struct cuesheet *)plugin_get_buffer(&bufsize);
+ if (!cue)
+ return false;
+
+ if (!parse_cuesheet(filename, cue))
+ return false;
+
+ browse_cuesheet(cue);
+ return true;
+}
+
+/* skips backwards or forward in the current cuesheet
+ * the return value indicates whether we're still in a cusheet after skipping
+ * it also returns false if we weren't in a cuesheet.
+ * direction should be 1 or -1.
+ */
+bool curr_cuesheet_skip(int direction, unsigned long curr_pos)
+{
+ int track = cue_find_current_track(curr_cue, curr_pos);
+
+ if (direction >= 0 && track == curr_cue->track_count - 1)
+ {
+ /* we want to get out of the cuesheet */
+ return false;
+ }
+ else
+ {
+ if (!(direction <= 0 && track == 0))
+ track += direction;
+
+ seek(curr_cue->tracks[track].offset);
+ return true;
+ }
+
+}
+
+void cue_spoof_id3(struct cuesheet *cue, struct mp3entry *id3)
+{
+ if (!cue)
+ return;
+
+ int i = cue->curr_track_idx;
+
+ id3->title = cue->tracks[i].title;
+ id3->artist = cue->tracks[i].performer;
+ id3->tracknum = i+1;
+ id3->album = cue->title;
+ id3->composer = cue->performer;
+ if (id3->track_string)
+ snprintf(id3->track_string, 10, "%d/%d", i+1, cue->track_count);
+}
+
+#ifdef HAVE_LCD_BITMAP
+static inline void draw_veritcal_line_mark(struct screen * screen,
+ int x, int y, int h)
+{
+ screen->set_drawmode(DRMODE_COMPLEMENT);
+ screen->vline(x, y, y+h-1);
+}
+
+/* draw the cuesheet markers for a track of length "tracklen",
+ between (x1,y) and (x2,y) */
+void cue_draw_markers(struct screen *screen, unsigned long tracklen,
+ int x1, int x2, int y, int h)
+{
+ int i,xi;
+ int w = x2 - x1;
+ for (i=1; i < curr_cue->track_count; i++)
+ {
+ xi = x1 + (w * curr_cue->tracks[i].offset)/tracklen;
+ draw_veritcal_line_mark(screen, xi, y, h);
+ }
+}
+#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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $$
+ *
+ * Copyright (C) 2007 Nicolas Pennequin, Jonathan Gordon
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _CUESHEET_H_
+#define _CUESHEET_H_
+
+#include <stdbool.h>
+#include "screens.h"
+
+#define MAX_NAME 64 /* Max length of information strings */
+#define MAX_TRACKS 99 /* Max number of tracks in a cuesheet */
+
+struct cue_track_info {
+ char title[MAX_NAME];
+ char performer[MAX_NAME];
+ unsigned long offset; /* ms from start of track */
+};
+
+struct cuesheet {
+ char path[MAX_PATH];
+ char audio_filename[MAX_PATH];
+
+ char title[MAX_NAME];
+ char performer[MAX_NAME];
+
+ int track_count;
+ struct cue_track_info tracks[MAX_TRACKS];
+
+ int curr_track_idx;
+ struct cue_track_info *curr_track;
+};
+
+struct cuesheet *curr_cue;
+struct cuesheet *temp_cue;
+
+/* returns true if cuesheet support is initialised */
+bool cuesheet_is_enabled(void);
+
+/* allocates the cuesheet buffer */
+void cuesheet_init(void);
+
+/* looks if there is a cuesheet file that has a name matching "trackpath" */
+bool look_for_cuesheet_file(const char *trackpath);
+
+/* parse cuesheet "file" and store the information in "cue" */
+bool parse_cuesheet(char *file, struct cuesheet *cue);
+
+/* reads a cuesheet to find the audio track associated to it */
+bool get_trackname_from_cuesheet(char *filename, char *buf);
+
+/* displays a cuesheet to the screen (it is stored in the plugin buffer) */
+bool display_cuesheet_content(char* filename);
+
+/* finds the index of the current track played within a cuesheet */
+int cue_find_current_track(struct cuesheet *cue, unsigned long curpos);
+
+/* update the id3 info to that of the currently playing track in the cuesheet */
+void cue_spoof_id3(struct cuesheet *cue, struct mp3entry *id3);
+
+/* skip to next track in the cuesheet towards "direction" (which is 1 or -1) */
+bool curr_cuesheet_skip(int direction, unsigned long curr_pos);
+
+#ifdef HAVE_LCD_BITMAP
+/* draw track markers on the progressbar */
+void cue_draw_markers(struct screen *screen, unsigned long tracklen,
+ int x1, int x2, int y, int h);
+#endif
+
+#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 @@
#include "dircache.h"
#include "splash.h"
#include "yesno.h"
+#include "cuesheet.h"
#ifdef HAVE_LCD_BITMAP
#include "keyboard.h"
#endif
@@ -550,6 +551,10 @@ int ft_enter(struct tree_context* c)
}
break;
+ case TREE_ATTR_CUE:
+ display_cuesheet_content(buf);
+ break;
+
default:
{
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 @@
#endif
#include "dsp.h"
#include "action.h"
+#include "cuesheet.h"
#ifdef HAVE_LCD_CHARCELLS
static bool draw_player_progress(struct gui_wps *gwps);
@@ -1850,6 +1851,14 @@ bool gui_wps_refresh(struct gui_wps *gwps, int ffwd_offset,
data->progress_start, data->progress_end, sb_y,
data->progress_height);
#endif
+
+ if (cuesheet_is_enabled() && state->id3->cuesheet_type)
+ {
+ cue_draw_markers(display, state->id3->length,
+ data->progress_start, data->progress_end,
+ sb_y+1, data->progress_height-2);
+ }
+
update_line = true;
}
if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) {
@@ -2561,6 +2570,35 @@ bool update(struct gui_wps *gwps)
{
gwps->display->stop_scroll();
gwps->state->id3 = audio_current_track();
+
+ if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type
+ && strcmp(gwps->state->id3->path, curr_cue->audio_filename))
+ {
+ /* the current cuesheet isn't the right one any more */
+
+ if (!strcmp(gwps->state->id3->path, temp_cue->audio_filename)) {
+ /* We have the new cuesheet in memory (temp_cue),
+ let's make it the current one ! */
+ memcpy(curr_cue, temp_cue, sizeof(struct cuesheet));
+ }
+ else {
+ /* We need to parse the new cuesheet */
+
+ char cuepath[MAX_PATH];
+ strncpy(cuepath, gwps->state->id3->path, MAX_PATH);
+ char *dot = strrchr(cuepath, '.');
+ strcpy(dot, ".cue");
+
+ if (parse_cuesheet(cuepath, curr_cue))
+ {
+ gwps->state->id3->cuesheet_type = 1;
+ strcpy(curr_cue->audio_filename, gwps->state->id3->path);
+ }
+ }
+
+ cue_spoof_id3(curr_cue, gwps->state->id3);
+ }
+
if (gui_wps_display())
retcode = true;
else{
@@ -2572,12 +2610,34 @@ bool update(struct gui_wps *gwps)
sizeof(gwps->state->current_track_path));
}
+ if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type
+ && (gwps->state->id3->elapsed < curr_cue->curr_track->offset
+ || (curr_cue->curr_track_idx < curr_cue->track_count - 1
+ && gwps->state->id3->elapsed >= (curr_cue->curr_track+1)->offset)))
+ {
+ /* We've changed tracks within the cuesheet :
+ we need to update the ID3 info and refresh the WPS */
+
+ cue_find_current_track(curr_cue, gwps->state->id3->elapsed);
+ cue_spoof_id3(curr_cue, gwps->state->id3);
+
+ gwps->display->stop_scroll();
+ if (gui_wps_display())
+ retcode = true;
+ else{
+ gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL);
+ }
+ gui_wps_statusbar_draw(gwps, false);
+
+ return retcode;
+ }
+
if (gwps->state->id3)
gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);
gui_wps_statusbar_draw(gwps, false);
-
- return retcode;
+
+ return retcode;
}
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 @@
#include "abrepeat.h"
#include "playback.h"
#include "splash.h"
+#include "cuesheet.h"
#if LCD_DEPTH > 1
#include "backdrop.h"
#endif
@@ -333,7 +334,16 @@ long gui_wps_show(void)
if (global_settings.party_mode)
break;
if (current_tick -last_right < HZ)
- audio_next_dir();
+ {
+ if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
+ {
+ audio_next();
+ }
+ else
+ {
+ audio_next_dir();
+ }
+ }
else ffwd_rew(ACTION_WPS_SEEKFWD);
last_right = 0;
break;
@@ -343,7 +353,22 @@ long gui_wps_show(void)
if (global_settings.party_mode)
break;
if (current_tick -last_left < HZ)
- audio_prev_dir();
+ {
+ if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
+ {
+ if (!wps_state.paused)
+#if (CONFIG_CODEC == SWCODEC)
+ audio_pre_ff_rewind();
+#else
+ audio_pause();
+#endif
+ audio_ff_rewind(0);
+ }
+ else
+ {
+ audio_prev_dir();
+ }
+ }
else ffwd_rew(ACTION_WPS_SEEKBACK);
last_left = 0;
break;
@@ -377,6 +402,13 @@ long gui_wps_show(void)
audio_prev();
}
else {
+
+ if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
+ {
+ curr_cuesheet_skip(-1, wps_state.id3->elapsed);
+ break;
+ }
+
if (!wps_state.paused)
#if (CONFIG_CODEC == SWCODEC)
audio_pre_ff_rewind();
@@ -417,6 +449,18 @@ long gui_wps_show(void)
}
/* ...otherwise, do it normally */
#endif
+
+ /* take care of if we're playing a cuesheet */
+ if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
+ {
+ if (curr_cuesheet_skip(1, wps_state.id3->elapsed))
+ {
+ /* if the result was false, then we really want
+ to skip to the next track */
+ break;
+ }
+ }
+
audio_next();
break;
/* 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 @@
*: ""
</voice>
</phrase>
+<phrase>
+ id: LANG_CUESHEET
+ desc:
+ user:
+ <source>
+ *: "Cuesheet"
+ </source>
+ <dest>
+ *: "Cuesheet"
+ </dest>
+ <voice>
+ *: "Cuesheet"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CUESHEET_ENABLE
+ desc: cuesheet support option
+ user:
+ <source>
+ *: "Cuesheet Support"
+ </source>
+ <dest>
+ *: "Cuesheet Support"
+ </dest>
+ <voice>
+ *: "Cuesheet Support"
+ </voice>
+</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 @@
#include "m5636.h"
#endif
+#include "cuesheet.h"
+
/*#define AUTOROCK*/ /* define this to check for "autostart.rock" on boot */
const char appsversion[]=APPSVERSION;
@@ -274,7 +276,8 @@ static void init(void)
global_settings.superbass);
scrobbler_init();
-
+ cuesheet_init();
+
/* audio_init must to know the size of voice buffer so init voice first */
#if CONFIG_CODEC == SWCODEC
talk_init();
@@ -489,6 +492,7 @@ static void init(void)
playlist_init();
tree_init();
scrobbler_init();
+ cuesheet_init();
/* No buffer allocation (see buffer.c) may take place after the call to
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 @@
#include "dsp.h"
#include "scrobbler.h"
#include "audio.h"
+#include "cuesheet.h"
#if CONFIG_CODEC == SWCODEC
int 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)
}
MENUITEM_SETTING(audioscrobbler, &global_settings.audioscrobbler, audioscrobbler_callback);
+
+int cuesheet_callback(int action,const struct menu_item_ex *this_item)
+{
+ (void)this_item;
+ switch (action)
+ {
+ case ACTION_EXIT_MENUITEM: /* on exit */
+ if (!cuesheet_is_enabled() && global_settings.cuesheet)
+ gui_syncsplash(HZ*2, true, str(LANG_PLEASE_REBOOT));
+ break;
+ }
+ return action;
+}
+MENUITEM_SETTING(cuesheet, &global_settings.cuesheet, cuesheet_callback);
+
#ifdef HAVE_HEADPHONE_DETECTION
MENUITEM_SETTING(unplug_mode, &global_settings.unplug_mode, NULL);
MENUITEM_SETTING(unplug_rw, &global_settings.unplug_rw, NULL);
@@ -167,7 +183,7 @@ MAKE_MENU(playback_menu_item,ID2P(LANG_PLAYBACK),0,
#ifdef HAVE_SPDIF_POWER
&spdif_enable,
#endif
- &id3_v1_first, &next_folder, &audioscrobbler
+ &id3_v1_first, &next_folder, &audioscrobbler, &cuesheet
#ifdef HAVE_HEADPHONE_DETECTION
,&unplug_menu
#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 @@
#include "replaygain.h"
#include "debug.h"
#include "system.h"
+#include "cuesheet.h"
enum tagtype { TAGTYPE_APE = 1, TAGTYPE_VORBIS };
@@ -2272,6 +2273,11 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
/* We have successfully read the metadata from the file */
+ if (cuesheet_is_enabled() && look_for_cuesheet_file(trackname))
+ {
+ track->id3.cuesheet_type = 1;
+ }
+
lseek(fd, 0, SEEK_SET);
strncpy(track->id3.path, trackname, sizeof(track->id3.path));
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 @@
#include "buffer.h"
#include "dsp.h"
#include "abrepeat.h"
+#include "cuesheet.h"
#ifdef HAVE_TAGCACHE
#include "tagcache.h"
#endif
@@ -2742,6 +2743,23 @@ static bool audio_load_track(int offset, bool start_play, bool rebuffer)
}
+ if (cuesheet_is_enabled() && tracks[track_widx].id3.cuesheet_type == 1)
+ {
+ char cuepath[MAX_PATH];
+ strncpy(cuepath, trackname, MAX_PATH);
+ char *dot = strrchr(cuepath, '.');
+ strcpy(dot, ".cue");
+
+ struct cuesheet *cue = start_play ? curr_cue : temp_cue;
+
+ if (parse_cuesheet(cuepath, cue))
+ {
+ strcpy((cue)->audio_filename, trackname);
+ if (start_play)
+ cue_spoof_id3(curr_cue, &tracks[track_widx].id3);
+ }
+ }
+
/* Load the codec. */
tracks[track_widx].codecbuf = &filebuf[buf_widx];
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
#endif
/* Encoder Settings End */
#endif /* CONFIG_CODEC == SWCODEC */
+ bool cuesheet;
};
/** 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[] = {
OFFON_SETTING(0,usb_charging,LANG_USB_CHARGING,false,"usb charging",NULL),
#endif
#endif
+ OFFON_SETTING(0,cuesheet,LANG_CUESHEET_ENABLE,false,"cuesheet support", NULL),
};
const 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[] = {
{ "kbd", TREE_ATTR_KBD, Icon_Keyboard, VOICE_EXT_KBD },
#endif
{ "bmark",TREE_ATTR_BMARK, Icon_Bookmark, VOICE_EXT_BMARK },
+ { "cue", TREE_ATTR_CUE, Icon_Bookmark, LANG_CUESHEET },
#ifdef BOOTFILE_EXT
{ BOOTFILE_EXT, TREE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ },
#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 {
#define TREE_ATTR_BMP 0x1100 /* backdrop bmp file */
#define TREE_ATTR_KBD 0x1200 /* keyboard file */
#define TREE_ATTR_FMR 0x1300 /* preset file */
+#define TREE_ATTR_CUE 0x1400 /* cuesheet file */
#define TREE_ATTR_MASK 0xFF00 /* which bits tree.c uses for file types */
void tree_get_filetypes(const struct filetype**, int*);