Audaspace: merge changes from upstream.
Add the Pipewire backend and Pulseaudio fixes. There are also some other minor changes.
This commit is contained in:
committed by
Sebastian Parborg
parent
33af6d9c5b
commit
bc39ee692d
6
extern/audaspace/AUTHORS
vendored
6
extern/audaspace/AUTHORS
vendored
@@ -19,9 +19,15 @@ The Equalizer sound effect has been added by
|
||||
|
||||
- Marcos Perez
|
||||
|
||||
Some performance improvements, especially to the JOSResampler have been made by:
|
||||
|
||||
- Aras Pranckevičius
|
||||
|
||||
Several people provided fixes:
|
||||
|
||||
- Aaron Carlisle
|
||||
- Sebastian Parborg
|
||||
- Leon Zandman
|
||||
- Richard Antalik
|
||||
- Robert-André Mauchin
|
||||
- Lalit Shankar Chowdhury
|
||||
|
||||
27
extern/audaspace/CHANGES
vendored
27
extern/audaspace/CHANGES
vendored
@@ -1,3 +1,30 @@
|
||||
Audaspace 1.5
|
||||
|
||||
- Performance improvements and two more quality presets for the JOS resampler.
|
||||
- Bugfixes for PulseAudio.
|
||||
- CoreAudio device is only opened on demand.
|
||||
- FFMPEG 7 support and dropped support for FFMPEG older than 6.
|
||||
- Various minor fixes and improvements.
|
||||
|
||||
Detailed list of changes:
|
||||
|
||||
3566597 FFMPEG Update
|
||||
c158a27 Porting bugfix from Blender.
|
||||
edb388b Bugfix for PulseAudio: buffers not cleared after pause.
|
||||
6affec8 Port fix from Blender.
|
||||
a99262e Load CoreAudio device on demand
|
||||
ae29ce2 Adding default quality parameter also to JOSResample.
|
||||
dae2044 Renaming resample quality enum values in the C API.
|
||||
8c810b5 Add resampling quality parameter to various mixdown functions, and to PySound resample
|
||||
1b9b7f9 Add ResampleQuality enum and APIs to control resampling quality
|
||||
4bfd596 Avoid std::string copies by value
|
||||
0d18fe7 JOSResampleReader performance and faster quality settings (#18)
|
||||
2a300d9 Filter design python script.
|
||||
5f745ff Bugfix for reading an animated property with a negative time value.
|
||||
04eeb56 Fix python documentation.
|
||||
631850b Update AUTHORS.
|
||||
db2ff58 Improve seeking for animated sequences
|
||||
|
||||
Audaspace 1.4
|
||||
|
||||
- Support for OS specific/native audio devices/backends has been added, that is PulseAudio (Linux), WASAPI (Windows) and CoreAudio (MacOS).
|
||||
|
||||
95
extern/audaspace/CMakeLists.txt
vendored
95
extern/audaspace/CMakeLists.txt
vendored
@@ -23,7 +23,7 @@ endif()
|
||||
|
||||
project(audaspace)
|
||||
|
||||
set(AUDASPACE_VERSION 1.4)
|
||||
set(AUDASPACE_VERSION 1.5)
|
||||
set(AUDASPACE_LONG_VERSION ${AUDASPACE_VERSION}.0)
|
||||
|
||||
if(DEFINED AUDASPACE_CMAKE_CFG)
|
||||
@@ -298,6 +298,7 @@ if(AUDASPACE_STANDALONE)
|
||||
endif()
|
||||
if(NOT WIN32 AND NOT APPLE)
|
||||
option(WITH_PULSEAUDIO "Build With PulseAudio" TRUE)
|
||||
option(WITH_PIPEWIRE "Build With PipeWire" TRUE)
|
||||
endif()
|
||||
if(WIN32)
|
||||
option(WITH_WASAPI "Build With WASAPI" TRUE)
|
||||
@@ -306,9 +307,7 @@ if(AUDASPACE_STANDALONE)
|
||||
if(WITH_STRICT_DEPENDENCIES)
|
||||
set(PACKAGE_OPTION REQUIRED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(AUDASPACE_STANDALONE)
|
||||
if(WIN32 OR APPLE)
|
||||
set(DEFAULT_PLUGIN_PATH "." CACHE STRING "Default plugin installation and loading path.")
|
||||
set(DOCUMENTATION_INSTALL_PATH "doc" CACHE PATH "Path where the documentation is installed.")
|
||||
@@ -316,9 +315,7 @@ if(AUDASPACE_STANDALONE)
|
||||
set(DEFAULT_PLUGIN_PATH "${CMAKE_INSTALL_PREFIX}/share/audaspace/plugins" CACHE STRING "Default plugin installation and loading path.")
|
||||
set(DOCUMENTATION_INSTALL_PATH "share/doc/audaspace" CACHE PATH "Path where the documentation is installed.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(AUDASPACE_STANDALONE)
|
||||
cmake_dependent_option(SEPARATE_C "Build C Binding as separate library" TRUE "WITH_C" FALSE)
|
||||
cmake_dependent_option(PLUGIN_COREAUDIO "Build CoreAudio Plugin" TRUE "WITH_COREAUDIO;SHARED_LIBRARY" FALSE)
|
||||
cmake_dependent_option(PLUGIN_FFMPEG "Build FFMPEG Plugin" TRUE "WITH_FFMPEG;SHARED_LIBRARY" FALSE)
|
||||
@@ -326,18 +323,18 @@ if(AUDASPACE_STANDALONE)
|
||||
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_PIPEWIRE "Build PipeWire Plugin" TRUE "WITH_PIPEWIRE;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)
|
||||
cmake_dependent_option(DYNLOAD_PULSEAUDIO "Dynamically load PulseAudio" FALSE "WITH_PULSEAUDIO" FALSE)
|
||||
cmake_dependent_option(DYNLOAD_PIPEWIRE "Dynamically load PipeWire" FALSE "WITH_PIPEWIRE" FALSE)
|
||||
cmake_dependent_option(WITH_BINDING_DOCS "Build C/Python HTML Documentation with Sphinx" TRUE "WITH_PYTHON_MODULE" FALSE)
|
||||
endif()
|
||||
cmake_dependent_option(WITH_VERSIONED_PLUGINS "Build Plugins With Sonumber" TRUE "SHARED_LIBRARY" FALSE)
|
||||
|
||||
# compiler options
|
||||
|
||||
if(AUDASPACE_STANDALONE)
|
||||
# compiler options
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
@@ -726,6 +723,47 @@ if(WITH_PULSEAUDIO)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Pipewire
|
||||
if(WITH_PIPEWIRE)
|
||||
if(AUDASPACE_STANDALONE)
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(PIPEWIRE ${PACKAGE_OPTION} libpipewire-0.3)
|
||||
endif()
|
||||
|
||||
if(PIPEWIRE_FOUND)
|
||||
set(PIPEWIRE_SRC
|
||||
plugins/pipewire/PipeWireDevice.cpp
|
||||
plugins/pipewire/PipeWireLibrary.cpp
|
||||
)
|
||||
set(PIPEWIRE_HDR
|
||||
plugins/pipewire/PipeWireDevice.h
|
||||
plugins/pipewire/PipeWireLibrary.h
|
||||
plugins/pipewire/PipeWireSymbols.h
|
||||
)
|
||||
|
||||
if(DYNLOAD_PIPEWIRE)
|
||||
add_definitions(-DDYNLOAD_PIPEWIRE)
|
||||
endif()
|
||||
|
||||
if(NOT PLUGIN_PIPEWIRE)
|
||||
list(APPEND INCLUDE ${PIPEWIRE_INCLUDE_DIRS})
|
||||
if(NOT DYNLOAD_PIPEWIRE)
|
||||
list(APPEND LIBRARIES ${PIPEWIRE_LIBRARIES})
|
||||
endif()
|
||||
list(APPEND SRC ${PIPEWIRE_SRC})
|
||||
list(APPEND HDR ${PIPEWIRE_HDR})
|
||||
list(APPEND STATIC_PLUGINS PipeWireDevice)
|
||||
endif()
|
||||
else()
|
||||
if(AUDASPACE_STANDALONE)
|
||||
set(WITH_PIPEWIRE FALSE CACHE BOOL "Build With PipeWire" FORCE)
|
||||
else()
|
||||
set(WITH_PIPEWIRE FALSE)
|
||||
endif()
|
||||
message(WARNING "PipeWire not found, plugin will not be built.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Python
|
||||
if(WITH_PYTHON)
|
||||
if(AUDASPACE_STANDALONE)
|
||||
@@ -926,7 +964,9 @@ if(WITH_FFMPEG AND PLUGIN_FFMPEG)
|
||||
include_directories(${INCLUDE} ${FFMPEG_INCLUDE_DIRS})
|
||||
add_library(audffmpeg SHARED ${FFMPEG_SRC} ${FFMPEG_HDR} ${HDR})
|
||||
target_link_libraries(audffmpeg audaspace ${FFMPEG_LIBRARIES})
|
||||
set_target_properties(audffmpeg PROPERTIES SOVERSION ${AUDASPACE_VERSION})
|
||||
if(WITH_VERSIONED_PLUGINS)
|
||||
set_target_properties(audffmpeg PROPERTIES SOVERSION ${AUDASPACE_VERSION})
|
||||
endif()
|
||||
install(TARGETS audffmpeg DESTINATION ${DEFAULT_PLUGIN_PATH})
|
||||
endif()
|
||||
|
||||
@@ -939,7 +979,9 @@ if(WITH_JACK AND PLUGIN_JACK)
|
||||
else()
|
||||
target_link_libraries(audjack audaspace ${JACK_LIBRARIES})
|
||||
endif()
|
||||
set_target_properties(audjack PROPERTIES SOVERSION ${AUDASPACE_VERSION})
|
||||
if(WITH_VERSIONED_PLUGINS)
|
||||
set_target_properties(audjack PROPERTIES SOVERSION ${AUDASPACE_VERSION})
|
||||
endif()
|
||||
install(TARGETS audjack DESTINATION ${DEFAULT_PLUGIN_PATH})
|
||||
endif()
|
||||
|
||||
@@ -947,7 +989,9 @@ if(WITH_LIBSNDFILE AND PLUGIN_LIBSNDFILE)
|
||||
add_definitions(-DLIBSNDFILE_PLUGIN)
|
||||
include_directories(${INCLUDE} ${LIBSNDFILE_INCLUDE_DIRS})
|
||||
add_library(audlibsndfile SHARED ${LIBSNDFILE_SRC} ${LIBSNDFILE_HDR} ${HDR})
|
||||
set_target_properties(audlibsndfile PROPERTIES SOVERSION ${AUDASPACE_VERSION})
|
||||
if(WITH_VERSIONED_PLUGINS)
|
||||
set_target_properties(audlibsndfile PROPERTIES SOVERSION ${AUDASPACE_VERSION})
|
||||
endif()
|
||||
target_link_libraries(audlibsndfile audaspace ${LIBSNDFILE_LIBRARIES})
|
||||
install(TARGETS audlibsndfile DESTINATION ${DEFAULT_PLUGIN_PATH})
|
||||
endif()
|
||||
@@ -956,7 +1000,9 @@ if(WITH_OPENAL AND PLUGIN_OPENAL)
|
||||
add_definitions(-DOPENAL_PLUGIN)
|
||||
include_directories(${INCLUDE} ${OPENAL_INCLUDE_DIR})
|
||||
add_library(audopenal SHARED ${OPENAL_SRC} ${OPENAL_HDR} ${HDR})
|
||||
set_target_properties(audopenal PROPERTIES SOVERSION ${AUDASPACE_VERSION})
|
||||
if(WITH_VERSIONED_PLUGINS)
|
||||
set_target_properties(audopenal PROPERTIES SOVERSION ${AUDASPACE_VERSION})
|
||||
endif()
|
||||
target_link_libraries(audopenal audaspace ${OPENAL_LIBRARY})
|
||||
install(TARGETS audopenal DESTINATION ${DEFAULT_PLUGIN_PATH})
|
||||
endif()
|
||||
@@ -965,7 +1011,9 @@ 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(WITH_VERSIONED_PLUGINS)
|
||||
set_target_properties(audpulseaudio PROPERTIES SOVERSION ${AUDASPACE_VERSION})
|
||||
endif()
|
||||
if(DYNLOAD_PULSEAUDIO)
|
||||
target_link_libraries(audpulseaudio audaspace)
|
||||
else()
|
||||
@@ -974,11 +1022,28 @@ if(WITH_PULSEAUDIO AND PLUGIN_PULSEAUDIO)
|
||||
install(TARGETS audpulseaudio DESTINATION ${DEFAULT_PLUGIN_PATH})
|
||||
endif()
|
||||
|
||||
if(WITH_PIPEWIRE AND PLUGIN_PIPEWIRE)
|
||||
add_definitions(-DPIPEWIRE_PLUGIN)
|
||||
include_directories(${INCLUDE} ${PIPEWIRE_INCLUDE_DIRS})
|
||||
add_library(audpipewire SHARED ${PIPEWIRE_SRC} ${PIPEWIRE_HDR} ${HDR})
|
||||
if(WITH_VERSIONED_PLUGINS)
|
||||
set_target_properties(audpipewire PROPERTIES SOVERSION ${AUDASPACE_VERSION})
|
||||
endif()
|
||||
if(DYNLOAD_PIPEWIRE)
|
||||
target_link_libraries(audpipewire audaspace)
|
||||
else()
|
||||
target_link_libraries(audpipewire audaspace ${PIPEWIRE_LIBRARIES})
|
||||
endif()
|
||||
install(TARGETS audpipewire DESTINATION ${DEFAULT_PLUGIN_PATH})
|
||||
endif()
|
||||
|
||||
if(WITH_SDL AND PLUGIN_SDL)
|
||||
add_definitions(-DSDL_PLUGIN)
|
||||
include_directories(${INCLUDE} ${SDL_INCLUDE_DIR})
|
||||
add_library(audsdl SHARED ${SDL_SRC} ${SDL_HDR} ${HDR})
|
||||
set_target_properties(audsdl PROPERTIES SOVERSION ${AUDASPACE_VERSION})
|
||||
if(WITH_VERSIONED_PLUGINS)
|
||||
set_target_properties(audsdl PROPERTIES SOVERSION ${AUDASPACE_VERSION})
|
||||
endif()
|
||||
target_link_libraries(audsdl audaspace ${SDL_LIBRARY})
|
||||
install(TARGETS audsdl DESTINATION ${DEFAULT_PLUGIN_PATH})
|
||||
endif()
|
||||
|
||||
2
extern/audaspace/README.md
vendored
2
extern/audaspace/README.md
vendored
@@ -32,7 +32,7 @@ The following (probably incomplete) features are supported by audaspace:
|
||||
License
|
||||
-------
|
||||
|
||||
> Copyright © 2009-2023 Jörg Müller. All rights reserved.
|
||||
> Copyright © 2009-2024 Jörg Müller. All rights reserved.
|
||||
>
|
||||
> Licensed under the Apache License, Version 2.0 (the "License");
|
||||
> you may not use this file except in compliance with the License.
|
||||
|
||||
2
extern/audaspace/bindings/python/PySound.cpp
vendored
2
extern/audaspace/bindings/python/PySound.cpp
vendored
@@ -886,7 +886,7 @@ Sound_fadeout(Sound* self, PyObject* args)
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(M_aud_Sound_filter_doc,
|
||||
".. method:: filter(b, a = (1))\n\n"
|
||||
".. method:: filter(b, a = (1,))\n\n"
|
||||
" Filters a sound with the supplied IIR filter coefficients.\n"
|
||||
" Without the second parameter you'll get a FIR filter.\n\n"
|
||||
" If the first value of the a sequence is 0,\n"
|
||||
|
||||
2
extern/audaspace/bindings/python/setup.py.in
vendored
2
extern/audaspace/bindings/python/setup.py.in
vendored
@@ -38,7 +38,7 @@ else:
|
||||
|
||||
audaspace = Extension(
|
||||
'aud',
|
||||
include_dirs = ['@CMAKE_CURRENT_BINARY_DIR@', '@FFTW_INCLUDE_DIR@', os.path.join(source_directory, '../../include'), numpy.get_include()],
|
||||
include_dirs = ['@CMAKE_CURRENT_BINARY_DIR@', os.path.join(source_directory, '../../include'), numpy.get_include()] + (['@FFTW_INCLUDE_DIR@'] if '@WITH_FFTW@' == 'ON' else []),
|
||||
libraries = ['audaspace'],
|
||||
library_dirs = ['.', 'Release', 'Debug'],
|
||||
language = 'c++',
|
||||
|
||||
2
extern/audaspace/plugins/ffmpeg/FFMPEG.cpp
vendored
2
extern/audaspace/plugins/ffmpeg/FFMPEG.cpp
vendored
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright 2009-2016 Jörg Müller
|
||||
* Copyright 2009-2024 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.
|
||||
|
||||
2
extern/audaspace/plugins/ffmpeg/FFMPEG.h
vendored
2
extern/audaspace/plugins/ffmpeg/FFMPEG.h
vendored
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright 2009-2016 Jörg Müller
|
||||
* Copyright 2009-2024 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright 2009-2016 Jörg Müller
|
||||
* Copyright 2009-2024 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright 2009-2016 Jörg Müller
|
||||
* Copyright 2009-2024 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright 2009-2016 Jörg Müller
|
||||
* Copyright 2009-2024 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright 2009-2016 Jörg Müller
|
||||
* Copyright 2009-2024 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.
|
||||
|
||||
387
extern/audaspace/plugins/pipewire/PipeWireDevice.cpp
vendored
Normal file
387
extern/audaspace/plugins/pipewire/PipeWireDevice.cpp
vendored
Normal file
@@ -0,0 +1,387 @@
|
||||
/*******************************************************************************
|
||||
* Copyright 2009-2024 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 "PipeWireDevice.h"
|
||||
|
||||
#include <spa/param/audio/format-utils.h>
|
||||
|
||||
#include "Exception.h"
|
||||
#include "IReader.h"
|
||||
#include "PipeWireLibrary.h"
|
||||
|
||||
#include "devices/DeviceManager.h"
|
||||
#include "devices/IDeviceFactory.h"
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
PipeWireDevice::PipeWireSynchronizer::PipeWireSynchronizer(PipeWireDevice* device) : m_device(device)
|
||||
{
|
||||
}
|
||||
|
||||
void PipeWireDevice::PipeWireSynchronizer::updateTickStart()
|
||||
{
|
||||
if (!m_get_tick_start)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pw_time tm;
|
||||
AUD_pw_stream_get_time_n(m_device->m_stream, &tm, sizeof(tm));
|
||||
m_tick_start = tm.ticks;
|
||||
m_get_tick_start = false;
|
||||
}
|
||||
|
||||
void PipeWireDevice::PipeWireSynchronizer::play()
|
||||
{
|
||||
m_playing = true;
|
||||
m_get_tick_start = true;
|
||||
}
|
||||
|
||||
void PipeWireDevice::PipeWireSynchronizer::stop()
|
||||
{
|
||||
std::shared_ptr<IHandle> dummy_handle;
|
||||
m_seek_pos = getPosition(dummy_handle);
|
||||
m_playing = false;
|
||||
}
|
||||
|
||||
void PipeWireDevice::PipeWireSynchronizer::seek(std::shared_ptr<IHandle> handle, double time)
|
||||
{
|
||||
/* Update start time here as we might update the seek position while playing back. */
|
||||
m_get_tick_start = true;
|
||||
m_seek_pos = time;
|
||||
handle->seek(time);
|
||||
}
|
||||
|
||||
double PipeWireDevice::PipeWireSynchronizer::getPosition(std::shared_ptr<IHandle> handle)
|
||||
{
|
||||
if (!m_playing || m_get_tick_start)
|
||||
{
|
||||
return m_seek_pos;
|
||||
}
|
||||
pw_time tm;
|
||||
AUD_pw_stream_get_time_n(m_device->m_stream, &tm, sizeof(tm));
|
||||
uint64_t now = AUD_pw_stream_get_nsec(m_device->m_stream);
|
||||
int64_t diff = now - tm.now;
|
||||
/* Elapsed time since the last sample was queued. */
|
||||
int64_t elapsed = (tm.rate.denom * diff) / (tm.rate.num * SPA_NSEC_PER_SEC);
|
||||
|
||||
/* Calculate the elapsed time in seconds from the last seek position. */
|
||||
double elapsed_time = (tm.ticks - m_tick_start + elapsed) * tm.rate.num / double(tm.rate.denom);
|
||||
return elapsed_time + m_seek_pos;
|
||||
}
|
||||
|
||||
void PipeWireDevice::handleStateChanged(void* device_ptr, enum pw_stream_state old, enum pw_stream_state state, const char* error)
|
||||
{
|
||||
PipeWireDevice* device = (PipeWireDevice*) device_ptr;
|
||||
//fprintf(stderr, "stream state: \"%s\"\n", pw_stream_state_as_string(state));
|
||||
if (state == PW_STREAM_STATE_PAUSED)
|
||||
{
|
||||
AUD_pw_stream_flush(device->m_stream, false);
|
||||
}
|
||||
}
|
||||
|
||||
void PipeWireDevice::updateRingBuffers()
|
||||
{
|
||||
uint32_t samplesize = AUD_DEVICE_SAMPLE_SIZE(m_specs);
|
||||
|
||||
sample_t* rb_data = m_ringbuffer_data.getBuffer();
|
||||
uint32_t rb_size = m_ringbuffer_data.getSize();
|
||||
uint32_t rb_index;
|
||||
Buffer mix_buffer = Buffer(rb_size);
|
||||
sample_t* mix_buffer_data = mix_buffer.getBuffer();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mixingLock);
|
||||
|
||||
while (m_run_mixing_thread)
|
||||
{
|
||||
/* Get the amount of bytes available for writing. */
|
||||
int32_t rb_avail = rb_size - spa_ringbuffer_get_write_index(&m_ringbuffer, &rb_index);
|
||||
if (m_fill_ringbuffer && rb_avail > 0) {
|
||||
/* As we allocated the ring buffer ourselves, we assume that the samplesize and
|
||||
* the available bytes to read is evenly divisable.
|
||||
*/
|
||||
int32_t sample_count = rb_avail / samplesize;
|
||||
mix(reinterpret_cast<data_t*>(mix_buffer_data), sample_count);
|
||||
spa_ringbuffer_write_data(&m_ringbuffer, rb_data, rb_size, rb_index % rb_size, mix_buffer_data, rb_avail);
|
||||
rb_index += rb_avail;
|
||||
spa_ringbuffer_write_update(&m_ringbuffer, rb_index);
|
||||
}
|
||||
if (!m_fill_ringbuffer) {
|
||||
/* Clear the ringbuffer when we are not playing back to make sure we don't
|
||||
* keep any outdated data.
|
||||
*/
|
||||
spa_ringbuffer_read_update(&m_ringbuffer, rb_index);
|
||||
}
|
||||
m_mixingCondition.wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
void PipeWireDevice::mixAudioBuffer(void* device_ptr)
|
||||
{
|
||||
PipeWireDevice* device = (PipeWireDevice*) device_ptr;
|
||||
|
||||
pw_buffer* pw_buf = AUD_pw_stream_dequeue_buffer(device->m_stream);
|
||||
if(!pw_buf)
|
||||
{
|
||||
/* Couldn't get any buffer from PipeWire...*/
|
||||
return;
|
||||
}
|
||||
|
||||
/* We call this here as the tick is not guaranteed to be up to date
|
||||
* until the "process" callback is triggered.
|
||||
*/
|
||||
device->m_synchronizer.updateTickStart();
|
||||
|
||||
spa_data& spa_data = pw_buf->buffer->datas[0];
|
||||
spa_chunk* chunk = spa_data.chunk;
|
||||
|
||||
chunk->offset = 0;
|
||||
chunk->stride = AUD_DEVICE_SAMPLE_SIZE(device->m_specs);
|
||||
int n_frames = spa_data.maxsize / chunk->stride;
|
||||
if(pw_buf->requested)
|
||||
{
|
||||
n_frames = SPA_MIN(pw_buf->requested, n_frames);
|
||||
}
|
||||
chunk->size = n_frames * chunk->stride;
|
||||
|
||||
if(!device->m_fill_ringbuffer)
|
||||
{
|
||||
/* Queue up silence if we are not queuing up any samples.
|
||||
* If we don't give Pipewire any buffers, it will think we encountered an error.
|
||||
*/
|
||||
memset(spa_data.data, 0, AUD_FORMAT_SIZE(device->m_specs.format) * chunk->size);
|
||||
AUD_pw_stream_queue_buffer(device->m_stream, pw_buf);
|
||||
return;
|
||||
}
|
||||
uint32_t rb_index;
|
||||
spa_ringbuffer* ringbuffer = &device->m_ringbuffer;
|
||||
|
||||
int32_t rb_avail = spa_ringbuffer_get_read_index(ringbuffer, &rb_index);
|
||||
if (!rb_avail)
|
||||
{
|
||||
/* Nothing to read from the ring buffer. */
|
||||
device->m_mixingCondition.notify_all();
|
||||
memset(spa_data.data, 0, AUD_FORMAT_SIZE(device->m_specs.format) * chunk->size);
|
||||
AUD_pw_stream_queue_buffer(device->m_stream, pw_buf);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Here we assume that, if we have available space to read, that the read
|
||||
* buffer size is always enough to fill the output buffer.
|
||||
* This is because the PW_KEY_NODE_LATENCY property that we set should guarantee
|
||||
* that pipewire can't request any bigger buffer sizes than we requested.
|
||||
* (But they can be smaller)
|
||||
*/
|
||||
uint32_t rb_size = device->m_ringbuffer_data.getSize();
|
||||
sample_t* rb_data = device->m_ringbuffer_data.getBuffer();
|
||||
spa_ringbuffer_read_data(ringbuffer, rb_data, rb_size, rb_index % rb_size, spa_data.data, chunk->size);
|
||||
spa_ringbuffer_read_update(ringbuffer, rb_index + chunk->size);
|
||||
device->m_mixingCondition.notify_all();
|
||||
AUD_pw_stream_queue_buffer(device->m_stream, pw_buf);
|
||||
}
|
||||
|
||||
void PipeWireDevice::playing(bool playing)
|
||||
{
|
||||
AUD_pw_thread_loop_lock(m_thread);
|
||||
AUD_pw_stream_set_active(m_stream, playing);
|
||||
AUD_pw_thread_loop_unlock(m_thread);
|
||||
m_fill_ringbuffer = playing;
|
||||
/* Poke the mixing thread to ensure that it reacts to the m_fill_ringbuffer change. */
|
||||
m_mixingCondition.notify_all();
|
||||
}
|
||||
|
||||
PipeWireDevice::PipeWireDevice(const std::string& name, DeviceSpecs specs, int buffersize) :
|
||||
m_synchronizer(this),
|
||||
m_fill_ringbuffer(false),
|
||||
m_run_mixing_thread(true)
|
||||
{
|
||||
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;
|
||||
spa_audio_format format = SPA_AUDIO_FORMAT_F32;
|
||||
switch(m_specs.format)
|
||||
{
|
||||
case FORMAT_U8:
|
||||
format = SPA_AUDIO_FORMAT_U8;
|
||||
break;
|
||||
case FORMAT_S16:
|
||||
format = SPA_AUDIO_FORMAT_S16;
|
||||
break;
|
||||
case FORMAT_S24:
|
||||
format = SPA_AUDIO_FORMAT_S24;
|
||||
break;
|
||||
case FORMAT_S32:
|
||||
format = SPA_AUDIO_FORMAT_S32;
|
||||
break;
|
||||
case FORMAT_FLOAT32:
|
||||
format = SPA_AUDIO_FORMAT_F32;
|
||||
break;
|
||||
case FORMAT_FLOAT64:
|
||||
format = SPA_AUDIO_FORMAT_F64;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
AUD_pw_init(nullptr, nullptr);
|
||||
|
||||
m_thread = AUD_pw_thread_loop_new(name.c_str(), nullptr);
|
||||
if(!m_thread)
|
||||
{
|
||||
AUD_THROW(DeviceException, "Could not create PipeWire thread.");
|
||||
}
|
||||
|
||||
m_events = std::make_unique<pw_stream_events>();
|
||||
m_events->version = PW_VERSION_STREAM_EVENTS;
|
||||
m_events->state_changed = PipeWireDevice::handleStateChanged;
|
||||
m_events->process = PipeWireDevice::mixAudioBuffer;
|
||||
|
||||
pw_properties *stream_props = AUD_pw_properties_new(
|
||||
PW_KEY_MEDIA_TYPE, "Audio",
|
||||
PW_KEY_MEDIA_CATEGORY, "Playback",
|
||||
PW_KEY_MEDIA_ROLE, "Production",
|
||||
NULL);
|
||||
|
||||
/* Set the requested sample rate and latency. */
|
||||
AUD_pw_properties_setf(stream_props, PW_KEY_NODE_RATE, "1/%u", uint(m_specs.rate));
|
||||
AUD_pw_properties_setf(stream_props, PW_KEY_NODE_LATENCY, "%u/%u", buffersize, uint(m_specs.rate));
|
||||
|
||||
m_stream = AUD_pw_stream_new_simple(
|
||||
AUD_pw_thread_loop_get_loop(m_thread),
|
||||
name.c_str(),
|
||||
stream_props,
|
||||
m_events.get(),
|
||||
this);
|
||||
if(!m_stream)
|
||||
{
|
||||
AUD_pw_thread_loop_destroy(m_thread);
|
||||
AUD_THROW(DeviceException, "Could not create PipeWire stream.");
|
||||
}
|
||||
|
||||
spa_audio_info_raw info{};
|
||||
info.channels = m_specs.channels;
|
||||
info.format = format;
|
||||
info.rate = m_specs.rate;
|
||||
|
||||
uint8_t buffer[1024];
|
||||
spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||
const spa_pod *param = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info);
|
||||
|
||||
AUD_pw_stream_connect(m_stream,
|
||||
PW_DIRECTION_OUTPUT,
|
||||
PW_ID_ANY,
|
||||
static_cast<pw_stream_flags>(PW_STREAM_FLAG_AUTOCONNECT |
|
||||
PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_INACTIVE |
|
||||
PW_STREAM_FLAG_RT_PROCESS),
|
||||
¶m, 1);
|
||||
AUD_pw_thread_loop_start(m_thread);
|
||||
|
||||
create();
|
||||
|
||||
spa_ringbuffer_init(&m_ringbuffer);
|
||||
m_ringbuffer_data.resize(buffersize * AUD_DEVICE_SAMPLE_SIZE(m_specs));
|
||||
m_mixingThread = std::thread(&PipeWireDevice::updateRingBuffers, this);
|
||||
}
|
||||
|
||||
PipeWireDevice::~PipeWireDevice()
|
||||
{
|
||||
/* Ensure that we are not playing back anything anymore. */
|
||||
destroy();
|
||||
|
||||
/* Destruct all PipeWire data. */
|
||||
AUD_pw_thread_loop_stop(m_thread);
|
||||
AUD_pw_stream_destroy(m_stream);
|
||||
AUD_pw_thread_loop_destroy(m_thread);
|
||||
AUD_pw_deinit();
|
||||
|
||||
{
|
||||
/* Ensure that the mixing thread exits. */
|
||||
std::unique_lock<std::mutex> lock(m_mixingLock);
|
||||
m_run_mixing_thread = false;
|
||||
m_mixingCondition.notify_all();
|
||||
}
|
||||
m_mixingThread.join();
|
||||
}
|
||||
|
||||
ISynchronizer* PipeWireDevice::getSynchronizer()
|
||||
{
|
||||
return &m_synchronizer;
|
||||
}
|
||||
|
||||
class PipeWireDeviceFactory : public IDeviceFactory
|
||||
{
|
||||
private:
|
||||
DeviceSpecs m_specs;
|
||||
int m_buffersize;
|
||||
std::string m_name;
|
||||
|
||||
public:
|
||||
PipeWireDeviceFactory() : 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 PipeWireDevice(m_name, m_specs, m_buffersize));
|
||||
}
|
||||
|
||||
virtual int getPriority()
|
||||
{
|
||||
return 1 << 16;
|
||||
}
|
||||
|
||||
virtual void setSpecs(DeviceSpecs specs)
|
||||
{
|
||||
m_specs = specs;
|
||||
}
|
||||
|
||||
virtual void setBufferSize(int buffersize)
|
||||
{
|
||||
m_buffersize = buffersize;
|
||||
}
|
||||
|
||||
virtual void setName(const std::string &name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
};
|
||||
|
||||
void PipeWireDevice::registerPlugin()
|
||||
{
|
||||
if(loadPipeWire())
|
||||
DeviceManager::registerDevice("PipeWire", std::shared_ptr<IDeviceFactory>(new PipeWireDeviceFactory));
|
||||
}
|
||||
|
||||
#ifdef PIPEWIRE_PLUGIN
|
||||
extern "C" AUD_PLUGIN_API void registerPlugin()
|
||||
{
|
||||
PipeWireDevice::registerPlugin();
|
||||
}
|
||||
|
||||
extern "C" AUD_PLUGIN_API const char* getName()
|
||||
{
|
||||
return "Pipewire";
|
||||
}
|
||||
#endif
|
||||
|
||||
AUD_NAMESPACE_END
|
||||
134
extern/audaspace/plugins/pipewire/PipeWireDevice.h
vendored
Normal file
134
extern/audaspace/plugins/pipewire/PipeWireDevice.h
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
/*******************************************************************************
|
||||
* Copyright 2009-2024 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 PIPEWIRE_PLUGIN
|
||||
#define AUD_BUILD_PLUGIN
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file PipeWireDevice.h
|
||||
* @ingroup plugin
|
||||
* The PipeWireDevice class.
|
||||
*/
|
||||
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <spa/utils/ringbuffer.h>
|
||||
|
||||
#include "devices/SoftwareDevice.h"
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* This device plays back through PipeWire, the simple direct media layer.
|
||||
*/
|
||||
class AUD_PLUGIN_API PipeWireDevice : public SoftwareDevice
|
||||
{
|
||||
private:
|
||||
class PipeWireSynchronizer : public DefaultSynchronizer
|
||||
{
|
||||
PipeWireDevice* m_device;
|
||||
bool m_playing = false;
|
||||
bool m_get_tick_start = false;
|
||||
int64_t m_tick_start = 0.0f;
|
||||
double m_seek_pos = 0.0f;
|
||||
|
||||
public:
|
||||
PipeWireSynchronizer(PipeWireDevice* device);
|
||||
|
||||
void updateTickStart();
|
||||
virtual void play();
|
||||
virtual void stop();
|
||||
virtual void seek(std::shared_ptr<IHandle> handle, double time);
|
||||
virtual double getPosition(std::shared_ptr<IHandle> handle);
|
||||
};
|
||||
|
||||
/// Synchronizer.
|
||||
PipeWireSynchronizer m_synchronizer;
|
||||
|
||||
/**
|
||||
* Whether we should start filling our ringbuffer with audio.
|
||||
*/
|
||||
bool m_fill_ringbuffer;
|
||||
|
||||
pw_stream* m_stream;
|
||||
pw_thread_loop* m_thread;
|
||||
std::unique_ptr<pw_stream_events> m_events;
|
||||
|
||||
/**
|
||||
* The mixing thread.
|
||||
*/
|
||||
std::thread m_mixingThread;
|
||||
bool m_run_mixing_thread;
|
||||
|
||||
/**
|
||||
* Mutex for mixing.
|
||||
*/
|
||||
std::mutex m_mixingLock;
|
||||
|
||||
/**
|
||||
* The mixing ringbuffer and mixing data
|
||||
*/
|
||||
spa_ringbuffer m_ringbuffer;
|
||||
Buffer m_ringbuffer_data;
|
||||
std::condition_variable m_mixingCondition;
|
||||
|
||||
AUD_LOCAL static void handleStateChanged(void* device_ptr, enum pw_stream_state old, enum pw_stream_state state, const char* error);
|
||||
|
||||
/**
|
||||
* Updates the ring buffers.
|
||||
*/
|
||||
AUD_LOCAL void updateRingBuffers();
|
||||
|
||||
/**
|
||||
* Mixes the next bytes into the buffer.
|
||||
* \param data The PipeWire device.
|
||||
*/
|
||||
AUD_LOCAL static void mixAudioBuffer(void* device_ptr);
|
||||
|
||||
// delete copy constructor and operator=
|
||||
PipeWireDevice(const PipeWireDevice&) = delete;
|
||||
PipeWireDevice& operator=(const PipeWireDevice&) = delete;
|
||||
|
||||
protected:
|
||||
virtual void playing(bool playing);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Opens the PipeWire 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.
|
||||
*/
|
||||
PipeWireDevice(const std::string& name, DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE);
|
||||
|
||||
/**
|
||||
* Closes the PipeWire audio device.
|
||||
*/
|
||||
virtual ~PipeWireDevice();
|
||||
|
||||
virtual ISynchronizer* getSynchronizer();
|
||||
/**
|
||||
* Registers this plugin.
|
||||
*/
|
||||
static void registerPlugin();
|
||||
};
|
||||
|
||||
AUD_NAMESPACE_END
|
||||
59
extern/audaspace/plugins/pipewire/PipeWireLibrary.cpp
vendored
Normal file
59
extern/audaspace/plugins/pipewire/PipeWireLibrary.cpp
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/*******************************************************************************
|
||||
* Copyright 2009-2024 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 PIPEWIRE_LIBRARY_IMPLEMENTATION
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
|
||||
#include "PipeWireLibrary.h"
|
||||
|
||||
#ifdef DYNLOAD_PIPEWIRE
|
||||
#include "plugin/PluginManager.h"
|
||||
#endif
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
bool loadPipeWire()
|
||||
{
|
||||
#ifdef DYNLOAD_PIPEWIRE
|
||||
std::array<const std::string, 2> names = {"libpipewire-0.3.so", "libpipewire-0.3.so.0"};
|
||||
|
||||
void* handle = nullptr;
|
||||
|
||||
for(auto& name : names)
|
||||
{
|
||||
handle = PluginManager::openLibrary(name);
|
||||
if(handle)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handle)
|
||||
return false;
|
||||
|
||||
#define PIPEWIRE_SYMBOL(sym) AUD_##sym = reinterpret_cast<decltype(&sym)>(PluginManager::lookupLibrary(handle, #sym))
|
||||
#else
|
||||
#define PIPEWIRE_SYMBOL(sym) AUD_##sym = &sym
|
||||
#endif
|
||||
|
||||
#include "PipeWireSymbols.h"
|
||||
|
||||
#undef PIPEWIRE_SYMBOL
|
||||
|
||||
return AUD_pw_init != nullptr;
|
||||
}
|
||||
|
||||
AUD_NAMESPACE_END
|
||||
47
extern/audaspace/plugins/pipewire/PipeWireLibrary.h
vendored
Normal file
47
extern/audaspace/plugins/pipewire/PipeWireLibrary.h
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/*******************************************************************************
|
||||
* Copyright 2009-2024 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 PIPEWIRE_PLUGIN
|
||||
#define AUD_BUILD_PLUGIN
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file PipeWireLibrary.h
|
||||
* @ingroup plugin
|
||||
*/
|
||||
|
||||
#include "Audaspace.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <pipewire/stream.h>
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef PIPEWIRE_LIBRARY_IMPLEMENTATION
|
||||
#define PIPEWIRE_SYMBOL(sym) decltype(&sym) AUD_##sym
|
||||
#else
|
||||
#define PIPEWIRE_SYMBOL(sym) extern decltype(&sym) AUD_##sym
|
||||
#endif
|
||||
|
||||
#include "PipeWireSymbols.h"
|
||||
|
||||
#undef PIPEWIRE_SYMBOL
|
||||
|
||||
bool loadPipeWire();
|
||||
|
||||
AUD_NAMESPACE_END
|
||||
40
extern/audaspace/plugins/pipewire/PipeWireSymbols.h
vendored
Normal file
40
extern/audaspace/plugins/pipewire/PipeWireSymbols.h
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/*******************************************************************************
|
||||
* Copyright 2009-2024 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.
|
||||
******************************************************************************/
|
||||
|
||||
PIPEWIRE_SYMBOL(pw_init);
|
||||
PIPEWIRE_SYMBOL(pw_deinit);
|
||||
|
||||
PIPEWIRE_SYMBOL(pw_properties_new);
|
||||
PIPEWIRE_SYMBOL(pw_properties_setf);
|
||||
|
||||
PIPEWIRE_SYMBOL(pw_stream_connect);
|
||||
PIPEWIRE_SYMBOL(pw_stream_destroy);
|
||||
PIPEWIRE_SYMBOL(pw_stream_get_nsec);
|
||||
PIPEWIRE_SYMBOL(pw_stream_get_time_n);
|
||||
PIPEWIRE_SYMBOL(pw_stream_new_simple);
|
||||
PIPEWIRE_SYMBOL(pw_stream_queue_buffer);
|
||||
PIPEWIRE_SYMBOL(pw_stream_dequeue_buffer);
|
||||
PIPEWIRE_SYMBOL(pw_stream_set_active);
|
||||
PIPEWIRE_SYMBOL(pw_stream_flush);
|
||||
|
||||
PIPEWIRE_SYMBOL(pw_thread_loop_destroy);
|
||||
PIPEWIRE_SYMBOL(pw_thread_loop_get_loop);
|
||||
PIPEWIRE_SYMBOL(pw_thread_loop_lock);
|
||||
PIPEWIRE_SYMBOL(pw_thread_loop_unlock);
|
||||
PIPEWIRE_SYMBOL(pw_thread_loop_new);
|
||||
PIPEWIRE_SYMBOL(pw_thread_loop_start);
|
||||
PIPEWIRE_SYMBOL(pw_thread_loop_stop);
|
||||
|
||||
@@ -15,28 +15,51 @@
|
||||
******************************************************************************/
|
||||
|
||||
#include "PulseAudioDevice.h"
|
||||
#include "PulseAudioLibrary.h"
|
||||
#include "devices/DeviceManager.h"
|
||||
#include "devices/IDeviceFactory.h"
|
||||
|
||||
#include "Exception.h"
|
||||
#include "IReader.h"
|
||||
#include "PulseAudioLibrary.h"
|
||||
|
||||
#include "devices/DeviceManager.h"
|
||||
#include "devices/IDeviceFactory.h"
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
PulseAudioDevice::PulseAudioSynchronizer::PulseAudioSynchronizer(PulseAudioDevice *device) :
|
||||
m_device(device)
|
||||
PulseAudioDevice::PulseAudioSynchronizer::PulseAudioSynchronizer(PulseAudioDevice* device) : m_device(device)
|
||||
{
|
||||
}
|
||||
|
||||
double PulseAudioDevice::PulseAudioSynchronizer::getPosition(std::shared_ptr<IHandle> handle)
|
||||
void PulseAudioDevice::PulseAudioSynchronizer::play()
|
||||
{
|
||||
pa_usec_t latency;
|
||||
int negative;
|
||||
AUD_pa_stream_get_latency(m_device->m_stream, &latency, &negative);
|
||||
/* Make sure that our start time is up to date. */
|
||||
AUD_pa_stream_get_time(m_device->m_stream, &m_time_start);
|
||||
m_playing = true;
|
||||
}
|
||||
|
||||
double delay = m_device->m_ring_buffer.getReadSize() / (AUD_SAMPLE_SIZE(m_device->m_specs) * m_device->m_specs.rate) + latency * 1.0e-6;
|
||||
void PulseAudioDevice::PulseAudioSynchronizer::stop()
|
||||
{
|
||||
std::shared_ptr<IHandle> dummy_handle;
|
||||
m_seek_pos = getPosition(dummy_handle);
|
||||
m_playing = false;
|
||||
}
|
||||
|
||||
return handle->getPosition() - delay;
|
||||
void PulseAudioDevice::PulseAudioSynchronizer::seek(std::shared_ptr<IHandle> handle, double time)
|
||||
{
|
||||
/* Update start time here as we might update the seek position while playing back. */
|
||||
AUD_pa_stream_get_time(m_device->m_stream, &m_time_start);
|
||||
m_seek_pos = time;
|
||||
handle->seek(time);
|
||||
}
|
||||
|
||||
double PulseAudioDevice::PulseAudioSynchronizer::getPosition(std::shared_ptr<IHandle> /*handle*/)
|
||||
{
|
||||
pa_usec_t time;
|
||||
if(!m_playing)
|
||||
{
|
||||
return m_seek_pos;
|
||||
}
|
||||
AUD_pa_stream_get_time(m_device->m_stream, &time);
|
||||
return (time - m_time_start) * 1.0e-6 + m_seek_pos;
|
||||
}
|
||||
|
||||
void PulseAudioDevice::updateRingBuffer()
|
||||
@@ -71,13 +94,12 @@ void PulseAudioDevice::updateRingBuffer()
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_ring_buffer.getReadSize() == 0 && !m_corked)
|
||||
if(m_ring_buffer.getReadSize() == 0)
|
||||
{
|
||||
AUD_pa_threaded_mainloop_lock(m_mainloop);
|
||||
AUD_pa_stream_cork(m_stream, 1, nullptr, nullptr);
|
||||
AUD_pa_stream_flush(m_stream, nullptr, nullptr);
|
||||
AUD_pa_threaded_mainloop_unlock(m_mainloop);
|
||||
m_corked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,18 +108,18 @@ void PulseAudioDevice::updateRingBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
void PulseAudioDevice::PulseAudio_state_callback(pa_context *context, void *data)
|
||||
void PulseAudioDevice::PulseAudio_state_callback(pa_context* context, void* data)
|
||||
{
|
||||
PulseAudioDevice* device = (PulseAudioDevice*)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 total_bytes, void *data)
|
||||
void PulseAudioDevice::PulseAudio_request(pa_stream* stream, size_t total_bytes, void* data)
|
||||
{
|
||||
PulseAudioDevice* device = (PulseAudioDevice*)data;
|
||||
PulseAudioDevice* device = (PulseAudioDevice*) data;
|
||||
|
||||
data_t* buffer;
|
||||
|
||||
@@ -141,14 +163,12 @@ void PulseAudioDevice::playing(bool playing)
|
||||
AUD_pa_threaded_mainloop_lock(m_mainloop);
|
||||
AUD_pa_stream_cork(m_stream, 0, nullptr, nullptr);
|
||||
AUD_pa_threaded_mainloop_unlock(m_mainloop);
|
||||
m_corked = false;
|
||||
}
|
||||
}
|
||||
|
||||
PulseAudioDevice::PulseAudioDevice(const std::string &name, DeviceSpecs specs, int buffersize) :
|
||||
m_synchronizer(this),
|
||||
m_playback(false),
|
||||
m_corked(true),
|
||||
m_state(PA_CONTEXT_UNCONNECTED),
|
||||
m_valid(true),
|
||||
m_underflows(0)
|
||||
|
||||
@@ -45,10 +45,16 @@ private:
|
||||
class PulseAudioSynchronizer : public DefaultSynchronizer
|
||||
{
|
||||
PulseAudioDevice* m_device;
|
||||
bool m_playing = false;
|
||||
pa_usec_t m_time_start = 0;
|
||||
double m_seek_pos = 0.0f;
|
||||
|
||||
public:
|
||||
PulseAudioSynchronizer(PulseAudioDevice* device);
|
||||
|
||||
virtual void play();
|
||||
virtual void stop();
|
||||
virtual void seek(std::shared_ptr<IHandle> handle, double time);
|
||||
virtual double getPosition(std::shared_ptr<IHandle> handle);
|
||||
};
|
||||
|
||||
@@ -60,8 +66,6 @@ private:
|
||||
*/
|
||||
volatile bool m_playback;
|
||||
|
||||
bool m_corked;
|
||||
|
||||
pa_threaded_mainloop* m_mainloop;
|
||||
pa_context* m_context;
|
||||
pa_stream* m_stream;
|
||||
|
||||
@@ -25,7 +25,7 @@ PULSEAUDIO_SYMBOL(pa_stream_begin_write);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_connect_playback);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_cork);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_flush);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_get_latency);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_get_time);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_is_corked);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_new);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_set_buffer_attr);
|
||||
|
||||
Reference in New Issue
Block a user