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')
-rw-r--r--extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp282
-rw-r--r--extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h105
-rw-r--r--extern/audaspace/plugins/pulseaudio/PulseAudioLibrary.cpp59
-rw-r--r--extern/audaspace/plugins/pulseaudio/PulseAudioLibrary.h46
-rw-r--r--extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h41
5 files changed, 533 insertions, 0 deletions
diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp
new file mode 100644
index 00000000000..0a50d5db2c7
--- /dev/null
+++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp
@@ -0,0 +1,282 @@
+/*******************************************************************************
+ * 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 "PulseAudioDevice.h"
+#include "PulseAudioLibrary.h"
+#include "devices/DeviceManager.h"
+#include "devices/IDeviceFactory.h"
+#include "Exception.h"
+#include "IReader.h"
+
+AUD_NAMESPACE_BEGIN
+
+void PulseAudioDevice::PulseAudio_state_callback(pa_context *context, void *data)
+{
+ PulseAudioDevice* device = (PulseAudioDevice*)data;
+
+ device->m_state = AUD_pa_context_get_state(context);
+
+ AUD_pa_threaded_mainloop_signal(device->m_mainloop, 0);
+}
+
+void PulseAudioDevice::PulseAudio_request(pa_stream *stream, size_t num_bytes, void *data)
+{
+ PulseAudioDevice* device = (PulseAudioDevice*)data;
+
+ void* buffer;
+
+ AUD_pa_stream_begin_write(stream, &buffer, &num_bytes);
+
+ device->mix((data_t*)buffer, num_bytes / AUD_DEVICE_SAMPLE_SIZE(device->m_specs));
+
+ AUD_pa_stream_write(stream, buffer, num_bytes, nullptr, 0, PA_SEEK_RELATIVE);
+}
+
+void PulseAudioDevice::PulseAudio_underflow(pa_stream *stream, void *data)
+{
+ PulseAudioDevice* device = (PulseAudioDevice*)data;
+
+ DeviceSpecs specs = device->getSpecs();
+
+ if(++device->m_underflows > 4 && device->m_buffersize < AUD_DEVICE_SAMPLE_SIZE(specs) * specs.rate * 2)
+ {
+ device->m_buffersize <<= 1;
+ device->m_underflows = 0;
+
+ pa_buffer_attr buffer_attr;
+
+ buffer_attr.fragsize = -1U;
+ buffer_attr.maxlength = -1U;
+ buffer_attr.minreq = -1U;
+ buffer_attr.prebuf = -1U;
+ buffer_attr.tlength = device->m_buffersize;
+
+ AUD_pa_stream_set_buffer_attr(stream, &buffer_attr, nullptr, nullptr);
+ }
+}
+
+void PulseAudioDevice::playing(bool playing)
+{
+ m_playback = playing;
+
+ AUD_pa_stream_cork(m_stream, playing ? 0 : 1, nullptr, 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();
+
+ AUD_pa_threaded_mainloop_lock(m_mainloop);
+
+ m_context = AUD_pa_context_new(AUD_pa_threaded_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_THROW(DeviceException, "Could not connect to PulseAudio.");
+ }
+
+ AUD_pa_context_set_state_callback(m_context, PulseAudio_state_callback, this);
+
+ 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_THROW(DeviceException, "Could not connect to PulseAudio.");
+ break;
+ default:
+ AUD_pa_threaded_mainloop_wait(m_mainloop);
+ break;
+ }
+ }
+
+ 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;
+
+ m_specs = specs;
+
+ pa_sample_spec sample_spec;
+
+ sample_spec.channels = specs.channels;
+ sample_spec.format = PA_SAMPLE_FLOAT32;
+ sample_spec.rate = specs.rate;
+
+ switch(m_specs.format)
+ {
+ case FORMAT_U8:
+ sample_spec.format = PA_SAMPLE_U8;
+ break;
+ case FORMAT_S16:
+ sample_spec.format = PA_SAMPLE_S16NE;
+ break;
+ case FORMAT_S24:
+ sample_spec.format = PA_SAMPLE_S24NE;
+ break;
+ case FORMAT_S32:
+ sample_spec.format = PA_SAMPLE_S32NE;
+ break;
+ case FORMAT_FLOAT32:
+ sample_spec.format = PA_SAMPLE_FLOAT32;
+ break;
+ case FORMAT_FLOAT64:
+ m_specs.format = FORMAT_FLOAT32;
+ break;
+ default:
+ break;
+ }
+
+ m_stream = AUD_pa_stream_new(m_context, "Playback", &sample_spec, nullptr);
+
+ 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_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);
+
+ pa_buffer_attr buffer_attr;
+
+ buffer_attr.fragsize = -1U;
+ buffer_attr.maxlength = -1U;
+ buffer_attr.minreq = -1U;
+ 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)
+ {
+ 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_THROW(DeviceException, "Could not connect PulseAudio stream.");
+ }
+
+ AUD_pa_threaded_mainloop_unlock(m_mainloop);
+
+ create();
+}
+
+PulseAudioDevice::~PulseAudioDevice()
+{
+ 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);
+
+ destroy();
+}
+
+class PulseAudioDeviceFactory : public IDeviceFactory
+{
+private:
+ DeviceSpecs m_specs;
+ int m_buffersize;
+ std::string m_name;
+
+public:
+ PulseAudioDeviceFactory() :
+ m_buffersize(AUD_DEFAULT_BUFFER_SIZE),
+ m_name("Audaspace")
+ {
+ m_specs.format = FORMAT_FLOAT32;
+ m_specs.channels = CHANNELS_STEREO;
+ m_specs.rate = RATE_48000;
+ }
+
+ virtual std::shared_ptr<IDevice> openDevice()
+ {
+ return std::shared_ptr<IDevice>(new PulseAudioDevice(m_name, 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)
+ {
+ m_name = name;
+ }
+};
+
+void PulseAudioDevice::registerPlugin()
+{
+ if(loadPulseAudio())
+ DeviceManager::registerDevice("PulseAudio", std::shared_ptr<IDeviceFactory>(new PulseAudioDeviceFactory));
+}
+
+#ifdef PULSEAUDIO_PLUGIN
+extern "C" AUD_PLUGIN_API void registerPlugin()
+{
+ PulseAudioDevice::registerPlugin();
+}
+
+extern "C" AUD_PLUGIN_API const char* getName()
+{
+ return "PulseAudio";
+}
+#endif
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h
new file mode 100644
index 00000000000..9efae5128b1
--- /dev/null
+++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * 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 PULSEAUDIO_PLUGIN
+#define AUD_BUILD_PLUGIN
+#endif
+
+/**
+ * @file PulseAudioDevice.h
+ * @ingroup plugin
+ * The PulseAudioDevice class.
+ */
+
+#include "devices/SoftwareDevice.h"
+
+#include <pulse/pulseaudio.h>
+
+AUD_NAMESPACE_BEGIN
+
+/**
+ * This device plays back through PulseAudio, the simple direct media layer.
+ */
+class AUD_PLUGIN_API PulseAudioDevice : public SoftwareDevice
+{
+private:
+ /**
+ * Whether there is currently playback.
+ */
+ volatile bool m_playback;
+
+ pa_threaded_mainloop* m_mainloop;
+ pa_context* m_context;
+ pa_stream* m_stream;
+ pa_context_state_t m_state;
+
+ int m_buffersize;
+ uint32_t m_underflows;
+
+ /**
+ * Reports the state of the PulseAudio server connection.
+ * \param context The PulseAudio context.
+ * \param data The PulseAudio device.
+ */
+ AUD_LOCAL static void PulseAudio_state_callback(pa_context* context, void* data);
+
+ /**
+ * Supplies the next samples to PulseAudio.
+ * \param stream The PulseAudio stream.
+ * \param num_bytes The length in bytes to be supplied.
+ * \param data The PulseAudio device.
+ */
+ AUD_LOCAL static void PulseAudio_request(pa_stream* stream, size_t num_bytes, void* data);
+
+ /**
+ * Reports an underflow from the PulseAudio server.
+ * Automatically adjusts the latency if this happens too often.
+ * @param stream The PulseAudio stream.
+ * \param data The PulseAudio device.
+ */
+ AUD_LOCAL static void PulseAudio_underflow(pa_stream* stream, void* data);
+
+ // 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.
+ * \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.
+ */
+ PulseAudioDevice(std::string name, DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE);
+
+ /**
+ * Closes the PulseAudio audio device.
+ */
+ virtual ~PulseAudioDevice();
+
+ /**
+ * Registers this plugin.
+ */
+ static void registerPlugin();
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioLibrary.cpp b/extern/audaspace/plugins/pulseaudio/PulseAudioLibrary.cpp
new file mode 100644
index 00000000000..66a0fae5ab6
--- /dev/null
+++ b/extern/audaspace/plugins/pulseaudio/PulseAudioLibrary.cpp
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * 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.
+ ******************************************************************************/
+
+#define PULSEAUDIO_LIBRARY_IMPLEMENTATION
+
+#include <string>
+#include <array>
+
+#include "PulseAudioLibrary.h"
+
+#ifdef DYNLOAD_PULSEAUDIO
+#include "plugin/PluginManager.h"
+#endif
+
+AUD_NAMESPACE_BEGIN
+
+bool loadPulseAudio()
+{
+#ifdef DYNLOAD_PULSEAUDIO
+ std::array<const std::string, 2> names = {"libpulse.so", "libpulse.so.0"};
+
+ void* handle = nullptr;
+
+ for(auto& name : names)
+ {
+ handle = PluginManager::openLibrary(name);
+ if(handle)
+ break;
+ }
+
+ if (!handle)
+ return false;
+
+#define PULSEAUDIO_SYMBOL(sym) AUD_##sym = reinterpret_cast<decltype(&sym)>(PluginManager::lookupLibrary(handle, #sym))
+#else
+#define PULSEAUDIO_SYMBOL(sym) AUD_##sym = &sym
+#endif
+
+#include "PulseAudioSymbols.h"
+
+#undef PULSEAUDIO_SYMBOL
+
+ return AUD_pa_context_new != nullptr;
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioLibrary.h b/extern/audaspace/plugins/pulseaudio/PulseAudioLibrary.h
new file mode 100644
index 00000000000..ae6c5951d11
--- /dev/null
+++ b/extern/audaspace/plugins/pulseaudio/PulseAudioLibrary.h
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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 PULSEAUDIO_PLUGIN
+#define AUD_BUILD_PLUGIN
+#endif
+
+/**
+ * @file PulseAudioLibrary.h
+ * @ingroup plugin
+ */
+
+#include "Audaspace.h"
+
+#include <pulse/pulseaudio.h>
+
+AUD_NAMESPACE_BEGIN
+
+#ifdef PULSEAUDIO_LIBRARY_IMPLEMENTATION
+#define PULSEAUDIO_SYMBOL(sym) decltype(&sym) AUD_##sym
+#else
+#define PULSEAUDIO_SYMBOL(sym) extern decltype(&sym) AUD_##sym
+#endif
+
+#include "PulseAudioSymbols.h"
+
+#undef PULSEAUDIO_SYMBOL
+
+bool loadPulseAudio();
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h b/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h
new file mode 100644
index 00000000000..9cefbc0c7e2
--- /dev/null
+++ b/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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.
+ ******************************************************************************/
+
+PULSEAUDIO_SYMBOL(pa_context_connect);
+PULSEAUDIO_SYMBOL(pa_context_disconnect);
+PULSEAUDIO_SYMBOL(pa_context_get_state);
+PULSEAUDIO_SYMBOL(pa_context_new);
+PULSEAUDIO_SYMBOL(pa_context_set_state_callback);
+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_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);