Mixer: add class MixerListener
Use a listener interface instead of GlobalEvents.
This commit is contained in:
parent
f4f8fa7c94
commit
8d6fedf817
@ -1033,6 +1033,7 @@ MIXER_LIBS = \
|
|||||||
$(PULSE_LIBS)
|
$(PULSE_LIBS)
|
||||||
|
|
||||||
MIXER_API_SRC = \
|
MIXER_API_SRC = \
|
||||||
|
src/mixer/Listener.hxx \
|
||||||
src/mixer/MixerPlugin.hxx \
|
src/mixer/MixerPlugin.hxx \
|
||||||
src/mixer/MixerList.hxx \
|
src/mixer/MixerList.hxx \
|
||||||
src/mixer/MixerControl.cxx src/mixer/MixerControl.hxx \
|
src/mixer/MixerControl.cxx src/mixer/MixerControl.hxx \
|
||||||
|
@ -42,9 +42,6 @@ namespace GlobalEvents {
|
|||||||
/** the current song's tag has changed */
|
/** the current song's tag has changed */
|
||||||
TAG,
|
TAG,
|
||||||
|
|
||||||
/** a hardware mixer plugin has detected a change */
|
|
||||||
MIXER,
|
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
/** shutdown requested */
|
/** shutdown requested */
|
||||||
SHUTDOWN,
|
SHUTDOWN,
|
||||||
|
@ -33,7 +33,6 @@
|
|||||||
#include "client/ClientList.hxx"
|
#include "client/ClientList.hxx"
|
||||||
#include "command/AllCommands.hxx"
|
#include "command/AllCommands.hxx"
|
||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
#include "mixer/Volume.hxx"
|
|
||||||
#include "tag/TagConfig.hxx"
|
#include "tag/TagConfig.hxx"
|
||||||
#include "ReplayGainConfig.hxx"
|
#include "ReplayGainConfig.hxx"
|
||||||
#include "Idle.hxx"
|
#include "Idle.hxx"
|
||||||
@ -469,7 +468,6 @@ int mpd_main(int argc, char *argv[])
|
|||||||
|
|
||||||
command_init();
|
command_init();
|
||||||
initialize_decoder_and_player();
|
initialize_decoder_and_player();
|
||||||
volume_init();
|
|
||||||
initAudioConfig();
|
initAudioConfig();
|
||||||
instance->partition->outputs.Configure(*instance->event_loop,
|
instance->partition->outputs.Configure(*instance->event_loop,
|
||||||
instance->partition->pc);
|
instance->partition->pc);
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
#include "DetachedSong.hxx"
|
#include "DetachedSong.hxx"
|
||||||
#include "output/MultipleOutputs.hxx"
|
#include "output/MultipleOutputs.hxx"
|
||||||
|
#include "mixer/Volume.hxx"
|
||||||
|
#include "Idle.hxx"
|
||||||
|
|
||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
|
|
||||||
@ -47,3 +49,12 @@ Partition::SyncWithPlayer()
|
|||||||
{
|
{
|
||||||
playlist.SyncWithPlayer(pc);
|
playlist.SyncWithPlayer(pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Partition::OnMixerVolumeChanged(gcc_unused Mixer &mixer, gcc_unused int volume)
|
||||||
|
{
|
||||||
|
InvalidateHardwareVolume();
|
||||||
|
|
||||||
|
/* notify clients */
|
||||||
|
idle_add(IDLE_MIXER);
|
||||||
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "Playlist.hxx"
|
#include "Playlist.hxx"
|
||||||
#include "output/MultipleOutputs.hxx"
|
#include "output/MultipleOutputs.hxx"
|
||||||
|
#include "mixer/Listener.hxx"
|
||||||
#include "PlayerControl.hxx"
|
#include "PlayerControl.hxx"
|
||||||
|
|
||||||
struct Instance;
|
struct Instance;
|
||||||
@ -32,7 +33,7 @@ class SongLoader;
|
|||||||
* A partition of the Music Player Daemon. It is a separate unit with
|
* A partition of the Music Player Daemon. It is a separate unit with
|
||||||
* a playlist, a player, outputs etc.
|
* a playlist, a player, outputs etc.
|
||||||
*/
|
*/
|
||||||
struct Partition {
|
struct Partition final : private MixerListener {
|
||||||
Instance &instance;
|
Instance &instance;
|
||||||
|
|
||||||
struct playlist playlist;
|
struct playlist playlist;
|
||||||
@ -46,6 +47,7 @@ struct Partition {
|
|||||||
unsigned buffer_chunks,
|
unsigned buffer_chunks,
|
||||||
unsigned buffered_before_play)
|
unsigned buffered_before_play)
|
||||||
:instance(_instance), playlist(max_length),
|
:instance(_instance), playlist(max_length),
|
||||||
|
outputs(*this),
|
||||||
pc(outputs, buffer_chunks, buffered_before_play) {}
|
pc(outputs, buffer_chunks, buffered_before_play) {}
|
||||||
|
|
||||||
void ClearQueue() {
|
void ClearQueue() {
|
||||||
@ -188,6 +190,10 @@ struct Partition {
|
|||||||
* Synchronize the player with the play queue.
|
* Synchronize the player with the play queue.
|
||||||
*/
|
*/
|
||||||
void SyncWithPlayer();
|
void SyncWithPlayer();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* virtual methods from class MixerListener */
|
||||||
|
virtual void OnMixerVolumeChanged(Mixer &mixer, int volume) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
34
src/mixer/Listener.hxx
Normal file
34
src/mixer/Listener.hxx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2014 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 MPD_MIXER_LISTENER_HXX
|
||||||
|
#define MPD_MIXER_LISTENER_HXX
|
||||||
|
|
||||||
|
class Mixer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface that listens on events from mixer plugins. The
|
||||||
|
* methods must be thread-safe and non-blocking.
|
||||||
|
*/
|
||||||
|
class MixerListener {
|
||||||
|
public:
|
||||||
|
virtual void OnMixerVolumeChanged(Mixer &mixer, int volume) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -27,10 +27,11 @@
|
|||||||
Mixer *
|
Mixer *
|
||||||
mixer_new(EventLoop &event_loop,
|
mixer_new(EventLoop &event_loop,
|
||||||
const MixerPlugin &plugin, AudioOutput &ao,
|
const MixerPlugin &plugin, AudioOutput &ao,
|
||||||
|
MixerListener &listener,
|
||||||
const config_param ¶m,
|
const config_param ¶m,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
Mixer *mixer = plugin.init(event_loop, ao, param, error);
|
Mixer *mixer = plugin.init(event_loop, ao, listener, param, error);
|
||||||
|
|
||||||
assert(mixer == nullptr || mixer->IsPlugin(plugin));
|
assert(mixer == nullptr || mixer->IsPlugin(plugin));
|
||||||
|
|
||||||
|
@ -30,10 +30,12 @@ class Mixer;
|
|||||||
class EventLoop;
|
class EventLoop;
|
||||||
struct AudioOutput;
|
struct AudioOutput;
|
||||||
struct MixerPlugin;
|
struct MixerPlugin;
|
||||||
|
class MixerListener;
|
||||||
struct config_param;
|
struct config_param;
|
||||||
|
|
||||||
Mixer *
|
Mixer *
|
||||||
mixer_new(EventLoop &event_loop, const MixerPlugin &plugin, AudioOutput &ao,
|
mixer_new(EventLoop &event_loop, const MixerPlugin &plugin, AudioOutput &ao,
|
||||||
|
MixerListener &listener,
|
||||||
const config_param ¶m,
|
const config_param ¶m,
|
||||||
Error &error);
|
Error &error);
|
||||||
|
|
||||||
|
@ -25,10 +25,14 @@
|
|||||||
#include "thread/Mutex.hxx"
|
#include "thread/Mutex.hxx"
|
||||||
#include "Compiler.h"
|
#include "Compiler.h"
|
||||||
|
|
||||||
|
class MixerListener;
|
||||||
|
|
||||||
class Mixer {
|
class Mixer {
|
||||||
public:
|
public:
|
||||||
const MixerPlugin &plugin;
|
const MixerPlugin &plugin;
|
||||||
|
|
||||||
|
MixerListener &listener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This mutex protects all of the mixer struct, including its
|
* This mutex protects all of the mixer struct, including its
|
||||||
* implementation, so plugins don't have to deal with that.
|
* implementation, so plugins don't have to deal with that.
|
||||||
@ -47,8 +51,8 @@ public:
|
|||||||
bool failed;
|
bool failed;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Mixer(const MixerPlugin &_plugin)
|
explicit Mixer(const MixerPlugin &_plugin, MixerListener &_listener)
|
||||||
:plugin(_plugin),
|
:plugin(_plugin), listener(_listener),
|
||||||
open(false),
|
open(false),
|
||||||
failed(false) {}
|
failed(false) {}
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
struct config_param;
|
struct config_param;
|
||||||
struct AudioOutput;
|
struct AudioOutput;
|
||||||
class Mixer;
|
class Mixer;
|
||||||
|
class MixerListener;
|
||||||
class EventLoop;
|
class EventLoop;
|
||||||
class Error;
|
class Error;
|
||||||
|
|
||||||
@ -44,6 +45,7 @@ struct MixerPlugin {
|
|||||||
* @return a mixer object, or nullptr on error
|
* @return a mixer object, or nullptr on error
|
||||||
*/
|
*/
|
||||||
Mixer *(*init)(EventLoop &event_loop, AudioOutput &ao,
|
Mixer *(*init)(EventLoop &event_loop, AudioOutput &ao,
|
||||||
|
MixerListener &listener,
|
||||||
const config_param ¶m,
|
const config_param ¶m,
|
||||||
Error &error);
|
Error &error);
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
#include "Volume.hxx"
|
#include "Volume.hxx"
|
||||||
#include "output/MultipleOutputs.hxx"
|
#include "output/MultipleOutputs.hxx"
|
||||||
#include "Idle.hxx"
|
#include "Idle.hxx"
|
||||||
#include "GlobalEvents.hxx"
|
|
||||||
#include "util/StringUtil.hxx"
|
#include "util/StringUtil.hxx"
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
#include "system/PeriodClock.hxx"
|
#include "system/PeriodClock.hxx"
|
||||||
@ -41,22 +40,11 @@ static int last_hardware_volume = -1;
|
|||||||
/** the age of #last_hardware_volume */
|
/** the age of #last_hardware_volume */
|
||||||
static PeriodClock hardware_volume_clock;
|
static PeriodClock hardware_volume_clock;
|
||||||
|
|
||||||
/**
|
void
|
||||||
* Handler for #GlobalEvents::MIXER.
|
InvalidateHardwareVolume()
|
||||||
*/
|
|
||||||
static void
|
|
||||||
mixer_event_callback(void)
|
|
||||||
{
|
{
|
||||||
/* flush the hardware volume cache */
|
/* flush the hardware volume cache */
|
||||||
last_hardware_volume = -1;
|
last_hardware_volume = -1;
|
||||||
|
|
||||||
/* notify clients */
|
|
||||||
idle_add(IDLE_MIXER);
|
|
||||||
}
|
|
||||||
|
|
||||||
void volume_init(void)
|
|
||||||
{
|
|
||||||
GlobalEvents::Register(GlobalEvents::MIXER, mixer_event_callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -26,7 +26,8 @@
|
|||||||
|
|
||||||
class MultipleOutputs;
|
class MultipleOutputs;
|
||||||
|
|
||||||
void volume_init(void);
|
void
|
||||||
|
InvalidateHardwareVolume();
|
||||||
|
|
||||||
gcc_pure
|
gcc_pure
|
||||||
int
|
int
|
||||||
|
@ -19,8 +19,8 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "mixer/MixerInternal.hxx"
|
#include "mixer/MixerInternal.hxx"
|
||||||
|
#include "mixer/Listener.hxx"
|
||||||
#include "output/OutputAPI.hxx"
|
#include "output/OutputAPI.hxx"
|
||||||
#include "GlobalEvents.hxx"
|
|
||||||
#include "event/MultiSocketMonitor.hxx"
|
#include "event/MultiSocketMonitor.hxx"
|
||||||
#include "event/DeferredMonitor.hxx"
|
#include "event/DeferredMonitor.hxx"
|
||||||
#include "event/Loop.hxx"
|
#include "event/Loop.hxx"
|
||||||
@ -76,8 +76,9 @@ class AlsaMixer final : public Mixer {
|
|||||||
AlsaMixerMonitor *monitor;
|
AlsaMixerMonitor *monitor;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AlsaMixer(EventLoop &_event_loop)
|
AlsaMixer(EventLoop &_event_loop, MixerListener &_listener)
|
||||||
:Mixer(alsa_mixer_plugin), event_loop(_event_loop) {}
|
:Mixer(alsa_mixer_plugin, _listener),
|
||||||
|
event_loop(_event_loop) {}
|
||||||
|
|
||||||
virtual ~AlsaMixer();
|
virtual ~AlsaMixer();
|
||||||
|
|
||||||
@ -142,10 +143,15 @@ AlsaMixerMonitor::DispatchSockets()
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
alsa_mixer_elem_callback(gcc_unused snd_mixer_elem_t *elem, unsigned mask)
|
alsa_mixer_elem_callback(snd_mixer_elem_t *elem, unsigned mask)
|
||||||
{
|
{
|
||||||
if (mask & SND_CTL_EVENT_MASK_VALUE)
|
AlsaMixer &mixer = *(AlsaMixer *)
|
||||||
GlobalEvents::Emit(GlobalEvents::MIXER);
|
snd_mixer_elem_get_callback_private(elem);
|
||||||
|
|
||||||
|
if (mask & SND_CTL_EVENT_MASK_VALUE) {
|
||||||
|
int volume = mixer.GetVolume(IgnoreError());
|
||||||
|
mixer.listener.OnMixerVolumeChanged(mixer, volume);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -168,10 +174,11 @@ AlsaMixer::Configure(const config_param ¶m)
|
|||||||
|
|
||||||
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,
|
||||||
const config_param ¶m,
|
const config_param ¶m,
|
||||||
gcc_unused Error &error)
|
gcc_unused Error &error)
|
||||||
{
|
{
|
||||||
AlsaMixer *am = new AlsaMixer(event_loop);
|
AlsaMixer *am = new AlsaMixer(event_loop, listener);
|
||||||
am->Configure(param);
|
am->Configure(param);
|
||||||
|
|
||||||
return am;
|
return am;
|
||||||
@ -236,6 +243,7 @@ AlsaMixer::Setup(Error &error)
|
|||||||
snd_mixer_selem_get_playback_volume_range(elem, &volume_min,
|
snd_mixer_selem_get_playback_volume_range(elem, &volume_min,
|
||||||
&volume_max);
|
&volume_max);
|
||||||
|
|
||||||
|
snd_mixer_elem_set_callback_private(elem, this);
|
||||||
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);
|
||||||
|
@ -49,7 +49,8 @@ class OssMixer final : public Mixer {
|
|||||||
int volume_control;
|
int volume_control;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
OssMixer():Mixer(oss_mixer_plugin) {}
|
OssMixer(MixerListener &_listener)
|
||||||
|
:Mixer(oss_mixer_plugin, _listener) {}
|
||||||
|
|
||||||
bool Configure(const config_param ¶m, Error &error);
|
bool Configure(const config_param ¶m, Error &error);
|
||||||
|
|
||||||
@ -98,10 +99,11 @@ OssMixer::Configure(const config_param ¶m, Error &error)
|
|||||||
|
|
||||||
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,
|
||||||
const config_param ¶m,
|
const config_param ¶m,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
OssMixer *om = new OssMixer();
|
OssMixer *om = new OssMixer(listener);
|
||||||
|
|
||||||
if (!om->Configure(param, error)) {
|
if (!om->Configure(param, error)) {
|
||||||
delete om;
|
delete om;
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "PulseMixerPlugin.hxx"
|
#include "PulseMixerPlugin.hxx"
|
||||||
#include "mixer/MixerInternal.hxx"
|
#include "mixer/MixerInternal.hxx"
|
||||||
|
#include "mixer/Listener.hxx"
|
||||||
#include "output/plugins/PulseOutputPlugin.hxx"
|
#include "output/plugins/PulseOutputPlugin.hxx"
|
||||||
#include "GlobalEvents.hxx"
|
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
@ -41,9 +41,9 @@ class PulseMixer final : public Mixer {
|
|||||||
struct pa_cvolume volume;
|
struct pa_cvolume volume;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PulseMixer(PulseOutput &_output)
|
PulseMixer(PulseOutput &_output, MixerListener &_listener)
|
||||||
:Mixer(pulse_mixer_plugin),
|
:Mixer(pulse_mixer_plugin, _listener),
|
||||||
output(_output), online(false)
|
output(_output), online(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ PulseMixer::Offline()
|
|||||||
|
|
||||||
online = false;
|
online = false;
|
||||||
|
|
||||||
GlobalEvents::Emit(GlobalEvents::MIXER);
|
listener.OnMixerVolumeChanged(*this, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
@ -92,7 +92,7 @@ PulseMixer::VolumeCallback(const pa_sink_input_info *i, int eol)
|
|||||||
online = true;
|
online = true;
|
||||||
volume = i->volume;
|
volume = i->volume;
|
||||||
|
|
||||||
GlobalEvents::Emit(GlobalEvents::MIXER);
|
listener.OnMixerVolumeChanged(*this, GetVolume(IgnoreError()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -165,11 +165,12 @@ 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,
|
||||||
gcc_unused const config_param ¶m,
|
gcc_unused const config_param ¶m,
|
||||||
gcc_unused Error &error)
|
gcc_unused Error &error)
|
||||||
{
|
{
|
||||||
PulseOutput &po = (PulseOutput &)ao;
|
PulseOutput &po = (PulseOutput &)ao;
|
||||||
PulseMixer *pm = new PulseMixer(po);
|
PulseMixer *pm = new PulseMixer(po, listener);
|
||||||
|
|
||||||
pulse_output_set_mixer(po, *pm);
|
pulse_output_set_mixer(po, *pm);
|
||||||
|
|
||||||
|
@ -29,9 +29,9 @@ class RoarMixer final : public Mixer {
|
|||||||
RoarOutput &self;
|
RoarOutput &self;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RoarMixer(RoarOutput &_output)
|
RoarMixer(RoarOutput &_output, MixerListener &_listener)
|
||||||
:Mixer(roar_mixer_plugin),
|
:Mixer(roar_mixer_plugin, _listener),
|
||||||
self(_output) {}
|
self(_output) {}
|
||||||
|
|
||||||
/* virtual methods from class Mixer */
|
/* virtual methods from class Mixer */
|
||||||
virtual bool Open(gcc_unused Error &error) override {
|
virtual bool Open(gcc_unused Error &error) override {
|
||||||
@ -47,10 +47,11 @@ public:
|
|||||||
|
|
||||||
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,
|
||||||
gcc_unused const config_param ¶m,
|
gcc_unused const config_param ¶m,
|
||||||
gcc_unused Error &error)
|
gcc_unused Error &error)
|
||||||
{
|
{
|
||||||
return new RoarMixer((RoarOutput &)ao);
|
return new RoarMixer((RoarOutput &)ao, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -52,8 +52,8 @@ class SoftwareMixer final : public Mixer {
|
|||||||
unsigned volume;
|
unsigned volume;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SoftwareMixer()
|
SoftwareMixer(MixerListener &_listener)
|
||||||
:Mixer(software_mixer_plugin),
|
:Mixer(software_mixer_plugin, _listener),
|
||||||
filter(CreateVolumeFilter()),
|
filter(CreateVolumeFilter()),
|
||||||
owns_filter(true),
|
owns_filter(true),
|
||||||
volume(100)
|
volume(100)
|
||||||
@ -86,10 +86,11 @@ public:
|
|||||||
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,
|
||||||
gcc_unused const config_param ¶m,
|
gcc_unused const config_param ¶m,
|
||||||
gcc_unused Error &error)
|
gcc_unused Error &error)
|
||||||
{
|
{
|
||||||
return new SoftwareMixer();
|
return new SoftwareMixer(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -34,8 +34,8 @@ class WinmmMixer final : public Mixer {
|
|||||||
WinmmOutput &output;
|
WinmmOutput &output;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WinmmMixer(WinmmOutput &_output)
|
WinmmMixer(WinmmOutput &_output, MixerListener &_listener)
|
||||||
:Mixer(winmm_mixer_plugin),
|
:Mixer(winmm_mixer_plugin, _listener),
|
||||||
output(_output) {
|
output(_output) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,10 +68,11 @@ 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,
|
||||||
gcc_unused const config_param ¶m,
|
gcc_unused const config_param ¶m,
|
||||||
gcc_unused Error &error)
|
gcc_unused Error &error)
|
||||||
{
|
{
|
||||||
return new WinmmMixer((WinmmOutput &)ao);
|
return new WinmmMixer((WinmmOutput &)ao, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -116,6 +116,7 @@ audio_output_load_mixer(EventLoop &event_loop, AudioOutput &ao,
|
|||||||
const config_param ¶m,
|
const config_param ¶m,
|
||||||
const MixerPlugin *plugin,
|
const MixerPlugin *plugin,
|
||||||
Filter &filter_chain,
|
Filter &filter_chain,
|
||||||
|
MixerListener &listener,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
Mixer *mixer;
|
Mixer *mixer;
|
||||||
@ -129,10 +130,12 @@ audio_output_load_mixer(EventLoop &event_loop, AudioOutput &ao,
|
|||||||
if (plugin == nullptr)
|
if (plugin == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return mixer_new(event_loop, *plugin, ao, param, error);
|
return mixer_new(event_loop, *plugin, ao, listener,
|
||||||
|
param, error);
|
||||||
|
|
||||||
case MIXER_TYPE_SOFTWARE:
|
case MIXER_TYPE_SOFTWARE:
|
||||||
mixer = mixer_new(event_loop, software_mixer_plugin, ao,
|
mixer = mixer_new(event_loop, software_mixer_plugin, ao,
|
||||||
|
listener,
|
||||||
config_param(),
|
config_param(),
|
||||||
IgnoreError());
|
IgnoreError());
|
||||||
assert(mixer != nullptr);
|
assert(mixer != nullptr);
|
||||||
@ -212,6 +215,7 @@ AudioOutput::Configure(const config_param ¶m, Error &error)
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
audio_output_setup(EventLoop &event_loop, AudioOutput &ao,
|
audio_output_setup(EventLoop &event_loop, AudioOutput &ao,
|
||||||
|
MixerListener &mixer_listener,
|
||||||
const config_param ¶m,
|
const config_param ¶m,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
@ -244,7 +248,9 @@ audio_output_setup(EventLoop &event_loop, AudioOutput &ao,
|
|||||||
Error mixer_error;
|
Error mixer_error;
|
||||||
ao.mixer = audio_output_load_mixer(event_loop, ao, param,
|
ao.mixer = audio_output_load_mixer(event_loop, ao, param,
|
||||||
ao.plugin.mixer_plugin,
|
ao.plugin.mixer_plugin,
|
||||||
*ao.filter, mixer_error);
|
*ao.filter,
|
||||||
|
mixer_listener,
|
||||||
|
mixer_error);
|
||||||
if (ao.mixer == nullptr && mixer_error.IsDefined())
|
if (ao.mixer == nullptr && mixer_error.IsDefined())
|
||||||
FormatError(mixer_error,
|
FormatError(mixer_error,
|
||||||
"Failed to initialize hardware mixer for '%s'",
|
"Failed to initialize hardware mixer for '%s'",
|
||||||
@ -279,6 +285,7 @@ audio_output_setup(EventLoop &event_loop, AudioOutput &ao,
|
|||||||
|
|
||||||
AudioOutput *
|
AudioOutput *
|
||||||
audio_output_new(EventLoop &event_loop, const config_param ¶m,
|
audio_output_new(EventLoop &event_loop, const config_param ¶m,
|
||||||
|
MixerListener &mixer_listener,
|
||||||
PlayerControl &pc,
|
PlayerControl &pc,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
@ -317,7 +324,8 @@ audio_output_new(EventLoop &event_loop, const config_param ¶m,
|
|||||||
if (ao == nullptr)
|
if (ao == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (!audio_output_setup(event_loop, *ao, param, error)) {
|
if (!audio_output_setup(event_loop, *ao, mixer_listener,
|
||||||
|
param, error)) {
|
||||||
ao_plugin_finish(ao);
|
ao_plugin_finish(ao);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,8 @@ class Error;
|
|||||||
class Filter;
|
class Filter;
|
||||||
class MusicPipe;
|
class MusicPipe;
|
||||||
class EventLoop;
|
class EventLoop;
|
||||||
|
class Mixer;
|
||||||
|
class MixerListener;
|
||||||
struct music_chunk;
|
struct music_chunk;
|
||||||
struct config_param;
|
struct config_param;
|
||||||
struct PlayerControl;
|
struct PlayerControl;
|
||||||
@ -79,7 +81,7 @@ struct AudioOutput {
|
|||||||
* May be nullptr if none is available, or if software volume is
|
* May be nullptr if none is available, or if software volume is
|
||||||
* configured.
|
* configured.
|
||||||
*/
|
*/
|
||||||
class Mixer *mixer;
|
Mixer *mixer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will this output receive tags from the decoder? The
|
* Will this output receive tags from the decoder? The
|
||||||
@ -424,6 +426,7 @@ extern struct notify audio_output_client_notify;
|
|||||||
|
|
||||||
AudioOutput *
|
AudioOutput *
|
||||||
audio_output_new(EventLoop &event_loop, const config_param ¶m,
|
audio_output_new(EventLoop &event_loop, const config_param ¶m,
|
||||||
|
MixerListener &mixer_listener,
|
||||||
PlayerControl &pc,
|
PlayerControl &pc,
|
||||||
Error &error);
|
Error &error);
|
||||||
|
|
||||||
|
@ -35,8 +35,9 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
MultipleOutputs::MultipleOutputs()
|
MultipleOutputs::MultipleOutputs(MixerListener &_mixer_listener)
|
||||||
:buffer(nullptr), pipe(nullptr),
|
:mixer_listener(_mixer_listener),
|
||||||
|
buffer(nullptr), pipe(nullptr),
|
||||||
elapsed_time(-1)
|
elapsed_time(-1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -50,10 +51,13 @@ MultipleOutputs::~MultipleOutputs()
|
|||||||
}
|
}
|
||||||
|
|
||||||
static AudioOutput *
|
static AudioOutput *
|
||||||
LoadOutput(EventLoop &event_loop, PlayerControl &pc, const config_param ¶m)
|
LoadOutput(EventLoop &event_loop, MixerListener &mixer_listener,
|
||||||
|
PlayerControl &pc, const config_param ¶m)
|
||||||
{
|
{
|
||||||
Error error;
|
Error error;
|
||||||
AudioOutput *output = audio_output_new(event_loop, param, pc, error);
|
AudioOutput *output = audio_output_new(event_loop, param,
|
||||||
|
mixer_listener,
|
||||||
|
pc, error);
|
||||||
if (output == nullptr) {
|
if (output == nullptr) {
|
||||||
if (param.line > 0)
|
if (param.line > 0)
|
||||||
FormatFatalError("line %i: %s",
|
FormatFatalError("line %i: %s",
|
||||||
@ -72,7 +76,8 @@ MultipleOutputs::Configure(EventLoop &event_loop, PlayerControl &pc)
|
|||||||
const config_param *param = nullptr;
|
const config_param *param = nullptr;
|
||||||
while ((param = config_get_next_param(CONF_AUDIO_OUTPUT,
|
while ((param = config_get_next_param(CONF_AUDIO_OUTPUT,
|
||||||
param)) != nullptr) {
|
param)) != nullptr) {
|
||||||
auto output = LoadOutput(event_loop, pc, *param);
|
auto output = LoadOutput(event_loop, mixer_listener,
|
||||||
|
pc, *param);
|
||||||
if (FindByName(output->name) != nullptr)
|
if (FindByName(output->name) != nullptr)
|
||||||
FormatFatalError("output devices with identical "
|
FormatFatalError("output devices with identical "
|
||||||
"names: %s", output->name);
|
"names: %s", output->name);
|
||||||
@ -83,7 +88,8 @@ MultipleOutputs::Configure(EventLoop &event_loop, PlayerControl &pc)
|
|||||||
if (outputs.empty()) {
|
if (outputs.empty()) {
|
||||||
/* auto-detect device */
|
/* auto-detect device */
|
||||||
const config_param empty;
|
const config_param empty;
|
||||||
auto output = LoadOutput(event_loop, pc, empty);
|
auto output = LoadOutput(event_loop, mixer_listener,
|
||||||
|
pc, empty);
|
||||||
outputs.push_back(output);
|
outputs.push_back(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,12 +38,15 @@ struct AudioFormat;
|
|||||||
class MusicBuffer;
|
class MusicBuffer;
|
||||||
class MusicPipe;
|
class MusicPipe;
|
||||||
class EventLoop;
|
class EventLoop;
|
||||||
|
class MixerListener;
|
||||||
struct music_chunk;
|
struct music_chunk;
|
||||||
struct PlayerControl;
|
struct PlayerControl;
|
||||||
struct AudioOutput;
|
struct AudioOutput;
|
||||||
class Error;
|
class Error;
|
||||||
|
|
||||||
class MultipleOutputs {
|
class MultipleOutputs {
|
||||||
|
MixerListener &mixer_listener;
|
||||||
|
|
||||||
std::vector<AudioOutput *> outputs;
|
std::vector<AudioOutput *> outputs;
|
||||||
|
|
||||||
AudioFormat input_audio_format;
|
AudioFormat input_audio_format;
|
||||||
@ -70,7 +73,7 @@ public:
|
|||||||
* Load audio outputs from the configuration file and
|
* Load audio outputs from the configuration file and
|
||||||
* initialize them.
|
* initialize them.
|
||||||
*/
|
*/
|
||||||
MultipleOutputs();
|
MultipleOutputs(MixerListener &_mixer_listener);
|
||||||
~MultipleOutputs();
|
~MultipleOutputs();
|
||||||
|
|
||||||
void Configure(EventLoop &event_loop, PlayerControl &pc);
|
void Configure(EventLoop &event_loop, PlayerControl &pc);
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
#include "mixer/MixerList.hxx"
|
#include "mixer/MixerList.hxx"
|
||||||
#include "filter/FilterRegistry.hxx"
|
#include "filter/FilterRegistry.hxx"
|
||||||
#include "pcm/Volume.hxx"
|
#include "pcm/Volume.hxx"
|
||||||
#include "GlobalEvents.hxx"
|
|
||||||
#include "Main.hxx"
|
#include "Main.hxx"
|
||||||
#include "event/Loop.hxx"
|
#include "event/Loop.hxx"
|
||||||
#include "config/ConfigData.hxx"
|
#include "config/ConfigData.hxx"
|
||||||
@ -35,11 +34,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
void
|
|
||||||
GlobalEvents::Emit(gcc_unused Event event)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct filter_plugin *
|
const struct filter_plugin *
|
||||||
filter_plugin_by_name(gcc_unused const char *name)
|
filter_plugin_by_name(gcc_unused const char *name)
|
||||||
{
|
{
|
||||||
@ -65,6 +59,7 @@ int main(int argc, gcc_unused char **argv)
|
|||||||
Error error;
|
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,
|
||||||
config_param(), error);
|
config_param(), error);
|
||||||
if (mixer == NULL) {
|
if (mixer == NULL) {
|
||||||
LogError(error, "mixer_new() failed");
|
LogError(error, "mixer_new() failed");
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
#include "Idle.hxx"
|
#include "Idle.hxx"
|
||||||
#include "Main.hxx"
|
#include "Main.hxx"
|
||||||
#include "event/Loop.hxx"
|
#include "event/Loop.hxx"
|
||||||
#include "GlobalEvents.hxx"
|
|
||||||
#include "IOThread.hxx"
|
#include "IOThread.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
#include "AudioParser.hxx"
|
#include "AudioParser.hxx"
|
||||||
@ -47,11 +46,6 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
void
|
|
||||||
GlobalEvents::Emit(gcc_unused Event event)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct filter_plugin *
|
const struct filter_plugin *
|
||||||
filter_plugin_by_name(gcc_unused const char *name)
|
filter_plugin_by_name(gcc_unused const char *name)
|
||||||
{
|
{
|
||||||
@ -95,7 +89,9 @@ load_audio_output(EventLoop &event_loop, const char *name)
|
|||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
AudioOutput *ao =
|
AudioOutput *ao =
|
||||||
audio_output_new(event_loop, *param, dummy_player_control,
|
audio_output_new(event_loop, *param,
|
||||||
|
*(MixerListener *)nullptr,
|
||||||
|
dummy_player_control,
|
||||||
error);
|
error);
|
||||||
if (ao == nullptr)
|
if (ao == nullptr)
|
||||||
LogError(error);
|
LogError(error);
|
||||||
|
Loading…
Reference in New Issue
Block a user