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:
authorJörg Müller <nexyon@gmail.com>2021-03-02 19:29:18 +0300
committerJörg Müller <nexyon@gmail.com>2021-03-17 01:21:45 +0300
commitd33339ebf44855b62200a2784c046792d78c75bc (patch)
tree425a1d78d6c40f8b188e499cf0db2bc29e8f2d32
parent7b8fc307dc4ff608fc68e2decee45bae59f0c7c9 (diff)
Audaspace: add support for PulseAudio on Linux
This adds PulseAudio as audio backend on Linux. PulseAudio is the main audio engine used on most, if not all, Linux distributions today. Ref T86590
-rw-r--r--CMakeLists.txt9
-rwxr-xr-xbuild_files/build_environment/install_deps.sh33
-rw-r--r--build_files/buildbot/config/blender_linux.cmake1
-rw-r--r--build_files/cmake/Modules/FindPulse.cmake60
-rw-r--r--build_files/cmake/config/blender_full.cmake1
-rw-r--r--build_files/cmake/config/blender_headless.cmake1
-rw-r--r--build_files/cmake/config/blender_lite.cmake1
-rw-r--r--build_files/cmake/config/blender_release.cmake1
-rw-r--r--build_files/cmake/config/bpy_module.cmake1
-rw-r--r--build_files/cmake/macros.cmake3
-rw-r--r--build_files/cmake/platform/platform_unix.cmake8
-rw-r--r--doc/doxygen/doxygen.intern.h3
-rw-r--r--extern/audaspace/CMakeLists.txt55
-rw-r--r--extern/audaspace/blender_config.cmake3
-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
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt4
-rw-r--r--source/blender/python/intern/CMakeLists.txt4
-rw-r--r--source/blender/python/intern/bpy_app_build_options.c7
-rw-r--r--source/creator/creator_args.c2
23 files changed, 728 insertions, 2 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c0b9b28c610..c9af0b2285c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -311,6 +311,12 @@ endif()
if(UNIX AND NOT APPLE)
option(WITH_SDL_DYNLOAD "Enable runtime dynamic SDL libraries loading" OFF)
endif()
+if(UNIX AND NOT APPLE)
+ option(WITH_PULSEAUDIO "Enable PulseAudio for audio support on Linux" ON)
+ option(WITH_PULSEAUDIO_DYNLOAD "Enable runtime dynamic PulseAudio libraries loading" OFF)
+else()
+ set(WITH_PULSEAUDIO OFF)
+endif()
# Compression
option(WITH_LZO "Enable fast LZO compression (used for pointcache)" ON)
@@ -675,6 +681,7 @@ endif()
set_and_warn_dependency(WITH_AUDASPACE WITH_OPENAL OFF)
set_and_warn_dependency(WITH_AUDASPACE WITH_JACK OFF)
+set_and_warn_dependency(WITH_AUDASPACE WITH_PULSEAUDIO OFF)
if(NOT WITH_SDL AND WITH_GHOST_SDL)
message(FATAL_ERROR "WITH_GHOST_SDL requires WITH_SDL")
@@ -1937,6 +1944,8 @@ if(FIRST_RUN)
info_cfg_option(WITH_OPENAL)
info_cfg_option(WITH_SDL)
info_cfg_option(WITH_SDL_DYNLOAD)
+ info_cfg_option(WITH_PULSEAUDIO)
+ info_cfg_option(WITH_PULSEAUDIO_DYNLOAD)
info_cfg_text("Compression:")
info_cfg_option(WITH_LZMA)
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index 3b364c21f7d..7cd21b2885c 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -51,7 +51,7 @@ ARGS=$( \
getopt \
-o s:i:t:h \
--long source:,install:,tmp:,info:,threads:,help,show-deps,no-sudo,no-build,no-confirm,\
-with-all,with-opencollada,with-jack,with-embree,with-oidn,with-nanovdb,\
+with-all,with-opencollada,with-jack,with-pulseaudio,with-embree,with-oidn,with-nanovdb,\
ver-ocio:,ver-oiio:,ver-llvm:,ver-osl:,ver-osd:,ver-openvdb:,ver-xr-openxr:,\
force-all,force-python,force-boost,force-tbb,\
force-ocio,force-openexr,force-oiio,force-llvm,force-osl,force-osd,force-openvdb,\
@@ -157,6 +157,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
--with-jack
Install the jack libraries.
+ --with-pulseaudio
+ Install the pulseaudio libraries.
+
--ver-ocio=<ver>
Force version of OCIO library.
@@ -721,6 +724,9 @@ while true; do
--with-jack)
WITH_JACK=true; shift; continue;
;;
+ --with-pulseaudio)
+ WITH_PULSEAUDIO=true; shift; continue;
+ ;;
--ver-ocio)
OCIO_VERSION="$2"
OCIO_VERSION_MIN=$OCIO_VERSION
@@ -985,6 +991,7 @@ fi
if [ "$WITH_ALL" = true ]; then
WITH_JACK=true
WITH_NANOVDB=true
+ WITH_PULSEAUDIO=true
fi
if [ "$WITH_NANOVDB" = true ]; then
@@ -3877,6 +3884,10 @@ install_DEB() {
fi
fi
+ if [ "$WITH_PULSEAUDIO" = true ]; then
+ _packages="$_packages libpulse-dev"
+ fi
+
PRINT ""
install_packages_DEB $_packages
@@ -4499,6 +4510,10 @@ install_RPM() {
_packages="$_packages jack-audio-connection-kit-devel"
fi
+ if [ "$WITH_PULSEAUDIO" = true ]; then
+ _packages="$_packages pulseaudio-libs-devel"
+ fi
+
PRINT ""
install_packages_RPM $_packages
@@ -4542,6 +4557,10 @@ install_RPM() {
X264_USE=true
fi
+ if [ "$WITH_PULSEAUDIO" = true ]; then
+ _packages="$_packages libpulse-devel"
+ fi
+
if [ "$WITH_ALL" = true ]; then
PRINT ""
XVID_DEV="libxvidcore-devel"
@@ -5073,6 +5092,10 @@ install_ARCH() {
_packages="$_packages jack2"
fi
+ if [ "$WITH_PULSEAUDIO" = true ]; then
+ _packages="$_packages libpulse"
+ fi
+
PRINT ""
install_packages_ARCH $_packages
@@ -5888,6 +5911,14 @@ print_info() {
_buildargs="$_buildargs $_1 $_2"
fi
+ if [ "$WITH_PULSEAUDIO" = true ]; then
+ _1="-D WITH_PULSEAUDIO=ON"
+ _2="-D WITH_PULSEAUDIO_DYNLOAD=ON"
+ PRINT " $_1"
+ PRINT " $_2"
+ _buildargs="$_buildargs $_1 $_2"
+ fi
+
if [ "$ALEMBIC_SKIP" = false ]; then
_1="-D WITH_ALEMBIC=ON"
PRINT " $_1"
diff --git a/build_files/buildbot/config/blender_linux.cmake b/build_files/buildbot/config/blender_linux.cmake
index 29004654807..20b4821959c 100644
--- a/build_files/buildbot/config/blender_linux.cmake
+++ b/build_files/buildbot/config/blender_linux.cmake
@@ -16,6 +16,7 @@ set(WITH_DOC_MANPAGE OFF CACHE BOOL "" FORCE)
# Options which are specific to Linux release builds only
set(WITH_JACK_DYNLOAD ON CACHE BOOL "" FORCE)
+set(WITH_PULSEAUDIO_DYNLOAD ON CACHE BOOL "" FORCE)
set(WITH_SDL_DYNLOAD ON CACHE BOOL "" FORCE)
# ######## Release environment specific settings ########
diff --git a/build_files/cmake/Modules/FindPulse.cmake b/build_files/cmake/Modules/FindPulse.cmake
new file mode 100644
index 00000000000..d5c37e7df10
--- /dev/null
+++ b/build_files/cmake/Modules/FindPulse.cmake
@@ -0,0 +1,60 @@
+# - Find PulseAudio library
+# Find the native PulseAudio includes and library
+# This module defines
+# LIBPULSE_INCLUDE_DIRS, where to find pulse/pulseaudio.h, Set when
+# LIBPULSE_INCLUDE_DIR is found.
+# LIBPULSE_LIBRARIES, libraries to link against to use PulseAudio.
+# LIBPULSE_ROOT_DIR, The base directory to search for PulseAudio.
+# This can also be an environment variable.
+# PULSE_FOUND, If false, do not try to use PulseAudio.
+#
+# also defined, but not for general use are
+# LIBPULSE_LIBRARY, where to find the PulseAudio library.
+
+#=============================================================================
+# Copyright 2021 Blender Foundation.
+#
+# Distributed under the OSI-approved BSD 3-Clause License,
+# see accompanying file BSD-3-Clause-license.txt for details.
+#=============================================================================
+
+# If LIBPULSE_ROOT_DIR was defined in the environment, use it.
+IF(NOT LIBPULSE_ROOT_DIR AND NOT $ENV{LIBPULSE_ROOT_DIR} STREQUAL "")
+ SET(LIBPULSE_ROOT_DIR $ENV{LIBPULSE_ROOT_DIR})
+ENDIF()
+
+SET(_pulse_SEARCH_DIRS
+ ${LIBPULSE_ROOT_DIR}
+)
+
+FIND_PATH(LIBPULSE_INCLUDE_DIR pulse/pulseaudio.h
+ HINTS
+ ${_pulse_SEARCH_DIRS}
+ PATH_SUFFIXES
+ include
+)
+
+FIND_LIBRARY(LIBPULSE_LIBRARY
+ NAMES
+ pulse
+ HINTS
+ ${_pulse_SEARCH_DIRS}
+ PATH_SUFFIXES
+ lib64 lib
+ )
+
+# handle the QUIETLY and REQUIRED arguments and set PULSE_FOUND to TRUE if
+# all listed variables are TRUE
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(Pulse DEFAULT_MSG
+ LIBPULSE_LIBRARY LIBPULSE_INCLUDE_DIR)
+
+IF(PULSE_FOUND)
+ SET(LIBPULSE_LIBRARIES ${LIBPULSE_LIBRARY})
+ SET(LIBPULSE_INCLUDE_DIRS ${LIBPULSE_INCLUDE_DIR})
+ENDIF()
+
+MARK_AS_ADVANCED(
+ LIBPULSE_INCLUDE_DIR
+ LIBPULSE_LIBRARY
+)
diff --git a/build_files/cmake/config/blender_full.cmake b/build_files/cmake/config/blender_full.cmake
index 75f78befb60..bfc390dc13b 100644
--- a/build_files/cmake/config/blender_full.cmake
+++ b/build_files/cmake/config/blender_full.cmake
@@ -65,6 +65,7 @@ endif()
if(UNIX AND NOT APPLE)
set(WITH_DOC_MANPAGE ON CACHE BOOL "" FORCE)
set(WITH_GHOST_XDND ON CACHE BOOL "" FORCE)
+ set(WITH_PULSEAUDIO ON CACHE BOOL "" FORCE)
set(WITH_X11_XINPUT ON CACHE BOOL "" FORCE)
set(WITH_X11_XF86VMODE ON CACHE BOOL "" FORCE)
endif()
diff --git a/build_files/cmake/config/blender_headless.cmake b/build_files/cmake/config/blender_headless.cmake
index 53163986b7a..761cb5b2d93 100644
--- a/build_files/cmake/config/blender_headless.cmake
+++ b/build_files/cmake/config/blender_headless.cmake
@@ -11,6 +11,7 @@ set(WITH_HEADLESS ON CACHE BOOL "" FORCE)
# so the python module doesn't hold the audio device and loads quickly.
set(WITH_AUDASPACE OFF CACHE BOOL "" FORCE)
set(WITH_JACK OFF CACHE BOOL "" FORCE)
+set(WITH_PULSEAUDIO OFF CACHE BOOL "" FORCE)
set(WITH_SDL OFF CACHE BOOL "" FORCE)
set(WITH_OPENAL OFF CACHE BOOL "" FORCE)
set(WITH_CODEC_FFMPEG OFF CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake
index 680734aba6e..76666b1215d 100644
--- a/build_files/cmake/config/blender_lite.cmake
+++ b/build_files/cmake/config/blender_lite.cmake
@@ -54,6 +54,7 @@ set(WITH_OPENSUBDIV OFF CACHE BOOL "" FORCE)
set(WITH_OPENVDB OFF CACHE BOOL "" FORCE)
set(WITH_POTRACE OFF CACHE BOOL "" FORCE)
set(WITH_PUGIXML OFF CACHE BOOL "" FORCE)
+set(WITH_PULSEAUDIO OFF CACHE BOOL "" FORCE)
set(WITH_NANOVDB OFF CACHE BOOL "" FORCE)
set(WITH_QUADRIFLOW OFF CACHE BOOL "" FORCE)
set(WITH_SDL OFF CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/config/blender_release.cmake b/build_files/cmake/config/blender_release.cmake
index 973d6cdb34e..67d34b5edb2 100644
--- a/build_files/cmake/config/blender_release.cmake
+++ b/build_files/cmake/config/blender_release.cmake
@@ -69,6 +69,7 @@ endif()
if(UNIX AND NOT APPLE)
set(WITH_DOC_MANPAGE ON CACHE BOOL "" FORCE)
set(WITH_GHOST_XDND ON CACHE BOOL "" FORCE)
+ set(WITH_PULSEAUDIO ON CACHE BOOL "" FORCE)
set(WITH_X11_XINPUT ON CACHE BOOL "" FORCE)
set(WITH_X11_XF86VMODE ON CACHE BOOL "" FORCE)
endif()
diff --git a/build_files/cmake/config/bpy_module.cmake b/build_files/cmake/config/bpy_module.cmake
index 7fc68f97f29..b0daed2a4c0 100644
--- a/build_files/cmake/config/bpy_module.cmake
+++ b/build_files/cmake/config/bpy_module.cmake
@@ -16,6 +16,7 @@ set(WITH_PYTHON_INSTALL OFF CACHE BOOL "" FORCE)
# so the python module doesn't hold the audio device and loads quickly.
set(WITH_AUDASPACE OFF CACHE BOOL "" FORCE)
set(WITH_JACK OFF CACHE BOOL "" FORCE)
+set(WITH_PULSEAUDIO OFF CACHE BOOL "" FORCE)
set(WITH_SDL OFF CACHE BOOL "" FORCE)
set(WITH_OPENAL OFF CACHE BOOL "" FORCE)
set(WITH_CODEC_FFMPEG OFF CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake
index cad1d86b75a..813ac013cdf 100644
--- a/build_files/cmake/macros.cmake
+++ b/build_files/cmake/macros.cmake
@@ -569,6 +569,9 @@ function(SETUP_LIBDIRS)
if(WITH_JACK AND NOT WITH_JACK_DYNLOAD)
link_directories(${JACK_LIBPATH})
endif()
+ if(WITH_PULSEAUDIO AND NOT WITH_PULSEAUDIO_DYNLOAD)
+ link_directories(${LIBPULSE_LIBPATH})
+ endif()
if(WITH_CODEC_SNDFILE)
link_directories(${LIBSNDFILE_LIBPATH})
endif()
diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake
index 47c788c7eb6..7791112c6ab 100644
--- a/build_files/cmake/platform/platform_unix.cmake
+++ b/build_files/cmake/platform/platform_unix.cmake
@@ -552,6 +552,14 @@ if(WITH_JACK)
endif()
endif()
+# Pulse is intended to use the system library.
+if(WITH_PULSEAUDIO)
+ find_package_wrapper(Pulse)
+ if(NOT PULSE_FOUND)
+ set(WITH_PULSEAUDIO OFF)
+ endif()
+endif()
+
# Audio IO
if(WITH_SYSTEM_AUDASPACE)
find_package_wrapper(Audaspace)
diff --git a/doc/doxygen/doxygen.intern.h b/doc/doxygen/doxygen.intern.h
index 98fb039c90b..03f8ef2fb14 100644
--- a/doc/doxygen/doxygen.intern.h
+++ b/doc/doxygen/doxygen.intern.h
@@ -60,6 +60,9 @@
/** \defgroup audopenal Audaspace OpenAL
* \ingroup audaspace
*/
+/** \defgroup audpulseaudio Audaspace PulseAudio
+ * \ingroup audaspace
+ */
/** \defgroup audpython Audaspace Python
* \ingroup audaspace
*/
diff --git a/extern/audaspace/CMakeLists.txt b/extern/audaspace/CMakeLists.txt
index 996d4b27911..92b1cbcb290 100644
--- a/extern/audaspace/CMakeLists.txt
+++ b/extern/audaspace/CMakeLists.txt
@@ -285,6 +285,10 @@ if(AUDASPACE_STANDALONE)
option(WITH_SDL "Build With SDL" TRUE)
option(WITH_STRICT_DEPENDENCIES "Error and abort instead of warning if a library is not found." FALSE)
+ if(NOT WIN32 AND NOT APPLE)
+ option(WITH_PULSEAUDIO "Build With PulseAudio" TRUE)
+ endif()
+
if(WITH_STRICT_DEPENDENCIES)
set(PACKAGE_OPTION REQUIRED)
endif()
@@ -306,10 +310,12 @@ if(AUDASPACE_STANDALONE)
cmake_dependent_option(PLUGIN_JACK "Build JACK Plugin" TRUE "WITH_JACK;SHARED_LIBRARY" FALSE)
cmake_dependent_option(PLUGIN_LIBSNDFILE "Build LibSndFile Plugin" TRUE "WITH_LIBSNDFILE;SHARED_LIBRARY" FALSE)
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(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)
+ cmake_dependent_option(DYNLOAD_PULSEAUDIO "Dynamically load PulseAudio" FALSE "WITH_PULSEAUDIO" FALSE)
cmake_dependent_option(WITH_BINDING_DOCS "Build C/Python HTML Documentation with Sphinx" TRUE "WITH_PYTHON_MODULE" FALSE)
endif()
@@ -598,6 +604,42 @@ if(WITH_OPENAL)
endif()
endif()
+# PulseAudio
+if(WITH_PULSEAUDIO)
+ if(AUDASPACE_STANDALONE)
+ find_package(LibPulse ${PACKAGE_OPTION})
+ endif()
+
+ if(LIBPULSE_FOUND)
+ set(PULSEAUDIO_SRC
+ plugins/pulseaudio/PulseAudioDevice.cpp
+ plugins/pulseaudio/PulseAudioLibrary.cpp
+ )
+ set(PULSEAUDIO_HDR
+ plugins/pulseaudio/PulseAudioDevice.h
+ plugins/pulseaudio/PulseAudioLibrary.h
+ plugins/pulseaudio/PulseAudioSymbols.h
+ )
+
+ if(DYNLOAD_PULSEAUDIO)
+ add_definitions(-DDYNLOAD_PULSEAUDIO)
+ endif()
+
+ if(NOT PLUGIN_PULSEAUDIO)
+ list(APPEND INCLUDE ${LIBPULSE_INCLUDE_DIR})
+ if(NOT DYNLOAD_PULSEAUDIO)
+ list(APPEND LIBRARIES ${LIBPULSE_LIBRARY})
+ endif()
+ list(APPEND SRC ${PULSEAUDIO_SRC})
+ list(APPEND HDR ${PULSEAUDIO_HDR})
+ list(APPEND STATIC_PLUGINS PulseAudioDevice)
+ endif()
+ else()
+ set(WITH_PULSEAUDIO FALSE CACHE BOOL "Build With PulseAudio" FORCE)
+ message(WARNING "PulseAudio not found, plugin will not be built.")
+ endif()
+endif()
+
# Python
if(WITH_PYTHON)
if(AUDASPACE_STANDALONE)
@@ -797,6 +839,19 @@ if(WITH_OPENAL AND PLUGIN_OPENAL)
install(TARGETS audopenal DESTINATION ${DEFAULT_PLUGIN_PATH})
endif()
+if(WITH_PULSEAUDIO AND PLUGIN_PULSEAUDIO)
+ add_definitions(-DPULSEAUDIO_PLUGIN)
+ include_directories(${INCLUDE} ${LIBPULSE_INCLUDE_DIR})
+ add_library(audpulseaudio SHARED ${PULSEAUDIO_SRC} ${PULSEAUDIO_HDR} ${HDR})
+ set_target_properties(audpulseaudio PROPERTIES SOVERSION ${AUDASPACE_VERSION})
+ if(DYNLOAD_PULSEAUDIO)
+ target_link_libraries(audpulseaudio audaspace)
+ else()
+ target_link_libraries(audpulseaudio audaspace ${LIBPULSE_LIBRARY})
+ endif()
+ install(TARGETS audpulseaudio DESTINATION ${DEFAULT_PLUGIN_PATH})
+endif()
+
if(WITH_SDL AND PLUGIN_SDL)
add_definitions(-DSDL_PLUGIN)
include_directories(${INCLUDE} ${SDL_INCLUDE_DIR})
diff --git a/extern/audaspace/blender_config.cmake b/extern/audaspace/blender_config.cmake
index 12810e2b044..22e6564ad89 100644
--- a/extern/audaspace/blender_config.cmake
+++ b/extern/audaspace/blender_config.cmake
@@ -14,15 +14,18 @@ set(PLUGIN_FFMPEG FALSE) # "Build FFMPEG Plugin"
set(PLUGIN_JACK FALSE) # "Build JACK Plugin"
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(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"
set(WITH_BINDING_DOCS FALSE) # "Build C/Python HTML Documentation with Sphinx"
set(DEFAULT_PLUGIN_PATH "plugins") # "Default plugin installation and loading path."
set(FFMPEG_FOUND ${WITH_CODEC_FFMPEG})
set(JACK_FOUND ${WITH_JACK})
set(LIBSNDFILE_FOUND ${WITH_CODEC_SNDFILE})
set(OPENAL_FOUND ${WITH_OPENAL})
+set(LIBPULSE_FOUND ${WITH_PULSEAUDIO})
set(PYTHONLIBS_FOUND TRUE)
set(NUMPY_FOUND ${WITH_PYTHON_NUMPY})
set(NUMPY_INCLUDE_DIRS ${PYTHON_NUMPY_INCLUDE_DIRS})
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);
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index c04ccf59327..98447a29b6e 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -302,6 +302,10 @@ if(WITH_JACK)
add_definitions(-DWITH_JACK)
endif()
+if(WITH_PULSEAUDIO)
+ add_definitions(-DWITH_PULSEAUDIO)
+endif()
+
if(WITH_OPENCOLLADA)
add_definitions(-DWITH_COLLADA)
endif()
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 56ef5c8187a..5991fa0ac8f 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -284,6 +284,10 @@ if(WITH_LIBMV)
add_definitions(-DWITH_LIBMV)
endif()
+if(WITH_PULSEAUDIO)
+ add_definitions(-DWITH_PULSEAUDIO)
+endif()
+
if(WITH_MOD_OCEANSIM)
add_definitions(-DWITH_OCEANSIM)
endif()
diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c
index aaceb7b393f..68c654bc879 100644
--- a/source/blender/python/intern/bpy_app_build_options.c
+++ b/source/blender/python/intern/bpy_app_build_options.c
@@ -50,6 +50,7 @@ static PyStructSequence_Field app_builtopts_info_fields[] = {
{"sdl", NULL},
{"sdl_dynload", NULL},
{"jack", NULL},
+ {"pulseaudio", NULL},
{"libmv", NULL},
{"mod_oceansim", NULL},
{"mod_remesh", NULL},
@@ -217,6 +218,12 @@ static PyObject *make_builtopts_info(void)
SetObjIncref(Py_False);
#endif
+#ifdef WITH_PULSEAUDIO
+ SetObjIncref(Py_True);
+#else
+ SetObjIncref(Py_False);
+#endif
+
#ifdef WITH_LIBMV
SetObjIncref(Py_True);
#else
diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c
index 22a56165ab4..6110f4172b1 100644
--- a/source/creator/creator_args.c
+++ b/source/creator/creator_args.c
@@ -1330,7 +1330,7 @@ static const char arg_handle_audio_set_doc[] =
"\n\t"
"Force sound system to a specific device."
"\n\t"
- "'None' 'SDL' 'OpenAL' 'JACK'.";
+ "'None' 'SDL' 'OpenAL' 'JACK' 'PulseAudio'.";
static int arg_handle_audio_set(int argc, const char **argv, void *UNUSED(data))
{
if (argc < 1) {