Break Linux? Fix volume catcher? Only time will tell

This commit is contained in:
Lirusaito
2019-04-17 18:41:13 -04:00
parent f670c774d3
commit e5f3d1d8bc
2 changed files with 72 additions and 151 deletions

View File

@@ -155,55 +155,40 @@ extern "C" {
} }
class VolumeCatcherImpl class VolumeCatcherImpl : public LLSingleton<VolumeCatcherImpl>
{ {
public: friend LLSingleton<VolumeCatcherImpl>;
VolumeCatcherImpl(); VolumeCatcherImpl();
// This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance.
~VolumeCatcherImpl(); ~VolumeCatcherImpl();
public:
void setVolume(F32 volume); void setVolume(F32 volume);
void pump(void); void setPan(F32 pan) {};
// for internal use - can't be private because used from our C callbacks // for internal use - can't be private because used from our C callbacks
bool loadsyms(std::string pulse_dso_name);
void init();
void cleanup();
void update_all_volumes(F32 volume);
void update_index_volume(U32 index, F32 volume); void update_index_volume(U32 index, F32 volume);
void connected_okay(); void connected_okay();
std::set<U32> mSinkInputIndices; std::set<U32> mSinkInputIndices;
std::map<U32,U32> mSinkInputNumChannels; std::map<U32,U32> mSinkInputNumChannels;
F32 mDesiredVolume; F32 mVolume;
pa_glib_mainloop *mMainloop; pa_glib_mainloop *mMainloop;
pa_context *mPAContext; pa_context *mPAContext;
bool mConnected; bool mConnected;
bool mGotSyms; bool mGotSyms;
F32 mPan;
}; };
VolumeCatcherImpl::VolumeCatcherImpl() VolumeCatcherImpl::VolumeCatcherImpl()
: mDesiredVolume(0.0f), : mVolume(0.0f),
mMainloop(NULL), mMainloop(nullptr),
mPAContext(NULL), mPAContext(nullptr),
mConnected(false), mConnected(false),
mGotSyms(false) mGotSyms(false),
{ mPan(0.f) // default pan is centered
init();
}
VolumeCatcherImpl::~VolumeCatcherImpl()
{
cleanup();
}
bool VolumeCatcherImpl::loadsyms(std::string pulse_dso_name)
{
return grab_pa_syms(pulse_dso_name);
}
void VolumeCatcherImpl::init()
{ {
// try to be as defensive as possible because PA's interface is a // try to be as defensive as possible because PA's interface is a
// bit fragile and (for our purposes) we'd rather simply not function // bit fragile and (for our purposes) we'd rather simply not function
@@ -213,7 +198,7 @@ void VolumeCatcherImpl::init()
// libpulse.so.0 - this isn't a great assumption, and the two DSOs should // libpulse.so.0 - this isn't a great assumption, and the two DSOs should
// probably be loaded separately. Our Linux DSO framework needs refactoring, // probably be loaded separately. Our Linux DSO framework needs refactoring,
// we do this sort of thing a lot with practically identical logic... // we do this sort of thing a lot with practically identical logic...
mGotSyms = loadsyms("libpulse-mainloop-glib.so.0"); mGotSyms = grab_pa_syms("libpulse-mainloop-glib.so.0");
if (!mGotSyms) return; if (!mGotSyms) return;
// better make double-sure glib itself is initialized properly. // better make double-sure glib itself is initialized properly.
@@ -245,7 +230,7 @@ void VolumeCatcherImpl::init()
// PA context to a PA daemon. // PA context to a PA daemon.
if (mPAContext) if (mPAContext)
{ {
llpa_context_set_state_callback(mPAContext, callback_context_state, this); llpa_context_set_state_callback(mPAContext, callback_context_state, nullptr);
pa_context_flags_t cflags = (pa_context_flags)0; // maybe add PA_CONTEXT_NOAUTOSPAWN? pa_context_flags_t cflags = (pa_context_flags)0; // maybe add PA_CONTEXT_NOAUTOSPAWN?
if (llpa_context_connect(mPAContext, NULL, cflags, NULL) >= 0) if (llpa_context_connect(mPAContext, NULL, cflags, NULL) >= 0)
{ {
@@ -260,7 +245,7 @@ void VolumeCatcherImpl::init()
} }
} }
void VolumeCatcherImpl::cleanup() VolumeCatcherImpl::~VolumeCatcherImpl()
{ {
mConnected = false; mConnected = false;
@@ -269,33 +254,30 @@ void VolumeCatcherImpl::cleanup()
llpa_context_disconnect(mPAContext); llpa_context_disconnect(mPAContext);
llpa_context_unref(mPAContext); llpa_context_unref(mPAContext);
} }
mPAContext = NULL; mPAContext = nullptr;
if (mGotSyms && mMainloop) if (mGotSyms && mMainloop)
{ {
llpa_glib_mainloop_free(mMainloop); llpa_glib_mainloop_free(mMainloop);
} }
mMainloop = NULL; mMainloop = nullptr;
} }
void VolumeCatcherImpl::setVolume(F32 volume) void VolumeCatcherImpl::setVolume(F32 volume)
{ {
mDesiredVolume = volume; mVolume = volume;
if (!mGotSyms) return; if (!mGotSyms) return;
if (mConnected && mPAContext) if (mConnected && mPAContext)
{ {
update_all_volumes(mDesiredVolume); for (auto& input : mSinkInputIndices)
{
update_index_volume(input, mVolume);
}
} }
pump(); g_main_context_iteration(g_main_context_default(), /*may_block = */false);
}
void VolumeCatcherImpl::pump()
{
gboolean may_block = FALSE;
g_main_context_iteration(g_main_context_default(), may_block);
} }
void VolumeCatcherImpl::connected_okay() void VolumeCatcherImpl::connected_okay()
@@ -305,7 +287,7 @@ void VolumeCatcherImpl::connected_okay()
// fetch global list of existing sinkinputs // fetch global list of existing sinkinputs
if ((op = llpa_context_get_sink_input_info_list(mPAContext, if ((op = llpa_context_get_sink_input_info_list(mPAContext,
callback_discovered_sinkinput, callback_discovered_sinkinput,
this))) nullptr)))
{ {
llpa_operation_unref(op); llpa_operation_unref(op);
} }
@@ -313,7 +295,7 @@ void VolumeCatcherImpl::connected_okay()
// subscribe to future global sinkinput changes // subscribe to future global sinkinput changes
llpa_context_set_subscribe_callback(mPAContext, llpa_context_set_subscribe_callback(mPAContext,
callback_subscription_alert, callback_subscription_alert,
this); nullptr);
if ((op = llpa_context_subscribe(mPAContext, (pa_subscription_mask_t) if ((op = llpa_context_subscribe(mPAContext, (pa_subscription_mask_t)
(PA_SUBSCRIPTION_MASK_SINK_INPUT), (PA_SUBSCRIPTION_MASK_SINK_INPUT),
NULL, NULL))) NULL, NULL)))
@@ -322,15 +304,6 @@ void VolumeCatcherImpl::connected_okay()
} }
} }
void VolumeCatcherImpl::update_all_volumes(F32 volume)
{
for (std::set<U32>::iterator it = mSinkInputIndices.begin();
it != mSinkInputIndices.end(); ++it)
{
update_index_volume(*it, volume);
}
}
void VolumeCatcherImpl::update_index_volume(U32 index, F32 volume) void VolumeCatcherImpl::update_index_volume(U32 index, F32 volume)
{ {
static pa_cvolume cvol; static pa_cvolume cvol;
@@ -340,10 +313,10 @@ void VolumeCatcherImpl::update_index_volume(U32 index, F32 volume)
pa_context *c = mPAContext; pa_context *c = mPAContext;
uint32_t idx = index; uint32_t idx = index;
const pa_cvolume *cvolumep = &cvol; const pa_cvolume *cvolumep = &cvol;
pa_context_success_cb_t cb = NULL; // okay as null pa_context_success_cb_t cb = nullptr; // okay as null
void *userdata = NULL; // okay as null void* userdata = nullptr; // okay as null
pa_operation *op; pa_operation* op;
if ((op = llpa_context_set_sink_input_volume(c, idx, cvolumep, cb, userdata))) if ((op = llpa_context_set_sink_input_volume(c, idx, cvolumep, cb, userdata)))
{ {
llpa_operation_unref(op); llpa_operation_unref(op);
@@ -353,9 +326,6 @@ void VolumeCatcherImpl::update_index_volume(U32 index, F32 volume)
void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *sii, int eol, void *userdata) void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *sii, int eol, void *userdata)
{ {
VolumeCatcherImpl *impl = dynamic_cast<VolumeCatcherImpl*>((VolumeCatcherImpl*)userdata);
llassert(impl);
if (0 == eol) if (0 == eol)
{ {
pa_proplist *proplist = sii->proplist; pa_proplist *proplist = sii->proplist;
@@ -363,6 +333,7 @@ void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info
if (sinkpid == getpid()) // does the discovered sinkinput belong to this process? if (sinkpid == getpid()) // does the discovered sinkinput belong to this process?
{ {
auto impl = VolumeCatcherImpl::getInstance();
bool is_new = (impl->mSinkInputIndices.find(sii->index) == bool is_new = (impl->mSinkInputIndices.find(sii->index) ==
impl->mSinkInputIndices.end()); impl->mSinkInputIndices.end());
@@ -371,12 +342,7 @@ void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info
if (is_new) if (is_new)
{ {
// new! impl->update_index_volume(sii->index, impl->mVolume);
impl->update_index_volume(sii->index, impl->mDesiredVolume);
}
else
{
// seen it already, do nothing.
} }
} }
} }
@@ -384,8 +350,7 @@ void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info
void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata) void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata)
{ {
VolumeCatcherImpl *impl = dynamic_cast<VolumeCatcherImpl*>((VolumeCatcherImpl*)userdata); VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
llassert(impl);
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
case PA_SUBSCRIPTION_EVENT_SINK_INPUT: case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
@@ -401,15 +366,11 @@ void callback_subscription_alert(pa_context *context, pa_subscription_event_type
{ {
// ask for more info about this new sinkinput // ask for more info about this new sinkinput
pa_operation *op; pa_operation *op;
if ((op = llpa_context_get_sink_input_info(impl->mPAContext, index, callback_discovered_sinkinput, impl))) if ((op = llpa_context_get_sink_input_info(impl->mPAContext, index, callback_discovered_sinkinput, nullptr)))
{ {
llpa_operation_unref(op); llpa_operation_unref(op);
} }
} }
else
{
// property change on this sinkinput - we don't care.
}
break; break;
default:; default:;
@@ -418,8 +379,7 @@ void callback_subscription_alert(pa_context *context, pa_subscription_event_type
void callback_context_state(pa_context *context, void *userdata) void callback_context_state(pa_context *context, void *userdata)
{ {
VolumeCatcherImpl *impl = dynamic_cast<VolumeCatcherImpl*>((VolumeCatcherImpl*)userdata); VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
llassert(impl);
switch (llpa_context_get_state(context)) switch (llpa_context_get_state(context))
{ {
@@ -441,28 +401,25 @@ void callback_context_state(pa_context *context, void *userdata)
VolumeCatcher::VolumeCatcher() VolumeCatcher::VolumeCatcher()
{ {
pimpl = new VolumeCatcherImpl(); pimpl = VolumeCatcherImpl::getInstance();
} }
VolumeCatcher::~VolumeCatcher() VolumeCatcher::~VolumeCatcher()
{ {
delete pimpl; // Let the instance persist until exit.
pimpl = NULL;
} }
void VolumeCatcher::setVolume(F32 volume) void VolumeCatcher::setVolume(F32 volume)
{ {
llassert(pimpl);
pimpl->setVolume(volume); pimpl->setVolume(volume);
} }
void VolumeCatcher::setPan(F32 pan) void VolumeCatcher::setPan(F32 pan)
{ {
// TODO: implement this (if possible) pimpl->setPan(pan);
} }
void VolumeCatcher::pump() void VolumeCatcher::pump()
{ {
llassert(pimpl); g_main_context_iteration(g_main_context_default(), /*may_block = */false);
pimpl->pump();
} }

View File

@@ -45,67 +45,47 @@
struct VolumeCatcherStorage; struct VolumeCatcherStorage;
class VolumeCatcherImpl class VolumeCatcherImpl : public LLSingleton<VolumeCatcherImpl>
{ {
friend LLSingleton<VolumeCatcherImpl>;
VolumeCatcherImpl();
// This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance.
~VolumeCatcherImpl();
public: public:
void setVolume(F32 volume); void setVolume(F32 volume);
void setPan(F32 pan); void setPan(F32 pan);
void setInstanceVolume(VolumeCatcherStorage *instance);
std::list<VolumeCatcherStorage*> mComponentInstances; std::list<VolumeCatcherStorage*> mComponentInstances;
Component mOriginalDefaultOutput; Component mOriginalDefaultOutput;
Component mVolumeAdjuster; Component mVolumeAdjuster;
static VolumeCatcherImpl *getInstance();
private: private:
// This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance.
VolumeCatcherImpl();
static VolumeCatcherImpl *sInstance;
// The singlar instance of this class is expected to last until the process exits.
// To ensure this, we declare the destructor here but never define it, so any code which attempts to destroy the instance will not link.
~VolumeCatcherImpl();
F32 mVolume; F32 mVolume;
F32 mPan; F32 mPan;
}; };
VolumeCatcherImpl *VolumeCatcherImpl::sInstance = NULL;;
struct VolumeCatcherStorage struct VolumeCatcherStorage
{ {
ComponentInstance self; ComponentInstance self, delegate;
ComponentInstance delegate;
}; };
static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage); static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage);
static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self); static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self);
static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self); static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self);
VolumeCatcherImpl *VolumeCatcherImpl::getInstance()
{
if(!sInstance)
{
sInstance = new VolumeCatcherImpl;
}
return sInstance;
}
VolumeCatcherImpl::VolumeCatcherImpl() VolumeCatcherImpl::VolumeCatcherImpl()
: mVolume(1.0f), // default volume is max
mPan(0.f) // default pan is centered
{ {
mVolume = 1.0; // default to full volume
mPan = 0.0; // and center pan
ComponentDescription desc; ComponentDescription desc;
desc.componentType = kAudioUnitType_Output; desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_DefaultOutput; desc.componentSubType = kAudioUnitSubType_DefaultOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0; desc.componentFlags = 0;
desc.componentFlagsMask = 0; desc.componentFlagsMask = 0;
// Find the original default output component // Find the original default output component
mOriginalDefaultOutput = FindNextComponent(NULL, &desc); mOriginalDefaultOutput = FindNextComponent(NULL, &desc);
@@ -114,7 +94,6 @@ VolumeCatcherImpl::VolumeCatcherImpl()
// Capture the original component, so we always get found instead. // Capture the original component, so we always get found instead.
CaptureComponent(mOriginalDefaultOutput, mVolumeAdjuster); CaptureComponent(mOriginalDefaultOutput, mVolumeAdjuster);
} }
static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage) static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage)
@@ -146,20 +125,18 @@ static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, H
static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self) static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self)
{ {
ComponentResult result = noErr;
VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance(); VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
storage = new VolumeCatcherStorage; storage = new VolumeCatcherStorage;
storage->self = self; storage->self = self;
storage->delegate = NULL; storage->delegate = nullptr;
result = OpenAComponent(impl->mOriginalDefaultOutput, &(storage->delegate)); ComponentResult result = OpenAComponent(impl->mOriginalDefaultOutput, &(storage->delegate));
if(result != noErr) if(result != noErr)
{ {
// std::cerr << "OpenAComponent result = " << result << ", component ref = " << storage->delegate << std::endl; // std::cerr << "OpenAComponent result = " << result << ", component ref = " << storage->delegate << std::endl;
// If we failed to open the delagate component, our open is going to fail. Clean things up. // If we failed to open the delagate component, our open is going to fail. Clean things up.
delete storage; delete storage;
} }
@@ -172,7 +149,7 @@ static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *stora
impl->mComponentInstances.push_back(storage); impl->mComponentInstances.push_back(storage);
// and set up the initial volume // and set up the initial volume
impl->setInstanceVolume(storage); impl->setVolume(storage);
} }
return result; return result;
@@ -180,14 +157,12 @@ static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *stora
static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self) static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self)
{ {
ComponentResult result = noErr;
if(storage) if(storage)
{ {
if(storage->delegate) if(storage->delegate)
{ {
CloseComponent(storage->delegate); CloseComponent(storage->delegate);
storage->delegate = NULL; storage->delegate = nullptr;
} }
VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance(); VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
@@ -195,18 +170,30 @@ static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *stor
delete[] storage; delete[] storage;
} }
return result; return noErr;
} }
void VolumeCatcherImpl::setVolume(F32 volume) void VolumeCatcherImpl::setVolume(F32 volume)
{ {
VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance(); mVolume = volume;
impl->mVolume = volume;
// Iterate through all known instances, setting the volume on each. // Iterate through all known instances, setting the volume on each.
for(std::list<VolumeCatcherStorage*>::iterator iter = mComponentInstances.begin(); iter != mComponentInstances.end(); ++iter) for(auto& instance : mComponentInstances)
{ {
impl->setInstanceVolume(*iter); // std::cerr << "Setting volume on component instance: " << (instance->delegate) << " to " << mVolume << std::endl;
if (instance && instance->delegate)
{
if (OSStatus err = AudioUnitSetParameter(
instance->delegate,
kHALOutputParam_Volume,
kAudioUnitScope_Global,
0,
mVolume,
0))
{
// std::cerr << " AudioUnitSetParameter returned " << err << std::endl;
}
}
} }
} }
@@ -214,35 +201,12 @@ void VolumeCatcherImpl::setPan(F32 pan)
{ {
VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance(); VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
impl->mPan = pan; impl->mPan = pan;
// TODO: implement this. // TODO: implement this.
// This will probably require adding a "panner" audio unit to the chain somehow. // This will probably require adding a "panner" audio unit to the chain somehow.
// There's also a "3d mixer" component that we might be able to use... // There's also a "3d mixer" component that we might be able to use...
} }
void VolumeCatcherImpl::setInstanceVolume(VolumeCatcherStorage *instance)
{
// std::cerr << "Setting volume on component instance: " << (instance->delegate) << " to " << mVolume << std::endl;
OSStatus err = noErr;
if(instance && instance->delegate)
{
err = AudioUnitSetParameter(
instance->delegate,
kHALOutputParam_Volume,
kAudioUnitScope_Global,
0,
mVolume,
0);
}
if(err)
{
// std::cerr << " AudioUnitSetParameter returned " << err << std::endl;
}
}
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
VolumeCatcher::VolumeCatcher() VolumeCatcher::VolumeCatcher()