aboutsummaryrefslogtreecommitdiffstats
path: root/src/decoder_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/decoder_api.c')
-rw-r--r--src/decoder_api.c277
1 files changed, 183 insertions, 94 deletions
diff --git a/src/decoder_api.c b/src/decoder_api.c
index c696ba101..fe34ea34a 100644
--- a/src/decoder_api.c
+++ b/src/decoder_api.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2009 The Music Player Daemon Project
+ * Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include "config.h"
#include "decoder_api.h"
#include "decoder_internal.h"
#include "decoder_control.h"
@@ -24,10 +25,9 @@
#include "audio.h"
#include "song.h"
#include "buffer.h"
-
-#include "normalize.h"
#include "pipe.h"
#include "chunk.h"
+#include "replay_gain_config.h"
#include <glib.h>
@@ -37,12 +37,16 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "decoder"
-void decoder_initialized(G_GNUC_UNUSED struct decoder * decoder,
- const struct audio_format *audio_format,
- bool seekable, float total_time)
+void
+decoder_initialized(struct decoder *decoder,
+ const struct audio_format *audio_format,
+ bool seekable, float total_time)
{
- assert(dc.state == DECODE_STATE_START);
- assert(dc.pipe != NULL);
+ struct decoder_control *dc = decoder->dc;
+ struct audio_format_string af_string;
+
+ assert(dc->state == DECODE_STATE_START);
+ assert(dc->pipe != NULL);
assert(decoder != NULL);
assert(decoder->stream_tag == NULL);
assert(decoder->decoder_tag == NULL);
@@ -51,47 +55,49 @@ void decoder_initialized(G_GNUC_UNUSED struct decoder * decoder,
assert(audio_format_defined(audio_format));
assert(audio_format_valid(audio_format));
- dc.in_audio_format = *audio_format;
- getOutputAudioFormat(audio_format, &dc.out_audio_format);
+ dc->in_audio_format = *audio_format;
+ getOutputAudioFormat(audio_format, &dc->out_audio_format);
+
+ dc->seekable = seekable;
+ dc->total_time = total_time;
- dc.seekable = seekable;
- dc.total_time = total_time;
+ decoder_lock(dc);
+ dc->state = DECODE_STATE_DECODE;
+ decoder_unlock(dc);
- dc.state = DECODE_STATE_DECODE;
- notify_signal(&pc.notify);
+ player_lock_signal();
- g_debug("audio_format=%u:%u:%u, seekable=%s",
- dc.in_audio_format.sample_rate, dc.in_audio_format.bits,
- dc.in_audio_format.channels,
+ g_debug("audio_format=%s, seekable=%s",
+ audio_format_to_string(&dc->in_audio_format, &af_string),
seekable ? "true" : "false");
- if (!audio_format_equals(&dc.in_audio_format, &dc.out_audio_format))
- g_debug("converting to %u:%u:%u",
- dc.out_audio_format.sample_rate,
- dc.out_audio_format.bits,
- dc.out_audio_format.channels);
+ if (!audio_format_equals(&dc->in_audio_format,
+ &dc->out_audio_format))
+ g_debug("converting to %s",
+ audio_format_to_string(&dc->out_audio_format,
+ &af_string));
}
-char *decoder_get_uri(G_GNUC_UNUSED struct decoder *decoder)
+enum decoder_command decoder_get_command(G_GNUC_UNUSED struct decoder * decoder)
{
- assert(dc.pipe != NULL);
+ const struct decoder_control *dc = decoder->dc;
+
+ assert(dc->pipe != NULL);
- return song_get_uri(dc.current_song);
+ return dc->command;
}
-enum decoder_command decoder_get_command(G_GNUC_UNUSED struct decoder * decoder)
+void
+decoder_command_finished(struct decoder *decoder)
{
- assert(dc.pipe != NULL);
+ struct decoder_control *dc = decoder->dc;
- return dc.command;
-}
+ decoder_lock(dc);
-void decoder_command_finished(G_GNUC_UNUSED struct decoder * decoder)
-{
- assert(dc.command != DECODE_COMMAND_NONE);
- assert(dc.command != DECODE_COMMAND_SEEK ||
- dc.seek_error || decoder->seeking);
- assert(dc.pipe != NULL);
+ assert(dc->command != DECODE_COMMAND_NONE);
+ assert(dc->command != DECODE_COMMAND_SEEK ||
+ dc->seek_error || decoder->seeking);
+ assert(dc->pipe != NULL);
if (decoder->seeking) {
decoder->seeking = false;
@@ -99,33 +105,41 @@ void decoder_command_finished(G_GNUC_UNUSED struct decoder * decoder)
/* delete frames from the old song position */
if (decoder->chunk != NULL) {
- music_buffer_return(dc.buffer, decoder->chunk);
+ music_buffer_return(dc->buffer, decoder->chunk);
decoder->chunk = NULL;
}
- music_pipe_clear(dc.pipe, dc.buffer);
+ music_pipe_clear(dc->pipe, dc->buffer);
+
+ decoder->timestamp = dc->seek_where;
}
- dc.command = DECODE_COMMAND_NONE;
- notify_signal(&pc.notify);
+ dc->command = DECODE_COMMAND_NONE;
+ decoder_unlock(dc);
+
+ player_lock_signal();
}
double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder)
{
- assert(dc.command == DECODE_COMMAND_SEEK);
- assert(dc.pipe != NULL);
+ const struct decoder_control *dc = decoder->dc;
+
+ assert(dc->command == DECODE_COMMAND_SEEK);
+ assert(dc->pipe != NULL);
decoder->seeking = true;
- return dc.seek_where;
+ return dc->seek_where;
}
void decoder_seek_error(struct decoder * decoder)
{
- assert(dc.command == DECODE_COMMAND_SEEK);
- assert(dc.pipe != NULL);
+ struct decoder_control *dc = decoder->dc;
+
+ assert(dc->command == DECODE_COMMAND_SEEK);
+ assert(dc->pipe != NULL);
- dc.seek_error = true;
+ dc->seek_error = true;
decoder->seeking = false;
decoder_command_finished(decoder);
@@ -135,11 +149,14 @@ size_t decoder_read(struct decoder *decoder,
struct input_stream *is,
void *buffer, size_t length)
{
+ const struct decoder_control *dc =
+ decoder != NULL ? decoder->dc : NULL;
+ GError *error = NULL;
size_t nbytes;
assert(decoder == NULL ||
- dc.state == DECODE_STATE_START ||
- dc.state == DECODE_STATE_DECODE);
+ dc->state == DECODE_STATE_START ||
+ dc->state == DECODE_STATE_DECODE);
assert(is != NULL);
assert(buffer != NULL);
@@ -152,12 +169,19 @@ size_t decoder_read(struct decoder *decoder,
/* ignore the SEEK command during initialization,
the plugin should handle that after it has
initialized successfully */
- (dc.command != DECODE_COMMAND_SEEK ||
- (dc.state != DECODE_STATE_START && !decoder->seeking)) &&
- dc.command != DECODE_COMMAND_NONE)
+ (dc->command != DECODE_COMMAND_SEEK ||
+ (dc->state != DECODE_STATE_START && !decoder->seeking)) &&
+ dc->command != DECODE_COMMAND_NONE)
return 0;
- nbytes = input_stream_read(is, buffer, length);
+ nbytes = input_stream_read(is, buffer, length, &error);
+
+ if (G_UNLIKELY(nbytes == 0 && error != NULL)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return 0;
+ }
+
if (nbytes > 0 || input_stream_eof(is))
return nbytes;
@@ -167,6 +191,15 @@ size_t decoder_read(struct decoder *decoder,
}
}
+void
+decoder_timestamp(struct decoder *decoder, double t)
+{
+ assert(decoder != NULL);
+ assert(t >= 0);
+
+ decoder->timestamp = t;
+}
+
/**
* Sends a #tag as-is to the music pipe. Flushes the current chunk
* (decoder.chunk) if there is one.
@@ -181,15 +214,15 @@ do_send_tag(struct decoder *decoder, struct input_stream *is,
/* there is a partial chunk - flush it, we want the
tag in a new chunk */
decoder_flush_chunk(decoder);
- notify_signal(&pc.notify);
+ player_lock_signal();
}
assert(decoder->chunk == NULL);
chunk = decoder_get_chunk(decoder, is);
if (chunk == NULL) {
- assert(dc.command != DECODE_COMMAND_NONE);
- return dc.command;
+ assert(decoder->dc->command != DECODE_COMMAND_NONE);
+ return decoder->dc->command;
}
chunk->tag = tag_dup(tag);
@@ -225,31 +258,34 @@ enum decoder_command
decoder_data(struct decoder *decoder,
struct input_stream *is,
const void *_data, size_t length,
- float data_time, uint16_t bitRate,
- struct replay_gain_info *replay_gain_info)
+ uint16_t kbit_rate)
{
+ struct decoder_control *dc = decoder->dc;
const char *data = _data;
+ GError *error = NULL;
+ enum decoder_command cmd;
+
+ assert(dc->state == DECODE_STATE_DECODE);
+ assert(dc->pipe != NULL);
+ assert(length % audio_format_frame_size(&dc->in_audio_format) == 0);
- assert(dc.state == DECODE_STATE_DECODE);
- assert(dc.pipe != NULL);
- assert(length % audio_format_frame_size(&dc.in_audio_format) == 0);
+ decoder_lock(dc);
+ cmd = dc->command;
+ decoder_unlock(dc);
- if (dc.command == DECODE_COMMAND_STOP ||
- dc.command == DECODE_COMMAND_SEEK ||
+ if (cmd == DECODE_COMMAND_STOP || cmd == DECODE_COMMAND_SEEK ||
length == 0)
- return dc.command;
+ return cmd;
/* send stream tags */
if (update_stream_tag(decoder, is)) {
- enum decoder_command cmd;
-
if (decoder->decoder_tag != NULL) {
/* merge with tag from decoder plugin */
struct tag *tag;
- tag = tag_merge(decoder->stream_tag,
- decoder->decoder_tag);
+ tag = tag_merge(decoder->decoder_tag,
+ decoder->stream_tag);
cmd = do_send_tag(decoder, is, tag);
tag_free(tag);
} else
@@ -260,17 +296,18 @@ decoder_data(struct decoder *decoder,
return cmd;
}
- if (!audio_format_equals(&dc.in_audio_format, &dc.out_audio_format)) {
+ if (!audio_format_equals(&dc->in_audio_format, &dc->out_audio_format)) {
data = pcm_convert(&decoder->conv_state,
- &dc.in_audio_format, data, length,
- &dc.out_audio_format, &length);
-
- /* under certain circumstances, pcm_convert() may
- return an empty buffer - this condition should be
- investigated further, but for now, do this check as
- a workaround: */
- if (data == NULL)
- return DECODE_COMMAND_NONE;
+ &dc->in_audio_format, data, length,
+ &dc->out_audio_format, &length,
+ &error);
+ if (data == NULL) {
+ /* the PCM conversion has failed - stop
+ playback, since we have no better way to
+ bail out */
+ g_warning("%s", error->message);
+ return DECODE_COMMAND_STOP;
+ }
}
while (length > 0) {
@@ -281,16 +318,18 @@ decoder_data(struct decoder *decoder,
chunk = decoder_get_chunk(decoder, is);
if (chunk == NULL) {
- assert(dc.command != DECODE_COMMAND_NONE);
- return dc.command;
+ assert(dc->command != DECODE_COMMAND_NONE);
+ return dc->command;
}
- dest = music_chunk_write(chunk, &dc.out_audio_format,
- data_time, bitRate, &nbytes);
+ dest = music_chunk_write(chunk, &dc->out_audio_format,
+ decoder->timestamp -
+ dc->song->start_ms / 1000.0,
+ kbit_rate, &nbytes);
if (dest == NULL) {
/* the chunk is full, flush it */
decoder_flush_chunk(decoder);
- notify_signal(&pc.notify);
+ player_lock_signal();
continue;
}
@@ -303,26 +342,26 @@ decoder_data(struct decoder *decoder,
memcpy(dest, data, nbytes);
- /* apply replay gain or normalization */
-
- if (replay_gain_info != NULL &&
- replay_gain_mode != REPLAY_GAIN_OFF)
- replay_gain_apply(replay_gain_info, dest, nbytes,
- &dc.out_audio_format);
- else if (normalizationEnabled)
- normalizeData(dest, nbytes, &dc.out_audio_format);
-
/* expand the music pipe chunk */
- full = music_chunk_expand(chunk, &dc.out_audio_format, nbytes);
+ full = music_chunk_expand(chunk, &dc->out_audio_format, nbytes);
if (full) {
/* the chunk is full, flush it */
decoder_flush_chunk(decoder);
- notify_signal(&pc.notify);
+ player_lock_signal();
}
data += nbytes;
length -= nbytes;
+
+ decoder->timestamp += (double)nbytes /
+ audio_format_time_to_size(&dc->out_audio_format);
+
+ if (dc->song->end_ms > 0 &&
+ decoder->timestamp >= dc->song->end_ms / 1000.0)
+ /* the end of this range has been reached:
+ stop decoding */
+ return DECODE_COMMAND_STOP;
}
return DECODE_COMMAND_NONE;
@@ -332,10 +371,11 @@ enum decoder_command
decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is,
const struct tag *tag)
{
+ G_GNUC_UNUSED const struct decoder_control *dc = decoder->dc;
enum decoder_command cmd;
- assert(dc.state == DECODE_STATE_DECODE);
- assert(dc.pipe != NULL);
+ assert(dc->state == DECODE_STATE_DECODE);
+ assert(dc->pipe != NULL);
assert(tag != NULL);
/* save the tag */
@@ -363,3 +403,52 @@ decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is,
return cmd;
}
+
+float
+decoder_replay_gain(struct decoder *decoder,
+ const struct replay_gain_info *replay_gain_info)
+{
+ float return_db = 0;
+ assert(decoder != NULL);
+
+ if (replay_gain_info != NULL) {
+ static unsigned serial;
+ if (++serial == 0)
+ serial = 1;
+
+ if (REPLAY_GAIN_OFF != replay_gain_mode) {
+ return_db = 20.0 * log10f(
+ replay_gain_tuple_scale(
+ &replay_gain_info->tuples[replay_gain_get_real_mode()],
+ replay_gain_preamp, replay_gain_missing_preamp,
+ replay_gain_limit));
+ }
+
+ decoder->replay_gain_info = *replay_gain_info;
+ decoder->replay_gain_serial = serial;
+
+ if (decoder->chunk != NULL) {
+ /* flush the current chunk because the new
+ replay gain values affect the following
+ samples */
+ decoder_flush_chunk(decoder);
+ player_lock_signal();
+ }
+ } else
+ decoder->replay_gain_serial = 0;
+
+ return return_db;
+}
+
+void
+decoder_mixramp(struct decoder *decoder, float replay_gain_db,
+ char *mixramp_start, char *mixramp_end)
+{
+ assert(decoder != NULL);
+ struct decoder_control *dc = decoder->dc;
+ assert(dc != NULL);
+
+ dc->replay_gain_db = replay_gain_db;
+ dc_mixramp_start(dc, mixramp_start);
+ dc_mixramp_end(dc, mixramp_end);
+}