Discussion:
[PATCH] Draft CUE sheets support - please test!
Gregory Petrosyan
2011-10-01 09:55:05 UTC
Permalink
When adding a file, check for the CUE sheet with the same name. If it exists,
add entries from it instead of the original file.

TODO:
- sometimes screen garbled due to libcue
- cache refresh etc.: will it work?

Signed-off-by: Gregory Petrosyan <***@gmail.com>
---
Makefile | 12 ++-
cmus.c | 2 +-
configure | 11 ++-
cue.c | 406 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cue_utils.c | 76 +++++++++++
cue_utils.h | 29 ++++
input.c | 3 +
job.c | 34 +++++-
track_info.c | 5 +
utils.h | 7 +-
10 files changed, 578 insertions(+), 7 deletions(-)
create mode 100644 cue.c
create mode 100644 cue_utils.c
create mode 100644 cue_utils.h

diff --git a/Makefile b/Makefile
index 7e00434..9fa7ef9 100644
--- a/Makefile
+++ b/Makefile
@@ -18,12 +18,13 @@ include scripts/lib.mk

CFLAGS += -D_FILE_OFFSET_BITS=64

-CMUS_LIBS = $(PTHREAD_LIBS) $(NCURSES_LIBS) $(ICONV_LIBS) $(DL_LIBS) $(DISCID_LIBS) -lm $(COMPAT_LIBS)
+CMUS_LIBS = $(PTHREAD_LIBS) $(NCURSES_LIBS) $(ICONV_LIBS) $(DL_LIBS) $(DISCID_LIBS) $(CUE_LIBS) -lm $(COMPAT_LIBS)

input.o main.o ui_curses.o pulse.lo: .version
input.o main.o ui_curses.o pulse.lo: CFLAGS += -DVERSION=\"$(VERSION)\"
main.o server.o: CFLAGS += -DDEFAULT_PORT=3000
discid.o: CFLAGS += $(DISCID_CFLAGS)
+cue_utils.o: CFLAGS += $(CUE_CFLAGS)

.version: Makefile
@test "`cat $@ 2> /dev/null`" = "$(VERSION)" && exit 0; \
@@ -32,7 +33,7 @@ discid.o: CFLAGS += $(DISCID_CFLAGS)
# programs {{{
cmus-y := \
ape.o browser.o buffer.o cache.o cmdline.o cmus.o command_mode.o comment.o \
- channelmap.o convert.lo debug.o discid.o editable.o expr.o filters.o \
+ channelmap.o convert.lo cue_utils.o debug.o discid.o editable.o expr.o filters.o \
format_print.o gbuf.o glob.o help.o history.o http.o id3.o input.o job.o \
keys.o keyval.o lib.o load_dir.o locking.o mergesort.o misc.o options.o \
output.o pcm.o pl.o play_queue.o player.o \
@@ -76,6 +77,7 @@ wav-objs := wav.lo
mp4-objs := mp4.lo
aac-objs := aac.lo
ffmpeg-objs := ffmpeg.lo
+cue-objs := cue.lo

ip-$(CONFIG_CDIO) += cdio.so
ip-$(CONFIG_FLAC) += flac.so
@@ -88,7 +90,7 @@ ip-$(CONFIG_WAVPACK) += wavpack.so
ip-$(CONFIG_WAV) += wav.so
ip-$(CONFIG_MP4) += mp4.so
ip-$(CONFIG_AAC) += aac.so
-ip-$(CONFIG_FFMPEG) += ffmpeg.so
+ip-$(CONFIG_CUE) += cue.so

$(cdio-objs): CFLAGS += $(CDIO_CFLAGS) $(CDDB_CFLAGS)
$(flac-objs): CFLAGS += $(FLAC_CFLAGS)
@@ -101,6 +103,7 @@ $(wavpack-objs): CFLAGS += $(WAVPACK_CFLAGS)
$(mp4-objs): CFLAGS += $(MP4_CFLAGS)
$(aac-objs): CFLAGS += $(AAC_CFLAGS)
$(ffmpeg-objs): CFLAGS += $(FFMPEG_CFLAGS)
+$(cue-objs): CFLAGS += $(CUE_CFLAGS)

cdio.so: $(cdio-objs) $(libcmus-y)
$(call cmd,ld_dl,$(CDIO_LIBS) $(CDDB_LIBS))
@@ -138,6 +141,9 @@ aac.so: $(aac-objs) $(libcmus-y)
ffmpeg.so: $(ffmpeg-objs) $(libcmus-y)
$(call cmd,ld_dl,$(FFMPEG_LIBS))

+cue.so: $(cue-objs) $(libcmus-y)
+ $(call cmd,ld_dl,$(CUE_LIBS))
+
# }}}

# output plugins {{{
diff --git a/cmus.c b/cmus.c
index d156f06..e9cf9b0 100644
--- a/cmus.c
+++ b/cmus.c
@@ -125,7 +125,7 @@ enum file_type cmus_detect_ft(const char *name, char **ret)
char *absolute;
struct stat st;

- if (is_http_url(name)) {
+ if (is_http_url(name) || is_cue_url(name)) {
*ret = xstrdup(name);
return FILE_TYPE_URL;
}
diff --git a/configure b/configure
index 327654d..cac4d32 100755
--- a/configure
+++ b/configure
@@ -181,6 +181,13 @@ check_mad()
return $?
}

+check_cue()
+{
+ pkg_config CUE "libcue >= 1.3"
+ return $?
+}
+
+
mikmod_code="
#include <mikmod.h>
int main() {
@@ -407,6 +414,7 @@ Optional Features: y/n
CONFIG_MP4 MPEG-4 AAC (.mp4, .m4a, .m4b) [auto]
CONFIG_AAC AAC (.aac, audio/aac, audio/aacp) [auto]
CONFIG_FFMPEG FFMPEG (.shn, .wma) [auto]
+ CONFIG_CUE CUE sheets (.cue) [auto]
CONFIG_ROAR native RoarAudio output [auto]
CONFIG_PULSE native PulseAudio output [auto]
CONFIG_ALSA ALSA [auto]
@@ -465,6 +473,7 @@ check check_wavpack CONFIG_WAVPACK
check check_mp4 CONFIG_MP4
check check_aac CONFIG_AAC
check check_ffmpeg CONFIG_FFMPEG
+check check_cue CONFIG_CUE
# nothing to check, just validate the variable values
check true CONFIG_TREMOR
check true CONFIG_WAV
@@ -506,7 +515,7 @@ config_header config/iconv.h HAVE_ICONV
config_header config/xmalloc.h HAVE_STRDUP HAVE_STRNDUP

makefile_vars bindir datadir libdir mandir exampledir
-makefile_vars CONFIG_CDIO CONFIG_FLAC CONFIG_MAD CONFIG_MIKMOD CONFIG_MODPLUG CONFIG_MPC CONFIG_VORBIS CONFIG_WAVPACK CONFIG_WAV CONFIG_MP4 CONFIG_AAC CONFIG_FFMPEG
+makefile_vars CONFIG_CDIO CONFIG_FLAC CONFIG_MAD CONFIG_MIKMOD CONFIG_MODPLUG CONFIG_MPC CONFIG_VORBIS CONFIG_WAVPACK CONFIG_WAV CONFIG_MP4 CONFIG_AAC CONFIG_FFMPEG CONFIG_CUE
makefile_vars CONFIG_ROAR CONFIG_PULSE CONFIG_ALSA CONFIG_AO CONFIG_ARTS CONFIG_OSS CONFIG_SUN CONFIG_WAVEOUT

generate_config_mk
diff --git a/cue.c b/cue.c
new file mode 100644
index 0000000..a5fa73e
--- /dev/null
+++ b/cue.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2008-2011 Various Authors
+ * Copyright (C) 2011 Gregory Petrosyan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ip.h"
+#include "debug.h"
+#include "input.h"
+#include "utils.h"
+#include "comment.h"
+#include "xmalloc.h"
+
+#include <libcue/libcue.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+
+
+struct cue_private {
+ struct input_plugin *child;
+
+ char *cue_filename;
+ int track_n;
+
+ double start_offset;
+ double current_offset;
+ double end_offset;
+};
+
+
+static int __parse_cue_url(const char *url, char **filename, int *track_n)
+{
+ const char *slash;
+ long n;
+
+ if (!is_cue_url(url))
+ return 1;
+
+ url += 6;
+
+ slash = strrchr(url, '/');
+ if (!slash)
+ return 1;
+
+ if (str_to_int(slash + 1, &n) != 0)
+ return 1;
+
+ *filename = xstrndup(url, slash - url);
+ *track_n = n;
+ return 0;
+}
+
+
+static double __to_seconds(long v)
+{
+ const int FRAMES_IN_SECOND = 75;
+
+ return (double)v / FRAMES_IN_SECOND;
+}
+
+
+static char *__make_absolute_path(const char *abs_filename, const char *rel_filename)
+{
+ char *s;
+ const char *slash;
+ char buf[4096] = {0};
+
+ slash = strrchr(abs_filename, '/');
+ if (slash == NULL)
+ return xstrdup(rel_filename);
+
+ s = xstrndup(abs_filename, slash - abs_filename);
+ snprintf(buf, sizeof buf, "%s/%s", s, rel_filename);
+
+ free(s);
+ return xstrdup(buf);
+}
+
+
+static int cue_open(struct input_plugin_data *ip_data)
+{
+ int rc;
+ FILE *cue;
+ Cd *cd;
+ Track *t;
+ char *child_filename;
+ struct cue_private *priv;
+
+ priv = xnew(struct cue_private, 1);
+
+ rc = __parse_cue_url(ip_data->filename, &priv->cue_filename, &priv->track_n);
+ if (rc) {
+ rc = -IP_ERROR_INVALID_URI;
+ goto url_parse_failed;
+ }
+
+ cue = fopen(priv->cue_filename, "r");
+ if (cue == NULL) {
+ rc = -IP_ERROR_ERRNO;
+ goto cue_open_failed;
+ }
+
+ cd = cue_parse_file(cue);
+ if (cd == NULL) {
+ rc = -IP_ERROR_FILE_FORMAT;
+ goto cue_parse_failed;
+ }
+
+ t = cd_get_track(cd, priv->track_n);
+ if (t == NULL) {
+ rc = -IP_ERROR_FILE_FORMAT;
+ goto cue_read_failed;
+ }
+
+ child_filename = track_get_filename(t);
+ if (child_filename == NULL) {
+ rc = -IP_ERROR_FILE_FORMAT;
+ goto cue_read_failed;
+ }
+ child_filename = __make_absolute_path(priv->cue_filename, child_filename);
+
+ priv->child = ip_new(child_filename);
+ free(child_filename);
+
+ rc = ip_open(priv->child);
+ if (rc)
+ goto ip_open_failed;
+
+ ip_setup(priv->child);
+
+ priv->start_offset = __to_seconds(track_get_start(t));
+ priv->current_offset = priv->start_offset;
+
+ rc = ip_seek(priv->child, priv->start_offset);
+ if (rc)
+ goto ip_open_failed;
+
+ if (track_get_length(t) != 0)
+ priv->end_offset = priv->start_offset + __to_seconds(track_get_length(t));
+ else
+ priv->end_offset = ip_duration(priv->child);
+
+ ip_data->fd = open(ip_get_filename(priv->child), O_RDONLY);
+ if (ip_data->fd == -1)
+ goto ip_open_failed;
+
+ ip_data->private = priv;
+ ip_data->sf = ip_get_sf(priv->child);
+ ip_get_channel_map(priv->child, ip_data->channel_map);
+
+ fclose(cue);
+ cd_delete(cd);
+ return 0;
+
+ip_open_failed:
+ ip_delete(priv->child);
+
+cue_read_failed:
+ cd_delete(cd);
+
+cue_parse_failed:
+ fclose(cue);
+
+cue_open_failed:
+ free(priv->cue_filename);
+
+url_parse_failed:
+ free(priv);
+
+ return rc;
+}
+
+
+static int cue_close(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ close(ip_data->fd);
+ ip_data->fd = -1;
+
+ ip_delete(priv->child);
+ free(priv->cue_filename);
+
+ free(priv);
+ ip_data->private = NULL;
+
+ return 0;
+}
+
+
+static int cue_read(struct input_plugin_data *ip_data, char *buffer, int count)
+{
+ int rc;
+ sample_format_t sf;
+ double len;
+ double rem_len;
+ struct cue_private *priv = ip_data->private;
+
+ if (priv->current_offset >= priv->end_offset)
+ return 0;
+
+ rc = ip_read(priv->child, buffer, count);
+ if (rc <= 0)
+ return rc;
+
+ sf = ip_get_sf(priv->child);
+ len = (double)rc / sf_get_second_size(sf);
+
+ rem_len = priv->end_offset - priv->current_offset;
+ priv->current_offset += len;
+
+ if (priv->current_offset >= priv->end_offset)
+ rc = (int)(rem_len * sf_get_rate(sf)) * sf_get_frame_size(sf);
+
+ return rc;
+}
+
+
+static int cue_seek(struct input_plugin_data *ip_data, double offset)
+{
+ struct cue_private *priv = ip_data->private;
+ double new_offset = priv->start_offset + offset;
+
+ if (new_offset > priv->end_offset)
+ new_offset = priv->end_offset;
+
+ priv->current_offset = new_offset;
+
+ return ip_seek(priv->child, new_offset);
+}
+
+
+static int cue_read_comments(struct input_plugin_data *ip_data, struct keyval **comments)
+{
+ int rc;
+ FILE *cue;
+ Cd *cd;
+ Rem *cd_rem;
+ Cdtext *cd_cdtext;
+ Track *t;
+ Rem *track_rem;
+ Cdtext *track_cdtext;
+ char *val;
+ char buf[32] = {0};
+ GROWING_KEYVALS(c);
+ struct cue_private *priv = ip_data->private;
+
+ cue = fopen(priv->cue_filename, "r");
+ if (cue == NULL) {
+ rc = -IP_ERROR_ERRNO;
+ goto cue_open_failed;
+ }
+
+ cd = cue_parse_file(cue);
+ if (cd == NULL) {
+ rc = -IP_ERROR_FILE_FORMAT;
+ goto cue_parse_failed;
+ }
+
+ t = cd_get_track(cd, priv->track_n);
+ if (t == NULL) {
+ rc = -IP_ERROR_FILE_FORMAT;
+ goto get_track_failed;
+ }
+
+ snprintf(buf, sizeof buf, "%d", priv->track_n);
+ comments_add(&c, "tracknumber", xstrdup(buf));
+
+ cd_rem = cd_get_rem(cd);
+ cd_cdtext = cd_get_cdtext(cd);
+ track_rem = track_get_rem(t);
+ track_cdtext = track_get_cdtext(t);
+
+ val = cdtext_get(PTI_TITLE, track_cdtext);
+ if (val != NULL)
+ comments_add(&c, "title", xstrdup(val));
+
+ val = cdtext_get(PTI_TITLE, cd_cdtext);
+ if (val != NULL)
+ comments_add(&c, "album", xstrdup(val));
+
+ val = cdtext_get(PTI_PERFORMER, track_cdtext);
+ if (val != NULL)
+ comments_add(&c, "artist", xstrdup(val));
+
+ val = cdtext_get(PTI_PERFORMER, cd_cdtext);
+ if (val != NULL)
+ comments_add(&c, "albumartist", xstrdup(val));
+
+ val = rem_get(REM_DATE, track_rem);
+ if (val != NULL) {
+ comments_add(&c, "date", xstrdup(val));
+ } else {
+ val = rem_get(REM_DATE, cd_rem);
+ if (val != NULL)
+ comments_add(&c, "date", xstrdup(val));
+ }
+
+ /*
+ * TODO:
+ * - replaygain REMs
+ * - genre?
+ */
+
+ keyvals_terminate(&c);
+ *comments = c.keyvals;
+
+ cd_delete(cd);
+ fclose(cue);
+ return 0;
+
+get_track_failed:
+ cd_delete(cd);
+
+cue_parse_failed:
+ fclose(cue);
+
+cue_open_failed:
+ return rc;
+}
+
+
+static int cue_duration(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ return priv->end_offset - priv->start_offset;
+}
+
+
+static long cue_bitrate(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ return ip_bitrate(priv->child);
+}
+
+
+static long cue_current_bitrate(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ return ip_current_bitrate(priv->child);
+}
+
+
+static char *cue_codec(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ return ip_codec(priv->child);
+}
+
+
+static char *cue_codec_profile(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ return ip_codec_profile(priv->child);
+}
+
+
+static int cue_set_option(int key, const char *val)
+{
+ return -IP_ERROR_NOT_OPTION;
+}
+
+
+static int cue_get_option(int key, char **val)
+{
+ return -IP_ERROR_NOT_OPTION;
+}
+
+
+const struct input_plugin_ops ip_ops = {
+ .open = cue_open,
+ .close = cue_close,
+ .read = cue_read,
+ .seek = cue_seek,
+ .read_comments = cue_read_comments,
+ .duration = cue_duration,
+ .bitrate = cue_bitrate,
+ .bitrate_current = cue_current_bitrate,
+ .codec = cue_codec,
+ .codec_profile = cue_codec_profile,
+ .set_option = cue_set_option,
+ .get_option = cue_get_option
+};
+
+const int ip_priority = 50;
+const char * const ip_extensions[] = { "cue", NULL };
+const char * const ip_mime_types[] = { "application/x-cue", NULL };
+const char * const ip_options[] = { NULL };
diff --git a/cue_utils.c b/cue_utils.c
new file mode 100644
index 0000000..4c040b0
--- /dev/null
+++ b/cue_utils.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008-2011 Various Authors
+ * Copyright (C) 2011 Gregory Petrosyan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cue_utils.h"
+#include "xmalloc.h"
+
+#include <libcue/libcue.h>
+
+#include <stdio.h>
+
+
+char *associated_cue(const char *filename)
+{
+ char *f;
+ char buf[4096] = {0};
+ const char *dot = strrchr(filename, '.');
+
+ if (dot == NULL)
+ return NULL;
+
+ f = xstrndup(filename, dot - filename);
+ snprintf(buf, sizeof buf, "%s.cue", f);
+
+ free(f);
+ return xstrdup(buf);
+}
+
+
+int cue_get_ntracks(const char *filename)
+{
+ int n;
+ FILE *cue;
+ Cd *cd;
+
+ cue = fopen(filename, "r");
+ if (cue == NULL)
+ return -1;
+
+ cd = cue_parse_file(cue);
+ if (cd == NULL) {
+ fclose(cue);
+ return -1;
+ }
+
+ n = cd_get_ntrack(cd);
+
+ cd_delete(cd);
+ fclose(cue);
+
+ return n;
+}
+
+
+char *construct_cue_url(const char *cue_filename, int track_n)
+{
+ char buf[4096] = {0};
+
+ snprintf(buf, sizeof buf, "cue://%s/%d", cue_filename, track_n);
+
+ return xstrdup(buf);
+}
diff --git a/cue_utils.h b/cue_utils.h
new file mode 100644
index 0000000..41407a4
--- /dev/null
+++ b/cue_utils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2008-2011 Various Authors
+ * Copyright (C) 2011 Gregory Petrosyan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __CUE_UTILS_H__
+#define __CUE_UTILS_H__
+
+
+char *associated_cue(const char *filename);
+int cue_get_ntracks(const char *filename);
+char *construct_cue_url(const char *cue_filename, int track_n);
+
+
+#endif
diff --git a/input.c b/input.c
index 0019aee..48f105e 100644
--- a/input.c
+++ b/input.c
@@ -541,6 +541,9 @@ int ip_open(struct input_plugin *ip)
if (is_cdda_url(ip->data.filename)) {
ip->ops = get_ops_by_mime_type("x-content/audio-cdda");
rc = ip->ops ? ip->ops->open(&ip->data) : 1;
+ } else if (is_cue_url(ip->data.filename)) {
+ ip->ops = get_ops_by_mime_type("application/x-cue");
+ rc = ip->ops ? ip->ops->open(&ip->data) : 1;
} else
rc = open_file(ip);
}
diff --git a/job.c b/job.c
index 187a8e0..289c7b6 100644
--- a/job.c
+++ b/job.c
@@ -33,6 +33,7 @@
#include "player.h"
#include "discid.h"
#include "xstrjoin.h"
+#include "cue_utils.h"

#include <string.h>
#include <unistd.h>
@@ -63,10 +64,15 @@ static void add_ti(struct track_info *ti)
ti_buffer[ti_buffer_fill++] = ti;
}

+static int add_file_cue(const char *filename);
+
static void add_file(const char *filename)
{
struct track_info *ti;

+ if (!is_cue_url(filename) && add_file_cue(filename))
+ return;
+
cache_lock();
ti = cache_get_ti(filename);
cache_unlock();
@@ -75,6 +81,32 @@ static void add_file(const char *filename)
add_ti(ti);
}

+static int add_file_cue(const char *filename)
+{
+ int n_tracks;
+ char *url;
+ char *cue_filename;
+
+ cue_filename = associated_cue(filename);
+ if (cue_filename == NULL)
+ return 0;
+
+ n_tracks = cue_get_ntracks(cue_filename);
+ if (n_tracks <= 0) {
+ free(cue_filename);
+ return 0;
+ }
+
+ for (int i = 1; i <= n_tracks; ++i) {
+ url = construct_cue_url(cue_filename, i);
+ add_file(url);
+ free(url);
+ }
+
+ free(cue_filename);
+ return 1;
+}
+
static void add_url(const char *url)
{
add_file(url);
@@ -204,7 +236,7 @@ static int handle_line(void *data, const char *line)
if (worker_cancelling())
return 1;

- if (is_http_url(line)) {
+ if (is_http_url(line) || is_cue_url(line)) {
add_url(line);
} else {
char *absolute = path_absolute_cwd(line, data);
diff --git a/track_info.c b/track_info.c
index 2109a4d..0e582b0 100644
--- a/track_info.c
+++ b/track_info.c
@@ -71,6 +71,11 @@ void track_info_set_comments(struct track_info *ti, struct keyval *comments) {
ti->artistsort = comments_get_artistsort(comments);
ti->is_va_compilation = track_is_va_compilation(comments);

+ if (ti->artist == NULL && ti->albumartist != NULL) {
+ /* best guess */
+ ti->artist = ti->albumartist;
+ }
+
if (track_info_has_tag(ti) && ti->title == NULL) {
/* best guess */
ti->title = path_basename(ti->filename);
diff --git a/utils.h b/utils.h
index f3cb45b..d3aa75f 100644
--- a/utils.h
+++ b/utils.h
@@ -149,9 +149,14 @@ static inline int is_cdda_url(const char *name)
return strncmp(name, "cdda://", 7) == 0;
}

+static inline int is_cue_url(const char *name)
+{
+ return strncmp(name, "cue://", 6) == 0;
+}
+
static inline int is_url(const char *name)
{
- return is_http_url(name) || is_cdda_url(name);
+ return is_http_url(name) || is_cdda_url(name) || is_cue_url(name);
}

static inline int is_freeform_true(const char *c)
--
1.7.4.4
Gregory Petrosyan
2011-10-04 09:38:18 UTC
Permalink
On Sat, Oct 1, 2011 at 1:55 PM, Gregory Petrosyan
 cue.c        |  406 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
OK, my own testing shows that everything CUE-related works fine now.
Since the patch only adds new functionality, and touches almost no
generic code, it should not break anything that is working now.

So I'll go ahead and merge it to master. Enjoy :-)

                Gregory
Paul van der Walt
2011-10-04 09:45:28 UTC
Permalink
Great initiative, thanks! I've wanted this for really long :D
Post by Gregory Petrosyan
On Sat, Oct 1, 2011 at 1:55 PM, Gregory Petrosyan
 cue.c        |  406 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
OK, my own testing shows that everything CUE-related works fine now.
Since the patch only adds new functionality, and touches almost no
generic code, it should not break anything that is working now.
So I'll go ahead and merge it to master. Enjoy :-)
                Gregory
------------------------------------------------------------------------------
All the data continuously generated in your IT infrastructure contains a
definitive record of customers, application performance, security
threats, fraudulent activity and more. Splunk takes this data and makes
sense of it. Business sense. IT sense. Common sense.
http://p.sf.net/sfu/splunk-d2dcopy1
Gregory Petrosyan
2011-10-04 09:59:22 UTC
Permalink
Post by Paul van der Walt
Great initiative, thanks! I've wanted this for really long :D
Oh, I'll be happy to hear how it works for you!

You use it mostly to play .flac + .cue lossless rips, right?

                Gregory
Paul van der Walt
2011-10-04 10:05:23 UTC
Permalink
Well, I've converted all those I had to multiple flacs by now, but
that was the intention, yes.
Post by Gregory Petrosyan
Post by Paul van der Walt
Great initiative, thanks! I've wanted this for really long :D
Oh, I'll be happy to hear how it works for you!
You use it mostly to play .flac + .cue lossless rips, right?
                Gregory
Jason Woofenden
2011-10-22 18:29:58 UTC
Permalink
Oy, I've been shirking in my cmus testing!

I just went to build master to test the browser-up and browser-dir
patches, and I'm getting an error about missing libcue headers. It
auto-detects correctly that I don't have libcue-dev installed, but
then apparently it tries to access the header anyway.

I've not set CONFIG_CUE manually.

Build log attached.


Looks like a few files are missing #ifdef CONFIG_CUE


Thank you, - Jason

P.S. Doesn't look like Johannes's "shuffle" branch got merged yet.
I've been running it for weeks without issue. It's a definite
improvement. I'm not going to be very interested in running master
until this is merged :)
Gregory Petrosyan
2011-10-23 07:06:22 UTC
Permalink
Post by Jason Woofenden
Oy, I've been shirking in my cmus testing!
I just went to build master to test the browser-up and browser-dir
patches, and I'm getting an error about missing libcue headers. It
auto-detects correctly that I don't have libcue-dev installed, but
then apparently it tries to access the header anyway.
I've not set CONFIG_CUE manually.
Build log attached.
Looks like a few files are missing #ifdef CONFIG_CUE
Can you please test that the attached patch fixes the issue?
Post by Jason Woofenden
P.S. Doesn't look like Johannes's "shuffle" branch got merged yet.
I've been running it for weeks without issue. It's a definite
improvement. I'm not going to be very interested in running master
until this is merged :)
Oh! Somehow it got lost — I'll try to look at it ASAP!

                Gregory
Jason Woofenden
2011-10-24 07:12:07 UTC
Permalink
OK, I tested Gregory's patch (attached) and now cmus (master)
builds and works beautifully again without libcue installed.

- Jason
Johannes Weißl
2011-10-29 01:50:35 UTC
Permalink
Hi Jason,
Post by Jason Woofenden
P.S. Doesn't look like Johannes's "shuffle" branch got merged yet.
I've been running it for weeks without issue. It's a definite
improvement. I'm not going to be very interested in running master
until this is merged :)
I try to always rebase my "next" branch on the current master, so you
can run it until the patches are merged. Of course it would be better to
make them ready for master and submit them!

While I'm pretty happy with the improvements by the "shuffle" branch, it
is still not 100% perfect. The biggest issue is that live-filtering
clears out all shuffle information (as detected by you). It needs a
completely different handling of live-filtering to fix this, so this
could make the shuffle-patch implementation completely obsolete (or not,
I have to think about it).

Also, your "hitting previous track should be like browsers back button"
idea got me thinking... maybe it would be nice to just have a history
like the browser does, regardless of the current mode. It could be an
automatic hidden playlist, which then can be saved or loaded (and does
resume). This feature could make the exact handling of prev/next track
in shuffle mode less crucial.

Of course the feature would be optional, not everybody likes the idea of
a music player who remembers all songs.

What do you think of the idea?


Johannes
Jason Woofenden
2011-10-29 05:20:46 UTC
Permalink
Post by Johannes Weißl
While I'm pretty happy with the improvements by the "shuffle" branch, it
is still not 100% perfect.
I know it's not perfect. Should we merge it into master now, or
wait and see if you decide you're going to replace it soon-ish
anyway?
Post by Johannes Weißl
Also, your "hitting previous track should be like browsers back button"
idea got me thinking... maybe it would be nice to just have a history
like the browser does, regardless of the current mode. It could be an
automatic hidden playlist, which then can be saved or loaded (and does
resume). This feature could make the exact handling of prev/next track
in shuffle mode less crucial.
Of course the feature would be optional, not everybody likes the idea of
a music player who remembers all songs.
What do you think of the idea?
I like the idea.

I was very surprised to find that cmus pre-calculated a
shuffle-list with all the tracks in it. I assumed that the way
shuffle worked was to keep a history of recently played songs, and
when it was time to play the "next" track on shuffle, it'd randomly
pick a track (and pick again if the RNG pointed to a track on the
history list.) With this method you of course have to be careful
when your history list nears the length of your playlist, but
that should be fun to solve.

This seems lighter on resources, and also works with the case where
the songs to be "shuffled" changes (eg live-filtering) before all
tracks are played.

This can be very light on resources if you limit the history
length to say 100. This does change the behavior slightly (so
it's possible for a track to be repeated after 100 tracks, even if
you have 2,000 tracks.) I personally wouldn't really care though. I
think after 100 tracks I could hear it again :)

If people don't like that idea, you can let the history size grow
up to the length of the library/playlist that's being shuffled.
This could eat resources a little if you have very many tracks and
leave it running for a very long time.

Auto-resume of history info sounds like a very cool feature. I
think you may have a point that some people wouldn't want their
history written to disk. So here's my suggestion:

1. Add a setting for history_length which says how many recent
tracks to remember (default 100)

2. Add a setting for whether history is saved/resumed. I think
this could default to "yes" since this file wouldn't be any
more visible to the world than your playlist or library file.

3. If you want to get fancy: When a track that is already in the
recent history is selected (manually, by the queue, etc.) to
be played again: There could be a new setting to choose
whether it's moved from its old place in the history, or if
another copy is appended to the history.



- Jason

Johannes Weißl
2011-10-26 00:35:34 UTC
Permalink
Hi Gregory,
Post by Gregory Petrosyan
When adding a file, check for the CUE sheet with the same name. If it exists,
add entries from it instead of the original file.
Sorry for not looking at this and other posts earlier, I was busy
completing my master's thesis and moving to a new city :-)!

I read the code and tested the patch, and it works great!
Post by Gregory Petrosyan
- cache refresh etc.: will it work?
It worked well for "update-cache", but strangely not for
"win-update-cache", maybe there is a if-clause for URL-style tracks. I
have to look into it!


Maybe we could extend the associated_cue() function to also look for
e.g. file.wav.cue instead of just file.cue? All the cue-albums I have
downloaded (just 3) have this style.
Is it true that now cmus has an additional fopen() check for every added
file? I tested the performance before and after the libcue-patch, and
it doesn't suffer...
Post by Gregory Petrosyan
-ip-$(CONFIG_FFMPEG) += ffmpeg.so
Hmm, I guess this was accidental :-)? Could you add it back, otherwise
compiling the ffmpeg-plugins fails for me...


Greetings from Berlin,
Johannes
Gregory Petrosyan
2011-10-26 08:01:07 UTC
Permalink
Post by Johannes Weißl
Post by Gregory Petrosyan
- cache refresh etc.: will it work?
It worked well for "update-cache", but strangely not for
"win-update-cache", maybe there is a if-clause for URL-style tracks. I
have to look into it!
Weird! Is this the case for the current -master?
Post by Johannes Weißl
Maybe we could extend the associated_cue() function to also look for
e.g. file.wav.cue instead of just file.cue? All the cue-albums I have
downloaded (just 3) have this style.
OK, I think this can be done!
Post by Johannes Weißl
Is it true that now cmus has an additional fopen() check for every added
file? I tested the performance before and after the libcue-patch, and
it doesn't suffer...
Yes, that is true. I guess fopen() for non-existent files is pretty cheap.
Post by Johannes Weißl
Post by Gregory Petrosyan
-ip-$(CONFIG_FFMPEG) += ffmpeg.so
Hmm, I guess this was accidental :-)? Could you add it back, otherwise
compiling the ffmpeg-plugins fails for me...
Thanks — good catch! Fixed now.

Gregory
Loading...