forked from bartvdbraak/blender
Fix T86851: PulseAudio randomly asserts in background rendering
Upstream fix from Audaspace with simplified PulseAudio code. Maniphest Tasks: T86851 Differential Revision: https://developer.blender.org/D10840
This commit is contained in:
parent
ae9d61e7fe
commit
35cf34de6d
2
extern/audaspace/CMakeLists.txt
vendored
2
extern/audaspace/CMakeLists.txt
vendored
@ -42,6 +42,7 @@ set(SRC
|
||||
src/devices/NULLDevice.cpp
|
||||
src/devices/ReadDevice.cpp
|
||||
src/devices/SoftwareDevice.cpp
|
||||
src/devices/ThreadedDevice.cpp
|
||||
src/Exception.cpp
|
||||
src/file/File.cpp
|
||||
src/file/FileManager.cpp
|
||||
@ -148,6 +149,7 @@ set(PUBLIC_HDR
|
||||
include/devices/NULLDevice.h
|
||||
include/devices/ReadDevice.h
|
||||
include/devices/SoftwareDevice.h
|
||||
include/devices/ThreadedDevice.h
|
||||
include/Exception.h
|
||||
include/file/File.h
|
||||
include/file/FileManager.h
|
||||
|
@ -255,6 +255,7 @@ protected:
|
||||
/**
|
||||
* This function tells the device, to start or pause playback.
|
||||
* \param playing True if device should playback.
|
||||
* \note This method is only called when the device is locked.
|
||||
*/
|
||||
virtual void playing(bool playing)=0;
|
||||
|
||||
|
95
extern/audaspace/include/devices/ThreadedDevice.h
vendored
Normal file
95
extern/audaspace/include/devices/ThreadedDevice.h
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
/*******************************************************************************
|
||||
* 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
|
||||
|
||||
/**
|
||||
* @file ThreadedDevice.h
|
||||
* @ingroup plugin
|
||||
* The ThreadedDevice class.
|
||||
*/
|
||||
|
||||
#include "devices/SoftwareDevice.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* This device extends the SoftwareDevice with code for running mixing in a separate thread.
|
||||
*/
|
||||
class AUD_PLUGIN_API ThreadedDevice : public SoftwareDevice
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Whether there is currently playback.
|
||||
*/
|
||||
bool m_playing;
|
||||
|
||||
/**
|
||||
* Whether the current playback should stop.
|
||||
*/
|
||||
bool m_stop;
|
||||
|
||||
/**
|
||||
* The streaming thread.
|
||||
*/
|
||||
std::thread m_thread;
|
||||
|
||||
/**
|
||||
* Starts the streaming thread.
|
||||
*/
|
||||
AUD_LOCAL void start();
|
||||
|
||||
/**
|
||||
* Streaming thread main function.
|
||||
*/
|
||||
AUD_LOCAL virtual void runMixingThread()=0;
|
||||
|
||||
// delete copy constructor and operator=
|
||||
ThreadedDevice(const ThreadedDevice&) = delete;
|
||||
ThreadedDevice& operator=(const ThreadedDevice&) = delete;
|
||||
|
||||
protected:
|
||||
virtual void playing(bool playing);
|
||||
|
||||
/**
|
||||
* Empty default constructor. To setup the device call the function create()
|
||||
* and to uninitialize call destroy().
|
||||
*/
|
||||
ThreadedDevice();
|
||||
|
||||
/**
|
||||
* Indicates that the mixing thread should be stopped.
|
||||
* \return Whether the mixing thread should be stopping.
|
||||
* \warning For thread safety, the device needs to be locked, when this method is called.
|
||||
*/
|
||||
inline bool shouldStop() { return m_stop; }
|
||||
|
||||
/**
|
||||
* This method needs to be called when the mixing thread is stopping.
|
||||
* \warning For thread safety, the device needs to be locked, when this method is called.
|
||||
*/
|
||||
inline void doStop() { m_stop = m_playing = false; }
|
||||
|
||||
/**
|
||||
* Stops all playback and notifies the mixing thread to stop.
|
||||
* \warning The device has to be unlocked to not run into a deadlock.
|
||||
*/
|
||||
void stopMixingThread();
|
||||
};
|
||||
|
||||
AUD_NAMESPACE_END
|
@ -27,9 +27,9 @@ void PulseAudioDevice::PulseAudio_state_callback(pa_context *context, void *data
|
||||
{
|
||||
PulseAudioDevice* device = (PulseAudioDevice*)data;
|
||||
|
||||
device->m_state = AUD_pa_context_get_state(context);
|
||||
std::lock_guard<ILockable> lock(*device);
|
||||
|
||||
AUD_pa_threaded_mainloop_signal(device->m_mainloop, 0);
|
||||
device->m_state = AUD_pa_context_get_state(context);
|
||||
}
|
||||
|
||||
void PulseAudioDevice::PulseAudio_request(pa_stream *stream, size_t num_bytes, void *data)
|
||||
@ -68,29 +68,40 @@ void PulseAudioDevice::PulseAudio_underflow(pa_stream *stream, void *data)
|
||||
}
|
||||
}
|
||||
|
||||
void PulseAudioDevice::playing(bool playing)
|
||||
void PulseAudioDevice::runMixingThread()
|
||||
{
|
||||
m_playback = playing;
|
||||
for(;;)
|
||||
{
|
||||
{
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
AUD_pa_stream_cork(m_stream, playing ? 0 : 1, nullptr, nullptr);
|
||||
if(shouldStop())
|
||||
{
|
||||
AUD_pa_stream_cork(m_stream, 1, nullptr, nullptr);
|
||||
doStop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(AUD_pa_stream_is_corked(m_stream))
|
||||
AUD_pa_stream_cork(m_stream, 0, nullptr, nullptr);
|
||||
|
||||
AUD_pa_mainloop_iterate(m_mainloop, true, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buffersize) :
|
||||
m_playback(false),
|
||||
m_state(PA_CONTEXT_UNCONNECTED),
|
||||
m_buffersize(buffersize),
|
||||
m_underflows(0)
|
||||
{
|
||||
m_mainloop = AUD_pa_threaded_mainloop_new();
|
||||
m_mainloop = AUD_pa_mainloop_new();
|
||||
|
||||
AUD_pa_threaded_mainloop_lock(m_mainloop);
|
||||
|
||||
m_context = AUD_pa_context_new(AUD_pa_threaded_mainloop_get_api(m_mainloop), name.c_str());
|
||||
m_context = AUD_pa_context_new(AUD_pa_mainloop_get_api(m_mainloop), name.c_str());
|
||||
|
||||
if(!m_context)
|
||||
{
|
||||
AUD_pa_threaded_mainloop_unlock(m_mainloop);
|
||||
AUD_pa_threaded_mainloop_free(m_mainloop);
|
||||
AUD_pa_mainloop_free(m_mainloop);
|
||||
|
||||
AUD_THROW(DeviceException, "Could not connect to PulseAudio.");
|
||||
}
|
||||
@ -99,26 +110,21 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff
|
||||
|
||||
AUD_pa_context_connect(m_context, nullptr, PA_CONTEXT_NOFLAGS, nullptr);
|
||||
|
||||
AUD_pa_threaded_mainloop_start(m_mainloop);
|
||||
|
||||
while(m_state != PA_CONTEXT_READY)
|
||||
{
|
||||
switch(m_state)
|
||||
{
|
||||
case PA_CONTEXT_FAILED:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
AUD_pa_threaded_mainloop_unlock(m_mainloop);
|
||||
AUD_pa_threaded_mainloop_stop(m_mainloop);
|
||||
|
||||
AUD_pa_context_disconnect(m_context);
|
||||
AUD_pa_context_unref(m_context);
|
||||
|
||||
AUD_pa_threaded_mainloop_free(m_mainloop);
|
||||
AUD_pa_mainloop_free(m_mainloop);
|
||||
|
||||
AUD_THROW(DeviceException, "Could not connect to PulseAudio.");
|
||||
break;
|
||||
default:
|
||||
AUD_pa_threaded_mainloop_wait(m_mainloop);
|
||||
AUD_pa_mainloop_iterate(m_mainloop, true, nullptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -166,13 +172,10 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff
|
||||
|
||||
if(!m_stream)
|
||||
{
|
||||
AUD_pa_threaded_mainloop_unlock(m_mainloop);
|
||||
AUD_pa_threaded_mainloop_stop(m_mainloop);
|
||||
|
||||
AUD_pa_context_disconnect(m_context);
|
||||
AUD_pa_context_unref(m_context);
|
||||
|
||||
AUD_pa_threaded_mainloop_free(m_mainloop);
|
||||
AUD_pa_mainloop_free(m_mainloop);
|
||||
|
||||
AUD_THROW(DeviceException, "Could not create PulseAudio stream.");
|
||||
}
|
||||
@ -188,32 +191,27 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff
|
||||
buffer_attr.prebuf = -1U;
|
||||
buffer_attr.tlength = buffersize;
|
||||
|
||||
if(AUD_pa_stream_connect_playback(m_stream, nullptr, &buffer_attr, static_cast<pa_stream_flags_t>(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr) < 0)
|
||||
if(AUD_pa_stream_connect_playback(m_stream, nullptr, &buffer_attr, static_cast<pa_stream_flags_t>(PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr) < 0)
|
||||
{
|
||||
AUD_pa_threaded_mainloop_unlock(m_mainloop);
|
||||
AUD_pa_threaded_mainloop_stop(m_mainloop);
|
||||
|
||||
AUD_pa_context_disconnect(m_context);
|
||||
AUD_pa_context_unref(m_context);
|
||||
|
||||
AUD_pa_threaded_mainloop_free(m_mainloop);
|
||||
AUD_pa_mainloop_free(m_mainloop);
|
||||
|
||||
AUD_THROW(DeviceException, "Could not connect PulseAudio stream.");
|
||||
}
|
||||
|
||||
AUD_pa_threaded_mainloop_unlock(m_mainloop);
|
||||
|
||||
create();
|
||||
}
|
||||
|
||||
PulseAudioDevice::~PulseAudioDevice()
|
||||
{
|
||||
AUD_pa_threaded_mainloop_stop(m_mainloop);
|
||||
stopMixingThread();
|
||||
|
||||
AUD_pa_context_disconnect(m_context);
|
||||
AUD_pa_context_unref(m_context);
|
||||
|
||||
AUD_pa_threaded_mainloop_free(m_mainloop);
|
||||
AUD_pa_mainloop_free(m_mainloop);
|
||||
|
||||
destroy();
|
||||
}
|
||||
|
@ -26,7 +26,7 @@
|
||||
* The PulseAudioDevice class.
|
||||
*/
|
||||
|
||||
#include "devices/SoftwareDevice.h"
|
||||
#include "devices/ThreadedDevice.h"
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
@ -35,15 +35,10 @@ AUD_NAMESPACE_BEGIN
|
||||
/**
|
||||
* This device plays back through PulseAudio, the simple direct media layer.
|
||||
*/
|
||||
class AUD_PLUGIN_API PulseAudioDevice : public SoftwareDevice
|
||||
class AUD_PLUGIN_API PulseAudioDevice : public ThreadedDevice
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Whether there is currently playback.
|
||||
*/
|
||||
volatile bool m_playback;
|
||||
|
||||
pa_threaded_mainloop* m_mainloop;
|
||||
pa_mainloop* m_mainloop;
|
||||
pa_context* m_context;
|
||||
pa_stream* m_stream;
|
||||
pa_context_state_t m_state;
|
||||
@ -74,13 +69,15 @@ private:
|
||||
*/
|
||||
AUD_LOCAL static void PulseAudio_underflow(pa_stream* stream, void* data);
|
||||
|
||||
/**
|
||||
* Streaming thread main function.
|
||||
*/
|
||||
AUD_LOCAL void runMixingThread();
|
||||
|
||||
// delete copy constructor and operator=
|
||||
PulseAudioDevice(const PulseAudioDevice&) = delete;
|
||||
PulseAudioDevice& operator=(const PulseAudioDevice&) = delete;
|
||||
|
||||
protected:
|
||||
virtual void playing(bool playing);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Opens the PulseAudio audio device for playback.
|
||||
|
@ -24,18 +24,14 @@ PULSEAUDIO_SYMBOL(pa_context_unref);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_begin_write);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_connect_playback);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_cork);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_is_corked);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_new);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_set_buffer_attr);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_set_underflow_callback);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_set_write_callback);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_write);
|
||||
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_free);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_get_api);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_lock);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_new);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_signal);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_start);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_stop);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_unlock);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_wait);
|
||||
PULSEAUDIO_SYMBOL(pa_mainloop_free);
|
||||
PULSEAUDIO_SYMBOL(pa_mainloop_get_api);
|
||||
PULSEAUDIO_SYMBOL(pa_mainloop_new);
|
||||
PULSEAUDIO_SYMBOL(pa_mainloop_iterate);
|
||||
|
193
extern/audaspace/plugins/wasapi/WASAPIDevice.cpp
vendored
193
extern/audaspace/plugins/wasapi/WASAPIDevice.cpp
vendored
@ -31,159 +31,83 @@ template <class T> void SafeRelease(T **ppT)
|
||||
}
|
||||
}
|
||||
|
||||
void WASAPIDevice::start()
|
||||
{
|
||||
lock();
|
||||
|
||||
// thread is still running, we can abort stopping it
|
||||
if(m_stop)
|
||||
m_stop = false;
|
||||
// thread is not running, let's start it
|
||||
else if(!m_playing)
|
||||
{
|
||||
if(m_thread.joinable())
|
||||
m_thread.join();
|
||||
|
||||
m_playing = true;
|
||||
|
||||
m_thread = std::thread(&WASAPIDevice::updateStream, this);
|
||||
}
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
void WASAPIDevice::updateStream()
|
||||
void WASAPIDevice::runMixingThread()
|
||||
{
|
||||
UINT32 buffer_size;
|
||||
UINT32 padding;
|
||||
UINT32 length;
|
||||
data_t* buffer;
|
||||
|
||||
lock();
|
||||
|
||||
if(FAILED(m_audio_client->GetBufferSize(&buffer_size)))
|
||||
{
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
IAudioRenderClient* render_client = nullptr;
|
||||
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
|
||||
|
||||
if(FAILED(m_audio_client->GetService(IID_IAudioRenderClient, reinterpret_cast<void**>(&render_client))))
|
||||
{
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
|
||||
|
||||
if(FAILED(m_audio_client->GetBufferSize(&buffer_size)))
|
||||
goto init_error;
|
||||
|
||||
if(FAILED(m_audio_client->GetService(IID_IAudioRenderClient, reinterpret_cast<void**>(&render_client))))
|
||||
goto init_error;
|
||||
|
||||
if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
|
||||
goto init_error;
|
||||
|
||||
length = buffer_size - padding;
|
||||
|
||||
if(FAILED(render_client->GetBuffer(length, &buffer)))
|
||||
goto init_error;
|
||||
|
||||
mix((data_t*)buffer, length);
|
||||
|
||||
if(FAILED(render_client->ReleaseBuffer(length, 0)))
|
||||
{
|
||||
init_error:
|
||||
SafeRelease(&render_client);
|
||||
doStop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UINT32 padding;
|
||||
|
||||
if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
|
||||
{
|
||||
SafeRelease(&render_client);
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
UINT32 length = buffer_size - padding;
|
||||
|
||||
if(FAILED(render_client->GetBuffer(length, &buffer)))
|
||||
{
|
||||
SafeRelease(&render_client);
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
mix((data_t*)buffer, length);
|
||||
|
||||
if(FAILED(render_client->ReleaseBuffer(length, 0)))
|
||||
{
|
||||
SafeRelease(&render_client);
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
m_audio_client->Start();
|
||||
|
||||
auto sleepDuration = std::chrono::milliseconds(buffer_size * 1000 / int(m_specs.rate) / 2);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
lock();
|
||||
|
||||
if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
|
||||
{
|
||||
m_audio_client->Stop();
|
||||
SafeRelease(&render_client);
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
|
||||
goto stop_thread;
|
||||
|
||||
length = buffer_size - padding;
|
||||
|
||||
if(FAILED(render_client->GetBuffer(length, &buffer)))
|
||||
goto stop_thread;
|
||||
|
||||
mix((data_t*)buffer, length);
|
||||
|
||||
if(FAILED(render_client->ReleaseBuffer(length, 0)))
|
||||
goto stop_thread;
|
||||
|
||||
// stop thread
|
||||
if(shouldStop())
|
||||
{
|
||||
stop_thread:
|
||||
m_audio_client->Stop();
|
||||
SafeRelease(&render_client);
|
||||
doStop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
length = buffer_size - padding;
|
||||
|
||||
if(FAILED(render_client->GetBuffer(length, &buffer)))
|
||||
{
|
||||
m_audio_client->Stop();
|
||||
SafeRelease(&render_client);
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
mix((data_t*)buffer, length);
|
||||
|
||||
if(FAILED(render_client->ReleaseBuffer(length, 0)))
|
||||
{
|
||||
m_audio_client->Stop();
|
||||
SafeRelease(&render_client);
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// stop thread
|
||||
if(m_stop)
|
||||
{
|
||||
m_audio_client->Stop();
|
||||
SafeRelease(&render_client);
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
std::this_thread::sleep_for(sleepDuration);
|
||||
}
|
||||
}
|
||||
|
||||
void WASAPIDevice::playing(bool playing)
|
||||
{
|
||||
if((!m_playing || m_stop) && playing)
|
||||
start();
|
||||
else
|
||||
m_stop = true;
|
||||
}
|
||||
|
||||
WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) :
|
||||
m_playing(false),
|
||||
m_stop(false),
|
||||
|
||||
m_imm_device_enumerator(nullptr),
|
||||
m_imm_device(nullptr),
|
||||
m_audio_client(nullptr),
|
||||
@ -361,14 +285,7 @@ WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) :
|
||||
|
||||
WASAPIDevice::~WASAPIDevice()
|
||||
{
|
||||
lock();
|
||||
|
||||
stopAll();
|
||||
|
||||
unlock();
|
||||
|
||||
if(m_thread.joinable())
|
||||
m_thread.join();
|
||||
stopMixingThread();
|
||||
|
||||
SafeRelease(&m_audio_client);
|
||||
SafeRelease(&m_imm_device);
|
||||
|
29
extern/audaspace/plugins/wasapi/WASAPIDevice.h
vendored
29
extern/audaspace/plugins/wasapi/WASAPIDevice.h
vendored
@ -26,7 +26,7 @@
|
||||
* The WASAPIDevice class.
|
||||
*/
|
||||
|
||||
#include "devices/SoftwareDevice.h"
|
||||
#include "devices/ThreadedDevice.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
@ -40,46 +40,23 @@ AUD_NAMESPACE_BEGIN
|
||||
/**
|
||||
* This device plays back through WASAPI, the Windows audio API.
|
||||
*/
|
||||
class AUD_PLUGIN_API WASAPIDevice : public SoftwareDevice
|
||||
class AUD_PLUGIN_API WASAPIDevice : public ThreadedDevice
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Whether there is currently playback.
|
||||
*/
|
||||
bool m_playing;
|
||||
|
||||
/**
|
||||
* Whether the current playback should stop.
|
||||
*/
|
||||
bool m_stop;
|
||||
|
||||
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();
|
||||
AUD_LOCAL void runMixingThread();
|
||||
|
||||
// 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.
|
||||
|
@ -737,7 +737,7 @@ void SoftwareDevice::mix(data_t* buffer, int length)
|
||||
{
|
||||
m_buffer.assureSize(length * AUD_SAMPLE_SIZE(m_specs));
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
{
|
||||
std::shared_ptr<SoftwareDevice::SoftwareHandle> sound;
|
||||
@ -880,7 +880,7 @@ std::shared_ptr<IHandle> SoftwareDevice::play(std::shared_ptr<IReader> reader, b
|
||||
// play sound
|
||||
std::shared_ptr<SoftwareDevice::SoftwareHandle> sound = std::shared_ptr<SoftwareDevice::SoftwareHandle>(new SoftwareDevice::SoftwareHandle(this, reader, pitch, resampler, mapper, keep));
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
m_playingSounds.push_back(sound);
|
||||
|
||||
@ -897,7 +897,7 @@ std::shared_ptr<IHandle> SoftwareDevice::play(std::shared_ptr<ISound> sound, boo
|
||||
|
||||
void SoftwareDevice::stopAll()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
while(!m_playingSounds.empty())
|
||||
m_playingSounds.front()->stop();
|
||||
|
65
extern/audaspace/src/devices/ThreadedDevice.cpp
vendored
Normal file
65
extern/audaspace/src/devices/ThreadedDevice.cpp
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/*******************************************************************************
|
||||
* 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 "devices/ThreadedDevice.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
void ThreadedDevice::start()
|
||||
{
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
// thread is still running, we can abort stopping it
|
||||
if(m_stop)
|
||||
m_stop = false;
|
||||
|
||||
// thread is not running, let's start it
|
||||
else if(!m_playing)
|
||||
{
|
||||
if(m_thread.joinable())
|
||||
m_thread.join();
|
||||
|
||||
m_playing = true;
|
||||
|
||||
m_thread = std::thread(&ThreadedDevice::runMixingThread, this);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadedDevice::playing(bool playing)
|
||||
{
|
||||
if((!m_playing || m_stop) && playing)
|
||||
start();
|
||||
else
|
||||
m_stop = true;
|
||||
}
|
||||
|
||||
ThreadedDevice::ThreadedDevice() :
|
||||
m_playing(false),
|
||||
m_stop(false)
|
||||
{
|
||||
}
|
||||
|
||||
void aud::ThreadedDevice::stopMixingThread()
|
||||
{
|
||||
stopAll();
|
||||
|
||||
if(m_thread.joinable())
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
AUD_NAMESPACE_END
|
Loading…
Reference in New Issue
Block a user