Merge 1.4.86 Dev branch.
1.4.86 Dev merge.
This commit is contained in:
Vendored
+2
-1
@@ -114,7 +114,8 @@
|
||||
{
|
||||
"name": "LD_LIBRARY_PATH",
|
||||
"value": "/usr/local/lib/aarch64-linux-gnu"
|
||||
}
|
||||
},
|
||||
|
||||
],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
|
||||
Vendored
+6
-1
@@ -114,11 +114,14 @@
|
||||
"Bassman",
|
||||
"Chowdhury",
|
||||
"Chowdhury's",
|
||||
"datasheet",
|
||||
"datasheets",
|
||||
"discursus",
|
||||
"distro",
|
||||
"distros",
|
||||
"dpkg",
|
||||
"flac",
|
||||
"Focusrite",
|
||||
"Freeverb",
|
||||
"Guitarix",
|
||||
"gxplugins",
|
||||
@@ -150,7 +153,9 @@
|
||||
"pipedalui",
|
||||
"rdfs",
|
||||
"RTAS",
|
||||
"Scarlett",
|
||||
"setlist",
|
||||
"Sonus",
|
||||
"stompboxes",
|
||||
"ToobAmp",
|
||||
"toolsets",
|
||||
@@ -163,7 +168,7 @@
|
||||
"cSpell.ignoreWords": [
|
||||
"nammodel"
|
||||
],
|
||||
"cSpell.enabled": true,
|
||||
"cSpell.enabled": false,
|
||||
|
||||
// Disable all automatic completion suggestions - only show when Ctrl+Space is pressed
|
||||
"editor.quickSuggestions": {
|
||||
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.16.0)
|
||||
project(pipedal
|
||||
VERSION 1.4.79
|
||||
VERSION 1.4.86
|
||||
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.79-Beta")
|
||||
set (DISPLAY_VERSION "PiPedal v1.4.86-Experimental")
|
||||
set (PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE})
|
||||
set (CMAKE_INSTALL_PREFIX "/usr/")
|
||||
|
||||
@@ -96,7 +96,7 @@ set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "IoT guitar effect pedal for Raspberry Pi"
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION sound)
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
set(CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION TRUE)
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "lv2-dev, ffmpeg, authbind, curl, gpg, alsa-base| pipewire, alsa-utils" )
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libicu-dev ,lv2-dev, ffmpeg, authbind, curl, gpg, alsa-base| pipewire, alsa-utils" )
|
||||
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE})
|
||||
set(CPACK_PACKAGING_INSTALL_PREFIX /usr)
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
|
||||
@@ -259,6 +259,8 @@ namespace pipedal
|
||||
// Open sequencer in input mode
|
||||
int rc;
|
||||
|
||||
Lv2Log::debug("Opening ALSA Sequencer");
|
||||
|
||||
rc = snd_seq_open(&seqHandle, "default", SND_SEQ_OPEN_DUPLEX, 0);
|
||||
if (rc < 0)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
* All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
// define restrict keyword for C++ compilers (which don't yet support it)
|
||||
#ifndef restrict
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define restrict __restrict__
|
||||
#elif defined(_MSC_VER)
|
||||
#define restrict __restrict
|
||||
#endif
|
||||
#endif
|
||||
@@ -9,11 +9,11 @@
|
||||
<img src="https://img.shields.io/github/downloads/rerdavies/pipedal/total?color=%23808080&link=https%3A%2F%2Frerdavies.github.io%2Fpipedal%2Fdownload.html"/>
|
||||
|
||||
|
||||
Download: <a href='https://rerdavies.github.io/pipedal/download.html'>v1.4.79</a>
|
||||
Download: <a href='https://rerdavies.github.io/pipedal/download.html'>v1.4.86</a>
|
||||
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.79 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. New Phaser and Graphic Equalizer plugins.
|
||||
#### NEW version 1.4.86 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. New Phaser and Graphic Equalizer plugins.
|
||||
|
||||
|
||||
|
||||
@@ -57,13 +57,17 @@ https://github.com/user-attachments/assets/9a9fd0c6-78fc-4284-8b44-6a1929c00cc6
|
||||
### [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)
|
||||
|
||||
|
||||
|
||||
|
||||
### [Frequently Asked Questions](https://rerdavies.github.io/pipedal/FAQ.html)
|
||||
|
||||
|
||||
|
||||
### [Building PiPedal from Source](https://rerdavies.github.io/pipedal/BuildingPiPedalFromSource.html)
|
||||
### [Build Prerequisites](https://rerdavies.github.io/pipedal/BuildPrerequisites.html)
|
||||
|
||||
Binary file not shown.
@@ -16,4 +16,4 @@ As a last resort, you be able to configure VSCode and/or CMake to cross-compile
|
||||
|
||||
|
||||
--------
|
||||
[<< Which LV2 Plugins Does PiPedal Support?](WhichLv2PluginsAreSupported.md) | [Up](Documentation.md) | [Build Prerequisites >>](BuildPrerequisites.md)
|
||||
[<< Frequently Asked Questions](FAQ.md) | [Up](Documentation.md) | [Build Prerequisites >>](BuildPrerequisites.md)
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
|
||||
|
||||
|
||||
#### [Frequently Asked Questions](FAQ.md)
|
||||
|
||||
|
||||
|
||||
#### [Building PiPedal from Source](BuildingPiPedalFromSource.md)
|
||||
#### [Build Prerequisites](BuildPrerequisites.md)
|
||||
#### [The Build Systems](TheBuildSystem.md)
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
---
|
||||
page_icon: img/faq.jpg
|
||||
icon_width: 320px
|
||||
icon_float: right
|
||||
---
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
{% include pageIcon.html %}
|
||||
|
||||
### Q. Why is it so important to use a good USB audio adapter?
|
||||
|
||||
If you use a cheap consumer-level USB audio adapter, you will not get the best possible results from PiPedal. You will, in fact, get good results, possibly better results than anything you have used before. But not the best possible results, which, I am somewhat embarrassed to say, are astounding, particularly when using TooB Neural Amp Modeler amp models. The quality of Toob Neural Amp Modeler amp models is so good that it is actually possible to make a guitar effects box that sounds better than retail guitar effects boxes that cost thousands of dollars. But you can't get there without a good USB audio adapter.
|
||||
|
||||
Consumer-level USB audio adapters provide somewhere around 96dB of actual dynamic range between the loudest possible signal and the noise floor. And disturbingly, many consumer-level USB audio adapters have a noise floors that are much worse. I, personally, have a USB adapter from a brand that most people would consider reputable, that only has a 60dB of measured dynamic range. That would be 10 bits of actual audio data, and 14 bits of pure noise, all packed into 24-bit words, for those of you who are counting.
|
||||
|
||||
Support for 24-bit audio is a marketing gimmick. The actual spec you should be interested in is the dynamic range of the device, which is the difference between the loudest possible signal and the noise floor.
|
||||
|
||||
Dynamic range matters enormously. By the time you have run that -96dB noise signal through a heavily-overdrive Neural Amp Modeler amp model, you will end up with a dynamic range on output somewhere around -12dB, or perhaps even less if you are into very heavy metal. It still sounds ok; but it doesn't sound nearly as good as it could. Models tend to sound muddy, and indistinct, and there's an audible hiss in the background. Still good. But they could be so much better.
|
||||
|
||||
Professional-level USB audio adapters, on the other hand, provide much higher dynamic ranges. A good professional-level USB audio adapter will provide somewhere between 110dB and 120dB of of usable dynamic range. That difference sounds may modest, but it makes a huge difference when using NAM amp models. It's like you were playing under a blanket before, and suddenly the blanket is gone, your eyes suddenly clear, and a light shines down from heaven. The emulations have clarity, and definition, and genuinely sound and feel like the real thing. As a point of reference, the current generation of Line6 Helix guitar effects boxes manage to provide a126dB of audio input dynamic range (120dB on output), which is admittedly impressive, and plays a very significant role in how good they do sound. (Although to my ears, the Helix amp models feel cold, and are unforgiving to play, compared to NAM amp models).
|
||||
|
||||
It's perfectly OK to use consumer-level USB audio adapters with PiPedal when you are trying out the software. I think you will like the result. But I would like to encourage you in the strongest possible terms to invest an a decent professional-level USB audio adapter. They are not that expensive -- somewhere in the $100 to $200 range. You will not get the full experience of using PiPedal at its best without a good USB audio adapter. I promise you, you will not regret the investment. With a consumer-level USB audio adapter, you end up with a pretty good stomp box, frankly. It does sound good. But with a pro level adapter, you are going to get a guitar stomp box that provides significantly genuinely better sound quality with a quality that exceeds that of retail guitar effects boxes like Helix that cost thousands of dollars.
|
||||
|
||||
I can only claim partial credit for this. The TooB Neural Amp Modeler, is based on Steven Atkins' open-source Neural Amp Modeler Core library which is the real hero here. The quality of TooB Neural Amp Modeler emulations is simply astounding. To be honest, I very much see PiPedal as a way to put the Neural Amp Modeler models into the hands of as many guitarists as possible, and to make it easy for them to do so. Those of us who work with Neural Amp Modeler often comment on how game-changing this technology is. How NAM changes everything. And how it is the future of guitar effects because it is so much better than anything that has come before it.
|
||||
|
||||
But you can't actually experience the full impact of NAM unless you give PiPedal a good USB audio adapter to work with.
|
||||
|
||||
Investing in a professional-level USB audio adapter is not going to be an investment that you will regret. "Professional-level" in this context means something in the $100 to $200 price range, not in a $30-$60 price range. The particular spec you need to look for on datasheets is "110dB dynamic range". "-130.5 dbU EIN A-weighted is actually not quite the right spec, but is a solid indicator that you are dealing with a device in the right class. "dB A-weighted" SN/R slightly inflates SN/R numbers, if you see it on a datasheet. But consumer-level USB audio adapters with only 96dB of actual dynamic range will not be able to quote an an A-weighted SN/R or dynamic range in the 110+ range no matter how hard they try. Generally, if the datasheet doesn't say what the device's SN/R is, you can assume that it not good, and not a professional-level device. The device that I personally use is a MOTU M2, which provides 115db of dynamic range (-129dbU EIN), and is a very solidly built device, with great controls, and extremely usable meters.
|
||||
|
||||
Devices that would make excellent choices:
|
||||
|
||||
- MOTU M2
|
||||
- Focusrite Scarlett 2i2 (4th gen)
|
||||
-PreSonus Quantum ES 2 USB-C Audio Interface
|
||||
- Solid State Logic SSL 2 MK II
|
||||
- Universal Audio Volt 176
|
||||
|
||||
Or other devices in that sort of class and price range.
|
||||
|
||||
The MOTU M2, and the Focusrite Scarlett 2i2 are relatively affordable.
|
||||
|
||||
#### Q. My Neural Amp Modeler amp models don't sound that great. Am I doing something wrong? the output signal not loud enough.
|
||||
|
||||
You probably are.
|
||||
|
||||
To get best results, you must make sure that analog input and output signal levels are trimmed correctly. If you have knobs and dials and buttons, make sure those knobs and dials and buttons are set correctly. If you don't have knobs and dials and buttons, see the following question for instructions on how to trim input and output levels using `alsamixer`.
|
||||
|
||||
The knobs and dials on your USB audio adapter (whether physical, or `alsamixer` controls) are used to set the analog signal levels being presented the Analog to Digital Converter (ADC). You want to set the input trim levels so that the digital audio signal that PiPedal gets is as close to 0dB as possible, without clipping (going into the red) at any point. If you set it too low, you will lose accuracy in the digital audio data, and your amp models will sound muddy and unpleasant. If you set it too high, you will cause clipping in the digital audio data, and which sounds extremely harsh and unpleasant. Avoid clipping at all costs.
|
||||
|
||||
If you click on the start node and end nodes of a Pipedal preset, you will see VU meters that measure the loudness of the digital audio coming into and going out of Pipedal. These meters are a good way to check that the analog trim levels of your input and output signals are correct. You should, in fact, NEVER use PiPedal's input and output trim controls to control the volume of your audio, unless you have a compelling reason to do so. Use your knobs and dials (or `alsamixer` controls) to set the analog input and control voume of audio comping out of the audio output jacks.
|
||||
|
||||
The quality of your USB audio adapter is also important. See "Q. Why is it so important to use a good USB audio adapter?", above.
|
||||
|
||||
#### Q. The signal levels on my USB adapter are too low. The input signal is too quiet, or the output signal not loud enough.
|
||||
|
||||
A. This is a common problem with USB audio adapters, especially inexpensive ones. USB adapters are designed to work with a wide range of signal levels. Most USB audio devices have knobs and controls and buttons on the front panel that allow you to adjust the analog input and output levels. But some USB adapters don't have those controls. For these devices, you must use Linux commandline tools to adjust the signal levels. Unfortunately, the default levels that are set by the operating system for these devices are much too low; so if you have a device like this, you will run into a problem.
|
||||
|
||||
To fix this, you can use the `alsamixer` command to adjust the levels. Open a terminal window and type `alsamixer`. This will open a graphical mixer interface in the terminal. Use the arrow keys to navigate to the input and output channels, and use the up and down arrow keys to adjust the levels. Make sure to set the levels appropriately for both input and output channels.
|
||||
|
||||
Fortunately, settings made in `alsamixer` are persistent, and will remain in effect even if you reboot your system, so you will usually only have to do this procedure once.
|
||||
|
||||
|
||||
#### Q. Dial control movements are too coarse. How do I make them finer?
|
||||
|
||||
A. If you are using a desktop browser, the Ctrl and Shift keys modify the behavior of the dial control.
|
||||
If you press the Shift key while you are dragging the control, the values will change in 10x smaller increments.
|
||||
If you press the Ctrl key while you are dragging the control, the values will change in 50x smaller increments.
|
||||
|
||||
If you are using
|
||||
a touch interface, the dials will change in 10x smaller increments if you have two fingers touching the screen while you are dragging the control,
|
||||
and 50x smaller increments if you have three fingers touching the screen while you are dragging the control. The three-finger touch gesture is a bit
|
||||
awkward. I find the best way to do it is to touch the screen with your middle finger first, and then touch the screen with your index finger and your ring finger. The position of the first finger to touch the screen determines how controls change.
|
||||
|
||||
#### Q. What do the buffer size controls do? How do I choose good values?
|
||||
|
||||
A. When configuring your audio device, you must choose a buffer size, and how many buffers to use.
|
||||
|
||||
The simple answer is: use 32x4 if your hardware can handle it, and use larger sizes if your hardware can't.
|
||||
|
||||
The longer answer is complicated (but not too complicated).
|
||||
|
||||
The buffer size determines how much audio data is processed at once by PiPedal. Pipedal processes audio data in chunks, the buffer size sets how large those chunks are. For example, if the buffer size is set to 64 samples, PiPedal will process audio 64 samples at a time. There is a slight cost to processing each chunk of audio, in addition to the cost of processing each sample of audio. So smaller buffer sizes mean that more CPU overhead is incurred for processing the buffer. Larger buffer sizes mean more efficient use of CPU, at the const of increased latency. As a loose guideline, choosing a buffer size of 32 samples incurs a few percentage points of CPU overhead -- not enough to be a problem on most hardware; a buffer size of 16 samples incurs a few percentage points more (5 to 10% depending on the effects you are using); and a buffer size of 64 samples has almost neglibible CPU overhead. The actual overhead depends on the hardware you are using, and the effects you are using. If you are determined to squeeze every last ounce of performance out of your hardware, you may want to experiment to find absolutely optimal settings; but 32x4 gives
|
||||
sufficiently good results for most users.
|
||||
|
||||
The number of buffers determines how large a queue of audio buffers Pipedal uses. The longer the queue, the less likely the hardware is going to run out of audio data to send or run out of space to store newly received audio data. For example, if the number of buffers is set to 4, Pipedal will use one of those buffers to process audio, and will do its best to keep the audio queue filled with 3 additional buffers that have already been processed, and are waiting to be sent to the hardware, one of which will be being actively processed by the drivers and/or hardware at any given time.
|
||||
|
||||
If the audio output queue runs out of buffers, this is known as an "underrun"; if the input queue runs out of buffers, this is known as an "overrun". And Pipedal refers to either or both of the conditions as "XRuns". You can see the number of XRuns in the status message that appears at the bottom of the screen, or at the top of the Settings page (if you have turned of status messages on the main screen).
|
||||
|
||||
XRuns are bad. They cause snaps and pops in the audio, and can sometimes cause the audio device to stop working altogether until it is restarted. So you want to avoid them at all costs.
|
||||
|
||||
The buffer size, and the number of buffers determine the latency of the audio system—the time it takes for audio to travel from the input jack to the output jack. The larger the buffer size, and the more buffers used, the longer the latency. Generally, you want to ensure that the overall latency, jack to jack, is less than 10 milliseconds. Latency longer than 10ms will make your instrument feel unpleasant to play. And very long latencies (in the order of 50ms) can actually make it impossible to play!
|
||||
|
||||
So smaller values are better when it comes to latency. And larger values are better when it comes to CPU efficiency and stability.
|
||||
|
||||
Unfortunately, choosing the right values depends on the the hardware you are using, and the operating system you are running, which drivers the device is using, and how heavily you are using the CPU when processing audio plugins, perhaps on which USB port you are using, and even the type of USB cable you are using. So there is no one-size-fits-all answer to this question of how to choose good values.
|
||||
|
||||
Fortunately, there is a soft and loose rule that works well with almost all modern hardware, whether consumer-level or professional-level. and that works well with almost all professional-level audio adapters ever built. And that rule is: use a 32x4 buffer configuration. This provides excellent latency, and is stable on most audio adapters. If you are using old, or cheap hardware, you may need to increase the buffer size, or the number of buffers, or both in order to prevent the audio device from x-running. Increase buffer sizes to the smallest value that prevents device from x-running. 16x4 is an excellent configuration too, with even less latency, but it does use more CPU. The CPU overhead of a 32x configuration is small, and the overhead of a 64x configuration is vanishingly small.
|
||||
|
||||
If you have to use larger buffers, there is absolutely nothing wrong with a 64x3 or 32x6 configuration. But by the time you get up to 128x buffer configurations, you are probably getting into the range where latency is going to be a problem.
|
||||
|
||||
For what it's worth, hardware doesn't really care whether buffer settings are 64x3 or 32x6. The hardware actually just sees one big buffer, and chases reads and writes memory as fast as it can. The interesting difference between 64x3 and 32x6 is that the 32x6 configuration gives the hardware more queued data. In the 64x3 configuration, PiPedal uses one buffer, the hardware uses another buffer, and there is (hopefully) one queued 64-sample buffer waiting to be sent. In the 32x6 configuration, there is one buffer being used by PiPedal, one buffer being used by the hardware, and there are (hopefully) 4 queued 32-sample buffers waiting to be sent. So twice as much queued data when using a 32x6 configuration.
|
||||
|
||||
--------
|
||||
[<< LV2 Plugins with MOD User Interfaces](ModUiSupport.md) | [Up](Documentation.md) | [BuildingPiPedal from Source >>](BuildingPiPedalFromSource.md)
|
||||
+5
-5
@@ -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.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)
|
||||
- [Raspberry Pi OS bookworm (aarch64) v1.4.86](https://github.com/rerdavies/pipedal/releases/download/v1.4.86/pipedal_1.4.86_arm64.deb)
|
||||
- [Ubuntu 24.x (aarch64) v1.4.86](https://github.com/rerdavies/pipedal/releases/download/v1.4.86/pipedal_1.4.86_arm64.deb)
|
||||
- [Ubuntu 24.x (amd64) v1.4.86](https://github.com/rerdavies/pipedal/releases/download/v1.4.86/pipedal_1.4.86_amd64.deb)
|
||||
|
||||
|
||||
Version 1.4.79 has been tested on Raspberry Pi OS bookworm, Ubuntu 24.04 (amd64), and Ubuntu 24.10 (aarch64). Download the appropriate package for your platform, and install using the following procedure:
|
||||
Version 1.4.86 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.79_arm64.deb
|
||||
sudo apt-get install ./pipedal_1.4.86_arm64.deb
|
||||
```
|
||||
You MUST use `apt-get`. `apt` will not install downloaded packages; and `dpkg -i` will not install dependencies.
|
||||
|
||||
|
||||
@@ -32,4 +32,4 @@ Support for MOD UIs in PiPedal is still experimental, so you may encounter some
|
||||
|
||||
|
||||
--------
|
||||
[<< Which Plugins are Supported?](WhichLv2PluginsAreSupported.md) | [Up](Documentation.md) | [BuildingPiPedal from Source >>](BuildingPiPedalFromSource.md)
|
||||
[<< Which Plugins are Supported?](WhichLv2PluginsAreSupported.md) | [Up](Documentation.md) | [Frequenty Asked Questions >>](FAQ.md)
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
# Release Notes
|
||||
## PiPedal 1.4.86 Beta
|
||||
The Big Feature:
|
||||
- Code optimizations for Raspberry Pi 5 (Cortex-A76), providing a potential 2x performance improvement for TooB Neural Amp Modeler.
|
||||
- New "Threaded" button optionally executes TooB NAM computation on a separate thread. Allows up to 5 NAM instances on Pi 4, and practically unlimited NAM instances on Pi 5.
|
||||
- TooB Neural Amp Modeler: Switch to the NeuralAudio NAM Backend
|
||||
Bug fixes:
|
||||
- Compatibility with plugins expecting certain MOD host features.
|
||||
- Improvements to recovery from audio underruns and overruns.
|
||||
- Fixes for loss of currently selected plugin when modifying pedalboard structure.
|
||||
|
||||
## Pipedal 1.4.79 Beta
|
||||
|
||||
The Big Feature:
|
||||
|
||||
+4
-4
@@ -4,9 +4,9 @@
|
||||
|
||||
Download the most recent Debian (.deb) package for your platform:
|
||||
|
||||
- [Raspberry Pi OS bookworm (aarch64) v1.4.79 Beta](https://github.com/rerdavies/pipedal/releases/download/v1.4.79/pipedal_1.4.79_arm64.deb)
|
||||
- [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)
|
||||
- [Raspberry Pi OS bookworm (aarch64) v1.4.86 Beta](https://github.com/rerdavies/pipedal/releases/download/v1.4.86/pipedal_1.4.86_arm64.deb)
|
||||
- [Ubuntu 24.x (aarch64) v1.4.86 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.4.86/pipedal_1.4.86_arm64.deb)
|
||||
- [Ubuntu 24.x (amd64) v1.4.86 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.4.86/pipedal_1.4.86_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.79_arm64.deb
|
||||
sudo apt-get install ./pipedal_1.4.86_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.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
@@ -93,7 +93,7 @@ cabir:impulseFile3
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
rdfs:comment """
|
||||
|
||||
TooB Cab IR is a convolution-based guitar cabinet impulse response simulator.
|
||||
|
||||
@@ -50,7 +50,7 @@ toob:frequencyResponseVector
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
|
||||
mod:brand "TooB";
|
||||
mod:label "TooB CabSim";
|
||||
|
||||
@@ -53,7 +53,7 @@ toobimpulse:impulseFile
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
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
|
||||
|
||||
@@ -51,7 +51,7 @@ toobimpulse:impulseFile
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
rdfs:comment """
|
||||
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ inputStage:filterGroup
|
||||
doap:license <https://two-play.com/TooB/licenses/isc> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
|
||||
mod:brand "TooB";
|
||||
mod:label "TooB Input";
|
||||
|
||||
@@ -68,7 +68,7 @@ pstage:stage3
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
|
||||
mod:brand "TooB";
|
||||
mod:label "Power Stage";
|
||||
|
||||
@@ -1,594 +0,0 @@
|
||||
@prefix doap: <http://usefulinc.com/ns/doap#> .
|
||||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
|
||||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
||||
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
|
||||
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
|
||||
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
|
||||
@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
|
||||
@prefix epp: <http://lv2plug.in/ns/ext/port-props#> .
|
||||
@prefix uiext: <http://lv2plug.in/ns/extensions/ui#> .
|
||||
@prefix idpy: <http://harrisonconsoles.com/lv2/inlinedisplay#> .
|
||||
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
||||
@prefix mod: <http://moddevices.com/ns/mod#> .
|
||||
@prefix param: <http://lv2plug.in/ns/ext/parameters#> .
|
||||
@prefix work: <http://lv2plug.in/ns/ext/worker#> .
|
||||
@prefix pg: <http://lv2plug.in/ns/ext/port-groups#> .
|
||||
|
||||
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
|
||||
@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
|
||||
@prefix plug: <http://two-play.com/plugins/toob-power-stage-2#> .
|
||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
||||
@prefix state: <http://lv2plug.in/ns/ext/state#> .
|
||||
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
|
||||
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
||||
@prefix modgui: <http://moddevices.com/ns/modgui#> .
|
||||
@prefix pstage: <http://two-play.com/plugins/toob-power-stage-2#> .
|
||||
|
||||
|
||||
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" .
|
||||
|
||||
|
||||
<http://two-play.com/plugins/toob-power-stage-2#uiState>
|
||||
a lv2:Parameter ;
|
||||
rdfs:label "uiState" ;
|
||||
rdfs:range atom:Vector .
|
||||
|
||||
|
||||
<http://two-play.com/rerdavies#me>
|
||||
a foaf:Person ;
|
||||
foaf:name "Robin Davies" ;
|
||||
foaf:mbox <mailto:rerdavies@gmail.com> ;
|
||||
foaf:homepage <https://github.com/sponsors/rerdavies> .
|
||||
|
||||
<http://two-play.com/plugins/toob-power-stage-2>
|
||||
a lv2:Plugin ,
|
||||
lv2:SimulatorPlugin ;
|
||||
|
||||
doap:name "TooB Power Stage";
|
||||
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 53 ;
|
||||
|
||||
mod:brand "TooB";
|
||||
mod:label "Power Stage";
|
||||
lv2:requiredFeature urid:map ;
|
||||
lv2:optionalFeature lv2:hardRTCapable;
|
||||
patch:readable
|
||||
<http://two-play.com/plugins/toob-power-stage-2#uiState>;
|
||||
|
||||
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." ;
|
||||
]
|
||||
.
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
rdfs:comment "TooB spectrum analyzer" ;
|
||||
|
||||
mod:brand "TooB";
|
||||
|
||||
@@ -57,7 +57,7 @@ tonestack:eqGroup
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
|
||||
uiext:ui <http://two-play.com/plugins/toob-tone-stack-ui>;
|
||||
|
||||
|
||||
Regular → Executable
BIN
Binary file not shown.
Regular → Executable
BIN
Binary file not shown.
@@ -40,7 +40,7 @@
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
rdfs:comment """
|
||||
Emulation of a Boss CE-2 Chorus.
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
rdfs:comment """
|
||||
A straightforward no-frills digital delay.
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
rdfs:comment """
|
||||
Emulation of a Boss BF-2 Flanger, based on circuit analysis and simulation of the original.
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
rdfs:comment """
|
||||
Digital emulation of a Boss BF-2 Flanger.
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
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.
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
rdfs:comment """
|
||||
A 7 octave graphic equalizer, with frequencies chosen to be useful for EQ-ing guitar signals.
|
||||
""" ;
|
||||
|
||||
@@ -92,7 +92,7 @@ myprefix:output_group
|
||||
doap:license <https://opensource.org/license/mit/> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 1 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
|
||||
ui:ui <http://two-play.com/plugins/toob-looper-four-ui>;
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ myprefix:output_group
|
||||
doap:license <https://opensource.org/license/mit/> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 1 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
|
||||
ui:ui <http://two-play.com/plugins/toob-looper-one-ui>;
|
||||
|
||||
|
||||
@@ -1,726 +0,0 @@
|
||||
@prefix doap: <http://usefulinc.com/ns/doap#> .
|
||||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
|
||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
||||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
||||
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
|
||||
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
|
||||
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
|
||||
@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
|
||||
@prefix epp: <http://lv2plug.in/ns/ext/port-props#> .
|
||||
@prefix uiext: <http://lv2plug.in/ns/extensions/ui#> .
|
||||
@prefix idpy: <http://harrisonconsoles.com/lv2/inlinedisplay#> .
|
||||
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
||||
@prefix mod: <http://moddevices.com/ns/mod#> .
|
||||
@prefix param: <http://lv2plug.in/ns/ext/parameters#> .
|
||||
@prefix work: <http://lv2plug.in/ns/ext/worker#> .
|
||||
@prefix pg: <http://lv2plug.in/ns/ext/port-groups#> .
|
||||
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
|
||||
@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
|
||||
@prefix state: <http://lv2plug.in/ns/ext/state#> .
|
||||
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
|
||||
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
||||
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
|
||||
@prefix pprop: <http://lv2plug.in/ns/ext/port-props#> .
|
||||
@prefix pipedal_ui: <http://github.com/rerdavies/pipedal/ui#> .
|
||||
|
||||
|
||||
@prefix looperPrefix: <http://two-play.com/plugins/toob-looper#> .
|
||||
|
||||
@prefix myprefix: <http://two-play.com/plugins/toob-looper-stereo#> .
|
||||
|
||||
|
||||
myprefix:stereoInGroup
|
||||
a pg:StereoGroup, pg:InputGroup ;
|
||||
rdfs:label "Stereo Input" ;
|
||||
lv2:symbol "in_g" .
|
||||
|
||||
myprefix:steroOutGroup a pg:Group ;
|
||||
a pg:StereoGroup, pg:OuputGroup ;
|
||||
rdfs:label "Stereo Output" ;
|
||||
lv2:symbol "out_g" .
|
||||
|
||||
|
||||
myprefix:syncGroup
|
||||
a pg:ControlGroup ;
|
||||
lv2:name "Clock" ;
|
||||
lv2:symbol "sync_g" .
|
||||
|
||||
|
||||
myprefix:loop_main_group
|
||||
a pg:ControlGroup ;
|
||||
lv2:name "Main Loop" ;
|
||||
lv2:symbol "loop_main_group" .
|
||||
|
||||
myprefix:loop2_group
|
||||
a pg:ControlGroup ;
|
||||
lv2:name "Loop 2" ;
|
||||
lv2:symbol "loop2_group" .
|
||||
|
||||
myprefix:loop3_group
|
||||
a pg:ControlGroup ;
|
||||
lv2:name "Loop 3" ;
|
||||
lv2:symbol "loop3_group" .
|
||||
|
||||
myprefix:loop4_group
|
||||
a pg:ControlGroup ;
|
||||
lv2:name "Loop 4" ;
|
||||
lv2:symbol "loop4_group" .
|
||||
|
||||
myprefix:options_group
|
||||
a pg:ControlGroup ;
|
||||
lv2:name "Options" ;
|
||||
lv2:symbol "options_group" .
|
||||
|
||||
myprefix:output_group
|
||||
a pg:ControlGroup ;
|
||||
lv2:name "Output" ;
|
||||
lv2:symbol "output_group" .
|
||||
|
||||
|
||||
|
||||
<http://two-play.com/rerdavies#me>
|
||||
a foaf:Person ;
|
||||
foaf:name "Robin Davies" ;
|
||||
foaf:mbox <mailto:rerdavies@gmail.com> ;
|
||||
foaf:homepage <https://github.com/sponsors/rerdavies> .
|
||||
|
||||
|
||||
<http://two-play.com/plugins/toob-looper-stereo>
|
||||
a lv2:Plugin ,
|
||||
lv2:UtilityPlugin ;
|
||||
doap:name "TooB 4Looper (stereo)"
|
||||
;
|
||||
doap:license <https://opensource.org/license/mit/> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 1 ;
|
||||
lv2:microVersion 57 ;
|
||||
|
||||
ui:ui <http://two-play.com/plugins/toob-looper-stereo-ui>;
|
||||
|
||||
patch:readable
|
||||
looperPrefix:audioFile;
|
||||
patch:writable
|
||||
looperPrefix:audioFile;
|
||||
|
||||
lv2:extensionData state:interface ;
|
||||
|
||||
rdfs:comment """
|
||||
A Stereo Looper.
|
||||
|
||||
- A single tap on the bound midi controller will start recording.
|
||||
- A second tap with mark the end of the loop, and start playback.
|
||||
- Subsequent taps will toggle overdub mode. When overdubbing, new audio input will be blended with the existing loop.
|
||||
- A touble tap will stop playback.
|
||||
- To erase an overdub, long press the MIDI controller. The loop will return to the state that it was in before the overdub was made.
|
||||
|
||||
|
||||
|
||||
""" ;
|
||||
|
||||
mod:brand "TooB";
|
||||
mod:label "4Looper";
|
||||
lv2:optionalFeature lv2:hardRTCapable;
|
||||
|
||||
lv2:port
|
||||
################# MAIN Loop
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 0;
|
||||
lv2:symbol "stop1" ;
|
||||
lv2:name "⏹";
|
||||
rdfs:comment "Reset loop 1.";
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
lv2:portProperty lv2:toggled,pprop:trigger;
|
||||
pg:group myprefix:loop_main_group ;
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 1;
|
||||
lv2:symbol "record1" ;
|
||||
lv2:name "⏺";
|
||||
rdfs:comment "Record/Overdub loop 1";
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
lv2:portProperty lv2:toggled,pprop:trigger;
|
||||
pg:group myprefix:loop_main_group ;
|
||||
],
|
||||
[
|
||||
a lv2:OutputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 2;
|
||||
lv2:symbol "record_led1" ;
|
||||
lv2:name "";
|
||||
lv2:portProperty lv2:toggled ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
pipedal_ui:ledColor "red";
|
||||
pg:group myprefix:loop_main_group ;
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 3;
|
||||
lv2:symbol "play1" ;
|
||||
lv2:name "⏵";
|
||||
rdfs:comment "Play Loop 1. Click again to stop.";
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
lv2:portProperty lv2:toggled,pprop:trigger;
|
||||
pg:group myprefix:loop_main_group ;
|
||||
],
|
||||
[
|
||||
a lv2:OutputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 4;
|
||||
lv2:symbol "play_led1" ;
|
||||
lv2:name "";
|
||||
lv2:portProperty lv2:toggled ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
pipedal_ui:ledColor "green";
|
||||
pg:group myprefix:loop_main_group ;
|
||||
],
|
||||
[
|
||||
a lv2:OutputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 5;
|
||||
lv2:symbol "position1" ;
|
||||
rdfs:comment "Time";
|
||||
lv2:name "\u00A0\u00A0\u00A0\u00A0";
|
||||
units:unit units:pc ;
|
||||
|
||||
lv2:default 0.0 ;
|
||||
lv2:minimum 0.0 ;
|
||||
lv2:maximum 100.0 ;
|
||||
pg:group myprefix:loop_main_group ;
|
||||
],
|
||||
|
||||
|
||||
################# Loop2
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 6;
|
||||
lv2:symbol "stop2" ;
|
||||
lv2:name "⏹";
|
||||
rdfs:comment "Reset loop 2.";
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
lv2:portProperty lv2:toggled,pprop:trigger;
|
||||
pg:group myprefix:loop2_group ;
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 7;
|
||||
lv2:symbol "record2" ;
|
||||
lv2:name "⏺";
|
||||
rdfs:comment "Record/Overdub loop 2";
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
lv2:portProperty lv2:toggled,pprop:trigger;
|
||||
pg:group myprefix:loop2_group ;
|
||||
],
|
||||
[
|
||||
a lv2:OutputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 8;
|
||||
lv2:symbol "record_led2" ;
|
||||
lv2:name "";
|
||||
lv2:portProperty lv2:toggled ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
pipedal_ui:ledColor "red";
|
||||
pg:group myprefix:loop2_group ;
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 9;
|
||||
lv2:symbol "play2" ;
|
||||
lv2:name "⏵";
|
||||
rdfs:comment "Play Loop 2. Click again to stop.";
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
lv2:portProperty lv2:toggled,pprop:trigger;
|
||||
pg:group myprefix:loop2_group ;
|
||||
],
|
||||
[
|
||||
a lv2:OutputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 10;
|
||||
lv2:symbol "play_led2" ;
|
||||
lv2:name "";
|
||||
lv2:portProperty lv2:toggled ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
pipedal_ui:ledColor "green";
|
||||
pg:group myprefix:loop2_group ;
|
||||
],
|
||||
[
|
||||
a lv2:OutputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 11;
|
||||
lv2:symbol "position2" ;
|
||||
rdfs:comment "Time";
|
||||
lv2:name "\u00A0\u00A0\u00A0\u00A0";
|
||||
units:unit units:pc ;
|
||||
|
||||
lv2:default 0.0 ;
|
||||
lv2:minimum 0.0 ;
|
||||
lv2:maximum 100.0 ;
|
||||
pg:group myprefix:loop2_group ;
|
||||
],
|
||||
|
||||
################# Loop3
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 12;
|
||||
lv2:symbol "stop3" ;
|
||||
lv2:name "⏹";
|
||||
rdfs:comment "Reset loop 3.";
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
lv2:portProperty lv2:toggled,pprop:trigger;
|
||||
pg:group myprefix:loop3_group ;
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 13;
|
||||
lv2:symbol "record3" ;
|
||||
lv2:name "⏺";
|
||||
rdfs:comment "Record/Overdub loop 3";
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
lv2:portProperty lv2:toggled,pprop:trigger;
|
||||
pg:group myprefix:loop3_group ;
|
||||
],
|
||||
[
|
||||
a lv2:OutputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 14;
|
||||
lv2:symbol "record_led3" ;
|
||||
lv2:name "";
|
||||
lv2:portProperty lv2:toggled ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
pipedal_ui:ledColor "red";
|
||||
pg:group myprefix:loop3_group ;
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 15;
|
||||
lv2:symbol "play3" ;
|
||||
lv2:name "⏵";
|
||||
rdfs:comment "Play Loop 3. Click again to stop.";
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
lv2:portProperty lv2:toggled,pprop:trigger;
|
||||
pg:group myprefix:loop3_group ;
|
||||
],
|
||||
[
|
||||
a lv2:OutputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 16;
|
||||
lv2:symbol "play_led3" ;
|
||||
lv2:name "";
|
||||
lv2:portProperty lv2:toggled ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
pipedal_ui:ledColor "green";
|
||||
pg:group myprefix:loop3_group ;
|
||||
],
|
||||
[
|
||||
a lv2:OutputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 17;
|
||||
lv2:symbol "position3" ;
|
||||
rdfs:comment "Time";
|
||||
lv2:name "\u00A0\u00A0\u00A0\u00A0";
|
||||
units:unit units:pc ;
|
||||
|
||||
lv2:default 0.0 ;
|
||||
lv2:minimum 0.0 ;
|
||||
lv2:maximum 100.0 ;
|
||||
pg:group myprefix:loop3_group ;
|
||||
],
|
||||
|
||||
|
||||
################# Loop4
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 18;
|
||||
lv2:symbol "stop4" ;
|
||||
lv2:name "⏹";
|
||||
rdfs:comment "Reset loop 4.";
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
lv2:portProperty lv2:toggled,pprop:trigger;
|
||||
pg:group myprefix:loop4_group ;
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 19;
|
||||
lv2:symbol "record4" ;
|
||||
lv2:name "⏺";
|
||||
rdfs:comment "Record/Overdub loop 4";
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
lv2:portProperty lv2:toggled,pprop:trigger;
|
||||
pg:group myprefix:loop4_group ;
|
||||
],
|
||||
[
|
||||
a lv2:OutputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 20;
|
||||
lv2:symbol "record_led4" ;
|
||||
lv2:name "";
|
||||
lv2:portProperty lv2:toggled ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
pipedal_ui:ledColor "red";
|
||||
pg:group myprefix:loop4_group ;
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 21;
|
||||
lv2:symbol "play4" ;
|
||||
lv2:name "⏵";
|
||||
rdfs:comment "Play Loop 4. Click again to stop.";
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
lv2:portProperty lv2:toggled,pprop:trigger;
|
||||
pg:group myprefix:loop4_group ;
|
||||
],
|
||||
[
|
||||
a lv2:OutputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 22;
|
||||
lv2:symbol "play_led4" ;
|
||||
lv2:name "";
|
||||
lv2:portProperty lv2:toggled ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
pipedal_ui:ledColor "green";
|
||||
pg:group myprefix:loop4_group ;
|
||||
],
|
||||
[
|
||||
a lv2:OutputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 23;
|
||||
lv2:symbol "position4" ;
|
||||
rdfs:comment "Time";
|
||||
lv2:name "\u00A0\u00A0\u00A0\u00A0";
|
||||
units:unit units:pc ;
|
||||
|
||||
lv2:default 0.0 ;
|
||||
lv2:minimum 0.0 ;
|
||||
lv2:maximum 100.0 ;
|
||||
pg:group myprefix:loop4_group ;
|
||||
],
|
||||
|
||||
|
||||
###### Sync
|
||||
[
|
||||
a lv2:OutputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 24;
|
||||
lv2:symbol "bar_led" ;
|
||||
lv2:name "\u00A0\u00A0Bar\u00A0\u00A0";
|
||||
lv2:portProperty lv2:toggled ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
pipedal_ui:ledColor "red";
|
||||
pg:group myprefix:syncGroup ;
|
||||
],
|
||||
[
|
||||
a lv2:OutputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 25;
|
||||
lv2:symbol "beat_led" ;
|
||||
lv2:name "\u00A0Beat\u00A0";
|
||||
lv2:portProperty lv2:toggled ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
pipedal_ui:ledColor "red";
|
||||
pg:group myprefix:syncGroup ;
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 26;
|
||||
lv2:symbol "tempo" ;
|
||||
lv2:name "Tempo";
|
||||
lv2:default 120.0 ;
|
||||
lv2:minimum 40.0;
|
||||
lv2:maximum 240.0;
|
||||
units:unit units:bpm;
|
||||
rdfs:comment "Tempo in quarter-notes per minute"
|
||||
; pg:group myprefix:syncGroup
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 27;
|
||||
lv2:symbol "timesig" ;
|
||||
rdfs:comment "Time Signature";
|
||||
lv2:name "TimeSig";
|
||||
lv2:default 2 ;
|
||||
lv2:minimum 0;
|
||||
lv2:maximum 5;
|
||||
lv2:portProperty lv2:enumeration ;
|
||||
|
||||
lv2:scalePoint [
|
||||
rdfs:label "2/2" ;
|
||||
rdf:value 0
|
||||
] , [
|
||||
rdfs:label "3/4" ;
|
||||
rdf:value 1
|
||||
] , [
|
||||
rdfs:label "4/4" ;
|
||||
rdf:value 2
|
||||
] ,
|
||||
[
|
||||
rdfs:label "5/4" ;
|
||||
rdf:value 3
|
||||
] ,
|
||||
[
|
||||
rdfs:label "6/8" ;
|
||||
rdf:value 4
|
||||
] ,
|
||||
[
|
||||
rdfs:label "7/4" ;
|
||||
rdf:value 5
|
||||
];
|
||||
pg:group myprefix:syncGroup
|
||||
],
|
||||
|
||||
########## Options
|
||||
|
||||
[
|
||||
lv2:name "Count In" ;
|
||||
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 28;
|
||||
lv2:symbol "rec_count_in" ;
|
||||
lv2:default 1.0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
lv2:portProperty lv2:toggled;
|
||||
rdfs:comment "Whether to count in when starting the main loop.";
|
||||
|
||||
pg:group myprefix:options_group ;
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 29;
|
||||
lv2:symbol "bars" ;
|
||||
lv2:name "Bars";
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0;
|
||||
lv2:maximum 32;
|
||||
units:unit units:bar ;
|
||||
lv2:portProperty lv2:integer;
|
||||
rdfs:comment "Loop length in bars. Set to minimum for arbitrary length loops.";
|
||||
pg:group myprefix:options_group ;
|
||||
lv2:scalePoint [
|
||||
rdfs:label "Free" ;
|
||||
rdf:value 0.0
|
||||
]
|
||||
],
|
||||
[
|
||||
lv2:name "Rec Sync" ;
|
||||
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 30;
|
||||
lv2:symbol "rec_sync_option" ;
|
||||
lv2:default 1.0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
lv2:portProperty lv2:toggled;
|
||||
rdfs:comment "Whether to sync the start of the loop with the main loop when recording secondary loops.";
|
||||
|
||||
pg:group myprefix:options_group ;
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 31;
|
||||
lv2:symbol "loop_end_option" ;
|
||||
lv2:name "On Rec End";
|
||||
lv2:default 1.0 ;
|
||||
lv2:minimum 1.0;
|
||||
lv2:maximum 2.0;
|
||||
lv2:portProperty lv2:enumeration;
|
||||
rdfs:comment "(One-control MIDI operation only) Whether to go to OVERDUB or PLAY state when recording of the main loop ends.";
|
||||
|
||||
lv2:scalePoint [
|
||||
rdfs:label "REC->PLAY" ;
|
||||
rdf:value 1.0
|
||||
] , [
|
||||
rdfs:label "REC->DUB" ;
|
||||
rdf:value 2.0
|
||||
] ;
|
||||
|
||||
pg:group myprefix:options_group ;
|
||||
],
|
||||
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 32;
|
||||
lv2:symbol "level" ;
|
||||
lv2:name "Level";
|
||||
lv2:name "Level";
|
||||
pg:group myprefix:output_group ;
|
||||
|
||||
lv2:default 0 ;
|
||||
lv2:minimum -60.0;
|
||||
lv2:maximum 30.0;
|
||||
lv2:scalePoint [
|
||||
rdfs:label "-INF" ;
|
||||
rdf:value -60.0
|
||||
];
|
||||
units:unit units:db
|
||||
],
|
||||
|
||||
################# Audio/Control Busses
|
||||
|
||||
[
|
||||
a lv2:AudioPort ,
|
||||
lv2:InputPort ;
|
||||
lv2:index 33 ;
|
||||
lv2:symbol "in" ;
|
||||
lv2:name "In";
|
||||
pg:group myprefix:stereoInGroup ;
|
||||
lv2:designation pg:left
|
||||
|
||||
],
|
||||
[
|
||||
a lv2:AudioPort ,
|
||||
lv2:OutputPort ;
|
||||
lv2:index 34 ;
|
||||
lv2:symbol "out" ;
|
||||
lv2:name "Out" ;
|
||||
pg:group myprefix:stereoOutGroup ;
|
||||
lv2:designation pg:left
|
||||
],
|
||||
|
||||
[
|
||||
a atom:AtomPort ,
|
||||
lv2:InputPort ;
|
||||
lv2:index 35 ;
|
||||
lv2:symbol "controlIn" ;
|
||||
lv2:name "ControlIn"
|
||||
],
|
||||
[
|
||||
a atom:AtomPort ,
|
||||
lv2:OutputPort ;
|
||||
lv2:index 36 ;
|
||||
lv2:symbol "controlOut" ;
|
||||
lv2:name "ControlOut"
|
||||
],
|
||||
[
|
||||
a lv2:AudioPort ,
|
||||
lv2:InputPort ;
|
||||
lv2:index 37;
|
||||
lv2:symbol "inR" ;
|
||||
lv2:name "InR" ;
|
||||
pg:group myprefix:stereoInGroup ;
|
||||
lv2:designation pg:right
|
||||
|
||||
],
|
||||
[
|
||||
a lv2:AudioPort ,
|
||||
lv2:OutputPort ;
|
||||
lv2:index 38 ;
|
||||
lv2:symbol "outR" ;
|
||||
lv2:name "OutR" ;
|
||||
pg:group myprefix:stereoOutGroup ;
|
||||
lv2:designation pg:right
|
||||
]
|
||||
|
||||
|
||||
;
|
||||
|
||||
.
|
||||
|
||||
|
||||
|
||||
<http://two-play.com/plugins/toob-record-stereo-ui>
|
||||
a ui:X11UI ;
|
||||
lv2:binary <.so>;
|
||||
|
||||
lv2:extensionData ui:resize ;
|
||||
lv2:extensionData ui:idleInterface ;
|
||||
lv2:requiredFeature ui:idleInterface ;
|
||||
.
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ toobml:sagGroup
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
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.
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
rdfs:comment """
|
||||
Remix a stereo input signal.
|
||||
|
||||
|
||||
@@ -61,36 +61,66 @@ toobNam:eqGroup
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
rdfs:comment """
|
||||
A port of Steven Atkinson's Neural Amp Modeler to LV2.
|
||||
TooB Neural Amp Modeler is a neural network based Amp Simulator. It uses .nam model files that are generated by training nueral networks
|
||||
on audio recordings of actual guitar amps, effects pedals and other equipment. .nam files contain data from the trained models, which can be loaded into
|
||||
TooB Neural Amp Modeler. The resulting models produce startlingly realistic emulations of the original equipment.
|
||||
|
||||
TooB Neural Amp Modeler uses uploadable .nam model files. Download .nam model files from http://tone3000.com, and then load them into TooB Neural Amp Modeler.
|
||||
The .nam file format (and the NAM technology itself) was developed by the open-source Neural Amp Modeler Core project. The NAM Core project provides both code that can be used by Neural Amp Modeler
|
||||
plugins, and code that can be used to train models for use in NAM plugins. Currently, NAM plugins are available on a wide variety of operating systems and audio platforms, and are
|
||||
increasingly being used in commercial pedals and amps.
|
||||
|
||||
The TONE3000 website contains a huge collection of community-developed amp models that can be used with TooB Neural Amp Modeler. You can also use .nam models from
|
||||
other sources. Using TONE3000 models with TooB Neural Amp Modeler is a two-step process. First, download model files to your local system using a
|
||||
Although NAM is, first and foremost, used to model guitar amps, it is equally good at emulating a wide variety of other guitar effects. Overdrive and distortion pedals,
|
||||
compressors, limiters, tube preamps on studio mixing strips, and many other effects can be simulated extremely well using NAM models. NAM will definitely become
|
||||
your go-to source for great amp emulations; but it should probably also become your go-to source for emulating distorion and overdrive pedals. And developers
|
||||
are constantly coming up with new and ingenious applications for NAM models.
|
||||
|
||||
.nam model files can be donwloaded from the internet. There are many online sources for .nam model files, both free, and commercial. Foremost among these is the
|
||||
http://tone3000.com website, which provides a vast collection of free .nam models files that have been developed and uploaded by a large online community of NAM users.
|
||||
|
||||
Loading .nam model files into PiPedal is a two-step process. First, download model files to your local computer, or your phone or tablet, using a
|
||||
web browser. Then upload the model files to the PiPedal server using the PiPedal web interface. You can find an "Upload" button in
|
||||
the file browser when you click on the "Model" control in the PiPedal web interface.
|
||||
|
||||
the file browser when you click on the "Model" control in the plugin's user interface within the PiPedal web interface.
|
||||
|
||||
If you are not using PiPedal, just click on the Model control, and use the file browser to select the .nam file directly on your local system.
|
||||
|
||||
TooB Neural Amp Modeler supports a much wider range of amp models than ToobML, but usually uses more CPU.
|
||||
You will need at least a Pi 4 to use Toob Neural Amp Modeler, and you may need to increase your audio buffer sizes to prevent overruns.
|
||||
If you are having trouble with CPU usage, tone3000.com does contain some smaller amp models. Search for the "feather" tag to find
|
||||
models that use less CPU. Generally, you can use up to three NAM models at once in a given preset on a Pi 4, and up to 5 MAM models
|
||||
simultaneously on a Pi 5. N100 micro PCs could presumably do even better. However, exact CPU use varies depending on the complexity of the amp models you are using.
|
||||
Users have reported success using NAM on Raspberry Pi 3 devices, but this is not recommended (or really supported), as CPU usage is very high on a Raspberry Pi 3.
|
||||
models that use less CPU. Generally, you can use up to three NAM plugins at once in a given preset on a Pi 4, and up to 16 NAM plugins
|
||||
simultaneously on a Pi 5. However, exact CPU use varies depending on the complexity of the amp models you are using.
|
||||
|
||||
If you are interested in profiling your own amps and effect pedals, please visit https://www.tone3000.com/capture
|
||||
|
||||
TooB Neural Amp Modeler contains code optimizations that allow use of a third model on Raspberry PI 4 devices. These optimizations
|
||||
were offered to the upstream NAM project, but we recommended that they not be merged into upstream sources, because they
|
||||
significantly affect maintainability of the codebase. and the performance increase they provide (about 30%) is really only
|
||||
relevant on Raspberry PI 4-class devices. If you are interested in these optimizations, they have been published in ToobAmp
|
||||
project sources on GitHub.
|
||||
When the Threaded control is on, neural amp modelling calculations are run on a separate thread from the
|
||||
real-time audio thread. This introduces one extra audio buffer of latency, but allows the plugin to make full
|
||||
use of all available processor cores on your Pi server. On a Pi 5, you can run more instance than you
|
||||
could ever practically use when using threading; on a Pi 4 you can run 5 simultanous instances when using threading.
|
||||
|
||||
TooB Neural Amp Modeler uses code from the NeuralAmp Modeler Core project (https://www.neuralampmodeler.com/). The TooB team wishes
|
||||
to express gratitude to Steven Atkinson for making this revolutionary technology available as open-source code.
|
||||
When using more than a couple of NAM instances, you need to check that the PiPedal server's CPU is not temperature throttling
|
||||
when using this feature.
|
||||
If the CPU temperature exceeds 80 degrees Celcius on a Raspberry Pi, the CPU clock rate is reduced, and
|
||||
the performance of the plugins will be affected. Having a good heat sink, or active cooling system is recommended when using more than a couple of
|
||||
NAM instances. PiPedal displays the current CPU temperature in a status message at the bottom of the display (unless you've turned
|
||||
that off in settings), and also displays the current CPU temperature in the status message found at the top
|
||||
of the Settings page. If you are running on a system that is not a Raspberry Pi, please consult your manufacturer's
|
||||
help documentation to find out what temperature heat throttling starts at on your particular computer, if you really need to know
|
||||
what the threshold is; but the effect on performance is pretty obvious when heat throttling does start.
|
||||
|
||||
Note that heat throttling is done to protect your processor from permanent damage due to overheating; but there's no real risk of actual
|
||||
damage, since heat throttling thresholds are set well below the point at which damage occurs.
|
||||
|
||||
TooB Neural Amp Modeler is based on technology developed by the NeuralAmp Modeler Core project (https://www.neuralampmodeler.com/). The TooB team wishes
|
||||
to express gratitude to Steven Atkinson for making this revolutionary technology available as open-source code. This is groundbreaking
|
||||
technology that changes everything. None of this would have happened without the work that Steven has done.
|
||||
|
||||
TooB Neural Amp Modeler uses code from the NeuralAudio project (https://www.neuralampmodeler.com/). The Neural Audio project provides highly
|
||||
optimized implementations of Neural Amp models. The TooB team wishes to express gratitude to Mike Oliphant for
|
||||
making his optimizations work available as open-source code.
|
||||
|
||||
Steve and Mike have done all of the hard work that was required to make NAM technology available through TooB Neural Amp Modeler. They
|
||||
deserve all the credit.
|
||||
|
||||
Code from the the NeuralAmpModelerCore project (https://github.com/sdatkinson/NeuralAmpModelerCore) is provided
|
||||
under the following license.
|
||||
@@ -117,6 +147,30 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
Code from the the NeuralAudio project (https://github.com/sdatkinson/NeuralAmpModelerCore) is provided
|
||||
under the following license.
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Mike Oliphant
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
""" ;
|
||||
|
||||
@@ -180,6 +234,21 @@ SOFTWARE.
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 3;
|
||||
lv2:symbol "buffer" ;
|
||||
lv2:name "Threaded";
|
||||
lv2:default 0.0 ;
|
||||
lv2:minimum 0.0;
|
||||
lv2:maximum 1.0;
|
||||
lv2:portProperty lv2:toggled;
|
||||
rdfs:comment "Run computation on a separate thread. This allows more instances of NAM to run simultaneously, but adds one extra audio buffer of latency.";
|
||||
rdfs:comment "Output gain";
|
||||
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 4;
|
||||
lv2:symbol "gate" ;
|
||||
lv2:name "Noise Gate";
|
||||
lv2:default -100.0 ;
|
||||
@@ -198,7 +267,7 @@ SOFTWARE.
|
||||
a lv2:OutputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 4;
|
||||
lv2:index 5;
|
||||
lv2:symbol "gateOut" ;
|
||||
lv2:name "\u00A0";
|
||||
lv2:default 0.0;
|
||||
@@ -212,7 +281,7 @@ SOFTWARE.
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 5 ;
|
||||
lv2:index 6 ;
|
||||
lv2:symbol "toneStack" ;
|
||||
lv2:name "Type";
|
||||
lv2:default 3.0 ;
|
||||
@@ -241,7 +310,7 @@ SOFTWARE.
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 6;
|
||||
lv2:index 7;
|
||||
lv2:symbol "bass" ;
|
||||
lv2:name "Bass";
|
||||
lv2:default 5.0;
|
||||
@@ -255,7 +324,7 @@ SOFTWARE.
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 7;
|
||||
lv2:index 8;
|
||||
lv2:symbol "mid" ;
|
||||
lv2:name "Mid";
|
||||
lv2:default 5.0;
|
||||
@@ -268,7 +337,7 @@ SOFTWARE.
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 8;
|
||||
lv2:index 9;
|
||||
lv2:symbol "treble" ;
|
||||
lv2:name "Treble";
|
||||
lv2:default 5.0;
|
||||
@@ -280,14 +349,14 @@ SOFTWARE.
|
||||
[
|
||||
a lv2:AudioPort ,
|
||||
lv2:InputPort ;
|
||||
lv2:index 9 ;
|
||||
lv2:index 10 ;
|
||||
lv2:symbol "in" ;
|
||||
lv2:name "In"
|
||||
],
|
||||
[
|
||||
a lv2:AudioPort ,
|
||||
lv2:OutputPort ;
|
||||
lv2:index 10 ;
|
||||
lv2:index 11 ;
|
||||
lv2:symbol "out" ;
|
||||
lv2:name "Out"
|
||||
],
|
||||
@@ -299,7 +368,7 @@ SOFTWARE.
|
||||
lv2:designation lv2:control ;
|
||||
atom:supports patch:Message ;
|
||||
|
||||
lv2:index 11 ;
|
||||
lv2:index 12 ;
|
||||
lv2:symbol "control" ;
|
||||
lv2:name "Control" ;
|
||||
rdfs:comment "Control" ;
|
||||
@@ -309,7 +378,7 @@ SOFTWARE.
|
||||
atom:bufferType atom:Sequence ;
|
||||
# atom:supports patch:Message;
|
||||
lv2:designation lv2:control ;
|
||||
lv2:index 12;
|
||||
lv2:index 13;
|
||||
lv2:symbol "notify" ;
|
||||
lv2:name "Notify" ;
|
||||
rdfs:comment "Notification" ;
|
||||
@@ -351,10 +420,9 @@ toobNam:ui
|
||||
pipedal_ui:frequencyPlot
|
||||
[
|
||||
pipedal_ui:patchProperty toobNam:FrequencyResponse;
|
||||
lv2:index 9 ;
|
||||
lv2:index 10 ;
|
||||
pg:group toobNam:eqGroup ;
|
||||
pipedal_ui:width: 200;
|
||||
]
|
||||
|
||||
.
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ noisegate:envelope_group
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
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.
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
rdfs:comment """
|
||||
A loose emulation of an MXR® Phase 90 Phaser.
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ toobPlayer:seek
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
rdfs:comment """
|
||||
Play audio files. Audio files can optionally be loooped by pressing the "Set loop" button.
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ recordPrefix:audioFile
|
||||
doap:license <https://opensource.org/license/mit/> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 1 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
|
||||
ui:ui <http://two-play.com/plugins/toob-record-mono-ui>;
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ myprefix:loop3_group
|
||||
doap:license <https://opensource.org/license/mit/> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 1 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
|
||||
ui:ui <http://two-play.com/plugins/toob-record-stereo-ui>;
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
rdfs:comment """
|
||||
TooB Tuner is a chromatic guitar tuner.
|
||||
""" ;
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 64 ;
|
||||
lv2:microVersion 66 ;
|
||||
rdfs:comment """
|
||||
Volume control.
|
||||
|
||||
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
+170
-72
@@ -245,6 +245,9 @@ namespace pipedal
|
||||
class AlsaDriverImpl : public AudioDriver
|
||||
{
|
||||
private:
|
||||
void TraceBuffers(size_t framesInBuffers, char code = ' ');
|
||||
std::recursive_mutex restartMutex;
|
||||
|
||||
pipedal::CpuUse cpuUse;
|
||||
|
||||
#ifdef ALSADRIVER_CONFIG_DBG
|
||||
@@ -378,16 +381,21 @@ namespace pipedal
|
||||
}
|
||||
|
||||
private:
|
||||
void AlsaCleanup()
|
||||
void AlsaCloseAudio()
|
||||
{
|
||||
std::lock_guard lock{restartMutex};
|
||||
|
||||
if (captureHandle)
|
||||
{
|
||||
Lv2Log::debug("ALSA capture handle closed.");
|
||||
snd_pcm_drain(captureHandle);
|
||||
snd_pcm_close(captureHandle);
|
||||
captureHandle = nullptr;
|
||||
}
|
||||
if (playbackHandle)
|
||||
{
|
||||
Lv2Log::debug("ALSA playback handle closed.");
|
||||
snd_pcm_drain(playbackHandle);
|
||||
snd_pcm_close(playbackHandle);
|
||||
playbackHandle = nullptr;
|
||||
}
|
||||
@@ -411,7 +419,10 @@ namespace pipedal
|
||||
snd_pcm_sw_params_free(playbackSwParams);
|
||||
playbackSwParams = nullptr;
|
||||
}
|
||||
this->alsaSequencer = nullptr;
|
||||
}
|
||||
void AlsaCleanup()
|
||||
{
|
||||
AlsaCloseAudio();
|
||||
}
|
||||
|
||||
std::string discover_alsa_using_apps()
|
||||
@@ -1085,6 +1096,40 @@ namespace pipedal
|
||||
}
|
||||
}
|
||||
|
||||
void RestartAlsa()
|
||||
{
|
||||
std::lock_guard lock{restartMutex};
|
||||
Lv2Log::info("Restarting ALSA devices.");
|
||||
|
||||
try
|
||||
{
|
||||
AlsaCloseAudio();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error(SS("Error cleaning up ALSA: " << e.what()));
|
||||
throw std::runtime_error("Unable to restart the audio stream.");
|
||||
}
|
||||
try
|
||||
{
|
||||
OpenAudio(this->jackServerSettings, this->channelSelection);
|
||||
validate_capture_handle();
|
||||
FillOutputBuffer();
|
||||
audioRunning = true;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error(SS("Error opening ALSA: " << e.what()));
|
||||
throw std::runtime_error("Unable to restart the audio stream.");
|
||||
}
|
||||
int err;
|
||||
if ((err = snd_pcm_start(captureHandle)) < 0)
|
||||
{
|
||||
Lv2Log::error(SS("Unable to restart ALSA capture: " << snd_strerror(err)));
|
||||
throw PiPedalStateException("Unable to restart ALSA capture.");
|
||||
}
|
||||
}
|
||||
|
||||
void PrepareCaptureFunctions(snd_pcm_format_t captureFormat)
|
||||
{
|
||||
this->captureFormat = captureFormat;
|
||||
@@ -1221,6 +1266,8 @@ namespace pipedal
|
||||
|
||||
void OpenAudio(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
|
||||
{
|
||||
std::lock_guard lock{restartMutex};
|
||||
|
||||
int err;
|
||||
|
||||
alsa_device_name = jackServerSettings.GetAlsaInputDevice();
|
||||
@@ -1245,7 +1292,7 @@ namespace pipedal
|
||||
{
|
||||
message =
|
||||
SS("Device " << alsa_device_name << " in use. The following applications are using your soundcard: " << apps
|
||||
<< ". Stop them as neccesary before trying to pipedald.");
|
||||
<< ". Stop them as neccesary before trying to start pipedald.");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1370,7 +1417,7 @@ namespace pipedal
|
||||
{
|
||||
throw PiPedalStateException(SS("Audio playback failed. " << snd_strerror(err)));
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
continue;
|
||||
}
|
||||
if (avail == 0)
|
||||
@@ -1386,94 +1433,106 @@ namespace pipedal
|
||||
}
|
||||
validate_capture_handle();
|
||||
}
|
||||
void recover_from_output_underrun(snd_pcm_t *capture_handle, snd_pcm_t *playback_handle, int err)
|
||||
void recover_from_output_underrun(snd_pcm_t *capture_handle, snd_pcm_t *playback_handle, int err, size_t framesRead)
|
||||
{
|
||||
validate_capture_handle();
|
||||
if (err == -EPIPE)
|
||||
try
|
||||
{
|
||||
err = snd_pcm_prepare(playback_handle);
|
||||
if (err < 0)
|
||||
|
||||
TraceBuffers(framesRead,'w');
|
||||
if (err == -EPIPE)
|
||||
{
|
||||
throw PiPedalStateException(SS("Can't recover from ALSA output underrun. (" << snd_strerror(err) << ")"));
|
||||
err = snd_pcm_prepare(playback_handle);
|
||||
if (err < 0)
|
||||
{
|
||||
Lv2Log::error(SS("Can't recover from ALSA output underrun. (" << snd_strerror(err) << ")"));
|
||||
throw PiPedalStateException(SS("Can't recover from ALSA output underrun. (" << snd_strerror(err) << ")"));
|
||||
}
|
||||
snd_pcm_drain(capture_handle);
|
||||
FillOutputBuffer();
|
||||
}
|
||||
else
|
||||
{
|
||||
Lv2Log::error(SS("Can't recover from ALSA output underrun. (" << snd_strerror(err) << ")"));
|
||||
throw PiPedalStateException(SS("Can't recover from ALSA output error. (" << snd_strerror(err) << ")"));
|
||||
}
|
||||
FillOutputBuffer();
|
||||
}
|
||||
else
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
throw PiPedalStateException(SS("Can't recover from ALSA output error. (" << snd_strerror(err) << ")"));
|
||||
Lv2Log::info(SS("Soft audio restart failed. " << e.what()));
|
||||
RestartAlsa();
|
||||
audioRunning = true;
|
||||
}
|
||||
validate_capture_handle();
|
||||
}
|
||||
void recover_from_input_underrun(snd_pcm_t *capture_handle, snd_pcm_t *playback_handle, int err)
|
||||
void recover_from_input_underrun(snd_pcm_t *capture_handle, snd_pcm_t *playback_handle, int err, size_t bufferedFrames)
|
||||
{
|
||||
validate_capture_handle();
|
||||
|
||||
if (err == -EPIPE)
|
||||
try
|
||||
{
|
||||
TraceBuffers(bufferedFrames,'r');
|
||||
if (err == -EPIPE)
|
||||
{
|
||||
|
||||
// Unlink the streams before recovery
|
||||
snd_pcm_unlink(capture_handle);
|
||||
// Unlink the streams before recovery
|
||||
snd_pcm_unlink(capture_handle);
|
||||
|
||||
err = snd_pcm_drop(capture_handle);
|
||||
if (err < 0)
|
||||
{
|
||||
throw PiPedalStateException(SS("Can't recover from ALSA underrun. (" << snd_strerror(err) << ")"));
|
||||
}
|
||||
err = snd_pcm_drop(playback_handle);
|
||||
if (err < 0)
|
||||
{
|
||||
throw PiPedalStateException(SS("Can't recover from ALSA underrun. (" << snd_strerror(err) << ")"));
|
||||
}
|
||||
// Prepare both streams
|
||||
if ((err = snd_pcm_prepare(playback_handle)) < 0)
|
||||
{
|
||||
throw std::runtime_error(SS("Cannot prepare playback stream: " << snd_strerror(err)));
|
||||
}
|
||||
if ((err = snd_pcm_prepare(capture_handle)) < 0)
|
||||
{
|
||||
throw std::runtime_error(SS("Cannot prepare capture stream: " << snd_strerror(err)));
|
||||
}
|
||||
|
||||
// Prepare both streams
|
||||
if ((err = snd_pcm_prepare(playback_handle)) < 0)
|
||||
{
|
||||
throw std::runtime_error(SS("Cannot prepare playback stream: " << snd_strerror(err)));
|
||||
}
|
||||
if ((err = snd_pcm_prepare(capture_handle)) < 0)
|
||||
{
|
||||
throw std::runtime_error(SS("Cannot prepare capture stream: " << snd_strerror(err)));
|
||||
}
|
||||
// Resynchronize the streams
|
||||
if ((err = snd_pcm_link(capture_handle, playback_handle)) < 0)
|
||||
{
|
||||
throw std::runtime_error(SS("Cannot relink streams: " << snd_strerror(err)));
|
||||
}
|
||||
|
||||
// Fill the playback buffer with silence
|
||||
FillOutputBuffer();
|
||||
// Start the streams
|
||||
FillOutputBuffer();
|
||||
if ((err = snd_pcm_start(capture_handle)) < 0)
|
||||
{
|
||||
throw std::runtime_error(SS("Cannot restart capture stream: " << snd_strerror(err)));
|
||||
}
|
||||
|
||||
// Resynchronize the streams
|
||||
if ((err = snd_pcm_link(capture_handle, playback_handle)) < 0)
|
||||
{
|
||||
throw std::runtime_error(SS("Cannot relink streams: " << snd_strerror(err)));
|
||||
validate_capture_handle();
|
||||
}
|
||||
else if (err == ESTRPIPE)
|
||||
{
|
||||
audioRunning = false;
|
||||
validate_capture_handle();
|
||||
|
||||
// Start the streams
|
||||
if ((err = snd_pcm_start(capture_handle)) < 0)
|
||||
{
|
||||
throw std::runtime_error(SS("Cannot restart capture stream: " << snd_strerror(err)));
|
||||
}
|
||||
validate_capture_handle();
|
||||
}
|
||||
else if (err == ESTRPIPE)
|
||||
{
|
||||
audioRunning = false;
|
||||
validate_capture_handle();
|
||||
|
||||
while ((err = snd_pcm_resume(capture_handle)) == -EAGAIN)
|
||||
{
|
||||
sleep(1);
|
||||
}
|
||||
if (err < 0)
|
||||
{
|
||||
err = snd_pcm_prepare(capture_handle);
|
||||
while ((err = snd_pcm_resume(capture_handle)) == -EAGAIN)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
if (err < 0)
|
||||
{
|
||||
throw PiPedalStateException(SS("Can't recover from ALSA suspend. (" << snd_strerror(err) << ")"));
|
||||
err = snd_pcm_prepare(capture_handle);
|
||||
if (err < 0)
|
||||
{
|
||||
throw PiPedalStateException(SS("Can't recover from ALSA suspend. (" << snd_strerror(err) << ")"));
|
||||
}
|
||||
}
|
||||
audioRunning = true;
|
||||
validate_capture_handle();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw PiPedalStateException(SS("Can't recover from ALSA input error. (" << snd_strerror(err) << ")"));
|
||||
}
|
||||
audioRunning = true;
|
||||
validate_capture_handle();
|
||||
}
|
||||
else
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
throw std::runtime_error(SS("Can't restart audio: " << snd_strerror(err)));
|
||||
Lv2Log::info(SS("Soft audio restart failed. " << e.what()));
|
||||
RestartAlsa();
|
||||
audioRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1516,6 +1575,7 @@ namespace pipedal
|
||||
{
|
||||
snd_pcm_wait(captureHandle, 1);
|
||||
}
|
||||
TraceBuffers(framesRead);
|
||||
} while (frames > 0);
|
||||
return framesRead;
|
||||
}
|
||||
@@ -1526,6 +1586,11 @@ namespace pipedal
|
||||
AlsaMidiMessage message;
|
||||
|
||||
midiEventCount = 0;
|
||||
auto alsaSequener = this->alsaSequencer; // take an addref
|
||||
if (!alsaSequener)
|
||||
{
|
||||
return;
|
||||
}
|
||||
while (alsaSequencer->ReadMessage(message, 0))
|
||||
{
|
||||
size_t messageSize = message.size;
|
||||
@@ -1580,6 +1645,7 @@ namespace pipedal
|
||||
void AudioThread()
|
||||
{
|
||||
SetThreadName("alsaDriver");
|
||||
|
||||
try
|
||||
{
|
||||
SetThreadPriority(SchedulerPriority::RealtimeAudio);
|
||||
@@ -1597,7 +1663,7 @@ namespace pipedal
|
||||
}
|
||||
|
||||
CrashGuardLock crashGuardLock;
|
||||
|
||||
|
||||
cpuUse.SetStartTime(cpuUse.Now());
|
||||
while (true)
|
||||
{
|
||||
@@ -1628,7 +1694,7 @@ namespace pipedal
|
||||
framesToRead)) < 0)
|
||||
{
|
||||
this->driverHost->OnUnderrun();
|
||||
recover_from_input_underrun(captureHandle, playbackHandle, nFrames);
|
||||
recover_from_input_underrun(captureHandle, playbackHandle, nFrames, framesRead);
|
||||
xrun = true;
|
||||
break;
|
||||
}
|
||||
@@ -1665,7 +1731,8 @@ namespace pipedal
|
||||
if (err < 0)
|
||||
{
|
||||
this->driverHost->OnUnderrun();
|
||||
recover_from_output_underrun(captureHandle, playbackHandle, err);
|
||||
|
||||
recover_from_output_underrun(captureHandle, playbackHandle, err, framesRead);
|
||||
}
|
||||
cpuUse.AddSample(ProfileCategory::Write);
|
||||
}
|
||||
@@ -1675,11 +1742,11 @@ namespace pipedal
|
||||
Lv2Log::error(e.what());
|
||||
Lv2Log::error("ALSA audio thread terminated abnormally.");
|
||||
}
|
||||
this->driverHost->OnAlsaDriverStopped();
|
||||
|
||||
// if we terminated abnormally, pump messages until we have been terminated.
|
||||
if (!terminateAudio())
|
||||
{
|
||||
this->driverHost->OnAlsaDriverStopped();
|
||||
// zero out input buffers.
|
||||
for (size_t i = 0; i < this->captureBuffers.size(); ++i)
|
||||
{
|
||||
@@ -1694,7 +1761,6 @@ namespace pipedal
|
||||
while (!terminateAudio())
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
// zero out input buffers.
|
||||
this->driverHost->OnProcess(this->bufferSize);
|
||||
}
|
||||
}
|
||||
@@ -1766,7 +1832,7 @@ namespace pipedal
|
||||
}
|
||||
|
||||
audioThread = std::make_unique<std::jthread>([this]()
|
||||
{ AudioThread(); });
|
||||
{ AudioThread(); });
|
||||
}
|
||||
|
||||
virtual void Deactivate()
|
||||
@@ -1848,6 +1914,8 @@ namespace pipedal
|
||||
Deactivate();
|
||||
AlsaCleanup();
|
||||
DeleteBuffers();
|
||||
this->alsaSequencer = nullptr;
|
||||
|
||||
std::atomic_thread_fence(std::memory_order::release);
|
||||
}
|
||||
|
||||
@@ -1940,7 +2008,7 @@ namespace pipedal
|
||||
try
|
||||
{
|
||||
int err;
|
||||
for (int retry = 0; retry < 2; ++retry)
|
||||
for (int retry = 0; retry < 4; ++retry)
|
||||
{
|
||||
err = snd_pcm_open(&playbackHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
||||
if (err < 0) // field report of a device that is present, but won't immediately open.
|
||||
@@ -2202,6 +2270,36 @@ namespace pipedal
|
||||
#endif
|
||||
}
|
||||
|
||||
struct BufferTrace
|
||||
{
|
||||
snd_pcm_sframes_t inAvail;
|
||||
snd_pcm_sframes_t outAvail;
|
||||
snd_pcm_sframes_t buffered;
|
||||
snd_pcm_sframes_t total;
|
||||
char code;
|
||||
};
|
||||
|
||||
std::vector<BufferTrace> bufferTraces(1000);
|
||||
size_t bufferTraceIndex;
|
||||
|
||||
void AlsaDriverImpl::TraceBuffers(size_t framesInBuffer,char code)
|
||||
{
|
||||
auto inAvail = snd_pcm_avail_update(this->captureHandle);
|
||||
auto outAvail = snd_pcm_avail_update(this->playbackHandle);
|
||||
|
||||
auto total = inAvail + outAvail + framesInBuffer;
|
||||
bufferTraces[bufferTraceIndex++] = {
|
||||
inAvail,
|
||||
outAvail,
|
||||
(snd_pcm_sframes_t)framesInBuffer,
|
||||
(snd_pcm_sframes_t)total,
|
||||
code};
|
||||
if (bufferTraceIndex == bufferTraces.size())
|
||||
{
|
||||
bufferTraceIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void FreeAlsaGlobals()
|
||||
{
|
||||
snd_config_update_free_global(); // to get a clean Valgrind report.
|
||||
|
||||
+4
-2
@@ -34,6 +34,7 @@
|
||||
#include "PluginHost.hpp"
|
||||
#include "PatchPropertyWriter.hpp"
|
||||
#include "CpuTemperatureMonitor.hpp"
|
||||
#include "restrict.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
@@ -618,7 +619,7 @@ private:
|
||||
{
|
||||
for (size_t i = 0; i < audioDriver->OutputBufferCount(); ++i)
|
||||
{
|
||||
float *out = (float *)audioDriver->GetOutputBuffer(i);
|
||||
float * out = (float *)audioDriver->GetOutputBuffer(i);
|
||||
if (out)
|
||||
{
|
||||
ZeroBuffer(out, nframes);
|
||||
@@ -1141,7 +1142,7 @@ private:
|
||||
{
|
||||
try
|
||||
{
|
||||
float *in, *out;
|
||||
float * restrict in , * restrict out;
|
||||
|
||||
Lv2Pedalboard *pedalboard = nullptr;
|
||||
pedalboard = this->realtimeActivePedalboard;
|
||||
@@ -1724,6 +1725,7 @@ public:
|
||||
{
|
||||
Close();
|
||||
active = false;
|
||||
isOpen = false;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
+66
-28
@@ -40,6 +40,40 @@
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
int avahiPollLockCount = 0;
|
||||
static std::mutex avahiLockMutex;
|
||||
class AvahiPollLock {
|
||||
public:
|
||||
AvahiPollLock() = delete;
|
||||
AvahiPollLock(AvahiThreadedPoll*threadedPoll)
|
||||
: threadedPoll(threadedPoll) {
|
||||
std::lock_guard<std::mutex> lock(avahiLockMutex);
|
||||
if (!threadedPoll) {
|
||||
throw std::runtime_error("AvahiPollLock: threadedPoll is null.");
|
||||
}
|
||||
if (++avahiPollLockCount > 1) {
|
||||
throw std::runtime_error("AvahiPollLock: nested locks are not allowed.");
|
||||
}
|
||||
avahi_threaded_poll_lock(threadedPoll);
|
||||
}
|
||||
void release() {
|
||||
std::lock_guard<std::mutex> lock(avahiLockMutex);
|
||||
if (threadedPoll)
|
||||
{
|
||||
if (avahiPollLockCount <= 0) {
|
||||
throw std::runtime_error("AvahiPollLock: release called without a lock.");
|
||||
}
|
||||
--avahiPollLockCount;
|
||||
avahi_threaded_poll_unlock(threadedPoll);
|
||||
threadedPoll = nullptr;
|
||||
}
|
||||
}
|
||||
~AvahiPollLock() {
|
||||
release();
|
||||
}
|
||||
private:
|
||||
AvahiThreadedPoll *threadedPoll;
|
||||
};
|
||||
void AvahiService::Announce(
|
||||
int portNumber,
|
||||
const std::string &name,
|
||||
@@ -75,7 +109,7 @@ void AvahiService::Announce(
|
||||
else
|
||||
{
|
||||
// already started, so we have to use poll_lock instead.
|
||||
avahi_threaded_poll_lock(threadedPoll);
|
||||
AvahiPollLock lock(threadedPoll);
|
||||
|
||||
// all state that we have to protect with the lock now that the avahi serv3ce is running.
|
||||
this->portNumber = portNumber;
|
||||
@@ -104,7 +138,7 @@ void AvahiService::Announce(
|
||||
Lv2Log::error("Failed to update mDNS service because of a synch problem.");
|
||||
}
|
||||
}
|
||||
avahi_threaded_poll_unlock(threadedPoll);
|
||||
lock.release();
|
||||
if (wait)
|
||||
{
|
||||
Wait();
|
||||
@@ -118,30 +152,31 @@ void AvahiService::Unannounce(bool wait)
|
||||
return;
|
||||
if (this->threadedPoll)
|
||||
{
|
||||
avahi_threaded_poll_lock(threadedPoll);
|
||||
if (started)
|
||||
{
|
||||
if (this->createPending)
|
||||
AvahiPollLock lock(threadedPoll);
|
||||
if (started)
|
||||
{
|
||||
// if service is starting up, and we haven't yet made our first announcement,
|
||||
// just signal that we don't want to announc.
|
||||
this->makeAnnouncement = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->makeAnnouncement = false;
|
||||
// we have previously made a successful announcement. Retract it.
|
||||
if (group)
|
||||
if (this->createPending)
|
||||
{
|
||||
int rc = avahi_entry_group_reset(group);
|
||||
if (rc < 0)
|
||||
// if service is starting up, and we haven't yet made our first announcement,
|
||||
// just signal that we don't want to announc.
|
||||
this->makeAnnouncement = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->makeAnnouncement = false;
|
||||
// we have previously made a successful announcement. Retract it.
|
||||
if (group)
|
||||
{
|
||||
Lv2Log::error("Avahi: failed to un-announce.");
|
||||
int rc = avahi_entry_group_reset(group);
|
||||
if (rc < 0)
|
||||
{
|
||||
Lv2Log::error("Avahi: failed to un-announce.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
avahi_threaded_poll_unlock(threadedPoll);
|
||||
if (wait)
|
||||
{
|
||||
WaitForUnannounce();
|
||||
@@ -411,6 +446,7 @@ void AvahiService::Start()
|
||||
|
||||
if (!client)
|
||||
{
|
||||
AvahiPollLock lock(threadedPoll);
|
||||
/* Allocate a new client */
|
||||
client = avahi_client_new(avahi_threaded_poll_get(threadedPoll), AvahiClientFlags::AVAHI_CLIENT_NO_FAIL, client_callback, (void *)this, &error);
|
||||
/* Check wether creating the client object succeeded */
|
||||
@@ -442,20 +478,22 @@ void AvahiService::Stop()
|
||||
|
||||
if (threadedPoll)
|
||||
{
|
||||
avahi_threaded_poll_lock(threadedPoll);
|
||||
if (group)
|
||||
{
|
||||
avahi_entry_group_free(group);
|
||||
group = nullptr;
|
||||
AvahiPollLock lock(threadedPoll);
|
||||
if (group)
|
||||
{
|
||||
avahi_entry_group_free(group);
|
||||
group = nullptr;
|
||||
}
|
||||
}
|
||||
avahi_threaded_poll_unlock(threadedPoll);
|
||||
avahi_threaded_poll_lock(threadedPoll);
|
||||
if (client)
|
||||
{
|
||||
avahi_client_free(client);
|
||||
client = nullptr;
|
||||
AvahiPollLock lock(threadedPoll);
|
||||
if (client)
|
||||
{
|
||||
avahi_client_free(client);
|
||||
client = nullptr;
|
||||
}
|
||||
}
|
||||
avahi_threaded_poll_unlock(threadedPoll);
|
||||
|
||||
avahi_threaded_poll_stop(threadedPoll);
|
||||
avahi_threaded_poll_free(threadedPoll);
|
||||
|
||||
@@ -46,7 +46,6 @@ namespace pipedal {
|
||||
virtual int GetNumberOfInputAudioChannels() const = 0;
|
||||
virtual int GetNumberOfOutputAudioChannels() const = 0;
|
||||
virtual std::shared_ptr<Lv2PluginInfo> GetPluginInfo(const std::string &uri) const = 0;
|
||||
virtual std::shared_ptr<HostWorkerThread> GetHostWorkerThread() = 0;
|
||||
|
||||
virtual IEffect *CreateEffect(PedalboardItem &pedalboard) = 0;
|
||||
|
||||
|
||||
+26
-21
@@ -18,6 +18,7 @@
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "pch.h"
|
||||
#include "restrict.hpp"
|
||||
#include "Lv2Effect.hpp"
|
||||
#include "PiPedalException.hpp"
|
||||
#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
|
||||
@@ -41,6 +42,7 @@
|
||||
#include "AudioHost.hpp"
|
||||
#include <exception>
|
||||
#include "RingBufferReader.hpp"
|
||||
#include "Worker.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
namespace fs = std::filesystem;
|
||||
@@ -70,6 +72,11 @@ Lv2Effect::Lv2Effect(
|
||||
|
||||
this->bypass = pedalboardItem.isEnabled();
|
||||
|
||||
this->workerThread = std::make_unique<HostWorkerThread>();
|
||||
if (info_->WantsWorkerThread())
|
||||
{
|
||||
workerThread->StartThread();
|
||||
}
|
||||
// stash a list of known file properties that we want to keep synced.
|
||||
if (info->piPedalUI())
|
||||
{
|
||||
@@ -179,10 +186,8 @@ Lv2Effect::Lv2Effect(
|
||||
const LV2_Worker_Interface *worker_interface =
|
||||
(const LV2_Worker_Interface *)lilv_instance_get_extension_data(pInstance,
|
||||
LV2_WORKER__interface);
|
||||
if (worker_interface)
|
||||
{
|
||||
this->worker = std::make_unique<Worker>(pHost->GetHostWorkerThread(), pInstance, worker_interface);
|
||||
}
|
||||
this->worker = std::make_unique<Worker>(workerThread, pInstance, worker_interface);
|
||||
|
||||
const LV2_State_Interface *state_interface =
|
||||
(const LV2_State_Interface *)lilv_instance_get_extension_data(pInstance,
|
||||
LV2_STATE__interface);
|
||||
@@ -510,15 +515,15 @@ int Lv2Effect::GetControlIndex(const std::string &key) const
|
||||
|
||||
Lv2Effect::~Lv2Effect()
|
||||
{
|
||||
if (activated)
|
||||
{
|
||||
Deactivate();
|
||||
}
|
||||
if (worker)
|
||||
{
|
||||
worker->Close();
|
||||
worker = nullptr; // delete the worker first!
|
||||
}
|
||||
if (activated)
|
||||
{
|
||||
Deactivate();
|
||||
}
|
||||
if (pInstance)
|
||||
{
|
||||
lilv_instance_free(pInstance);
|
||||
@@ -631,7 +636,7 @@ void Lv2Effect::Deactivate()
|
||||
lilv_instance_deactivate(pInstance);
|
||||
}
|
||||
|
||||
static inline void CopyBuffer(float *input, float *output, uint32_t frames)
|
||||
static inline void CopyBuffer(float *restrict input, float *restrict output, uint32_t frames)
|
||||
{
|
||||
for (uint32_t i = 0; i < frames; ++i)
|
||||
{
|
||||
@@ -669,7 +674,7 @@ void Lv2Effect::Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBuff
|
||||
{
|
||||
for (size_t i = 0; i < this->outputMixBuffers.size(); ++i)
|
||||
{
|
||||
float *__restrict input;
|
||||
float *restrict input;
|
||||
if (i >= this->inputAudioBuffers.size())
|
||||
{
|
||||
if (this->inputAudioBuffers.size() == 0)
|
||||
@@ -682,8 +687,8 @@ void Lv2Effect::Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBuff
|
||||
{
|
||||
input = this->inputAudioBuffers[i];
|
||||
}
|
||||
float *__restrict pluginOutput = this->outputMixBuffers[i].data();
|
||||
float *__restrict finalOutput = this->outputAudioBuffers[i];
|
||||
float *restrict pluginOutput = this->outputMixBuffers[i].data();
|
||||
float *restrict finalOutput = this->outputAudioBuffers[i];
|
||||
|
||||
for (uint32_t i = 0; i < samples; ++i)
|
||||
{
|
||||
@@ -694,11 +699,11 @@ void Lv2Effect::Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBuff
|
||||
else if (this->outputAudioPortIndices.size() == 1 && this->outputAudioBuffers.size() == 2)
|
||||
{
|
||||
// 1 plugin output into 2 outputs.
|
||||
float *__restrict pluginOutput = this->outputMixBuffers[0].data();
|
||||
float *restrict pluginOutput = this->outputMixBuffers[0].data();
|
||||
for (size_t i = 0; i < this->outputMixBuffers.size(); ++i)
|
||||
{
|
||||
float *__restrict input = this->inputAudioBuffers[i];
|
||||
float *__restrict finalOutput = this->outputAudioBuffers[i];
|
||||
float *restrict input = this->inputAudioBuffers[i];
|
||||
float *restrict finalOutput = this->outputAudioBuffers[i];
|
||||
|
||||
for (uint32_t i = 0; i < samples; ++i)
|
||||
{
|
||||
@@ -746,8 +751,8 @@ void Lv2Effect::Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBuff
|
||||
|
||||
if (this->outputAudioBuffers.size() == 1)
|
||||
{
|
||||
float *input = this->inputAudioBuffers[0];
|
||||
float *output = this->outputAudioBuffers[0];
|
||||
float * restrict input = this->inputAudioBuffers[0];
|
||||
float * restrict output = this->outputAudioBuffers[0];
|
||||
for (uint32_t i = 0; i < samples; ++i)
|
||||
{
|
||||
output[i] = currentBypass * output[i] + (1 - currentBypass) * input[i];
|
||||
@@ -762,8 +767,8 @@ void Lv2Effect::Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBuff
|
||||
}
|
||||
else
|
||||
{
|
||||
float *inputL;
|
||||
float *inputR;
|
||||
float * restrict inputL;
|
||||
float * restrict inputR;
|
||||
if (this->inputAudioBuffers.size() == 1)
|
||||
{
|
||||
inputL = inputR = inputAudioBuffers[0];
|
||||
@@ -773,8 +778,8 @@ void Lv2Effect::Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBuff
|
||||
inputL = inputAudioBuffers[0];
|
||||
inputR = inputAudioBuffers[1];
|
||||
}
|
||||
float *outputL = outputAudioBuffers[0];
|
||||
float *outputR = outputAudioBuffers[1];
|
||||
float * restrict outputL = outputAudioBuffers[0];
|
||||
float * restrict outputR = outputAudioBuffers[1];
|
||||
for (uint32_t i = 0; i < samples; ++i)
|
||||
{
|
||||
outputL[i] = currentBypass * outputL[i] + (1 - currentBypass) * inputL[i];
|
||||
|
||||
+5
-2
@@ -40,12 +40,12 @@
|
||||
#include "StateInterface.hpp"
|
||||
#include "LogFeature.hpp"
|
||||
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
|
||||
class RealtimeRingBufferWriter;
|
||||
class IPatchWriterCallback;
|
||||
class HostWorkerThread;
|
||||
|
||||
class Lv2Effect : public IEffect, private LogFeature::LogMessageListener
|
||||
{
|
||||
@@ -57,6 +57,10 @@ namespace pipedal
|
||||
|
||||
private:
|
||||
|
||||
|
||||
std::shared_ptr<HostWorkerThread> workerThread;
|
||||
std::unique_ptr<Worker> worker;
|
||||
|
||||
std::unordered_map<std::string,int> controlIndex;
|
||||
|
||||
FileBrowserFilesFeature fileBrowserFilesFeature;
|
||||
@@ -70,7 +74,6 @@ namespace pipedal
|
||||
int numberOfOutputs = 0;
|
||||
int numberOfMidiInputs = 0;
|
||||
|
||||
std::unique_ptr<Worker> worker;
|
||||
LilvInstance *pInstance;
|
||||
std::shared_ptr<Lv2PluginInfo> info;
|
||||
std::vector<float> controlValues;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "Lv2EventBufferWriter.hpp"
|
||||
#include "Lv2Log.hpp"
|
||||
#include "CrashGuard.hpp"
|
||||
#include "restrict.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
@@ -417,7 +418,7 @@ void Lv2Pedalboard::Deactivate()
|
||||
}
|
||||
}
|
||||
|
||||
static void Copy(float *input, float *output, uint32_t samples)
|
||||
static void Copy(float *restrict input, float *restrict output, uint32_t samples)
|
||||
{
|
||||
for (uint32_t i = 0; i < samples; ++i)
|
||||
{
|
||||
@@ -434,6 +435,7 @@ bool Lv2Pedalboard::Run(float **inputBuffers, float **outputBuffers, uint32_t sa
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < samples; ++i)
|
||||
{
|
||||
float volume = this->inputVolume.Tick();
|
||||
|
||||
+15
-5
@@ -47,6 +47,7 @@
|
||||
#include "StdErrorCapture.hpp"
|
||||
#include "util.hpp"
|
||||
#include "ModFileTypes.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
#include "Locale.hpp"
|
||||
|
||||
@@ -301,7 +302,6 @@ PluginHost::PluginHost()
|
||||
|
||||
this->urids = new Urids(mapFeature);
|
||||
|
||||
pHostWorkerThread = std::make_shared<HostWorkerThread>();
|
||||
}
|
||||
|
||||
void PluginHost::OnConfigurationChanged(const JackConfiguration &configuration, const JackChannelSelection &settings)
|
||||
@@ -870,6 +870,13 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP
|
||||
const LilvPluginClass *pClass = lilv_plugin_get_class(pPlugin);
|
||||
this->plugin_class_ = nodeAsString(lilv_plugin_class_get_uri(pClass));
|
||||
|
||||
if (this->plugin_class_ == "") // carla does this.
|
||||
{
|
||||
// set to plain Plugin.
|
||||
this->plugin_class_ = "http://lv2plug.in/ns/lv2core#Plugin";
|
||||
}
|
||||
|
||||
|
||||
AutoLilvNodes required_features = lilv_plugin_get_required_features(pPlugin);
|
||||
this->required_features_ = nodeAsStringArray(required_features);
|
||||
|
||||
@@ -1668,10 +1675,6 @@ bool Lv2PluginInfo::isSplit() const
|
||||
{
|
||||
return uri_ == SPLIT_PEDALBOARD_ITEM_URI;
|
||||
}
|
||||
std::shared_ptr<HostWorkerThread> PluginHost::GetHostWorkerThread()
|
||||
{
|
||||
return pHostWorkerThread;
|
||||
}
|
||||
|
||||
class ResourceInfo
|
||||
{
|
||||
@@ -1883,6 +1886,13 @@ Lv2PatchPropertyInfo::Lv2PatchPropertyInfo(PluginHost *pluginHost, const LilvNod
|
||||
}
|
||||
}
|
||||
|
||||
// ffs.
|
||||
static inline bool contains(const std::vector<std::string> &vec, const std::string &value) {
|
||||
return std::find(vec.begin(),vec.end(),value) != vec.end();
|
||||
}
|
||||
bool Lv2PluginInfo::WantsWorkerThread() const {
|
||||
return contains(this->required_features_,LV2_WORKER__schedule) || contains(this->supported_features_,LV2_WORKER__schedule);
|
||||
}
|
||||
// void PiPedalHostLogError(const std::string &error)
|
||||
// {
|
||||
// Lv2Log::error("%s",error.c_str());
|
||||
|
||||
+2
-2
@@ -454,6 +454,8 @@ namespace pipedal
|
||||
LV2_PROPERTY_GETSET(patchProperties)
|
||||
LV2_PROPERTY_GETSET(hasDefaultState)
|
||||
|
||||
bool WantsWorkerThread() const;
|
||||
|
||||
const Lv2PortInfo &getPort(const std::string &symbol)
|
||||
{
|
||||
for (size_t i = 0; i < ports_.size(); ++i)
|
||||
@@ -913,7 +915,6 @@ namespace pipedal
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<HostWorkerThread> pHostWorkerThread;
|
||||
// IHost implementation.
|
||||
virtual void SetMaxAudioBufferSize(size_t size) { maxBufferSize = size; }
|
||||
virtual size_t GetMaxAudioBufferSize() const { return maxBufferSize; }
|
||||
@@ -922,7 +923,6 @@ namespace pipedal
|
||||
virtual int GetNumberOfInputAudioChannels() const { return numberOfAudioInputChannels; }
|
||||
virtual int GetNumberOfOutputAudioChannels() const { return numberOfAudioOutputChannels; }
|
||||
virtual LV2_Feature *const *GetLv2Features() const { return (LV2_Feature *const *)&(this->lv2Features[0]); }
|
||||
virtual std::shared_ptr<HostWorkerThread> GetHostWorkerThread();
|
||||
|
||||
public:
|
||||
virtual MapFeature &GetMapFeature() override { return this->mapFeature; }
|
||||
|
||||
@@ -104,7 +104,7 @@ static void SetPriority(int realtimePriority, const char *priorityName)
|
||||
int result = sched_setscheduler(0, SCHED_RR, ¶m);
|
||||
if (result == 0)
|
||||
{
|
||||
Lv2Log::debug("Service thread priority successfully boosted.");
|
||||
Lv2Log::debug(SS("Service thread priority successfully boosted. (" << priorityName << ")"));
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
+44
-18
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
// (Borrows heavily from worker.c by David Robillard.)
|
||||
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
// Copyright (c) 2024 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
@@ -143,6 +143,7 @@ bool Worker::EmitResponses()
|
||||
|
||||
void Worker::WaitForAllResponses()
|
||||
{
|
||||
// do what we can to clear the response queue in order to avoid memory/object leaks
|
||||
using Clock = std::chrono::steady_clock;
|
||||
auto startTime = Clock::now();
|
||||
while (true)
|
||||
@@ -157,11 +158,15 @@ void Worker::WaitForAllResponses()
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::chrono::seconds waitDuration = std::chrono::duration_cast<std::chrono::seconds>(Clock::now()-startTime);
|
||||
if (waitDuration.count() > 5) {
|
||||
throw std::logic_error("Timed out waiting for a Worker task to complete.");
|
||||
// pump the plugin with a zero-length buffer.
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
std::chrono::milliseconds waitDuration = std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now()-startTime);
|
||||
if (waitDuration.count() > 1500) {
|
||||
// better to leak than to terminate the application.
|
||||
Lv2Log::error("Timed out waiting for a Worker task to complete.");
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(15));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,26 +247,34 @@ void HostWorkerThread::ThreadProc() noexcept
|
||||
HostWorkerThread::HostWorkerThread()
|
||||
{
|
||||
this->dataBuffer.resize(16*1024);
|
||||
pThread = std::make_unique<std::thread>([this]()
|
||||
{ this->ThreadProc(); });
|
||||
}
|
||||
|
||||
bool HostWorkerThread::StartThread()
|
||||
{
|
||||
if (!pThread)
|
||||
{
|
||||
if (closed) {
|
||||
return false;
|
||||
}
|
||||
pThread = std::make_unique<std::thread>([this]()
|
||||
{ this->ThreadProc(); });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void HostWorkerThread::Close()
|
||||
{
|
||||
|
||||
bool sendClose = false;
|
||||
{
|
||||
std::lock_guard lock{submitMutex};
|
||||
if (!closed)
|
||||
{
|
||||
closed = true;
|
||||
sendClose = true;
|
||||
if (pThread) {
|
||||
if (!closed)
|
||||
{
|
||||
closed = true;
|
||||
ScheduleWorkNoLock(nullptr, 0, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sendClose)
|
||||
{
|
||||
ScheduleWork(nullptr, 0, nullptr);
|
||||
}
|
||||
}
|
||||
HostWorkerThread::~HostWorkerThread()
|
||||
{
|
||||
@@ -278,7 +291,10 @@ LV2_Worker_Status HostWorkerThread::ScheduleWork(Worker *worker, size_t size, co
|
||||
{
|
||||
std::lock_guard lock(submitMutex);
|
||||
|
||||
if (exiting)
|
||||
if (!StartThread()) { // ensures thread is running, when plugins haven't declared they want a worker thread.
|
||||
return LV2_Worker_Status::LV2_WORKER_ERR_NO_SPACE;
|
||||
}
|
||||
if (exiting )
|
||||
{
|
||||
return LV2_Worker_Status::LV2_WORKER_ERR_NO_SPACE;
|
||||
}
|
||||
@@ -286,6 +302,11 @@ LV2_Worker_Status HostWorkerThread::ScheduleWork(Worker *worker, size_t size, co
|
||||
{
|
||||
exiting = true;
|
||||
}
|
||||
return ScheduleWorkNoLock(worker, size, data);
|
||||
|
||||
}
|
||||
LV2_Worker_Status HostWorkerThread::ScheduleWorkNoLock(Worker *worker, size_t size, const void *data)
|
||||
{
|
||||
|
||||
if (requestRingBuffer.writeSpace() < sizeof(worker) + sizeof(size) + size)
|
||||
{
|
||||
@@ -307,7 +328,12 @@ LV2_Worker_Status HostWorkerThread::ScheduleWork(Worker *worker, size_t size, co
|
||||
|
||||
void Worker::RunBackgroundTask(size_t size, uint8_t *data)
|
||||
{
|
||||
workerInterface->work(lilvInstance->lv2_handle, worker_respond_fn, (LV2_Handle)this, size, data);
|
||||
try {
|
||||
workerInterface->work(lilvInstance->lv2_handle, worker_respond_fn, (LV2_Handle)this, size, data);
|
||||
} catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error(SS("Unhandled exception on LV2 Worker thread: " << e.what()));
|
||||
}
|
||||
{
|
||||
std::lock_guard lock { this->outstandingRequestMutex};
|
||||
--this->outstandingRequests;
|
||||
|
||||
+9
-6
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
// Copyright (c) 2025 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "lv2/atom/atom.h"
|
||||
#include "lv2/worker/worker.h"
|
||||
#include "condition_variable"
|
||||
#include <atomic>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
@@ -51,10 +52,13 @@ namespace pipedal {
|
||||
HostWorkerThread();
|
||||
~HostWorkerThread();
|
||||
|
||||
bool StartThread();
|
||||
void Close();
|
||||
bool Closed() const { return closed || pThread == nullptr; }
|
||||
LV2_Worker_Status ScheduleWork(Worker*worker, size_t size, const void*data);
|
||||
private:
|
||||
bool closed = false;
|
||||
LV2_Worker_Status ScheduleWorkNoLock(Worker*worker, size_t size, const void*data);
|
||||
std::atomic<bool> closed = false;
|
||||
std::unique_ptr<std::thread> pThread;
|
||||
void ThreadProc() noexcept;
|
||||
|
||||
@@ -64,8 +68,6 @@ namespace pipedal {
|
||||
|
||||
std::vector<uint8_t> dataBuffer;
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
class Worker {
|
||||
@@ -75,8 +77,8 @@ namespace pipedal {
|
||||
LilvInstance*lilvInstance;
|
||||
const LV2_Worker_Interface*workerInterface;
|
||||
|
||||
bool closed = false;
|
||||
bool exiting = false;
|
||||
std::atomic<bool> closed = false;
|
||||
std::atomic<bool> exiting = false;
|
||||
RingBuffer<true,false> responseRingBuffer;
|
||||
|
||||
std::vector<uint8_t> responseBuffer;
|
||||
@@ -94,6 +96,7 @@ namespace pipedal {
|
||||
public:
|
||||
Worker(const std::shared_ptr<HostWorkerThread>& pHostWorker,LilvInstance *instance, const LV2_Worker_Interface *iface);
|
||||
~Worker();
|
||||
|
||||
void Close();
|
||||
|
||||
LV2_Worker_Status ScheduleWork(
|
||||
|
||||
@@ -106,6 +106,27 @@ static bool isJackServiceRunning()
|
||||
// }
|
||||
#endif
|
||||
|
||||
#define ENABLE_SEGV_DEBUG 0
|
||||
#if ENABLE_SEGV_DEBUG
|
||||
void debug_segvHandler(int sig)
|
||||
{
|
||||
// Print out all the frames to stderr
|
||||
const char *message = "Error: SEGV signal received.\n";
|
||||
auto _ = write(STDERR_FILENO, message, strlen(message));
|
||||
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void EnableDebugSevHandler()
|
||||
{
|
||||
signal(SIGSEGV, debug_segvHandler);
|
||||
signal(SIGILL, debug_segvHandler);
|
||||
signal(SIGKILL, debug_segvHandler);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static bool TryGetLogLevel(const std::string &strLogLevel, LogLevel *result)
|
||||
{
|
||||
if (strLogLevel == "debug")
|
||||
@@ -319,14 +340,25 @@ int main(int argc, char *argv[])
|
||||
sigaddset(&sigSet, SIGTERM);
|
||||
sigaddset(&sigSet, SIGUSR1);
|
||||
|
||||
// block these signasl for all threads.
|
||||
s = pthread_sigmask(SIG_BLOCK, &sigSet, NULL);
|
||||
if (s != 0)
|
||||
{
|
||||
throw std::logic_error("pthread_sigmask failed.");
|
||||
}
|
||||
|
||||
// Clear any pending signals before waiting
|
||||
struct timespec timeout = {0, 0};
|
||||
while (sigtimedwait(&sigSet, NULL, &timeout) > 0) {
|
||||
// Consume any pending signals
|
||||
}
|
||||
#if ENABLE_SEGV_DEBUG
|
||||
EnableDebugSevHandler();
|
||||
#endif
|
||||
|
||||
PiPedalModel model;
|
||||
|
||||
|
||||
model.SetNetworkChangedListener(
|
||||
[&server]() mutable
|
||||
{
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
Crash-proofing: do not reload pedalboard if the previous run crashed before normal shutdown.
|
||||
|
||||
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.
|
||||
|
||||
Carla Project icons.
|
||||
check reload after change of LV2 plugins.
|
||||
|
||||
Exactly one underrun per seek in Toob Player
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
==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<LocaleImpl>, 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<LocaleImpl>, 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<LocaleImpl>, 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<LocaleImpl>, 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*&>(pipedal::Lv2PluginInfo*, pipedal::PluginHost*&&, LilvWorldImpl*&, LilvPluginImpl const*&) (stl_construct.h:119)
|
||||
==5249== by 0x49A61F: void std::allocator_traits<std::allocator<void> >::construct<pipedal::Lv2PluginInfo, pipedal::PluginHost*, LilvWorldImpl*&, LilvPluginImpl const*&>(std::allocator<void>&, pipedal::Lv2PluginInfo*, pipedal::PluginHost*&&, LilvWorldImpl*&, LilvPluginImpl const*&) (alloc_traits.h:635)
|
||||
==5249== by 0x496C5F: std::_Sp_counted_ptr_inplace<pipedal::Lv2PluginInfo, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_Sp_counted_ptr_inplace<pipedal::PluginHost*, LilvWorldImpl*&, LilvPluginImpl const*&>(std::allocator<void>, pipedal::PluginHost*&&, LilvWorldImpl*&, LilvPluginImpl const*&) (shared_ptr_base.h:604)
|
||||
==5249== by 0x48F12B: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<pipedal::Lv2PluginInfo, std::allocator<void>, pipedal::PluginHost*, LilvWorldImpl*&, LilvPluginImpl const*&>(pipedal::Lv2PluginInfo*&, std::_Sp_alloc_shared_tag<std::allocator<void> >, pipedal::PluginHost*&&, LilvWorldImpl*&, LilvPluginImpl const*&) (shared_ptr_base.h:971)
|
||||
==5249== by 0x488A3B: std::__shared_ptr<pipedal::Lv2PluginInfo, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<void>, pipedal::PluginHost*, LilvWorldImpl*&, LilvPluginImpl const*&>(std::_Sp_alloc_shared_tag<std::allocator<void> >, pipedal::PluginHost*&&, LilvWorldImpl*&, LilvPluginImpl const*&) (shared_ptr_base.h:1712)
|
||||
==5249== by 0x482803: std::shared_ptr<pipedal::Lv2PluginInfo>::shared_ptr<std::allocator<void>, pipedal::PluginHost*, LilvWorldImpl*&, LilvPluginImpl const*&>(std::_Sp_alloc_shared_tag<std::allocator<void> >, pipedal::PluginHost*&&, LilvWorldImpl*&, LilvPluginImpl const*&) (shared_ptr.h:464)
|
||||
==5249== by 0x47E363: std::shared_ptr<pipedal::Lv2PluginInfo> std::make_shared<pipedal::Lv2PluginInfo, pipedal::PluginHost*, LilvWorldImpl*&, LilvPluginImpl const*&>(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)
|
||||
@@ -1,17 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px"
|
||||
height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<path display="inline" d="M47,41c0,3.313-2.687,6-6,6H7c-3.313,0-6-2.687-6-6V7c0-3.313,2.687-6,6-6h34c3.313,0,6,2.687,6,6V41z"/>
|
||||
<line display="inline" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-miterlimit="10" x1="1" y1="25" x2="25" y2="25"/>
|
||||
<circle display="inline" fill="#FFFFFF" cx="24" cy="25" r="4.667"/>
|
||||
<line display="inline" fill="none" stroke="#FFFFFF" stroke-width="4" x1="34" y1="25" x2="48" y2="25"/>
|
||||
<path display="inline" fill="#FFFFFF" d="M24,13c-4.43,0-8.297,2.419-10.376,6h5.103c1.41-1.241,3.253-2,5.274-2
|
||||
c4.411,0,8,3.589,8,8s-3.589,8-8,8c-2.021,0-3.864-0.76-5.274-2h-5.103c2.079,3.581,5.946,6,10.376,6c6.617,0,12-5.383,12-12
|
||||
C36,18.383,30.617,13,24,13z"/>
|
||||
</g>
|
||||
<g id="Combined">
|
||||
<path d="M35.819,27C34.863,32.666,29.935,37,24,37c-4.43,0-8.298-2.419-10.376-6h5.103c1.41,1.24,3.253,2,5.274,2
|
||||
c4.411,0,8-3.589,8-8s-3.589-8-8-8c-2.021,0-3.864,0.759-5.274,2h-5.103c2.079-3.581,5.947-6,10.376-6
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 900 B |
@@ -167,6 +167,7 @@ export interface FilePropertyDialogState {
|
||||
moveDialogOpen: boolean;
|
||||
copyDialogOpen: boolean;
|
||||
initialSelection: string;
|
||||
previousSelection: string;
|
||||
multiSelect: boolean,
|
||||
selectedFiles: string[],
|
||||
//openTone3000Dialog: boolean,
|
||||
@@ -241,6 +242,7 @@ export default withStyles(
|
||||
moveDialogOpen: false,
|
||||
copyDialogOpen: false,
|
||||
initialSelection: this.props.selectedFile,
|
||||
previousSelection: this.props.selectedFile,
|
||||
multiSelect: false,
|
||||
selectedFiles: [],
|
||||
//openTone3000Dialog: false,
|
||||
@@ -678,7 +680,7 @@ export default withStyles(
|
||||
this.requestScroll = true;
|
||||
if (!fileEntry.isDirectory) {
|
||||
if (!this.isFolderArtwork(fileEntry.pathname)) {
|
||||
this.props.onApply(this.props.fileProperty, fileEntry.pathname);
|
||||
this.handleApply(this.props.fileProperty, fileEntry.pathname);
|
||||
}
|
||||
}
|
||||
this.setState({
|
||||
@@ -695,6 +697,15 @@ export default withStyles(
|
||||
}
|
||||
this.openSelectedFile();
|
||||
}
|
||||
handleApply(fileProperty: UiFileProperty, selectedItem: string)
|
||||
{
|
||||
if (this.state.previousSelection == selectedItem) {
|
||||
return;
|
||||
}
|
||||
this.props.onApply(fileProperty, selectedItem);
|
||||
this.setState({previousSelection: selectedItem});
|
||||
}
|
||||
|
||||
|
||||
handleMenuOpen(event: React.MouseEvent<HTMLElement>) {
|
||||
this.setState({ menuAnchorEl: event.currentTarget });
|
||||
@@ -1565,7 +1576,7 @@ export default withStyles(
|
||||
<div style={{ flex: "1 1 auto" }}> </div>
|
||||
|
||||
<Button variant="dialogSecondary" onClick={() => {
|
||||
this.props.onApply(this.props.fileProperty, this.state.initialSelection);
|
||||
this.handleApply(this.props.fileProperty, this.state.initialSelection);
|
||||
this.props.onCancel();
|
||||
}} aria-label="cancel">
|
||||
Cancel
|
||||
|
||||
@@ -602,10 +602,12 @@ export const LoadPluginDialog =
|
||||
return;
|
||||
}
|
||||
if (this.changedSearchString !== undefined) {
|
||||
this.requestScrollTo();
|
||||
this.setState({
|
||||
search_string: this.changedSearchString
|
||||
});
|
||||
if (this.state.search_string !== this.changedSearchString) {
|
||||
this.requestScrollTo();
|
||||
this.setState({
|
||||
search_string: this.changedSearchString
|
||||
});
|
||||
}
|
||||
this.changedSearchString = undefined;
|
||||
}
|
||||
this.hSearchTimeout = undefined;
|
||||
@@ -642,7 +644,10 @@ export const LoadPluginDialog =
|
||||
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
let isFavorite: boolean = this.state.favoritesList[value.uri] ?? false;
|
||||
|
||||
let pluginType = value.plugin_type;
|
||||
if (value.uri === "http://two-play.com/plugins/toob-nam") {
|
||||
pluginType = PluginType.NamPlugin;
|
||||
}
|
||||
return (
|
||||
<div key={value.uri}
|
||||
onDoubleClick={(e) => { this.onDoubleClick(e, value.uri) }}
|
||||
@@ -654,7 +659,7 @@ export const LoadPluginDialog =
|
||||
<SelectHoverBackground selected={value.uri === this.state.selected_uri} showHover={true} />
|
||||
<div className={classes.content}>
|
||||
<div className={classes.iconBorder} >
|
||||
<PluginIcon pluginType={value.plugin_type} size={24} opacity={0.6} />
|
||||
<PluginIcon pluginType={pluginType} size={24} opacity={0.6} />
|
||||
</div>
|
||||
<div className={classes.content2}>
|
||||
<div className={classes.label} style={{ display: "flex", flexFlow: "row nowrap", alignItems: "center" }} >
|
||||
|
||||
@@ -471,6 +471,9 @@ export enum PluginType {
|
||||
None = "",
|
||||
InvalidPlugin = "InvalidPlugin",
|
||||
|
||||
// "special"
|
||||
NamPlugin = "NamPlugin",
|
||||
|
||||
Plugin = "Plugin",
|
||||
AllpassPlugin = "AllpassPlugin",
|
||||
AmplifierPlugin = "AmplifierPlugin",
|
||||
@@ -510,7 +513,7 @@ export enum PluginType {
|
||||
UtilityPlugin = "UtilityPlugin",
|
||||
WaveshaperPlugin = "WaveshaperPlugin",
|
||||
|
||||
// psuedo plugin type for the Amps node of the filter dialog.
|
||||
// pseudo plugin type for the Amps node of the filter dialog.
|
||||
PiPedalAmpsNode = "PiPedalAmpsNode",
|
||||
|
||||
// pseudo plugin types for splitter.
|
||||
@@ -521,7 +524,7 @@ export enum PluginType {
|
||||
|
||||
|
||||
|
||||
// psuedo plugin type for misc icons.
|
||||
// pseudo plugin type for misc icons.
|
||||
ErrorPlugin = "ErrorPlugin",
|
||||
Terminal = "Terminal", //"img/fx_terminal.svg";
|
||||
|
||||
|
||||
@@ -306,7 +306,11 @@ class CustomSelectPathControl implements ModGuiControl {
|
||||
this.requestUpdate();
|
||||
}
|
||||
if (this.enumeratedValueElement) {
|
||||
this.enumeratedValueElement.textContent = pathFileName(this.pathValue);
|
||||
if (this.pathValue === "") {
|
||||
this.enumeratedValueElement.textContent = "<none>";
|
||||
} else {
|
||||
this.enumeratedValueElement.textContent = pathFileName(this.pathValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1643,7 +1647,8 @@ function ModGuiHost(props: ModGuiHostProps) {
|
||||
|
||||
return (
|
||||
<ModGuiErrorBoundary plugin={props.plugin} onClose={() => { props.onClose(); setErrorMessage(null); }}>
|
||||
<div style={{
|
||||
<div
|
||||
style={{
|
||||
display: "inline-block",
|
||||
paddingLeft: 20, paddingRight: 20,
|
||||
overflow: "hidden",
|
||||
|
||||
@@ -327,8 +327,12 @@ class PedalLayout {
|
||||
else {
|
||||
let uiPlugin = model.getUiPlugin(pedalItem.uri);
|
||||
if (uiPlugin != null) {
|
||||
this.pluginType = uiPlugin.plugin_type;
|
||||
this.iconUrl = SelectIconUri(uiPlugin.plugin_type);
|
||||
let pluginType = uiPlugin.plugin_type;
|
||||
this.pluginType = pluginType;
|
||||
if (this.uri === "http://two-play.com/plugins/toob-nam") {
|
||||
pluginType = PluginType.NamPlugin;
|
||||
}
|
||||
this.iconUrl = SelectIconUri(pluginType);
|
||||
this.name = uiPlugin.label;
|
||||
if (pedalItem.title !== "") {
|
||||
this.name = pedalItem.title;
|
||||
@@ -1109,12 +1113,18 @@ withStyles(
|
||||
</div>
|
||||
)
|
||||
let uiPlugin = this.model.getUiPlugin(item.pedalItem?.uri ?? "");
|
||||
let pluginMissing = uiPlugin == null;
|
||||
let pluginMissing = uiPlugin === null;
|
||||
let pluginType = item.pluginType;
|
||||
if (uiPlugin && uiPlugin.uri === "http://two-play.com/plugins/toob-nam")
|
||||
{
|
||||
pluginType = PluginType.NamPlugin;
|
||||
|
||||
}
|
||||
|
||||
result.push(<div key={this.renderKey++} className={classes.pedalItem} style={{ left: item.bounds.x, top: item.bounds.y }} >
|
||||
{this.pedalButton(
|
||||
item.pedalItem?.instanceId ?? -1,
|
||||
item.pluginType,
|
||||
pluginType,
|
||||
!item.isEmpty(),
|
||||
item.pedalItem?.isEnabled ?? false,
|
||||
true,
|
||||
|
||||
@@ -1780,6 +1780,7 @@ export class PiPedalModel //implements PiPedalModel
|
||||
// null -> we've never seen a value.
|
||||
item.pathProperties[fileProperty.patchProperty] = "null";
|
||||
}
|
||||
newPedalboard.selectedPlugin = item.instanceId;
|
||||
this.setModelPedalboard(newPedalboard);
|
||||
this.updateServerPedalboard()
|
||||
return item.instanceId;
|
||||
@@ -1843,6 +1844,7 @@ export class PiPedalModel //implements PiPedalModel
|
||||
newPedalboard.deleteItem(fromInstanceId);
|
||||
|
||||
newPedalboard.addBefore(fromItem, toInstanceId);
|
||||
newPedalboard.selectedPlugin = fromItem.instanceId;
|
||||
this.setModelPedalboard(newPedalboard);
|
||||
this.updateServerPedalboard();
|
||||
|
||||
@@ -1862,7 +1864,7 @@ export class PiPedalModel //implements PiPedalModel
|
||||
newPedalboard.deleteItem(fromInstanceId);
|
||||
|
||||
newPedalboard.addAfter(fromItem, toInstanceId);
|
||||
|
||||
newPedalboard.selectedPlugin = fromItem.instanceId;
|
||||
this.setModelPedalboard(newPedalboard);
|
||||
this.updateServerPedalboard();
|
||||
}
|
||||
@@ -1884,6 +1886,7 @@ export class PiPedalModel //implements PiPedalModel
|
||||
|
||||
newPedalboard.addToStart(fromItem);
|
||||
|
||||
newPedalboard.selectedPlugin = fromItem.instanceId;
|
||||
this.setModelPedalboard(newPedalboard);
|
||||
this.updateServerPedalboard();
|
||||
}
|
||||
@@ -1903,6 +1906,7 @@ export class PiPedalModel //implements PiPedalModel
|
||||
|
||||
newPedalboard.addToEnd(fromItem);
|
||||
|
||||
newPedalboard.selectedPlugin = fromItem.instanceId;
|
||||
this.setModelPedalboard(newPedalboard);
|
||||
|
||||
this.updateServerPedalboard();
|
||||
@@ -1930,7 +1934,7 @@ export class PiPedalModel //implements PiPedalModel
|
||||
let emptyItem = newPedalboard.createEmptyItem();
|
||||
newPedalboard.replaceItem(fromInstanceId, emptyItem);
|
||||
newPedalboard.replaceItem(toInstanceId, fromItem);
|
||||
|
||||
newPedalboard.selectedPlugin = fromItem.instanceId;
|
||||
this.setModelPedalboard(newPedalboard);
|
||||
this.updateServerPedalboard();
|
||||
|
||||
@@ -1954,6 +1958,7 @@ export class PiPedalModel //implements PiPedalModel
|
||||
}
|
||||
let newItem = newPedalboard.createEmptyItem();
|
||||
newPedalboard.addItem(newItem, instanceId, append);
|
||||
newPedalboard.selectedPlugin = newItem.instanceId;
|
||||
this.setModelPedalboard(newPedalboard);
|
||||
this.updateServerPedalboard();
|
||||
return newItem.instanceId;
|
||||
@@ -1979,6 +1984,8 @@ export class PiPedalModel //implements PiPedalModel
|
||||
}
|
||||
let newItem = newPedalboard.createEmptySplit();
|
||||
newPedalboard.addItem(newItem, instanceId, append);
|
||||
newPedalboard.selectedPlugin = newItem.instanceId;
|
||||
|
||||
this.setModelPedalboard(newPedalboard);
|
||||
this.updateServerPedalboard();
|
||||
return newItem.instanceId;
|
||||
@@ -1997,6 +2004,7 @@ export class PiPedalModel //implements PiPedalModel
|
||||
newPedalboard.setItemEmpty(item);
|
||||
|
||||
|
||||
newPedalboard.selectedPlugin = item.instanceId;
|
||||
this.setModelPedalboard(newPedalboard);
|
||||
this.updateServerPedalboard();
|
||||
return item.instanceId;
|
||||
|
||||
@@ -32,6 +32,7 @@ import FxPhaserIcon from './svg/fx_phaser.svg?react';
|
||||
import FxFilterIcon from './svg/fx_filter.svg?react';
|
||||
import FxDelayIcon from './svg/fx_delay.svg?react';
|
||||
|
||||
import FxNamIcon from './svg/fx_nam.svg?react';
|
||||
import FxAmplifierIcon from './svg/fx_amplifier.svg?react';
|
||||
import FxChorusIcon from './svg/fx_chorus.svg?react';
|
||||
import FxModulatorIcon from './svg/fx_modulator.svg?react';
|
||||
@@ -97,9 +98,14 @@ export function SelectSvgIcon(plugin_type: PluginType, className: string, size:
|
||||
}
|
||||
let myStyle = { width: size, height: size, opacity: opacity, color: color, fill: color };
|
||||
switch (plugin_type) {
|
||||
|
||||
case PluginType.NamPlugin:
|
||||
return <FxNamIcon className={className} style={myStyle} />;
|
||||
case PluginType.PhaserPlugin:
|
||||
return <FxPhaserIcon className={className} style={myStyle} />;
|
||||
case PluginType.FilterPlugin:
|
||||
case PluginType.BandpassPlugin:
|
||||
case PluginType.AllpassPlugin:
|
||||
return <FxFilterIcon className={className} style={myStyle} />;
|
||||
case PluginType.DelayPlugin:
|
||||
return <FxDelayIcon className={className} style={myStyle} />;
|
||||
@@ -175,16 +181,20 @@ export function SelectSvgIcon(plugin_type: PluginType, className: string, size:
|
||||
case PluginType.Terminal:
|
||||
return <FxTerminalIcon className={className} style={myStyle} />;
|
||||
|
||||
default:
|
||||
case PluginType.Plugin:
|
||||
default:
|
||||
return <FxPluginIcon className={className} style={myStyle} />;
|
||||
}
|
||||
}
|
||||
export function SelectBaseIcon(plugin_type: PluginType): string {
|
||||
switch (plugin_type) {
|
||||
case PluginType.NamPlugin:
|
||||
return "fx_nam.svg";
|
||||
case PluginType.PhaserPlugin:
|
||||
return "fx_phaser.svg";
|
||||
case PluginType.FilterPlugin:
|
||||
case PluginType.BandpassPlugin:
|
||||
case PluginType.AllpassPlugin:
|
||||
return "fx_filter.svg";
|
||||
case PluginType.DelayPlugin:
|
||||
return "fx_delay.svg";
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 48 48"
|
||||
enable-background="new 0 0 48 48"
|
||||
xml:space="preserve"
|
||||
id="svg141"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs145" />
|
||||
|
||||
<path
|
||||
id="path138"
|
||||
d="M 7,1 C 3.6870066,1 1,3.6870066 1,7 v 34 c 0,3.312994 2.6870066,6 6,6 h 34 c 3.312994,0 6,-2.688006 6,-6 V 7 C 47,3.6870066 44.312994,1 41,1 Z m 8.841797,6.515625 h 5.041015 c 0.588947,0 1.082064,0.1968374 1.480469,0.5917969 0.398405,0.3949594 0.597657,0.8848969 0.597657,1.46875 v 8.2421871 h -3.119141 l -2.078125,2.060547 h 5.197266 v 6.181641 h -4.574219 l -2.078125,-4.121094 h -4.78125 L 9.4492188,24 H 15.0625 l 2.078125,4.121094 h 5.820313 v 10.302734 c 0,0.583853 -0.199252,1.073791 -0.597657,1.46875 -0.398405,0.39496 -0.891522,0.591797 -1.480469,0.591797 h -5.041015 c -0.381083,0 -0.737289,-0.09431 -1.066406,-0.283203 -0.329117,-0.188894 -0.578734,-0.453432 -0.751953,-0.796875 L 9.96875,32.242188 h 3.013672 l 2.080078,4.121093 h 4.779297 V 34.302734 H 16.308594 L 14.230469,30.181641 H 8.8261719 L 5.8632812,25.03125 C 5.7939934,24.859528 5.7336065,24.687346 5.6816406,24.515625 5.6296747,24.343903 5.6035156,24.171721 5.6035156,24 c 0,-0.137377 0.086546,-0.481741 0.2597656,-1.03125 l 2.9628907,-5.150391 h 5.4042971 l 2.078125,-4.121093 h 3.533203 V 11.636719 H 15.0625 l -2.080078,4.121093 H 9.96875 l 4.054688,-7.1621089 c 0.173219,-0.343443 0.422836,-0.6079813 0.751953,-0.796875 0.329117,-0.1888936 0.685323,-0.2832031 1.066406,-0.2832031 z m 11.275391,0 h 5.041015 c 0.381084,0 0.737289,0.094309 1.066406,0.2832031 0.329117,0.1888937 0.578734,0.453432 0.751953,0.796875 L 38.03125,15.757812 H 35.017578 L 32.9375,11.636719 h -4.779297 v 2.060547 h 3.533203 l 2.078125,4.121093 h 5.404297 l 2.962891,5.150391 c 0.06929,0.171721 0.129675,0.343903 0.18164,0.515625 0.05197,0.171721 0.07813,0.343903 0.07813,0.515625 0,0.137377 -0.08655,0.481741 -0.259765,1.03125 l -2.962891,5.150391 h -5.404297 l -2.078125,4.121093 h -3.533203 v 2.060547 H 32.9375 l 2.080078,-4.121093 h 3.013672 l -4.054688,7.162109 c -0.173219,0.343443 -0.422836,0.607981 -0.751953,0.796875 -0.329117,0.188894 -0.685322,0.283203 -1.066406,0.283203 h -5.041015 c -0.588947,0 -1.082064,-0.196837 -1.480469,-0.591797 -0.398406,-0.394959 -0.597657,-0.884897 -0.597657,-1.46875 V 28.121094 h 5.820313 L 32.9375,24 h 5.613281 l -2.078125,-2.060547 h -4.78125 l -2.078125,4.121094 h -4.574219 v -6.181641 h 5.197266 L 28.158203,17.818359 H 25.039062 V 9.5761719 c 10e-7,-0.5838531 0.199251,-1.0737906 0.597657,-1.46875 0.398405,-0.3949595 0.891522,-0.5917969 1.480469,-0.5917969 z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
Reference in New Issue
Block a user