diff --git a/.vscode/settings.json b/.vscode/settings.json index 429b7a3..d58ac81 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -115,6 +115,7 @@ "Chowdhury", "Chowdhury's", "discursus", + "distro", "distros", "dpkg", "flac", @@ -134,6 +135,7 @@ "midiclip", "midisong", "mlmodel", + "modui", "MOTU", "Netplan", "Pedalboard", diff --git a/2 b/2 new file mode 100644 index 0000000..e69de29 diff --git a/CMakeLists.txt b/CMakeLists.txt index efea7f6..9864b62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,13 @@ cmake_minimum_required(VERSION 3.16.0) project(pipedal - VERSION 1.4.77 + VERSION 1.4.79 DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi" HOMEPAGE_URL "https://rerdavies.github.io/pipedal" ) EXECUTE_PROCESS( COMMAND dpkg --print-architecture COMMAND tr -d '\n' OUTPUT_VARIABLE DEBIAN_ARCHITECTURE ) -set (DISPLAY_VERSION "PiPedal v1.4.77-Beta") +set (DISPLAY_VERSION "PiPedal v1.4.79-Beta") set (PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE}) set (CMAKE_INSTALL_PREFIX "/usr/") @@ -102,7 +102,11 @@ set(CPACK_PACKAGING_INSTALL_PREFIX /usr) set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) -set (CPACK_STRIP_FILES true) +if (CMAKE_BUILD_TYPE MATCHES Debug) + set (CPACK_STRIP_FILES false) +else() + set (CPACK_STRIP_FILES true) +endif() set(CPACK_DEBIAN_PACKAGE_SIGN_ALGORITHM "detached") set(CPACK_DEBIAN_PACKAGE_SIGN_TYPE "origin") diff --git a/PiPedalCommon/src/AlsaSequencer.cpp b/PiPedalCommon/src/AlsaSequencer.cpp index e4c11d1..7506e02 100644 --- a/PiPedalCommon/src/AlsaSequencer.cpp +++ b/PiPedalCommon/src/AlsaSequencer.cpp @@ -319,6 +319,7 @@ namespace pipedal } if (seqHandle) { + Lv2Log::debug("Closing ALSA Sequencer"); snd_seq_close(seqHandle); seqHandle = nullptr; } @@ -608,7 +609,7 @@ namespace pipedal #ifndef NDEBUG #define MSG_DEBUG_LOG(x) \ case x: \ - Lv2Log::debug("ALSA Sequencer Message" #x); \ + Lv2Log::debug("ALSA Sequencer Message " #x); \ break; #else #define MSG_DEBUG_LOG(x) diff --git a/PiPedalCommon/src/include/json.hpp b/PiPedalCommon/src/include/json.hpp index aa193aa..def138f 100644 --- a/PiPedalCommon/src/include/json.hpp +++ b/PiPedalCommon/src/include/json.hpp @@ -607,7 +607,7 @@ namespace pipedal if (!obj) { write_raw("null"); } else { - write(obj.get()); + write(obj.value()); } } diff --git a/README.md b/README.md index 9545dbe..4645e27 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,11 @@ -Download: v1.4.77 +Download: v1.4.79 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.4.77 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. New Phaser and Graphic Equalizer plugins. +#### NEW version 1.4.79 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. New Phaser and Graphic Equalizer plugins.   @@ -56,8 +56,12 @@ https://github.com/user-attachments/assets/9a9fd0c6-78fc-4284-8b44-6a1929c00cc6 ### [Optimizing Audio Latency](https://rerdavies.github.io/pipedal/AudioLatency.html) ### [Command-Line Configuration of PiPedal](https://rerdavies.github.io/pipedal/CommandLine.html) ### [Changing the Web Server Port](https://rerdavies.github.io/pipedal/ChangingTheWebServerPort.html) + +%nbsp; + ### [Using LV2 Audio Plugins](https://rerdavies.github.io/pipedal/UsingLv2Plugins.md) ### [Which LV2 Plugins does PiPedal support?](https://rerdavies.github.io/pipedal/WhichLv2PluginsAreSupported.html) +### [Support for LV2 Plugins with MOD User Interfaces](https://rerdavies.github.io/pipedal/ModUiSupport.html)    diff --git a/debugDocs.sh b/debugDocs.sh new file mode 100755 index 0000000..2261a47 --- /dev/null +++ b/debugDocs.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# Run local jeckyl-hosted sevever for GitHub documentation page. +# See https://jekyllrb.com/docs/installation/ for instructions on how to isntall jeckyll locally. +# Then +# cd docs +# bundle install +# +cd docs +bundle exec jekyll serve --host \ No newline at end of file diff --git a/docs/Documentation.md b/docs/Documentation.md index 7cbb9fd..7703e50 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -16,8 +16,11 @@ #### [Optimizing Audio Latency](AudioLatency.md) #### [Command-Line Configuration of PiPedal](CommandLine.md) #### [Changing the Web Server Port](ChangingTheWebServerPort.md) + +  #### [Using LV2 Audio Plugins](UsingLv2Plugins.md) #### [Which LV2 Plugins does PiPedal support?](WhichLv2PluginsAreSupported.md) +#### [LV2 Plugins with MOD User Interfaces](ModUiSupport.md)   diff --git a/docs/Gemfile b/docs/Gemfile index 8c717b7..2fc6a89 100644 --- a/docs/Gemfile +++ b/docs/Gemfile @@ -31,3 +31,5 @@ gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin] # Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem # do not have a Java counterpart. gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby] + +gem "webrick", "~> 1.9" diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 4b10e71..7b3f5fa 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -205,14 +205,14 @@ GEM rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) mercenary (0.3.6) + mini_portile2 (2.8.9) minima (2.5.1) jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) minitest (5.19.0) - nokogiri (1.18.8-aarch64-linux-gnu) - racc (~> 1.4) - nokogiri (1.18.8-x86_64-linux-gnu) + nokogiri (1.18.8) + mini_portile2 (~> 2.8.2) racc (~> 1.4) octokit (4.25.1) faraday (>= 1, < 3) @@ -249,6 +249,7 @@ GEM unf_ext unf_ext (0.0.8.2) unicode-display_width (1.8.0) + webrick (1.9.1) PLATFORMS aarch64-linux @@ -262,6 +263,7 @@ DEPENDENCIES tzinfo (~> 1.2) tzinfo-data wdm (~> 0.1.1) + webrick (~> 1.9) BUNDLED WITH 2.4.19 diff --git a/docs/Installing.md b/docs/Installing.md index 24ee378..f8935b8 100644 --- a/docs/Installing.md +++ b/docs/Installing.md @@ -13,18 +13,18 @@ page_icon: img/Install4.jpg Download the most recent Debian (.deb) package for your platform: -- [Raspberry Pi OS bookworm (aarch64) v1.4.77](https://github.com/rerdavies/pipedal/releases/download/v1.4.77/pipedal_1.4.77_arm64.deb) -- [Ubuntu 24.x (aarch64) v1.4.77](https://github.com/rerdavies/pipedal/releases/download/v1.4.77/pipedal_1.4.77_arm64.deb) -- [Ubuntu 24.x (amd64) v1.4.77](https://github.com/rerdavies/pipedal/releases/download/v1.4.77/pipedal_1.4.77_amd64.deb) +- [Raspberry Pi OS bookworm (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.79](https://github.com/rerdavies/pipedal/releases/download/v1.4.79/pipedal_1.4.79_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) -Version 1.4.77 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.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: ``` sudo apt update sudo apt upgrade cd ~/Downloads - sudo apt-get install ./pipedal_1.4.77_arm64.deb + sudo apt-get install ./pipedal_1.4.79_arm64.deb ``` You MUST use `apt-get`. `apt` will not install downloaded packages; and `dpkg -i` will not install dependencies. diff --git a/docs/ModUiSupport.md b/docs/ModUiSupport.md new file mode 100644 index 0000000..993b4f9 --- /dev/null +++ b/docs/ModUiSupport.md @@ -0,0 +1,35 @@ +--- +page_icon: img/moduiThumb.jpg +icon_width: 120px +--- +## Support for LV2 Plugins with MOD User Interfaces + +{% include pageIconL.html %} + +Some LV2 plugins (currently a minority of plugins) provide custom plugin user interfaces based on [MOD Audio's](https://mod.audio/desktop/) +[ModUi framework](https://wiki.mod.audio/wiki/MOD_Web_GUI_Framework). When using such a plugin, you can choose whether to use the MOD Web GUI Framework user interface (the MOD UI) or the default PiPedal user interface. For the most part, the same functionality is available in both interfaces. + +Plugins that are distributed via Linux distributions usually do not implement MOD user interfaces; but all (or almost all) of the plugins that are available from the [PatchStorage website](https://patchstorage.com/platform/lv2-plugins/) do implement MOD user interfaces. Many plugins that are available from GitHub or other sources also implement MOD user interfaces. Often you can find updated versions of a plugin that is published by a distro on PatchStorage which does provide a MOD UI. + +Here's an example of a MOD UI for the ZAM Eq2 plugin: + +ZAM Eq2 MOD UI + + +And the PiPedal UI for the same plugin: + +ZAM Eq2 PiPedal UI + +Which UI you use is entirely up to you. MOD UIs tend to be a bit unfriendly for small-format devices like phones, but they do look great in a desktop browser. Sometimes the MOD UI will make better use of available screen space; sometimes the Pipedal UI will look better. It really depends on the plugin and your personal preferences. In this particular case, the MOD UI is more space efficient, but that is not always true. + +To select which user interface to use, tap on the UI selection button in the toolbar for the plugin. If the plugin does not provide a MOD UI, the button will be disabled. + +MOD UI selection button + +Support for MOD UIs in PiPedal is still experimental, so you may encounter some issues. PiPedal does not yet support all of the features of the MOD UI framework, not because it can't, but because we haven't been able to locate examples of plugins that use those features, which makes it difficult to test. If you encounter any issues, please report them on PiPedal's [GitHub Issues page](https://github.com/rerdavies/pipedal/issues), and we will do our best to provide quick fixes. + +  + + +-------- +[<< Which Plugins are Supported?](WhichLv2PluginsAreSupported.md) | [Up](Documentation.md) | [BuildingPiPedal from Source >>](BuildingPiPedalFromSource.md) diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 221bee3..c1c857a 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -1,4 +1,37 @@ # Release Notes +## Pipedal 1.4.79 Beta +The Big Feature: + +- Support for custom MOD User Interfaces for plugins that prove a MOD UI. See the [MOD UI documentation page](https://rerdavies.github.io/pipedal/ModUiSupport.html) for details on how to use MOD User Interfaces with PiPedal. This feature is experimental. If you experience problems with plugins that have MOD user interfaces, please report them on the [PiPedal GitHub Issues page](https://github.com/rerdavies/pipedal/issues). + +New in this relase: + +- PiPedal remembers which plugin you were viewing when you reload a preset. +- Keep Screen On setting in the PiPedal Remote Client (requires updated Android client). +- Lock Screen Orientation setting in the PiPedal Remote Client (requires updated Android client). + +If you are using the Android PiPedal remote app, you should make sure it has upgraded to the latest version. The new Remote app has been fully rolled out and is now available in the Google Play Store. It should update automatically, but you can check by opening the App Info for PiPedal Remote. If an "Update" button is displayed, click it to upgrade to the current version right away. If you have received the update already, you should notice differences in the App startup procedure (notably an animated splash screen). + +The PiPedal Remote Android app has been updated to provide the following new features: + +- new Keep Screen On settings. +- new Lock Screen Orientation setting. +- Edit controls are automatically repositioned above the Keyboard IME when it opens. +- The Zoomed Control feature (large controls in landscape mode on phones) has been removed. Improved scrolling provides a better solution to the problem of controls being obscured by the keyboard IME. +- Support for Android 16 Edge-to-Edge display. +- Reduced display flickering while connecting. +- Complete support for Night Mode (the Android UI now follows the PiPedal Theme setting). +- Shiny new graphics on the opening screen in support of Android 16's Edge-to-Edge display feature (but you can see it in other versions of Android as well). + + +Bug Fixes: + +- The "Listen for Message" buttons in the MIDI Bindings dialog now ignore CC0 (Bank Select), and CC32-63 (CC LSB) MIDI message, since CC0 is used to control preset selection. +- Fixed errors loading plugins with MIDI Event ports. +- Changing volume control on low-resolution displays breaks web page. +- MIDI binding for bypass controls have been restored after having been accidentally disabled in v1.4.77. + + ## PiPedal 1.4.77 Beta New TooB Player plugin; improvements to MIDI input handling; and a slew of fit-and finish diff --git a/docs/WhichLv2PluginsAreSupported.md b/docs/WhichLv2PluginsAreSupported.md index 6e3cf9a..4a5adad 100644 --- a/docs/WhichLv2PluginsAreSupported.md +++ b/docs/WhichLv2PluginsAreSupported.md @@ -14,4 +14,4 @@ If you install a new LV2 plugin, PiPedal will detect the change and make it avai PiPedal does not currently allow plugins to provide custom user interfaces; it displays controls that are declared by the LV2 plugin without customization. In practice, this means that LV2 plugins with simple sets of controls work well, but those with complex sets of controls can be difficult to use. However, we would be pleased to collaborate with developers who need more than the basic set of controls. This is a feature set that Pipedal needs to address, so we would be happy to collaborate with other developers on this. Please contact rerdavies at gmail.com for further details. -------- -[<< Using LV2 Audio Plugins](UsingLv2Plugins.md) | [Up](Documentation.md) | [BuildingPiPedal from Source >>](BuildingPiPedalFromSource.md) +[<< Using LV2 Audio Plugins](UsingLv2Plugins.md) | [Up](Documentation.md) | [MOD UI Support >>](ModUiSupport.md) diff --git a/docs/_includes/pageIconL.html b/docs/_includes/pageIconL.html index a15ccce..5a3a026 100644 --- a/docs/_includes/pageIconL.html +++ b/docs/_includes/pageIconL.html @@ -1,7 +1,4 @@
-
-

Generated by DALL-E 2

-
diff --git a/docs/download.md b/docs/download.md index 6e4fb8e..8e8b412 100644 --- a/docs/download.md +++ b/docs/download.md @@ -4,9 +4,9 @@ Download the most recent Debian (.deb) package for your platform: -- [Raspberry Pi OS bookworm (aarch64) v1.4.77 Beta](https://github.com/rerdavies/pipedal/releases/download/v1.4.77/pipedal_1.4.77_arm64.deb) -- [Ubuntu 24.x (aarch64) v1.4.77 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.4.77/pipedal_1.4.77_arm64.deb) -- [Ubuntu 24.x (amd64) v1.4.77 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.4.77/pipedal_1.4.77_amd64.deb) +- [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) +- [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 (amd64) v1.4.79 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.4.79/pipedal_1.4.79_amd64.deb) Install the package by running @@ -14,7 +14,7 @@ Install the package by running ``` sudo apt update cd ~/Downloads - sudo apt-get install ./pipedal_1.4.77_arm64.deb + sudo apt-get install ./pipedal_1.4.79_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 expected, and can be safely ignored. diff --git a/docs/favicon.ico b/docs/favicon.ico new file mode 100644 index 0000000..cc7e70b Binary files /dev/null and b/docs/favicon.ico differ diff --git a/docs/favicon.svg b/docs/favicon.svg new file mode 100644 index 0000000..4ab2da1 --- /dev/null +++ b/docs/favicon.svg @@ -0,0 +1,264 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/img/moduiThumb.jpg b/docs/img/moduiThumb.jpg new file mode 100644 index 0000000..a6ad8c4 Binary files /dev/null and b/docs/img/moduiThumb.jpg differ diff --git a/docs/img/selectUi.png b/docs/img/selectUi.png new file mode 100644 index 0000000..07d0592 Binary files /dev/null and b/docs/img/selectUi.png differ diff --git a/docs/img/zam-modui.jpg b/docs/img/zam-modui.jpg new file mode 100644 index 0000000..334d0fa Binary files /dev/null and b/docs/img/zam-modui.jpg differ diff --git a/docs/img/zam-pp-ui.png b/docs/img/zam-pp-ui.png new file mode 100644 index 0000000..7d7fe3f Binary files /dev/null and b/docs/img/zam-pp-ui.png differ diff --git a/lv2/aarch64/ToobAmp.lv2/CabIR.ttl b/lv2/aarch64/ToobAmp.lv2/CabIR.ttl index a5cdcb5..fa85f89 100644 --- a/lv2/aarch64/ToobAmp.lv2/CabIR.ttl +++ b/lv2/aarch64/ToobAmp.lv2/CabIR.ttl @@ -93,7 +93,7 @@ cabir:impulseFile3 doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; 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 70ba918..8a17811 100644 --- a/lv2/aarch64/ToobAmp.lv2/CabSim.ttl +++ b/lv2/aarch64/ToobAmp.lv2/CabSim.ttl @@ -50,7 +50,7 @@ toob:frequencyResponseVector doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; mod:brand "TooB"; mod:label "TooB CabSim"; diff --git a/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl b/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl index 3165808..ee9eb6c 100644 --- a/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl @@ -53,7 +53,7 @@ toobimpulse:impulseFile doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; 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 a9ff502..2328645 100644 --- a/lv2/aarch64/ToobAmp.lv2/ConvolutionReverbStereo.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ConvolutionReverbStereo.ttl @@ -51,7 +51,7 @@ toobimpulse:impulseFile doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; rdfs:comment """ diff --git a/lv2/aarch64/ToobAmp.lv2/InputStage.ttl b/lv2/aarch64/ToobAmp.lv2/InputStage.ttl index 6ec1c19..95cac11 100644 --- a/lv2/aarch64/ToobAmp.lv2/InputStage.ttl +++ b/lv2/aarch64/ToobAmp.lv2/InputStage.ttl @@ -66,7 +66,7 @@ inputStage:filterGroup doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; mod:brand "TooB"; mod:label "TooB Input"; diff --git a/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl b/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl index eeede7d..f7cb004 100644 --- a/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl +++ b/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl @@ -68,7 +68,7 @@ pstage:stage3 doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; mod:brand "TooB"; mod:label "Power Stage"; diff --git a/lv2/aarch64/ToobAmp.lv2/SpectrumAnalyzer.ttl b/lv2/aarch64/ToobAmp.lv2/SpectrumAnalyzer.ttl index 70bda8c..84011d8 100644 --- a/lv2/aarch64/ToobAmp.lv2/SpectrumAnalyzer.ttl +++ b/lv2/aarch64/ToobAmp.lv2/SpectrumAnalyzer.ttl @@ -57,7 +57,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; 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 f9d51b9..05526a2 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToneStack.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToneStack.ttl @@ -57,7 +57,7 @@ tonestack:eqGroup doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; uiext:ui ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobAmp.so b/lv2/aarch64/ToobAmp.lv2/ToobAmp.so index b6357f6..e0d9698 100644 Binary files a/lv2/aarch64/ToobAmp.lv2/ToobAmp.so and b/lv2/aarch64/ToobAmp.lv2/ToobAmp.so differ diff --git a/lv2/aarch64/ToobAmp.lv2/ToobAmpUI.so b/lv2/aarch64/ToobAmp.lv2/ToobAmpUI.so index 92c76eb..a78d9a8 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 44e6b0b..6b0d594 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 63 ; + lv2:microVersion 64 ; 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 97573a7..d8e95a7 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 63 ; + lv2:microVersion 64 ; 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 7b2b30d..a4601a6 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 63 ; + lv2:microVersion 64 ; 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 5a85fdd..dcef7d7 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 63 ; + lv2:microVersion 64 ; 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 94a7c65..c19a1da 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 63 ; + lv2:microVersion 64 ; 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/ToobGraphicEq.ttl b/lv2/aarch64/ToobAmp.lv2/ToobGraphicEq.ttl index fcf298c..7581cc8 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobGraphicEq.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobGraphicEq.ttl @@ -41,7 +41,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; rdfs:comment """ A 7 octave graphic equalizer, with frequencies chosen to be useful for EQ-ing guitar signals. """ ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobLooperFour.ttl b/lv2/aarch64/ToobAmp.lv2/ToobLooperFour.ttl index cb2c508..4cc44e8 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobLooperFour.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobLooperFour.ttl @@ -92,7 +92,7 @@ myprefix:output_group doap:license ; doap:maintainer ; lv2:minorVersion 1 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; ui:ui ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobLooperOne.ttl b/lv2/aarch64/ToobAmp.lv2/ToobLooperOne.ttl index 9efd0ca..e37f594 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobLooperOne.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobLooperOne.ttl @@ -78,7 +78,7 @@ myprefix:output_group doap:license ; doap:maintainer ; lv2:minorVersion 1 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; ui:ui ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobML.ttl b/lv2/aarch64/ToobAmp.lv2/ToobML.ttl index 5e6e3ee..de2a07a 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobML.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobML.ttl @@ -67,7 +67,7 @@ toobml:sagGroup doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; 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/ToobMix.ttl b/lv2/aarch64/ToobAmp.lv2/ToobMix.ttl index 2e397d4..aa0ae6a 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobMix.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobMix.ttl @@ -40,7 +40,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; rdfs:comment """ Remix a stereo input signal. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobNeuralAmpModeler.ttl b/lv2/aarch64/ToobAmp.lv2/ToobNeuralAmpModeler.ttl index 5355066..67ede49 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobNeuralAmpModeler.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobNeuralAmpModeler.ttl @@ -61,7 +61,7 @@ toobNam:eqGroup doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; rdfs:comment """ A port of Steven Atkinson's Neural Amp Modeler to LV2. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobNoiseGate.ttl b/lv2/aarch64/ToobAmp.lv2/ToobNoiseGate.ttl index 932ca56..5672117 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobNoiseGate.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobNoiseGate.ttl @@ -54,7 +54,7 @@ noisegate:envelope_group doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; rdfs:comment """ A noise gate is an audio processing tool that controls the volume of an audio signal by allowing it to pass through only when it exceeds a set threshold. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobPhaser.ttl b/lv2/aarch64/ToobAmp.lv2/ToobPhaser.ttl index 6963a38..3676f05 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobPhaser.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobPhaser.ttl @@ -40,7 +40,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; rdfs:comment """ A loose emulation of an MXR® Phase 90 Phaser. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobPlayer.ttl b/lv2/aarch64/ToobAmp.lv2/ToobPlayer.ttl index 000489f..c11a17b 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobPlayer.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobPlayer.ttl @@ -60,7 +60,7 @@ toobPlayer:seek doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; rdfs:comment """ Play audio files. Audio files can optionally be loooped by pressing the "Set loop" button. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobRecordMono.ttl b/lv2/aarch64/ToobAmp.lv2/ToobRecordMono.ttl index b127513..841fb12 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobRecordMono.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobRecordMono.ttl @@ -51,7 +51,7 @@ recordPrefix:audioFile doap:license ; doap:maintainer ; lv2:minorVersion 1 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; ui:ui ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobRecordStereo.ttl b/lv2/aarch64/ToobAmp.lv2/ToobRecordStereo.ttl index b466568..9669716 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobRecordStereo.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobRecordStereo.ttl @@ -88,7 +88,7 @@ myprefix:loop3_group doap:license ; doap:maintainer ; lv2:minorVersion 1 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; ui:ui ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobTuner.ttl b/lv2/aarch64/ToobAmp.lv2/ToobTuner.ttl index 90b5392..bcaf601 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 63 ; + lv2:microVersion 64 ; rdfs:comment """ TooB Tuner is a chromatic guitar tuner. """ ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobVolume.ttl b/lv2/aarch64/ToobAmp.lv2/ToobVolume.ttl index d78713b..be47dd1 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobVolume.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobVolume.ttl @@ -39,7 +39,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 63 ; + lv2:microVersion 64 ; rdfs:comment """ Volume control. diff --git a/src/AlsaDriver.cpp b/src/AlsaDriver.cpp index 5df7144..783ab4f 100644 --- a/src/AlsaDriver.cpp +++ b/src/AlsaDriver.cpp @@ -1484,7 +1484,7 @@ namespace pipedal #endif } - std::jthread *audioThread = nullptr; + std::unique_ptr audioThread; bool audioRunning; bool block = false; @@ -1518,32 +1518,36 @@ namespace pipedal } while (frames > 0); return framesRead; } + protected: void ReadMidiData(uint32_t audioFrame) { AlsaMidiMessage message; midiEventCount = 0; - while(alsaSequencer->ReadMessage(message,0)) + while (alsaSequencer->ReadMessage(message, 0)) { size_t messageSize = message.size; - if (messageSize == 0) + if (messageSize == 0) { continue; } - if (midiEventMemoryIndex + messageSize >= this->midiEventMemory.size()) { + if (midiEventMemoryIndex + messageSize >= this->midiEventMemory.size()) + { continue; } - if (midiEventCount >= this->midiEvents.size()) { - midiEvents.resize(midiEventCount*2); + if (midiEventCount >= this->midiEvents.size()) + { + midiEvents.resize(midiEventCount * 2); } - // for now, prevent META event messages from propagating. - if (message.data[0] == 0xFF && message.size > 1) { + // for now, prevent META event messages from propagating. + if (message.data[0] == 0xFF && message.size > 1) + { continue; } MidiEvent *pEvent = midiEvents.data() + midiEventCount++; pEvent->time = audioFrame; - pEvent->size = messageSize; + pEvent->size = messageSize; pEvent->buffer = midiEventMemory.data() + midiEventMemoryIndex; memcpy( @@ -1551,11 +1555,10 @@ namespace pipedal message.data, message.size); midiEventMemoryIndex += messageSize; - } } - private: + private: long WriteBuffer(snd_pcm_t *handle, uint8_t *buf, size_t frames) { long framesRead; @@ -1759,7 +1762,7 @@ namespace pipedal } } - audioThread = new std::jthread([this]() + audioThread = std::make_unique([this]() { AudioThread(); }); } @@ -1773,8 +1776,7 @@ namespace pipedal terminateAudio(true); if (audioThread) { - this->audioThread->join(); - this->audioThread = 0; + this->audioThread = 0; // jthread joins. } Lv2Log::debug("Audio thread joined."); } @@ -1789,9 +1791,6 @@ namespace pipedal AlsaSequencer::ptr alsaSequencer; public: - - - virtual void SetAlsaSequencer(AlsaSequencer::ptr alsaSequencer) override { this->alsaSequencer = alsaSequencer; @@ -1822,7 +1821,7 @@ namespace pipedal { for (size_t i = 0; i < buffer.size(); ++i) { - // delete[] buffer[i]; + delete[] buffer[i]; buffer[i] = 0; } buffer.clear(); @@ -1887,6 +1886,51 @@ namespace pipedal snd_pcm_t *captureHandle = nullptr; snd_pcm_hw_params_t *playbackHwParams = nullptr; snd_pcm_hw_params_t *captureHwParams = nullptr; + + Finally ff_playbackHandle{ + [&playbackHandle]() + { + if (playbackHandle) + { + int rc = snd_pcm_close(playbackHandle); + if (rc < 0) + { + throw std::runtime_error("snd_pcm_close failed."); + } + playbackHandle = nullptr; + } + }}; + Finally ff_captureHandle{ + [&captureHandle]() + { + if (captureHandle) + { + int rc = snd_pcm_close(captureHandle); + if (rc < 0) + { + throw std::runtime_error("snd_pcm_close failed."); + } + + captureHandle = nullptr; + } + }}; + Finally ff_playbackHwParams{ + [&playbackHwParams]() + { + if (playbackHwParams) + { + snd_pcm_hw_params_free(playbackHwParams); + } + }}; + Finally ff_captureHwParams{ + [&captureHwParams]() + { + if (captureHwParams) + { + snd_pcm_hw_params_free(captureHwParams); + } + }}; + std::string alsaDeviceName = jackServerSettings.GetAlsaInputDevice(); bool result = false; @@ -2001,28 +2045,6 @@ namespace pipedal result = false; throw; } - if (playbackHwParams) - { - snd_pcm_hw_params_free(playbackHwParams); - playbackHwParams = nullptr; - } - - if (captureHwParams) - { - snd_pcm_hw_params_free(captureHwParams); - captureHwParams = nullptr; - } - - if (playbackHandle) - { - snd_pcm_close(playbackHandle); - playbackHandle = nullptr; - } - if (captureHandle) - { - snd_pcm_close(captureHandle); - captureHandle = nullptr; - } return result; } @@ -2176,4 +2198,9 @@ namespace pipedal } #endif } + + void FreeAlsaGlobals() + { + snd_config_update_free_global(); // to get a clean Valgrind report. + } } // namespace diff --git a/src/AlsaDriver.hpp b/src/AlsaDriver.hpp index d9a6aa2..c579813 100644 --- a/src/AlsaDriver.hpp +++ b/src/AlsaDriver.hpp @@ -40,5 +40,7 @@ namespace pipedal { // test only. void AlsaFormatEncodeDecodeTest(AudioDriverHost*driverHost); void MidiDecoderTest(); + + void FreeAlsaGlobals(); // for valgrind. Free the Alsa configuration cache. } diff --git a/src/AudioHost.cpp b/src/AudioHost.cpp index ebc387c..3cdb7cc 100644 --- a/src/AudioHost.cpp +++ b/src/AudioHost.cpp @@ -1511,8 +1511,10 @@ public: if (property != nullptr && value != nullptr && property->type == uris.atom_URID) { LV2_URID propertyUrid = ((LV2_Atom_URID *)property)->body; - this->pNotifyCallbacks->OnPatchSetReply(instanceId, propertyUrid, value); - // this->pNotifyCallbacks->OnNotifyMaybeLv2StateChanged(instanceId); + if (this->pNotifyCallbacks) + { + this->pNotifyCallbacks->OnPatchSetReply(instanceId, propertyUrid, value); + } } } } diff --git a/src/Pedalboard.cpp b/src/Pedalboard.cpp index 6093736..a6f1119 100644 --- a/src/Pedalboard.cpp +++ b/src/Pedalboard.cpp @@ -520,6 +520,7 @@ JSON_MAP_BEGIN(PedalboardItem) JSON_MAP_REFERENCE_CONDITIONAL(PedalboardItem,topChain,IsPedalboardSplitItem) JSON_MAP_REFERENCE_CONDITIONAL(PedalboardItem,bottomChain,&IsPedalboardSplitItem) JSON_MAP_REFERENCE(PedalboardItem,midiBindings) + JSON_MAP_REFERENCE(PedalboardItem,midiChannelBinding) JSON_MAP_REFERENCE(PedalboardItem,stateUpdateCount) JSON_MAP_REFERENCE(PedalboardItem,lv2State) JSON_MAP_REFERENCE(PedalboardItem,lilvPresetUri) diff --git a/src/Pedalboard.hpp b/src/Pedalboard.hpp index cb65855..f5d0ab8 100644 --- a/src/Pedalboard.hpp +++ b/src/Pedalboard.hpp @@ -81,7 +81,7 @@ public: }; -class PedalboardItem: public JsonMemberWritable { +class PedalboardItem { public: using PropertyMap = std::map; int64_t instanceId_ = 0; @@ -148,17 +148,17 @@ public: void AddResetsForMissingProperties(Snapshot&snapshot, size_t*index) const; - virtual void write_members(json_writer&writer) const { - writer.write_member("instanceId",instanceId_); - writer.write_member("uri",uri_); - writer.write_member("pluginName",pluginName_); - writer.write_member("isEnabled",isEnabled_); - if (isSplit()) - { - writer.write_member("topChain",topChain_); - writer.write_member("bottomChain",bottomChain_); - } - } + // virtual void write_members(json_writer&writer) const { + // writer.write_member("instanceId",instanceId_); + // writer.write_member("uri",uri_); + // writer.write_member("pluginName",pluginName_); + // writer.write_member("isEnabled",isEnabled_); + // if (isSplit()) + // { + // writer.write_member("topChain",topChain_); + // writer.write_member("bottomChain",bottomChain_); + // } + // } DECLARE_JSON_MAP(PedalboardItem); }; diff --git a/src/PiPedalModel.cpp b/src/PiPedalModel.cpp index 5049cb9..d79b286 100644 --- a/src/PiPedalModel.cpp +++ b/src/PiPedalModel.cpp @@ -2239,7 +2239,10 @@ void PiPedalModel::OnPatchSetReply(uint64_t instanceId, LV2_URID patchSetPropert } } } - audioHost->SetListenForAtomOutput(atomOutputListeners.size() != 0); + if (audioHost) + { + audioHost->SetListenForAtomOutput(atomOutputListeners.size() != 0); + } } void PiPedalModel::OnNotifyPathPatchPropertyReceived( diff --git a/src/PluginHost.cpp b/src/PluginHost.cpp index 4b09185..5a796cb 100644 --- a/src/PluginHost.cpp +++ b/src/PluginHost.cpp @@ -828,7 +828,10 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP std::string bundleUri = bundleUriNode.AsUri(); - std::string bundlePath = lilv_file_uri_parse(bundleUri.c_str(), nullptr); + char *lilvBundlePath = lilv_file_uri_parse(bundleUri.c_str(), nullptr); + std::string bundlePath = lilvBundlePath; + lilv_free(lilvBundlePath); + if (bundlePath.length() == 0) throw std::logic_error("Bundle uri is not a file uri."); @@ -1655,9 +1658,9 @@ Lv2PortGroup::Lv2PortGroup(PluginHost *lv2Host, const std::string &groupUri) this->uri_ = groupUri; AutoLilvNode uri = lilv_new_uri(pWorld, groupUri.c_str()); - LilvNode *symbolNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris->lv2core__symbol, nullptr); + AutoLilvNode symbolNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris->lv2core__symbol, nullptr); symbol_ = nodeAsString(symbolNode); - LilvNode *nameNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris->lv2core__name, nullptr); + AutoLilvNode nameNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris->lv2core__name, nullptr); name_ = nodeAsString(nameNode); } diff --git a/src/WebServer.cpp b/src/WebServer.cpp index e159704..4a86c9a 100644 --- a/src/WebServer.cpp +++ b/src/WebServer.cpp @@ -39,6 +39,7 @@ #include "ofstream_synced.hpp" #include "ModTemplateGenerator.hpp" + #include #include "WebServer.hpp" @@ -685,7 +686,7 @@ namespace pipedal int threads = 1; size_t maxUploadSize = 512 * 1024 * 1024; - std::thread *pBgThread = nullptr; + std::unique_ptr pBgThread; std::recursive_mutex io_mutex; boost::asio::io_context *pIoContext = nullptr; @@ -1417,7 +1418,7 @@ namespace pipedal throw std::runtime_error("Bad state."); } this->signalOnDone = signalOnDone; - this->pBgThread = new std::thread(ThreadProc, this); + this->pBgThread = std::make_unique(ThreadProc, this); } virtual void DisplayIpAddresses() override; diff --git a/src/main.cpp b/src/main.cpp index 3e38c95..99c823c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,6 +41,7 @@ #include "HtmlHelper.hpp" #include #include +#include "AlsaDriver.hpp" #include #include @@ -422,6 +423,8 @@ int main(int argc, char *argv[]) } Lv2Log::info("Shutdown complete."); + FreeAlsaGlobals(); + if (systemd) { sd_notify(0, "STOPPING=1"); diff --git a/todo.txt b/todo.txt index 5776346..e4a646c 100644 --- a/todo.txt +++ b/todo.txt @@ -1,19 +1,12 @@ Crash-proofing: do not reload pedalboard if the previous run crashed before normal shutdown. -Persist and reconnect IP addresses in client. -Zeroconv crash assert. /var/pipedal/audio_uploads/ReverbImpulseFiles/ - -Investigate if ((keyCode == KeyEvent.KEYCODE_BACK)) - MOD ir uses some scheme to limit the minimum buffer size, which is not implemented in Pipedal. - +wa Vu Meters, move rendering to background images, in order to reduce layout overhead. check reload after change of LV2 plugins. -Remove FullScreenIME in PluginControlView.tsx - Exactly one underrun per seek in Toob Player @@ -21,6 +14,7 @@ check filetime conversion in gcc 12.2! (missing c++ 20 time conversion functions AudioFiles.cpp fileTimeToInt64 +json "skip" code doesn't work with complex objects (MidiChannelBinding specifically). - pipewire aux in? @@ -39,3 +33,4 @@ pcm.pipedal_aux_in { } } + diff --git a/valgrind.sh b/valgrind.sh new file mode 100755 index 0000000..58d1723 --- /dev/null +++ b/valgrind.sh @@ -0,0 +1 @@ +valgrind --leak-check=full --log-file=vg.output ./build/src/pipedald /etc/pipedal/config ./vite/dist -port 0.0.0.0:8080 -log-level debug diff --git a/vg.output b/vg.output new file mode 100644 index 0000000..ae1253a --- /dev/null +++ b/vg.output @@ -0,0 +1,146 @@ +==5249== Memcheck, a memory error detector +==5249== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. +==5249== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info +==5249== Command: ./build/src/pipedald /etc/pipedal/config ./vite/dist -port 0.0.0.0:8080 -log-level debug +==5249== Parent PID: 5248 +==5249== +==5249== +==5249== HEAP SUMMARY: +==5249== in use at exit: 88,737 bytes in 474 blocks +==5249== total heap usage: 662,523 allocs, 662,049 frees, 52,720,309 bytes allocated +==5249== +==5249== 22 bytes in 1 blocks are definitely lost in loss record 11 of 102 +==5249== at 0x48850C8: malloc (vg_replace_malloc.c:381) +==5249== by 0x61DC32F: ??? +==5249== by 0x61BFFC3: ??? +==5249== by 0x61C01DB: ??? +==5249== by 0x61BF9D7: ??? +==5249== by 0x61C0267: ??? +==5249== by 0x61C0677: ??? +==5249== by 0x61C0C1B: ??? +==5249== by 0x61C0C47: ??? +==5249== by 0x5F143DB: ??? +==5249== by 0x5F14613: ??? +==5249== by 0x6046B87: ??? +==5249== +==5249== 56 bytes in 1 blocks are definitely lost in loss record 45 of 102 +==5249== at 0x48850C8: malloc (vg_replace_malloc.c:381) +==5249== by 0x6233FC7: ??? +==5249== by 0x6232503: ??? +==5249== by 0x6233197: ??? +==5249== by 0x6233343: ??? +==5249== by 0x6233AB7: ??? +==5249== by 0x5F14453: ??? +==5249== by 0x5F14613: ??? +==5249== by 0x6046B87: ??? +==5249== by 0x5EFD7B7: ??? +==5249== by 0x5EFE143: ??? +==5249== by 0x60467A7: ??? +==5249== +==5249== 88 bytes in 1 blocks are definitely lost in loss record 52 of 102 +==5249== at 0x48850C8: malloc (vg_replace_malloc.c:381) +==5249== by 0x625EF8B: ??? +==5249== by 0x61CD1E7: ??? +==5249== by 0x61CD3AF: ??? +==5249== by 0x61CD46B: ??? +==5249== by 0x5F1C0E7: ??? +==5249== by 0x5F0A637: ??? +==5249== by 0x5F143AB: ??? +==5249== by 0x5F14613: ??? +==5249== by 0x6046B87: ??? +==5249== by 0x5EFD7B7: ??? +==5249== by 0x5EFE143: ??? +==5249== +==5249== 3,352 (80 direct, 3,272 indirect) bytes in 1 blocks are definitely lost in loss record 89 of 102 +==5249== at 0x48850C8: malloc (vg_replace_malloc.c:381) +==5249== by 0x6235507: ??? +==5249== by 0x61BFFEB: ??? +==5249== by 0x61C01DB: ??? +==5249== by 0x61BF9D7: ??? +==5249== by 0x61C0267: ??? +==5249== by 0x61C0677: ??? +==5249== by 0x61C0C1B: ??? +==5249== by 0x61C0C47: ??? +==5249== by 0x5F143DB: ??? +==5249== by 0x5F14613: ??? +==5249== by 0x6046B87: ??? +==5249== +==5249== 3,709 (80 direct, 3,629 indirect) bytes in 1 blocks are definitely lost in loss record 91 of 102 +==5249== at 0x48850C8: malloc (vg_replace_malloc.c:381) +==5249== by 0x6235507: ??? +==5249== by 0x626268B: ??? +==5249== by 0x604751B: ??? +==5249== by 0x624C53B: ??? +==5249== by 0x6046AA7: ??? +==5249== by 0x6046C17: ??? +==5249== by 0x5EFD7B7: ??? +==5249== by 0x5EFE143: ??? +==5249== by 0x60467A7: ??? +==5249== by 0x1B727F: CollatorImpl::CollatorImpl(std::shared_ptr, char const*) (Locale.cpp:385) +==5249== by 0x1B7717: LocaleImpl::GetCollator() (Locale.cpp:444) +==5249== +==5249== 3,950 (56 direct, 3,894 indirect) bytes in 1 blocks are definitely lost in loss record 92 of 102 +==5249== at 0x48850C8: malloc (vg_replace_malloc.c:381) +==5249== by 0x625EF8B: ??? +==5249== by 0x624BE7B: ??? +==5249== by 0x60460BF: ??? +==5249== by 0x6046C0B: ??? +==5249== by 0x5EFD7B7: ??? +==5249== by 0x5EFE143: ??? +==5249== by 0x60467A7: ??? +==5249== by 0x1B727F: CollatorImpl::CollatorImpl(std::shared_ptr, char const*) (Locale.cpp:385) +==5249== by 0x1B7717: LocaleImpl::GetCollator() (Locale.cpp:444) +==5249== by 0x156B67: main (main.cpp:303) +==5249== +==5249== 4,264 bytes in 1 blocks are definitely lost in loss record 99 of 102 +==5249== at 0x48850C8: malloc (vg_replace_malloc.c:381) +==5249== by 0x625EFDF: ??? +==5249== by 0x61C063F: ??? +==5249== by 0x61C0C1B: ??? +==5249== by 0x61C0C47: ??? +==5249== by 0x5F143DB: ??? +==5249== by 0x5F14613: ??? +==5249== by 0x6046B87: ??? +==5249== by 0x5EFD7B7: ??? +==5249== by 0x5EFE143: ??? +==5249== by 0x60467A7: ??? +==5249== by 0x1B727F: CollatorImpl::CollatorImpl(std::shared_ptr, char const*) (Locale.cpp:385) +==5249== +==5249== 5,804 (256 direct, 5,548 indirect) bytes in 1 blocks are definitely lost in loss record 100 of 102 +==5249== at 0x48850C8: malloc (vg_replace_malloc.c:381) +==5249== by 0x625EF8B: ??? +==5249== by 0x5F143CF: ??? +==5249== by 0x5F14613: ??? +==5249== by 0x6046B87: ??? +==5249== by 0x5EFD7B7: ??? +==5249== by 0x5EFE143: ??? +==5249== by 0x60467A7: ??? +==5249== by 0x1B727F: CollatorImpl::CollatorImpl(std::shared_ptr, char const*) (Locale.cpp:385) +==5249== by 0x1B7717: LocaleImpl::GetCollator() (Locale.cpp:444) +==5249== by 0x156B67: main (main.cpp:303) +==5249== +==5249== 7,733 bytes in 276 blocks are definitely lost in loss record 101 of 102 +==5249== at 0x488A1C4: realloc (vg_replace_malloc.c:1437) +==5249== by 0x5912A2B: serd_chunk_sink (in /usr/lib/aarch64-linux-gnu/libserd-0.so.0.30.16) +==5249== by 0x5912A87: serd_chunk_sink_finish (in /usr/lib/aarch64-linux-gnu/libserd-0.so.0.30.16) +==5249== by 0x590DA67: serd_file_uri_parse (in /usr/lib/aarch64-linux-gnu/libserd-0.so.0.30.16) +==5249== by 0x470DE3: pipedal::Lv2PluginInfo::Lv2PluginInfo(pipedal::PluginHost*, LilvWorldImpl*, LilvPluginImpl const*) (PluginHost.cpp:831) +==5249== by 0x49D8F7: void std::_Construct(pipedal::Lv2PluginInfo*, pipedal::PluginHost*&&, LilvWorldImpl*&, LilvPluginImpl const*&) (stl_construct.h:119) +==5249== by 0x49A61F: void std::allocator_traits >::construct(std::allocator&, pipedal::Lv2PluginInfo*, pipedal::PluginHost*&&, LilvWorldImpl*&, LilvPluginImpl const*&) (alloc_traits.h:635) +==5249== by 0x496C5F: std::_Sp_counted_ptr_inplace, (__gnu_cxx::_Lock_policy)2>::_Sp_counted_ptr_inplace(std::allocator, pipedal::PluginHost*&&, LilvWorldImpl*&, LilvPluginImpl const*&) (shared_ptr_base.h:604) +==5249== by 0x48F12B: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count, pipedal::PluginHost*, LilvWorldImpl*&, LilvPluginImpl const*&>(pipedal::Lv2PluginInfo*&, std::_Sp_alloc_shared_tag >, pipedal::PluginHost*&&, LilvWorldImpl*&, LilvPluginImpl const*&) (shared_ptr_base.h:971) +==5249== by 0x488A3B: std::__shared_ptr::__shared_ptr, pipedal::PluginHost*, LilvWorldImpl*&, LilvPluginImpl const*&>(std::_Sp_alloc_shared_tag >, pipedal::PluginHost*&&, LilvWorldImpl*&, LilvPluginImpl const*&) (shared_ptr_base.h:1712) +==5249== by 0x482803: std::shared_ptr::shared_ptr, pipedal::PluginHost*, LilvWorldImpl*&, LilvPluginImpl const*&>(std::_Sp_alloc_shared_tag >, pipedal::PluginHost*&&, LilvWorldImpl*&, LilvPluginImpl const*&) (shared_ptr.h:464) +==5249== by 0x47E363: std::shared_ptr std::make_shared(pipedal::PluginHost*&&, LilvWorldImpl*&, LilvPluginImpl const*&) (shared_ptr.h:1010) +==5249== +==5249== LEAK SUMMARY: +==5249== definitely lost: 12,635 bytes in 284 blocks +==5249== indirectly lost: 16,343 bytes in 30 blocks +==5249== possibly lost: 0 bytes in 0 blocks +==5249== still reachable: 59,759 bytes in 160 blocks +==5249== suppressed: 0 bytes in 0 blocks +==5249== Reachable blocks (those to which a pointer was found) are not shown. +==5249== To see them, rerun with: --leak-check=full --show-leak-kinds=all +==5249== +==5249== For lists of detected and suppressed errors, rerun with: -s +==5249== ERROR SUMMARY: 9 errors from 9 contexts (suppressed: 0 from 0) diff --git a/vite/src/pipedal/MainPage.tsx b/vite/src/pipedal/MainPage.tsx index a1ea987..e7ba524 100644 --- a/vite/src/pipedal/MainPage.tsx +++ b/vite/src/pipedal/MainPage.tsx @@ -255,8 +255,11 @@ export const MainPage = // selectedItem = value.getFirstSelectableItem(); // } let selectedItem = value.selectedPlugin; - if (selectedItem === -1 || !value.hasItem(selectedItem)) { - selectedItem = value.getFirstSelectableItem(); + if (selectedItem !== -2 && selectedItem !== -3) // start and end nodes. + { + if (selectedItem === -1 || !value.hasItem(selectedItem)) { + selectedItem = value.getFirstSelectableItem(); + } } this.setState({ pedalboard: value, diff --git a/vite/src/pipedal/MidiChannelBinding.tsx b/vite/src/pipedal/MidiChannelBinding.tsx index 76feeeb..06dab83 100644 --- a/vite/src/pipedal/MidiChannelBinding.tsx +++ b/vite/src/pipedal/MidiChannelBinding.tsx @@ -24,6 +24,11 @@ export enum MidiDeviceSelection { DeviceList = 2 } +// This feature is not yet completely implemented. +export const midiChannelBindingControlFeatureEnabled: boolean = false; // set to true to enable the midi channel binding control in the plugin control view. + + + export default class MidiChannelBinding { deserialize(input: any) : MidiChannelBinding { this.deviceSelection = input.deviceSelection; diff --git a/vite/src/pipedal/ModGuiHost.tsx b/vite/src/pipedal/ModGuiHost.tsx index 11bbcff..cd58a2a 100644 --- a/vite/src/pipedal/ModGuiHost.tsx +++ b/vite/src/pipedal/ModGuiHost.tsx @@ -399,7 +399,6 @@ class CustomSelectPathControl implements ModGuiControl { } let valueElement = this.getValueElement(); if (valueElement) { - /// xxx: update the display value. if (this.pathValue === null || this.pathValue === "") { valueElement.textContent = "No file selected"; return; diff --git a/vite/src/pipedal/PedalboardView.tsx b/vite/src/pipedal/PedalboardView.tsx index 4b25517..7b3bfcd 100644 --- a/vite/src/pipedal/PedalboardView.tsx +++ b/vite/src/pipedal/PedalboardView.tsx @@ -43,6 +43,7 @@ import { } from './Pedalboard'; import MidiIcon from './svg/ic_midi.svg?react'; +import { midiChannelBindingControlFeatureEnabled } from './MidiChannelBinding'; const START_CONTROL = Pedalboard.START_CONTROL; @@ -995,7 +996,7 @@ withStyles( const classes = withStyles.getClasses(this.props); return (
{ e.preventDefault(); }}> - {hasMidiConnector && ( + {hasMidiConnector && midiChannelBindingControlFeatureEnabled && (
diff --git a/vite/src/pipedal/PluginControlView.tsx b/vite/src/pipedal/PluginControlView.tsx index 073fd86..76d483f 100644 --- a/vite/src/pipedal/PluginControlView.tsx +++ b/vite/src/pipedal/PluginControlView.tsx @@ -27,6 +27,8 @@ import AutoZoom from './AutoZoom'; import ModGuiHost from './ModGuiHost'; +import {midiChannelBindingControlFeatureEnabled} from './MidiChannelBinding'; + import { withStyles } from "tss-react/mui"; import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; import { UiPlugin, UiControl, UiFileProperty, UiFrequencyPlot, ScalePoint } from './Lv2Plugin'; @@ -762,7 +764,7 @@ const PluginControlView = makeIoPluginInfo("Output", Pedalboard.END_PEDALBOARD_ITEM_URI); midiBindingControl(pedalboardItem: PedalboardItem): ReactNode { - if (!pedalboardItem.midiChannelBinding) { + if ((!pedalboardItem.midiChannelBinding) || (midiChannelBindingControlFeatureEnabled === false)) { return false; } return ( @@ -890,7 +892,7 @@ const PluginControlView = pedalboardItem.midiChannelBinding = MidiChannelBinding.CreateMissingValue(); } - if (pedalboardItem.midiChannelBinding) { + if (pedalboardItem.midiChannelBinding && midiChannelBindingControlFeatureEnabled) { nodes.push(this.midiBindingControl(pedalboardItem)); } return ( diff --git a/vite/src/pipedal/VuMeter.tsx b/vite/src/pipedal/VuMeter.tsx index c77fbe0..ebcd333 100644 --- a/vite/src/pipedal/VuMeter.tsx +++ b/vite/src/pipedal/VuMeter.tsx @@ -95,7 +95,9 @@ class TelltaleState { } const LEFT_X = BORDER_THICKNESS; -const RIGHT_X = BORDER_THICKNESS + + CHANNEL_WIDTH + BORDER_THICKNESS; +const RIGHT_X = BORDER_THICKNESS + CHANNEL_WIDTH + BORDER_THICKNESS; +const TOP_Y = BORDER_THICKNESS; +const VU_BAR_HEIGHT = DISPLAY_HEIGHT-2*BORDER_THICKNESS; const styles = (theme: Theme) => ({ frame: css({ @@ -119,15 +121,16 @@ const styles = (theme: Theme) => ({ overflow: "hidden", }), monoTextFrame: css({ - display: "flex",flexFlow: "column nowrap", alignItems: "center", textAlign: "center" + display: "flex",flexFlow: "column nowrap", alignItems: "center", textAlign: "center", overflow: "clip", }), stereoTextFrame: css({ - display: "flex",flexFlow: "column nowrap", alignItems: "center", textAlign: "center" + display: "flex",flexFlow: "column nowrap", alignItems: "center", textAlign: "center",overflow: "clip", }), vuTextFrame: css({ color: theme.palette.text.secondary, - display: "table-cell", + display: "div", position: "relative", + overflow: "clip", marginTop: 4, textAlign: "center", width: 20, height: 16, @@ -135,73 +138,60 @@ const styles = (theme: Theme) => ({ justifyContent: "center", alignItems: "center" }), - redFrameL: css({ + frameL: css({ position: "absolute", - top: "100%", + top: TOP_Y , left: LEFT_X, width: CHANNEL_WIDTH, - height: DISPLAY_HEIGHT - 2, - background: "#F00" + height: VU_BAR_HEIGHT, + overflow: "clip" }), - yellowFrameL: css({ - top: "100%", - left: LEFT_X, + frameR: css({ + position: "absolute", + overflow: "clip", + top: TOP_Y, + left: RIGHT_X, width: CHANNEL_WIDTH, - position: "absolute", - height: DISPLAY_HEIGHT - 2, - background: "#CC0" + height: VU_BAR_HEIGHT, }), - greenFrameL: css({ - top: "100%", - left: LEFT_X, + yellowBar: css({ + top: 0, + left: 0, width: CHANNEL_WIDTH, + height: VU_BAR_HEIGHT, position: "absolute", - height: DISPLAY_HEIGHT - 2, - background: "#0C2" + background: "#CC0", + transform: "translateY(" + VU_BAR_HEIGHT + "px)" }), - indicatorL: css({ + greenBar: css({ + top: 0, + left: 0, + width: CHANNEL_WIDTH, + height: VU_BAR_HEIGHT, position: "absolute", - left: LEFT_X, + background: "#0C2", + transform: "translateY(" + VU_BAR_HEIGHT + "px)" + }), + redBar: css({ + top: 0, + left: 0, + width: CHANNEL_WIDTH, + height: VU_BAR_HEIGHT, + position: "absolute", + background: "#F00", + transform: "translateY(" + VU_BAR_HEIGHT + "px)" + }), + indicatorBar: css({ + position: "absolute", + left: 0, width: CHANNEL_WIDTH, height: TELLTALE_HEIGHT + "px", - top: "100%", - background: "#F00" + top: 0, + background: "#F00", + transform: "translateY(" + VU_BAR_HEIGHT + "px)" + }), - redFrameR: css({ - position: "absolute", - top: "100%", - left: RIGHT_X, - width: CHANNEL_WIDTH, - height: DISPLAY_HEIGHT - 2, - background: "#F00" - }), - yellowFrameR: css({ - top: "100%", - left: RIGHT_X, - width: CHANNEL_WIDTH, - position: "absolute", - height: DISPLAY_HEIGHT - 2, - background: "#CC0" - }), - greenFrameR: css({ - top: "100%", - left: RIGHT_X, - width: CHANNEL_WIDTH, - position: "absolute", - height: DISPLAY_HEIGHT - 2, - background: "#0C2" - }), - indicatorR: css({ - position: "absolute", - left: RIGHT_X, - width: CHANNEL_WIDTH, - height: TELLTALE_HEIGHT + "px", - top: "100%", - background: "#F00" - - }) - }); @@ -261,10 +251,12 @@ export const VuMeter = telltaleStateR: TelltaleState = new TelltaleState(); vuInfo?: VuUpdateInfo; - requesttedStereoState: boolean = false; + requestedStereoState: boolean = false; + private currentDisplayValue: string | null = null; updateText(telltaleDb: number) { + if (this.textRef.current) { let displayValue: string; if (telltaleDb <= MIN_DB) @@ -288,16 +280,39 @@ export const VuMeter = } } } - this.textRef.current.innerText = displayValue; + + if (this.currentDisplayValue !== displayValue) + { + this.currentDisplayValue = displayValue; + this.textRef.current.innerText = displayValue; + } } } + private currentVuInfo: VuUpdateInfo | null = null; + + private animationFrameHandle: number | null = null; onVuChanged(vuInfo: VuUpdateInfo): void { + this.currentVuInfo = vuInfo; + if (this.animationFrameHandle == null) + { + this.animationFrameHandle = window.requestAnimationFrame(() => { + this.animationFrameHandle = null; + this.animateVuUpdate(); + }); + } + } + private animateVuUpdate() : void + { let value: number; let valueR: number; - - this.vuInfo = vuInfo; - + let vuInfo = this.currentVuInfo; + if (!vuInfo) { + return; // no vu info. + } + if (!this.divRef.current) { // unmounted? + return; + } let isStereo: boolean; @@ -312,34 +327,37 @@ export const VuMeter = } if (this.state.isStereo !== isStereo) { - if (isStereo !== this.requesttedStereoState) // guard against overrunning the layout engine. + if (isStereo !== this.requestedStereoState) // guard against overrunning the layout engine. { - this.requesttedStereoState = isStereo; + this.requestedStereoState = isStereo; this.setState({ isStereo: isStereo }); - return; // we can't procede without stereo layout. + return; // we can't proceed without stereo layout. } } let childNodes = this.divRef.current!.childNodes; + let leftFrameChildNodes = childNodes[0].childNodes; + let vuData: VuChannelData = { value: value, - redDiv: childNodes[0] as HTMLDivElement, - yellowDiv: childNodes[1] as HTMLDivElement, - greenDiv: childNodes[2] as HTMLDivElement, - telltaleDiv: childNodes[3] as HTMLDivElement + redDiv: leftFrameChildNodes[0] as HTMLDivElement, + yellowDiv: leftFrameChildNodes[1] as HTMLDivElement, + greenDiv: leftFrameChildNodes[2] as HTMLDivElement, + telltaleDiv: leftFrameChildNodes[3] as HTMLDivElement }; this.updateChannel(vuData,this.telltaleStateL); if (this.state.isStereo) { + let rightFrameChildren = childNodes[1].childNodes; vuData = { value: valueR, - redDiv: childNodes[4] as HTMLDivElement, - yellowDiv: childNodes[5] as HTMLDivElement, - greenDiv: childNodes[6] as HTMLDivElement, - telltaleDiv: childNodes[7] as HTMLDivElement + redDiv: rightFrameChildren[0] as HTMLDivElement, + yellowDiv: rightFrameChildren[1] as HTMLDivElement, + greenDiv: rightFrameChildren[2] as HTMLDivElement, + telltaleDiv: rightFrameChildren[3] as HTMLDivElement }; this.updateChannel(vuData, this.telltaleStateR); } @@ -367,17 +385,17 @@ export const VuMeter = let INVISIBLE_Y = INTERIOR_DISPLAY_HEIGHT + "px"; if (y >= this.yYellow) { // green only. - vuData.greenDiv.style.top = y + "px"; - vuData.yellowDiv.style.top = INVISIBLE_Y; - vuData.redDiv.style.top = INVISIBLE_Y; + vuData.greenDiv.style.transform = `translateY(${y}px)`; + vuData.yellowDiv.style.transform = `translateY(${INVISIBLE_Y})`; + vuData.redDiv.style.transform = `translateY(${INVISIBLE_Y})`; } else { - vuData.greenDiv.style.top = this.yYellow + "px"; + vuData.greenDiv.style.transform = `translateY(${this.yYellow}px)`; if (y >= this.yZero) { - vuData.yellowDiv.style.top = y + "px"; - vuData.redDiv.style.top = INVISIBLE_Y; + vuData.yellowDiv.style.transform = `translateY(${y}px)`; + vuData.redDiv.style.transform = `translateY(${INVISIBLE_Y})`; } else { - vuData.yellowDiv.style.top = this.yZero + "px"; - vuData.redDiv.style.top = y + "px"; + vuData.yellowDiv.style.transform = `translateY(${this.yZero}px)`; + vuData.redDiv.style.transform = `translateY(${y}px)`; } } let dbTelltale = telltaleState.getTelltaleHoldValue(db); @@ -399,7 +417,7 @@ export const VuMeter = yTelltale = INTERIOR_DISPLAY_HEIGHT; } } - vuData.telltaleDiv.style.top = yTelltale + "px"; + vuData.telltaleDiv.style.transform = `translateY(${yTelltale}px)`; } @@ -467,23 +485,28 @@ export const VuMeter = if (!this.state.isStereo) { return (
-
-
-
-
+
+
+
+
+
+
); } else { return (
-
-
-
-
- -
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
);