Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/extern
diff options
context:
space:
mode:
authorJörg Müller <nexyon@gmail.com>2021-03-11 21:22:56 +0300
committerJörg Müller <nexyon@gmail.com>2021-03-17 01:21:45 +0300
commitbc5798530663a9b3fec8df60feba510bd681c5ad (patch)
tree0d2587f7d3c5f9ac55d6bcee8f91afce9cf9d50b /extern
parentd33339ebf44855b62200a2784c046792d78c75bc (diff)
Audaspace: add support for WASAPI on Windows
This adds WASAPI as audio backend on Windows. WASAPI is the modern standard audio API on Windows introduced with Windows Vista. Ref T86590
Diffstat (limited to 'extern')
-rw-r--r--extern/audaspace/CMakeLists.txt32
-rw-r--r--extern/audaspace/blender_config.cmake1
-rw-r--r--extern/audaspace/plugins/wasapi/WASAPIDevice.cpp401
-rw-r--r--extern/audaspace/plugins/wasapi/WASAPIDevice.h99
-rw-r--r--extern/audaspace/src/plugin/PluginManagerWindows.cpp.in4
5 files changed, 535 insertions, 2 deletions
diff --git a/extern/audaspace/CMakeLists.txt b/extern/audaspace/CMakeLists.txt
index 92b1cbcb290..dc851961d47 100644
--- a/extern/audaspace/CMakeLists.txt
+++ b/extern/audaspace/CMakeLists.txt
@@ -288,6 +288,9 @@ if(AUDASPACE_STANDALONE)
if(NOT WIN32 AND NOT APPLE)
option(WITH_PULSEAUDIO "Build With PulseAudio" TRUE)
endif()
+ if(WIN32)
+ option(WITH_WASAPI "Build With WASAPI" TRUE)
+ endif()
if(WITH_STRICT_DEPENDENCIES)
set(PACKAGE_OPTION REQUIRED)
@@ -312,6 +315,7 @@ if(AUDASPACE_STANDALONE)
cmake_dependent_option(PLUGIN_OPENAL "Build OpenAL Plugin" TRUE "WITH_OPENAL;SHARED_LIBRARY" FALSE)
cmake_dependent_option(PLUGIN_PULSEAUDIO "Build PulseAudio Plugin" TRUE "WITH_PULSEAUDIO;SHARED_LIBRARY" FALSE)
cmake_dependent_option(PLUGIN_SDL "Build SDL Plugin" TRUE "WITH_SDL;SHARED_LIBRARY" FALSE)
+ cmake_dependent_option(PLUGIN_WASAPI "Build WASAPI Plugin" TRUE "WITH_WASAPI;SHARED_LIBRARY" FALSE)
cmake_dependent_option(WITH_PYTHON_MODULE "Build Python Module" TRUE "WITH_PYTHON" FALSE)
cmake_dependent_option(USE_SDL2 "Use SDL2 instead of 1 if available" TRUE "WITH_SDL" FALSE)
cmake_dependent_option(DYNLOAD_JACK "Dynamically load JACK" FALSE "WITH_JACK" FALSE)
@@ -714,6 +718,23 @@ if(WITH_SDL)
endif()
endif()
+# WASAPI
+if(WITH_WASAPI)
+ set(WASAPI_SRC
+ plugins/wasapi/WASAPIDevice.cpp
+ )
+ set(WASAPI_HDR
+ plugins/wasapi/WASAPIDevice.h
+ )
+
+ if(NOT PLUGIN_WASAPI)
+ list(APPEND LIBRARIES ksuser)
+ list(APPEND SRC ${WASAPI_SRC})
+ list(APPEND HDR ${WASAPI_HDR})
+ list(APPEND STATIC_PLUGINS WASAPIDevice)
+ endif()
+endif()
+
# library configuration
if(SHARED_LIBRARY)
@@ -861,6 +882,17 @@ if(WITH_SDL AND PLUGIN_SDL)
install(TARGETS audsdl DESTINATION ${DEFAULT_PLUGIN_PATH})
endif()
+if(WITH_WASAPI AND PLUGIN_WASAPI)
+ add_definitions(-DWASAPI_PLUGIN)
+ include_directories(${INCLUDE})
+ add_library(audwasapi SHARED ${WASAPI_SRC} ${WASAPI_HDR} ${HDR})
+ if(WITH_VERSIONED_PLUGINS)
+ set_target_properties(audwasapi PROPERTIES SOVERSION ${AUDASPACE_VERSION})
+ endif()
+ target_link_libraries(audwasapi audaspace ksuser)
+ install(TARGETS audwasapi DESTINATION ${DEFAULT_PLUGIN_PATH})
+endif()
+
# dlls
if(WIN32)
diff --git a/extern/audaspace/blender_config.cmake b/extern/audaspace/blender_config.cmake
index 22e6564ad89..667788c182b 100644
--- a/extern/audaspace/blender_config.cmake
+++ b/extern/audaspace/blender_config.cmake
@@ -16,6 +16,7 @@ set(PLUGIN_LIBSNDFILE FALSE) # "Build LibSndFile Plugin"
set(PLUGIN_OPENAL FALSE) # "Build OpenAL Plugin"
set(PLUGIN_PULSEAUDIO FALSE) # "Build PulseAudio Plugin"
set(PLUGIN_SDL FALSE) # "Build SDL Plugin"
+set(PLUGIN_WASAPI FALSE) # "Build WASAPI Plugin"
set(WITH_PYTHON_MODULE FALSE) # "Build Python Module"
set(DYNLOAD_JACK ${WITH_JACK_DYNLOAD}) # "Dynamically load JACK"
set(DYNLOAD_PULSEAUDIO ${WITH_PULSEAUDIO_DYNLOAD}) # "Dynamically load PulseAudio"
diff --git a/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp b/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp
new file mode 100644
index 00000000000..241f694feb5
--- /dev/null
+++ b/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp
@@ -0,0 +1,401 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#include "WASAPIDevice.h"
+#include "devices/DeviceManager.h"
+#include "devices/IDeviceFactory.h"
+#include "Exception.h"
+#include "IReader.h"
+
+AUD_NAMESPACE_BEGIN
+
+template <class T> void SafeRelease(T **ppT)
+{
+ if(*ppT)
+ {
+ (*ppT)->Release();
+ *ppT = NULL;
+ }
+}
+
+void WASAPIDevice::start()
+{
+ lock();
+
+ if(!m_playing)
+ {
+ if(m_thread.joinable())
+ m_thread.join();
+
+ m_playing = true;
+
+ m_thread = std::thread(&WASAPIDevice::updateStream, this);
+ }
+
+ unlock();
+}
+
+void WASAPIDevice::updateStream()
+{
+ UINT32 buffer_size;
+ data_t* buffer;
+
+ if(FAILED(m_audio_client->GetBufferSize(&buffer_size)))
+ return;
+
+ IAudioRenderClient* render_client = nullptr;
+ const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
+
+ if(FAILED(m_audio_client->GetService(IID_IAudioRenderClient, reinterpret_cast<void**>(&render_client))))
+ return;
+
+ UINT32 padding;
+
+ if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
+ {
+ SafeRelease(&render_client);
+ return;
+ }
+
+ UINT32 length = buffer_size - padding;
+
+ if(FAILED(render_client->GetBuffer(length, &buffer)))
+ {
+ SafeRelease(&render_client);
+ return;
+ }
+
+ lock();
+
+ mix((data_t*)buffer, length);
+
+ unlock();
+
+ if(FAILED(render_client->ReleaseBuffer(length, 0)))
+ {
+ SafeRelease(&render_client);
+ return;
+ }
+
+ m_audio_client->Start();
+
+ auto sleepDuration = std::chrono::milliseconds(buffer_size * 1000 / int(m_specs.rate) / 2);
+
+ for(;;)
+ {
+ if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
+ {
+ m_audio_client->Stop();
+ SafeRelease(&render_client);
+ return;
+ }
+
+ length = buffer_size - padding;
+
+ if(FAILED(render_client->GetBuffer(length, &buffer)))
+ {
+ m_audio_client->Stop();
+ SafeRelease(&render_client);
+ return;
+ }
+
+ lock();
+
+ mix((data_t*)buffer, length);
+
+ unlock();
+
+ if(FAILED(render_client->ReleaseBuffer(length, 0)))
+ {
+ m_audio_client->Stop();
+ SafeRelease(&render_client);
+ return;
+ }
+
+ // stop thread
+ if(!m_playing)
+ {
+ m_audio_client->Stop();
+ SafeRelease(&render_client);
+ return;
+ }
+
+ std::this_thread::sleep_for(sleepDuration);
+ }
+}
+
+void WASAPIDevice::playing(bool playing)
+{
+ if(!m_playing && playing)
+ start();
+ else
+ m_playing = playing;
+}
+
+WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) :
+ m_playing(false),
+
+ m_imm_device_enumerator(nullptr),
+ m_imm_device(nullptr),
+ m_audio_client(nullptr),
+
+ m_wave_format_extensible({})
+{
+ // initialize COM if it hasn't happened yet
+ CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+
+ const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
+ const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
+ const IID IID_IAudioClient = __uuidof(IAudioClient);
+
+ WAVEFORMATEXTENSIBLE wave_format_extensible_closest_match;
+ WAVEFORMATEXTENSIBLE* closest_match_pointer = &wave_format_extensible_closest_match;
+
+ HRESULT result;
+
+ REFERENCE_TIME minimum_time = 0;
+ REFERENCE_TIME buffer_duration;
+
+ if(FAILED(CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, reinterpret_cast<void**>(&m_imm_device_enumerator))))
+ goto error;
+
+ if(FAILED(m_imm_device_enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &m_imm_device)))
+ goto error;
+
+ if(FAILED(m_imm_device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, reinterpret_cast<void**>(&m_audio_client))))
+ goto error;
+
+ if(specs.channels == CHANNELS_INVALID)
+ specs.channels = CHANNELS_STEREO;
+ if(specs.format == FORMAT_INVALID)
+ specs.format = FORMAT_FLOAT32;
+ if(specs.rate == RATE_INVALID)
+ specs.rate = RATE_48000;
+
+ switch(specs.format)
+ {
+ case FORMAT_U8:
+ case FORMAT_S16:
+ case FORMAT_S24:
+ case FORMAT_S32:
+ m_wave_format_extensible.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ break;
+ case FORMAT_FLOAT32:
+ m_wave_format_extensible.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ break;
+ default:
+ m_wave_format_extensible.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ specs.format = FORMAT_FLOAT32;
+ break;
+ }
+
+ switch(specs.channels)
+ {
+ case CHANNELS_MONO:
+ m_wave_format_extensible.dwChannelMask = SPEAKER_FRONT_CENTER;
+ break;
+ case CHANNELS_STEREO:
+ m_wave_format_extensible.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
+ break;
+ case CHANNELS_STEREO_LFE:
+ m_wave_format_extensible.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY;
+ break;
+ case CHANNELS_SURROUND4:
+ m_wave_format_extensible.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
+ break;
+ case CHANNELS_SURROUND5:
+ m_wave_format_extensible.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
+ break;
+ case CHANNELS_SURROUND51:
+ m_wave_format_extensible.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
+ break;
+ case CHANNELS_SURROUND61:
+ m_wave_format_extensible.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT;
+ break;
+ case CHANNELS_SURROUND71:
+ m_wave_format_extensible.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT;
+ break;
+ default:
+ specs.channels = CHANNELS_STEREO;
+ m_wave_format_extensible.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
+ break;
+ }
+
+ m_wave_format_extensible.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ m_wave_format_extensible.Format.nChannels = specs.channels;
+ m_wave_format_extensible.Format.nSamplesPerSec = specs.rate;
+ m_wave_format_extensible.Format.nAvgBytesPerSec = specs.rate * AUD_DEVICE_SAMPLE_SIZE(specs);
+ m_wave_format_extensible.Format.nBlockAlign = AUD_DEVICE_SAMPLE_SIZE(specs);
+ m_wave_format_extensible.Format.wBitsPerSample = AUD_FORMAT_SIZE(specs.format) * 8;
+ m_wave_format_extensible.Format.cbSize = 22;
+ m_wave_format_extensible.Samples.wValidBitsPerSample = m_wave_format_extensible.Format.wBitsPerSample;
+
+ result = m_audio_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, reinterpret_cast<const WAVEFORMATEX*>(&m_wave_format_extensible), reinterpret_cast<WAVEFORMATEX**>(&closest_match_pointer));
+
+ if(result == S_FALSE)
+ {
+ if(closest_match_pointer->Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE)
+ goto error;
+
+ specs.channels = Channels(closest_match_pointer->Format.nChannels);
+ specs.rate = closest_match_pointer->Format.nSamplesPerSec;
+
+ if(closest_match_pointer->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
+ {
+ if(closest_match_pointer->Format.wBitsPerSample == 32)
+ specs.format = FORMAT_FLOAT32;
+ else if(closest_match_pointer->Format.wBitsPerSample == 64)
+ specs.format = FORMAT_FLOAT64;
+ else
+ goto error;
+ }
+ else if(closest_match_pointer->SubFormat == KSDATAFORMAT_SUBTYPE_PCM)
+ {
+ switch(closest_match_pointer->Format.wBitsPerSample)
+ {
+ case 8:
+ specs.format = FORMAT_U8;
+ break;
+ case 16:
+ specs.format = FORMAT_S16;
+ break;
+ case 24:
+ specs.format = FORMAT_S24;
+ break;
+ case 32:
+ specs.format = FORMAT_S32;
+ break;
+ default:
+ goto error;
+ break;
+ }
+ }
+ else
+ goto error;
+
+ m_wave_format_extensible = *closest_match_pointer;
+
+ if(closest_match_pointer != &wave_format_extensible_closest_match)
+ {
+ CoTaskMemFree(closest_match_pointer);
+ closest_match_pointer = &wave_format_extensible_closest_match;
+ }
+ }
+ else if(FAILED(result))
+ goto error;
+
+ if(FAILED(m_audio_client->GetDevicePeriod(nullptr, &minimum_time)))
+ goto error;
+
+ buffer_duration = REFERENCE_TIME(buffersize) * REFERENCE_TIME(10000000) / REFERENCE_TIME(specs.rate);
+
+ if(minimum_time > buffer_duration)
+ buffer_duration = minimum_time;
+
+ m_specs = specs;
+
+ if(FAILED(m_audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, buffer_duration, 0, reinterpret_cast<WAVEFORMATEX*>(&m_wave_format_extensible), nullptr)))
+ goto error;
+
+ create();
+
+ return;
+
+ error:
+ if(closest_match_pointer != &wave_format_extensible_closest_match)
+ CoTaskMemFree(closest_match_pointer);
+ SafeRelease(&m_imm_device);
+ SafeRelease(&m_imm_device_enumerator);
+ SafeRelease(&m_audio_client);
+ AUD_THROW(DeviceException, "The audio device couldn't be opened with WASAPI.");
+}
+
+WASAPIDevice::~WASAPIDevice()
+{
+ lock();
+
+ stopAll();
+
+ unlock();
+
+ if(m_thread.joinable())
+ m_thread.join();
+
+ SafeRelease(&m_audio_client);
+ SafeRelease(&m_imm_device);
+ SafeRelease(&m_imm_device_enumerator);
+
+ destroy();
+}
+
+class WASAPIDeviceFactory : public IDeviceFactory
+{
+private:
+ DeviceSpecs m_specs;
+ int m_buffersize;
+
+public:
+ WASAPIDeviceFactory() :
+ m_buffersize(AUD_DEFAULT_BUFFER_SIZE)
+ {
+ m_specs.format = FORMAT_S16;
+ m_specs.channels = CHANNELS_STEREO;
+ m_specs.rate = RATE_48000;
+ }
+
+ virtual std::shared_ptr<IDevice> openDevice()
+ {
+ return std::shared_ptr<IDevice>(new WASAPIDevice(m_specs, m_buffersize));
+ }
+
+ virtual int getPriority()
+ {
+ return 1 << 15;
+ }
+
+ virtual void setSpecs(DeviceSpecs specs)
+ {
+ m_specs = specs;
+ }
+
+ virtual void setBufferSize(int buffersize)
+ {
+ m_buffersize = buffersize;
+ }
+
+ virtual void setName(std::string name)
+ {
+ }
+};
+
+void WASAPIDevice::registerPlugin()
+{
+ DeviceManager::registerDevice("WASAPI", std::shared_ptr<IDeviceFactory>(new WASAPIDeviceFactory));
+}
+
+#ifdef WASAPI_PLUGIN
+extern "C" AUD_PLUGIN_API void registerPlugin()
+{
+ WASAPIDevice::registerPlugin();
+}
+
+extern "C" AUD_PLUGIN_API const char* getName()
+{
+ return "WASAPI";
+}
+#endif
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/wasapi/WASAPIDevice.h b/extern/audaspace/plugins/wasapi/WASAPIDevice.h
new file mode 100644
index 00000000000..b7b520e802c
--- /dev/null
+++ b/extern/audaspace/plugins/wasapi/WASAPIDevice.h
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#pragma once
+
+#ifdef WASAPI_PLUGIN
+#define AUD_BUILD_PLUGIN
+#endif
+
+/**
+ * @file WASAPIDevice.h
+ * @ingroup plugin
+ * The WASAPIDevice class.
+ */
+
+#include "devices/SoftwareDevice.h"
+
+#include <thread>
+
+#include <windows.h>
+#include <audioclient.h>
+#include <mmdeviceapi.h>
+#include <mmreg.h>
+
+AUD_NAMESPACE_BEGIN
+
+/**
+ * This device plays back through WASAPI, the Windows audio API.
+ */
+class AUD_PLUGIN_API WASAPIDevice : public SoftwareDevice
+{
+private:
+ /**
+ * Whether there is currently playback.
+ */
+ bool m_playing;
+
+ IMMDeviceEnumerator* m_imm_device_enumerator;
+ IMMDevice* m_imm_device;
+ IAudioClient* m_audio_client;
+ WAVEFORMATEXTENSIBLE m_wave_format_extensible;
+
+ /**
+ * The streaming thread.
+ */
+ std::thread m_thread;
+
+ /**
+ * Starts the streaming thread.
+ */
+ AUD_LOCAL void start();
+
+ /**
+ * Streaming thread main function.
+ */
+ AUD_LOCAL void updateStream();
+
+ // delete copy constructor and operator=
+ WASAPIDevice(const WASAPIDevice&) = delete;
+ WASAPIDevice& operator=(const WASAPIDevice&) = delete;
+
+protected:
+ virtual void playing(bool playing);
+
+public:
+ /**
+ * Opens the WASAPI audio device for playback.
+ * \param specs The wanted audio specification.
+ * \param buffersize The size of the internal buffer.
+ * \note The specification really used for opening the device may differ.
+ * \exception Exception Thrown if the audio device cannot be opened.
+ */
+ WASAPIDevice(DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE);
+
+ /**
+ * Closes the WASAPI audio device.
+ */
+ virtual ~WASAPIDevice();
+
+ /**
+ * Registers this plugin.
+ */
+ static void registerPlugin();
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/src/plugin/PluginManagerWindows.cpp.in b/extern/audaspace/src/plugin/PluginManagerWindows.cpp.in
index 62350fd24fd..dd2df15751c 100644
--- a/extern/audaspace/src/plugin/PluginManagerWindows.cpp.in
+++ b/extern/audaspace/src/plugin/PluginManagerWindows.cpp.in
@@ -27,9 +27,9 @@ void* PluginManager::openLibrary(const std::string& path)
return reinterpret_cast<void*>(LoadLibrary(path.c_str()));
}
-void *PluginManager::lookupLibrary(void *handle, const std::string &name)
+void* PluginManager::lookupLibrary(void *handle, const std::string &name)
{
- return GetProcAddress(reinterpret_cast<HMODULE>(handle), name.c_str());
+ return reinterpret_cast<void*>(GetProcAddress(reinterpret_cast<HMODULE>(handle), name.c_str()));
}
void PluginManager::closeLibrary(void *handle)