src/output: add wasapi output and mixer plugin

parent e5eac71d
...@@ -36,6 +36,7 @@ extern const MixerPlugin oss_mixer_plugin; ...@@ -36,6 +36,7 @@ extern const MixerPlugin oss_mixer_plugin;
extern const MixerPlugin osx_mixer_plugin; extern const MixerPlugin osx_mixer_plugin;
extern const MixerPlugin pulse_mixer_plugin; extern const MixerPlugin pulse_mixer_plugin;
extern const MixerPlugin winmm_mixer_plugin; extern const MixerPlugin winmm_mixer_plugin;
extern const MixerPlugin wasapi_mixer_plugin;
extern const MixerPlugin sndio_mixer_plugin; extern const MixerPlugin sndio_mixer_plugin;
#endif #endif
/*
* Copyright 2020 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.
*/
#include "mixer/MixerInternal.hxx"
#include "output/plugins/WasapiOutputPlugin.hxx"
#include "win32/Com.hxx"
#include "win32/HResult.hxx"
#include <cmath>
#include <endpointvolume.h>
#include <optional>
class WasapiMixer final : public Mixer {
WasapiOutput &output;
std::optional<COM> com;
public:
WasapiMixer(WasapiOutput &_output, MixerListener &_listener)
: Mixer(wasapi_mixer_plugin, _listener), output(_output) {}
void Open() override { com.emplace(); }
void Close() noexcept override { com.reset(); }
int GetVolume() override {
HRESULT result;
float volume_level;
if (wasapi_is_exclusive(output)) {
ComPtr<IAudioEndpointVolume> endpoint_volume;
result = wasapi_output_get_device(output)->Activate(
__uuidof(IAudioEndpointVolume), CLSCTX_ALL, nullptr,
endpoint_volume.AddressCast());
if (FAILED(result)) {
throw FormatHResultError(
result, "Unable to get device endpoint volume");
}
result = endpoint_volume->GetMasterVolumeLevelScalar(
&volume_level);
if (FAILED(result)) {
throw FormatHResultError(
result, "Unable to get master volume level");
}
} else {
ComPtr<ISimpleAudioVolume> session_volume;
result = wasapi_output_get_client(output)->GetService(
__uuidof(ISimpleAudioVolume),
session_volume.AddressCast<void>());
if (FAILED(result)) {
throw FormatHResultError(
result, "Unable to get client session volume");
}
result = session_volume->GetMasterVolume(&volume_level);
if (FAILED(result)) {
throw FormatHResultError(result,
"Unable to get master volume");
}
}
return std::lround(volume_level * 100.0f);
}
void SetVolume(unsigned volume) override {
HRESULT result;
const float volume_level = volume / 100.0f;
if (wasapi_is_exclusive(output)) {
ComPtr<IAudioEndpointVolume> endpoint_volume;
result = wasapi_output_get_device(output)->Activate(
__uuidof(IAudioEndpointVolume), CLSCTX_ALL, nullptr,
endpoint_volume.AddressCast());
if (FAILED(result)) {
throw FormatHResultError(
result, "Unable to get device endpoint volume");
}
result = endpoint_volume->SetMasterVolumeLevelScalar(volume_level,
nullptr);
if (FAILED(result)) {
throw FormatHResultError(
result, "Unable to set master volume level");
}
} else {
ComPtr<ISimpleAudioVolume> session_volume;
result = wasapi_output_get_client(output)->GetService(
__uuidof(ISimpleAudioVolume),
session_volume.AddressCast<void>());
if (FAILED(result)) {
throw FormatHResultError(
result, "Unable to get client session volume");
}
result = session_volume->SetMasterVolume(volume_level, nullptr);
if (FAILED(result)) {
throw FormatHResultError(result,
"Unable to set master volume");
}
}
}
};
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,
};
...@@ -31,7 +31,10 @@ if libsndio_dep.found() ...@@ -31,7 +31,10 @@ if libsndio_dep.found()
endif endif
if is_windows if is_windows
mixer_plugins_sources += 'WinmmMixerPlugin.cxx' mixer_plugins_sources += [
'WinmmMixerPlugin.cxx',
'WasapiMixerPlugin.cxx',
]
endif endif
if is_android if is_android
......
...@@ -38,7 +38,12 @@ ...@@ -38,7 +38,12 @@
#include "plugins/ShoutOutputPlugin.hxx" #include "plugins/ShoutOutputPlugin.hxx"
#include "plugins/sles/SlesOutputPlugin.hxx" #include "plugins/sles/SlesOutputPlugin.hxx"
#include "plugins/SolarisOutputPlugin.hxx" #include "plugins/SolarisOutputPlugin.hxx"
#ifdef ENABLE_WINMM_OUTPUT
#include "plugins/WinmmOutputPlugin.hxx" #include "plugins/WinmmOutputPlugin.hxx"
#endif
#ifdef ENABLE_WASAPI_OUTPUT
#include "plugins/WasapiOutputPlugin.hxx"
#endif
#include "util/StringAPI.hxx" #include "util/StringAPI.hxx"
const AudioOutputPlugin *const audio_output_plugins[] = { const AudioOutputPlugin *const audio_output_plugins[] = {
...@@ -94,6 +99,9 @@ const AudioOutputPlugin *const audio_output_plugins[] = { ...@@ -94,6 +99,9 @@ const AudioOutputPlugin *const audio_output_plugins[] = {
#ifdef ENABLE_WINMM_OUTPUT #ifdef ENABLE_WINMM_OUTPUT
&winmm_output_plugin, &winmm_output_plugin,
#endif #endif
#ifdef ENABLE_WASAPI_OUTPUT
&wasapi_output_plugin,
#endif
nullptr nullptr
}; };
......
/*
* Copyright 2020 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_WASAPI_OUTPUT_PLUGIN_HXX
#define MPD_WASAPI_OUTPUT_PLUGIN_HXX
#include "output/Features.h"
#include "../OutputAPI.hxx"
#include "util/Compiler.h"
#include "win32/ComPtr.hxx"
#include <audioclient.h>
#include <mmdeviceapi.h>
extern const struct AudioOutputPlugin wasapi_output_plugin;
class WasapiOutput;
gcc_pure WasapiOutput &wasapi_output_downcast(AudioOutput &output) noexcept;
gcc_pure bool wasapi_is_exclusive(WasapiOutput &output) noexcept;
gcc_pure IMMDevice *wasapi_output_get_device(WasapiOutput &output) noexcept;
gcc_pure IAudioClient *wasapi_output_get_client(WasapiOutput &output) noexcept;
#endif
...@@ -39,7 +39,7 @@ if get_option('httpd') ...@@ -39,7 +39,7 @@ if get_option('httpd')
'httpd/HttpdClient.cxx', 'httpd/HttpdClient.cxx',
'httpd/HttpdOutputPlugin.cxx', 'httpd/HttpdOutputPlugin.cxx',
] ]
output_plugins_deps += [ event_dep, net_dep ] output_plugins_deps += [ event_dep, net_dep, boost_dep ]
need_encoder = true need_encoder = true
endif endif
...@@ -143,6 +143,19 @@ else ...@@ -143,6 +143,19 @@ else
winmm_dep = dependency('', required: false) winmm_dep = dependency('', required: false)
endif endif
output_features.set('ENABLE_WASAPI_OUTPUT', is_windows)
if is_windows
output_plugins_sources += [
'WasapiOutputPlugin.cxx',
]
wasapi_dep = [
c_compiler.find_library('ksuser', required: true),
c_compiler.find_library('ole32', required: true),
]
else
wasapi_dep = dependency('', required: false)
endif
output_plugins = static_library( output_plugins = static_library(
'output_plugins', 'output_plugins',
output_plugins_sources, output_plugins_sources,
...@@ -158,6 +171,8 @@ output_plugins = static_library( ...@@ -158,6 +171,8 @@ output_plugins = static_library(
openal_dep, openal_dep,
sles_dep, sles_dep,
winmm_dep, winmm_dep,
wasapi_dep,
boost_dep,
], ],
) )
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment