replay_gain: reimplement as a filter plugin

Apply the replay gain in the output thread.  This means a new setting
will be active instantly, without going through the whole music pipe.
And we might have different replay gain settings for each audio output
device.
This commit is contained in:
Max Kellermann
2010-02-14 17:04:39 +01:00
parent 5e0117b444
commit 752dfb3d95
18 changed files with 310 additions and 161 deletions

View File

@@ -0,0 +1,193 @@
/*
* 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
* 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "filter/replay_gain_filter_plugin.h"
#include "filter_plugin.h"
#include "filter_internal.h"
#include "filter_registry.h"
#include "audio_format.h"
#include "pcm_buffer.h"
#include "pcm_volume.h"
#include "replay_gain_info.h"
#include "replay_gain_config.h"
#include <string.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "replay_gain"
struct replay_gain_filter {
struct filter filter;
enum replay_gain_mode mode;
struct replay_gain_info info;
/**
* The current volume, between 0 and #PCM_VOLUME_1 (both
* including).
*/
unsigned volume;
struct audio_format audio_format;
struct pcm_buffer buffer;
};
static inline GQuark
replay_gain_quark(void)
{
return g_quark_from_static_string("replay_gain");
}
/**
* Recalculates the new volume after a property was changed.
*/
static void
replay_gain_filter_update(struct replay_gain_filter *filter)
{
if (filter->mode != REPLAY_GAIN_OFF) {
const struct replay_gain_tuple *tuple =
&filter->info.tuples[filter->mode];
float scale = replay_gain_tuple_defined(tuple)
? replay_gain_tuple_scale(tuple, replay_gain_preamp)
: replay_gain_missing_preamp;
g_debug("scale=%f\n", (double)scale);
filter->volume = pcm_float_to_volume(scale);
} else
filter->volume = PCM_VOLUME_1;
}
static struct filter *
replay_gain_filter_init(G_GNUC_UNUSED const struct config_param *param,
G_GNUC_UNUSED GError **error_r)
{
struct replay_gain_filter *filter = g_new(struct replay_gain_filter, 1);
filter_init(&filter->filter, &replay_gain_filter_plugin);
filter->mode = replay_gain_mode;
replay_gain_info_init(&filter->info);
filter->volume = PCM_VOLUME_1;
return &filter->filter;
}
static void
replay_gain_filter_finish(struct filter *filter)
{
g_free(filter);
}
static const struct audio_format *
replay_gain_filter_open(struct filter *_filter,
struct audio_format *audio_format,
G_GNUC_UNUSED GError **error_r)
{
struct replay_gain_filter *filter =
(struct replay_gain_filter *)_filter;
audio_format->reverse_endian = false;
filter->audio_format = *audio_format;
pcm_buffer_init(&filter->buffer);
return &filter->audio_format;
}
static void
replay_gain_filter_close(struct filter *_filter)
{
struct replay_gain_filter *filter =
(struct replay_gain_filter *)_filter;
pcm_buffer_deinit(&filter->buffer);
}
static const void *
replay_gain_filter_filter(struct filter *_filter,
const void *src, size_t src_size,
size_t *dest_size_r, GError **error_r)
{
struct replay_gain_filter *filter =
(struct replay_gain_filter *)_filter;
bool success;
void *dest;
/* check if the mode has been changed since the last call */
if (filter->mode != replay_gain_mode) {
filter->mode = replay_gain_mode;
replay_gain_filter_update(filter);
}
*dest_size_r = src_size;
if (filter->volume >= PCM_VOLUME_1)
/* optimized special case: 100% volume = no-op */
return src;
dest = pcm_buffer_get(&filter->buffer, src_size);
*dest_size_r = src_size;
if (filter->volume <= 0) {
/* optimized special case: 0% volume = memset(0) */
/* XXX is this valid for all sample formats? What
about floating point? */
memset(dest, 0, src_size);
return dest;
}
memcpy(dest, src, src_size);
success = pcm_volume(dest, src_size, &filter->audio_format,
filter->volume);
if (!success) {
g_set_error(error_r, replay_gain_quark(), 0,
"pcm_volume() has failed");
return NULL;
}
return dest;
}
const struct filter_plugin replay_gain_filter_plugin = {
.name = "replay_gain",
.init = replay_gain_filter_init,
.finish = replay_gain_filter_finish,
.open = replay_gain_filter_open,
.close = replay_gain_filter_close,
.filter = replay_gain_filter_filter,
};
void
replay_gain_filter_set_info(struct filter *_filter,
const struct replay_gain_info *info)
{
struct replay_gain_filter *filter =
(struct replay_gain_filter *)_filter;
if (info != NULL)
filter->info = *info;
else
replay_gain_info_init(&filter->info);
replay_gain_filter_update(filter);
}

View File

@@ -0,0 +1,37 @@
/*
* 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
* 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef REPLAY_GAIN_FILTER_PLUGIN_H
#define REPLAY_GAIN_FILTER_PLUGIN_H
#include "replay_gain_info.h"
struct filter;
/**
* Sets a new #replay_gain_info at the beginning of a new song.
*
* @param info the new #replay_gain_info value, or NULL if no replay
* gain data is available for the current song
*/
void
replay_gain_filter_set_info(struct filter *filter,
const struct replay_gain_info *info);
#endif