Files
polybar/src/interfaces/alsa.cpp
Michael Carlberg 39d3f61497 refactor(core): Clean-up
- use "#pragma once" instead of the regular include guard
- fix errors and warnings reported by cppcheck
2016-06-02 01:32:06 +02:00

188 lines
5.8 KiB
C++

#include "config.hpp"
#include "interfaces/alsa.hpp"
#include "services/logger.hpp"
#include "utils/config.hpp"
#include "utils/memory.hpp"
#include "utils/proc.hpp"
#include "utils/macros.hpp"
namespace alsa
{
ControlInterface::ControlInterface(int numid)
{
int err;
snd_ctl_elem_info_alloca(&this->info);
snd_ctl_elem_value_alloca(&this->value);
snd_ctl_elem_id_alloca(&this->id);
snd_ctl_elem_id_set_numid(this->id, numid);
snd_ctl_elem_info_set_id(this->info, this->id);
if ((err = snd_ctl_open(&this->ctl, ALSA_SOUNDCARD, SND_CTL_NONBLOCK | SND_CTL_READONLY)) < 0)
throw ControlInterfaceError(err, "Could not open control \""+ ToStr(ALSA_SOUNDCARD) +"\": "+ STRSNDERR(err));
if ((err = snd_ctl_elem_info(this->ctl, this->info)) < 0)
throw ControlInterfaceError(err, "Could not get control data: "+ STRSNDERR(err));
snd_ctl_elem_info_get_id(this->info, this->id);
if ((err = snd_hctl_open(&this->hctl, ALSA_SOUNDCARD, 0)) < 0)
throw ControlInterfaceError(err, STRSNDERR(err));
if ((err = snd_hctl_load(this->hctl)) < 0)
throw ControlInterfaceError(err, STRSNDERR(err));
if ((elem = snd_hctl_find_elem(this->hctl, this->id)) == nullptr)
throw ControlInterfaceError(err, "Could not find control with id "+ IntToStr(snd_ctl_elem_id_get_numid(this->id)));
if ((err = snd_ctl_subscribe_events(this->ctl, 1)) < 0)
throw ControlInterfaceError(err, "Could not subscribe to events: "+ IntToStr(snd_ctl_elem_id_get_numid(this->id)));
log_trace("Successfully initialized control interface");
}
ControlInterface::~ControlInterface() {
std::lock_guard<std::mutex> lck(this->mtx);
snd_ctl_close(this->ctl);
snd_hctl_close(this->hctl);
}
bool ControlInterface::wait(int timeout)
{
std::lock_guard<std::mutex> lck(this->mtx);
int err;
if ((err = snd_ctl_wait(this->ctl, timeout)) < 0)
throw ControlInterfaceError(err, "Failed to wait for events: "+ STRSNDERR(err));
snd_ctl_event_t *event;
snd_ctl_event_alloca(&event);
if ((err = snd_ctl_read(this->ctl, event)) < 0) {
log_trace(err);
return false;
}
if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM)
return false;
auto mask = snd_ctl_event_elem_get_mask(event);
return mask & SND_CTL_EVENT_MASK_VALUE;
}
bool ControlInterface::test_device_plugged()
{
int err;
if (!this->wait(0))
return false;
if ((err = snd_hctl_elem_read(this->elem, this->value)) < 0)
throw ControlInterfaceError(err, "Could not read control value: "+ STRSNDERR(err));
return snd_ctl_elem_value_get_boolean(this->value, 0);
}
Mixer::Mixer(const std::string& mixer_control_name)
{
snd_mixer_selem_id_t *mixer_id;
snd_mixer_selem_id_alloca(&mixer_id);
if (snd_mixer_open(&this->hardware_mixer, 1) < 0)
throw MixerError("Failed to open hardware mixer");
if (snd_mixer_attach(this->hardware_mixer, ALSA_SOUNDCARD) < 0)
throw MixerError("Failed to attach hardware mixer control");
if (snd_mixer_selem_register(this->hardware_mixer, nullptr, nullptr) < 0)
throw MixerError("Failed to register simple mixer element");
if (snd_mixer_load(this->hardware_mixer) < 0)
throw MixerError("Failed to load mixer");
snd_mixer_selem_id_set_index(mixer_id, 0);
snd_mixer_selem_id_set_name(mixer_id, mixer_control_name.c_str());
if ((this->mixer_element = snd_mixer_find_selem(this->hardware_mixer, mixer_id)) == nullptr)
throw MixerError("Cannot find simple element");
log_trace("Successfully initialized mixer");
}
Mixer::~Mixer() {
std::lock_guard<std::mutex> lck(this->mtx);
snd_mixer_elem_remove(this->mixer_element);
snd_mixer_detach(this->hardware_mixer, ALSA_SOUNDCARD);
snd_mixer_close(this->hardware_mixer);
}
bool Mixer::wait(int timeout)
{
std::lock_guard<std::mutex> lck(this->mtx);
int err, pend_n = 0;
if (this->hardware_mixer != nullptr && (err = snd_mixer_wait(this->hardware_mixer, timeout)) < 0)
throw MixerError("Failed to wait for events: "+ STRSNDERR(err));
if (this->hardware_mixer != nullptr && (pend_n = snd_mixer_handle_events(this->hardware_mixer)) < 0)
throw MixerError("Failed to process pending events: "+ STRSNDERR(err));
return pend_n > 0;
}
int Mixer::get_volume()
{
long chan_n = 0, vol_total = 0, vol, vol_min, vol_max;
snd_mixer_selem_get_playback_volume_range(this->mixer_element, &vol_min, &vol_max);
repeat(SND_MIXER_SCHN_LAST)
{
if (snd_mixer_selem_has_playback_channel(this->mixer_element, (snd_mixer_selem_channel_id_t) repeat_i)) {
snd_mixer_selem_get_playback_volume(this->mixer_element, (snd_mixer_selem_channel_id_t) repeat_i, &vol);
vol_total += vol;
chan_n++;
}
}
return (int) 100 * (vol_total / chan_n) / vol_max + 0.5f;
}
void Mixer::set_volume(float percentage)
{
if (this->is_muted())
return;
long vol_min, vol_max;
snd_mixer_selem_get_playback_volume_range(this->mixer_element, &vol_min, &vol_max);
snd_mixer_selem_set_playback_volume_all(this->mixer_element, vol_max * percentage / 100);
}
void Mixer::set_mute(bool mode) {
snd_mixer_selem_set_playback_switch_all(this->mixer_element, mode);
}
void Mixer::toggle_mute()
{
int state;
snd_mixer_selem_get_playback_switch(this->mixer_element, SND_MIXER_SCHN_FRONT_LEFT, &state);
snd_mixer_selem_set_playback_switch_all(this->mixer_element, !state);
}
bool Mixer::is_muted()
{
int state = 0;
repeat(SND_MIXER_SCHN_LAST)
{
if (snd_mixer_selem_has_playback_channel(this->mixer_element, (snd_mixer_selem_channel_id_t) repeat_i)) {
snd_mixer_selem_get_playback_switch(this->mixer_element, SND_MIXER_SCHN_FRONT_LEFT, &state);
if (state == 0)
return true;
}
}
return false;
}
}