mixer: migrate to C++ exceptions
This commit is contained in:
parent
ae1eb9ccde
commit
e7d327226a
@ -158,9 +158,11 @@ ReplayGainFilter::Update()
|
|||||||
if (_volume > 100)
|
if (_volume > 100)
|
||||||
_volume = 100;
|
_volume = 100;
|
||||||
|
|
||||||
Error error;
|
try {
|
||||||
if (!mixer_set_volume(mixer, _volume, error))
|
mixer_set_volume(mixer, _volume);
|
||||||
LogError(error, "Failed to update hardware mixer");
|
} catch (const std::runtime_error &e) {
|
||||||
|
LogError(e, "Failed to update hardware mixer");
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
pv.SetVolume(volume);
|
pv.SetVolume(volume);
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,10 @@
|
|||||||
#include "MixerList.hxx"
|
#include "MixerList.hxx"
|
||||||
#include "output/Internal.hxx"
|
#include "output/Internal.hxx"
|
||||||
#include "pcm/Volume.hxx"
|
#include "pcm/Volume.hxx"
|
||||||
#include "util/Error.hxx"
|
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -39,14 +40,14 @@ output_mixer_get_volume(const AudioOutput &ao)
|
|||||||
if (mixer == nullptr)
|
if (mixer == nullptr)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
Error error;
|
try {
|
||||||
int volume = mixer_get_volume(mixer, error);
|
return mixer_get_volume(mixer);
|
||||||
if (volume < 0 && error.IsDefined())
|
} catch (const std::runtime_error &e) {
|
||||||
FormatError(error,
|
FormatError(e,
|
||||||
"Failed to read mixer for '%s'",
|
"Failed to read mixer for '%s'",
|
||||||
ao.name);
|
ao.name);
|
||||||
|
return -1;
|
||||||
return volume;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -81,14 +82,15 @@ output_mixer_set_volume(AudioOutput &ao, unsigned volume)
|
|||||||
if (mixer == nullptr)
|
if (mixer == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Error error;
|
try {
|
||||||
bool success = mixer_set_volume(mixer, volume, error);
|
mixer_set_volume(mixer, volume);
|
||||||
if (!success && error.IsDefined())
|
return true;
|
||||||
FormatError(error,
|
} catch (const std::runtime_error &e) {
|
||||||
|
FormatError(e,
|
||||||
"Failed to set mixer for '%s'",
|
"Failed to set mixer for '%s'",
|
||||||
ao.name);
|
ao.name);
|
||||||
|
return false;
|
||||||
return success;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -114,7 +116,7 @@ output_mixer_get_software_volume(const AudioOutput &ao)
|
|||||||
if (mixer == nullptr || !mixer->IsPlugin(software_mixer_plugin))
|
if (mixer == nullptr || !mixer->IsPlugin(software_mixer_plugin))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return mixer_get_volume(mixer, IgnoreError());
|
return mixer_get_volume(mixer);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -148,6 +150,6 @@ MultipleOutputs::SetSoftwareVolume(unsigned volume)
|
|||||||
if (mixer != nullptr &&
|
if (mixer != nullptr &&
|
||||||
(&mixer->plugin == &software_mixer_plugin ||
|
(&mixer->plugin == &software_mixer_plugin ||
|
||||||
&mixer->plugin == &null_mixer_plugin))
|
&mixer->plugin == &null_mixer_plugin))
|
||||||
mixer_set_volume(mixer, volume, IgnoreError());
|
mixer_set_volume(mixer, volume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "MixerControl.hxx"
|
#include "MixerControl.hxx"
|
||||||
#include "MixerInternal.hxx"
|
#include "MixerInternal.hxx"
|
||||||
#include "util/Error.hxx"
|
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
@ -28,10 +27,9 @@ Mixer *
|
|||||||
mixer_new(EventLoop &event_loop,
|
mixer_new(EventLoop &event_loop,
|
||||||
const MixerPlugin &plugin, AudioOutput &ao,
|
const MixerPlugin &plugin, AudioOutput &ao,
|
||||||
MixerListener &listener,
|
MixerListener &listener,
|
||||||
const ConfigBlock &block,
|
const ConfigBlock &block)
|
||||||
Error &error)
|
|
||||||
{
|
{
|
||||||
Mixer *mixer = plugin.init(event_loop, ao, listener, block, error);
|
Mixer *mixer = plugin.init(event_loop, ao, listener, block);
|
||||||
|
|
||||||
assert(mixer == nullptr || mixer->IsPlugin(plugin));
|
assert(mixer == nullptr || mixer->IsPlugin(plugin));
|
||||||
|
|
||||||
@ -50,20 +48,24 @@ mixer_free(Mixer *mixer)
|
|||||||
delete mixer;
|
delete mixer;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
mixer_open(Mixer *mixer, Error &error)
|
mixer_open(Mixer *mixer)
|
||||||
{
|
{
|
||||||
bool success;
|
|
||||||
|
|
||||||
assert(mixer != nullptr);
|
assert(mixer != nullptr);
|
||||||
|
|
||||||
const ScopeLock protect(mixer->mutex);
|
const ScopeLock protect(mixer->mutex);
|
||||||
|
|
||||||
success = mixer->open || (mixer->open = mixer->Open(error));
|
if (mixer->open)
|
||||||
|
return;
|
||||||
|
|
||||||
mixer->failed = !success;
|
try {
|
||||||
|
mixer->Open();
|
||||||
return success;
|
mixer->open = true;
|
||||||
|
mixer->failed = false;
|
||||||
|
} catch (...) {
|
||||||
|
mixer->failed = true;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -109,39 +111,41 @@ mixer_failed(Mixer *mixer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
mixer_get_volume(Mixer *mixer, Error &error)
|
mixer_get_volume(Mixer *mixer)
|
||||||
{
|
{
|
||||||
int volume;
|
int volume;
|
||||||
|
|
||||||
assert(mixer != nullptr);
|
assert(mixer != nullptr);
|
||||||
|
|
||||||
if (mixer->plugin.global && !mixer->failed &&
|
if (mixer->plugin.global && !mixer->failed)
|
||||||
!mixer_open(mixer, error))
|
mixer_open(mixer);
|
||||||
return -1;
|
|
||||||
|
|
||||||
const ScopeLock protect(mixer->mutex);
|
const ScopeLock protect(mixer->mutex);
|
||||||
|
|
||||||
if (mixer->open) {
|
if (mixer->open) {
|
||||||
volume = mixer->GetVolume(error);
|
try {
|
||||||
if (volume < 0 && error.IsDefined())
|
volume = mixer->GetVolume();
|
||||||
|
} catch (...) {
|
||||||
mixer_failed(mixer);
|
mixer_failed(mixer);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
volume = -1;
|
volume = -1;
|
||||||
|
|
||||||
return volume;
|
return volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
mixer_set_volume(Mixer *mixer, unsigned volume, Error &error)
|
mixer_set_volume(Mixer *mixer, unsigned volume)
|
||||||
{
|
{
|
||||||
assert(mixer != nullptr);
|
assert(mixer != nullptr);
|
||||||
assert(volume <= 100);
|
assert(volume <= 100);
|
||||||
|
|
||||||
if (mixer->plugin.global && !mixer->failed &&
|
if (mixer->plugin.global && !mixer->failed)
|
||||||
!mixer_open(mixer, error))
|
mixer_open(mixer);
|
||||||
return false;
|
|
||||||
|
|
||||||
const ScopeLock protect(mixer->mutex);
|
const ScopeLock protect(mixer->mutex);
|
||||||
|
|
||||||
return mixer->open && mixer->SetVolume(volume, error);
|
if (mixer->open)
|
||||||
|
mixer->SetVolume(volume);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
#ifndef MPD_MIXER_CONTROL_HXX
|
#ifndef MPD_MIXER_CONTROL_HXX
|
||||||
#define MPD_MIXER_CONTROL_HXX
|
#define MPD_MIXER_CONTROL_HXX
|
||||||
|
|
||||||
class Error;
|
|
||||||
class Mixer;
|
class Mixer;
|
||||||
class EventLoop;
|
class EventLoop;
|
||||||
struct AudioOutput;
|
struct AudioOutput;
|
||||||
@ -33,17 +32,22 @@ struct MixerPlugin;
|
|||||||
class MixerListener;
|
class MixerListener;
|
||||||
struct ConfigBlock;
|
struct ConfigBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws std::runtime_error on error.
|
||||||
|
*/
|
||||||
Mixer *
|
Mixer *
|
||||||
mixer_new(EventLoop &event_loop, const MixerPlugin &plugin, AudioOutput &ao,
|
mixer_new(EventLoop &event_loop, const MixerPlugin &plugin, AudioOutput &ao,
|
||||||
MixerListener &listener,
|
MixerListener &listener,
|
||||||
const ConfigBlock &block,
|
const ConfigBlock &block);
|
||||||
Error &error);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
mixer_free(Mixer *mixer);
|
mixer_free(Mixer *mixer);
|
||||||
|
|
||||||
bool
|
/**
|
||||||
mixer_open(Mixer *mixer, Error &error);
|
* Throws std::runtime_error on error.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
mixer_open(Mixer *mixer);
|
||||||
|
|
||||||
void
|
void
|
||||||
mixer_close(Mixer *mixer);
|
mixer_close(Mixer *mixer);
|
||||||
@ -55,10 +59,16 @@ mixer_close(Mixer *mixer);
|
|||||||
void
|
void
|
||||||
mixer_auto_close(Mixer *mixer);
|
mixer_auto_close(Mixer *mixer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws std::runtime_error on error.
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
mixer_get_volume(Mixer *mixer, Error &error);
|
mixer_get_volume(Mixer *mixer);
|
||||||
|
|
||||||
bool
|
/**
|
||||||
mixer_set_volume(Mixer *mixer, unsigned volume, Error &error);
|
* Throws std::runtime_error on error.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
mixer_set_volume(Mixer *mixer, unsigned volume);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -67,9 +67,9 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Open mixer device
|
* Open mixer device
|
||||||
*
|
*
|
||||||
* @return true on success, false on error
|
* Throws std::runtime_error on error.
|
||||||
*/
|
*/
|
||||||
virtual bool Open(Error &error) = 0;
|
virtual void Open() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close mixer device
|
* Close mixer device
|
||||||
@ -79,19 +79,22 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Reads the current volume.
|
* Reads the current volume.
|
||||||
*
|
*
|
||||||
|
* Throws std::runtime_error on error.
|
||||||
|
*
|
||||||
* @return the current volume (0..100 including) or -1 if
|
* @return the current volume (0..100 including) or -1 if
|
||||||
* unavailable or on error (error set, mixer will be closed)
|
* unavailable
|
||||||
*/
|
*/
|
||||||
gcc_pure
|
gcc_pure
|
||||||
virtual int GetVolume(Error &error) = 0;
|
virtual int GetVolume() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the volume.
|
* Sets the volume.
|
||||||
*
|
*
|
||||||
* @param volume the new volume (0..100 including) @return
|
* Throws std::runtime_error on error.
|
||||||
* true on success, false on error
|
*
|
||||||
|
* @param volume the new volume (0..100 including)
|
||||||
*/
|
*/
|
||||||
virtual bool SetVolume(unsigned volume, Error &error) = 0;
|
virtual void SetVolume(unsigned volume) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -32,22 +32,20 @@ struct AudioOutput;
|
|||||||
class Mixer;
|
class Mixer;
|
||||||
class MixerListener;
|
class MixerListener;
|
||||||
class EventLoop;
|
class EventLoop;
|
||||||
class Error;
|
|
||||||
|
|
||||||
struct MixerPlugin {
|
struct MixerPlugin {
|
||||||
/**
|
/**
|
||||||
* Alocates and configures a mixer device.
|
* Alocates and configures a mixer device.
|
||||||
*
|
*
|
||||||
|
* Throws std::runtime_error on error.
|
||||||
|
*
|
||||||
* @param ao the associated AudioOutput
|
* @param ao the associated AudioOutput
|
||||||
* @param param the configuration section
|
* @param param the configuration section
|
||||||
* @param error_r location to store the error occurring, or
|
* @return a mixer object
|
||||||
* nullptr to ignore errors
|
|
||||||
* @return a mixer object, or nullptr on error
|
|
||||||
*/
|
*/
|
||||||
Mixer *(*init)(EventLoop &event_loop, AudioOutput &ao,
|
Mixer *(*init)(EventLoop &event_loop, AudioOutput &ao,
|
||||||
MixerListener &listener,
|
MixerListener &listener,
|
||||||
const ConfigBlock &block,
|
const ConfigBlock &block);
|
||||||
Error &error);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If true, then the mixer is automatically opened, even if
|
* If true, then the mixer is automatically opened, even if
|
||||||
|
@ -26,8 +26,8 @@
|
|||||||
#include "util/ASCII.hxx"
|
#include "util/ASCII.hxx"
|
||||||
#include "util/ReusableArray.hxx"
|
#include "util/ReusableArray.hxx"
|
||||||
#include "util/Clamp.hxx"
|
#include "util/Clamp.hxx"
|
||||||
#include "util/Error.hxx"
|
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
|
#include "util/RuntimeError.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -82,13 +82,13 @@ public:
|
|||||||
virtual ~AlsaMixer();
|
virtual ~AlsaMixer();
|
||||||
|
|
||||||
void Configure(const ConfigBlock &block);
|
void Configure(const ConfigBlock &block);
|
||||||
bool Setup(Error &error);
|
void Setup();
|
||||||
|
|
||||||
/* virtual methods from class Mixer */
|
/* virtual methods from class Mixer */
|
||||||
virtual bool Open(Error &error) override;
|
void Open() override;
|
||||||
virtual void Close() override;
|
void Close() override;
|
||||||
virtual int GetVolume(Error &error) override;
|
int GetVolume() override;
|
||||||
virtual bool SetVolume(unsigned volume, Error &error) override;
|
void SetVolume(unsigned volume) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr Domain alsa_mixer_domain("alsa_mixer");
|
static constexpr Domain alsa_mixer_domain("alsa_mixer");
|
||||||
@ -148,8 +148,11 @@ alsa_mixer_elem_callback(snd_mixer_elem_t *elem, unsigned mask)
|
|||||||
snd_mixer_elem_get_callback_private(elem);
|
snd_mixer_elem_get_callback_private(elem);
|
||||||
|
|
||||||
if (mask & SND_CTL_EVENT_MASK_VALUE) {
|
if (mask & SND_CTL_EVENT_MASK_VALUE) {
|
||||||
int volume = mixer.GetVolume(IgnoreError());
|
try {
|
||||||
mixer.listener.OnMixerVolumeChanged(mixer, volume);
|
int volume = mixer.GetVolume();
|
||||||
|
mixer.listener.OnMixerVolumeChanged(mixer, volume);
|
||||||
|
} catch (const std::runtime_error &) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -174,8 +177,7 @@ AlsaMixer::Configure(const ConfigBlock &block)
|
|||||||
static Mixer *
|
static Mixer *
|
||||||
alsa_mixer_init(EventLoop &event_loop, gcc_unused AudioOutput &ao,
|
alsa_mixer_init(EventLoop &event_loop, gcc_unused AudioOutput &ao,
|
||||||
MixerListener &listener,
|
MixerListener &listener,
|
||||||
const ConfigBlock &block,
|
const ConfigBlock &block)
|
||||||
gcc_unused Error &error)
|
|
||||||
{
|
{
|
||||||
AlsaMixer *am = new AlsaMixer(event_loop, listener);
|
AlsaMixer *am = new AlsaMixer(event_loop, listener);
|
||||||
am->Configure(block);
|
am->Configure(block);
|
||||||
@ -205,39 +207,26 @@ alsa_mixer_lookup_elem(snd_mixer_t *handle, const char *name, unsigned idx)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline void
|
||||||
AlsaMixer::Setup(Error &error)
|
AlsaMixer::Setup()
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if ((err = snd_mixer_attach(handle, device)) < 0) {
|
if ((err = snd_mixer_attach(handle, device)) < 0)
|
||||||
error.Format(alsa_mixer_domain, err,
|
throw FormatRuntimeError("failed to attach to %s: %s",
|
||||||
"failed to attach to %s: %s",
|
device, snd_strerror(err));
|
||||||
device, snd_strerror(err));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = snd_mixer_selem_register(handle, nullptr,
|
if ((err = snd_mixer_selem_register(handle, nullptr, nullptr)) < 0)
|
||||||
nullptr)) < 0) {
|
throw FormatRuntimeError("snd_mixer_selem_register() failed: %s",
|
||||||
error.Format(alsa_mixer_domain, err,
|
snd_strerror(err));
|
||||||
"snd_mixer_selem_register() failed: %s",
|
|
||||||
snd_strerror(err));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = snd_mixer_load(handle)) < 0) {
|
if ((err = snd_mixer_load(handle)) < 0)
|
||||||
error.Format(alsa_mixer_domain, err,
|
throw FormatRuntimeError("snd_mixer_load() failed: %s\n",
|
||||||
"snd_mixer_load() failed: %s\n",
|
snd_strerror(err));
|
||||||
snd_strerror(err));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
elem = alsa_mixer_lookup_elem(handle, control, index);
|
elem = alsa_mixer_lookup_elem(handle, control, index);
|
||||||
if (elem == nullptr) {
|
if (elem == nullptr)
|
||||||
error.Format(alsa_mixer_domain, 0,
|
throw FormatRuntimeError("no such mixer control: %s", control);
|
||||||
"no such mixer control: %s", control);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_mixer_selem_get_playback_volume_range(elem, &volume_min,
|
snd_mixer_selem_get_playback_volume_range(elem, &volume_min,
|
||||||
&volume_max);
|
&volume_max);
|
||||||
@ -246,33 +235,29 @@ AlsaMixer::Setup(Error &error)
|
|||||||
snd_mixer_elem_set_callback(elem, alsa_mixer_elem_callback);
|
snd_mixer_elem_set_callback(elem, alsa_mixer_elem_callback);
|
||||||
|
|
||||||
monitor = new AlsaMixerMonitor(event_loop, handle);
|
monitor = new AlsaMixerMonitor(event_loop, handle);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
void
|
||||||
AlsaMixer::Open(Error &error)
|
AlsaMixer::Open()
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
volume_set = -1;
|
volume_set = -1;
|
||||||
|
|
||||||
err = snd_mixer_open(&handle, 0);
|
err = snd_mixer_open(&handle, 0);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
error.Format(alsa_mixer_domain, err,
|
throw FormatRuntimeError("snd_mixer_open() failed: %s",
|
||||||
"snd_mixer_open() failed: %s", snd_strerror(err));
|
snd_strerror(err));
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Setup(error)) {
|
try {
|
||||||
|
Setup();
|
||||||
|
} catch (...) {
|
||||||
snd_mixer_close(handle);
|
snd_mixer_close(handle);
|
||||||
return false;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
void
|
||||||
AlsaMixer::Close()
|
AlsaMixer::Close()
|
||||||
{
|
{
|
||||||
assert(handle != nullptr);
|
assert(handle != nullptr);
|
||||||
@ -283,8 +268,8 @@ AlsaMixer::Close()
|
|||||||
snd_mixer_close(handle);
|
snd_mixer_close(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int
|
int
|
||||||
AlsaMixer::GetVolume(Error &error)
|
AlsaMixer::GetVolume()
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
int ret;
|
int ret;
|
||||||
@ -293,22 +278,16 @@ AlsaMixer::GetVolume(Error &error)
|
|||||||
assert(handle != nullptr);
|
assert(handle != nullptr);
|
||||||
|
|
||||||
err = snd_mixer_handle_events(handle);
|
err = snd_mixer_handle_events(handle);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
error.Format(alsa_mixer_domain, err,
|
throw FormatRuntimeError("snd_mixer_handle_events() failed: %s",
|
||||||
"snd_mixer_handle_events() failed: %s",
|
snd_strerror(err));
|
||||||
snd_strerror(err));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = snd_mixer_selem_get_playback_volume(elem,
|
err = snd_mixer_selem_get_playback_volume(elem,
|
||||||
SND_MIXER_SCHN_FRONT_LEFT,
|
SND_MIXER_SCHN_FRONT_LEFT,
|
||||||
&level);
|
&level);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
error.Format(alsa_mixer_domain, err,
|
throw FormatRuntimeError("failed to read ALSA volume: %s",
|
||||||
"failed to read ALSA volume: %s",
|
snd_strerror(err));
|
||||||
snd_strerror(err));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = ((volume_set / 100.0) * (volume_max - volume_min)
|
ret = ((volume_set / 100.0) * (volume_max - volume_min)
|
||||||
+ volume_min) + 0.5;
|
+ volume_min) + 0.5;
|
||||||
@ -322,8 +301,8 @@ AlsaMixer::GetVolume(Error &error)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
void
|
||||||
AlsaMixer::SetVolume(unsigned volume, Error &error)
|
AlsaMixer::SetVolume(unsigned volume)
|
||||||
{
|
{
|
||||||
float vol;
|
float vol;
|
||||||
long level;
|
long level;
|
||||||
@ -340,14 +319,9 @@ AlsaMixer::SetVolume(unsigned volume, Error &error)
|
|||||||
level = Clamp(level, volume_min, volume_max);
|
level = Clamp(level, volume_min, volume_max);
|
||||||
|
|
||||||
err = snd_mixer_selem_set_playback_volume_all(elem, level);
|
err = snd_mixer_selem_set_playback_volume_all(elem, level);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
error.Format(alsa_mixer_domain, err,
|
throw FormatRuntimeError("failed to set ALSA volume: %s",
|
||||||
"failed to set ALSA volume: %s",
|
snd_strerror(err));
|
||||||
snd_strerror(err));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MixerPlugin alsa_mixer_plugin = {
|
const MixerPlugin alsa_mixer_plugin = {
|
||||||
|
@ -34,20 +34,18 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* virtual methods from class Mixer */
|
/* virtual methods from class Mixer */
|
||||||
bool Open(gcc_unused Error &error) override {
|
void Open() override {
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Close() override {
|
void Close() override {
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetVolume(gcc_unused Error &error) override {
|
int GetVolume() override {
|
||||||
return volume;
|
return volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SetVolume(unsigned _volume, gcc_unused Error &error) override {
|
void SetVolume(unsigned _volume) override {
|
||||||
volume = _volume;
|
volume = _volume;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -55,8 +53,7 @@ static Mixer *
|
|||||||
null_mixer_init(gcc_unused EventLoop &event_loop,
|
null_mixer_init(gcc_unused EventLoop &event_loop,
|
||||||
gcc_unused AudioOutput &ao,
|
gcc_unused AudioOutput &ao,
|
||||||
MixerListener &listener,
|
MixerListener &listener,
|
||||||
gcc_unused const ConfigBlock &block,
|
gcc_unused const ConfigBlock &block)
|
||||||
gcc_unused Error &error)
|
|
||||||
{
|
{
|
||||||
return new NullMixer(listener);
|
return new NullMixer(listener);
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,10 @@
|
|||||||
#include "mixer/MixerInternal.hxx"
|
#include "mixer/MixerInternal.hxx"
|
||||||
#include "config/Block.hxx"
|
#include "config/Block.hxx"
|
||||||
#include "system/fd_util.h"
|
#include "system/fd_util.h"
|
||||||
|
#include "system/Error.hxx"
|
||||||
#include "util/ASCII.hxx"
|
#include "util/ASCII.hxx"
|
||||||
#include "util/Error.hxx"
|
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
|
#include "util/RuntimeError.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@ -49,16 +50,18 @@ class OssMixer final : public Mixer {
|
|||||||
int volume_control;
|
int volume_control;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
OssMixer(MixerListener &_listener)
|
OssMixer(MixerListener &_listener, const ConfigBlock &block)
|
||||||
:Mixer(oss_mixer_plugin, _listener) {}
|
:Mixer(oss_mixer_plugin, _listener) {
|
||||||
|
Configure(block);
|
||||||
|
}
|
||||||
|
|
||||||
bool Configure(const ConfigBlock &block, Error &error);
|
void Configure(const ConfigBlock &block);
|
||||||
|
|
||||||
/* virtual methods from class Mixer */
|
/* virtual methods from class Mixer */
|
||||||
virtual bool Open(Error &error) override;
|
void Open() override;
|
||||||
virtual void Close() override;
|
void Close() override;
|
||||||
virtual int GetVolume(Error &error) override;
|
int GetVolume() override;
|
||||||
virtual bool SetVolume(unsigned volume, Error &error) override;
|
void SetVolume(unsigned volume) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr Domain oss_mixer_domain("oss_mixer");
|
static constexpr Domain oss_mixer_domain("oss_mixer");
|
||||||
@ -78,39 +81,27 @@ oss_find_mixer(const char *name)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline void
|
||||||
OssMixer::Configure(const ConfigBlock &block, Error &error)
|
OssMixer::Configure(const ConfigBlock &block)
|
||||||
{
|
{
|
||||||
device = block.GetBlockValue("mixer_device", VOLUME_MIXER_OSS_DEFAULT);
|
device = block.GetBlockValue("mixer_device", VOLUME_MIXER_OSS_DEFAULT);
|
||||||
control = block.GetBlockValue("mixer_control");
|
control = block.GetBlockValue("mixer_control");
|
||||||
|
|
||||||
if (control != NULL) {
|
if (control != NULL) {
|
||||||
volume_control = oss_find_mixer(control);
|
volume_control = oss_find_mixer(control);
|
||||||
if (volume_control < 0) {
|
if (volume_control < 0)
|
||||||
error.Format(oss_mixer_domain, 0,
|
throw FormatRuntimeError("no such mixer control: %s",
|
||||||
"no such mixer control: %s", control);
|
control);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else
|
} else
|
||||||
volume_control = SOUND_MIXER_PCM;
|
volume_control = SOUND_MIXER_PCM;
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Mixer *
|
static Mixer *
|
||||||
oss_mixer_init(gcc_unused EventLoop &event_loop, gcc_unused AudioOutput &ao,
|
oss_mixer_init(gcc_unused EventLoop &event_loop, gcc_unused AudioOutput &ao,
|
||||||
MixerListener &listener,
|
MixerListener &listener,
|
||||||
const ConfigBlock &block,
|
const ConfigBlock &block)
|
||||||
Error &error)
|
|
||||||
{
|
{
|
||||||
OssMixer *om = new OssMixer(listener);
|
return new OssMixer(listener, block);
|
||||||
|
|
||||||
if (!om->Configure(block, error)) {
|
|
||||||
delete om;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return om;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -121,38 +112,32 @@ OssMixer::Close()
|
|||||||
close(device_fd);
|
close(device_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
OssMixer::Open(Error &error)
|
OssMixer::Open()
|
||||||
{
|
{
|
||||||
device_fd = open_cloexec(device, O_RDONLY, 0);
|
device_fd = open_cloexec(device, O_RDONLY, 0);
|
||||||
if (device_fd < 0) {
|
if (device_fd < 0)
|
||||||
error.FormatErrno("failed to open %s", device);
|
throw FormatErrno("failed to open %s", device);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (control) {
|
try {
|
||||||
int devmask = 0;
|
if (control) {
|
||||||
|
int devmask = 0;
|
||||||
|
|
||||||
if (ioctl(device_fd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
|
if (ioctl(device_fd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0)
|
||||||
error.SetErrno("READ_DEVMASK failed");
|
throw MakeErrno("READ_DEVMASK failed");
|
||||||
Close();
|
|
||||||
return false;
|
if (((1 << volume_control) & devmask) == 0)
|
||||||
}
|
throw FormatErrno("mixer control \"%s\" not usable",
|
||||||
|
control);
|
||||||
if (((1 << volume_control) & devmask) == 0) {
|
|
||||||
error.Format(oss_mixer_domain, 0,
|
|
||||||
"mixer control \"%s\" not usable",
|
|
||||||
control);
|
|
||||||
Close();
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
} catch (...) {
|
||||||
|
Close();
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
OssMixer::GetVolume(Error &error)
|
OssMixer::GetVolume()
|
||||||
{
|
{
|
||||||
int left, right, level;
|
int left, right, level;
|
||||||
int ret;
|
int ret;
|
||||||
@ -160,10 +145,8 @@ OssMixer::GetVolume(Error &error)
|
|||||||
assert(device_fd >= 0);
|
assert(device_fd >= 0);
|
||||||
|
|
||||||
ret = ioctl(device_fd, MIXER_READ(volume_control), &level);
|
ret = ioctl(device_fd, MIXER_READ(volume_control), &level);
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
error.SetErrno("failed to read OSS volume");
|
throw MakeErrno("failed to read OSS volume");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
left = level & 0xff;
|
left = level & 0xff;
|
||||||
right = (level & 0xff00) >> 8;
|
right = (level & 0xff00) >> 8;
|
||||||
@ -177,24 +160,18 @@ OssMixer::GetVolume(Error &error)
|
|||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
OssMixer::SetVolume(unsigned volume, Error &error)
|
OssMixer::SetVolume(unsigned volume)
|
||||||
{
|
{
|
||||||
int level;
|
int level;
|
||||||
int ret;
|
|
||||||
|
|
||||||
assert(device_fd >= 0);
|
assert(device_fd >= 0);
|
||||||
assert(volume <= 100);
|
assert(volume <= 100);
|
||||||
|
|
||||||
level = (volume << 8) + volume;
|
level = (volume << 8) + volume;
|
||||||
|
|
||||||
ret = ioctl(device_fd, MIXER_WRITE(volume_control), &level);
|
if (ioctl(device_fd, MIXER_WRITE(volume_control), &level) < 0)
|
||||||
if (ret < 0) {
|
throw MakeErrno("failed to set OSS volume");
|
||||||
error.SetErrno("failed to set OSS volume");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MixerPlugin oss_mixer_plugin = {
|
const MixerPlugin oss_mixer_plugin = {
|
||||||
|
@ -25,13 +25,14 @@
|
|||||||
#include "mixer/MixerInternal.hxx"
|
#include "mixer/MixerInternal.hxx"
|
||||||
#include "mixer/Listener.hxx"
|
#include "mixer/Listener.hxx"
|
||||||
#include "output/plugins/PulseOutputPlugin.hxx"
|
#include "output/plugins/PulseOutputPlugin.hxx"
|
||||||
#include "util/Error.hxx"
|
|
||||||
|
|
||||||
#include <pulse/context.h>
|
#include <pulse/context.h>
|
||||||
#include <pulse/introspect.h>
|
#include <pulse/introspect.h>
|
||||||
#include <pulse/stream.h>
|
#include <pulse/stream.h>
|
||||||
#include <pulse/subscribe.h>
|
#include <pulse/subscribe.h>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
class PulseMixer final : public Mixer {
|
class PulseMixer final : public Mixer {
|
||||||
@ -52,18 +53,17 @@ public:
|
|||||||
void Offline();
|
void Offline();
|
||||||
void VolumeCallback(const pa_sink_input_info *i, int eol);
|
void VolumeCallback(const pa_sink_input_info *i, int eol);
|
||||||
void Update(pa_context *context, pa_stream *stream);
|
void Update(pa_context *context, pa_stream *stream);
|
||||||
int GetVolumeInternal(Error &error);
|
int GetVolumeInternal();
|
||||||
|
|
||||||
/* virtual methods from class Mixer */
|
/* virtual methods from class Mixer */
|
||||||
bool Open(gcc_unused Error &error) override {
|
void Open() override {
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Close() override {
|
void Close() override {
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetVolume(Error &error) override;
|
int GetVolume() override;
|
||||||
bool SetVolume(unsigned volume, Error &error) override;
|
void SetVolume(unsigned volume) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -91,7 +91,7 @@ PulseMixer::VolumeCallback(const pa_sink_input_info *i, int eol)
|
|||||||
online = true;
|
online = true;
|
||||||
volume = i->volume;
|
volume = i->volume;
|
||||||
|
|
||||||
listener.OnMixerVolumeChanged(*this, GetVolumeInternal(IgnoreError()));
|
listener.OnMixerVolumeChanged(*this, GetVolumeInternal());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -163,8 +163,7 @@ pulse_mixer_on_change(PulseMixer &pm,
|
|||||||
static Mixer *
|
static Mixer *
|
||||||
pulse_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao,
|
pulse_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao,
|
||||||
MixerListener &listener,
|
MixerListener &listener,
|
||||||
gcc_unused const ConfigBlock &block,
|
gcc_unused const ConfigBlock &block)
|
||||||
gcc_unused Error &error)
|
|
||||||
{
|
{
|
||||||
PulseOutput &po = (PulseOutput &)ao;
|
PulseOutput &po = (PulseOutput &)ao;
|
||||||
PulseMixer *pm = new PulseMixer(po, listener);
|
PulseMixer *pm = new PulseMixer(po, listener);
|
||||||
@ -180,42 +179,37 @@ PulseMixer::~PulseMixer()
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
PulseMixer::GetVolume(gcc_unused Error &error)
|
PulseMixer::GetVolume()
|
||||||
{
|
{
|
||||||
Pulse::LockGuard lock(pulse_output_get_mainloop(output));
|
Pulse::LockGuard lock(pulse_output_get_mainloop(output));
|
||||||
|
|
||||||
return GetVolumeInternal(error);
|
return GetVolumeInternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pulse mainloop lock must be held by caller
|
* Pulse mainloop lock must be held by caller
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
PulseMixer::GetVolumeInternal(gcc_unused Error &error)
|
PulseMixer::GetVolumeInternal()
|
||||||
{
|
{
|
||||||
return online ?
|
return online ?
|
||||||
(int)((100 * (pa_cvolume_avg(&volume) + 1)) / PA_VOLUME_NORM)
|
(int)((100 * (pa_cvolume_avg(&volume) + 1)) / PA_VOLUME_NORM)
|
||||||
: -1;
|
: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
PulseMixer::SetVolume(unsigned new_volume, Error &error)
|
PulseMixer::SetVolume(unsigned new_volume)
|
||||||
{
|
{
|
||||||
Pulse::LockGuard lock(pulse_output_get_mainloop(output));
|
Pulse::LockGuard lock(pulse_output_get_mainloop(output));
|
||||||
|
|
||||||
if (!online) {
|
if (!online)
|
||||||
error.Set(pulse_domain, "disconnected");
|
throw std::runtime_error("disconnected");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct pa_cvolume cvolume;
|
struct pa_cvolume cvolume;
|
||||||
pa_cvolume_set(&cvolume, volume.channels,
|
pa_cvolume_set(&cvolume, volume.channels,
|
||||||
(new_volume * PA_VOLUME_NORM + 50) / 100);
|
(new_volume * PA_VOLUME_NORM + 50) / 100);
|
||||||
bool success = pulse_output_set_volume(output, &cvolume, error);
|
pulse_output_set_volume(output, &cvolume);
|
||||||
if (success)
|
volume = cvolume;
|
||||||
volume = cvolume;
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MixerPlugin pulse_mixer_plugin = {
|
const MixerPlugin pulse_mixer_plugin = {
|
||||||
|
@ -34,36 +34,34 @@ public:
|
|||||||
self(_output) {}
|
self(_output) {}
|
||||||
|
|
||||||
/* virtual methods from class Mixer */
|
/* virtual methods from class Mixer */
|
||||||
virtual bool Open(gcc_unused Error &error) override {
|
void Open() override {
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Close() override {
|
virtual void Close() override {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int GetVolume(Error &error) override;
|
int GetVolume() override;
|
||||||
virtual bool SetVolume(unsigned volume, Error &error) override;
|
void SetVolume(unsigned volume) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Mixer *
|
static Mixer *
|
||||||
roar_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao,
|
roar_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao,
|
||||||
MixerListener &listener,
|
MixerListener &listener,
|
||||||
gcc_unused const ConfigBlock &block,
|
gcc_unused const ConfigBlock &block)
|
||||||
gcc_unused Error &error)
|
|
||||||
{
|
{
|
||||||
return new RoarMixer((RoarOutput &)ao, listener);
|
return new RoarMixer((RoarOutput &)ao, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
RoarMixer::GetVolume(gcc_unused Error &error)
|
RoarMixer::GetVolume()
|
||||||
{
|
{
|
||||||
return roar_output_get_volume(self);
|
return roar_output_get_volume(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
RoarMixer::SetVolume(unsigned volume, gcc_unused Error &error)
|
RoarMixer::SetVolume(unsigned volume)
|
||||||
{
|
{
|
||||||
return roar_output_set_volume(self, volume);
|
roar_output_set_volume(self, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
const MixerPlugin roar_mixer_plugin = {
|
const MixerPlugin roar_mixer_plugin = {
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
#include "filter/plugins/VolumeFilterPlugin.hxx"
|
#include "filter/plugins/VolumeFilterPlugin.hxx"
|
||||||
#include "pcm/Volume.hxx"
|
#include "pcm/Volume.hxx"
|
||||||
#include "config/Block.hxx"
|
#include "config/Block.hxx"
|
||||||
#include "util/Error.hxx"
|
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
@ -48,26 +47,24 @@ public:
|
|||||||
void SetFilter(Filter *_filter);
|
void SetFilter(Filter *_filter);
|
||||||
|
|
||||||
/* virtual methods from class Mixer */
|
/* virtual methods from class Mixer */
|
||||||
virtual bool Open(gcc_unused Error &error) override {
|
void Open() override {
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Close() override {
|
virtual void Close() override {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int GetVolume(gcc_unused Error &error) override {
|
int GetVolume() override {
|
||||||
return volume;
|
return volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool SetVolume(unsigned volume, Error &error) override;
|
void SetVolume(unsigned volume) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Mixer *
|
static Mixer *
|
||||||
software_mixer_init(gcc_unused EventLoop &event_loop,
|
software_mixer_init(gcc_unused EventLoop &event_loop,
|
||||||
gcc_unused AudioOutput &ao,
|
gcc_unused AudioOutput &ao,
|
||||||
MixerListener &listener,
|
MixerListener &listener,
|
||||||
gcc_unused const ConfigBlock &block,
|
gcc_unused const ConfigBlock &block)
|
||||||
gcc_unused Error &error)
|
|
||||||
{
|
{
|
||||||
return new SoftwareMixer(listener);
|
return new SoftwareMixer(listener);
|
||||||
}
|
}
|
||||||
@ -87,8 +84,8 @@ PercentVolumeToSoftwareVolume(unsigned volume)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
SoftwareMixer::SetVolume(unsigned new_volume, gcc_unused Error &error)
|
SoftwareMixer::SetVolume(unsigned new_volume)
|
||||||
{
|
{
|
||||||
assert(new_volume <= 100);
|
assert(new_volume <= 100);
|
||||||
|
|
||||||
@ -96,7 +93,6 @@ SoftwareMixer::SetVolume(unsigned new_volume, gcc_unused Error &error)
|
|||||||
|
|
||||||
if (filter != nullptr)
|
if (filter != nullptr)
|
||||||
volume_filter_set(filter, PercentVolumeToSoftwareVolume(new_volume));
|
volume_filter_set(filter, PercentVolumeToSoftwareVolume(new_volume));
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MixerPlugin software_mixer_plugin = {
|
const MixerPlugin software_mixer_plugin = {
|
||||||
|
@ -21,11 +21,11 @@
|
|||||||
#include "mixer/MixerInternal.hxx"
|
#include "mixer/MixerInternal.hxx"
|
||||||
#include "output/OutputAPI.hxx"
|
#include "output/OutputAPI.hxx"
|
||||||
#include "output/plugins/WinmmOutputPlugin.hxx"
|
#include "output/plugins/WinmmOutputPlugin.hxx"
|
||||||
#include "util/Error.hxx"
|
|
||||||
#include "util/Domain.hxx"
|
|
||||||
|
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@ -40,19 +40,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* virtual methods from class Mixer */
|
/* virtual methods from class Mixer */
|
||||||
virtual bool Open(gcc_unused Error &error) override {
|
void Open() override {
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Close() override {
|
void Close() override {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int GetVolume(Error &error) override;
|
int GetVolume() override;
|
||||||
virtual bool SetVolume(unsigned volume, Error &error) override;
|
void SetVolume(unsigned volume) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr Domain winmm_mixer_domain("winmm_mixer");
|
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
winmm_volume_decode(DWORD volume)
|
winmm_volume_decode(DWORD volume)
|
||||||
{
|
{
|
||||||
@ -69,40 +66,33 @@ winmm_volume_encode(int volume)
|
|||||||
static Mixer *
|
static Mixer *
|
||||||
winmm_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao,
|
winmm_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao,
|
||||||
MixerListener &listener,
|
MixerListener &listener,
|
||||||
gcc_unused const ConfigBlock &block,
|
gcc_unused const ConfigBlock &block)
|
||||||
gcc_unused Error &error)
|
|
||||||
{
|
{
|
||||||
return new WinmmMixer((WinmmOutput &)ao, listener);
|
return new WinmmMixer((WinmmOutput &)ao, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
WinmmMixer::GetVolume(Error &error)
|
WinmmMixer::GetVolume()
|
||||||
{
|
{
|
||||||
DWORD volume;
|
DWORD volume;
|
||||||
HWAVEOUT handle = winmm_output_get_handle(output);
|
HWAVEOUT handle = winmm_output_get_handle(output);
|
||||||
MMRESULT result = waveOutGetVolume(handle, &volume);
|
MMRESULT result = waveOutGetVolume(handle, &volume);
|
||||||
|
|
||||||
if (result != MMSYSERR_NOERROR) {
|
if (result != MMSYSERR_NOERROR)
|
||||||
error.Set(winmm_mixer_domain, "Failed to get winmm volume");
|
throw std::runtime_error("Failed to get winmm volume");
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return winmm_volume_decode(volume);
|
return winmm_volume_decode(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
WinmmMixer::SetVolume(unsigned volume, Error &error)
|
WinmmMixer::SetVolume(unsigned volume)
|
||||||
{
|
{
|
||||||
DWORD value = winmm_volume_encode(volume);
|
DWORD value = winmm_volume_encode(volume);
|
||||||
HWAVEOUT handle = winmm_output_get_handle(output);
|
HWAVEOUT handle = winmm_output_get_handle(output);
|
||||||
MMRESULT result = waveOutSetVolume(handle, value);
|
MMRESULT result = waveOutSetVolume(handle, value);
|
||||||
|
|
||||||
if (result != MMSYSERR_NOERROR) {
|
if (result != MMSYSERR_NOERROR)
|
||||||
error.Set(winmm_mixer_domain, "Failed to set winmm volume");
|
throw std::runtime_error("Failed to set winmm volume");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MixerPlugin winmm_mixer_plugin = {
|
const MixerPlugin winmm_mixer_plugin = {
|
||||||
|
@ -115,8 +115,7 @@ audio_output_load_mixer(EventLoop &event_loop, AudioOutput &ao,
|
|||||||
const ConfigBlock &block,
|
const ConfigBlock &block,
|
||||||
const MixerPlugin *plugin,
|
const MixerPlugin *plugin,
|
||||||
PreparedFilter &filter_chain,
|
PreparedFilter &filter_chain,
|
||||||
MixerListener &listener,
|
MixerListener &listener)
|
||||||
Error &error)
|
|
||||||
{
|
{
|
||||||
Mixer *mixer;
|
Mixer *mixer;
|
||||||
|
|
||||||
@ -127,20 +126,19 @@ audio_output_load_mixer(EventLoop &event_loop, AudioOutput &ao,
|
|||||||
|
|
||||||
case MixerType::NULL_:
|
case MixerType::NULL_:
|
||||||
return mixer_new(event_loop, null_mixer_plugin, ao, listener,
|
return mixer_new(event_loop, null_mixer_plugin, ao, listener,
|
||||||
block, error);
|
block);
|
||||||
|
|
||||||
case MixerType::HARDWARE:
|
case MixerType::HARDWARE:
|
||||||
if (plugin == nullptr)
|
if (plugin == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return mixer_new(event_loop, *plugin, ao, listener,
|
return mixer_new(event_loop, *plugin, ao, listener,
|
||||||
block, error);
|
block);
|
||||||
|
|
||||||
case MixerType::SOFTWARE:
|
case MixerType::SOFTWARE:
|
||||||
mixer = mixer_new(event_loop, software_mixer_plugin, ao,
|
mixer = mixer_new(event_loop, software_mixer_plugin, ao,
|
||||||
listener,
|
listener,
|
||||||
ConfigBlock(),
|
ConfigBlock());
|
||||||
IgnoreError());
|
|
||||||
assert(mixer != nullptr);
|
assert(mixer != nullptr);
|
||||||
|
|
||||||
filter_chain_append(filter_chain, "software_mixer",
|
filter_chain_append(filter_chain, "software_mixer",
|
||||||
@ -246,16 +244,16 @@ audio_output_setup(EventLoop &event_loop, AudioOutput &ao,
|
|||||||
|
|
||||||
/* set up the mixer */
|
/* set up the mixer */
|
||||||
|
|
||||||
Error mixer_error;
|
try {
|
||||||
ao.mixer = audio_output_load_mixer(event_loop, ao, block,
|
ao.mixer = audio_output_load_mixer(event_loop, ao, block,
|
||||||
ao.plugin.mixer_plugin,
|
ao.plugin.mixer_plugin,
|
||||||
*ao.prepared_filter,
|
*ao.prepared_filter,
|
||||||
mixer_listener,
|
mixer_listener);
|
||||||
mixer_error);
|
} catch (const std::runtime_error &e) {
|
||||||
if (ao.mixer == nullptr && mixer_error.IsDefined())
|
FormatError(e,
|
||||||
FormatError(mixer_error,
|
|
||||||
"Failed to initialize hardware mixer for '%s'",
|
"Failed to initialize hardware mixer for '%s'",
|
||||||
ao.name);
|
ao.name);
|
||||||
|
}
|
||||||
|
|
||||||
/* use the hardware mixer for replay gain? */
|
/* use the hardware mixer for replay gain? */
|
||||||
|
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
/** after a failure, wait this number of seconds before
|
/** after a failure, wait this number of seconds before
|
||||||
@ -145,10 +147,11 @@ AudioOutput::Open(const AudioFormat audio_format, const MusicPipe &mp)
|
|||||||
const bool open2 = open;
|
const bool open2 = open;
|
||||||
|
|
||||||
if (open2 && mixer != nullptr) {
|
if (open2 && mixer != nullptr) {
|
||||||
Error error;
|
try {
|
||||||
if (!mixer_open(mixer, error))
|
mixer_open(mixer);
|
||||||
FormatWarning(output_domain,
|
} catch (const std::runtime_error &e) {
|
||||||
"Failed to open mixer for '%s'", name);
|
FormatError(e, "Failed to open mixer for '%s'", name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return open2;
|
return open2;
|
||||||
|
@ -37,6 +37,8 @@
|
|||||||
#include <pulse/subscribe.h>
|
#include <pulse/subscribe.h>
|
||||||
#include <pulse/version.h>
|
#include <pulse/version.h>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -74,7 +76,7 @@ public:
|
|||||||
mixer = nullptr;
|
mixer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SetVolume(const pa_cvolume &volume, Error &error);
|
void SetVolume(const pa_cvolume &volume);
|
||||||
|
|
||||||
struct pa_threaded_mainloop *GetMainloop() {
|
struct pa_threaded_mainloop *GetMainloop() {
|
||||||
return mainloop;
|
return mainloop;
|
||||||
@ -217,34 +219,27 @@ pulse_output_clear_mixer(PulseOutput &po, PulseMixer &pm)
|
|||||||
po.ClearMixer(pm);
|
po.ClearMixer(pm);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline void
|
||||||
PulseOutput::SetVolume(const pa_cvolume &volume, Error &error)
|
PulseOutput::SetVolume(const pa_cvolume &volume)
|
||||||
{
|
{
|
||||||
if (context == nullptr || stream == nullptr ||
|
if (context == nullptr || stream == nullptr ||
|
||||||
pa_stream_get_state(stream) != PA_STREAM_READY) {
|
pa_stream_get_state(stream) != PA_STREAM_READY)
|
||||||
error.Set(pulse_domain, "disconnected");
|
throw std::runtime_error("disconnected");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pa_operation *o =
|
pa_operation *o =
|
||||||
pa_context_set_sink_input_volume(context,
|
pa_context_set_sink_input_volume(context,
|
||||||
pa_stream_get_index(stream),
|
pa_stream_get_index(stream),
|
||||||
&volume, nullptr, nullptr);
|
&volume, nullptr, nullptr);
|
||||||
if (o == nullptr) {
|
if (o == nullptr)
|
||||||
SetPulseError(error, context,
|
throw std::runtime_error("failed to set PulseAudio volume");
|
||||||
"failed to set PulseAudio volume");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pa_operation_unref(o);
|
pa_operation_unref(o);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
pulse_output_set_volume(PulseOutput &po, const pa_cvolume *volume,
|
pulse_output_set_volume(PulseOutput &po, const pa_cvolume *volume)
|
||||||
Error &error)
|
|
||||||
{
|
{
|
||||||
return po.SetVolume(*volume, error);
|
return po.SetVolume(*volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,8 +36,7 @@ pulse_output_set_mixer(PulseOutput &po, PulseMixer &pm);
|
|||||||
void
|
void
|
||||||
pulse_output_clear_mixer(PulseOutput &po, PulseMixer &pm);
|
pulse_output_clear_mixer(PulseOutput &po, PulseMixer &pm);
|
||||||
|
|
||||||
bool
|
void
|
||||||
pulse_output_set_volume(PulseOutput &po,
|
pulse_output_set_volume(PulseOutput &po, const pa_cvolume *volume);
|
||||||
const pa_cvolume *volume, Error &error);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
/* libroar/services.h declares roar_service_stream::new - work around
|
/* libroar/services.h declares roar_service_stream::new - work around
|
||||||
this C++ problem */
|
this C++ problem */
|
||||||
@ -74,7 +75,7 @@ public:
|
|||||||
void Cancel();
|
void Cancel();
|
||||||
|
|
||||||
int GetVolume() const;
|
int GetVolume() const;
|
||||||
bool SetVolume(unsigned volume);
|
void SetVolume(unsigned volume);
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr Domain roar_output_domain("roar_output");
|
static constexpr Domain roar_output_domain("roar_output");
|
||||||
@ -90,7 +91,7 @@ RoarOutput::GetVolume() const
|
|||||||
float l, r;
|
float l, r;
|
||||||
int error;
|
int error;
|
||||||
if (roar_vs_volume_get(vss, &l, &r, &error) < 0)
|
if (roar_vs_volume_get(vss, &l, &r, &error) < 0)
|
||||||
return -1;
|
throw std::runtime_error(roar_vs_strerr(error));
|
||||||
|
|
||||||
return (l + r) * 50;
|
return (l + r) * 50;
|
||||||
}
|
}
|
||||||
@ -101,26 +102,26 @@ roar_output_get_volume(RoarOutput &roar)
|
|||||||
return roar.GetVolume();
|
return roar.GetVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
inline void
|
||||||
RoarOutput::SetVolume(unsigned volume)
|
RoarOutput::SetVolume(unsigned volume)
|
||||||
{
|
{
|
||||||
assert(volume <= 100);
|
assert(volume <= 100);
|
||||||
|
|
||||||
const ScopeLock protect(mutex);
|
const ScopeLock protect(mutex);
|
||||||
if (vss == nullptr || !alive)
|
if (vss == nullptr || !alive)
|
||||||
return false;
|
throw std::runtime_error("closed");
|
||||||
|
|
||||||
int error;
|
int error;
|
||||||
float level = volume / 100.0;
|
float level = volume / 100.0;
|
||||||
|
|
||||||
roar_vs_volume_mono(vss, level, &error);
|
if (roar_vs_volume_mono(vss, level, &error) < 0)
|
||||||
return true;
|
throw std::runtime_error(roar_vs_strerr(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
roar_output_set_volume(RoarOutput &roar, unsigned volume)
|
roar_output_set_volume(RoarOutput &roar, unsigned volume)
|
||||||
{
|
{
|
||||||
return roar.SetVolume(volume);
|
roar.SetVolume(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
|
@ -27,7 +27,7 @@ extern const struct AudioOutputPlugin roar_output_plugin;
|
|||||||
int
|
int
|
||||||
roar_output_get_volume(RoarOutput &roar);
|
roar_output_get_volume(RoarOutput &roar);
|
||||||
|
|
||||||
bool
|
void
|
||||||
roar_output_set_volume(RoarOutput &roar, unsigned volume);
|
roar_output_set_volume(RoarOutput &roar, unsigned volume);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -41,7 +41,7 @@ filter_plugin_by_name(gcc_unused const char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, gcc_unused char **argv)
|
int main(int argc, gcc_unused char **argv)
|
||||||
{
|
try {
|
||||||
int volume;
|
int volume;
|
||||||
|
|
||||||
if (argc != 2) {
|
if (argc != 2) {
|
||||||
@ -51,36 +51,27 @@ int main(int argc, gcc_unused char **argv)
|
|||||||
|
|
||||||
EventLoop event_loop;
|
EventLoop event_loop;
|
||||||
|
|
||||||
Error error;
|
|
||||||
Mixer *mixer = mixer_new(event_loop, alsa_mixer_plugin,
|
Mixer *mixer = mixer_new(event_loop, alsa_mixer_plugin,
|
||||||
*(AudioOutput *)nullptr,
|
*(AudioOutput *)nullptr,
|
||||||
*(MixerListener *)nullptr,
|
*(MixerListener *)nullptr,
|
||||||
ConfigBlock(), error);
|
ConfigBlock());
|
||||||
if (mixer == NULL) {
|
|
||||||
LogError(error, "mixer_new() failed");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mixer_open(mixer, error)) {
|
mixer_open(mixer);
|
||||||
mixer_free(mixer);
|
|
||||||
LogError(error, "failed to open the mixer");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
volume = mixer_get_volume(mixer, error);
|
volume = mixer_get_volume(mixer);
|
||||||
mixer_close(mixer);
|
mixer_close(mixer);
|
||||||
mixer_free(mixer);
|
mixer_free(mixer);
|
||||||
|
|
||||||
assert(volume >= -1 && volume <= 100);
|
assert(volume >= -1 && volume <= 100);
|
||||||
|
|
||||||
if (volume < 0) {
|
if (volume < 0) {
|
||||||
if (error.IsDefined()) {
|
fprintf(stderr, "failed to read volume\n");
|
||||||
LogError(error, "failed to read volume");
|
|
||||||
} else
|
|
||||||
fprintf(stderr, "failed to read volume\n");
|
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%d\n", volume);
|
printf("%d\n", volume);
|
||||||
return 0;
|
return 0;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
LogError(e);
|
||||||
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -41,11 +41,10 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
bool
|
void
|
||||||
mixer_set_volume(gcc_unused Mixer *mixer,
|
mixer_set_volume(gcc_unused Mixer *mixer,
|
||||||
gcc_unused unsigned volume, gcc_unused Error &error)
|
gcc_unused unsigned volume)
|
||||||
{
|
{
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PreparedFilter *
|
static PreparedFilter *
|
||||||
|
Loading…
Reference in New Issue
Block a user