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
diff options
context:
space:
mode:
Diffstat (limited to 'extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp')
-rw-r--r--extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp169
1 files changed, 112 insertions, 57 deletions
diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp
index bf3fad82620..cddc411cfc6 100644
--- a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp
+++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp
@@ -23,95 +23,121 @@
AUD_NAMESPACE_BEGIN
-void PulseAudioDevice::PulseAudio_state_callback(pa_context *context, void *data)
+PulseAudioDevice::PulseAudioSynchronizer::PulseAudioSynchronizer(PulseAudioDevice *device) :
+ m_device(device)
{
- PulseAudioDevice* device = (PulseAudioDevice*)data;
+}
- std::lock_guard<ILockable> lock(*device);
+double PulseAudioDevice::PulseAudioSynchronizer::getPosition(std::shared_ptr<IHandle> handle)
+{
+ pa_usec_t latency;
+ int negative;
+ AUD_pa_stream_get_latency(m_device->m_stream, &latency, &negative);
- device->m_state = AUD_pa_context_get_state(context);
+ double delay = m_device->m_ring_buffer.getReadSize() / (AUD_SAMPLE_SIZE(m_device->m_specs) * m_device->m_specs.rate) + latency * 1.0e-6;
+
+ return handle->getPosition() - delay;
}
-void PulseAudioDevice::PulseAudio_request(pa_stream *stream, size_t total_bytes, void *data)
+void PulseAudioDevice::updateRingBuffer()
{
- PulseAudioDevice* device = (PulseAudioDevice*)data;
+ unsigned int samplesize = AUD_SAMPLE_SIZE(m_specs);
- void* buffer;
+ std::unique_lock<std::mutex> lock(m_mixingLock);
- while(total_bytes > 0)
+ Buffer buffer;
+
+ while(m_valid)
{
- size_t num_bytes = total_bytes;
+ size_t size = m_ring_buffer.getWriteSize();
- AUD_pa_stream_begin_write(stream, &buffer, &num_bytes);
+ size_t sample_count = size / samplesize;
- device->mix((data_t*)buffer, num_bytes / AUD_DEVICE_SAMPLE_SIZE(device->m_specs));
+ if(sample_count > 0)
+ {
+ size = sample_count * samplesize;
- AUD_pa_stream_write(stream, buffer, num_bytes, nullptr, 0, PA_SEEK_RELATIVE);
+ buffer.assureSize(size);
- total_bytes -= num_bytes;
+ mix(reinterpret_cast<data_t*>(buffer.getBuffer()), sample_count);
+
+ m_ring_buffer.write(reinterpret_cast<data_t*>(buffer.getBuffer()), size);
+ }
+
+ m_mixingCondition.wait(lock);
}
}
-void PulseAudioDevice::PulseAudio_underflow(pa_stream *stream, void *data)
+void PulseAudioDevice::PulseAudio_state_callback(pa_context *context, void *data)
{
PulseAudioDevice* device = (PulseAudioDevice*)data;
- DeviceSpecs specs = device->getSpecs();
+ device->m_state = AUD_pa_context_get_state(context);
- if(++device->m_underflows > 4 && device->m_buffersize < AUD_DEVICE_SAMPLE_SIZE(specs) * specs.rate * 2)
- {
- device->m_buffersize <<= 1;
- device->m_underflows = 0;
+ AUD_pa_threaded_mainloop_signal(device->m_mainloop, 0);
+}
- pa_buffer_attr buffer_attr;
+void PulseAudioDevice::PulseAudio_request(pa_stream *stream, size_t total_bytes, void *data)
+{
+ PulseAudioDevice* device = (PulseAudioDevice*)data;
- buffer_attr.fragsize = -1U;
- buffer_attr.maxlength = -1U;
- buffer_attr.minreq = -1U;
- buffer_attr.prebuf = -1U;
- buffer_attr.tlength = device->m_buffersize;
+ data_t* buffer;
- AUD_pa_stream_set_buffer_attr(stream, &buffer_attr, nullptr, nullptr);
- }
-}
+ size_t sample_size = AUD_DEVICE_SAMPLE_SIZE(device->m_specs);
-void PulseAudioDevice::runMixingThread()
-{
- for(;;)
+ while(total_bytes > 0)
{
+ size_t num_bytes = total_bytes;
+
+ AUD_pa_stream_begin_write(stream, reinterpret_cast<void**>(&buffer), &num_bytes);
+
+ size_t readsamples = device->m_ring_buffer.getReadSize();
+
+ readsamples = std::min(readsamples, size_t(num_bytes)) / sample_size;
+
+ device->m_ring_buffer.read(buffer, readsamples * sample_size);
+
+ if(readsamples * sample_size < num_bytes)
+ std::memset(buffer + readsamples * sample_size, 0, num_bytes - readsamples * sample_size);
+
+ if(device->m_mixingLock.try_lock())
{
- std::lock_guard<ILockable> lock(*this);
-
- if(shouldStop())
- {
- AUD_pa_stream_cork(m_stream, 1, nullptr, nullptr);
- AUD_pa_stream_flush(m_stream, nullptr, nullptr);
- doStop();
- return;
- }
+ device->m_mixingCondition.notify_all();
+ device->m_mixingLock.unlock();
}
- if(AUD_pa_stream_is_corked(m_stream))
- AUD_pa_stream_cork(m_stream, 0, nullptr, nullptr);
+ AUD_pa_stream_write(stream, reinterpret_cast<void*>(buffer), num_bytes, nullptr, 0, PA_SEEK_RELATIVE);
- // similar to AUD_pa_mainloop_iterate(m_mainloop, false, nullptr); except with a longer timeout
- AUD_pa_mainloop_prepare(m_mainloop, 1 << 14);
- AUD_pa_mainloop_poll(m_mainloop);
- AUD_pa_mainloop_dispatch(m_mainloop);
+ total_bytes -= num_bytes;
}
}
+void PulseAudioDevice::playing(bool playing)
+{
+ m_playback = playing;
+
+ AUD_pa_threaded_mainloop_lock(m_mainloop);
+ AUD_pa_stream_cork(m_stream, playing ? 0 : 1, nullptr, nullptr);
+ AUD_pa_threaded_mainloop_unlock(m_mainloop);
+}
+
PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buffersize) :
+ m_synchronizer(this),
+ m_playback(false),
m_state(PA_CONTEXT_UNCONNECTED),
+ m_valid(true),
m_underflows(0)
{
- m_mainloop = AUD_pa_mainloop_new();
+ m_mainloop = AUD_pa_threaded_mainloop_new();
+
+ AUD_pa_threaded_mainloop_lock(m_mainloop);
- m_context = AUD_pa_context_new(AUD_pa_mainloop_get_api(m_mainloop), name.c_str());
+ m_context = AUD_pa_context_new(AUD_pa_threaded_mainloop_get_api(m_mainloop), name.c_str());
if(!m_context)
{
- AUD_pa_mainloop_free(m_mainloop);
+ AUD_pa_threaded_mainloop_unlock(m_mainloop);
+ AUD_pa_threaded_mainloop_free(m_mainloop);
AUD_THROW(DeviceException, "Could not connect to PulseAudio.");
}
@@ -120,21 +146,26 @@ 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_mainloop_free(m_mainloop);
+ AUD_pa_threaded_mainloop_free(m_mainloop);
AUD_THROW(DeviceException, "Could not connect to PulseAudio.");
break;
default:
- AUD_pa_mainloop_iterate(m_mainloop, true, nullptr);
+ AUD_pa_threaded_mainloop_wait(m_mainloop);
break;
}
}
@@ -182,16 +213,18 @@ 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_mainloop_free(m_mainloop);
+ AUD_pa_threaded_mainloop_free(m_mainloop);
AUD_THROW(DeviceException, "Could not create PulseAudio stream.");
}
AUD_pa_stream_set_write_callback(m_stream, PulseAudio_request, this);
- AUD_pa_stream_set_underflow_callback(m_stream, PulseAudio_underflow, this);
buffersize *= AUD_DEVICE_SAMPLE_SIZE(m_specs);
m_buffersize = buffersize;
@@ -204,31 +237,53 @@ 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_START_CORKED | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr) < 0)
+ m_ring_buffer.resize(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)
{
+ 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_mainloop_free(m_mainloop);
+ AUD_pa_threaded_mainloop_free(m_mainloop);
AUD_THROW(DeviceException, "Could not connect PulseAudio stream.");
}
+ AUD_pa_threaded_mainloop_unlock(m_mainloop);
+
create();
+
+ m_mixingThread = std::thread(&PulseAudioDevice::updateRingBuffer, this);
}
PulseAudioDevice::~PulseAudioDevice()
{
- stopMixingThread();
+ m_valid = false;
+
+ m_mixingLock.lock();
+ m_mixingCondition.notify_all();
+ m_mixingLock.unlock();
+
+ m_mixingThread.join();
+
+ AUD_pa_threaded_mainloop_stop(m_mainloop);
AUD_pa_context_disconnect(m_context);
AUD_pa_context_unref(m_context);
- AUD_pa_mainloop_free(m_mainloop);
+ AUD_pa_threaded_mainloop_free(m_mainloop);
destroy();
}
+ISynchronizer *PulseAudioDevice::getSynchronizer()
+{
+ return &m_synchronizer;
+}
+
class PulseAudioDeviceFactory : public IDeviceFactory
{
private: