aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2009-07-06 10:01:47 +0200
committerMax Kellermann <max@duempel.org>2009-07-06 10:01:47 +0200
commite47bdfe8e6f7da67c9714db7e650fa6a925f7847 (patch)
tree67f466706685a053da1549a9702ef0b0caa52715 /src
parentcd9c0a6b3e0a113d873483d214e1be1c37301b06 (diff)
output: attach a filter chain to each audio_output
This patch adds initial filter support for audio outputs. Each audio output gets a "filter" attribute, which is used by ao_play_chunk(). The PCM conversion is now performed by convert_filter_plugin. audio_output.convert_state has been removed.
Diffstat (limited to 'src')
-rw-r--r--src/output_control.c3
-rw-r--r--src/output_init.c15
-rw-r--r--src/output_internal.h15
-rw-r--r--src/output_thread.c92
4 files changed, 107 insertions, 18 deletions
diff --git a/src/output_control.c b/src/output_control.c
index 2c193c30..70c6d2b1 100644
--- a/src/output_control.c
+++ b/src/output_control.c
@@ -23,6 +23,7 @@
#include "output_thread.h"
#include "mixer_control.h"
#include "mixer_plugin.h"
+#include "filter_plugin.h"
#include <assert.h>
#include <stdlib.h>
@@ -171,4 +172,6 @@ void audio_output_finish(struct audio_output *ao)
notify_deinit(&ao->notify);
g_mutex_free(ao->mutex);
+
+ filter_free(ao->filter);
}
diff --git a/src/output_init.c b/src/output_init.c
index d72893ba..3b2c9d54 100644
--- a/src/output_init.c
+++ b/src/output_init.c
@@ -23,9 +23,14 @@
#include "output_list.h"
#include "audio_parser.h"
#include "mixer_control.h"
+#include "filter_plugin.h"
+#include "filter_registry.h"
+#include "filter/chain_filter_plugin.h"
#include <glib.h>
+#include <assert.h>
+
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "output"
@@ -124,7 +129,15 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
ao->open = false;
ao->fail_timer = NULL;
- pcm_convert_init(&ao->convert_state);
+ /* set up the filter chain */
+
+ ao->filter = filter_chain_new();
+ assert(ao->filter != NULL);
+
+ ao->convert_filter = filter_new(&convert_filter_plugin, NULL, NULL);
+ assert(ao->convert_filter != NULL);
+
+ filter_chain_append(ao->filter, ao->convert_filter);
ao->config_audio_format = format != NULL;
if (ao->config_audio_format) {
diff --git a/src/output_internal.h b/src/output_internal.h
index 5f7a2406..6ca17928 100644
--- a/src/output_internal.h
+++ b/src/output_internal.h
@@ -21,7 +21,6 @@
#define MPD_OUTPUT_INTERNAL_H
#include "audio_format.h"
-#include "pcm_convert.h"
#include "notify.h"
#include <time.h>
@@ -108,7 +107,19 @@ struct audio_output {
*/
struct audio_format out_audio_format;
- struct pcm_convert_state convert_state;
+ /**
+ * The filter object of this audio output. This is an
+ * instance of chain_filter_plugin.
+ */
+ struct filter *filter;
+
+ /**
+ * The convert_filter_plugin instance of this audio output.
+ * It is the last item in the filter chain, and is responsible
+ * for converting the input data into the appropriate format
+ * for this audio output.
+ */
+ struct filter *convert_filter;
/**
* The thread handle, or NULL if the output thread isn't
diff --git a/src/output_thread.c b/src/output_thread.c
index b3c13441..4b60d9d6 100644
--- a/src/output_thread.c
+++ b/src/output_thread.c
@@ -23,6 +23,8 @@
#include "chunk.h"
#include "pipe.h"
#include "player_control.h"
+#include "filter_plugin.h"
+#include "filter/convert_filter_plugin.h"
#include <glib.h>
@@ -45,12 +47,29 @@ ao_open(struct audio_output *ao)
{
bool success;
GError *error = NULL;
+ const struct audio_format *filter_audio_format;
assert(!ao->open);
assert(ao->fail_timer == NULL);
assert(ao->pipe != NULL);
assert(ao->chunk == NULL);
+ /* open the filter */
+
+ filter_audio_format = filter_open(ao->filter, &ao->in_audio_format,
+ &error);
+ if (filter_audio_format == NULL) {
+ g_warning("Failed to open filter for \"%s\" [%s]: %s",
+ ao->name, ao->plugin->name, error->message);
+ g_error_free(error);
+
+ ao->fail_timer = g_timer_new();
+ return;
+ }
+
+ if (!ao->config_audio_format)
+ ao->out_audio_format = *filter_audio_format;
+
success = ao_plugin_open(ao->plugin, ao->data,
&ao->out_audio_format,
&error);
@@ -62,11 +81,12 @@ ao_open(struct audio_output *ao)
ao->name, ao->plugin->name, error->message);
g_error_free(error);
+ filter_close(ao->filter);
ao->fail_timer = g_timer_new();
return;
}
- pcm_convert_init(&ao->convert_state);
+ convert_filter_set(ao->convert_filter, &ao->out_audio_format);
g_mutex_lock(ao->mutex);
ao->open = true;
@@ -100,12 +120,46 @@ ao_close(struct audio_output *ao)
g_mutex_unlock(ao->mutex);
ao_plugin_close(ao->plugin, ao->data);
- pcm_convert_deinit(&ao->convert_state);
+ filter_close(ao->filter);
g_debug("closed plugin=%s name=\"%s\"", ao->plugin->name, ao->name);
}
static void
+ao_reopen_filter(struct audio_output *ao)
+{
+ const struct audio_format *filter_audio_format;
+ GError *error = NULL;
+
+ filter_close(ao->filter);
+ filter_audio_format = filter_open(ao->filter, &ao->in_audio_format,
+ &error);
+ if (filter_audio_format == NULL) {
+ g_warning("Failed to open filter for \"%s\" [%s]: %s",
+ ao->name, ao->plugin->name, error->message);
+ g_error_free(error);
+
+ /* this is a little code duplication fro ao_close(),
+ but we cannot call this function because we must
+ not call filter_close(ao->filter) again */
+
+ ao->pipe = NULL;
+
+ g_mutex_lock(ao->mutex);
+ ao->chunk = NULL;
+ ao->open = false;
+ g_mutex_unlock(ao->mutex);
+
+ ao_plugin_close(ao->plugin, ao->data);
+
+ ao->fail_timer = g_timer_new();
+ return;
+ }
+
+ convert_filter_set(ao->convert_filter, &ao->out_audio_format);
+}
+
+static void
ao_reopen(struct audio_output *ao)
{
if (!ao->config_audio_format) {
@@ -121,7 +175,11 @@ ao_reopen(struct audio_output *ao)
ao->out_audio_format = ao->in_audio_format;
}
- if (!ao->open)
+ if (ao->open)
+ /* the audio format has changed, and all filters have
+ to be reconfigured */
+ ao_reopen_filter(ao);
+ else
ao_open(ao);
}
@@ -132,6 +190,8 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
size_t size = chunk->length;
GError *error = NULL;
+ assert(ao != NULL);
+ assert(ao->filter != NULL);
assert(!music_chunk_is_empty(chunk));
assert(music_chunk_check_format(chunk, &ao->in_audio_format));
assert(size % audio_format_frame_size(&ao->in_audio_format) == 0);
@@ -142,18 +202,19 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
if (size == 0)
return true;
- if (!audio_format_equals(&ao->in_audio_format,
- &ao->out_audio_format)) {
- data = pcm_convert(&ao->convert_state,
- &ao->in_audio_format, data, size,
- &ao->out_audio_format, &size);
-
- /* 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 true;
+ data = filter_filter(ao->filter, data, size, &size, &error);
+ if (data == NULL) {
+ g_warning("\"%s\" [%s] failed to filter: %s",
+ ao->name, ao->plugin->name, error->message);
+ g_error_free(error);
+
+ ao_plugin_cancel(ao->plugin, ao->data);
+ ao_close(ao);
+
+ /* don't automatically reopen this device for 10
+ seconds */
+ ao->fail_timer = g_timer_new();
+ return false;
}
while (size > 0 && ao->command == AO_COMMAND_NONE) {
@@ -271,6 +332,7 @@ static gpointer audio_output_task(gpointer arg)
ao_plugin_cancel(ao->plugin, ao->data);
ao_close(ao);
+ filter_close(ao->filter);
ao_command_finished(ao);
break;