mixer/Alsa: convert to a class

This commit is contained in:
Max Kellermann 2013-04-16 20:05:29 +02:00
parent e63420a8c2
commit 80ba3c5932
1 changed files with 111 additions and 75 deletions

View File

@ -45,10 +45,7 @@ private:
virtual void DispatchSockets() override;
};
struct alsa_mixer {
/** the base mixer class */
struct mixer base;
class AlsaMixer final : public mixer {
const char *device;
const char *control;
unsigned int index;
@ -60,6 +57,19 @@ struct alsa_mixer {
int volume_set;
AlsaMixerMonitor *monitor;
public:
AlsaMixer() {
mixer_init(this, &alsa_mixer_plugin);
}
void Configure(const config_param *param);
bool Setup(GError **error_r);
bool Open(GError **error_r);
void Close();
int GetVolume(GError **error_r);
bool SetVolume(unsigned volume, GError **error_r);
};
/**
@ -129,30 +139,33 @@ alsa_mixer_elem_callback(G_GNUC_UNUSED snd_mixer_elem_t *elem, unsigned mask)
*
*/
inline void
AlsaMixer::Configure(const config_param *param)
{
device = config_get_block_string(param, "mixer_device",
VOLUME_MIXER_ALSA_DEFAULT);
control = config_get_block_string(param, "mixer_control",
VOLUME_MIXER_ALSA_CONTROL_DEFAULT);
index = config_get_block_unsigned(param, "mixer_index",
VOLUME_MIXER_ALSA_INDEX_DEFAULT);
}
static struct mixer *
alsa_mixer_init(G_GNUC_UNUSED void *ao, const struct config_param *param,
G_GNUC_UNUSED GError **error_r)
{
struct alsa_mixer *am = g_new(struct alsa_mixer, 1);
AlsaMixer *am = new AlsaMixer();
am->Configure(param);
mixer_init(&am->base, &alsa_mixer_plugin);
am->device = config_get_block_string(param, "mixer_device",
VOLUME_MIXER_ALSA_DEFAULT);
am->control = config_get_block_string(param, "mixer_control",
VOLUME_MIXER_ALSA_CONTROL_DEFAULT);
am->index = config_get_block_unsigned(param, "mixer_index",
VOLUME_MIXER_ALSA_INDEX_DEFAULT);
return &am->base;
return am;
}
static void
alsa_mixer_finish(struct mixer *data)
{
struct alsa_mixer *am = (struct alsa_mixer *)data;
AlsaMixer *am = (AlsaMixer *)data;
g_free(am);
delete am;
/* free libasound's config cache */
snd_config_update_free_global();
@ -174,19 +187,19 @@ alsa_mixer_lookup_elem(snd_mixer_t *handle, const char *name, unsigned idx)
return NULL;
}
static bool
alsa_mixer_setup(struct alsa_mixer *am, GError **error_r)
inline bool
AlsaMixer::Setup(GError **error_r)
{
int err;
if ((err = snd_mixer_attach(am->handle, am->device)) < 0) {
if ((err = snd_mixer_attach(handle, device)) < 0) {
g_set_error(error_r, alsa_mixer_quark(), err,
"failed to attach to %s: %s",
am->device, snd_strerror(err));
device, snd_strerror(err));
return false;
}
if ((err = snd_mixer_selem_register(am->handle, NULL,
if ((err = snd_mixer_selem_register(handle, NULL,
NULL)) < 0) {
g_set_error(error_r, alsa_mixer_quark(), err,
"snd_mixer_selem_register() failed: %s",
@ -194,27 +207,48 @@ alsa_mixer_setup(struct alsa_mixer *am, GError **error_r)
return false;
}
if ((err = snd_mixer_load(am->handle)) < 0) {
if ((err = snd_mixer_load(handle)) < 0) {
g_set_error(error_r, alsa_mixer_quark(), err,
"snd_mixer_load() failed: %s\n",
snd_strerror(err));
return false;
}
am->elem = alsa_mixer_lookup_elem(am->handle, am->control, am->index);
if (am->elem == NULL) {
elem = alsa_mixer_lookup_elem(handle, control, index);
if (elem == NULL) {
g_set_error(error_r, alsa_mixer_quark(), 0,
"no such mixer control: %s", am->control);
"no such mixer control: %s", control);
return false;
}
snd_mixer_selem_get_playback_volume_range(am->elem,
&am->volume_min,
&am->volume_max);
snd_mixer_selem_get_playback_volume_range(elem, &volume_min,
&volume_max);
snd_mixer_elem_set_callback(am->elem, alsa_mixer_elem_callback);
snd_mixer_elem_set_callback(elem, alsa_mixer_elem_callback);
am->monitor = new AlsaMixerMonitor(*main_loop, am->handle);
monitor = new AlsaMixerMonitor(*main_loop, handle);
return true;
}
inline bool
AlsaMixer::Open(GError **error_r)
{
int err;
volume_set = -1;
err = snd_mixer_open(&handle, 0);
if (err < 0) {
g_set_error(error_r, alsa_mixer_quark(), err,
"snd_mixer_open() failed: %s", snd_strerror(err));
return false;
}
if (!Setup(error_r)) {
snd_mixer_close(handle);
return false;
}
return true;
}
@ -222,50 +256,39 @@ alsa_mixer_setup(struct alsa_mixer *am, GError **error_r)
static bool
alsa_mixer_open(struct mixer *data, GError **error_r)
{
struct alsa_mixer *am = (struct alsa_mixer *)data;
int err;
AlsaMixer *am = (AlsaMixer *)data;
am->volume_set = -1;
return am->Open(error_r);
}
err = snd_mixer_open(&am->handle, 0);
if (err < 0) {
g_set_error(error_r, alsa_mixer_quark(), err,
"snd_mixer_open() failed: %s", snd_strerror(err));
return false;
}
inline void
AlsaMixer::Close()
{
assert(handle != NULL);
if (!alsa_mixer_setup(am, error_r)) {
snd_mixer_close(am->handle);
return false;
}
delete monitor;
return true;
snd_mixer_elem_set_callback(elem, NULL);
snd_mixer_close(handle);
}
static void
alsa_mixer_close(struct mixer *data)
{
struct alsa_mixer *am = (struct alsa_mixer *)data;
assert(am->handle != NULL);
delete am->monitor;
snd_mixer_elem_set_callback(am->elem, NULL);
snd_mixer_close(am->handle);
AlsaMixer *am = (AlsaMixer *)data;
am->Close();
}
static int
alsa_mixer_get_volume(struct mixer *mixer, GError **error_r)
inline int
AlsaMixer::GetVolume(GError **error_r)
{
struct alsa_mixer *am = (struct alsa_mixer *)mixer;
int err;
int ret;
long level;
assert(am->handle != NULL);
assert(handle != NULL);
err = snd_mixer_handle_events(am->handle);
err = snd_mixer_handle_events(handle);
if (err < 0) {
g_set_error(error_r, alsa_mixer_quark(), err,
"snd_mixer_handle_events() failed: %s",
@ -273,7 +296,7 @@ alsa_mixer_get_volume(struct mixer *mixer, GError **error_r)
return false;
}
err = snd_mixer_selem_get_playback_volume(am->elem,
err = snd_mixer_selem_get_playback_volume(elem,
SND_MIXER_SCHN_FRONT_LEFT,
&level);
if (err < 0) {
@ -283,38 +306,44 @@ alsa_mixer_get_volume(struct mixer *mixer, GError **error_r)
return false;
}
ret = ((am->volume_set / 100.0) * (am->volume_max - am->volume_min)
+ am->volume_min) + 0.5;
if (am->volume_set > 0 && ret == level) {
ret = am->volume_set;
ret = ((volume_set / 100.0) * (volume_max - volume_min)
+ volume_min) + 0.5;
if (volume_set > 0 && ret == level) {
ret = volume_set;
} else {
ret = (int)(100 * (((float)(level - am->volume_min)) /
(am->volume_max - am->volume_min)) + 0.5);
ret = (int)(100 * (((float)(level - volume_min)) /
(volume_max - volume_min)) + 0.5);
}
return ret;
}
static bool
alsa_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r)
static int
alsa_mixer_get_volume(struct mixer *mixer, GError **error_r)
{
AlsaMixer *am = (AlsaMixer *)mixer;
return am->GetVolume(error_r);
}
inline bool
AlsaMixer::SetVolume(unsigned volume, GError **error_r)
{
struct alsa_mixer *am = (struct alsa_mixer *)mixer;
float vol;
long level;
int err;
assert(am->handle != NULL);
assert(handle != NULL);
vol = volume;
am->volume_set = vol + 0.5;
volume_set = vol + 0.5;
level = (long)(((vol / 100.0) * (am->volume_max - am->volume_min) +
am->volume_min) + 0.5);
level = level > am->volume_max ? am->volume_max : level;
level = level < am->volume_min ? am->volume_min : level;
level = (long)(((vol / 100.0) * (volume_max - volume_min) +
volume_min) + 0.5);
level = level > volume_max ? volume_max : level;
level = level < volume_min ? volume_min : level;
err = snd_mixer_selem_set_playback_volume_all(am->elem, level);
err = snd_mixer_selem_set_playback_volume_all(elem, level);
if (err < 0) {
g_set_error(error_r, alsa_mixer_quark(), err,
"failed to set ALSA volume: %s",
@ -325,6 +354,13 @@ alsa_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r)
return true;
}
static bool
alsa_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r)
{
AlsaMixer *am = (AlsaMixer *)mixer;
return am->SetVolume(volume, error_r);
}
const struct mixer_plugin alsa_mixer_plugin = {
alsa_mixer_init,
alsa_mixer_finish,