1
0
Fork 0
forked from len0rd/rockbox

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
This commit is contained in:
Nicolas Pennequin 2007-02-14 14:40:24 +00:00
parent 0403c2a572
commit 9f4bd8712f
16 changed files with 640 additions and 6 deletions

View file

@ -32,6 +32,7 @@ settings_list.c
settings_menu.c settings_menu.c
sound_menu.c sound_menu.c
status.c status.c
cuesheet.c
#if !defined(SIMULATOR) || CONFIG_CODEC == SWCODEC #if !defined(SIMULATOR) || CONFIG_CODEC == SWCODEC
talk.c talk.c
#endif #endif

360
apps/cuesheet.c Normal file
View file

@ -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

85
apps/cuesheet.h Normal file
View file

@ -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

View file

@ -41,6 +41,7 @@
#include "dircache.h" #include "dircache.h"
#include "splash.h" #include "splash.h"
#include "yesno.h" #include "yesno.h"
#include "cuesheet.h"
#ifdef HAVE_LCD_BITMAP #ifdef HAVE_LCD_BITMAP
#include "keyboard.h" #include "keyboard.h"
#endif #endif
@ -550,6 +551,10 @@ int ft_enter(struct tree_context* c)
} }
break; break;
case TREE_ATTR_CUE:
display_cuesheet_content(buf);
break;
default: default:
{ {
char* plugin; char* plugin;

View file

@ -53,6 +53,7 @@
#endif #endif
#include "dsp.h" #include "dsp.h"
#include "action.h" #include "action.h"
#include "cuesheet.h"
#ifdef HAVE_LCD_CHARCELLS #ifdef HAVE_LCD_CHARCELLS
static bool draw_player_progress(struct gui_wps *gwps); 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_start, data->progress_end, sb_y,
data->progress_height); data->progress_height);
#endif #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; update_line = true;
} }
if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) { if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) {
@ -2561,6 +2570,35 @@ bool update(struct gui_wps *gwps)
{ {
gwps->display->stop_scroll(); gwps->display->stop_scroll();
gwps->state->id3 = audio_current_track(); 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()) if (gui_wps_display())
retcode = true; retcode = true;
else{ else{
@ -2572,6 +2610,28 @@ bool update(struct gui_wps *gwps)
sizeof(gwps->state->current_track_path)); 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) if (gwps->state->id3)
gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC); gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);

View file

@ -54,6 +54,7 @@
#include "abrepeat.h" #include "abrepeat.h"
#include "playback.h" #include "playback.h"
#include "splash.h" #include "splash.h"
#include "cuesheet.h"
#if LCD_DEPTH > 1 #if LCD_DEPTH > 1
#include "backdrop.h" #include "backdrop.h"
#endif #endif
@ -333,7 +334,16 @@ long gui_wps_show(void)
if (global_settings.party_mode) if (global_settings.party_mode)
break; break;
if (current_tick -last_right < HZ) if (current_tick -last_right < HZ)
{
if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
{
audio_next();
}
else
{
audio_next_dir(); audio_next_dir();
}
}
else ffwd_rew(ACTION_WPS_SEEKFWD); else ffwd_rew(ACTION_WPS_SEEKFWD);
last_right = 0; last_right = 0;
break; break;
@ -343,7 +353,22 @@ long gui_wps_show(void)
if (global_settings.party_mode) if (global_settings.party_mode)
break; break;
if (current_tick -last_left < HZ) if (current_tick -last_left < HZ)
{
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(); audio_prev_dir();
}
}
else ffwd_rew(ACTION_WPS_SEEKBACK); else ffwd_rew(ACTION_WPS_SEEKBACK);
last_left = 0; last_left = 0;
break; break;
@ -377,6 +402,13 @@ long gui_wps_show(void)
audio_prev(); audio_prev();
} }
else { else {
if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
{
curr_cuesheet_skip(-1, wps_state.id3->elapsed);
break;
}
if (!wps_state.paused) if (!wps_state.paused)
#if (CONFIG_CODEC == SWCODEC) #if (CONFIG_CODEC == SWCODEC)
audio_pre_ff_rewind(); audio_pre_ff_rewind();
@ -417,6 +449,18 @@ long gui_wps_show(void)
} }
/* ...otherwise, do it normally */ /* ...otherwise, do it normally */
#endif #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(); audio_next();
break; break;
/* next / prev directories */ /* next / prev directories */

View file

@ -10515,3 +10515,31 @@
*: "" *: ""
</voice> </voice>
</phrase> </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>

View file

@ -103,6 +103,8 @@
#include "m5636.h" #include "m5636.h"
#endif #endif
#include "cuesheet.h"
/*#define AUTOROCK*/ /* define this to check for "autostart.rock" on boot */ /*#define AUTOROCK*/ /* define this to check for "autostart.rock" on boot */
const char appsversion[]=APPSVERSION; const char appsversion[]=APPSVERSION;
@ -274,6 +276,7 @@ static void init(void)
global_settings.superbass); global_settings.superbass);
scrobbler_init(); scrobbler_init();
cuesheet_init();
/* audio_init must to know the size of voice buffer so init voice first */ /* audio_init must to know the size of voice buffer so init voice first */
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
@ -489,6 +492,7 @@ static void init(void)
playlist_init(); playlist_init();
tree_init(); tree_init();
scrobbler_init(); scrobbler_init();
cuesheet_init();
/* No buffer allocation (see buffer.c) may take place after the call to /* 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 */ audio_init() since the mpeg thread takes the rest of the buffer space */

View file

@ -33,6 +33,7 @@
#include "dsp.h" #include "dsp.h"
#include "scrobbler.h" #include "scrobbler.h"
#include "audio.h" #include "audio.h"
#include "cuesheet.h"
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
int setcrossfadeonexit_callback(int action,const struct menu_item_ex *this_item) 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); 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 #ifdef HAVE_HEADPHONE_DETECTION
MENUITEM_SETTING(unplug_mode, &global_settings.unplug_mode, NULL); MENUITEM_SETTING(unplug_mode, &global_settings.unplug_mode, NULL);
MENUITEM_SETTING(unplug_rw, &global_settings.unplug_rw, 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 #ifdef HAVE_SPDIF_POWER
&spdif_enable, &spdif_enable,
#endif #endif
&id3_v1_first, &next_folder, &audioscrobbler &id3_v1_first, &next_folder, &audioscrobbler, &cuesheet
#ifdef HAVE_HEADPHONE_DETECTION #ifdef HAVE_HEADPHONE_DETECTION
,&unplug_menu ,&unplug_menu
#endif #endif

View file

@ -30,6 +30,7 @@
#include "replaygain.h" #include "replaygain.h"
#include "debug.h" #include "debug.h"
#include "system.h" #include "system.h"
#include "cuesheet.h"
enum tagtype { TAGTYPE_APE = 1, TAGTYPE_VORBIS }; 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 */ /* 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); lseek(fd, 0, SEEK_SET);
strncpy(track->id3.path, trackname, sizeof(track->id3.path)); strncpy(track->id3.path, trackname, sizeof(track->id3.path));
track->taginfo_ready = true; track->taginfo_ready = true;

View file

@ -57,6 +57,7 @@
#include "buffer.h" #include "buffer.h"
#include "dsp.h" #include "dsp.h"
#include "abrepeat.h" #include "abrepeat.h"
#include "cuesheet.h"
#ifdef HAVE_TAGCACHE #ifdef HAVE_TAGCACHE
#include "tagcache.h" #include "tagcache.h"
#endif #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. */ /* Load the codec. */
tracks[track_widx].codecbuf = &filebuf[buf_widx]; tracks[track_widx].codecbuf = &filebuf[buf_widx];
if (!audio_loadcodec(start_play)) if (!audio_loadcodec(start_play))

View file

@ -683,6 +683,7 @@ struct user_settings
#endif #endif
/* Encoder Settings End */ /* Encoder Settings End */
#endif /* CONFIG_CODEC == SWCODEC */ #endif /* CONFIG_CODEC == SWCODEC */
bool cuesheet;
}; };
/** global variables **/ /** global variables **/

View file

@ -919,6 +919,7 @@ const struct settings_list settings[] = {
OFFON_SETTING(0,usb_charging,LANG_USB_CHARGING,false,"usb charging",NULL), OFFON_SETTING(0,usb_charging,LANG_USB_CHARGING,false,"usb charging",NULL),
#endif #endif
#endif #endif
OFFON_SETTING(0,cuesheet,LANG_CUESHEET_ENABLE,false,"cuesheet support", NULL),
}; };
const int nb_settings = sizeof(settings)/sizeof(*settings); const int nb_settings = sizeof(settings)/sizeof(*settings);

View file

@ -133,6 +133,7 @@ const struct filetype filetypes[] = {
{ "kbd", TREE_ATTR_KBD, Icon_Keyboard, VOICE_EXT_KBD }, { "kbd", TREE_ATTR_KBD, Icon_Keyboard, VOICE_EXT_KBD },
#endif #endif
{ "bmark",TREE_ATTR_BMARK, Icon_Bookmark, VOICE_EXT_BMARK }, { "bmark",TREE_ATTR_BMARK, Icon_Bookmark, VOICE_EXT_BMARK },
{ "cue", TREE_ATTR_CUE, Icon_Bookmark, LANG_CUESHEET },
#ifdef BOOTFILE_EXT #ifdef BOOTFILE_EXT
{ BOOTFILE_EXT, TREE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ }, { BOOTFILE_EXT, TREE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ },
#endif /* #ifndef SIMULATOR */ #endif /* #ifndef SIMULATOR */

View file

@ -94,6 +94,7 @@ struct tree_context {
#define TREE_ATTR_BMP 0x1100 /* backdrop bmp file */ #define TREE_ATTR_BMP 0x1100 /* backdrop bmp file */
#define TREE_ATTR_KBD 0x1200 /* keyboard file */ #define TREE_ATTR_KBD 0x1200 /* keyboard file */
#define TREE_ATTR_FMR 0x1300 /* preset 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 */ #define TREE_ATTR_MASK 0xFF00 /* which bits tree.c uses for file types */
void tree_get_filetypes(const struct filetype**, int*); void tree_get_filetypes(const struct filetype**, int*);

View file

@ -213,6 +213,9 @@ struct mp3entry {
long track_peak; /* 7.24 signed fixed point. 0 for no peak. */ long track_peak; /* 7.24 signed fixed point. 0 for no peak. */
long album_peak; long album_peak;
#endif #endif
/* Cuesheet support */
int cuesheet_type; /* 0: none, 1: external, 2: embedded */
}; };
enum { enum {