You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
113 lines
2.9 KiB
C++
113 lines
2.9 KiB
C++
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
// Copyright The Music Player Daemon Project
|
|
|
|
#undef NOUSER // COM needs the "MSG" typedef
|
|
|
|
#include "WasapiMixerPlugin.hxx"
|
|
#include "output/plugins/wasapi/ForMixer.hxx"
|
|
#include "output/plugins/wasapi/AudioClient.hxx"
|
|
#include "output/plugins/wasapi/Device.hxx"
|
|
#include "mixer/Mixer.hxx"
|
|
#include "win32/ComPtr.hxx"
|
|
#include "win32/ComWorker.hxx"
|
|
#include "win32/HResult.hxx"
|
|
|
|
#include <cmath>
|
|
#include <optional>
|
|
|
|
#include <audioclient.h>
|
|
#include <endpointvolume.h>
|
|
#include <mmdeviceapi.h>
|
|
|
|
class WasapiMixer final : public Mixer {
|
|
WasapiOutput &output;
|
|
|
|
public:
|
|
WasapiMixer(WasapiOutput &_output, MixerListener &_listener)
|
|
: Mixer(wasapi_mixer_plugin, _listener), output(_output) {}
|
|
|
|
void Open() override {}
|
|
|
|
void Close() noexcept override {}
|
|
|
|
int GetVolume() override {
|
|
auto com_worker = wasapi_output_get_com_worker(output);
|
|
if (!com_worker)
|
|
return -1;
|
|
|
|
auto future = com_worker->Async([&]() -> int {
|
|
HRESULT result;
|
|
float volume_level;
|
|
|
|
if (wasapi_is_exclusive(output)) {
|
|
auto endpoint_volume =
|
|
Activate<IAudioEndpointVolume>(*wasapi_output_get_device(output));
|
|
|
|
result = endpoint_volume->GetMasterVolumeLevelScalar(
|
|
&volume_level);
|
|
if (FAILED(result)) {
|
|
throw MakeHResultError(result,
|
|
"Unable to get master "
|
|
"volume level");
|
|
}
|
|
} else {
|
|
auto session_volume =
|
|
GetService<ISimpleAudioVolume>(*wasapi_output_get_client(output));
|
|
|
|
result = session_volume->GetMasterVolume(&volume_level);
|
|
if (FAILED(result)) {
|
|
throw MakeHResultError(
|
|
result, "Unable to get master volume");
|
|
}
|
|
}
|
|
|
|
return std::lround(volume_level * 100.0f);
|
|
});
|
|
return future.get();
|
|
}
|
|
|
|
void SetVolume(unsigned volume) override {
|
|
auto com_worker = wasapi_output_get_com_worker(output);
|
|
if (!com_worker)
|
|
throw std::runtime_error("Cannot set WASAPI volume");
|
|
|
|
com_worker->Async([&]() {
|
|
HRESULT result;
|
|
const float volume_level = volume / 100.0f;
|
|
|
|
if (wasapi_is_exclusive(output)) {
|
|
auto endpoint_volume =
|
|
Activate<IAudioEndpointVolume>(*wasapi_output_get_device(output));
|
|
|
|
result = endpoint_volume->SetMasterVolumeLevelScalar(
|
|
volume_level, nullptr);
|
|
if (FAILED(result)) {
|
|
throw MakeHResultError(
|
|
result,
|
|
"Unable to set master volume level");
|
|
}
|
|
} else {
|
|
auto session_volume =
|
|
GetService<ISimpleAudioVolume>(*wasapi_output_get_client(output));
|
|
|
|
result = session_volume->SetMasterVolume(volume_level,
|
|
nullptr);
|
|
if (FAILED(result)) {
|
|
throw MakeHResultError(
|
|
result, "Unable to set master volume");
|
|
}
|
|
}
|
|
}).get();
|
|
}
|
|
};
|
|
|
|
static Mixer *wasapi_mixer_init(EventLoop &, AudioOutput &ao, MixerListener &listener,
|
|
const ConfigBlock &) {
|
|
return new WasapiMixer(wasapi_output_downcast(ao), listener);
|
|
}
|
|
|
|
const MixerPlugin wasapi_mixer_plugin = {
|
|
wasapi_mixer_init,
|
|
false,
|
|
};
|