replay_gain: optionally use hardware mixer to apply replay gain
Add an option for each audio output which enables the use of the hardware mixer, instead of the software volume code. This is hardware specific, and assumes linear volume control. This is not the case for hardware mixers which were tested, making this patch somewhat useless, but we will use it to experiment with the settings, to find a good solution.
This commit is contained in:
@@ -27,7 +27,9 @@
|
||||
#include "pcm_volume.h"
|
||||
#include "replay_gain_info.h"
|
||||
#include "replay_gain_config.h"
|
||||
#include "mixer_control.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
@@ -36,6 +38,18 @@
|
||||
struct replay_gain_filter {
|
||||
struct filter filter;
|
||||
|
||||
/**
|
||||
* If set, then this hardware mixer is used for applying
|
||||
* replay gain, instead of the software volume library.
|
||||
*/
|
||||
struct mixer *mixer;
|
||||
|
||||
/**
|
||||
* The base volume level for scale=1.0, between 1 and 100
|
||||
* (including).
|
||||
*/
|
||||
unsigned base;
|
||||
|
||||
enum replay_gain_mode mode;
|
||||
|
||||
struct replay_gain_info info;
|
||||
@@ -74,6 +88,21 @@ replay_gain_filter_update(struct replay_gain_filter *filter)
|
||||
filter->volume = pcm_float_to_volume(scale);
|
||||
} else
|
||||
filter->volume = PCM_VOLUME_1;
|
||||
|
||||
if (filter->mixer != NULL) {
|
||||
/* update the hardware mixer volume */
|
||||
|
||||
unsigned volume = (filter->volume * filter->base) / PCM_VOLUME_1;
|
||||
if (volume > 100)
|
||||
volume = 100;
|
||||
|
||||
GError *error = NULL;
|
||||
if (!mixer_set_volume(filter->mixer, volume, &error)) {
|
||||
g_warning("Failed to update hardware mixer: %s",
|
||||
error->message);
|
||||
g_error_free(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct filter *
|
||||
@@ -83,6 +112,7 @@ replay_gain_filter_init(G_GNUC_UNUSED const struct config_param *param,
|
||||
struct replay_gain_filter *filter = g_new(struct replay_gain_filter, 1);
|
||||
|
||||
filter_init(&filter->filter, &replay_gain_filter_plugin);
|
||||
filter->mixer = NULL;
|
||||
|
||||
filter->mode = replay_gain_mode;
|
||||
replay_gain_info_init(&filter->info);
|
||||
@@ -177,6 +207,21 @@ const struct filter_plugin replay_gain_filter_plugin = {
|
||||
.filter = replay_gain_filter_filter,
|
||||
};
|
||||
|
||||
void
|
||||
replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer,
|
||||
unsigned base)
|
||||
{
|
||||
struct replay_gain_filter *filter =
|
||||
(struct replay_gain_filter *)_filter;
|
||||
|
||||
assert(mixer == NULL || (base > 0 && base <= 100));
|
||||
|
||||
filter->mixer = mixer;
|
||||
filter->base = base;
|
||||
|
||||
replay_gain_filter_update(filter);
|
||||
}
|
||||
|
||||
void
|
||||
replay_gain_filter_set_info(struct filter *_filter,
|
||||
const struct replay_gain_info *info)
|
||||
|
@@ -23,6 +23,19 @@
|
||||
#include "replay_gain_info.h"
|
||||
|
||||
struct filter;
|
||||
struct mixer;
|
||||
|
||||
/**
|
||||
* Enables or disables the hardware mixer for applying replay gain.
|
||||
*
|
||||
* @param mixer the hardware mixer, or NULL to fall back to software
|
||||
* volume
|
||||
* @param base the base volume level for scale=1.0, between 1 and 100
|
||||
* (including).
|
||||
*/
|
||||
void
|
||||
replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer,
|
||||
unsigned base);
|
||||
|
||||
/**
|
||||
* Sets a new #replay_gain_info at the beginning of a new song.
|
||||
|
@@ -32,6 +32,7 @@
|
||||
#include "filter_config.h"
|
||||
#include "filter/chain_filter_plugin.h"
|
||||
#include "filter/autoconvert_filter_plugin.h"
|
||||
#include "filter/replay_gain_filter_plugin.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
@@ -196,12 +197,19 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
|
||||
|
||||
/* create the replay_gain filter */
|
||||
|
||||
ao->replay_gain_filter = filter_new(&replay_gain_filter_plugin,
|
||||
param, NULL);
|
||||
assert(ao->replay_gain_filter != NULL);
|
||||
const char *replay_gain_handler =
|
||||
config_get_block_string(param, "replay_gain_handler",
|
||||
"software");
|
||||
|
||||
filter_chain_append(ao->filter, ao->replay_gain_filter);
|
||||
ao->replay_gain_serial = 0;
|
||||
if (strcmp(replay_gain_handler, "none") != 0) {
|
||||
ao->replay_gain_filter = filter_new(&replay_gain_filter_plugin,
|
||||
param, NULL);
|
||||
assert(ao->replay_gain_filter != NULL);
|
||||
|
||||
filter_chain_append(ao->filter, ao->replay_gain_filter);
|
||||
ao->replay_gain_serial = 0;
|
||||
} else
|
||||
ao->replay_gain_filter = NULL;
|
||||
|
||||
/* create the normalization filter (if configured) */
|
||||
|
||||
@@ -247,6 +255,21 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
|
||||
g_error_free(error);
|
||||
}
|
||||
|
||||
/* use the hardware mixer for replay gain? */
|
||||
|
||||
if (strcmp(replay_gain_handler, "mixer") == 0) {
|
||||
if (ao->mixer != NULL)
|
||||
replay_gain_filter_set_mixer(ao->replay_gain_filter,
|
||||
ao->mixer, 100);
|
||||
else
|
||||
g_warning("No such mixer for output '%s'", ao->name);
|
||||
} else if (strcmp(replay_gain_handler, "software") != 0 &&
|
||||
ao->replay_gain_filter != NULL) {
|
||||
g_set_error(error_r, audio_output_quark(), 0,
|
||||
"Invalid \"replay_gain_handler\" value");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* the "convert" filter must be the last one in the chain */
|
||||
|
||||
ao->convert_filter = filter_new(&convert_filter_plugin, NULL, NULL);
|
||||
|
@@ -264,7 +264,8 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
|
||||
|
||||
/* update replay gain */
|
||||
|
||||
if (chunk->replay_gain_serial != ao->replay_gain_serial) {
|
||||
if (ao->replay_gain_filter != NULL &&
|
||||
chunk->replay_gain_serial != ao->replay_gain_serial) {
|
||||
replay_gain_filter_set_info(ao->replay_gain_filter,
|
||||
chunk->replay_gain_serial != 0
|
||||
? &chunk->replay_gain_info
|
||||
|
Reference in New Issue
Block a user