diff options
Diffstat (limited to 'src/decoder_api.c')
-rw-r--r-- | src/decoder_api.c | 277 |
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); +} |