aboutsummaryrefslogtreecommitdiffstats
path: root/src/decoder
diff options
context:
space:
mode:
authorTim Phipps <mpd@phipps-hutton.freeserve.co.uk>2010-03-21 18:21:47 +0100
committerMax Kellermann <max@duempel.org>2010-03-21 18:21:47 +0100
commite7a515c8b11c643332406d60a13ab1fe06d2b226 (patch)
treeaa7179b453b6fe7b163d1b4b807157359cb436cf /src/decoder
parente9b75d462c4d0ffee3b3b26582800ec4f657a333 (diff)
downloadmpd-e7a515c8b11c643332406d60a13ab1fe06d2b226.tar.gz
mpd-e7a515c8b11c643332406d60a13ab1fe06d2b226.tar.xz
mpd-e7a515c8b11c643332406d60a13ab1fe06d2b226.zip
Add support for MixRamp tags
Adds mixrampdb and mixrampdelay commands. Reads MIXRAP_START and MIXRAMP_END tags from FLAC files and overlaps instead of crossfading.
Diffstat (limited to '')
-rw-r--r--src/decoder/_flac_common.c11
-rw-r--r--src/decoder/flac_decoder_plugin.c7
-rw-r--r--src/decoder/flac_metadata.c43
-rw-r--r--src/decoder/flac_metadata.h4
-rw-r--r--src/decoder/mad_decoder_plugin.c47
-rw-r--r--src/decoder_api.c12
-rw-r--r--src/decoder_api.h11
-rw-r--r--src/decoder_control.c50
-rw-r--r--src/decoder_control.h13
-rw-r--r--src/decoder_thread.c11
10 files changed, 203 insertions, 6 deletions
diff --git a/src/decoder/_flac_common.c b/src/decoder/_flac_common.c
index b642121ba..6f6d33f05 100644
--- a/src/decoder/_flac_common.c
+++ b/src/decoder/_flac_common.c
@@ -102,11 +102,6 @@ flac_got_stream_info(struct flac_data *data,
if (data->total_frames == 0)
data->total_frames = stream_info->total_samples;
- decoder_initialized(data->decoder, &data->audio_format,
- data->input_stream->seekable,
- (float)data->total_frames /
- (float)data->audio_format.sample_rate);
-
data->initialized = true;
}
@@ -117,6 +112,8 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
return;
struct replay_gain_info rgi;
+ char *mixramp_start;
+ char *mixramp_end;
switch (block->type) {
case FLAC__METADATA_TYPE_STREAMINFO:
@@ -126,6 +123,10 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
if (flac_parse_replay_gain(&rgi, block))
decoder_replay_gain(data->decoder, &rgi);
+ if (flac_parse_mixramp(&mixramp_start, &mixramp_end, block)) {
+ g_debug("setting mixramp_tags");
+ decoder_mixramp(data->decoder, mixramp_start, mixramp_end);
+ }
if (data->tag != NULL)
flac_vorbis_comments_to_tag(data->tag, NULL,
diff --git a/src/decoder/flac_decoder_plugin.c b/src/decoder/flac_decoder_plugin.c
index 022ee7045..e89e2ea11 100644
--- a/src/decoder/flac_decoder_plugin.c
+++ b/src/decoder/flac_decoder_plugin.c
@@ -247,9 +247,14 @@ flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd,
return false;
}
- if (data->initialized)
+ if (data->initialized) {
/* done */
+ decoder_initialized(data->decoder, &data->audio_format,
+ data->input_stream->seekable,
+ (float)data->total_frames /
+ (float)data->audio_format.sample_rate);
return true;
+ }
if (data->input_stream->seekable)
/* allow the workaround below only for nonseekable
diff --git a/src/decoder/flac_metadata.c b/src/decoder/flac_metadata.c
index 926cd3af7..68d15f6d4 100644
--- a/src/decoder/flac_metadata.c
+++ b/src/decoder/flac_metadata.c
@@ -80,6 +80,49 @@ flac_parse_replay_gain(struct replay_gain_info *rgi,
return found;
}
+static bool
+flac_find_string_comment(const FLAC__StreamMetadata *block,
+ const char *cmnt, char **str)
+{
+ int offset;
+ size_t pos;
+ int len;
+ unsigned char tmp, *p;
+
+ *str = NULL;
+ offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0,
+ cmnt);
+ if (offset < 0)
+ return false;
+
+ pos = strlen(cmnt) + 1; /* 1 is for '=' */
+ len = block->data.vorbis_comment.comments[offset].length - pos;
+ if (len <= 0)
+ return false;
+
+ p = &block->data.vorbis_comment.comments[offset].entry[pos];
+ tmp = p[len];
+ p[len] = '\0';
+ *str = strdup((char *)p);
+ p[len] = tmp;
+
+ return true;
+}
+
+bool
+flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
+ const FLAC__StreamMetadata *block)
+{
+ bool found = false;
+
+ if (flac_find_string_comment(block, "mixramp_start", mixramp_start))
+ found = true;
+ if (flac_find_string_comment(block, "mixramp_end", mixramp_end))
+ found = true;
+
+ return found;
+}
+
/**
* Checks if the specified name matches the entry's name, and if yes,
* returns the comment value (not null-temrinated).
diff --git a/src/decoder/flac_metadata.h b/src/decoder/flac_metadata.h
index 3cc333617..06e691d1d 100644
--- a/src/decoder/flac_metadata.h
+++ b/src/decoder/flac_metadata.h
@@ -37,6 +37,10 @@ bool
flac_parse_replay_gain(struct replay_gain_info *rgi,
const FLAC__StreamMetadata *block);
+bool
+flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
+ const FLAC__StreamMetadata *block);
+
void
flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
const FLAC__StreamMetadata_VorbisComment *comment);
diff --git a/src/decoder/mad_decoder_plugin.c b/src/decoder/mad_decoder_plugin.c
index 379cb9b8a..6f6ee8fa4 100644
--- a/src/decoder/mad_decoder_plugin.c
+++ b/src/decoder/mad_decoder_plugin.c
@@ -347,6 +347,47 @@ parse_id3_replay_gain_info(struct replay_gain_info *replay_gain_info,
}
#endif
+#ifdef HAVE_ID3TAG
+static bool
+parse_id3_mixramp(char **mixramp_start, char **mixramp_end,
+ struct id3_tag *tag)
+{
+ int i;
+ char *key;
+ char *value;
+ struct id3_frame *frame;
+ bool found = false;
+
+ *mixramp_start = NULL;
+ *mixramp_end = NULL;
+
+ for (i = 0; (frame = id3_tag_findframe(tag, "TXXX", i)); i++) {
+ if (frame->nfields < 3)
+ continue;
+
+ key = (char *)
+ id3_ucs4_latin1duplicate(id3_field_getstring
+ (&frame->fields[1]));
+ value = (char *)
+ id3_ucs4_latin1duplicate(id3_field_getstring
+ (&frame->fields[2]));
+
+ if (g_ascii_strcasecmp(key, "mixramp_start") == 0) {
+ *mixramp_start = strdup(value);
+ found = true;
+ } else if (g_ascii_strcasecmp(key, "mixramp_end") == 0) {
+ *mixramp_end = strdup(value);
+ found = true;
+ }
+
+ free(key);
+ free(value);
+ }
+
+ return found;
+}
+#endif
+
static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
struct tag **mpd_tag)
{
@@ -403,10 +444,16 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
if (data->decoder != NULL) {
struct replay_gain_info rgi;
+ char *mixramp_start;
+ char *mixramp_end;
if (parse_id3_replay_gain_info(&rgi, id3_tag)) {
decoder_replay_gain(data->decoder, &rgi);
data->found_replay_gain = true;
}
+ if (parse_id3_mixramp(&mixramp_start, &mixramp_end, id3_tag)) {
+ g_debug("setting mixramp_tags");
+ decoder_mixramp(data->decoder, mixramp_start, mixramp_end);
+ }
}
id3_tag_delete(id3_tag);
diff --git a/src/decoder_api.c b/src/decoder_api.c
index fc7ed3900..948ccb567 100644
--- a/src/decoder_api.c
+++ b/src/decoder_api.c
@@ -427,3 +427,15 @@ decoder_replay_gain(struct decoder *decoder,
} else
decoder->replay_gain_serial = 0;
}
+
+void
+decoder_mixramp(struct decoder *decoder,
+ char *mixramp_start, char *mixramp_end)
+{
+ assert(decoder != NULL);
+ struct decoder_control *dc = decoder->dc;
+ assert(dc != NULL);
+
+ dc_mixramp_start(dc, mixramp_start);
+ dc_mixramp_end(dc, mixramp_end);
+}
diff --git a/src/decoder_api.h b/src/decoder_api.h
index 8c7133251..e2b645f6d 100644
--- a/src/decoder_api.h
+++ b/src/decoder_api.h
@@ -157,4 +157,15 @@ void
decoder_replay_gain(struct decoder *decoder,
const struct replay_gain_info *replay_gain_info);
+/**
+ * Store MixRamp tags.
+ *
+ * @param decoder the decoder object
+ * @param mixramp_start the mixramp_start tag; may be NULL to invalidate
+ * @param mixramp_end the mixramp_end tag; may be NULL to invalidate
+ */
+void
+decoder_mixramp(struct decoder *decoder,
+ char *mixramp_start, char *mixramp_end);
+
#endif
diff --git a/src/decoder_control.c b/src/decoder_control.c
index eeb4670a0..7388d307f 100644
--- a/src/decoder_control.c
+++ b/src/decoder_control.c
@@ -22,6 +22,10 @@
#include "player_control.h"
#include <assert.h>
+#include <malloc.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "decoder_control"
void
dc_init(struct decoder_control *dc)
@@ -33,6 +37,10 @@ dc_init(struct decoder_control *dc)
dc->state = DECODE_STATE_STOP;
dc->command = DECODE_COMMAND_NONE;
+
+ dc->mixramp_start = NULL;
+ dc->mixramp_end = NULL;
+ dc->mixramp_prev_end = NULL;
}
void
@@ -40,6 +48,15 @@ dc_deinit(struct decoder_control *dc)
{
g_cond_free(dc->cond);
g_mutex_free(dc->mutex);
+ if (dc->mixramp_start)
+ free(dc->mixramp_start);
+ if (dc->mixramp_end)
+ free(dc->mixramp_end);
+ if (dc->mixramp_prev_end)
+ free(dc->mixramp_prev_end);
+ dc->mixramp_start = NULL;
+ dc->mixramp_end = NULL;
+ dc->mixramp_prev_end = NULL;
}
static void
@@ -147,3 +164,36 @@ dc_quit(struct decoder_control *dc)
g_thread_join(dc->thread);
dc->thread = NULL;
}
+
+void
+dc_mixramp_start(struct decoder_control *dc, char *mixramp_start)
+{
+ assert(dc != NULL);
+
+ if (dc->mixramp_start)
+ free(dc->mixramp_start);
+ dc->mixramp_start = mixramp_start;
+ g_debug("mixramp_start = %s", mixramp_start ? mixramp_start : "NULL");
+}
+
+void
+dc_mixramp_end(struct decoder_control *dc, char *mixramp_end)
+{
+ assert(dc != NULL);
+
+ if (dc->mixramp_end)
+ free(dc->mixramp_end);
+ dc->mixramp_end = mixramp_end;
+ g_debug("mixramp_end = %s", mixramp_end ? mixramp_end : "NULL");
+}
+
+void
+dc_mixramp_prev_end(struct decoder_control *dc, char *mixramp_prev_end)
+{
+ assert(dc != NULL);
+
+ if (dc->mixramp_prev_end)
+ free(dc->mixramp_prev_end);
+ dc->mixramp_prev_end = mixramp_prev_end;
+ g_debug("mixramp_prev_end = %s", mixramp_prev_end ? mixramp_prev_end : "NULL");
+}
diff --git a/src/decoder_control.h b/src/decoder_control.h
index 9c6f6e88c..7794258c9 100644
--- a/src/decoder_control.h
+++ b/src/decoder_control.h
@@ -89,6 +89,10 @@ struct decoder_control {
* owns this object, and is responsible for freeing it.
*/
struct music_pipe *pipe;
+
+ char *mixramp_start;
+ char *mixramp_end;
+ char *mixramp_prev_end;
};
void
@@ -235,4 +239,13 @@ dc_seek(struct decoder_control *dc, double where);
void
dc_quit(struct decoder_control *dc);
+void
+dc_mixramp_start(struct decoder_control *dc, char *mixramp_start);
+
+void
+dc_mixramp_end(struct decoder_control *dc, char *mixramp_end);
+
+void
+dc_mixramp_prev_end(struct decoder_control *dc, char *mixramp_prev_end);
+
#endif
diff --git a/src/decoder_thread.c b/src/decoder_thread.c
index 99fa2c7e2..3eab61295 100644
--- a/src/decoder_thread.c
+++ b/src/decoder_thread.c
@@ -23,6 +23,7 @@
#include "decoder_internal.h"
#include "decoder_list.h"
#include "decoder_plugin.h"
+#include "decoder_api.h"
#include "input_stream.h"
#include "player_control.h"
#include "pipe.h"
@@ -36,6 +37,9 @@
#include <unistd.h>
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "decoder_thread"
+
static enum decoder_command
decoder_lock_get_command(struct decoder_control *dc)
{
@@ -430,6 +434,13 @@ decoder_task(gpointer arg)
switch (dc->command) {
case DECODE_COMMAND_START:
+ g_debug("clearing mixramp tags");
+ dc_mixramp_start(dc, NULL);
+ dc_mixramp_prev_end(dc, dc->mixramp_end);
+ dc->mixramp_end = NULL; /* Don't free, it's copied above. */
+
+ /* fall through */
+
case DECODE_COMMAND_SEEK:
decoder_run(dc);