diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index a239fad..b845419 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -30,7 +30,7 @@ jobs: sudo apt install libasound2-dev sudo apt install libwebsocketpp-dev authbind sudo apt install libsdbus-c++-dev libsdbus-c++-bin - sudo apt install libavahi-client-dev libzip-dev libicu-dev + sudo apt install libavahi-client-dev libzip-dev libicu-dev apt git submodule update --init --recursive ./react-config diff --git a/.vscode/launch.json b/.vscode/launch.json index cc7a005..0ef6f21 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -172,6 +172,38 @@ } ] }, + { + "name": "(gdb) profilePlugin Launch", + "type": "cppdbg", + "request": "launch", + // Resolved by CMake Tools: + "program": "${command:cmake.launchTargetPath}", + + "args": [ "Nam_Profile", "--no-profile","-w" ], + + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [ + { + "name": "PATH", + "value": "$PATH:${command:cmake.launchTargetDirectory}" + }, + { + "name": "OTHER_VALUE", + "value": "Something something" + } + ], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a429cc..8b46af9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,10 @@ cmake_minimum_required(VERSION 3.16.0) project(pipedal - VERSION 1.2.45 + VERSION 1.2.47 DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi" HOMEPAGE_URL "https://rerdavies.github.io/pipedal" ) -set (DISPLAY_VERSION "PiPedal v1.2.45-Release") +set (DISPLAY_VERSION "PiPedal v1.2.47-Release") set (PACKAGE_ARCHITECTURE "arm64") set (CMAKE_INSTALL_PREFIX "/usr/") diff --git a/NetworkManagerP2P/src/DBusLog.cpp b/NetworkManagerP2P/src/DBusLog.cpp index d3a4668..9c01c28 100644 --- a/NetworkManagerP2P/src/DBusLog.cpp +++ b/NetworkManagerP2P/src/DBusLog.cpp @@ -6,6 +6,7 @@ #include #include "ss.hpp" #include +#include "ofstream_synced.hpp" namespace impl { @@ -72,7 +73,7 @@ public: f << "trace: " << message << std::endl; } private: - std::ofstream f; + pipedal::ofstream_synced f; }; class SystemdDBusLogger : public IDBusLogger { diff --git a/NetworkManagerP2P/src/NMP2pSettings.cpp b/NetworkManagerP2P/src/NMP2pSettings.cpp index 21b9917..9caf9a6 100644 --- a/NetworkManagerP2P/src/NMP2pSettings.cpp +++ b/NetworkManagerP2P/src/NMP2pSettings.cpp @@ -10,6 +10,7 @@ #include "ChannelInfo.hpp" #include "DBusLog.hpp" #include +#include "ofstream_synced.hpp" using namespace pipedal; P2pSettings::P2pSettings(const std::filesystem::path&configDirectoryPath, const std::filesystem::path&varDirectoryPath) @@ -193,7 +194,7 @@ void P2pSettings::Load() } static void openWithPerms( - std::ofstream &f, + pipedal::ofstream_synced &f, const std::filesystem::path &path, std::filesystem::perms perms = std::filesystem::perms::owner_read | std::filesystem::perms::owner_write | @@ -221,7 +222,7 @@ void P2pSettings::Save() { auto filename = config_filename(); try { - std::ofstream f; + pipedal::ofstream_synced f; openWithPerms(f,filename); if (!f.is_open()) diff --git a/PiPedalCommon/src/CMakeLists.txt b/PiPedalCommon/src/CMakeLists.txt index b6d34e6..295a63e 100644 --- a/PiPedalCommon/src/CMakeLists.txt +++ b/PiPedalCommon/src/CMakeLists.txt @@ -41,6 +41,7 @@ message(STATUS "NMPIPEDAL CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") # Use the sdbus-c++ target in SDBusCpp namespace add_library(PiPedalCommon STATIC + ofstream_synced.cpp include/ofstream_synced.hpp ConfigSerializer.cpp include/ConfigSerializer.hpp WifiRegs.cpp include/WifiRegs.hpp WifiRegulations.cpp include/WifiRegulations.hpp diff --git a/PiPedalCommon/src/ServiceConfiguration.cpp b/PiPedalCommon/src/ServiceConfiguration.cpp index 7afb2ce..eab9617 100644 --- a/PiPedalCommon/src/ServiceConfiguration.cpp +++ b/PiPedalCommon/src/ServiceConfiguration.cpp @@ -30,6 +30,7 @@ #include #include #include +#include "ofstream_synced.hpp" using namespace pipedal; @@ -65,7 +66,7 @@ void ServiceConfiguration::Save() std::filesystem::create_directories(directory); // make sure the file has correct permissions. - std::ofstream t; + pipedal::ofstream_synced t; t.open(filename); t.close(); diff --git a/PiPedalCommon/src/WriteTemplateFile.cpp b/PiPedalCommon/src/WriteTemplateFile.cpp index 84720e0..102e675 100644 --- a/PiPedalCommon/src/WriteTemplateFile.cpp +++ b/PiPedalCommon/src/WriteTemplateFile.cpp @@ -21,6 +21,7 @@ #include "WriteTemplateFile.hpp" #include #include +#include "ofstream_synced.hpp" using namespace pipedal; @@ -37,7 +38,7 @@ void pipedal::WriteTemplateFile( const std::filesystem::path &inputFile, const std::filesystem::path &outputFile) { - std::ofstream out(outputFile); + pipedal::ofstream_synced out(outputFile); std::ifstream in(inputFile); if (!in.is_open()) diff --git a/PiPedalCommon/src/include/ConfigSerializer.hpp b/PiPedalCommon/src/include/ConfigSerializer.hpp index 0cd5dfa..1bd2f6e 100644 --- a/PiPedalCommon/src/include/ConfigSerializer.hpp +++ b/PiPedalCommon/src/include/ConfigSerializer.hpp @@ -30,6 +30,7 @@ #include "autoptr_vector.h" #include #include +#include "ofstream_synced.hpp" namespace config_serializer { @@ -256,7 +257,7 @@ namespace config_serializer } void Save(const std::string &path) { - std::ofstream f; + pipedal::ofstream_synced f; f.open(path); if (!f.is_open()) { diff --git a/PiPedalCommon/src/include/ofstream_synced.hpp b/PiPedalCommon/src/include/ofstream_synced.hpp new file mode 100644 index 0000000..fa7264f --- /dev/null +++ b/PiPedalCommon/src/include/ofstream_synced.hpp @@ -0,0 +1,28 @@ + +#pragma once + +#include + +namespace pipedal +{ + void FileSystemSync(); + + class ofstream_synced : public std::ofstream + { + public: + using super = std::ofstream; + ofstream_synced() {} + + explicit ofstream_synced(const std::string &filename, ios_base::openmode mode = ios_base::out) + : std::ofstream(filename, mode) + { + } + explicit ofstream_synced(const char *filename, ios_base::openmode mode = ios_base::out) + : std::ofstream(filename, mode) + { + } + void close(); + ~ofstream_synced(); + }; + +} \ No newline at end of file diff --git a/PiPedalCommon/src/ofstream_synced.cpp b/PiPedalCommon/src/ofstream_synced.cpp new file mode 100644 index 0000000..7caa3dd --- /dev/null +++ b/PiPedalCommon/src/ofstream_synced.cpp @@ -0,0 +1,21 @@ +#include "ofstream_synced.hpp" +#include "unistd.h" + +using namespace pipedal; + + +void pipedal::FileSystemSync() +{ + ::sync(); +} +void ofstream_synced::close() { + if (is_open()) + { + super::close(); + ::sync(); + } +} +ofstream_synced::~ofstream_synced() +{ + ofstream_synced::close(); +} \ No newline at end of file diff --git a/README.md b/README.md index 1639f4d..00f5dc5 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,13 @@ -Download: v1.2.45 +Download: v1.2.47 Website: [https://rerdavies.github.io/pipedal](https://rerdavies.github.io/pipedal). Documentation: [https://rerdavies.github.io/pipedal/Documentation.html](https://rerdavies.github.io/pipedal/Documentation.html).   -#### NEW version 1.2.45 Release, providing support for Raspberry Pi OS Bookworm. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. +#### NEW version 1.2.47 Release, providing support for Raspberry Pi OS Bookworm. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details.   diff --git a/docs/BuildPrerequisites.md b/docs/BuildPrerequisites.md index 0bc656f..fb0bb47 100644 --- a/docs/BuildPrerequisites.md +++ b/docs/BuildPrerequisites.md @@ -27,7 +27,7 @@ Run the following commands to install dependent libraries required by the PiPeda sudo apt install -y authbind sudo apt install -y libavahi-client-dev sudo apt install -y libnm-dev libicu-dev - sudo apt install -y libsdbus-c++-dev libzip-dev + sudo apt install -y libsdbus-c++-dev libzip-dev google-perftools libgoogle-perftools-dev ### Installing Sources diff --git a/docs/Installing.md b/docs/Installing.md index 32a2bde..6e3b6e2 100644 --- a/docs/Installing.md +++ b/docs/Installing.md @@ -13,17 +13,17 @@ page_icon: img/Install4.jpg Download the most recent Debian (.deb) package for your platform: -- [Raspberry Pi OS bookworm (64-bit) v1.2.45](https://github.com/rerdavies/pipedal/releases/download/) +- [Raspberry Pi OS bookworm (64-bit) v1.2.47](https://github.com/rerdavies/pipedal/releases/download/) - [Ubuntu/Raspberry Pi OS bullseyeye (64-bit) v1.2.31](https://github.com/rerdavies/pipedal/releases/download/v1.1.31/pipedal_1.1.31_arm64.deb) -Version 1.2.45 has not yet been tested on Ubuntu or Raspberry Pi OS bullseye. On these platforms, we recommend that you use version 1.1.31. +Version 1.2.47 has not yet been tested on Ubuntu or Raspberry Pi OS bullseye. On these platforms, we recommend that you use version 1.1.31. Install the package by running ``` sudo apt update cd ~/Downloads - sudo apt-get install pipedal_1.2.45_arm64.deb + sudo apt-get install pipedal_1.2.47_arm64.deb ``` Adjust accordingly if you have downloaded v1.1.31. diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index d2dd809..7878458 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -1,4 +1,13 @@ # Release Notes +## PiPedal 1.2.47 Release + +This version fixes an significant and dangerous defect that may cause loss of presets, banks or configuration data if you remove power from your Raspberry Pi within up to five minutes of last saving data instead of performing an orderly shutdown or reboot. Users of PiPedal should upgrade immediately. + +This release also fixes a significant performance issue with TooB Neural Amp Modeler. + +Bug fixes: +- Explicity sync files to disk immiately after saving, so that they won't be lost if power is removed from the Raspberry Pi. +- Fixes a significant performance issue with TooB Neural Amp Modeler. ## PiPedal 1.2.45 Release diff --git a/docs/download.md b/docs/download.md index 16ce4ed..96df911 100644 --- a/docs/download.md +++ b/docs/download.md @@ -4,17 +4,17 @@ Download the most recent Debian (.deb) package for your platform: -- Raspberry Pi OS Bookworm (64-bit) v1.2.45 +- Raspberry Pi OS Bookworm (64-bit) v1.2.47 - Ubuntu 21.04 or Raspberry Pi OS bullseyeyeye (64-bit) v1.1.31 -v1.2.45 is does not currently support Ubuntu 21.04, or older versions of Raspberry Pi OS. +v1.2.47 is does not currently support Ubuntu 21.04, or older versions of Raspberry Pi OS. Install the package by running ``` sudo apt update cd ~/Downloads - sudo apt-get install ./pipedal_1.2.45_arm64.deb + sudo apt-get install ./pipedal_1.2.47_arm64.deb ``` Follow the instructions in [_Configuring PiPedal After Installation_](https://rerdavies.github.io/pipedal/Configuring.html) to complete the installation. diff --git a/docs/index.md b/docs/index.md index 7660af7..32d6163 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,7 +1,7 @@ -v1.2.45 +v1.2.47   @@ -9,7 +9,7 @@ To download PiPedal, click [here](download.md). To view PiPedal documentation, click [here](Documentation.md). -#### NEW version 1.2.45 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. +#### NEW version 1.2.47 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details.   diff --git a/lv2/aarch64/ToobAmp.lv2/CabIR.ttl b/lv2/aarch64/ToobAmp.lv2/CabIR.ttl index a191411..12c5d7f 100644 --- a/lv2/aarch64/ToobAmp.lv2/CabIR.ttl +++ b/lv2/aarch64/ToobAmp.lv2/CabIR.ttl @@ -88,7 +88,7 @@ cabir:impulseFile3 doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 45 ; + lv2:microVersion 47 ; rdfs:comment """ TooB Cab IR is a convolution-based guitar cabinet impulse response simulator. diff --git a/lv2/aarch64/ToobAmp.lv2/CabSim.ttl b/lv2/aarch64/ToobAmp.lv2/CabSim.ttl index c89b203..8ac7cce 100644 --- a/lv2/aarch64/ToobAmp.lv2/CabSim.ttl +++ b/lv2/aarch64/ToobAmp.lv2/CabSim.ttl @@ -49,7 +49,7 @@ toob:frequencyResponseVector doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 45 ; + lv2:microVersion 47 ; mod:brand "TooB"; mod:label "TooB CabSim"; diff --git a/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl b/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl index 789f16c..86ed992 100644 --- a/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl @@ -51,7 +51,7 @@ toobimpulse:impulseFile doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 45 ; + lv2:microVersion 47 ; rdfs:comment """ Convolution reverb is a notoriously compute-intensive effect. If you are having performance issues, use the Max T control to constrain the length of the impulse file to diff --git a/lv2/aarch64/ToobAmp.lv2/ConvolutionReverbStereo.ttl b/lv2/aarch64/ToobAmp.lv2/ConvolutionReverbStereo.ttl index fb049b4..bdd2240 100644 --- a/lv2/aarch64/ToobAmp.lv2/ConvolutionReverbStereo.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ConvolutionReverbStereo.ttl @@ -49,7 +49,7 @@ toobimpulse:impulseFile doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 45 ; + lv2:microVersion 47 ; rdfs:comment """ diff --git a/lv2/aarch64/ToobAmp.lv2/InputStage.ttl b/lv2/aarch64/ToobAmp.lv2/InputStage.ttl index 6ee4129..6cb7769 100644 --- a/lv2/aarch64/ToobAmp.lv2/InputStage.ttl +++ b/lv2/aarch64/ToobAmp.lv2/InputStage.ttl @@ -65,7 +65,7 @@ inputStage:filterGroup doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 45 ; + lv2:microVersion 47 ; mod:brand "TooB"; mod:label "TooB Input"; diff --git a/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl b/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl index c4052c8..c50570a 100644 --- a/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl +++ b/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl @@ -1,594 +1,594 @@ -@prefix doap: . -@prefix lv2: . -@prefix rdf: . -@prefix rdfs: . -@prefix units: . -@prefix urid: . -@prefix atom: . -@prefix midi: . -@prefix epp: . -@prefix uiext: . -@prefix idpy: . -@prefix foaf: . -@prefix mod: . -@prefix param: . -@prefix work: . -@prefix pg: . - -@prefix atom: . -@prefix patch: . -@prefix plug: . -@prefix rdfs: . -@prefix state: . -@prefix urid: . -@prefix xsd: . -@prefix modgui: . -@prefix pstage: . - - -pstage:stage1 - a param:ControlGroup , - pg:InputGroup ; - lv2:name "Stage 1" ; - lv2:symbol "stage1" . - -pstage:stage2 - a param:ControlGroup , - pg:InputGroup ; - lv2:name "Stage 2" ; - lv2:symbol "stage2" . - - -pstage:stage3 - a param:ControlGroup , - pg:InputGroup ; - lv2:name "Stage 3" ; - lv2:symbol "stage3" . - - - - a lv2:Parameter ; - rdfs:label "uiState" ; - rdfs:range atom:Vector . - - - - a foaf:Person ; - foaf:name "Robin Davies" ; - foaf:mbox ; - foaf:homepage . - - - a lv2:Plugin , - lv2:SimulatorPlugin ; - - doap:name "TooB Power Stage"; - - doap:license ; - doap:maintainer ; - lv2:minorVersion 0 ; - lv2:microVersion 29 ; - - mod:brand "TooB"; - mod:label "Power Stage"; - lv2:requiredFeature urid:map ; - lv2:optionalFeature lv2:hardRTCapable; - patch:readable - ; - -rdfs:comment """ -The TooB Power Stage provides flexible and nuanced emulations of guitar amp power stages. -The goal of TooB Power Stage is emulate the best features of real tube amps without slavishly -reproducing the undesirable features as well. - -TooB Power Stage provides 3 consecutive gain stages, each of which can be overdriven to varying -degrees. Power transformer sag is emulated using the SAG and SAGD controls, which emulate the -volume and distortion effects introduced when power transformer voltage sags. - -Each gain section expects an input level that is nominally in the range of 12db to 0db. -It's ok if the levels are slightly over 0db, but they should not be massively over. Use the -Trim control to adjust input signal levels. - -The Gain controls use oversampled atan wave shaping to emulate the effect of overdriving -gain stages. Atan wave shaping provides rich even-order harmonics similar to those produced by a tube -amp power stage. A modest amount of gain will produce the characteristically "warm" sound of a tube -gain stage. More extreme settings produce harder overdrive. - -Judicious use of the LO CUT and HI CUT controls have interesting and subtle effects on the overall sound. -You are encouraged to play with these controls to see what happens. Tightening up low and high frequencies, -in early gain sections can allow you to drive the signal much harder in later gain sections without totally -breaking up. Real amps will usually cut low frequency response at somewhere between 80Hz (Fender) -and 140Hz (Marshall). - -General experience seems to suggest that gradually increasing the gain in each section allows you to drive -the signal harder without introducing undesirable effects in the output. However, there definitely interesting -and subtle differences in tone that can be acheived by not adhering to that rule of thumb. - -The SAG and SAGD controls the emulation of "forgiveness", and "compression" that are provided in a real tube -amp by sagging of power supply voltage. SAG controls how much -the overall volume of the output decreases as the transform sags. It provides the subtle "compression" that -tube amps provide, which is quite different from the compression provided by compressors. SAGD controls -the amount of additional distortion that occurs as the gain stages receive reduced voltage. It provides -the "forgiveness" of a good tube amp. Because SAGD operates in a feedback loop that runs through -all three gain stages. Increasing the distortion in the gain stages increases the power use of the gain stages; -increasing the power use of the gain stages increases the effect of SAG and SAGD which has an opposing effect -on power use. The net result is that SAGD causes signals to break up more gradually -(hence the perceived "forgiveness"). The effects are non-linear, so not easy to describe completely. You -are strongly urged to experiment with SAG and SAGD settings in order to get best tone results. Use very -moderate settings to emulate tube amp output; higher settings of SAG and SAGD will cause audible compression -and pumping of the outputs which may or may not be desireable. - -The TooB Power Stage design is based on an experimental design, that unexpectedly produced extraordinarily -good amp tones. We have not completely explored the sonic possibilities of the -TooB Power Stage. What's known so far: it produces exceptionally pretty clean tones; it can be driven really -hard, and seems to preserve an unusual amount of detail in the signal even when being driven hard; and it -can definitely produce signals that have many of the most prized features of tube amplifiers: warmth, detail, -subtle "compression" without coloration, and forgiving break up. - -Explore! - -The TooB Input Stage is part of the TooB Amp Emulation Toolkit. Typically, you would -use the following plugins in the following order. - -- TooB Input Stage - -- TooB Tone Stack - -- Toob Power Stage - -- Toob Cabinet Simulator -"""; - lv2:port - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 0 ; - pg:group pstage:stage1 ; - lv2:symbol "trim1" ; - lv2:name "Trim"; - lv2:default 0.0 ; - lv2:minimum -20.0 ; - lv2:maximum 20.0 ; - units:unit units:db ; - rdfs:comment "Trim input level (stage 1)" ; - ], - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 1 ; - pg:group pstage:stage1 ; - lv2:symbol "locut1" ; - lv2:name "Lo Cut"; - lv2:default 30.0 ; - lv2:minimum 30.0 ; - lv2:maximum 300.0 ; - units:unit units:hz; - lv2:designation param:cutoffFrequency ; - - lv2:scalePoint [ - rdfs:label "OFF" ; - rdf:value 30.0 - ]; - rdfs:comment "Low frequency cutoff (stage 1). Set to minimum to disable." ; - - ], [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 2 ; - pg:group pstage:stage1 ; - lv2:symbol "hicut1" ; - lv2:name "Hi Cut"; - lv2:default 19000.0 ; - lv2:minimum 1000.0 ; - lv2:maximum 19000.0; - units:unit units:hz; - lv2:designation param:cutoffFrequency ; - lv2:scalePoint [ - rdfs:label "OFF" ; - rdf:value 19000.0 - ]; - rdfs:comment "High cutoff frequency (stage 1). Set to maximum to disable." ; - - ], - [ - a lv2:InputPort , - lv2:ControlPort ; - pg:group pstage:stage1 ; - - lv2:index 3 ; - lv2:symbol "shape1" ; - lv2:name "Shape1"; - lv2:default 0.0 ; - lv2:minimum 0.0 ; - lv2:maximum 1.0; - lv2:rangeStep 2; - - lv2:portProperty lv2:enumeration ; - rdfs:comment "Wave Shape" ; - lv2:scalePoint [ - rdfs:label "atan" ; - rdf:value 0.0 - ], - [ - rdfs:label "triode" ; - rdf:value 1.0 - ]; - ], - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 4 ; - pg:group pstage:stage1 ; - lv2:symbol "gain1" ; - lv2:name "Gain"; - lv2:default 0.0 ; - lv2:minimum 0.0 ; - lv2:maximum 1.0 ; - rdfs:comment "Gain (stage 1)" - ], - [ - a lv2:InputPort , - lv2:ControlPort ; - pg:group pstage:stage1 ; - - lv2:index 5 ; - lv2:symbol "bias1" ; - lv2:name "Bias"; - lv2:default 0.0 ; - lv2:minimum -2.0 ; - lv2:maximum 2.0; - rdfs:comment "Waveshape bias (stage 1)" - ], - - - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 6 ; - pg:group pstage:stage2 ; - lv2:symbol "trim2" ; - lv2:name "Trim"; - lv2:default 0.0 ; - lv2:minimum -20.0 ; - lv2:maximum 20.0 ; - units:unit units:db ; - rdfs:comment "Trim input (stage 2)" ; - ], - - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 7 ; - pg:group pstage:stage2 ; - lv2:symbol "locut2" ; - lv2:name "Lo Cut"; - lv2:default 30.0 ; - lv2:minimum 30.0 ; - lv2:maximum 300.0 ; - units:unit units:hz; - lv2:designation param:cutoffFrequency ; - - lv2:scalePoint [ - rdfs:label "OFF" ; - rdf:value 30.0 - ] - ; - rdfs:comment "Low frequency cutoff (stage 2). Set to minimum to disable." ; - - ], - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 8 ; - pg:group pstage:stage2 ; - lv2:symbol "hicut2" ; - lv2:name "Hi Cut"; - lv2:default 19000.0 ; - lv2:minimum 1000.0 ; - lv2:maximum 19000.0; - units:unit units:hz; - lv2:designation param:cutoffFrequency ; - lv2:scalePoint [ - rdfs:label "OFF" ; - rdf:value 19000.0 - ]; - rdfs:comment "High cutoff frequency (stage 2). Set to maximum to disable." ; - - ], - - [ - a lv2:InputPort , - lv2:ControlPort ; - pg:group pstage:stage2 ; - - lv2:index 9 ; - lv2:symbol "shape2" ; - lv2:name "Shape2"; - lv2:default 0.0 ; - lv2:minimum 0.0 ; - lv2:maximum 1.0; - lv2:rangeStep 2; - - lv2:portProperty lv2:enumeration ; - rdfs:comment "Wave Shape" ; - lv2:scalePoint [ - rdfs:label "atan" ; - rdf:value 0.0 - ], - [ - rdfs:label "triode" ; - rdf:value 1.0 - ] - ], - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 10 ; - pg:group pstage:stage2 ; - lv2:symbol "gain2" ; - lv2:name "Gain"; - lv2:default 0.0 ; - lv2:minimum 0.0 ; - lv2:maximum 1.0 ; - rdfs:comment "Gain (stage 2)" - ], - - [ - a lv2:InputPort , - lv2:ControlPort ; - pg:group pstage:stage2 ; - - lv2:index 11 ; - lv2:symbol "bias2" ; - lv2:name "Bias"; - lv2:default 0.0 ; - lv2:minimum -2.0 ; - lv2:maximum 2.0; - rdfs:comment "Waveshape bias (stage 2)" - - ], - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 12 ; - pg:group pstage:stage2 ; - lv2:symbol "gain2_enable" ; - lv2:name "Enable2"; - lv2:default 1 ; - lv2:minimum 0 ; - lv2:maximum 1; - lv2:portProperty lv2:toggled ; - rdfs:comment "Enable gain stage 2" ; - ], - - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 13 ; - pg:group pstage:stage3 ; - lv2:symbol "trim3" ; - lv2:name "Trim"; - lv2:default 0.0 ; - lv2:minimum -20.0 ; - lv2:maximum 20.0 ; - units:unit units:db ; - rdfs:comment "Trim input level (stage 3)." ; - ], - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 14 ; - pg:group pstage:stage3 ; - lv2:symbol "locut3" ; - lv2:name "Lo Cut"; - lv2:default 30.0 ; - lv2:minimum 30.0 ; - lv2:maximum 300.0 ; - units:unit units:hz; - lv2:designation param:cutoffFrequency ; - - lv2:scalePoint [ - rdfs:label "OFF" ; - rdf:value 30.0 - ]; - rdfs:comment "Low frequency cutoff (stage 3). Set to minimum to disable." ; - - ], - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 15 ; - pg:group pstage:stage3 ; - lv2:symbol "hicut3" ; - lv2:name "Hi Cut"; - lv2:default 19000.0 ; - lv2:minimum 1000.0 ; - lv2:maximum 19000.0; - units:unit units:hz; - lv2:designation param:cutoffFrequency ; - lv2:scalePoint [ - rdfs:label "OFF" ; - rdf:value 19000 - ]; - rdfs:comment "High cutoff frequency (stage 3). Set to minimum to disable." ; - - ], - [ - a lv2:InputPort , - lv2:ControlPort ; - pg:group pstage:stage3 ; - - lv2:index 16 ; - lv2:symbol "shape3" ; - lv2:name "Shape3"; - lv2:default 0.0 ; - lv2:minimum 0.0 ; - lv2:maximum 1.0; - lv2:rangeStep 2; - - lv2:portProperty lv2:enumeration ; - rdfs:comment "Wave Shape" ; - lv2:scalePoint [ - rdfs:label "atan" ; - rdf:value 0.0 - ], - [ - rdfs:label "triode" ; - rdf:value 1.0 - ] - ], - - - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 17 ; - pg:group pstage:stage3 ; - lv2:symbol "gain3" ; - lv2:name "Gain"; - lv2:default 0.0 ; - lv2:minimum 0.0 ; - lv2:maximum 1.0 ; - rdfs:comment "Gain (stage 3)." - ], - [ - a lv2:InputPort , - lv2:ControlPort ; - pg:group pstage:stage3 ; - - lv2:index 18 ; - lv2:symbol "bias3" ; - lv2:name "Bias"; - lv2:default 0.0 ; - lv2:minimum -2.0 ; - lv2:maximum 2.0; - rdfs:comment "Waveshape bias (stage 3)" - - ], - - - - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 19 ; - pg:group pstage:stage3 ; - lv2:symbol "gain3_enable" ; - lv2:name "Enable3"; - lv2:default 1 ; - lv2:minimum 0 ; - lv2:maximum 1; - lv2:portProperty lv2:toggled ; - rdfs:comment "Enable gain stage 3" ; - ], - - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 20 ; - lv2:symbol "sag" ; - lv2:name "Sag Vol"; - lv2:default 0.0 ; - lv2:minimum 0.0 ; - lv2:maximum 1.0; - rdfs:comment "Transformer sag (volume)." ; - ], - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 21 ; - lv2:symbol "sagd" ; - lv2:name "Sag Dist"; - lv2:default 0.0 ; - lv2:minimum 0.0 ; - lv2:maximum 1.0; - rdfs:comment "Transformer sag (distortion)." ; - ], - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 22 ; - lv2:symbol "master" ; - lv2:name "Master"; - lv2:default -0.0 ; - lv2:minimum -60.0 ; - lv2:maximum 30.0; - units:unit units:db; - lv2:scalePoint [ - rdfs:label "-60db" ; - rdf:value -60.0 - ] , [ - rdfs:label "0db" ; - rdf:value 0.0 - ], [ - rdfs:label "30db" ; - rdf:value -30.0 - ]; - rdfs:comment "Master volume." ; - - ], - [ - a lv2:AudioPort , - lv2:InputPort ; - lv2:index 23 ; - lv2:symbol "in" ; - lv2:name "In" - ] , [ - a lv2:AudioPort , - lv2:OutputPort ; - lv2:index 24 ; - lv2:symbol "out" ; - lv2:name "Out" - ],[ - a atom:AtomPort , - lv2:InputPort; - atom:bufferType atom:Sequence ; - # atom:supports patch:Message; - lv2:designation lv2:control ; - lv2:index 25 ; - lv2:symbol "control" ; - lv2:name "Control" ; - rdfs:comment "Plugin to GUI communication" ; - ] , [ - a atom:AtomPort , - lv2:OutputPort ; - atom:bufferType atom:Sequence ; - # atom:supports patch:Message; - lv2:designation lv2:control ; - lv2:index 26 ; - lv2:symbol "notify" ; - lv2:name "Notify" ; - rdfs:comment "Plugin to GUI communication" ; - ], - [ - a lv2:InputPort , - lv2:ControlPort ; - - lv2:index 27 ; - lv2:symbol "sagf" ; - lv2:name "SagF"; - lv2:default 13.0 ; - lv2:minimum 5.0 ; - lv2:maximum 25.0; - rdfs:comment "Sag filter Fc." ; - ] - . - - +@prefix doap: . +@prefix lv2: . +@prefix rdf: . +@prefix rdfs: . +@prefix units: . +@prefix urid: . +@prefix atom: . +@prefix midi: . +@prefix epp: . +@prefix uiext: . +@prefix idpy: . +@prefix foaf: . +@prefix mod: . +@prefix param: . +@prefix work: . +@prefix pg: . + +@prefix atom: . +@prefix patch: . +@prefix plug: . +@prefix rdfs: . +@prefix state: . +@prefix urid: . +@prefix xsd: . +@prefix modgui: . +@prefix pstage: . + + +pstage:stage1 + a param:ControlGroup , + pg:InputGroup ; + lv2:name "Stage 1" ; + lv2:symbol "stage1" . + +pstage:stage2 + a param:ControlGroup , + pg:InputGroup ; + lv2:name "Stage 2" ; + lv2:symbol "stage2" . + + +pstage:stage3 + a param:ControlGroup , + pg:InputGroup ; + lv2:name "Stage 3" ; + lv2:symbol "stage3" . + + + + a lv2:Parameter ; + rdfs:label "uiState" ; + rdfs:range atom:Vector . + + + + a foaf:Person ; + foaf:name "Robin Davies" ; + foaf:mbox ; + foaf:homepage . + + + a lv2:Plugin , + lv2:SimulatorPlugin ; + + doap:name "TooB Power Stage"; + + doap:license ; + doap:maintainer ; + lv2:minorVersion 0 ; + lv2:microVersion 47 ; + + mod:brand "TooB"; + mod:label "Power Stage"; + lv2:requiredFeature urid:map ; + lv2:optionalFeature lv2:hardRTCapable; + patch:readable + ; + +rdfs:comment """ +The TooB Power Stage provides flexible and nuanced emulations of guitar amp power stages. +The goal of TooB Power Stage is emulate the best features of real tube amps without slavishly +reproducing the undesirable features as well. + +TooB Power Stage provides 3 consecutive gain stages, each of which can be overdriven to varying +degrees. Power transformer sag is emulated using the SAG and SAGD controls, which emulate the +volume and distortion effects introduced when power transformer voltage sags. + +Each gain section expects an input level that is nominally in the range of 12db to 0db. +It's ok if the levels are slightly over 0db, but they should not be massively over. Use the +Trim control to adjust input signal levels. + +The Gain controls use oversampled atan wave shaping to emulate the effect of overdriving +gain stages. Atan wave shaping provides rich even-order harmonics similar to those produced by a tube +amp power stage. A modest amount of gain will produce the characteristically "warm" sound of a tube +gain stage. More extreme settings produce harder overdrive. + +Judicious use of the LO CUT and HI CUT controls have interesting and subtle effects on the overall sound. +You are encouraged to play with these controls to see what happens. Tightening up low and high frequencies, +in early gain sections can allow you to drive the signal much harder in later gain sections without totally +breaking up. Real amps will usually cut low frequency response at somewhere between 80Hz (Fender) +and 140Hz (Marshall). + +General experience seems to suggest that gradually increasing the gain in each section allows you to drive +the signal harder without introducing undesirable effects in the output. However, there definitely interesting +and subtle differences in tone that can be acheived by not adhering to that rule of thumb. + +The SAG and SAGD controls the emulation of "forgiveness", and "compression" that are provided in a real tube +amp by sagging of power supply voltage. SAG controls how much +the overall volume of the output decreases as the transform sags. It provides the subtle "compression" that +tube amps provide, which is quite different from the compression provided by compressors. SAGD controls +the amount of additional distortion that occurs as the gain stages receive reduced voltage. It provides +the "forgiveness" of a good tube amp. Because SAGD operates in a feedback loop that runs through +all three gain stages. Increasing the distortion in the gain stages increases the power use of the gain stages; +increasing the power use of the gain stages increases the effect of SAG and SAGD which has an opposing effect +on power use. The net result is that SAGD causes signals to break up more gradually +(hence the perceived "forgiveness"). The effects are non-linear, so not easy to describe completely. You +are strongly urged to experiment with SAG and SAGD settings in order to get best tone results. Use very +moderate settings to emulate tube amp output; higher settings of SAG and SAGD will cause audible compression +and pumping of the outputs which may or may not be desireable. + +The TooB Power Stage design is based on an experimental design, that unexpectedly produced extraordinarily +good amp tones. We have not completely explored the sonic possibilities of the +TooB Power Stage. What's known so far: it produces exceptionally pretty clean tones; it can be driven really +hard, and seems to preserve an unusual amount of detail in the signal even when being driven hard; and it +can definitely produce signals that have many of the most prized features of tube amplifiers: warmth, detail, +subtle "compression" without coloration, and forgiving break up. + +Explore! + +The TooB Input Stage is part of the TooB Amp Emulation Toolkit. Typically, you would +use the following plugins in the following order. + +- TooB Input Stage + +- TooB Tone Stack + +- Toob Power Stage + +- Toob Cabinet Simulator +"""; + lv2:port + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 0 ; + pg:group pstage:stage1 ; + lv2:symbol "trim1" ; + lv2:name "Trim"; + lv2:default 0.0 ; + lv2:minimum -20.0 ; + lv2:maximum 20.0 ; + units:unit units:db ; + rdfs:comment "Trim input level (stage 1)" ; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 1 ; + pg:group pstage:stage1 ; + lv2:symbol "locut1" ; + lv2:name "Lo Cut"; + lv2:default 30.0 ; + lv2:minimum 30.0 ; + lv2:maximum 300.0 ; + units:unit units:hz; + lv2:designation param:cutoffFrequency ; + + lv2:scalePoint [ + rdfs:label "OFF" ; + rdf:value 30.0 + ]; + rdfs:comment "Low frequency cutoff (stage 1). Set to minimum to disable." ; + + ], [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 2 ; + pg:group pstage:stage1 ; + lv2:symbol "hicut1" ; + lv2:name "Hi Cut"; + lv2:default 19000.0 ; + lv2:minimum 1000.0 ; + lv2:maximum 19000.0; + units:unit units:hz; + lv2:designation param:cutoffFrequency ; + lv2:scalePoint [ + rdfs:label "OFF" ; + rdf:value 19000.0 + ]; + rdfs:comment "High cutoff frequency (stage 1). Set to maximum to disable." ; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + pg:group pstage:stage1 ; + + lv2:index 3 ; + lv2:symbol "shape1" ; + lv2:name "Shape1"; + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 1.0; + lv2:rangeStep 2; + + lv2:portProperty lv2:enumeration ; + rdfs:comment "Wave Shape" ; + lv2:scalePoint [ + rdfs:label "atan" ; + rdf:value 0.0 + ], + [ + rdfs:label "triode" ; + rdf:value 1.0 + ]; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 4 ; + pg:group pstage:stage1 ; + lv2:symbol "gain1" ; + lv2:name "Gain"; + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 1.0 ; + rdfs:comment "Gain (stage 1)" + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + pg:group pstage:stage1 ; + + lv2:index 5 ; + lv2:symbol "bias1" ; + lv2:name "Bias"; + lv2:default 0.0 ; + lv2:minimum -2.0 ; + lv2:maximum 2.0; + rdfs:comment "Waveshape bias (stage 1)" + ], + + + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 6 ; + pg:group pstage:stage2 ; + lv2:symbol "trim2" ; + lv2:name "Trim"; + lv2:default 0.0 ; + lv2:minimum -20.0 ; + lv2:maximum 20.0 ; + units:unit units:db ; + rdfs:comment "Trim input (stage 2)" ; + ], + + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 7 ; + pg:group pstage:stage2 ; + lv2:symbol "locut2" ; + lv2:name "Lo Cut"; + lv2:default 30.0 ; + lv2:minimum 30.0 ; + lv2:maximum 300.0 ; + units:unit units:hz; + lv2:designation param:cutoffFrequency ; + + lv2:scalePoint [ + rdfs:label "OFF" ; + rdf:value 30.0 + ] + ; + rdfs:comment "Low frequency cutoff (stage 2). Set to minimum to disable." ; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 8 ; + pg:group pstage:stage2 ; + lv2:symbol "hicut2" ; + lv2:name "Hi Cut"; + lv2:default 19000.0 ; + lv2:minimum 1000.0 ; + lv2:maximum 19000.0; + units:unit units:hz; + lv2:designation param:cutoffFrequency ; + lv2:scalePoint [ + rdfs:label "OFF" ; + rdf:value 19000.0 + ]; + rdfs:comment "High cutoff frequency (stage 2). Set to maximum to disable." ; + + ], + + [ + a lv2:InputPort , + lv2:ControlPort ; + pg:group pstage:stage2 ; + + lv2:index 9 ; + lv2:symbol "shape2" ; + lv2:name "Shape2"; + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 1.0; + lv2:rangeStep 2; + + lv2:portProperty lv2:enumeration ; + rdfs:comment "Wave Shape" ; + lv2:scalePoint [ + rdfs:label "atan" ; + rdf:value 0.0 + ], + [ + rdfs:label "triode" ; + rdf:value 1.0 + ] + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 10 ; + pg:group pstage:stage2 ; + lv2:symbol "gain2" ; + lv2:name "Gain"; + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 1.0 ; + rdfs:comment "Gain (stage 2)" + ], + + [ + a lv2:InputPort , + lv2:ControlPort ; + pg:group pstage:stage2 ; + + lv2:index 11 ; + lv2:symbol "bias2" ; + lv2:name "Bias"; + lv2:default 0.0 ; + lv2:minimum -2.0 ; + lv2:maximum 2.0; + rdfs:comment "Waveshape bias (stage 2)" + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 12 ; + pg:group pstage:stage2 ; + lv2:symbol "gain2_enable" ; + lv2:name "Enable2"; + lv2:default 1 ; + lv2:minimum 0 ; + lv2:maximum 1; + lv2:portProperty lv2:toggled ; + rdfs:comment "Enable gain stage 2" ; + ], + + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 13 ; + pg:group pstage:stage3 ; + lv2:symbol "trim3" ; + lv2:name "Trim"; + lv2:default 0.0 ; + lv2:minimum -20.0 ; + lv2:maximum 20.0 ; + units:unit units:db ; + rdfs:comment "Trim input level (stage 3)." ; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 14 ; + pg:group pstage:stage3 ; + lv2:symbol "locut3" ; + lv2:name "Lo Cut"; + lv2:default 30.0 ; + lv2:minimum 30.0 ; + lv2:maximum 300.0 ; + units:unit units:hz; + lv2:designation param:cutoffFrequency ; + + lv2:scalePoint [ + rdfs:label "OFF" ; + rdf:value 30.0 + ]; + rdfs:comment "Low frequency cutoff (stage 3). Set to minimum to disable." ; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 15 ; + pg:group pstage:stage3 ; + lv2:symbol "hicut3" ; + lv2:name "Hi Cut"; + lv2:default 19000.0 ; + lv2:minimum 1000.0 ; + lv2:maximum 19000.0; + units:unit units:hz; + lv2:designation param:cutoffFrequency ; + lv2:scalePoint [ + rdfs:label "OFF" ; + rdf:value 19000 + ]; + rdfs:comment "High cutoff frequency (stage 3). Set to minimum to disable." ; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + pg:group pstage:stage3 ; + + lv2:index 16 ; + lv2:symbol "shape3" ; + lv2:name "Shape3"; + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 1.0; + lv2:rangeStep 2; + + lv2:portProperty lv2:enumeration ; + rdfs:comment "Wave Shape" ; + lv2:scalePoint [ + rdfs:label "atan" ; + rdf:value 0.0 + ], + [ + rdfs:label "triode" ; + rdf:value 1.0 + ] + ], + + + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 17 ; + pg:group pstage:stage3 ; + lv2:symbol "gain3" ; + lv2:name "Gain"; + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 1.0 ; + rdfs:comment "Gain (stage 3)." + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + pg:group pstage:stage3 ; + + lv2:index 18 ; + lv2:symbol "bias3" ; + lv2:name "Bias"; + lv2:default 0.0 ; + lv2:minimum -2.0 ; + lv2:maximum 2.0; + rdfs:comment "Waveshape bias (stage 3)" + + ], + + + + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 19 ; + pg:group pstage:stage3 ; + lv2:symbol "gain3_enable" ; + lv2:name "Enable3"; + lv2:default 1 ; + lv2:minimum 0 ; + lv2:maximum 1; + lv2:portProperty lv2:toggled ; + rdfs:comment "Enable gain stage 3" ; + ], + + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 20 ; + lv2:symbol "sag" ; + lv2:name "Sag Vol"; + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 1.0; + rdfs:comment "Transformer sag (volume)." ; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 21 ; + lv2:symbol "sagd" ; + lv2:name "Sag Dist"; + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 1.0; + rdfs:comment "Transformer sag (distortion)." ; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 22 ; + lv2:symbol "master" ; + lv2:name "Master"; + lv2:default -0.0 ; + lv2:minimum -60.0 ; + lv2:maximum 30.0; + units:unit units:db; + lv2:scalePoint [ + rdfs:label "-60db" ; + rdf:value -60.0 + ] , [ + rdfs:label "0db" ; + rdf:value 0.0 + ], [ + rdfs:label "30db" ; + rdf:value -30.0 + ]; + rdfs:comment "Master volume." ; + + ], + [ + a lv2:AudioPort , + lv2:InputPort ; + lv2:index 23 ; + lv2:symbol "in" ; + lv2:name "In" + ] , [ + a lv2:AudioPort , + lv2:OutputPort ; + lv2:index 24 ; + lv2:symbol "out" ; + lv2:name "Out" + ],[ + a atom:AtomPort , + lv2:InputPort; + atom:bufferType atom:Sequence ; + # atom:supports patch:Message; + lv2:designation lv2:control ; + lv2:index 25 ; + lv2:symbol "control" ; + lv2:name "Control" ; + rdfs:comment "Plugin to GUI communication" ; + ] , [ + a atom:AtomPort , + lv2:OutputPort ; + atom:bufferType atom:Sequence ; + # atom:supports patch:Message; + lv2:designation lv2:control ; + lv2:index 26 ; + lv2:symbol "notify" ; + lv2:name "Notify" ; + rdfs:comment "Plugin to GUI communication" ; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 27 ; + lv2:symbol "sagf" ; + lv2:name "SagF"; + lv2:default 13.0 ; + lv2:minimum 5.0 ; + lv2:maximum 25.0; + rdfs:comment "Sag filter Fc." ; + ] + . + + diff --git a/lv2/aarch64/ToobAmp.lv2/SpectrumAnalyzer.ttl b/lv2/aarch64/ToobAmp.lv2/SpectrumAnalyzer.ttl index 7887804..120e6d3 100644 --- a/lv2/aarch64/ToobAmp.lv2/SpectrumAnalyzer.ttl +++ b/lv2/aarch64/ToobAmp.lv2/SpectrumAnalyzer.ttl @@ -58,7 +58,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 45 ; + lv2:microVersion 47 ; rdfs:comment "TooB spectrum analyzer" ; mod:brand "TooB"; diff --git a/lv2/aarch64/ToobAmp.lv2/ToneStack.ttl b/lv2/aarch64/ToobAmp.lv2/ToneStack.ttl index a1f71fe..a6d85d6 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToneStack.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToneStack.ttl @@ -55,7 +55,7 @@ tonestack:eqGroup doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 45 ; + lv2:microVersion 47 ; uiext:ui ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.0 b/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.0 index b60a56e..476f52d 120000 --- a/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.0 +++ b/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.0 @@ -1 +1 @@ -ToobAmp.so.1.1.45 \ No newline at end of file +ToobAmp.so.1.1.47 \ No newline at end of file diff --git a/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.1.1.45 b/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.1.1.45 deleted file mode 100644 index 4a0f8af..0000000 Binary files a/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.1.1.45 and /dev/null differ diff --git a/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.1.1.47 b/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.1.1.47 new file mode 100644 index 0000000..ba1c373 Binary files /dev/null and b/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.1.1.47 differ diff --git a/lv2/aarch64/ToobAmp.lv2/ToobAmpUI.so b/lv2/aarch64/ToobAmp.lv2/ToobAmpUI.so index cd832d4..ac1cea1 100644 Binary files a/lv2/aarch64/ToobAmp.lv2/ToobAmpUI.so and b/lv2/aarch64/ToobAmp.lv2/ToobAmpUI.so differ diff --git a/lv2/aarch64/ToobAmp.lv2/ToobChorus.ttl b/lv2/aarch64/ToobAmp.lv2/ToobChorus.ttl index c0a9add..8ae66fb 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobChorus.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobChorus.ttl @@ -40,7 +40,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 45 ; + lv2:microVersion 47 ; rdfs:comment """ Emulation of a Boss CE-2 Chorus. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl b/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl index d5e37c8..3e43dc4 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl @@ -41,7 +41,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 45 ; + lv2:microVersion 47 ; rdfs:comment """ A straightforward no-frills digital delay. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobFlanger.ttl b/lv2/aarch64/ToobAmp.lv2/ToobFlanger.ttl index c09137e..cc9fa8a 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobFlanger.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobFlanger.ttl @@ -41,7 +41,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 45 ; + lv2:microVersion 47 ; rdfs:comment """ Emulation of a Boss BF-2 Flanger, based on circuit analysis and simulation of the original. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobFlangerStereo.ttl b/lv2/aarch64/ToobAmp.lv2/ToobFlangerStereo.ttl index 90126b8..10bf743 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobFlangerStereo.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobFlangerStereo.ttl @@ -41,7 +41,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 45 ; + lv2:microVersion 47 ; rdfs:comment """ Digital emulation of a Boss BF-2 Flanger. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobFreeverb.ttl b/lv2/aarch64/ToobAmp.lv2/ToobFreeverb.ttl index 61564d0..e07b734 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobFreeverb.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobFreeverb.ttl @@ -41,7 +41,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 45 ; + lv2:microVersion 47 ; rdfs:comment """ Toob Freeverb is an Lv2 implementation of the famous Freeverb reverb effect. FreeVerb delivers a well-balanced reverb with very little tonal coloration. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobML.ttl b/lv2/aarch64/ToobAmp.lv2/ToobML.ttl index c4b586e..a712563 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobML.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobML.ttl @@ -63,7 +63,7 @@ toobml:sagGroup doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 45 ; + lv2:microVersion 47 ; rdfs:comment """ The TooB ML Amplifier plugin provides emulation of a variety of amplifiers and overdrive pedals that are implemented using neural-network-based machine learning models of real amplifiers. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobNeuralAmpModeler.ttl b/lv2/aarch64/ToobAmp.lv2/ToobNeuralAmpModeler.ttl index beb259e..52ebf6d 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobNeuralAmpModeler.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobNeuralAmpModeler.ttl @@ -58,7 +58,7 @@ toobNam:eqGroup doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 45 ; + lv2:microVersion 47 ; rdfs:comment """ A port of Steven Atkinson's Neural Amp Modeler to LV2. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobTuner.ttl b/lv2/aarch64/ToobAmp.lv2/ToobTuner.ttl index f09a822..ab6491d 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobTuner.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobTuner.ttl @@ -40,7 +40,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 45 ; + lv2:microVersion 47 ; rdfs:comment """ TooB Tuner is a chromatic guitar tuner. """ ; diff --git a/profiler_tools/ToobNam_profile b/profiler_tools/ToobNam_profile new file mode 100755 index 0000000..7c9fef7 --- /dev/null +++ b/profiler_tools/ToobNam_profile @@ -0,0 +1,3 @@ +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +${SCRIPT_DIR}/../build/src/profilePlugin -w ToobNam_Profile -o /tmp/ToobNam.perf &&\ +google-pprof --text ${SCRIPT_DIR}/../build/src/profilePlugin --add_lib /usr/lib/lv2/ToobAmp.lv2/ToobAmp.so /tmp/ToobNam.perf >./ToobNam.txt \ No newline at end of file diff --git a/profiler_tools/nam_profile b/profiler_tools/nam_profile new file mode 100755 index 0000000..9654887 --- /dev/null +++ b/profiler_tools/nam_profile @@ -0,0 +1,3 @@ +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +${SCRIPT_DIR}/../build/src/profilePlugin -w Nam_Profile -o /tmp/nam.perf &&\ +google-pprof --text ${SCRIPT_DIR}/../build/src/profilePlugin --add_lib /usr/lib/lv2/neural_amp_modeler.lv2/neural_amp_modeler.so /tmp/nam.perf >./nam.txt \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 416e77d..7e58950 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,13 @@ set(CXX_STANDARD 20) include(FindPkgConfig) + +if(DEFINED ENV{GITHUB_ACTIONS}) + message(STATUS "Building in GitHub Actions environment") + set(GITHUB_ACTIONS TRUE) +else() + set(GITHUB_ACTIONS FALSE) +endif() ################################################################# # ENABLE/DISABLE VST3 Support. # Disabled, pending approval of Steinberg VST3 License. @@ -310,6 +317,17 @@ target_link_libraries(pipedaltest PRIVATE PiPedalCommon) target_include_directories(pipedaltest PRIVATE ${PIPEDAL_INCLUDES} ) +if (NOT GITHUB_ACTIONS) # Google perftools are not available on Github action servers. + + + add_executable(profilePlugin + profilePluginMain.cpp + ) + target_link_libraries(profilePlugin PRIVATE profiler ${PIPEDAL_LIBS}) + + target_include_directories(profilePlugin PRIVATE ${PIPEDAL_INCLUDES} + ) +endif() add_executable(jsonTest testMain.cpp jsonTest.cpp diff --git a/src/CommandLineParser.hpp b/src/CommandLineParser.hpp index 6ce4276..d1a08ed 100644 --- a/src/CommandLineParser.hpp +++ b/src/CommandLineParser.hpp @@ -162,7 +162,7 @@ namespace pipedal { if (shortOption.length() != 0) { - auto option = "-" + longOption; + auto option = "-" + shortOption; options.push_back(new BooleanOption(option, pResult, true)); options.push_back(new BooleanOption(option + "+", pResult, true)); options.push_back(new BooleanOption(option + "-", pResult, false)); diff --git a/src/ConfigMain.cpp b/src/ConfigMain.cpp index 6789ed3..d330149 100644 --- a/src/ConfigMain.cpp +++ b/src/ConfigMain.cpp @@ -40,6 +40,8 @@ #include "WifiChannelSelectors.hpp" #include "PiPedalConfiguration.hpp" #include +#include "ofstream_synced.hpp" + #if JACK_HOST #define INSTALL_JACK_SERVICE 1 @@ -1346,10 +1348,12 @@ int main(int argc, char **argv) portOption = "0.0.0.0:" + portOption; } Install(prefix, portOption); + FileSystemSync(); } else if (uninstall) { Uninstall(); + FileSystemSync(); } else if (stop) { diff --git a/src/FileBrowserFilesFeature.cpp b/src/FileBrowserFilesFeature.cpp index 9894c5a..3207688 100644 --- a/src/FileBrowserFilesFeature.cpp +++ b/src/FileBrowserFilesFeature.cpp @@ -29,6 +29,7 @@ #include #include #include "util.hpp" +#include "ofstream_synced.hpp" using namespace pipedal; using namespace pipedal::implementation; @@ -101,7 +102,7 @@ FileBrowserFilesFeature::FileBrowserFilesFeature() } void BrowserFilesVersionInfo::Save(std::filesystem::path &path) { - std::ofstream f{path}; + pipedal::ofstream_synced f{path}; if (!f.is_open()) return; f << version << std::endl; diff --git a/src/PiPedalModel.cpp b/src/PiPedalModel.cpp index 92eadcc..da2147d 100644 --- a/src/PiPedalModel.cpp +++ b/src/PiPedalModel.cpp @@ -492,7 +492,7 @@ void PiPedalModel::FirePedalboardChanged(int64_t clientId, bool loadAudioThread) if (loadAudioThread) { // notify the audio thread. - if (audioHost->IsOpen()) + if (audioHost && audioHost->IsOpen()) { LoadCurrentPedalboard(); @@ -2070,6 +2070,21 @@ void PiPedalModel::CheckForResourceInitialization(Pedalboard &pedalboard) } } } +Pedalboard &PiPedalModel::GetPedalboard() +{ + return this->pedalboard; +} +std::shared_ptr PiPedalModel::GetLv2Pedalboard() +{ + // test only. + Lv2PedalboardErrorList errorMessages; + std::shared_ptr lv2Pedalboard{this->pluginHost.CreateLv2Pedalboard(this->pedalboard, errorMessages)}; + if (errorMessages.size() != 0) + { + throw std::runtime_error(errorMessages[0].message); + } + return lv2Pedalboard; +} bool PiPedalModel::LoadCurrentPedalboard() { Lv2PedalboardErrorList errorMessages; @@ -2174,8 +2189,8 @@ UpdateStatus PiPedalModel::GetUpdateStatus() void PiPedalModel::UpdateNow(const std::string &updateUrl) { std::lock_guard lock(mutex); - std::filesystem::path fileName,signatureName; - updater.DownloadUpdate(updateUrl,&fileName,&signatureName); + std::filesystem::path fileName, signatureName; + updater.DownloadUpdate(updateUrl, &fileName, &signatureName); adminClient.InstallUpdate(fileName); } diff --git a/src/PiPedalModel.hpp b/src/PiPedalModel.hpp index 06c97b1..8fc4104 100644 --- a/src/PiPedalModel.hpp +++ b/src/PiPedalModel.hpp @@ -251,6 +251,9 @@ namespace pipedal void SetPedalboard(int64_t clientId, Pedalboard &pedalboard); + Pedalboard&GetPedalboard(); + std::shared_ptr GetLv2Pedalboard(); + void UpdateCurrentPedalboard(int64_t clientId, Pedalboard &pedalboard); void GetPresets(PresetIndex *pResult); diff --git a/src/PluginHost.hpp b/src/PluginHost.hpp index 9ffa5ef..0e41275 100644 --- a/src/PluginHost.hpp +++ b/src/PluginHost.hpp @@ -740,9 +740,9 @@ namespace pipedal size_t maxBufferSize = 1024; size_t maxAtomBufferSize = 16 * 1024; bool hasMidiInputChannel; - int numberOfAudioInputChannels = 2; - int numberOfAudioOutputChannels = 2; - double sampleRate = 0; + int numberOfAudioInputChannels = 1; + int numberOfAudioOutputChannels = 1; + double sampleRate = 48000; std::string vst3CachePath; diff --git a/src/SetWifiConfig.cpp b/src/SetWifiConfig.cpp index 13b0f0a..04f1d62 100644 --- a/src/SetWifiConfig.cpp +++ b/src/SetWifiConfig.cpp @@ -26,6 +26,7 @@ #include "SystemConfigFile.hpp" #include "SysExec.hpp" #include "WriteTemplateFile.hpp" +#include using namespace pipedal; using namespace std; @@ -473,6 +474,7 @@ void pipedal::SetWifiConfig(const WifiConfigSettings &settings) sysExec(SYSTEMCTL_BIN " restart dnsmasq"); sysExec(SYSTEMCTL_BIN " enable dnsmasq"); } + ::sync(); } /********************************************************************************* @@ -563,6 +565,7 @@ void UninstallP2p() SetWifiDirectConfig(wifiDirectConfigSettings); } + ::sync(); } static void RemoveDhcpcdConfig() @@ -679,6 +682,7 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings) { cout << e.what() << endl; } + ::sync(); } void pipedal::OnWifiReinstall() { WifiDirectConfigSettings settings; @@ -686,6 +690,7 @@ void pipedal::OnWifiReinstall() { if (settings.enable_) { SetWifiDirectConfig(settings); + ::sync(); } } void pipedal::OnWifiUninstall(bool preserveState) @@ -707,6 +712,7 @@ void pipedal::OnWifiUninstall(bool preserveState) settings.Save(); } } + ::sync(); } void pipedal::OnWifiInstallComplete() { diff --git a/src/Storage.cpp b/src/Storage.cpp index d593afb..9f47749 100644 --- a/src/Storage.cpp +++ b/src/Storage.cpp @@ -31,6 +31,7 @@ #include "PiPedalUI.hpp" #include "PluginHost.hpp" #include "ss.hpp" +#include "ofstream_synced.hpp" using namespace pipedal; @@ -344,7 +345,7 @@ void Storage::SavePluginPresetIndex() if (pluginPresetIndexChanged) { pluginPresetIndexChanged = false; - std::ofstream os; + pipedal::ofstream_synced os; auto path = GetPluginPresetsDirectory() / "index.json"; os.open(path, std::ios_base::trunc); if (os.fail()) @@ -359,7 +360,7 @@ void Storage::SavePluginPresetIndex() void Storage::SaveBankIndex() { - std::ofstream os; + pipedal::ofstream_synced os; os.open(GetIndexFileName(), std::ios_base::trunc); json_writer writer(os, false); writer.write(this->bankIndex); @@ -434,7 +435,7 @@ void Storage::SaveBankFile(const std::string &name, const BankFile &bankFile) } try { - std::ofstream s; + pipedal::ofstream_synced s; s.open(fileName, std::ios_base::trunc); json_writer writer(s, false); writer.write(bankFile); @@ -721,7 +722,7 @@ void Storage::SaveChannelSelection() auto fileName = this->GetChannelSelectionFileName(); try { - std::ofstream s(fileName); + pipedal::ofstream_synced s(fileName); json_writer writer(s, false); writer.write(this->jackChannelSelection); } @@ -906,7 +907,7 @@ int64_t Storage::UploadBank(BankFile &bankFile, int64_t uploadAfter) bankFile.name(s.str()); } std::filesystem::path path = this->GetBankFileName(bankFile.name()); - std::ofstream f(path); + pipedal::ofstream_synced f(path); if (!f.is_open()) { throw PiPedalException("Can't write to bank file."); @@ -928,7 +929,7 @@ void Storage::SaveUserSettings() { std::filesystem::path path = this->dataRoot / USER_SETTINGS_FILENAME; { - std::ofstream f(path); + pipedal::ofstream_synced f(path); if (!f.is_open()) { throw PiPedalException("Unable to write to " + ((std::string)path)); @@ -964,7 +965,7 @@ void Storage::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings std::filesystem::path path = this->dataRoot / WIFI_CONFIG_SETTINGS_FILENAME; { - std::ofstream f(path); + pipedal::ofstream_synced f(path); if (!f.is_open()) { throw PiPedalException("Unable to write to " + ((std::string)path)); @@ -1077,7 +1078,7 @@ void Storage::SaveCurrentPreset(const CurrentPreset ¤tPreset) { std::filesystem::path path = GetCurrentPresetPath(); - std::ofstream f(path); + pipedal::ofstream_synced f(path); json_writer writer(f, false); writer.write(currentPreset); } @@ -1152,7 +1153,7 @@ void Storage::SavePluginPresets(const std::string &pluginUri, const PluginPreset } auto tempPath = path.string() + ".$$$"; { - std::ofstream os; + pipedal::ofstream_synced os; os.open(tempPath, std::ios_base::trunc); if (os.fail()) { @@ -1396,7 +1397,7 @@ std::map Storage::GetFavorites() const void Storage::SetFavorites(const std::map &favorites) { std::filesystem::path fileName = this->dataRoot / "favorites.json"; - std::ofstream f; + pipedal::ofstream_synced f; f.open(fileName); if (f.is_open()) { @@ -1425,7 +1426,7 @@ pipedal::JackServerSettings Storage::GetJackServerSettings() void Storage::SetJackServerSettings(const pipedal::JackServerSettings &jackConfiguration) { std::filesystem::path fileName = this->dataRoot / "AudioConfig.json"; - std::ofstream f; + pipedal::ofstream_synced f; f.open(fileName); if (f.is_open()) { @@ -1440,7 +1441,7 @@ void Storage::SetJackServerSettings(const pipedal::JackServerSettings &jackConfi void Storage::SetSystemMidiBindings(const std::vector &bindings) { std::filesystem::path fileName = this->dataRoot / "SystemMidiBindings.json"; - std::ofstream f; + pipedal::ofstream_synced f; f.open(fileName); if (f.is_open()) { @@ -1714,7 +1715,7 @@ std::string Storage::UploadUserFile(const std::string &directory, const std::str try { std::filesystem::create_directories(path.parent_path()); - std::ofstream f(path, std::ios_base::trunc | std::ios_base::binary); + pipedal::ofstream_synced f(path, std::ios_base::trunc | std::ios_base::binary); if (!f.is_open()) { throw std::logic_error(SS("Can't create file " << path << ".")); diff --git a/src/UpdateResults.cpp b/src/UpdateResults.cpp index 1bc8b6a..e5ed66d 100644 --- a/src/UpdateResults.cpp +++ b/src/UpdateResults.cpp @@ -22,6 +22,7 @@ #include #include "Lv2Log.hpp" #include "ss.hpp" +#include "ofstream_synced.hpp" @@ -40,7 +41,7 @@ void UpdateResults::Load() } void UpdateResults::Save() { - std::ofstream f {UPDATE_RESULT_PATH}; + pipedal::ofstream_synced f {UPDATE_RESULT_PATH}; if (f.is_open()) { json_writer writer(f); diff --git a/src/Updater.cpp b/src/Updater.cpp index e5b35a0..8c612cc 100644 --- a/src/Updater.cpp +++ b/src/Updater.cpp @@ -34,6 +34,7 @@ #include #include "UpdaterSecurity.hpp" #include "SysExec.hpp" +#include "ofstream_synced.hpp" using namespace pipedal; namespace fs = std::filesystem; @@ -88,7 +89,7 @@ static void SetCachedUpdateStatus(UpdateStatus &updateStatus) updateStatus.LastUpdateTime(std::chrono::system_clock::now()); try { - std::ofstream f{UPDATE_STATUS_CACHE_FILE}; + pipedal::ofstream_synced f{UPDATE_STATUS_CACHE_FILE}; json_writer writer{f}; writer.write(updateStatus); } diff --git a/src/WebServer.cpp b/src/WebServer.cpp index 781d726..551d39d 100644 --- a/src/WebServer.cpp +++ b/src/WebServer.cpp @@ -36,6 +36,7 @@ #include #include "Ipv6Helpers.hpp" #include "util.hpp" +#include "ofstream_synced.hpp" #include "WebServer.hpp" diff --git a/src/WebServerConfig.cpp b/src/WebServerConfig.cpp index ed08964..f88a4e1 100644 --- a/src/WebServerConfig.cpp +++ b/src/WebServerConfig.cpp @@ -28,6 +28,7 @@ #include #include "ZipFile.hpp" #include "PiPedalUI.hpp" +#include "ofstream_synced.hpp" #define PRESET_EXTENSION ".piPreset" @@ -439,6 +440,7 @@ public: Lv2Log::error(SS("Unzip failed. " << e.what())); throw; } + FileSystemSync(); } else { outputFileName = this->model->UploadUserFile(directory,patchProperty,filename,req.get_body_input_stream(), req.content_length()); diff --git a/src/profilePluginMain.cpp b/src/profilePluginMain.cpp new file mode 100644 index 0000000..d34acdc --- /dev/null +++ b/src/profilePluginMain.cpp @@ -0,0 +1,255 @@ +#include "PiPedalModel.hpp" +#include "PiPedalConfiguration.hpp" +#include "Pedalboard.hpp" +#include "Lv2Log.hpp" +#include "RingBufferReader.hpp" +#include "CommandLineParser.hpp" +#include +#include + +// apt install google-perftools +#include + +using namespace pipedal; +using namespace std; +namespace fs = std::filesystem; + +// discards data from the ring buffer without discarding it. + +using WriterRingbuffer = RingBuffer; + +class RingBufferSink +{ +public: + RingBufferSink(WriterRingbuffer &writerRingBuffer) + : writerRingbuffer(writerRingBuffer) + { + thread = std::make_unique( + [this]() + { + ThreadProc(); + }); + } + void Close() + { + if (!closed) + { + closed = true; + writerRingbuffer.close(); + terminateThread = true; + thread->join(); + thread = nullptr; + } + } + ~RingBufferSink() + { + Close(); + } + +private: + void ThreadProc() + { + std::vector dataVector(1024); + uint8_t *data = dataVector.data(); + while (true) + { + RingBufferStatus status = writerRingbuffer.readWait_for(std::chrono::milliseconds(10)); + if (status == RingBufferStatus::Closed) + { + break; + } + if (status == RingBufferStatus::Ready) + { + size_t available = writerRingbuffer.readSpace(); + while (available != 0) + { + size_t thisTime = std::min(dataVector.size(), available); + writerRingbuffer.read(thisTime, data); + available -= thisTime; + } + } + } + } + bool closed = false; + std::atomic terminateThread{false}; + + std::unique_ptr thread; + WriterRingbuffer &writerRingbuffer; +}; +struct ProfileOptions +{ + std::string presetName; + float benchmark_seconds = 20; + std::string outputFilename = "/tmp/profilePlugin.perf"; + bool noProfile = false; + bool waitForWork = false; + size_t frameSize = 64; +}; + +void profilePlugin(const ProfileOptions &profileOptions) +{ + size_t nFrames = profileOptions.frameSize; + Lv2Log::log_level(LogLevel::Info); + + /*** Initialize the model */ + PiPedalModel model; + fs::path doc_root = "/etc/pipedal/config"; + PiPedalConfiguration configuration; + try + { + configuration.Load(doc_root, ""); + } + catch (const std::exception &e) + { + std::stringstream s; + s << "Unable to read configuration from '" << (doc_root / "config.json") << "'. (" << e.what() << ")"; + throw std::runtime_error(s.str()); + } + + model.Init(configuration); + + model.LoadLv2PluginInfo(); + + // model.Load(); don't start audio. + + /* *** Load the preset. */ + PresetIndex presetIndex; + model.GetPresets(&presetIndex); + + int64_t presetIndexId = 0; + bool presetIndexFound = false; + for (const auto &preset : presetIndex.presets()) + { + if (preset.name() == profileOptions.presetName) + { + presetIndexId = preset.instanceId(); + presetIndexFound = true; + break; + } + } + if (!presetIndexFound) + { + throw std::runtime_error("Preset not found."); + } + model.LoadPreset(-1, presetIndexId); + + auto pedalboard = model.GetCurrentPedalboardCopy(); + cout << "uri: " << pedalboard.items()[0].uri() << endl; + /* *** Get and prepare the audio thread pedalboard. */ + auto lv2Pedalboard = model.GetLv2Pedalboard(); + + lv2Pedalboard->Activate(); + + std::vector inputBufferVector; + std::vector outputBufferVector; + inputBufferVector.resize(nFrames); + outputBufferVector.resize(nFrames); + + float *inputBuffers[2]{inputBufferVector.data(), nullptr}; + float *outputBuffers[2] = {inputBufferVector.data(), nullptr}; + + WriterRingbuffer writerRingbuffer; + RealtimeRingBufferWriter ringBufferWriter(&writerRingbuffer); + + RingBufferSink ringBufferSink(writerRingbuffer); + + /* *** Pump the plugin for a bit if it is expected to do work on the scheduler thread when initializing */ + if (profileOptions.waitForWork) + { + using clock = std::chrono::steady_clock; + + // idle, pumping the plugin occasionally to allow inital scheduler work to complete. + auto waitStart = clock::now(); + auto waitDuration = std::chrono::duration_cast(std::chrono::seconds(3)); + + while (true) + { + lv2Pedalboard->Run(inputBuffers, outputBuffers, nFrames, &ringBufferWriter); + + auto elapsed = clock::now() - waitStart; + if (elapsed >= waitDuration) + { + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // the scheduler thread runs. + } + } + + /* *** Run the benchmark */ + if (!profileOptions.noProfile) + { + ProfilerStart(profileOptions.outputFilename.c_str()); + } + auto startTime = std::chrono::system_clock::now(); + + size_t repetitions = static_cast(48000 * profileOptions.benchmark_seconds / nFrames); + for (size_t i = 0; i < repetitions; ++i) + { + lv2Pedalboard->Run(inputBuffers, outputBuffers, nFrames, &ringBufferWriter); + } + + auto elapsedTime = std::chrono::high_resolution_clock::now() - startTime; + if (!profileOptions.noProfile) + { + ProfilerStop(); + } + std::chrono::milliseconds us = std::chrono::duration_cast(elapsedTime); + std::cout << "Ellapsed time: " << (us.count() / 1000.0) << "s" << endl; + + ringBufferSink.Close(); + + lv2Pedalboard->Deactivate(); +} + +int main(int argc, char **argv) +{ + try + { + ProfileOptions profileOptions; + bool help = false; + CommandLineParser commandLineParser; + commandLineParser.AddOption("w", "wait-for-work", &profileOptions.waitForWork); + commandLineParser.AddOption("", "no-profile", &profileOptions.noProfile); + commandLineParser.AddOption("o", "output", &profileOptions.outputFilename); + commandLineParser.AddOption("s", "seconds", &profileOptions.benchmark_seconds); + commandLineParser.AddOption("h", "help", &help); + + commandLineParser.Parse(argc, (const char **)argv); + + if (commandLineParser.Arguments().size() != 1 || help) + { + cout << "profilePlugin - Generate profiling data for LV2 Plugins" << endl; + cout << "Copyright (c) 2024 Robin E. R. Davies" << endl; + cout << endl; + cout << "Syntax: profilePlugin preset_name [options...]" << endl; + cout << " where preset_name is the name of a PiPedal preset." << endl; + cout << endl; + cout << " A google-perf profile capture will be written to " << endl; + cout << " /tmp/profilePlugin.perf" << endl; + cout << endl; + cout << "Options:" << endl; + cout << " --no-profile:" << endl; + cout << " do NOT generate a perf file." << endl; + cout << " -o, --output filename:" << endl; + cout << " The file to which profiler data is written. " << endl; + cout << " Defaults to /tmp/profilePlugin.perf" << endl; + cout << " -w, --wait-for-work: " << endl; + cout << " Assume that the plugin will load data on the LV2 scheduler thread." << endl; + cout << " -s, --seconds time_in_seconds: " << endl; + cout << " The number of seconds of audio to process." << endl; + cout << " -h, --help: display this message." << endl; + cout << endl; + return help ? EXIT_SUCCESS : EXIT_FAILURE; + } + + profileOptions.presetName = commandLineParser.Arguments()[0]; + + profilePlugin(profileOptions); + } + catch (const std::exception &e) + { + cerr << "Error: " << e.what() << endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} \ No newline at end of file