diff --git a/docs/AudioLatency.md b/docs/AudioLatency.md
index 837e10e..8d91258 100644
--- a/docs/AudioLatency.md
+++ b/docs/AudioLatency.md
@@ -1,26 +1,58 @@
+---
+page_icon: img/AudioLatency.jpg
+---
## Optimizing Audio Latency
-Note that PiPedal is not intended for use when logged in to Raspbian. Screen updates and heavy filesystem activity will cause audio dropouts. For best results, access PiPedal using the web interface remotely, through the Wi-Fi hotspot. Accessing the web interface via Wi-Fi has little or no effect on audio latency or dropouts.
+ {% include pageIcon.html %}
-With a good USB audio device, PiPedal should be able to provide stable audio with 4ms (good), or 2ms (excellent) latency on a Raspberry Pi 4 when running on a stock PREEMPT kernel. Your actual results may vary.
+This section provides information on optimizing USB audio latency on operating systems other than Raspberry Pi OS.
-The current Linux kernel provides best latency on USB audio devices when they are configured with a sampling rate of 48kHz, and 3 buffers. Cheap USB audio devices (e.g. M-Audio M-Track Solo, available for less than $60) should be able to run without dropouts at 48kHz with 3x64 sample buffers. Most devices in this class use the same Burr-Brown chipset. Premium USB Audio devices should run stably at 48kHZ 3x32 sample buffers (about 2ms latency). I personally use the MOTU M2 USB audio adapter, which I highly recommend -- stable, quiet, low-latency, great controls, and built like a tank).
+Note that PiPedal is not intended for use when logged in interactively. Screen updates and heavy filesystem activity will cause audio dropouts. For best results, access PiPedal using the web interface remotely, through a Wi-Fi hotspot connection, Wi-Fi connection or Ethernet connection. Accessing the web interface has little or no effect on audio latency or dropouts.
-Make sure your system is fully updated, and that you are running with a kernel version of 5.10 or later, since version 5.10 of the Linux kernel provides improved support for class-compliant USB audio. The MOTU M2 (and many other USB audio adapters) will not work on versions of the kernel prior to 5.10.
+With a good USB audio device, PiPedal should be able to provide stable audio with sub-4ms latency on a Raspberry Pi 4 when running on a stock PREEMPT kernel. Your actual results may vary.
-Prefer 64-bit operating systems to 32-bit operating systems. ARM processors execute 64-bit code about 40% faster than 32-bit code providing the same functionality.
+PiPedal uses ALSA audio drivers directly. Unlike Jack audio drivers, ALSA can use 44.1k or 48k audio with no significant difference in latency, and
+does not require 3 audio buffers to work efficiently.
-RT_PREEMPT realtime kernels (when available) are preferred but not required. (As if February 2022, there aren't any good sources for latest versions of Ubuntu or Raspberry Pi OS). PiPedal provides better (but not dramatically better) latency when running on a Raspbian Realtime kernel. Stock Raspbian provides PREEMPT real-time scheduling, but does not currently have all of the realtime patches, so interrupt latency is slightly more variable on stock Raspberry Pi OS than on custom builds of Raspbian with RT_PREEMPT patches applied.
+PiPedal provides the pipedal_latency_test utility to measure actual round-trip audio latency. You must temporarily disable pipedal (`sudo systemctl stop pipedal`), and connect the left audio output of your audio device to the left audio input of your audio device with a guitar cable to use this test.
+
+The following table shows measured round-trip audio latencies for a MOTU M2 external USB adapter running on Raspbery Pi OS with a 48 KHz sample rate. You can use these figures as a rough guideline; but actual round-trip audio latency will depend on the audio device you are using.
+
+
+ | Buffers |
+ | Size | 2 | 3 | 4 |
+ | 16 | Fails | 185/3.9ms | 201/4.2ms |
+ | 24 | 192/4.0ms | 213/4.4ms | 236/4.9ms |
+ | 32 | 219/4.6ms | 236/4.9ms | 272/5.7ms |
+ | 48 | 253/5.3ms | 299/6.2ms | 348/7.2ms |
+ | 64 | 280/5.8ms | 346/7.2ms | 411/8.6ms |
+ | 128 | 442/9.2ms | 571/11.9ms | 699/14.6ms |
+
+ Table 1: Delay in samples/delay in ms on MOTU M2 @ 48KHz.
+
+Make sure your system is fully updated. A kernel version greater than 5.15 is recommened, as Linux 5.15 is known to contain patches that
+provide significant performance improvements for USB audio devices. Version 5.10 provides patches that provide broad support for
+USB audio adapters. Kernel versions prior to 5.10 provide limited support for a limited number of mostly obsolete USB adapters.
+PiPedal will run on kernel versions less than 5.10, but may not provide robust, stable audio.
+
+Prefer 64-bit operating systems to 32-bit operating systems. ARM processors execute 64-bit audio code about 40% faster than 32-bit code providing the same functionality.
+
+RT_PREEMPT realtime kernels (when available) are preferred but not required. However, as of February 2022, there aren't any good sources for RT_PREEMPT kernels for Ubuntu or Raspberry Pi OS, and this may not change in the future. PiPedal provides better (but not dramatically better) latency when running on an RT_PREEMPT kernel. Stock Raspberry PI OS provides PREEMPT real-time scheduling, but does not currently have all of the realtime patches, so interrupt latency is slightly more variable on stock Raspberry Pi OS than on custom builds of Raspbian with RT_PREEMPT patches applied.
The Ubuntu Studio installer will install a realtime kernel if there is one available. But -- at least for Ubuntu 21.04 -- there is no stock RT_PREEMPT kernel for ARM aarch64.
-On a Raspberry Pi 4 device, Wi-Fi, USB 2.0, USB 3.0 and SD Card access are performed over separate buses (which is not true for previous versions of Raspberry Pi). It's therefore a good idea to ensure that your USB audio device is either the only device connected to the USB 2.0 ports, or the only device connected to the UBS 3.0 ports. There's no significant advantage to using USB 3.0 over USB 2.0 for USB audio. Network traffic does not seem to adversely affect USB audio operations on Raspberry Pi 4 (which isn't true on previous versions of Raspberry Pi). Filesystem activity does affect USB audio operation on Raspberry Pi OS, even with an RT_PREEMPT kernel; but interestingly, filesystem activity has much less effect on UBS audio on Ubuntu 21.04, even on a plain PREEMPT kernel.
+On a Raspberry Pi 4 device, Wi-Fi, USB 2.0, USB 3.0 and SD Card access are performed over separate buses (which is not true for previous versions of Raspberry Pi). It's therefore a good idea to ensure that your USB audio device is either the only device connected to the USB 2.0 ports, or the only device connected to the UBS 3.0 ports. There are theoretical advantages to USB 3.0 over USB 2.0 for USB audio; but I don't beleive there are currently any USB audio devices that take advanage of USB 3.0 features. If in doubt, use a UBS 3.0 ports; if there are problems, of if you have a high-speed drive attached to a USB 3.0 port, use a UBS 2.0 port.
-There is some reason to believe that there are outstanding issues with the Broadcom 2711 PCI Express bus drivers on Raspberry Pi OS realtime kernels, but as of September 2021, this is still a research issue. If you are brave, there is strong anecdotal evidence that these issues arise when the Pi 4 PCI-express bus goes into and out of power-saving mode, which can be prevented by building a realtime kernel with all power-saving options disabled. But this is currently unconfirmed speculation. And building realtime kernels is well outside the scope of this document. (source: a youtube video on horrendously difficult bugs encountered while supporting RT_PREEMPT, by one of the RT_PREEMPT team members).
+Network traffic does not seem to adversely affect USB audio operations on Raspberry Pi 4 (which isn't true on previous versions of Raspberry Pi, which use a common bus for network and USB devices). Filesystem activity does affect USB audio operation on Raspberry Pi OS, even with an RT_PREEMPT kernel; but interestingly, filesystem activity has much less effect on USB audio on Ubuntu 21.04, even on a plain PREEMPT kernel.
-For the meantime, for best results, log off from your Raspberry Pi, and use the web interface only.
+There is some reason to believe that there are outstanding issues with the Broadcom 2711 PCI Express bus drivers on Raspberry Pi OS kernels, but as of September 2021, this is still a research issue. If you are brave, there is weak anecdotal evidence that these issues arise when the Pi 4 PCI-express bus goes into and out of power-saving mode, which can be prevented by building a realtime kernel with all power-saving options disabled. But this is currently unconfirmed speculation. And building realtime kernels is well outside the scope of this document. (source: a youtube video on horrendously difficult bugs encountered while supporting RT_PREEMPT, by one of the RT_PREEMPT team members hints at this problem).
-You may also want to watch out for temperature throttling of the CPUs. PiPedal displays the current CPU temperature in the bottom-right corner of the display. The system will reduce CPU speed in order prevent damage to the system if the CPU temperature goes above 70C (perhaps above 60C). The Pi 400 already has good heat sinks, so you shouldn't run into problems when running on a Pi 400. If you run into throttling problems on a Raspberry Pi 4, you may want to buy and install a heat sink (ridiculously cheap), or install a cooling fan. As a temporary work-around, you can orient the Raspberry Pi 4 board vertically, which can provide a real and meaningful reduction in CPU temperature.
+Temperature Throttling
+----------------------
+
+You may want to watch out for temperature throttling of the CPUs. PiPedal can be configured to display the current CPU temperature in the bottom-right corner of the display. The system will reduce CPU speed in order prevent damage to the system if the CPU temperature goes above 70C (perhaps above 60C). The Pi 400 already has good heat sinks, so you shouldn't run into problems when running on a Pi 400. If you run into throttling problems on a Raspberry Pi 4, you may want to buy and install a heat sink (ridiculously cheap), or install a cooling fan (a couple of dollars). As a temporary work-around, you can orient the Raspberry Pi 4 board vertically, which can provide a real and meaningful reduction in CPU temperature.
+
+My development system has both a heat sink, and a fan. The CPU temperature rarely goes above 60C.
--------
[<< System Requirements](ChoosingAUsbAudioAdapter.md) | [Up](Documentation.md) | [Command-Line Configuration of PiPedal >>](CommandLine.md)
\ No newline at end of file
diff --git a/docs/ChoosingAUsbAudioAdapter.md b/docs/ChoosingAUsbAudioAdapter.md
index 1c7f91c..c8ddf57 100644
--- a/docs/ChoosingAUsbAudioAdapter.md
+++ b/docs/ChoosingAUsbAudioAdapter.md
@@ -1,12 +1,21 @@
+---
+page_icon: img/UsbAdapter.jpg
+---
+
## Choosing a USB Audio Adapter
-The qualitty of the audio adapter dramatically affects the quality of the guitar effects. Amp models are particularly sensitive to poor signal-to-noise ratios, because overdrive effects boost the level of noise more than the level of the actual signal. For best results, you should choose a 24-bit audio adapter that provides at least 105db (A-weighted) signal-to-noise ratio. I personally use and recommend the Motu M2 USB audio adapter, although there are plenty of other pro-quality audio adapters that will work as well. I would urge you to avoid cheaper USB audio adapters that sell for less than US$70, which will almost certainly not provide input signals of sufficient quality.
+{% include pageIcon.html %}
-Ideally, you want a USB adatper that provides an input volume knob. For best results, you want the input signal to be as high as possible without clipping. (Clipped input signals sound horrible). USB audio adapters without volume knobs usually do provide internal input volume controls, which have to be set using custom software. PiPedal does not currently allow you to set the internal trim levels of USB audio adapters. By default, these adapters will expect line level-signals. But instrument levels are about 40db quieter than line-level signals; so you will not get good results if you don't adjust the input signal trim levels of your audio adapter.
+
+The quality of the audio adapter you use dramatically affects the quality of the guitar effects. Amp models are particularly sensitive to poor signal-to-noise ratios, because overdrive effects boost the level of noise more than the level of the actual signal. For best results, you should choose a 24-bit audio adapter that provides at least 105db (A-weighted) signal-to-noise ratio.
+
+I personally use and recommend the Motu M2 USB audio adapter, although there are plenty of other pro-quality audio adapters that will work as well. Cheaper USB audio adapters that sell for less than US$70 will almost certainly not provide adequate signal-to-noise ratio for best results.
+
+Ideally, you want a USB adapter that provides an input volume knob, and an instrument-level input jack. For best results, you want the input signal to be as high as possible without clipping. (Clipped input signals sound horrible).
Pay close attention to the input VU meter of the first effect in your guitar effect chain. That will indicate the signal level coming into the USB adapter. Ideally, you want the value peaking solidly in the yellow range of the VU meter, and NEVER going red.
-Again, the MOTU M2 excels in this regard. It provides large, volume knobs for input and output, along with very readable VU meters which indicate both input and output signal levels.
+Again, the MOTU M2 excels in this regard. It provides large, volume knobs for input and output trim, along with very readable VU meters on the front panel which indicate both input and output signal levels.
--------
[<< Configuring PiPedal After Installation](Configuring.md) | [Up](Documentation.md) | [Optimizing Audio Latency >>](AudioLatency.md)
diff --git a/docs/Configuring.md b/docs/Configuring.md
index ea7f015..9e2733a 100644
--- a/docs/Configuring.md
+++ b/docs/Configuring.md
@@ -1,7 +1,15 @@
+---
+page_icon: img/Setup.jpg
+icon_width: 320px
+icon_float: left
+---
## Configuring PiPedal After Installation
+
Before using PiPedal, you will need to configure settings for the audio device that PiPedal will use.
+{% include pageIconL.html %}
+
You will also want to configure PiPedal to provide a Wi-Fi Direct access point (hotspot) that you can connect to using your phone. It's fine to use your home Wi-Fi network to connect to PiPedal when you're at home; but don't forget that when you take PiPedal out to a gig, you will need to ensure that the Wi-Fi Direct access point is enabled before you do.
PiPedal uses LV2 audio plugins. There are literally thousands of freely available high-quality LV2 plugins that are suitable for use as guitar effects.
@@ -50,7 +58,7 @@ The following table shows measured round-trip audio latencies for a MOTU M2 exte
| Buffers |
| Size | 2 | 3 | 4 |
- | 16 | No signal | 185/3.9ms | 201/4.2ms |
+ | 16 | Fails | 185/3.9ms | 201/4.2ms |
| 24 | 192/4.0ms | 213/4.4ms | 236/4.9ms |
| 32 | 219/4.6ms | 236/4.9ms | 272/5.7ms |
| 48 | 253/5.3ms | 299/6.2ms | 348/7.2ms |
diff --git a/docs/Installing.md b/docs/Installing.md
index 382532c..b904d6d 100644
--- a/docs/Installing.md
+++ b/docs/Installing.md
@@ -1,6 +1,14 @@
+---
+page_icon: img/Install3.jpg
+---
+
+
## Installing PiPedal
-### Install for Ubuntu or Raspberry Pi OS (64-bit)
+{% include pageIcon.html %}
+
+### Install for Ubuntu
or Raspberry Pi OS (64-bit)
+
Download the most recent Debian (.deb) package for your platform:
@@ -13,9 +21,11 @@ Install the package by running
cd ~/Downloads
sudo dpkg --install pipedal_1.0.15_arm64.deb
```
-On Raspberry Pi OS, if you have a graphical desktop installed, you can also install package by double-clicking on the downloaded package in the File Manager.
+On Raspberry Pi OS, if you have a graphical desktop installed, you can also install the package by double-clicking on the downloaded package in the File Manager.
After installing, follow the instructions in [Configuring PiPedal after Installation](Configuring.md).
+
+
--------
[<< System Requirements](SystemRequirements.md) | [Up](Documentation.md) | [Configuring PiPedal after Installation >>](Configuring.md)
diff --git a/docs/SystemRequirements.md b/docs/SystemRequirements.md
index 70b638f..8f3bbc8 100644
--- a/docs/SystemRequirements.md
+++ b/docs/SystemRequirements.md
@@ -1,5 +1,10 @@
+---
+page_icon: img/Requirements.jpg
+---
## System Requirements
+{% include pageIcon.html %}
+
* A Raspberry PI 4B or 400, with at least 2GB of RAM to run (4GB recommended).
* To build PiPedal, at least 4GB of RAM (8GB recommended).
* An external USB Audio Adapter, or a Pi audio hat with at least one audio input, and one audio output.
diff --git a/docs/UsingLv2Plugins.md b/docs/UsingLv2Plugins.md
index f597637..82e787b 100644
--- a/docs/UsingLv2Plugins.md
+++ b/docs/UsingLv2Plugins.md
@@ -15,7 +15,7 @@ Here is a brief list of particularly recommend plugin collections.
|[Zam Plugins](https://zamaudio.com) ★★★☆☆ | sudo apt install zam-plugins | Filtering, EQ, and mastering effects. |
| [Calf Studio Gear](https:://calf-studio-gear.org) ★★★☆☆ | sudo apt install calf-plugins | Flanger, filters, reverb, rotary speaker &c |
-The GxPlugins pack requires a manual build; but it's worth the effort. It contains some extraordinarily beautiful amp and effect emulations, many of which are superior to plugins in the main Guitarix package.
+The GxPlugins pack requires a manual build; but it's worth the effort. A pre-built version for aarch64 can be downloaded [here](GxPlugins.md). It contains some extraordinarily beautiful amp and effect emulations, many of which are superior to plugins in the main Guitarix package.
For a more complete (but still incomplete) list of LV2 audio plugins, see [here](https://lv2plug.in/pages/projects.html).
diff --git a/docs/_includes/gallery.html b/docs/_includes/gallery.html
index 691c320..6a458cb 100644
--- a/docs/_includes/gallery.html
+++ b/docs/_includes/gallery.html
@@ -201,8 +201,8 @@
+ "px; left: 0px; top: 0px; opacity: 0.0;"
+ " display: flex; flex-flow: row none; justify-content: space-between;align-items: end; padding:20; opacity: 0.8;'>");
{
- document.writeln("◄
");
- document.writeln("►
");
+ document.writeln("◄
");
+ document.writeln("►
");
}
document.writeln("");
diff --git a/docs/_includes/pageIcon.html b/docs/_includes/pageIcon.html
new file mode 100644
index 0000000..db40f10
--- /dev/null
+++ b/docs/_includes/pageIcon.html
@@ -0,0 +1,8 @@
+
+
+

+
+
diff --git a/docs/_includes/pageIconL.html b/docs/_includes/pageIconL.html
new file mode 100644
index 0000000..f5eb164
--- /dev/null
+++ b/docs/_includes/pageIconL.html
@@ -0,0 +1,7 @@
+
+

+
+
diff --git a/docs/img/AudioLatency.jpg b/docs/img/AudioLatency.jpg
new file mode 100644
index 0000000..45ce738
Binary files /dev/null and b/docs/img/AudioLatency.jpg differ
diff --git a/docs/img/Install2.jpg b/docs/img/Install2.jpg
new file mode 100644
index 0000000..5ac07e8
Binary files /dev/null and b/docs/img/Install2.jpg differ
diff --git a/docs/img/Install3.jpg b/docs/img/Install3.jpg
new file mode 100644
index 0000000..89713d1
Binary files /dev/null and b/docs/img/Install3.jpg differ
diff --git a/docs/img/Installing.jpg b/docs/img/Installing.jpg
new file mode 100644
index 0000000..3138997
Binary files /dev/null and b/docs/img/Installing.jpg differ
diff --git a/docs/img/Requirements.jpg b/docs/img/Requirements.jpg
new file mode 100644
index 0000000..0b6cbca
Binary files /dev/null and b/docs/img/Requirements.jpg differ
diff --git a/docs/img/Setup.jpg b/docs/img/Setup.jpg
new file mode 100644
index 0000000..720ee7a
Binary files /dev/null and b/docs/img/Setup.jpg differ
diff --git a/docs/img/UsbAdapter.jpg b/docs/img/UsbAdapter.jpg
new file mode 100644
index 0000000..e77cce8
Binary files /dev/null and b/docs/img/UsbAdapter.jpg differ
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 946cfc7..0d1ecc5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -9,20 +9,19 @@ include(FindPkgConfig)
#################################################################
# ENABLE/DISABLE VST3 Support.
-# Disabled, pending Steinberg licensing agreement implementation
-# and approval..
+# Disabled, pending approval of Steinberg VST3 License.
# Do not enable unless you have non-GPL3 access to the Steinberg
# SDK.
#################################################################
-set (ENABLE_VST3 0)
+set (ENABLE_VST3 1)
if (ENABLE_VST3)
set (VST3PATH "../../vst3sdk")
message(STATUS " VST3 Support Enabled")
set (VST3_INCLUDES "${VST3PATH}")
-
+ set (VST3_LIBRARIES vst3_lib dl)
if (CMAKE_BUILD_TYPE MATCHES Debug)
add_compile_definitions("DEVELOPMENT")
else()
@@ -34,6 +33,7 @@ if (ENABLE_VST3)
else()
message(STATUS " VST3 Support Disabled")
set (VST3_INCLUDES "")
+ set (VST3_LIBRARIES "")
endif()
########### VST 3 SUPPORT #############################################3
@@ -218,15 +218,12 @@ set (PIPEDAL_INCLUDES
set(PIPEDAL_LIBS libpipedald
pthread atomic stdc++fs asound avahi-common avahi-client systemd
- ${LILV_0_LIBRARIES}
- # ${JACK_LIBRARIES}
+ ${VST3_LIBRARIES}
+ ${LILV_0_LIBRARIES}
+ # ${JACK_LIBRARIES} - pending delete for JACK support.
${LIBNL3_LIBRARIES}
)
-if (VST3_ENABLED)
-
- set (PIPEDAL_LIBS vst3_lib ${PIPEDAL_LIBS})
-endif()
##########################
diff --git a/src/Vst3Effect.cpp b/src/Vst3Effect.cpp
index e0a2ccd..6769e4f 100644
--- a/src/Vst3Effect.cpp
+++ b/src/Vst3Effect.cpp
@@ -70,6 +70,7 @@ std::unique_ptr Vst3Effect::CreateInstance(uint64_t instanceId, cons
result->Load(instanceId, info, pHost);
+
return std::move(result);
}
@@ -876,6 +877,13 @@ void Vst3EffectImpl::fireControlChanged(int control, float normalizedValue)
}
}
+
+const Lv2PluginUiInfo& Vst3EffectImpl::GetCurrentPluginInfo()
+{
+ Vst3Host::Private::UpdateControlInfo(this->controller,this->info.pluginInfo_);
+ return this->info.pluginInfo_;
+}
+
JSON_MAP_BEGIN(Vst3PluginInfo)
JSON_MAP_REFERENCE(Vst3PluginInfo, filePath)
JSON_MAP_REFERENCE(Vst3PluginInfo, version)
diff --git a/src/Vst3Host.cpp b/src/Vst3Host.cpp
index c10740b..03dae19 100644
--- a/src/Vst3Host.cpp
+++ b/src/Vst3Host.cpp
@@ -22,7 +22,6 @@
* SOFTWARE.
*/
-
#include "ss.hpp"
#include "PiPedalHost.hpp"
#include "vst3/Vst3Host.hpp"
@@ -80,17 +79,17 @@ namespace pipedal
std::string cacheFilePath;
public:
- Vst3HostImpl(const std::string&cacheFilePath);
+ Vst3HostImpl(const std::string &cacheFilePath);
const Vst3Host::PluginList &RescanPlugins() override;
const Vst3Host::PluginList &RefreshPlugins() override;
const Vst3Host::PluginList &getPluginList() override { return pluginList; }
std::unique_ptr CreatePlugin(long instanceId, const std::string &url, IHost *pHost) override;
- std::unique_ptr CreatePlugin(PedalBoardItem&pedalBoardItem, IHost*pHost) override;
+ std::unique_ptr CreatePlugin(PedalBoardItem &pedalBoardItem, IHost *pHost) override;
private:
- Lv2PluginUiInfo *GetPluginInfo(const std::string&uri);
+ Lv2PluginUiInfo *GetPluginInfo(const std::string &uri);
void LoadPluginCache();
void SavePluginCache();
@@ -104,7 +103,7 @@ namespace pipedal
void MakeVst3Info(const std::filesystem::path &path, Vst3Host::PluginList &list);
};
- Vst3Host::Ptr Vst3Host::CreateInstance(const std::string&cacheFilePath)
+ Vst3Host::Ptr Vst3Host::CreateInstance(const std::string &cacheFilePath)
{
return std::make_unique(cacheFilePath);
}
@@ -113,15 +112,13 @@ namespace pipedal
} // namespace pipedal
-Vst3HostImpl::Vst3HostImpl(const std::string&cacheFilePath)
-:cacheFilePath(cacheFilePath)
+Vst3HostImpl::Vst3HostImpl(const std::string &cacheFilePath)
+ : cacheFilePath(cacheFilePath)
{
this->hostApplication = new Vst3HostApplication();
EnsureContext();
-
}
-
static bool hasSubCategory(const ClassInfo &classInfo, const std::string &subcategory)
{
for (auto &category : classInfo.subCategories())
@@ -248,6 +245,192 @@ void Vst3HostImpl::EnsureContext()
PluginContextFactory::instance().setPluginContext(this->hostApplication);
}
+void Vst3Host::Private::UpdateControlInfo(IEditController *controller, Lv2PluginUiInfo &pluginInfo)
+{
+ // update UI text and control info.
+ FUnknownPtr iUnitInfo(controller);
+ pluginInfo.controls().resize(0);
+ pluginInfo.port_groups().resize(0);
+
+ if (iUnitInfo)
+ {
+ auto nUnits = iUnitInfo->getUnitCount();
+ for (int i = 0; i < nUnits; ++i)
+ {
+ Steinberg::Vst::UnitInfo unitInfo;
+ iUnitInfo->getUnitInfo(i, unitInfo);
+ Lv2PluginUiPortGroup portGroup(
+ SS(unitInfo.id), VST3::StringConvert::convert(unitInfo.name), SS(unitInfo.parentUnitId), unitInfo.programListId);
+
+ pluginInfo.port_groups().push_back(portGroup);
+ }
+ }
+
+ auto nParams = controller->getParameterCount();
+ for (int32 param = 0; param < nParams; ++param)
+ {
+ Steinberg::Vst::ParameterInfo info;
+ tresult tErr = controller->getParameterInfo(param, info);
+
+ Lv2PluginUiControlPort port;
+ port.index(param); // used for LV2 purposes.
+ port.symbol(SS(info.id)); // Used for VST3 purposes. convert to int to specifiy a vst control id.
+
+ std::string shortTitle = VST3::StringConvert::convert(info.shortTitle);
+ std::string title = VST3::StringConvert::convert(info.title);
+ if (shortTitle.length() == 0)
+ {
+ shortTitle = title;
+ }
+ port.name(shortTitle);
+ port.comment(title);
+
+ // guard against NaN value (mda Talkbox)
+ port.display_priority(param);
+
+ bool isList = (info.flags & ParameterInfo::kIsList) != ParameterInfo::kNoFlags;
+ bool canAutomate = (info.flags & ParameterInfo::kCanAutomate) != ParameterInfo::kNoFlags;
+ bool isProgramChange = (info.flags & ParameterInfo::kIsProgramChange) != ParameterInfo::kNoFlags;
+ bool isReadOnly = (info.flags & ParameterInfo::kIsReadOnly) != ParameterInfo::kNoFlags;
+ bool notOnGui = (info.flags & ParameterInfo::kIsHidden) != ParameterInfo::kNoFlags;
+ bool isBypass = (info.flags & ParameterInfo::kIsBypass) != ParameterInfo::kNoFlags;
+
+ port.is_bypass(isBypass);
+
+ port.not_on_gui(isReadOnly | notOnGui);
+
+ port.min_value(controller->normalizedParamToPlain(info.id, 0));
+ port.max_value(controller->normalizedParamToPlain(info.id, 1));
+
+ double t = controller->normalizedParamToPlain(info.id, info.defaultNormalizedValue);
+ if (std::isnan(t) || std::isinf(t))
+ {
+ t = 0;
+ }
+
+ port.default_value(t);
+
+ port.range_steps(info.stepCount == 0 ? 0 : info.stepCount + 1);
+ if (isList && !isProgramChange)
+ {
+ port.enumeration_property(true);
+ for (int i = 0; i <= info.stepCount; ++i)
+ {
+ double normalizedValue;
+ if (info.stepCount == 0)
+ {
+ normalizedValue = 0;
+ }
+ else
+ {
+ normalizedValue = i * 1.0 / (info.stepCount);
+ }
+
+ String128 strValue;
+ if (controller->getParamStringByValue(info.id, normalizedValue, strValue) == kResultOk)
+ {
+ Lv2ScalePoint scalePoint((float)controller->normalizedParamToPlain(info.id, normalizedValue), VST3::StringConvert::convert(strValue));
+ port.scale_points().push_back(scalePoint);
+ }
+ }
+ }
+ if (info.unitId != 0)
+ {
+ port.port_group(SS(info.unitId));
+ }
+ std::string units = VST3::StringConvert::convert(info.units);
+ if (units.size() != 0)
+ {
+ if (units == "dB")
+ {
+ port.units(Units::db);
+ }
+ else if (units == "%")
+ {
+ port.units(Units::pc);
+ }
+ else if (units == "Hz")
+ {
+ port.units(Units::hz);
+ }
+ else if (units == "kHz")
+ {
+ port.units(Units::khz);
+ }
+ else if (units == "sec" || units == "s")
+ {
+ port.units(Units::s);
+ }
+ else if (units == "ms")
+ {
+ port.units(Units::ms);
+ }
+ else if (units == "cent")
+ {
+ port.units(Units::cent);
+ }
+ else
+ {
+ port.units(Units::custom);
+ port.custom_units(units);
+ }
+ }
+
+ pluginInfo.controls().push_back(port);
+ }
+}
+
+static void UpdateBusInfo(IComponent *component, Lv2PluginUiInfo &pluginInfo)
+{
+
+ int audioOutputs = 0;
+ auto count = component->getBusCount(MediaTypes::kAudio, BusDirections::kOutput);
+ for (int32_t i = 0; i < count; i++)
+ {
+ BusInfo busInfo;
+ if (component->getBusInfo(MediaTypes::kAudio, BusDirections::kOutput, i, busInfo) ==
+ kResultOk)
+ {
+ auto channelName = VST3::StringConvert::convert(busInfo.name, 128);
+
+ if (busInfo.busType == BusTypes::kMain) // aux channels not currently supported.
+ {
+ audioOutputs += busInfo.channelCount;
+ }
+ }
+ }
+ pluginInfo.audio_outputs(audioOutputs);
+
+ count = component->getBusCount(MediaTypes::kAudio, BusDirections::kInput);
+ int audioInputs = 0;
+ for (int32_t i = 0; i < count; i++)
+ {
+ BusInfo busInfo;
+ if (component->getBusInfo(MediaTypes::kAudio, BusDirections::kInput, i, busInfo) ==
+ kResultOk)
+ {
+ auto channelName = VST3::StringConvert::convert(busInfo.name, 128);
+ if (busInfo.busType == BusTypes::kMain) // aux channels not currently supported.
+ {
+ audioInputs += busInfo.channelCount;
+ }
+ }
+ }
+
+ pluginInfo.audio_inputs(audioInputs);
+
+ count = component->getBusCount(MediaTypes::kEvent, BusDirections::kInput);
+ if (count >= 1)
+ {
+ pluginInfo.has_midi_input(1);
+ }
+ count = component->getBusCount(MediaTypes::kEvent, BusDirections::kOutput);
+ if (count >= 1)
+ {
+ pluginInfo.has_midi_output(1);
+ }
+}
+
unique_ptr Vst3HostImpl::MakeDetailedPluginInfo(const PluginFactory &factory, const ClassInfo &classInfo, const std::string &path)
{
IPtr plugProvider = owned(NEW PlugProvider(factory, classInfo, true));
@@ -257,8 +440,6 @@ unique_ptr Vst3HostImpl::MakeDetailedPluginInfo(const PluginFact
FUnknownPtr midiMapping(controller);
- FUnknownPtr iUnitInfo(controller);
-
unique_ptr info = make_unique();
info->filePath_ = path;
@@ -277,166 +458,9 @@ unique_ptr Vst3HostImpl::MakeDetailedPluginInfo(const PluginFact
getVst3PluginType(classInfo));
pluginInfo.plugin_display_type(""); // fill this in later. VSTHost has list of name translations; we don;'t.
- if (iUnitInfo)
- {
- auto nUnits = iUnitInfo->getUnitCount();
- for (int i = 0; i < nUnits; ++i)
- {
- Steinberg::Vst::UnitInfo unitInfo;
- iUnitInfo->getUnitInfo(i, unitInfo);
- Lv2PluginUiPortGroup portGroup(
- SS(unitInfo.id), VST3::StringConvert::convert(unitInfo.name), SS(unitInfo.parentUnitId), unitInfo.programListId);
+ Private::UpdateControlInfo(controller, pluginInfo);
+ UpdateBusInfo(component, pluginInfo);
- pluginInfo.port_groups().push_back(portGroup);
- }
- }
-
- auto nParams = controller->getParameterCount();
- for (int32 param = 0; param < nParams; ++param)
- {
- Steinberg::Vst::ParameterInfo info;
- tresult tErr = controller->getParameterInfo(param, info);
-
- Lv2PluginUiControlPort port;
- port.index(param); // used for LV2 purposes.
- port.symbol(SS(info.id)); // Used for VST3 purposes. convert to int to specifiy a vst control id.
-
- std::string shortTitle = VST3::StringConvert::convert(info.shortTitle);
- std::string title = VST3::StringConvert::convert(info.title);
- if (shortTitle.length() == 0)
- {
- shortTitle = title;
- }
- port.name(shortTitle);
- port.comment(title);
-
-
- // guard against NaN value (mda Talkbox)
- port.display_priority(param);
-
- bool isList = (info.flags & ParameterInfo::kIsList) != ParameterInfo::kNoFlags;
- bool canAutomate = (info.flags & ParameterInfo::kCanAutomate) != ParameterInfo::kNoFlags;
- bool isProgramChange = (info.flags & ParameterInfo::kIsProgramChange) != ParameterInfo::kNoFlags;
- bool isReadOnly = (info.flags & ParameterInfo::kIsReadOnly) != ParameterInfo::kNoFlags;
- bool notOnGui = (info.flags & ParameterInfo::kIsHidden) != ParameterInfo::kNoFlags;
- bool isBypass = (info.flags & ParameterInfo::kIsBypass) != ParameterInfo::kNoFlags;
-
- port.is_bypass(isBypass);
-
- port.not_on_gui(isReadOnly | notOnGui);
-
- port.min_value(controller->normalizedParamToPlain(info.id,0));
- port.max_value(controller->normalizedParamToPlain(info.id,1));
-
- double t = controller->normalizedParamToPlain(info.id,info.defaultNormalizedValue);
- if (std::isnan(t) || std::isinf(t))
- {
- t = 0;
- }
-
- port.default_value(t);
-
- port.range_steps(info.stepCount == 0 ? 0 : info.stepCount + 1);
- if (isList && !isProgramChange)
- {
- port.enumeration_property(true);
- for (int i = 0; i <= info.stepCount; ++i)
- {
- double normalizedValue;
- if (info.stepCount == 0)
- {
- normalizedValue = 0;
- }
- else
- {
- normalizedValue = i * 1.0 / (info.stepCount);
- }
-
- String128 strValue;
- if (controller->getParamStringByValue(info.id, normalizedValue, strValue) == kResultOk)
- {
- Lv2ScalePoint scalePoint((float)controller->normalizedParamToPlain(info.id,normalizedValue), VST3::StringConvert::convert(strValue));
- port.scale_points().push_back(scalePoint);
- }
- }
- }
- if (info.unitId != 0)
- {
- port.port_group(SS(info.unitId));
- }
- std::string units = VST3::StringConvert::convert(info.units);
- if (units.size() != 0)
- {
- if (units == "dB")
- {
- port.units(Units::db);
- } else if (units == "%")
- {
- port.units(Units::pc);
- } else if (units == "Hz")
- {
- port.units(Units::hz);
- } else if (units == "kHz")
- {
- port.units(Units::khz);
- } else if (units == "sec" || units == "s")
- {
- port.units(Units::s);
- } else if (units == "ms")
- {
- port.units(Units::ms);
- } else if (units == "cent")
- {
- port.units(Units::cent);
- } else {
- port.units(Units::custom);
- port.custom_units(units);
- }
- }
-
- pluginInfo.controls().push_back(port);
- }
-
- auto count = component->getBusCount(MediaTypes::kAudio, BusDirections::kOutput);
- for (int32_t i = 0; i < count; i++)
- {
- BusInfo busInfo;
- if (component->getBusInfo(MediaTypes::kAudio, BusDirections::kOutput, i, busInfo) ==
- kResultOk)
- {
- auto channelName = VST3::StringConvert::convert(busInfo.name, 128);
-
- if (busInfo.busType == BusTypes::kMain) // aux channels not currently supported.
- {
- pluginInfo.audio_outputs(pluginInfo.audio_outputs() + busInfo.channelCount);
- }
- }
- }
- count = component->getBusCount(MediaTypes::kAudio, BusDirections::kInput);
- for (int32_t i = 0; i < count; i++)
- {
- BusInfo busInfo;
- if (component->getBusInfo(MediaTypes::kAudio, BusDirections::kInput, i, busInfo) ==
- kResultOk)
- {
- auto channelName = VST3::StringConvert::convert(busInfo.name, 128);
-
- if (busInfo.busType == BusTypes::kMain) // aux channels not currently supported.
- {
- pluginInfo.audio_inputs(pluginInfo.audio_inputs() + busInfo.channelCount);
- }
- }
- }
- count = component->getBusCount(MediaTypes::kEvent, BusDirections::kInput);
- if (count >= 1)
- {
- pluginInfo.has_midi_input(1);
- }
- count = component->getBusCount(MediaTypes::kEvent, BusDirections::kOutput);
- if (count >= 1)
- {
- pluginInfo.has_midi_output(1);
- }
return info;
}
}
@@ -525,8 +549,8 @@ const Vst3Host::PluginList &Vst3HostImpl::RescanPlugins()
PluginList list;
- //pathList.insert(pathList.begin(),"/home/pi/.vst3/mda-vst3.vst3");
-
+ // pathList.insert(pathList.begin(),"/home/pi/.vst3/mda-vst3.vst3");
+
for (auto &path : pathList)
{
if (path == std::string("/home/pi/.vst3/mda-vst3.vst3"))
@@ -571,26 +595,29 @@ std::unique_ptr Vst3HostImpl::CreatePlugin(long instanceId, const st
static inline uint8_t Hex(char c)
{
- if (c >= '0' && c <= '9') return c-'0';
- if (c >= 'a' && c <= 'z') return c-'a'+10;
- if (c >= 'A' && c <= 'Z') return c-'A'+10;
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'z')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'Z')
+ return c - 'A' + 10;
throw Vst3Exception("Invalid state bundle.");
}
-static std::vector HexToByteArray(const std::string&hexState)
+static std::vector HexToByteArray(const std::string &hexState)
{
std::vector result;
- result.resize(hexState.length()/2);
+ result.resize(hexState.length() / 2);
for (int i = 0; i < result.size(); ++i)
{
- result[i] = Hex(hexState[i*2])*16 + Hex(hexState[i*2+1]);
+ result[i] = Hex(hexState[i * 2]) * 16 + Hex(hexState[i * 2 + 1]);
}
return result;
}
-std::unique_ptr Vst3HostImpl::CreatePlugin(PedalBoardItem&pedalBoardItem, IHost*pHost)
+std::unique_ptr Vst3HostImpl::CreatePlugin(PedalBoardItem &pedalBoardItem, IHost *pHost)
{
- std::unique_ptr result = CreatePlugin(pedalBoardItem.instanceId(),pedalBoardItem.uri(),pHost);
+ std::unique_ptr result = CreatePlugin(pedalBoardItem.instanceId(), pedalBoardItem.uri(), pHost);
auto pluginInfo = this->GetPluginInfo(pedalBoardItem.uri());
if (!pluginInfo)
{
@@ -600,8 +627,10 @@ std::unique_ptr Vst3HostImpl::CreatePlugin(PedalBoardItem&pedalBoard
{
std::vector state = HexToByteArray(pedalBoardItem.vstState());
result->SetState(state);
-
- for (ControlValue &controlValue: pedalBoardItem.controlValues())
+ }
+ else
+ {
+ for (const ControlValue &controlValue : pedalBoardItem.controlValues())
{
int32_t index = -1;
for (size_t i = 0; i < pluginInfo->controls().size(); ++i)
@@ -614,35 +643,39 @@ std::unique_ptr Vst3HostImpl::CreatePlugin(PedalBoardItem&pedalBoard
}
if (index != -1)
{
- float value = result->GetControlValue(index);
- controlValue.value(value);
+ result->SetControl(index, controlValue.value());
}
-
- }
-
- } else {
- for (const ControlValue &controlValue: pedalBoardItem.controlValues())
- {
- int32_t index = -1;
- for (size_t i = 0; i < pluginInfo->controls().size(); ++i)
- {
- if (pluginInfo->controls()[i].symbol() == controlValue.key())
- {
- index = (int32_t)(pluginInfo->controls()[i].index());
- break;
- }
- }
- if (index != -1)
- {
- result->SetControl(index,controlValue.value());
- }
-
}
}
+ for (ControlValue &controlValue : pedalBoardItem.controlValues())
+ {
+ int32_t index = -1;
+ for (size_t i = 0; i < pluginInfo->controls().size(); ++i)
+ {
+ if (pluginInfo->controls()[i].symbol() == controlValue.key())
+ {
+ index = (int32_t)(pluginInfo->controls()[i].index());
+ break;
+ }
+ }
+ if (index != -1)
+ {
+ float t = result->GetControlValue(index);
+ if (std::isnan(t) || std::isinf(t))
+ {
+ t = 0;
+ }
+ controlValue.value(t);
+ } else {
+ Lv2Log::warning(SS(pedalBoardItem.pluginName() << ": Control key not found. key=" << controlValue.key() ));
+ }
+ }
+
+ result->GetCurrentPluginInfo();
+
return result;
}
-
#include
void Vst3HostImpl::LoadPluginCache()
@@ -656,7 +689,6 @@ void Vst3HostImpl::LoadPluginCache()
reader.read(&(this->pluginList));
}
-
}
}
void Vst3HostImpl::SavePluginCache()
@@ -670,14 +702,12 @@ void Vst3HostImpl::SavePluginCache()
writer.write(this->pluginList);
}
-
}
-
}
-Lv2PluginUiInfo *Vst3HostImpl::GetPluginInfo(const std::string&uri)
+Lv2PluginUiInfo *Vst3HostImpl::GetPluginInfo(const std::string &uri)
{
- for (const auto &pluginInfo: pluginList)
+ for (const auto &pluginInfo : pluginList)
{
if (pluginInfo->pluginInfo_.uri() == uri)
{
@@ -686,4 +716,3 @@ Lv2PluginUiInfo *Vst3HostImpl::GetPluginInfo(const std::string&uri)
}
return nullptr;
}
-
\ No newline at end of file
diff --git a/src/vst3/Vst3Effect.hpp b/src/vst3/Vst3Effect.hpp
index 0b3271c..fcc160b 100644
--- a/src/vst3/Vst3Effect.hpp
+++ b/src/vst3/Vst3Effect.hpp
@@ -59,8 +59,11 @@ namespace pipedal {
public:
using Ptr = std::unique_ptr;
+
virtual void Prepare(int32_t sampleRate, size_t maxBufferSize,int inputChannels, int outputChannels) = 0;
+ virtual const Lv2PluginUiInfo& GetCurrentPluginInfo() = 0;
+
virtual void Activate() = 0;
virtual void Deactivate() = 0;
virtual void Unprepare() = 0;
diff --git a/src/vst3/Vst3EffectImpl.hpp b/src/vst3/Vst3EffectImpl.hpp
index a544cd6..abf4d82 100644
--- a/src/vst3/Vst3EffectImpl.hpp
+++ b/src/vst3/Vst3EffectImpl.hpp
@@ -81,6 +81,9 @@ namespace pipedal
void SetState(const std::vector state);
+
+ const Lv2PluginUiInfo& GetCurrentPluginInfo();
+
virtual void CheckSync();
//- PiPedalHost Interfaces.
diff --git a/src/vst3/Vst3Host.hpp b/src/vst3/Vst3Host.hpp
index f4b1049..1217725 100644
--- a/src/vst3/Vst3Host.hpp
+++ b/src/vst3/Vst3Host.hpp
@@ -35,6 +35,14 @@
#include "PedalBoard.hpp"
+namespace Steinberg
+{
+ namespace Vst {
+ class IEditController; // forward decl.
+ }
+}
+
+
namespace pipedal {
@@ -64,6 +72,13 @@
static Ptr CreateInstance(const std::string &cacheFilePath = "");
+ public:
+ class Private { // private use.
+ public:
+
+ static void UpdateControlInfo(::Steinberg::Vst::IEditController *controller, Lv2PluginUiInfo &pluginInfo);
+ };
+
};