This commit is contained in:
Robin E. R. Davies
2025-07-23 17:12:10 -04:00
17 changed files with 386 additions and 241 deletions
+2 -2
View File
@@ -1,13 +1,13 @@
cmake_minimum_required(VERSION 3.16.0) cmake_minimum_required(VERSION 3.16.0)
project(pipedal project(pipedal
VERSION 1.4.79 VERSION 1.4.83
DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi" DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi"
HOMEPAGE_URL "https://rerdavies.github.io/pipedal" HOMEPAGE_URL "https://rerdavies.github.io/pipedal"
) )
EXECUTE_PROCESS( COMMAND dpkg --print-architecture COMMAND tr -d '\n' OUTPUT_VARIABLE DEBIAN_ARCHITECTURE ) EXECUTE_PROCESS( COMMAND dpkg --print-architecture COMMAND tr -d '\n' OUTPUT_VARIABLE DEBIAN_ARCHITECTURE )
set (DISPLAY_VERSION "PiPedal v1.4.79-Beta") set (DISPLAY_VERSION "PiPedal v1.4.83-Experimental")
set (PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE}) set (PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE})
set (CMAKE_INSTALL_PREFIX "/usr/") set (CMAKE_INSTALL_PREFIX "/usr/")
+2
View File
@@ -259,6 +259,8 @@ namespace pipedal
// Open sequencer in input mode // Open sequencer in input mode
int rc; int rc;
Lv2Log::debug("Opening ALSA Sequencer");
rc = snd_seq_open(&seqHandle, "default", SND_SEQ_OPEN_DUPLEX, 0); rc = snd_seq_open(&seqHandle, "default", SND_SEQ_OPEN_DUPLEX, 0);
if (rc < 0) if (rc < 0)
{ {
+33
View File
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2022 Robin E. R. Davies
* All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
// define restrict keyword for C++ compilers (which don't yet support it)
#ifndef restrict
#if defined(__GNUC__) || defined(__clang__)
#define restrict __restrict__
#elif defined(_MSC_VER)
#define restrict __restrict
#endif
#endif
+2 -2
View File
@@ -9,11 +9,11 @@
<img src="https://img.shields.io/github/downloads/rerdavies/pipedal/total?color=%23808080&link=https%3A%2F%2Frerdavies.github.io%2Fpipedal%2Fdownload.html"/> <img src="https://img.shields.io/github/downloads/rerdavies/pipedal/total?color=%23808080&link=https%3A%2F%2Frerdavies.github.io%2Fpipedal%2Fdownload.html"/>
Download:&nbsp;<a href='https://rerdavies.github.io/pipedal/download.html'>v1.4.79</a> Download:&nbsp;<a href='https://rerdavies.github.io/pipedal/download.html'>v1.4.83</a>
Website:&nbsp;[https://rerdavies.github.io/pipedal](https://rerdavies.github.io/pipedal). Website:&nbsp;[https://rerdavies.github.io/pipedal](https://rerdavies.github.io/pipedal).
Documentation:&nbsp;[https://rerdavies.github.io/pipedal/Documentation.html](https://rerdavies.github.io/pipedal/Documentation.html). Documentation:&nbsp;[https://rerdavies.github.io/pipedal/Documentation.html](https://rerdavies.github.io/pipedal/Documentation.html).
#### NEW version 1.4.79 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. New Phaser and Graphic Equalizer plugins. #### NEW version 1.4.83 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. New Phaser and Graphic Equalizer plugins.
&nbsp; &nbsp;
+5 -5
View File
@@ -13,18 +13,18 @@ page_icon: img/Install4.jpg
Download the most recent Debian (.deb) package for your platform: Download the most recent Debian (.deb) package for your platform:
- [Raspberry Pi OS bookworm (aarch64) v1.4.79](https://github.com/rerdavies/pipedal/releases/download/v1.4.79/pipedal_1.4.79_arm64.deb) - [Raspberry Pi OS bookworm (aarch64) v1.4.83](https://github.com/rerdavies/pipedal/releases/download/v1.4.83/pipedal_1.4.83_arm64.deb)
- [Ubuntu 24.x (aarch64) v1.4.79](https://github.com/rerdavies/pipedal/releases/download/v1.4.79/pipedal_1.4.79_arm64.deb) - [Ubuntu 24.x (aarch64) v1.4.83](https://github.com/rerdavies/pipedal/releases/download/v1.4.83/pipedal_1.4.83_arm64.deb)
- [Ubuntu 24.x (amd64) v1.4.79](https://github.com/rerdavies/pipedal/releases/download/v1.4.79/pipedal_1.4.79_amd64.deb) - [Ubuntu 24.x (amd64) v1.4.83](https://github.com/rerdavies/pipedal/releases/download/v1.4.83/pipedal_1.4.83_amd64.deb)
Version 1.4.79 has been tested on Raspberry Pi OS bookworm, Ubuntu 24.04 (amd64), and Ubuntu 24.10 (aarch64). Download the appropriate package for your platform, and install using the following procedure: Version 1.4.83 has been tested on Raspberry Pi OS bookworm, Ubuntu 24.04 (amd64), and Ubuntu 24.10 (aarch64). Download the appropriate package for your platform, and install using the following procedure:
``` ```
sudo apt update sudo apt update
sudo apt upgrade sudo apt upgrade
cd ~/Downloads cd ~/Downloads
sudo apt-get install ./pipedal_1.4.79_arm64.deb sudo apt-get install ./pipedal_1.4.83_arm64.deb
``` ```
You MUST use `apt-get`. `apt` will not install downloaded packages; and `dpkg -i` will not install dependencies. You MUST use `apt-get`. `apt` will not install downloaded packages; and `dpkg -i` will not install dependencies.
+5
View File
@@ -1,4 +1,9 @@
# Release Notes # Release Notes
## Pipedal 1.4.79 Experimental
Bug fixes:
- Thread-per-plugin-instance for LV2_WORKER threads (see issue #339)
- Fixes for selection issues when modifying pedalboard structure.
## Pipedal 1.4.79 Beta ## Pipedal 1.4.79 Beta
The Big Feature: The Big Feature:
+4 -4
View File
@@ -4,9 +4,9 @@
Download the most recent Debian (.deb) package for your platform: Download the most recent Debian (.deb) package for your platform:
- [Raspberry Pi OS bookworm (aarch64) v1.4.79 Beta](https://github.com/rerdavies/pipedal/releases/download/v1.4.79/pipedal_1.4.79_arm64.deb) - [Raspberry Pi OS bookworm (aarch64) v1.4.83 Beta](https://github.com/rerdavies/pipedal/releases/download/v1.4.83/pipedal_1.4.83_arm64.deb)
- [Ubuntu 24.x (aarch64) v1.4.79 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.4.79/pipedal_1.4.79_arm64.deb) - [Ubuntu 24.x (aarch64) v1.4.83 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.4.83/pipedal_1.4.83_arm64.deb)
- [Ubuntu 24.x (amd64) v1.4.79 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.4.79/pipedal_1.4.79_amd64.deb) - [Ubuntu 24.x (amd64) v1.4.83 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.4.83/pipedal_1.4.83_amd64.deb)
Install the package by running Install the package by running
@@ -14,7 +14,7 @@ Install the package by running
``` ```
sudo apt update sudo apt update
cd ~/Downloads cd ~/Downloads
sudo apt-get install ./pipedal_1.4.79_arm64.deb sudo apt-get install ./pipedal_1.4.83_arm64.deb
``` ```
You MUST use `apt-get` to install the package. `apt install` will NOT install the package correctly. The message about missing permissions given by `apt-get` is You MUST use `apt-get` to install the package. `apt install` will NOT install the package correctly. The message about missing permissions given by `apt-get` is
expected, and can be safely ignored. expected, and can be safely ignored.
+68 -5
View File
@@ -245,6 +245,8 @@ namespace pipedal
class AlsaDriverImpl : public AudioDriver class AlsaDriverImpl : public AudioDriver
{ {
private: private:
std::recursive_mutex restartMutex;
pipedal::CpuUse cpuUse; pipedal::CpuUse cpuUse;
#ifdef ALSADRIVER_CONFIG_DBG #ifdef ALSADRIVER_CONFIG_DBG
@@ -378,16 +380,21 @@ namespace pipedal
} }
private: private:
void AlsaCleanup() void AlsaCloseAudio()
{ {
std::lock_guard lock{restartMutex};
if (captureHandle) if (captureHandle)
{ {
Lv2Log::debug("ALSA capture handle closed.");
snd_pcm_drain(captureHandle);
snd_pcm_close(captureHandle); snd_pcm_close(captureHandle);
captureHandle = nullptr; captureHandle = nullptr;
} }
if (playbackHandle) if (playbackHandle)
{ {
Lv2Log::debug("ALSA playback handle closed.");
snd_pcm_drain(playbackHandle);
snd_pcm_close(playbackHandle); snd_pcm_close(playbackHandle);
playbackHandle = nullptr; playbackHandle = nullptr;
} }
@@ -411,7 +418,11 @@ namespace pipedal
snd_pcm_sw_params_free(playbackSwParams); snd_pcm_sw_params_free(playbackSwParams);
playbackSwParams = nullptr; playbackSwParams = nullptr;
} }
this->alsaSequencer = nullptr;
}
void AlsaCleanup()
{
AlsaCloseAudio();
} }
std::string discover_alsa_using_apps() std::string discover_alsa_using_apps()
@@ -1085,6 +1096,34 @@ namespace pipedal
} }
} }
void RestartAlsa()
{
std::lock_guard lock{restartMutex};
Lv2Log::info("Restarting ALSA devices.");
try {
AlsaCloseAudio();
} catch (const std::exception &e) {
Lv2Log::error(SS("Error cleaning up ALSA: " << e.what()));
throw std::runtime_error("Unable to restart the audio stream.");
}
try {
OpenAudio(this->jackServerSettings, this->channelSelection);
validate_capture_handle();
FillOutputBuffer();
audioRunning = true;
} catch (const std::exception &e) {
Lv2Log::error(SS("Error opening ALSA: " << e.what()));
throw std::runtime_error("Unable to restart the audio stream.");
}
int err;
if ((err = snd_pcm_start(captureHandle)) < 0)
{
throw PiPedalStateException("Unable to restart ALSA capture.");
}
}
void PrepareCaptureFunctions(snd_pcm_format_t captureFormat) void PrepareCaptureFunctions(snd_pcm_format_t captureFormat)
{ {
this->captureFormat = captureFormat; this->captureFormat = captureFormat;
@@ -1221,6 +1260,8 @@ namespace pipedal
void OpenAudio(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection) void OpenAudio(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
{ {
std::lock_guard lock{ restartMutex };
int err; int err;
alsa_device_name = jackServerSettings.GetAlsaInputDevice(); alsa_device_name = jackServerSettings.GetAlsaInputDevice();
@@ -1389,6 +1430,8 @@ namespace pipedal
void recover_from_output_underrun(snd_pcm_t *capture_handle, snd_pcm_t *playback_handle, int err) void recover_from_output_underrun(snd_pcm_t *capture_handle, snd_pcm_t *playback_handle, int err)
{ {
validate_capture_handle(); validate_capture_handle();
try {
if (err == -EPIPE) if (err == -EPIPE)
{ {
err = snd_pcm_prepare(playback_handle); err = snd_pcm_prepare(playback_handle);
@@ -1402,12 +1445,18 @@ namespace pipedal
{ {
throw PiPedalStateException(SS("Can't recover from ALSA output error. (" << snd_strerror(err) << ")")); throw PiPedalStateException(SS("Can't recover from ALSA output error. (" << snd_strerror(err) << ")"));
} }
} catch (const std::exception &e) {
Lv2Log::info(SS("Soft audio restart failed. " << e.what()));
RestartAlsa();
audioRunning = true;
}
validate_capture_handle(); validate_capture_handle();
} }
void recover_from_input_underrun(snd_pcm_t *capture_handle, snd_pcm_t *playback_handle, int err) void recover_from_input_underrun(snd_pcm_t *capture_handle, snd_pcm_t *playback_handle, int err)
{ {
validate_capture_handle(); validate_capture_handle();
try {
if (err == -EPIPE) if (err == -EPIPE)
{ {
@@ -1473,7 +1522,13 @@ namespace pipedal
} }
else else
{ {
throw std::runtime_error(SS("Can't restart audio: " << snd_strerror(err))); throw PiPedalStateException(SS("Can't recover from ALSA input error. (" << snd_strerror(err) << ")"));
}
} catch (const std::exception &e) {
Lv2Log::info(SS("Soft audio restart failed. " << e.what()));
RestartAlsa();
audioRunning = true;
} }
} }
@@ -1526,6 +1581,11 @@ namespace pipedal
AlsaMidiMessage message; AlsaMidiMessage message;
midiEventCount = 0; midiEventCount = 0;
auto alsaSequener = this->alsaSequencer; // take an addref
if (!alsaSequener)
{
return;
}
while (alsaSequencer->ReadMessage(message, 0)) while (alsaSequencer->ReadMessage(message, 0))
{ {
size_t messageSize = message.size; size_t messageSize = message.size;
@@ -1675,11 +1735,11 @@ namespace pipedal
Lv2Log::error(e.what()); Lv2Log::error(e.what());
Lv2Log::error("ALSA audio thread terminated abnormally."); Lv2Log::error("ALSA audio thread terminated abnormally.");
} }
this->driverHost->OnAlsaDriverStopped();
// if we terminated abnormally, pump messages until we have been terminated. // if we terminated abnormally, pump messages until we have been terminated.
if (!terminateAudio()) if (!terminateAudio())
{ {
this->driverHost->OnAlsaDriverStopped();
// zero out input buffers. // zero out input buffers.
for (size_t i = 0; i < this->captureBuffers.size(); ++i) for (size_t i = 0; i < this->captureBuffers.size(); ++i)
{ {
@@ -1848,6 +1908,8 @@ namespace pipedal
Deactivate(); Deactivate();
AlsaCleanup(); AlsaCleanup();
DeleteBuffers(); DeleteBuffers();
this->alsaSequencer = nullptr;
std::atomic_thread_fence(std::memory_order::release); std::atomic_thread_fence(std::memory_order::release);
} }
@@ -1940,7 +2002,7 @@ namespace pipedal
try try
{ {
int err; int err;
for (int retry = 0; retry < 2; ++retry) for (int retry = 0; retry < 4; ++retry)
{ {
err = snd_pcm_open(&playbackHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); err = snd_pcm_open(&playbackHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (err < 0) // field report of a device that is present, but won't immediately open. if (err < 0) // field report of a device that is present, but won't immediately open.
@@ -2202,6 +2264,7 @@ namespace pipedal
#endif #endif
} }
void FreeAlsaGlobals() void FreeAlsaGlobals()
{ {
snd_config_update_free_global(); // to get a clean Valgrind report. snd_config_update_free_global(); // to get a clean Valgrind report.
+4 -2
View File
@@ -34,6 +34,7 @@
#include "PluginHost.hpp" #include "PluginHost.hpp"
#include "PatchPropertyWriter.hpp" #include "PatchPropertyWriter.hpp"
#include "CpuTemperatureMonitor.hpp" #include "CpuTemperatureMonitor.hpp"
#include "restrict.hpp"
using namespace pipedal; using namespace pipedal;
@@ -618,7 +619,7 @@ private:
{ {
for (size_t i = 0; i < audioDriver->OutputBufferCount(); ++i) for (size_t i = 0; i < audioDriver->OutputBufferCount(); ++i)
{ {
float *out = (float *)audioDriver->GetOutputBuffer(i); float * out = (float *)audioDriver->GetOutputBuffer(i);
if (out) if (out)
{ {
ZeroBuffer(out, nframes); ZeroBuffer(out, nframes);
@@ -1141,7 +1142,7 @@ private:
{ {
try try
{ {
float *in, *out; float * restrict in , * restrict out;
Lv2Pedalboard *pedalboard = nullptr; Lv2Pedalboard *pedalboard = nullptr;
pedalboard = this->realtimeActivePedalboard; pedalboard = this->realtimeActivePedalboard;
@@ -1724,6 +1725,7 @@ public:
{ {
Close(); Close();
active = false; active = false;
isOpen = false;
throw; throw;
} }
} }
+46 -8
View File
@@ -40,6 +40,40 @@
using namespace pipedal; using namespace pipedal;
int avahiPollLockCount = 0;
static std::mutex avahiLockMutex;
class AvahiPollLock {
public:
AvahiPollLock() = delete;
AvahiPollLock(AvahiThreadedPoll*threadedPoll)
: threadedPoll(threadedPoll) {
std::lock_guard<std::mutex> lock(avahiLockMutex);
if (!threadedPoll) {
throw std::runtime_error("AvahiPollLock: threadedPoll is null.");
}
if (++avahiPollLockCount > 1) {
throw std::runtime_error("AvahiPollLock: nested locks are not allowed.");
}
avahi_threaded_poll_lock(threadedPoll);
}
void release() {
std::lock_guard<std::mutex> lock(avahiLockMutex);
if (threadedPoll)
{
if (avahiPollLockCount <= 0) {
throw std::runtime_error("AvahiPollLock: release called without a lock.");
}
--avahiPollLockCount;
avahi_threaded_poll_unlock(threadedPoll);
threadedPoll = nullptr;
}
}
~AvahiPollLock() {
release();
}
private:
AvahiThreadedPoll *threadedPoll;
};
void AvahiService::Announce( void AvahiService::Announce(
int portNumber, int portNumber,
const std::string &name, const std::string &name,
@@ -75,7 +109,7 @@ void AvahiService::Announce(
else else
{ {
// already started, so we have to use poll_lock instead. // already started, so we have to use poll_lock instead.
avahi_threaded_poll_lock(threadedPoll); AvahiPollLock lock(threadedPoll);
// all state that we have to protect with the lock now that the avahi serv3ce is running. // all state that we have to protect with the lock now that the avahi serv3ce is running.
this->portNumber = portNumber; this->portNumber = portNumber;
@@ -104,7 +138,7 @@ void AvahiService::Announce(
Lv2Log::error("Failed to update mDNS service because of a synch problem."); Lv2Log::error("Failed to update mDNS service because of a synch problem.");
} }
} }
avahi_threaded_poll_unlock(threadedPoll); lock.release();
if (wait) if (wait)
{ {
Wait(); Wait();
@@ -118,7 +152,8 @@ void AvahiService::Unannounce(bool wait)
return; return;
if (this->threadedPoll) if (this->threadedPoll)
{ {
avahi_threaded_poll_lock(threadedPoll); {
AvahiPollLock lock(threadedPoll);
if (started) if (started)
{ {
if (this->createPending) if (this->createPending)
@@ -141,7 +176,7 @@ void AvahiService::Unannounce(bool wait)
} }
} }
} }
avahi_threaded_poll_unlock(threadedPoll); }
if (wait) if (wait)
{ {
WaitForUnannounce(); WaitForUnannounce();
@@ -411,6 +446,7 @@ void AvahiService::Start()
if (!client) if (!client)
{ {
AvahiPollLock lock(threadedPoll);
/* Allocate a new client */ /* Allocate a new client */
client = avahi_client_new(avahi_threaded_poll_get(threadedPoll), AvahiClientFlags::AVAHI_CLIENT_NO_FAIL, client_callback, (void *)this, &error); client = avahi_client_new(avahi_threaded_poll_get(threadedPoll), AvahiClientFlags::AVAHI_CLIENT_NO_FAIL, client_callback, (void *)this, &error);
/* Check wether creating the client object succeeded */ /* Check wether creating the client object succeeded */
@@ -442,20 +478,22 @@ void AvahiService::Stop()
if (threadedPoll) if (threadedPoll)
{ {
avahi_threaded_poll_lock(threadedPoll); {
AvahiPollLock lock(threadedPoll);
if (group) if (group)
{ {
avahi_entry_group_free(group); avahi_entry_group_free(group);
group = nullptr; group = nullptr;
} }
avahi_threaded_poll_unlock(threadedPoll); }
avahi_threaded_poll_lock(threadedPoll); {
AvahiPollLock lock(threadedPoll);
if (client) if (client)
{ {
avahi_client_free(client); avahi_client_free(client);
client = nullptr; client = nullptr;
} }
avahi_threaded_poll_unlock(threadedPoll); }
avahi_threaded_poll_stop(threadedPoll); avahi_threaded_poll_stop(threadedPoll);
avahi_threaded_poll_free(threadedPoll); avahi_threaded_poll_free(threadedPoll);
+14 -27
View File
@@ -59,7 +59,7 @@ find_package(sdbus-c++ REQUIRED)
find_package(ICU REQUIRED COMPONENTS uc i18n ) find_package(ICU REQUIRED COMPONENTS uc i18n)
message(STATUS "ICU_LIBRARIES: ${ICU_LIBRARIES}") message(STATUS "ICU_LIBRARIES: ${ICU_LIBRARIES}")
@@ -133,7 +133,7 @@ set (LV2DEV_INCLUDE_DIRS ) # use lvt headers fro /usr/include
# version of websocketpp cannot compile under GCC 12.x. # version of websocketpp cannot compile under GCC 12.x.
set (WEBSOCKETPP_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/modules/websocketpp) set (WEBSOCKETPP_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/modules/websocketpp)
message(STATUS "WEBSOCKETPP_INCLUDE_DIRS ${WEBSOCKETPP_INCLUDE_DIRS}" ) message(STATUS "WEBSOCKETPP_INCLUDE_DIRS ${WEBSOCKETPP_INCLUDE_DIRS}")
# pkg_check_modules(JACK "jack") # pkg_check_modules(JACK "jack")
# if(!JACK_FOUND) # if(!JACK_FOUND)
@@ -169,10 +169,10 @@ if (CMAKE_BUILD_TYPE MATCHES Debug)
endif() endif()
elseif(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) elseif(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo)
message(STATUS "RelWithgDebInfo build") message(STATUS "RelWithgDebInfo build")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O3 -DNDEBUG" ) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O3 -ffast-math -DNDEBUG" )
else() else()
message(STATUS "Release build") message(STATUS "Release build")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -DNDEBUG" ) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -ffast-math -DNDEBUG" )
endif() endif()
if (ENABLE_BACKTRACE) if (ENABLE_BACKTRACE)
@@ -189,7 +189,7 @@ endif()
# message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") # message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
# set(CMAKE_ENABLE_EXPORTS 1) # set(CMAKE_ENABLE_EXPORTS 1)
message (STATUS "Cxx flags: ${CMAKE_CXX_FLAGS}" ) message (STATUS "Cxx flags: ${CMAKE_CXX_FLAGS}")
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY -DBOOST_BIND_GLOBAL_PLACEHOLDERS -DONBOARDING) add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY -DBOOST_BIND_GLOBAL_PLACEHOLDERS -DONBOARDING)
@@ -350,8 +350,7 @@ add_library(libpipedald STATIC ${PIPEDAL_SOURCES})
target_compile_definitions(libpipedald PUBLIC "_REENTRANT") target_compile_definitions(libpipedald PUBLIC "_REENTRANT")
target_include_directories(libpipedald PUBLIC ${PIPEDAL_INCLUDES} target_include_directories(libpipedald PUBLIC ${PIPEDAL_INCLUDES})
)
target_link_libraries(libpipedald target_link_libraries(libpipedald
PUBLIC PUBLIC
@@ -394,10 +393,7 @@ target_include_directories(pipedald PRIVATE ${PIPEDAL_INCLUDES})
target_compile_definitions(pipedald PRIVATE "ENABLE_BACKTRACE=${ENABLE_BACKTRACE}") target_compile_definitions(pipedald PRIVATE "ENABLE_BACKTRACE=${ENABLE_BACKTRACE}")
target_link_libraries(pipedald PRIVATE PiPedalCommon target_link_libraries(pipedald PRIVATE PiPedalCommon ${PIPEDAL_LIBS})
${PIPEDAL_LIBS}
)
################################# #################################
@@ -448,15 +444,11 @@ add_executable(pipedaltest
MemDebug.hpp MemDebug.hpp
) )
target_link_libraries(pipedaltest PRIVATE ${PIPEDAL_LIBS} ${ICU_LIBRARIES}) target_link_libraries(pipedaltest PRIVATE ${PIPEDAL_LIBS} ${ICU_LIBRARIES})
target_include_directories(pipedaltest PRIVATE ${PIPEDAL_INCLUDES} target_include_directories(pipedaltest PRIVATE ${PIPEDAL_INCLUDES})
)
set_target_properties(pipedaltest PROPERTIES EXCLUDE_FROM_ALL ${PIPEDAL_EXCLUDE_TESTS}) set_target_properties(pipedaltest PROPERTIES EXCLUDE_FROM_ALL ${PIPEDAL_EXCLUDE_TESTS})
if (NOT GITHUB_ACTIONS) # Google perftools are not available on Github action servers. if (NOT GITHUB_ACTIONS) # Google perftools are not available on Github action servers.
add_executable(pipedalProfilePlugin add_executable(pipedalProfilePlugin
profilePluginMain.cpp ArmPerformanceCounters.hpp profilePluginMain.cpp ArmPerformanceCounters.hpp
) )
@@ -467,10 +459,8 @@ if (NOT GITHUB_ACTIONS) # Google perftools are not available on Github action se
if (${DEBIAN_ARCHITECTURE} MATCHES arm64) if (${DEBIAN_ARCHITECTURE} MATCHES arm64)
target_compile_definitions(pipedalProfilePlugin PRIVATE "PIPEDAL_AARCH64") target_compile_definitions(pipedalProfilePlugin PRIVATE "PIPEDAL_AARCH64")
endif() endif()
endif() endif()
add_executable(jsonTest add_executable(jsonTest
testMain.cpp testMain.cpp
jsonTest.cpp jsonTest.cpp
@@ -484,8 +474,7 @@ add_executable(jsonTest
PromiseTest.cpp PromiseTest.cpp
) )
target_link_libraries(jsonTest PRIVATE PiPedalCommon) target_link_libraries(jsonTest PRIVATE PiPedalCommon)
target_include_directories(jsonTest PRIVATE ${PIPEDAL_INCLUDES} target_include_directories(jsonTest PRIVATE ${PIPEDAL_INCLUDES})
)
set_target_properties(jsonTest PROPERTIES EXCLUDE_FROM_ALL ${PIPEDAL_EXCLUDE_TESTS}) set_target_properties(jsonTest PROPERTIES EXCLUDE_FROM_ALL ${PIPEDAL_EXCLUDE_TESTS})
@@ -710,8 +699,7 @@ if(ENABLE_VST3)
# asan_options.cpp # asan_options.cpp
# ) # )
target_include_directories(vst3test PRIVATE ${PIPEDAL_INCLUDES} target_include_directories(vst3test PRIVATE ${PIPEDAL_INCLUDES})
)
target_link_libraries(vst3test PRIVATE ${PIPEDAL_LIBS} target_link_libraries(vst3test PRIVATE ${PIPEDAL_LIBS}
@@ -773,8 +761,7 @@ add_executable(pipedal_latency_test
CpuUse.cpp CpuUse.cpp
) )
target_link_libraries(pipedal_latency_test PRIVATE pthread asound PiPedalCommon target_link_libraries(pipedal_latency_test PRIVATE pthread asound PiPedalCommon)
)
add_executable(pipedal_alsa_info add_executable(pipedal_alsa_info
@@ -834,7 +821,7 @@ target_link_libraries(pipedaladmind PRIVATE PiPedalCommon pthread atomic stdc++f
add_executable(processcopyrights copyrightMain.cpp add_executable(processcopyrights copyrightMain.cpp
CommandLineParser.hpp CommandLineParser.hpp
PiPedalException.hpp PiPedalException.hpp
asan_options.cpp ) asan_options.cpp)
target_link_libraries(processcopyrights PRIVATE PiPedalCommon stdc++fs) target_link_libraries(processcopyrights PRIVATE PiPedalCommon stdc++fs)
set (REACT_BUILD_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../build/react/build/) set (REACT_BUILD_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../build/react/build/)
@@ -876,7 +863,7 @@ add_custom_target (
install (TARGETS pipedalconfig pipedal_kconfig pipedal_latency_test DESTINATION ${CMAKE_INSTALL_PREFIX}/bin install (TARGETS pipedalconfig pipedal_kconfig pipedal_latency_test DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
EXPORT pipedalTargets) EXPORT pipedalTargets)
install (TARGETS pipedald pipedaladmind pipedal_update DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin install (TARGETS pipedald pipedaladmind pipedal_update DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin
EXPORT pipedalSbinTargets) EXPORT pipedalSbinTargets)
+18 -17
View File
@@ -18,6 +18,7 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "pch.h" #include "pch.h"
#include "restrict.hpp"
#include "Lv2Effect.hpp" #include "Lv2Effect.hpp"
#include "PiPedalException.hpp" #include "PiPedalException.hpp"
#include <lv2/lv2plug.in/ns/ext/worker/worker.h> #include <lv2/lv2plug.in/ns/ext/worker/worker.h>
@@ -514,15 +515,15 @@ int Lv2Effect::GetControlIndex(const std::string &key) const
Lv2Effect::~Lv2Effect() Lv2Effect::~Lv2Effect()
{ {
if (activated)
{
Deactivate();
}
if (worker) if (worker)
{ {
worker->Close(); worker->Close();
worker = nullptr; // delete the worker first! worker = nullptr; // delete the worker first!
} }
if (activated)
{
Deactivate();
}
if (pInstance) if (pInstance)
{ {
lilv_instance_free(pInstance); lilv_instance_free(pInstance);
@@ -635,7 +636,7 @@ void Lv2Effect::Deactivate()
lilv_instance_deactivate(pInstance); lilv_instance_deactivate(pInstance);
} }
static inline void CopyBuffer(float *input, float *output, uint32_t frames) static inline void CopyBuffer(float *restrict input, float *restrict output, uint32_t frames)
{ {
for (uint32_t i = 0; i < frames; ++i) for (uint32_t i = 0; i < frames; ++i)
{ {
@@ -673,7 +674,7 @@ void Lv2Effect::Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBuff
{ {
for (size_t i = 0; i < this->outputMixBuffers.size(); ++i) for (size_t i = 0; i < this->outputMixBuffers.size(); ++i)
{ {
float *__restrict input; float *restrict input;
if (i >= this->inputAudioBuffers.size()) if (i >= this->inputAudioBuffers.size())
{ {
if (this->inputAudioBuffers.size() == 0) if (this->inputAudioBuffers.size() == 0)
@@ -686,8 +687,8 @@ void Lv2Effect::Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBuff
{ {
input = this->inputAudioBuffers[i]; input = this->inputAudioBuffers[i];
} }
float *__restrict pluginOutput = this->outputMixBuffers[i].data(); float *restrict pluginOutput = this->outputMixBuffers[i].data();
float *__restrict finalOutput = this->outputAudioBuffers[i]; float *restrict finalOutput = this->outputAudioBuffers[i];
for (uint32_t i = 0; i < samples; ++i) for (uint32_t i = 0; i < samples; ++i)
{ {
@@ -698,11 +699,11 @@ void Lv2Effect::Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBuff
else if (this->outputAudioPortIndices.size() == 1 && this->outputAudioBuffers.size() == 2) else if (this->outputAudioPortIndices.size() == 1 && this->outputAudioBuffers.size() == 2)
{ {
// 1 plugin output into 2 outputs. // 1 plugin output into 2 outputs.
float *__restrict pluginOutput = this->outputMixBuffers[0].data(); float *restrict pluginOutput = this->outputMixBuffers[0].data();
for (size_t i = 0; i < this->outputMixBuffers.size(); ++i) for (size_t i = 0; i < this->outputMixBuffers.size(); ++i)
{ {
float *__restrict input = this->inputAudioBuffers[i]; float *restrict input = this->inputAudioBuffers[i];
float *__restrict finalOutput = this->outputAudioBuffers[i]; float *restrict finalOutput = this->outputAudioBuffers[i];
for (uint32_t i = 0; i < samples; ++i) for (uint32_t i = 0; i < samples; ++i)
{ {
@@ -750,8 +751,8 @@ void Lv2Effect::Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBuff
if (this->outputAudioBuffers.size() == 1) if (this->outputAudioBuffers.size() == 1)
{ {
float *input = this->inputAudioBuffers[0]; float * restrict input = this->inputAudioBuffers[0];
float *output = this->outputAudioBuffers[0]; float * restrict output = this->outputAudioBuffers[0];
for (uint32_t i = 0; i < samples; ++i) for (uint32_t i = 0; i < samples; ++i)
{ {
output[i] = currentBypass * output[i] + (1 - currentBypass) * input[i]; output[i] = currentBypass * output[i] + (1 - currentBypass) * input[i];
@@ -766,8 +767,8 @@ void Lv2Effect::Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBuff
} }
else else
{ {
float *inputL; float * restrict inputL;
float *inputR; float * restrict inputR;
if (this->inputAudioBuffers.size() == 1) if (this->inputAudioBuffers.size() == 1)
{ {
inputL = inputR = inputAudioBuffers[0]; inputL = inputR = inputAudioBuffers[0];
@@ -777,8 +778,8 @@ void Lv2Effect::Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBuff
inputL = inputAudioBuffers[0]; inputL = inputAudioBuffers[0];
inputR = inputAudioBuffers[1]; inputR = inputAudioBuffers[1];
} }
float *outputL = outputAudioBuffers[0]; float * restrict outputL = outputAudioBuffers[0];
float *outputR = outputAudioBuffers[1]; float * restrict outputR = outputAudioBuffers[1];
for (uint32_t i = 0; i < samples; ++i) for (uint32_t i = 0; i < samples; ++i)
{ {
outputL[i] = currentBypass * outputL[i] + (1 - currentBypass) * inputL[i]; outputL[i] = currentBypass * outputL[i] + (1 - currentBypass) * inputL[i];
+3 -1
View File
@@ -28,6 +28,7 @@
#include "Lv2EventBufferWriter.hpp" #include "Lv2EventBufferWriter.hpp"
#include "Lv2Log.hpp" #include "Lv2Log.hpp"
#include "CrashGuard.hpp" #include "CrashGuard.hpp"
#include "restrict.hpp"
using namespace pipedal; using namespace pipedal;
@@ -417,7 +418,7 @@ void Lv2Pedalboard::Deactivate()
} }
} }
static void Copy(float *input, float *output, uint32_t samples) static void Copy(float *restrict input, float *restrict output, uint32_t samples)
{ {
for (uint32_t i = 0; i < samples; ++i) for (uint32_t i = 0; i < samples; ++i)
{ {
@@ -434,6 +435,7 @@ bool Lv2Pedalboard::Run(float **inputBuffers, float **outputBuffers, uint32_t sa
return false; return false;
} }
} }
for (size_t i = 0; i < samples; ++i) for (size_t i = 0; i < samples; ++i)
{ {
float volume = this->inputVolume.Tick(); float volume = this->inputVolume.Tick();
+1 -1
View File
@@ -104,7 +104,7 @@ static void SetPriority(int realtimePriority, const char *priorityName)
int result = sched_setscheduler(0, SCHED_RR, &param); int result = sched_setscheduler(0, SCHED_RR, &param);
if (result == 0) if (result == 0)
{ {
Lv2Log::debug("Service thread priority successfully boosted."); Lv2Log::debug(SS("Service thread priority successfully boosted. (" << priorityName << ")"));
return; return;
} }
else else
+14 -10
View File
@@ -143,6 +143,7 @@ bool Worker::EmitResponses()
void Worker::WaitForAllResponses() void Worker::WaitForAllResponses()
{ {
// do what we can to clear the response queue in order to avoid memory/object leaks
using Clock = std::chrono::steady_clock; using Clock = std::chrono::steady_clock;
auto startTime = Clock::now(); auto startTime = Clock::now();
while (true) while (true)
@@ -157,11 +158,14 @@ void Worker::WaitForAllResponses()
break; break;
} }
} }
std::chrono::seconds waitDuration = std::chrono::duration_cast<std::chrono::seconds>(Clock::now()-startTime); // pump the plugin with a zero-length buffer.
if (waitDuration.count() > 5) {
throw std::logic_error("Timed out waiting for a Worker task to complete."); std::this_thread::sleep_for(std::chrono::milliseconds(50));
std::chrono::milliseconds waitDuration = std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now()-startTime);
if (waitDuration.count() > 1500) {
Lv2Log::error("Timed out waiting for a Worker task to complete.");
// better to leak than to terminate the application.
} }
std::this_thread::sleep_for(std::chrono::milliseconds(15));
} }
} }
@@ -260,21 +264,16 @@ bool HostWorkerThread::StartThread()
void HostWorkerThread::Close() void HostWorkerThread::Close()
{ {
bool sendClose = false;
{ {
std::lock_guard lock{submitMutex}; std::lock_guard lock{submitMutex};
if (pThread) { if (pThread) {
if (!closed) if (!closed)
{ {
closed = true; closed = true;
sendClose = true; ScheduleWorkNoLock(nullptr, 0, nullptr);
} }
} }
} }
if (sendClose)
{
ScheduleWork(nullptr, 0, nullptr);
}
} }
HostWorkerThread::~HostWorkerThread() HostWorkerThread::~HostWorkerThread()
{ {
@@ -302,6 +301,11 @@ LV2_Worker_Status HostWorkerThread::ScheduleWork(Worker *worker, size_t size, co
{ {
exiting = true; exiting = true;
} }
return ScheduleWorkNoLock(worker, size, data);
}
LV2_Worker_Status HostWorkerThread::ScheduleWorkNoLock(Worker *worker, size_t size, const void *data)
{
if (requestRingBuffer.writeSpace() < sizeof(worker) + sizeof(size) + size) if (requestRingBuffer.writeSpace() < sizeof(worker) + sizeof(size) + size)
{ {
+1
View File
@@ -57,6 +57,7 @@ namespace pipedal {
bool Closed() const { return closed || pThread == nullptr; } bool Closed() const { return closed || pThread == nullptr; }
LV2_Worker_Status ScheduleWork(Worker*worker, size_t size, const void*data); LV2_Worker_Status ScheduleWork(Worker*worker, size_t size, const void*data);
private: private:
LV2_Worker_Status ScheduleWorkNoLock(Worker*worker, size_t size, const void*data);
std::atomic<bool> closed = false; std::atomic<bool> closed = false;
std::unique_ptr<std::thread> pThread; std::unique_ptr<std::thread> pThread;
void ThreadProc() noexcept; void ThreadProc() noexcept;
+9 -2
View File
@@ -1844,6 +1844,7 @@ export class PiPedalModel //implements PiPedalModel
newPedalboard.deleteItem(fromInstanceId); newPedalboard.deleteItem(fromInstanceId);
newPedalboard.addBefore(fromItem, toInstanceId); newPedalboard.addBefore(fromItem, toInstanceId);
newPedalboard.selectedPlugin = fromItem.instanceId;
this.setModelPedalboard(newPedalboard); this.setModelPedalboard(newPedalboard);
this.updateServerPedalboard(); this.updateServerPedalboard();
@@ -1863,7 +1864,7 @@ export class PiPedalModel //implements PiPedalModel
newPedalboard.deleteItem(fromInstanceId); newPedalboard.deleteItem(fromInstanceId);
newPedalboard.addAfter(fromItem, toInstanceId); newPedalboard.addAfter(fromItem, toInstanceId);
newPedalboard.selectedPlugin = fromItem.instanceId;
this.setModelPedalboard(newPedalboard); this.setModelPedalboard(newPedalboard);
this.updateServerPedalboard(); this.updateServerPedalboard();
} }
@@ -1885,6 +1886,7 @@ export class PiPedalModel //implements PiPedalModel
newPedalboard.addToStart(fromItem); newPedalboard.addToStart(fromItem);
newPedalboard.selectedPlugin = fromItem.instanceId;
this.setModelPedalboard(newPedalboard); this.setModelPedalboard(newPedalboard);
this.updateServerPedalboard(); this.updateServerPedalboard();
} }
@@ -1904,6 +1906,7 @@ export class PiPedalModel //implements PiPedalModel
newPedalboard.addToEnd(fromItem); newPedalboard.addToEnd(fromItem);
newPedalboard.selectedPlugin = fromItem.instanceId;
this.setModelPedalboard(newPedalboard); this.setModelPedalboard(newPedalboard);
this.updateServerPedalboard(); this.updateServerPedalboard();
@@ -1931,7 +1934,7 @@ export class PiPedalModel //implements PiPedalModel
let emptyItem = newPedalboard.createEmptyItem(); let emptyItem = newPedalboard.createEmptyItem();
newPedalboard.replaceItem(fromInstanceId, emptyItem); newPedalboard.replaceItem(fromInstanceId, emptyItem);
newPedalboard.replaceItem(toInstanceId, fromItem); newPedalboard.replaceItem(toInstanceId, fromItem);
newPedalboard.selectedPlugin = fromItem.instanceId;
this.setModelPedalboard(newPedalboard); this.setModelPedalboard(newPedalboard);
this.updateServerPedalboard(); this.updateServerPedalboard();
@@ -1955,6 +1958,7 @@ export class PiPedalModel //implements PiPedalModel
} }
let newItem = newPedalboard.createEmptyItem(); let newItem = newPedalboard.createEmptyItem();
newPedalboard.addItem(newItem, instanceId, append); newPedalboard.addItem(newItem, instanceId, append);
newPedalboard.selectedPlugin = newItem.instanceId;
this.setModelPedalboard(newPedalboard); this.setModelPedalboard(newPedalboard);
this.updateServerPedalboard(); this.updateServerPedalboard();
return newItem.instanceId; return newItem.instanceId;
@@ -1980,6 +1984,8 @@ export class PiPedalModel //implements PiPedalModel
} }
let newItem = newPedalboard.createEmptySplit(); let newItem = newPedalboard.createEmptySplit();
newPedalboard.addItem(newItem, instanceId, append); newPedalboard.addItem(newItem, instanceId, append);
newPedalboard.selectedPlugin = newItem.instanceId;
this.setModelPedalboard(newPedalboard); this.setModelPedalboard(newPedalboard);
this.updateServerPedalboard(); this.updateServerPedalboard();
return newItem.instanceId; return newItem.instanceId;
@@ -1998,6 +2004,7 @@ export class PiPedalModel //implements PiPedalModel
newPedalboard.setItemEmpty(item); newPedalboard.setItemEmpty(item);
newPedalboard.selectedPlugin = item.instanceId;
this.setModelPedalboard(newPedalboard); this.setModelPedalboard(newPedalboard);
this.updateServerPedalboard(); this.updateServerPedalboard();
return item.instanceId; return item.instanceId;