+2
-2
@@ -1,13 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.16.0)
|
||||
project(pipedal
|
||||
VERSION 1.5.93
|
||||
VERSION 1.5.94
|
||||
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.5.93-Experimental")
|
||||
set (DISPLAY_VERSION "PiPedal v1.5.94-Experimental")
|
||||
set (PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE})
|
||||
set (CMAKE_INSTALL_PREFIX "/usr/")
|
||||
|
||||
|
||||
@@ -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.5.93</a>
|
||||
Download: <a href='https://rerdavies.github.io/pipedal/download.html'>v1.5.94</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.5.93 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. TooB NAM calibration, manipulation of i/r files in Toob Convolution Reverb, new Parametric EQ and 3 Band EQ plugins, and MORE!
|
||||
#### NEW version 1.5.94 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. TooB NAM calibration, manipulation of i/r files in Toob Convolution Reverb, new Parametric EQ and 3 Band EQ plugins, and MORE!
|
||||
|
||||
|
||||
|
||||
|
||||
+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.5.93](https://github.com/rerdavies/pipedal/releases/download/v1.5.93/pipedal_1.5.93_arm64.deb)
|
||||
- [Ubuntu 24.04 through 25.04 (aarch64) v1.5.93](https://github.com/rerdavies/pipedal/releases/download/v1.5.93/pipedal_1.5.93_arm64.deb)
|
||||
- [Ubuntu 24.04 through 25.04 (amd64) v1.5.93](https://github.com/rerdavies/pipedal/releases/download/v1.5.93/pipedal_1.5.93_amd64.deb)
|
||||
- [Raspberry Pi OS bookworm (aarch64) v1.5.94](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_arm64.deb)
|
||||
- [Ubuntu 24.04 through 25.04 (aarch64) v1.5.94](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_arm64.deb)
|
||||
- [Ubuntu 24.04 through 25.04 (amd64) v1.5.94](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_amd64.deb)
|
||||
|
||||
|
||||
Version 1.5.93 has been tested on Raspberry Pi OS bookworm, Ubuntu 24.04 (amd64), Ubuntu 24.10 (aarch64), and Ubuntu 25.04 (aarch64). Download the appropriate package for your platform, and install using the following procedure:
|
||||
Version 1.5.94 has been tested on Raspberry Pi OS bookworm, Ubuntu 24.04 (amd64), Ubuntu 24.10 (aarch64), and Ubuntu 25.04 (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.5.93_arm64.deb
|
||||
sudo apt-get install ./pipedal_1.5.94_arm64.deb
|
||||
```
|
||||
You MUST use `apt-get`. `apt` will not install downloaded packages; and `dpkg -i` will not install dependencies.
|
||||
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
# Release Notes
|
||||
|
||||
## PiPedal 1.5.94 Beta
|
||||
|
||||
A minor release that allows use of plugins with audio sidechains.
|
||||
|
||||
### Features in this release
|
||||
|
||||
- **Sidechains**. You can now load plugins that have sidechain inputs. This allows you to use compressor and gate plugins, particularly, that often have sidechains, but also enables a broad class of dynamics processing plugins that would not be loadable in previous versions of PiPedal. Plugins that have sidechain inputs will show a dropdown control in their control view which allows you to select the source of the sidechain input. The source plugin for the sidechain must appear earlier in the signal chain.
|
||||
|
||||
Although sidechain processing is occasionally useful in guitar effect pedalboards, the more important part of this feature is that it allows you to load plugins that were rejected by previous versions of PiPedal because they had optional sidechain inputs.
|
||||
|
||||
|
||||
## PiPedal 1.5.93 Release
|
||||
|
||||
### Major Features in This Build
|
||||
|
||||
@@ -17,7 +17,7 @@ Here is a brief list of other particularly recommended plugin collections.
|
||||
| Collection | To Install | Description |
|
||||
|---------------------------------|---------------------------------------|------------------|
|
||||
| [Guitarix](https://guitarix.org) ★★★☆☆ | `sudo apt install guitarix-lv2` | A large collection of guitar amplifiers and effects. Prior to the advent of neural network amplifier emulations, Guitarix was the go-to source for open-source amp modeling.|
|
||||
| [GxPlugins](https://github.com/brummer10/GxPlugins.lv2) ★★★★★ | `sudo apt install gxplugins` | Additional amps and effects from the the principle developer of Guitarix, many of which are significantly superior to the original Guitarix plugins. Most of these plugins
|
||||
| [GxPlugins](https://github.com/brummer10/GxPlugins.lv2) ★★★★★ | On Raspberry Pi Os: `sudo apt install gxplugins`, on amd64/x64 donwload from [Github](https://github.com/brummer10/GxPlugins.lv2/releases) | Additional amps and effects from the the principle developer of Guitarix, many of which are significantly superior to the original Guitarix plugins. Most of these plugins
|
||||
use circuit simulation to directly model actual original circuits. The results are uniformly excellent.|
|
||||
| [DPF Plugins](https://github.com/DISTRHO/DPF-Plugins) ★★★★★ | `sudo apt install dpf-plugins-lv2` | Useful and interesting guitar effect plugins. Bit crusher, pitch shift, reverbs, 3-band EQ and more. |
|
||||
| [MDA Plugins](http://drobilla.net/software/mda-lv2.html) ★★★★☆ | `sudo apt install mda-lv2` | 36 high-quality plugs |
|
||||
|
||||
+4
-4
@@ -4,9 +4,9 @@
|
||||
|
||||
Download the most recent Debian (.deb) package for your platform:
|
||||
|
||||
- [Raspberry Pi OS bookworm (aarch64) v1.5.93 Beta](https://github.com/rerdavies/pipedal/releases/download/v1.5.93/pipedal_1.5.93_arm64.deb)
|
||||
- [Ubuntu 24.x, 25.04 (aarch64) v1.5.93 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.5.93/pipedal_1.5.93_arm64.deb)
|
||||
- [Ubuntu 24.x, 25.04 (amd64) v1.5.93 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.5.93/pipedal_1.5.93_amd64.deb)
|
||||
- [Raspberry Pi OS bookworm (aarch64) v1.5.94 Beta](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_arm64.deb)
|
||||
- [Ubuntu 24.x, 25.04 (aarch64) v1.5.94 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_arm64.deb)
|
||||
- [Ubuntu 24.x, 25.04 (amd64) v1.5.94 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_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.5.93_arm64.deb
|
||||
sudo apt-get install ./pipedal_1.5.94_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.
|
||||
|
||||
+1
-1
Submodule modules/libzip updated: 92035140df...fb59b49dff
@@ -54,7 +54,9 @@ namespace pipedal {
|
||||
|
||||
virtual int GetNumberOfInputAudioBuffers() const = 0; // may be different if plugin has zero inputs.
|
||||
virtual int GetNumberOfOutputAudioBuffers() const = 0; // may be different if plugin has zero inputs.
|
||||
virtual int GetNumberOfSidechainAudioBuffers() const { return 0; }
|
||||
virtual float *GetAudioInputBuffer(int index) const = 0;
|
||||
virtual float *GetAudioSidechainBuffer(int index) const { throw std::runtime_error("Not implemented"); }
|
||||
virtual float *GetAudioOutputBuffer(int index) const = 0;
|
||||
virtual void ResetAtomBuffers() = 0;
|
||||
|
||||
@@ -65,6 +67,7 @@ namespace pipedal {
|
||||
|
||||
virtual void SetAudioInputBuffer(int index, float *buffer) = 0;
|
||||
virtual void SetAudioOutputBuffer(int index, float*buffer) = 0;
|
||||
virtual void SetAudioSidechainBuffer(int index, float *buffer) { throw std::runtime_error("Not implemented");}
|
||||
|
||||
virtual void Activate() = 0;
|
||||
|
||||
|
||||
+78
-5
@@ -384,7 +384,12 @@ void Lv2Effect::PreparePortIndices()
|
||||
{
|
||||
if (port->is_input())
|
||||
{
|
||||
this->inputAudioPortIndices.push_back(portIndex);
|
||||
if (port->is_sidechain())
|
||||
{
|
||||
this->inputSidechainPortIndices.push_back(portIndex);
|
||||
} else {
|
||||
this->inputAudioPortIndices.push_back(portIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -432,6 +437,7 @@ void Lv2Effect::PreparePortIndices()
|
||||
this->maxInputControlPort = maxInputControlPort;
|
||||
|
||||
inputAudioBuffers.resize(inputAudioPortIndices.size());
|
||||
inputSidechainBuffers.resize(inputSidechainPortIndices.size());
|
||||
outputAudioBuffers.resize(outputAudioPortIndices.size());
|
||||
inputAtomBuffers.resize(inputAtomPortIndices.size());
|
||||
outputAtomBuffers.resize(outputAtomPortIndices.size());
|
||||
@@ -439,9 +445,7 @@ void Lv2Effect::PreparePortIndices()
|
||||
if (RequiresBufferStaging())
|
||||
{
|
||||
EnableBufferStaging(
|
||||
GetStagedBufferSize(),
|
||||
this->GetNumberOfInputAudioBuffers(),
|
||||
this->GetNumberOfOutputAudioBuffers());
|
||||
GetStagedBufferSize());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,6 +477,7 @@ void Lv2Effect::PrepareNoInputEffect(int numberOfInputs, size_t maxBufferSize)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Lv2Effect::SetAudioInputBuffer(int index, float *buffer)
|
||||
{
|
||||
this->inputAudioBuffers.at(index) = buffer;
|
||||
@@ -512,6 +517,32 @@ void Lv2Effect::SetAudioInputBuffer(int index, float *buffer)
|
||||
// }
|
||||
}
|
||||
}
|
||||
void Lv2Effect::SetAudioSidechainBuffer(int index, float *buffer)
|
||||
{
|
||||
this->inputSidechainBuffers.at(index) = buffer;
|
||||
|
||||
if (borrowedEffect)
|
||||
{
|
||||
// Already running on the realtime thread,
|
||||
// so don't update the audio ports until the effect gets placed on the realtime thread.
|
||||
return;
|
||||
}
|
||||
|
||||
if (stagingBufferSize != 0)
|
||||
{
|
||||
int pluginIndex = this->inputSidechainPortIndices.at(index);
|
||||
if (index >= sidechainStagingBufferPointers.size())
|
||||
{
|
||||
throw std::runtime_error("Invalid input staging buffer index.");
|
||||
}
|
||||
lilv_instance_connect_port(this->pInstance, pluginIndex, sidechainStagingBufferPointers.at(index));
|
||||
}
|
||||
else
|
||||
{
|
||||
int pluginIndex = this->inputSidechainPortIndices.at(index);
|
||||
lilv_instance_connect_port(this->pInstance, pluginIndex, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void Lv2Effect::SetAudioInputBuffer(float *left)
|
||||
{
|
||||
@@ -664,6 +695,14 @@ void Lv2Effect::UpdateAudioPorts()
|
||||
lilv_instance_connect_port(pInstance, portIndex, outputStagingBufferPointers.at(i));
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < this->inputSidechainPortIndices.size(); ++i)
|
||||
{
|
||||
int portIndex = this->inputSidechainPortIndices.at(i);
|
||||
if (sidechainStagingBufferPointers.at(i) != nullptr)
|
||||
{
|
||||
lilv_instance_connect_port(pInstance, portIndex, sidechainStagingBufferPointers.at(i));
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < this->inputAtomPortIndices.size(); ++i)
|
||||
{
|
||||
if (i == 0)
|
||||
@@ -710,6 +749,15 @@ void Lv2Effect::UpdateAudioPorts()
|
||||
lilv_instance_connect_port(pInstance, portIndex, GetAudioOutputBuffer(i));
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < this->inputSidechainPortIndices.size(); ++i)
|
||||
{
|
||||
int portIndex = this->inputSidechainPortIndices.at(i);
|
||||
if (GetAudioSidechainBuffer(i) != nullptr)
|
||||
{
|
||||
lilv_instance_connect_port(pInstance, portIndex, GetAudioSidechainBuffer(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < this->inputAtomPortIndices.size(); ++i)
|
||||
{
|
||||
auto atomInputBuffer = this->GetAtomInputBuffer(i);
|
||||
@@ -877,6 +925,16 @@ size_t Lv2Effect::stageToInput(size_t inputSampleOffset, size_t samples)
|
||||
pOutput[i] = pInput[i];
|
||||
}
|
||||
}
|
||||
for (size_t nSidechain = 0; nSidechain < this->inputSidechainBuffers.size(); ++nSidechain)
|
||||
{
|
||||
float *restrict pInput = this->inputSidechainBuffers[nSidechain] + inputSampleOffset;
|
||||
float *restrict pOutput = this->sidechainStagingBufferPointers.at(nSidechain) + this->stagingInputIx;
|
||||
for (size_t i = 0; i < thisTime; ++i)
|
||||
{
|
||||
pOutput[i] = pInput[i];
|
||||
}
|
||||
|
||||
}
|
||||
this->stagingInputIx += thisTime;
|
||||
inputSampleOffset += thisTime;
|
||||
|
||||
@@ -1487,8 +1545,11 @@ void Lv2Effect::SetPathPatchProperty(const std::string &propertyUri, const std::
|
||||
mainThreadPathProperties[propertyUri] = jsonAtom;
|
||||
}
|
||||
|
||||
void Lv2Effect::EnableBufferStaging(size_t bufferSize, size_t nInputs, size_t nOutputs)
|
||||
void Lv2Effect::EnableBufferStaging(size_t bufferSize )
|
||||
{
|
||||
size_t nInputs = this->GetNumberOfInputAudioBuffers();
|
||||
size_t nSidechainInputs = this->GetNumberOfSidechainAudioBuffers();
|
||||
size_t nOutputs = this->GetNumberOfOutputAudioBuffers();
|
||||
|
||||
stagingBufferSize = bufferSize;
|
||||
stagingOutputIx = bufferSize;
|
||||
@@ -1520,6 +1581,12 @@ void Lv2Effect::EnableBufferStaging(size_t bufferSize, size_t nInputs, size_t nO
|
||||
inputStagingBuffers[i][bufferSize] = 99.9f; // guard entry
|
||||
inputStagingBufferPointers.at(i) = inputStagingBuffers.at(i).data();
|
||||
}
|
||||
for (size_t i = 0; i < nSidechainInputs; ++i)
|
||||
{
|
||||
sidechainStagingBuffers.at(i).resize(bufferSize + 1);
|
||||
sidechainStagingBuffers[i][bufferSize] = 99.9f; // guard entry
|
||||
sidechainStagingBufferPointers.at(i) = sidechainStagingBuffers.at(i).data();
|
||||
}
|
||||
for (size_t i = 0; i < nOutputs; ++i)
|
||||
{
|
||||
outputStagingBuffers.at(i).resize(bufferSize + 1);
|
||||
@@ -1571,6 +1638,12 @@ float *Lv2Effect::GetAudioInputBuffer(int index) const
|
||||
throw std::range_error("Lv2Effect::GetAudioInputBuffer");
|
||||
return this->inputAudioBuffers.at(index);
|
||||
}
|
||||
float *Lv2Effect::GetAudioSidechainBuffer(int index) const
|
||||
{
|
||||
if (index < 0 || index >= this->inputSidechainBuffers.size())
|
||||
throw std::range_error("Lv2Effect::GetAudioSidechainBuffer");
|
||||
return this->inputSidechainBuffers.at(index);
|
||||
}
|
||||
|
||||
float *Lv2Effect::GetAudioOutputBuffer(int index) const
|
||||
{
|
||||
|
||||
+20
-11
@@ -81,6 +81,8 @@ namespace pipedal
|
||||
std::vector<int> inputAudioPortIndices;
|
||||
std::vector<int> outputAudioPortIndices;
|
||||
|
||||
std::vector<int> inputSidechainPortIndices;
|
||||
|
||||
std::vector<int> inputAtomPortIndices;
|
||||
std::vector<int> outputAtomPortIndices;
|
||||
|
||||
@@ -90,6 +92,7 @@ namespace pipedal
|
||||
std::vector<int> midiInputIndices;
|
||||
|
||||
std::vector<float *> inputAudioBuffers;
|
||||
std::vector<float *> inputSidechainBuffers;
|
||||
std::vector<float *> outputAudioBuffers;
|
||||
|
||||
std::vector<char *> inputAtomBuffers;
|
||||
@@ -212,7 +215,7 @@ namespace pipedal
|
||||
|
||||
bool borrowedEffect = false;
|
||||
bool activated = false;
|
||||
void EnableBufferStaging(size_t bufferSize, size_t nInputs, size_t nOutputs);
|
||||
void EnableBufferStaging(size_t bufferSize);
|
||||
void CheckStagingBufferSentries();
|
||||
|
||||
public:
|
||||
@@ -262,8 +265,10 @@ namespace pipedal
|
||||
size_t stagingInputIx = 0;
|
||||
size_t stagingOutputIx = 0;
|
||||
std::vector<std::vector<float>> inputStagingBuffers;
|
||||
std::vector<std::vector<float>> sidechainStagingBuffers;
|
||||
std::vector<std::vector<float>> outputStagingBuffers;
|
||||
std::vector<float*> inputStagingBufferPointers;
|
||||
std::vector<float*> sidechainStagingBufferPointers;
|
||||
std::vector<float*> outputStagingBufferPointers;
|
||||
|
||||
std::vector<uint8_t> stagedInputAtomBuffer;
|
||||
@@ -297,22 +302,26 @@ namespace pipedal
|
||||
|
||||
virtual void ResetAtomBuffers();
|
||||
virtual uint64_t GetInstanceId() const { return instanceId; }
|
||||
virtual int GetNumberOfInputAudioPorts() const { return inputAudioPortIndices.size(); }
|
||||
virtual int GetNumberOfOutputAudioPorts() const { return outputAudioPortIndices.size(); }
|
||||
virtual int GetNumberOfInputAudioPorts() const override { return inputAudioPortIndices.size(); }
|
||||
virtual int GetNumberOfOutputAudioPorts() const override { return outputAudioPortIndices.size(); }
|
||||
|
||||
virtual int GetNumberOfInputAtomPorts() const { return inputAtomPortIndices.size(); }
|
||||
virtual int GetNumberOfInputAtomPorts() const { return inputAtomPortIndices.size(); }
|
||||
|
||||
virtual int GetNumberOfOutputAtomPorts() const { return outputAtomPortIndices.size(); }
|
||||
virtual int GetNumberOfMidiInputPorts() const { return inputMidiPortIndices.size(); }
|
||||
virtual int GetNumberOfMidiOutputPorts() const { return outputMidiPortIndices.size(); }
|
||||
virtual int GetNumberOfOutputAtomPorts() const { return outputAtomPortIndices.size(); }
|
||||
virtual int GetNumberOfMidiInputPorts() const { return inputMidiPortIndices.size(); }
|
||||
virtual int GetNumberOfMidiOutputPorts() const { return outputMidiPortIndices.size(); }
|
||||
|
||||
|
||||
|
||||
virtual int GetNumberOfInputAudioBuffers() const { return this->inputAudioBuffers.size(); }
|
||||
virtual int GetNumberOfOutputAudioBuffers() const {return this->outputAudioBuffers.size(); }
|
||||
virtual int GetNumberOfInputAudioBuffers() const override { return this->inputAudioBuffers.size(); }
|
||||
virtual int GetNumberOfSidechainAudioBuffers() const override { return this->inputSidechainBuffers.size(); }
|
||||
virtual int GetNumberOfOutputAudioBuffers() const override {return this->outputAudioBuffers.size(); }
|
||||
|
||||
virtual void SetAudioInputBuffer(int index, float *buffer);
|
||||
virtual float *GetAudioInputBuffer(int index) const;
|
||||
virtual void SetAudioInputBuffer(int index, float *buffer) override;
|
||||
virtual float *GetAudioInputBuffer(int index) const override;
|
||||
|
||||
virtual void SetAudioSidechainBuffer(int index, float *buffer) override;
|
||||
virtual float *GetAudioSidechainBuffer(int index) const override;
|
||||
|
||||
|
||||
virtual void SetAudioInputBuffer(float *buffer);
|
||||
|
||||
+54
-1
@@ -176,6 +176,60 @@ std::vector<float *> Lv2Pedalboard::PrepareItems(
|
||||
auto bufferR = inputBuffers[1];
|
||||
}
|
||||
}
|
||||
// Connect sidechain buffers.
|
||||
|
||||
if (pLv2Effect->GetNumberOfSidechainAudioBuffers() != 0)
|
||||
{
|
||||
if (item.sideChainInputId() == -2) // -2 means "use pedalboard inputs".
|
||||
{
|
||||
for (size_t i = 0; i < pLv2Effect->GetNumberOfSidechainAudioBuffers(); ++i)
|
||||
{
|
||||
if (i < this->pedalboardInputBuffers.size())
|
||||
{
|
||||
pLv2Effect->SetAudioSidechainBuffer(i, this->pedalboardInputBuffers[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// just use the first output buffer for all sidechain inputs.
|
||||
pLv2Effect->SetAudioSidechainBuffer(i, this->pedalboardInputBuffers[0]);
|
||||
}
|
||||
}
|
||||
} else if (item.sideChainInputId() != -1) {
|
||||
IEffect *pSideChainInput = GetEffect(item.sideChainInputId());
|
||||
|
||||
if (pSideChainInput)
|
||||
{
|
||||
for (size_t i = 0; i < pLv2Effect->GetNumberOfSidechainAudioBuffers(); ++i)
|
||||
{
|
||||
if (i < pSideChainInput->GetNumberOfOutputAudioBuffers())
|
||||
{
|
||||
pLv2Effect->SetAudioSidechainBuffer(i, pSideChainInput->GetAudioOutputBuffer(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
// just use the first output buffer for all sidechain inputs.
|
||||
pLv2Effect->SetAudioSidechainBuffer(i, pSideChainInput->GetAudioOutputBuffer(0));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Internal error: Sidechain input IEffect not found.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No sidechain input. Feed zero buffer to all sidechain audio ports..
|
||||
for (size_t i = 0; i < pLv2Effect->GetNumberOfSidechainAudioBuffers(); ++i)
|
||||
{
|
||||
|
||||
// just use one buffer for all plugins
|
||||
if (!this->pedalboardSidechainBuffer)
|
||||
{
|
||||
this->pedalboardSidechainBuffer = CreateNewAudioBuffer();
|
||||
}
|
||||
pLv2Effect->SetAudioSidechainBuffer(i, this->pedalboardSidechainBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check to see whether we need buffer staging.
|
||||
bool requiresBufferStaging = false;
|
||||
@@ -194,7 +248,6 @@ std::vector<float *> Lv2Pedalboard::PrepareItems(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!requiresBufferStaging)
|
||||
{
|
||||
this->processActions.push_back(
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace pipedal
|
||||
BufferPool bufferPool;
|
||||
std::vector<float *> pedalboardInputBuffers;
|
||||
std::vector<float *> pedalboardOutputBuffers;
|
||||
float *pedalboardSidechainBuffer = nullptr;
|
||||
|
||||
std::vector<std::shared_ptr<IEffect>> effects;
|
||||
std::vector<IEffect *> realtimeEffects;
|
||||
|
||||
@@ -534,6 +534,7 @@ JSON_MAP_BEGIN(PedalboardItem)
|
||||
JSON_MAP_REFERENCE(PedalboardItem,title)
|
||||
JSON_MAP_REFERENCE(PedalboardItem,useModUi)
|
||||
JSON_MAP_REFERENCE(PedalboardItem,iconColor)
|
||||
JSON_MAP_REFERENCE(PedalboardItem,sideChainInputId)
|
||||
JSON_MAP_END()
|
||||
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ public:
|
||||
std::string title_;
|
||||
bool useModUi_ = false;
|
||||
std::string iconColor_;
|
||||
int64_t sideChainInputId_ = -1;
|
||||
|
||||
// non persistent state.
|
||||
PropertyMap patchProperties;
|
||||
@@ -138,6 +139,7 @@ public:
|
||||
GETTER_SETTER_REF(title)
|
||||
GETTER_SETTER_REF(iconColor)
|
||||
GETTER_SETTER(useModUi)
|
||||
GETTER_SETTER(sideChainInputId)
|
||||
|
||||
Lv2PluginState&lv2State() { return lv2State_; } // non-const version.
|
||||
GETTER_SETTER_REF(lilvPresetUri)
|
||||
|
||||
@@ -2428,7 +2428,7 @@ void PiPedalModel::OnNotifyPathPatchPropertyReceived(
|
||||
auto i = pedalboardItem->pathProperties_.find(pathPatchPropertyUri);
|
||||
if (i != pedalboardItem->pathProperties_.end())
|
||||
{
|
||||
std::string abstractAtomString = storage.ToAbstractPathJson(atomString);
|
||||
std::string abstractAtomString = storage.ToAbstractPathFromJson(atomString);
|
||||
pedalboardItem->pathProperties_[pathPatchPropertyUri] = abstractAtomString;
|
||||
|
||||
std::vector<IPiPedalModelSubscriber::ptr> t{subscribers.begin(), subscribers.end()};
|
||||
|
||||
+111
-7
@@ -121,6 +121,7 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld)
|
||||
enumeration_property_uri = lilv_new_uri(pWorld, LV2_CORE__enumeration);
|
||||
core__toggled = lilv_new_uri(pWorld, LV2_CORE__toggled);
|
||||
core__connectionOptional = lilv_new_uri(pWorld, LV2_CORE__connectionOptional);
|
||||
core__isSideChain = lilv_new_uri(pWorld, LV2_CORE_PREFIX "isSideChain"); // missing in lv2.h
|
||||
portprops__not_on_gui_property_uri = lilv_new_uri(pWorld, LV2_PORT_PROPS__notOnGUI);
|
||||
portprops__trigger = lilv_new_uri(pWorld, LV2_PORT_PROPS__trigger);
|
||||
midi__event = lilv_new_uri(pWorld, LV2_MIDI__MidiEvent);
|
||||
@@ -134,6 +135,7 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld)
|
||||
|
||||
atom__bufferType = lilv_new_uri(pWorld, LV2_ATOM__bufferType);
|
||||
atom__Path = lilv_new_uri(pWorld, LV2_ATOM__Path);
|
||||
atom__String = lilv_new_uri(pWorld, LV2_ATOM__String);
|
||||
presets__preset = lilv_new_uri(pWorld, LV2_PRESETS__Preset);
|
||||
state__state = lilv_new_uri(pWorld, LV2_STATE__state);
|
||||
rdfs__label = lilv_new_uri(pWorld, LILV_NS_RDFS "label");
|
||||
@@ -214,6 +216,7 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld)
|
||||
buf_size__maxBlockLength = lilv_new_uri(pWorld, LV2_BUF_SIZE__maxBlockLength);
|
||||
buf_size__fixedBlockLength = lilv_new_uri(pWorld, LV2_BUF_SIZE__fixedBlockLength);
|
||||
buf_size__coarseBlockLength = lilv_new_uri(pWorld, LV2_BUF_SIZE__coarseBlockLength);
|
||||
port_groups__sideChainOf = lilv_new_uri(pWorld, LV2_PORT_GROUPS__sideChainOf);
|
||||
|
||||
|
||||
}
|
||||
@@ -794,7 +797,12 @@ Lv2PluginInfo::FindWritablePathProperties(PluginHost *lv2Host, const LilvPlugin
|
||||
}
|
||||
else
|
||||
{
|
||||
unsupportedPatchProperty = true;
|
||||
if (lilv_world_ask(pWorld, propertyUri, lv2Host->lilvUris->rdfs__range, lv2Host->lilvUris->atom__String))
|
||||
{
|
||||
|
||||
} else {
|
||||
unsupportedPatchProperty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -945,6 +953,60 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP
|
||||
|
||||
|
||||
std::sort(ports_.begin(), ports_.end(), ports_sort_compare);
|
||||
|
||||
|
||||
// Check the portgroup for audio inputs to see if they are sidechains.
|
||||
std::string last_sidechain_title;
|
||||
for (auto&port: this->ports_)
|
||||
{
|
||||
if (port->is_audio_port() && port->is_input()) {
|
||||
if (!port->port_group().empty())
|
||||
{
|
||||
for (auto &pg: this->port_groups_)
|
||||
{
|
||||
if (pg->uri() == port->port_group())
|
||||
{
|
||||
if (!pg->sideChainOf().empty())
|
||||
{
|
||||
port->is_sidechain(true);
|
||||
audio_sidechain_title_ = pg->name();
|
||||
if (last_sidechain_title.empty()) {
|
||||
last_sidechain_title = pg->name();
|
||||
}
|
||||
else if (last_sidechain_title != pg->name()) {
|
||||
// multiple sidechain titles!
|
||||
isValid = false;
|
||||
Lv2Log::debug("Plugin %s (%s) has multiple sidechain titles (%s and %s).",
|
||||
this->name_.c_str(),
|
||||
this->uri_.c_str(),
|
||||
last_sidechain_title.c_str(),
|
||||
pg->name().c_str());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (port->is_sidechain()) {
|
||||
// port is marked as sidechain, but has no port group.
|
||||
if (last_sidechain_title.empty())
|
||||
{
|
||||
last_sidechain_title = port->name();
|
||||
this->audio_sidechain_title_ = port->name();
|
||||
}
|
||||
else if (last_sidechain_title != port->name()) {
|
||||
isValid = false;
|
||||
Lv2Log::debug("Plugin %s (%s) has multiple sidechain ports, but no portgroup. (%s)",
|
||||
this->name_.c_str(),
|
||||
this->uri_.c_str(),
|
||||
port->name().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch patch properties.
|
||||
{
|
||||
AutoLilvNodes patchWritables = lilv_world_find_nodes(pWorld, plugUri, lv2Host->lilvUris->patch__writable, nullptr);
|
||||
@@ -1034,17 +1096,28 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP
|
||||
}
|
||||
|
||||
int nInputs = 0;
|
||||
int nSideChains = 0;
|
||||
for (size_t i = 0; i < ports_.size(); ++i)
|
||||
{
|
||||
auto port = ports_[i];
|
||||
if (port->is_audio_port() && port->is_input())
|
||||
{
|
||||
if (nInputs >= 2 && !port->connection_optional())
|
||||
{
|
||||
isValid = false;
|
||||
break;
|
||||
if (!port->is_sidechain()) {
|
||||
if (nInputs >= 2)
|
||||
{
|
||||
isValid = false;
|
||||
Lv2Log::debug("Plugin %s (%s) has more than 2 audio inputs.", this->name_.c_str(), this->uri_.c_str());
|
||||
break;
|
||||
}
|
||||
++nInputs;
|
||||
} else {
|
||||
if (nSideChains >= 2) {
|
||||
Lv2Log::debug("Plugin %s (%s) has more than 2 audio sidechain inputs.", this->name_.c_str(), this->uri_.c_str());
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
++nSideChains;
|
||||
}
|
||||
++nInputs;
|
||||
}
|
||||
}
|
||||
int nOutputs = 0;
|
||||
@@ -1199,6 +1272,7 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP
|
||||
this->not_on_gui_ = lilv_port_has_property(plugin, pPort, host->lilvUris->portprops__not_on_gui_property_uri);
|
||||
this->connection_optional_ = lilv_port_has_property(plugin, pPort, host->lilvUris->core__connectionOptional);
|
||||
this->trigger_property_ = lilv_port_has_property(plugin, pPort, host->lilvUris->portprops__trigger);
|
||||
this->is_sidechain_ = lilv_port_has_property(plugin,pPort,host->lilvUris->core__isSideChain);
|
||||
|
||||
AutoLilvNode port_ledColor = lilv_port_get(plugin, pPort, host->lilvUris->pipedalUI__ledColor);
|
||||
if (port_ledColor)
|
||||
@@ -1402,7 +1476,12 @@ Lv2PluginUiInfo::Lv2PluginUiInfo(PluginHost *pHost, const Lv2PluginInfo *plugin)
|
||||
}
|
||||
else if (port->is_audio_port())
|
||||
{
|
||||
++audio_inputs_;
|
||||
if (port->is_sidechain()) {
|
||||
// sidechain outputs don't count.
|
||||
++audio_side_chain_inputs_;
|
||||
} else {
|
||||
++audio_inputs_;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (port->is_output())
|
||||
@@ -1918,12 +1997,29 @@ Lv2PortGroup::Lv2PortGroup(PluginHost *lv2Host, const std::string &groupUri)
|
||||
{
|
||||
LilvWorld *pWorld = lv2Host->pWorld;
|
||||
|
||||
// Enumerate all Lilv nodes for groupUri that have an 'a' predicate
|
||||
|
||||
this->uri_ = groupUri;
|
||||
AutoLilvNode uri = lilv_new_uri(pWorld, groupUri.c_str());
|
||||
|
||||
|
||||
AutoLilvNode aPredicate = lilv_new_uri(pWorld, "http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
|
||||
|
||||
AutoLilvNodes types = lilv_world_find_nodes(pWorld, uri, aPredicate, nullptr);
|
||||
LILV_FOREACH(nodes, iNode, types)
|
||||
{
|
||||
AutoLilvNode typeNode = lilv_nodes_get(types, iNode);
|
||||
std::string typeUri = nodeAsString(typeNode);
|
||||
this->isA_.push_back(std::move(typeUri));
|
||||
}
|
||||
|
||||
|
||||
AutoLilvNode symbolNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris->lv2core__symbol, nullptr);
|
||||
symbol_ = nodeAsString(symbolNode);
|
||||
AutoLilvNode nameNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris->lv2core__name, nullptr);
|
||||
name_ = nodeAsString(nameNode);
|
||||
AutoLilvNode sideChainOfNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris->port_groups__sideChainOf, nullptr);
|
||||
sideChainOf_ = nodeAsString(sideChainOfNode);
|
||||
}
|
||||
|
||||
bool Lv2PluginInfo::isSplit() const
|
||||
@@ -2187,6 +2283,7 @@ json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{
|
||||
|
||||
json_map::reference("is_input", &Lv2PortInfo::is_input_),
|
||||
json_map::reference("is_output", &Lv2PortInfo::is_output_),
|
||||
json_map::reference("is_sidechain", &Lv2PortInfo::is_sidechain_),
|
||||
|
||||
json_map::reference("is_control_port", &Lv2PortInfo::is_control_port_),
|
||||
json_map::reference("is_audio_port", &Lv2PortInfo::is_audio_port_),
|
||||
@@ -2226,7 +2323,11 @@ json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{
|
||||
|
||||
|
||||
json_map::storage_type<Lv2PortGroup> Lv2PortGroup::jmap{{
|
||||
MAP_REF(Lv2PortGroup, isA),
|
||||
MAP_REF(Lv2PortGroup, uri),
|
||||
MAP_REF(Lv2PortGroup, name),
|
||||
MAP_REF(Lv2PortGroup, symbol),
|
||||
MAP_REF(Lv2PortGroup, sideChainOf),
|
||||
|
||||
}};
|
||||
|
||||
@@ -2281,6 +2382,7 @@ json_map::storage_type<Lv2PluginUiPort> Lv2PluginUiPort::jmap{{
|
||||
json_map::reference("name", &Lv2PluginUiPort::name_),
|
||||
MAP_REF(Lv2PluginUiPort, index),
|
||||
MAP_REF(Lv2PluginUiPort, is_input),
|
||||
MAP_REF(Lv2PluginUiPort, is_sidechain),
|
||||
MAP_REF(Lv2PluginUiPort, min_value),
|
||||
MAP_REF(Lv2PluginUiPort, max_value),
|
||||
MAP_REF(Lv2PluginUiPort, default_value),
|
||||
@@ -2324,7 +2426,9 @@ json_map::storage_type<Lv2PluginUiInfo>
|
||||
json_map::reference("author_name", &Lv2PluginUiInfo::author_name_),
|
||||
json_map::reference("author_homepage", &Lv2PluginUiInfo::author_homepage_),
|
||||
json_map::reference("audio_inputs", &Lv2PluginUiInfo::audio_inputs_),
|
||||
json_map::reference("audio_side_chain_inputs", &Lv2PluginUiInfo::audio_side_chain_inputs_),
|
||||
json_map::reference("audio_outputs", &Lv2PluginUiInfo::audio_outputs_),
|
||||
json_map::reference("audio_side_chain_title", &Lv2PluginUiInfo::audio_side_chain_title_),
|
||||
json_map::reference("description", &Lv2PluginUiInfo::description_),
|
||||
json_map::reference("has_midi_input", &Lv2PluginUiInfo::has_midi_input_),
|
||||
json_map::reference("has_midi_output", &Lv2PluginUiInfo::has_midi_output_),
|
||||
|
||||
+61
-49
@@ -33,7 +33,7 @@
|
||||
#include <set>
|
||||
#include "ModGui.hpp"
|
||||
|
||||
//#include "lv2.h"
|
||||
// #include "lv2.h"
|
||||
#include "Units.hpp"
|
||||
#include "PluginPreset.hpp"
|
||||
|
||||
@@ -206,6 +206,7 @@ namespace pipedal
|
||||
|
||||
bool is_control_port_ = false;
|
||||
bool is_audio_port_ = false;
|
||||
bool is_sidechain_ = false;
|
||||
bool is_atom_port_ = false;
|
||||
bool is_cv_port_ = false;
|
||||
bool connection_optional_ = false;
|
||||
@@ -287,6 +288,7 @@ namespace pipedal
|
||||
LV2_PROPERTY_GETSET_SCALAR(is_output);
|
||||
LV2_PROPERTY_GETSET_SCALAR(is_control_port);
|
||||
LV2_PROPERTY_GETSET_SCALAR(is_audio_port);
|
||||
LV2_PROPERTY_GETSET_SCALAR(is_sidechain);
|
||||
LV2_PROPERTY_GETSET_SCALAR(is_atom_port);
|
||||
LV2_PROPERTY_GETSET_SCALAR(connection_optional);
|
||||
LV2_PROPERTY_GETSET_SCALAR(is_cv_port);
|
||||
@@ -321,7 +323,6 @@ namespace pipedal
|
||||
return Lv2BufferType::Unknown;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
Lv2PortInfo() {}
|
||||
~Lv2PortInfo() = default;
|
||||
@@ -330,7 +331,8 @@ namespace pipedal
|
||||
static json_map::storage_type<Lv2PortInfo> jmap;
|
||||
};
|
||||
|
||||
class Lv2PatchPropertyInfo {
|
||||
class Lv2PatchPropertyInfo
|
||||
{
|
||||
|
||||
private:
|
||||
std::string uri_;
|
||||
@@ -343,9 +345,10 @@ namespace pipedal
|
||||
std::string shortName_;
|
||||
std::vector<std::string> fileTypes_;
|
||||
std::vector<std::string> supportedExtensions_;
|
||||
|
||||
public:
|
||||
Lv2PatchPropertyInfo() {}
|
||||
Lv2PatchPropertyInfo(PluginHost *pluginHost, const LilvNode *propertyUri);
|
||||
Lv2PatchPropertyInfo(PluginHost *pluginHost, const LilvNode *propertyUri);
|
||||
|
||||
LV2_PROPERTY_GETSET(uri);
|
||||
LV2_PROPERTY_GETSET(writable);
|
||||
@@ -359,21 +362,24 @@ namespace pipedal
|
||||
LV2_PROPERTY_GETSET(supportedExtensions);
|
||||
|
||||
DECLARE_JSON_MAP(Lv2PatchPropertyInfo);
|
||||
|
||||
};
|
||||
|
||||
class Lv2PortGroup
|
||||
{
|
||||
private:
|
||||
std::vector<std::string> isA_;
|
||||
std::string uri_;
|
||||
std::string symbol_;
|
||||
std::string name_;
|
||||
std::string sideChainOf_;
|
||||
|
||||
public:
|
||||
LV2_PROPERTY_GETSET(isA);
|
||||
LV2_PROPERTY_GETSET(uri);
|
||||
LV2_PROPERTY_GETSET(symbol);
|
||||
LV2_PROPERTY_GETSET(name);
|
||||
|
||||
LV2_PROPERTY_GETSET(sideChainOf);
|
||||
|
||||
Lv2PortGroup() {}
|
||||
Lv2PortGroup(PluginHost *lv2Host, const std::string &groupUri);
|
||||
|
||||
@@ -389,10 +395,11 @@ namespace pipedal
|
||||
using ptr = std::shared_ptr<Lv2PluginInfo>;
|
||||
Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvPlugin *);
|
||||
Lv2PluginInfo() {}
|
||||
bool isSplit() const ;
|
||||
bool isSplit() const;
|
||||
|
||||
private:
|
||||
struct FindWritablePathPropertiesResult {
|
||||
struct FindWritablePathPropertiesResult
|
||||
{
|
||||
std::shared_ptr<PiPedalUI> pipedalUi;
|
||||
bool hasUnsupportedPatchProperties = false;
|
||||
};
|
||||
@@ -406,7 +413,7 @@ namespace pipedal
|
||||
std::string name_;
|
||||
std::string plugin_class_;
|
||||
std::string brand_;
|
||||
std::string label_;
|
||||
std::string label_;
|
||||
std::vector<std::string> supported_features_;
|
||||
std::vector<std::string> required_features_;
|
||||
std::vector<std::string> optional_features_;
|
||||
@@ -420,9 +427,9 @@ namespace pipedal
|
||||
std::vector<std::shared_ptr<Lv2PortInfo>> ports_;
|
||||
std::vector<std::shared_ptr<Lv2PortGroup>> port_groups_;
|
||||
std::vector<Lv2PatchPropertyInfo> patchProperties_;
|
||||
|
||||
std::string audio_sidechain_title_;
|
||||
bool hasDefaultState_;
|
||||
|
||||
|
||||
bool is_valid_ = false;
|
||||
PiPedalUI::ptr piPedalUI_;
|
||||
ModGui::ptr modGui_;
|
||||
@@ -463,6 +470,7 @@ namespace pipedal
|
||||
LV2_PROPERTY_GETSET(minBlockLength)
|
||||
LV2_PROPERTY_GETSET(maxBlockLength)
|
||||
LV2_PROPERTY_GETSET(powerOf2BlockLength)
|
||||
LV2_PROPERTY_GETSET(audio_sidechain_title)
|
||||
|
||||
bool WantsWorkerThread() const;
|
||||
|
||||
@@ -529,9 +537,11 @@ namespace pipedal
|
||||
class Lv2PluginUiPortGroup
|
||||
{
|
||||
private:
|
||||
std::vector<std::string> isA_;
|
||||
std::string uri_;
|
||||
std::string symbol_;
|
||||
std::string name_;
|
||||
std::string sideChainOf_;
|
||||
|
||||
std::string parent_group_;
|
||||
int32_t program_list_id_ = -1; // used by VST3.
|
||||
@@ -546,9 +556,7 @@ namespace pipedal
|
||||
public:
|
||||
Lv2PluginUiPortGroup() {}
|
||||
Lv2PluginUiPortGroup(Lv2PortGroup *pPortGroup)
|
||||
: symbol_(pPortGroup->symbol())
|
||||
, name_(pPortGroup->name())
|
||||
, uri_(pPortGroup->uri())
|
||||
: isA_(pPortGroup->isA()), symbol_(pPortGroup->symbol()), name_(pPortGroup->name()), uri_(pPortGroup->uri()), sideChainOf_(pPortGroup->sideChainOf())
|
||||
{
|
||||
}
|
||||
Lv2PluginUiPortGroup(
|
||||
@@ -570,14 +578,18 @@ namespace pipedal
|
||||
}
|
||||
Lv2PluginUiPort(const Lv2PluginInfo *pPlugin, const Lv2PortInfo *pPort)
|
||||
: symbol_(pPort->symbol()), index_(pPort->index()),
|
||||
is_input_(pPort->is_input()), name_(pPort->name()), min_value_(pPort->min_value()), max_value_(pPort->max_value()),
|
||||
is_input_(pPort->is_input()),
|
||||
is_sidechain_(pPort->is_sidechain()),
|
||||
name_(pPort->name()),
|
||||
min_value_(pPort->min_value()),
|
||||
max_value_(pPort->max_value()),
|
||||
default_value_(pPort->default_value()), range_steps_(pPort->range_steps()), display_priority_(pPort->display_priority()),
|
||||
is_logarithmic_(pPort->is_logarithmic()),
|
||||
integer_property_(pPort->integer_property()),
|
||||
is_logarithmic_(pPort->is_logarithmic()),
|
||||
integer_property_(pPort->integer_property()),
|
||||
mod_momentaryOffByDefault_(pPort->mod_momentaryOffByDefault()),
|
||||
mod_momentaryOnByDefault_(pPort->mod_momentaryOnByDefault()),
|
||||
pipedal_graphicEq_(pPort->pipedal_graphicEq()),
|
||||
|
||||
|
||||
enumeration_property_(pPort->enumeration_property()),
|
||||
toggled_property_(pPort->toggled_property()), not_on_gui_(pPort->not_on_gui()), scale_points_(pPort->scale_points()),
|
||||
trigger_property_(pPort->trigger_property()),
|
||||
@@ -606,7 +618,7 @@ namespace pipedal
|
||||
}
|
||||
}
|
||||
}
|
||||
is_bypass_ =
|
||||
is_bypass_ =
|
||||
pPort->is_bypass() ||
|
||||
name_ == "bypass" || name_ == "Bypass" || symbol_ == "bypass" || symbol_ == "Bypass";
|
||||
}
|
||||
@@ -616,6 +628,7 @@ namespace pipedal
|
||||
int index_;
|
||||
std::string name_;
|
||||
bool is_input_ = true;
|
||||
bool is_sidechain_ = false;
|
||||
float min_value_ = 0, max_value_ = 1, default_value_ = 0;
|
||||
bool is_logarithmic_ = false;
|
||||
int display_priority_ = -1;
|
||||
@@ -682,7 +695,8 @@ namespace pipedal
|
||||
std::string uri_;
|
||||
std::string name_;
|
||||
uint32_t minorVersion_ = 0;
|
||||
uint32_t microVersion_ = 0;;
|
||||
uint32_t microVersion_ = 0;
|
||||
;
|
||||
std::string brand_;
|
||||
std::string label_;
|
||||
std::string author_name_;
|
||||
@@ -690,12 +704,17 @@ namespace pipedal
|
||||
PluginType plugin_type_;
|
||||
std::string plugin_display_type_;
|
||||
int audio_inputs_ = 0;
|
||||
int audio_side_chain_inputs_ = 0;
|
||||
int audio_outputs_ = 0;
|
||||
std::string audio_side_chain_title_;
|
||||
int has_midi_input_ = false;
|
||||
int has_midi_output_ = false;
|
||||
std::string description_;
|
||||
bool is_vst3_ = false;
|
||||
|
||||
int64_t side_chain_input_id = -1;
|
||||
|
||||
|
||||
std::vector<Lv2PluginUiPort> controls_;
|
||||
std::vector<Lv2PluginUiPortGroup> port_groups_;
|
||||
std::vector<UiFileProperty::ptr> fileProperties_;
|
||||
@@ -704,7 +723,6 @@ namespace pipedal
|
||||
ModGui::ptr modGui_;
|
||||
std::vector<Lv2PatchPropertyInfo> patchProperties_;
|
||||
|
||||
|
||||
public:
|
||||
LV2_PROPERTY_GETSET(uri)
|
||||
LV2_PROPERTY_GETSET(name)
|
||||
@@ -718,6 +736,7 @@ namespace pipedal
|
||||
LV2_PROPERTY_GETSET(plugin_display_type)
|
||||
LV2_PROPERTY_GETSET_SCALAR(audio_inputs)
|
||||
LV2_PROPERTY_GETSET_SCALAR(audio_outputs)
|
||||
LV2_PROPERTY_GETSET(audio_side_chain_title)
|
||||
LV2_PROPERTY_GETSET_SCALAR(has_midi_input)
|
||||
LV2_PROPERTY_GETSET_SCALAR(has_midi_output)
|
||||
LV2_PROPERTY_GETSET_SCALAR(description)
|
||||
@@ -729,6 +748,7 @@ namespace pipedal
|
||||
LV2_PROPERTY_GETSET(uiPortNotifications)
|
||||
LV2_PROPERTY_GETSET(modGui)
|
||||
LV2_PROPERTY_GETSET(patchProperties)
|
||||
LV2_PROPERTY_GETSET_SCALAR(audio_side_chain_inputs)
|
||||
|
||||
static json_map::storage_type<Lv2PluginUiInfo> jmap;
|
||||
};
|
||||
@@ -772,6 +792,7 @@ namespace pipedal
|
||||
AutoLilvNode enumeration_property_uri;
|
||||
AutoLilvNode core__toggled;
|
||||
AutoLilvNode core__connectionOptional;
|
||||
AutoLilvNode core__isSideChain;
|
||||
AutoLilvNode portprops__not_on_gui_property_uri;
|
||||
AutoLilvNode portprops__trigger;
|
||||
AutoLilvNode midi__event;
|
||||
@@ -782,9 +803,9 @@ namespace pipedal
|
||||
AutoLilvNode invada_units__unit; // typo in invada plugins.
|
||||
AutoLilvNode invada_portprops__logarithmic; // typo in invada plugins.
|
||||
|
||||
|
||||
AutoLilvNode atom__bufferType;
|
||||
AutoLilvNode atom__Path;
|
||||
AutoLilvNode atom__String;
|
||||
AutoLilvNode presets__preset;
|
||||
AutoLilvNode state__state;
|
||||
AutoLilvNode rdfs__label;
|
||||
@@ -816,7 +837,6 @@ namespace pipedal
|
||||
AutoLilvNode pipedalUI__width;
|
||||
AutoLilvNode pipedalUI__graphicEq;
|
||||
|
||||
|
||||
AutoLilvNode pipedalUI__outputPorts;
|
||||
AutoLilvNode pipedalUI__text;
|
||||
|
||||
@@ -846,7 +866,6 @@ namespace pipedal
|
||||
AutoLilvNode patch__readable;
|
||||
AutoLilvNode pipedal_patch__readable;
|
||||
|
||||
|
||||
AutoLilvNode mod__brand;
|
||||
AutoLilvNode mod__label;
|
||||
AutoLilvNode mod__preferMomentaryOffByDefault;
|
||||
@@ -861,11 +880,9 @@ namespace pipedal
|
||||
AutoLilvNode buf_size__maxBlockLength;
|
||||
AutoLilvNode buf_size__fixedBlockLength;
|
||||
AutoLilvNode buf_size__coarseBlockLength;
|
||||
|
||||
|
||||
|
||||
AutoLilvNode port_groups__sideChainOf;
|
||||
};
|
||||
LilvUris* lilvUris = nullptr;
|
||||
LilvUris *lilvUris = nullptr;
|
||||
|
||||
private:
|
||||
bool vst3Enabled = true;
|
||||
@@ -896,7 +913,7 @@ namespace pipedal
|
||||
void free_world();
|
||||
|
||||
std::vector<std::shared_ptr<Lv2PluginInfo>> plugins_;
|
||||
std::map<std::string,std::shared_ptr<Lv2PluginInfo>> pluginsByUri;
|
||||
std::map<std::string, std::shared_ptr<Lv2PluginInfo>> pluginsByUri;
|
||||
std::vector<Lv2PluginUiInfo> ui_plugins_;
|
||||
|
||||
std::map<std::string, std::shared_ptr<Lv2PluginClass>> classesMap;
|
||||
@@ -947,22 +964,19 @@ namespace pipedal
|
||||
|
||||
public:
|
||||
virtual MapFeature &GetMapFeature() override { return this->mapFeature; }
|
||||
void CheckForResourceInitialization(const std::string& pluginUri,const std::filesystem::path& pluginUploadDirectory);
|
||||
void CheckForResourceInitialization(const std::string &pluginUri, const std::filesystem::path &pluginUploadDirectory);
|
||||
|
||||
|
||||
|
||||
std::string MapResourcePath(const std::string&uri, const std::string&relativePath);
|
||||
std::string MapResourcePath(const std::string &uri, const std::string &relativePath);
|
||||
|
||||
void ReloadPlugins();
|
||||
|
||||
// equivalent to LV2 MapPath AbstractPath features.
|
||||
std::string MapPath(const std::string &abstractPath);
|
||||
std::string AbstractPath(const std::string&path);
|
||||
json_variant MapPath(const json_variant&json);
|
||||
json_variant AbstractPath(const json_variant&json);
|
||||
std::string AbstractPath(const std::string &path);
|
||||
json_variant MapPath(const json_variant &json);
|
||||
json_variant AbstractPath(const json_variant &json);
|
||||
|
||||
private:
|
||||
|
||||
std::set<std::string> pluginsThatHaveBeenCheckedForResources;
|
||||
|
||||
void CheckForResourceInitization(const std::string pluginUri);
|
||||
@@ -973,12 +987,11 @@ namespace pipedal
|
||||
}
|
||||
static void PortValueCallback(const char *symbol, void *user_data, const void *value, uint32_t size, uint32_t type);
|
||||
static void StateRestoreCallback(
|
||||
const char* port_symbol,
|
||||
void* user_data,
|
||||
const void* value,
|
||||
uint32_t size,
|
||||
uint32_t type);
|
||||
|
||||
const char *port_symbol,
|
||||
void *user_data,
|
||||
const void *value,
|
||||
uint32_t size,
|
||||
uint32_t type);
|
||||
|
||||
virtual IEffect *CreateEffect(PedalboardItem &pedalboardItem);
|
||||
void LoadPluginClassesFromLilv();
|
||||
@@ -995,9 +1008,9 @@ namespace pipedal
|
||||
|
||||
IHost *asIHost() { return this; }
|
||||
|
||||
virtual Lv2Pedalboard *CreateLv2Pedalboard(Pedalboard &pedalboard,Lv2PedalboardErrorList &errorList);
|
||||
virtual Lv2Pedalboard *CreateLv2Pedalboard(Pedalboard &pedalboard, Lv2PedalboardErrorList &errorList);
|
||||
|
||||
virtual Lv2Pedalboard *UpdateLv2PedalboardStructure(Pedalboard &pedalboard,Lv2Pedalboard *existingPedalboard,Lv2PedalboardErrorList &errorList);
|
||||
virtual Lv2Pedalboard *UpdateLv2PedalboardStructure(Pedalboard &pedalboard, Lv2Pedalboard *existingPedalboard, Lv2PedalboardErrorList &errorList);
|
||||
|
||||
void setSampleRate(double sampleRate)
|
||||
{
|
||||
@@ -1010,9 +1023,8 @@ namespace pipedal
|
||||
|
||||
class Urids;
|
||||
|
||||
Urids *urids = nullptr;
|
||||
ModGuiUris* mod_gui_uris = nullptr;
|
||||
|
||||
Urids *urids = nullptr;
|
||||
ModGuiUris *mod_gui_uris = nullptr;
|
||||
|
||||
void OnConfigurationChanged(const JackConfiguration &configuration, const JackChannelSelection &settings);
|
||||
|
||||
@@ -1021,7 +1033,7 @@ namespace pipedal
|
||||
|
||||
std::shared_ptr<Lv2PluginClass> GetLv2PluginClass() const;
|
||||
|
||||
const std::vector<std::shared_ptr<Lv2PluginInfo>>& GetPlugins() const { return plugins_; }
|
||||
const std::vector<std::shared_ptr<Lv2PluginInfo>> &GetPlugins() const { return plugins_; }
|
||||
const std::vector<Lv2PluginUiInfo> &GetUiPlugins() const { return ui_plugins_; }
|
||||
|
||||
virtual std::shared_ptr<Lv2PluginInfo> GetPluginInfo(const std::string &uri) const;
|
||||
|
||||
+14
-2
@@ -1717,7 +1717,7 @@ void Storage::ToAbstractPaths(PluginPreset &pluginPreset)
|
||||
std::map<std::string, std::string> newPathProperties;
|
||||
for (const auto &pair : pluginPreset.pathProperties_)
|
||||
{
|
||||
newPathProperties[pair.first] = this->ToAbstractPathJson(pair.second);
|
||||
newPathProperties[pair.first] = this->ToAbstractPathFromJson(pair.second);
|
||||
}
|
||||
pluginPreset.pathProperties_ = newPathProperties;
|
||||
}
|
||||
@@ -2999,7 +2999,19 @@ std::string Storage::GetTone3000Auth() const
|
||||
return tone3000Auth;
|
||||
}
|
||||
|
||||
std::string Storage::ToAbstractPathJson(const std::string &pathJson)
|
||||
std::filesystem::path Storage::FromAbstractPathString(const std::string &stringPath)
|
||||
{
|
||||
if (stringPath.empty()) {
|
||||
return "";
|
||||
}
|
||||
fs::path path { stringPath};
|
||||
if (path.is_absolute()) {
|
||||
return path;
|
||||
}
|
||||
return GetPluginUploadDirectory() / path;
|
||||
}
|
||||
|
||||
std::string Storage::ToAbstractPathFromJson(const std::string &pathJson)
|
||||
{
|
||||
json_variant v = json_variant::parse(pathJson);
|
||||
|
||||
|
||||
+2
-1
@@ -129,8 +129,9 @@ public:
|
||||
|
||||
const std::filesystem::path &GetPluginUploadDirectory() const;
|
||||
|
||||
std::string ToAbstractPathJson(const std::string &jsonAtomPath);
|
||||
std::string ToAbstractPathFromJson(const std::string &jsonAtomPath);
|
||||
std::string FromAbstractPathJson(const std::string &jsonAtomPath);
|
||||
std::filesystem::path FromAbstractPathString(const std::string &stringPath);
|
||||
|
||||
|
||||
//std::vector<std::string> GetPedalboards();
|
||||
|
||||
@@ -520,7 +520,7 @@ public:
|
||||
|
||||
res.set(HttpField::content_type, "application/json");
|
||||
res.set(HttpField::cache_control, "no-cache");
|
||||
fs::path path = model->GetStorage().FromAbstractPathJson(request_uri.query("path"));
|
||||
fs::path path = model->GetStorage().FromAbstractPathString(request_uri.query("path"));
|
||||
|
||||
if (!fs::exists(path) || !this->model->IsInUploadsDirectory(path) || HasDotDot(path))
|
||||
{
|
||||
|
||||
+147
-123
@@ -24,17 +24,40 @@ import { withStyles } from "tss-react/mui";
|
||||
import { createStyles } from './WithStyles';
|
||||
import FullscreenIcon from '@mui/icons-material/Fullscreen';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import { isDarkMode } from './DarkMode';
|
||||
import Backdrop from '@mui/material/Backdrop';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
|
||||
import Rect from './Rect';
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
|
||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
pgraph: {
|
||||
pgraph: css({
|
||||
paddingBottom: 16
|
||||
}
|
||||
}),
|
||||
backdrop: css({
|
||||
zIndex: 1101,
|
||||
overflow: "clip",
|
||||
background: theme.palette.background.default,
|
||||
|
||||
}),
|
||||
noToolbar: css({
|
||||
display: "none"
|
||||
}),
|
||||
topToolBar: css({
|
||||
position: "absolute",
|
||||
zIndex: 1101,
|
||||
top: 0,
|
||||
display: "flex", flexFlow: "row nowrap", justifyContent: "start", columnGap: 8,
|
||||
}),
|
||||
rightToolbar: css({
|
||||
position: "absolute",
|
||||
zIndex: 1101,
|
||||
top: 24,
|
||||
display: "flex", flexFlow: "column nowrap", rowGap: 8
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -42,6 +65,7 @@ export interface AutoZoomProps extends WithStyles<typeof styles> {
|
||||
leftPad: number,
|
||||
rightPad: number,
|
||||
children?: React.ReactNode,
|
||||
toolbarChildren?: React.ReactNode[],
|
||||
contentReady: boolean,
|
||||
showZoomed: boolean,
|
||||
setShowZoomed: (value: boolean) => void;
|
||||
@@ -53,6 +77,15 @@ export interface AutoZoomState {
|
||||
}
|
||||
|
||||
|
||||
// non-react zoom state.
|
||||
class UnzoomedLayout {
|
||||
horizontalToolbar = false;
|
||||
effectiveClientRect: Rect = new Rect();
|
||||
showToolbar = false;
|
||||
zoom: number = 1;
|
||||
showZoomButton = false;
|
||||
contentRect: Rect = new Rect();
|
||||
}
|
||||
|
||||
const AutoZoom = withStyles(
|
||||
class extends ResizeResponsiveComponent<AutoZoomProps, AutoZoomState> {
|
||||
@@ -97,38 +130,43 @@ const AutoZoom = withStyles(
|
||||
if (this.normalRef) {
|
||||
this.updateZoom(this.normalRef);
|
||||
}
|
||||
if (this.maximizedRef) {
|
||||
this.updateMaximizedZoom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resizeObserver: ResizeObserver | null = null;
|
||||
|
||||
|
||||
calculateZoomedClientRect(
|
||||
calculateUnzoomedLayout(
|
||||
clientRect: Rect,
|
||||
contentWidth: number,
|
||||
contentHeight: number,
|
||||
buttonPadding: number
|
||||
): Rect {
|
||||
): UnzoomedLayout {
|
||||
|
||||
let result = new UnzoomedLayout();
|
||||
|
||||
console.log("Zoom: " + clientRect.toString() + " content: " + contentWidth + "x" + contentHeight + " buttonPadding: " + buttonPadding);
|
||||
let rightButtonZoom = Math.min((clientRect.width - buttonPadding) / contentWidth, clientRect.height / contentHeight);
|
||||
let topButtonZoom = Math.min((clientRect.width - buttonPadding) / contentWidth, (clientRect.height - buttonPadding) / contentHeight);
|
||||
|
||||
let effectiveClientRect = clientRect.copy();
|
||||
if (rightButtonZoom > topButtonZoom) {
|
||||
result.horizontalToolbar = false;
|
||||
effectiveClientRect.width -= buttonPadding;
|
||||
} else {
|
||||
result.horizontalToolbar = true;
|
||||
effectiveClientRect.y += buttonPadding;
|
||||
effectiveClientRect.height -= buttonPadding;
|
||||
}
|
||||
result.effectiveClientRect = effectiveClientRect;
|
||||
|
||||
let zoom = Math.min(effectiveClientRect.width / contentWidth, effectiveClientRect.height / contentHeight);
|
||||
if (zoom > 1) {
|
||||
zoom = 1;
|
||||
}
|
||||
result.zoom = zoom;
|
||||
result.showZoomButton = zoom !== 1;
|
||||
result.showToolbar = result.showZoomButton || !!this.props.toolbarChildren;
|
||||
|
||||
let leftPad = (effectiveClientRect.width - contentWidth * zoom) / 2;
|
||||
if (leftPad < 0) {
|
||||
leftPad = 0;
|
||||
@@ -137,7 +175,8 @@ const AutoZoom = withStyles(
|
||||
if (topPad < 0) {
|
||||
topPad = 0;
|
||||
}
|
||||
return new Rect(effectiveClientRect.x + leftPad, effectiveClientRect.y + topPad, contentWidth * zoom, contentHeight * zoom);
|
||||
result.contentRect = new Rect(effectiveClientRect.x + leftPad, effectiveClientRect.y + topPad, contentWidth * zoom, contentHeight * zoom);
|
||||
return result;
|
||||
}
|
||||
|
||||
updateZoom(ref: HTMLDivElement) {
|
||||
@@ -145,59 +184,58 @@ const AutoZoom = withStyles(
|
||||
return;
|
||||
}
|
||||
let content = ref.firstElementChild as HTMLElement | null;
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
|
||||
if (content) {
|
||||
const { leftPad, rightPad } = this.props;
|
||||
let bounds = new Rect(leftPad, 0, ref.clientWidth - leftPad - rightPad, ref.clientHeight - 8);
|
||||
let zoomedClientRect = this.calculateZoomedClientRect(
|
||||
let layout = this.calculateUnzoomedLayout(
|
||||
bounds, content.clientWidth, content.clientHeight, 48);
|
||||
|
||||
let contentRect = layout.contentRect;
|
||||
|
||||
content.style.left = "0px";
|
||||
content.style.top = "0px";
|
||||
content.style.transformOrigin = "0 0"; // top-left corner
|
||||
content.style.transform =
|
||||
`translate(${zoomedClientRect.x}px, ${zoomedClientRect.y}px) scale(${zoomedClientRect.width / content.clientWidth}, ${zoomedClientRect.height / content.clientHeight})`;
|
||||
`translate(${contentRect.x}px, ${contentRect.y}px) scale(${layout.zoom}, ${layout.zoom})`;
|
||||
|
||||
if (this.zoomButton) {
|
||||
this.zoomButton.style.display = zoomedClientRect.width < content.clientWidth ? "block" : "none";
|
||||
let rightSizeSpace = ref.clientWidth - zoomedClientRect.right;
|
||||
if (rightSizeSpace - rightPad >= 48) {
|
||||
this.zoomButton.style.top = `${zoomedClientRect.y + 24}px`;
|
||||
this.zoomButton.style.left = `${zoomedClientRect.right + 4}px`;
|
||||
if (this.zoomToolbar) {
|
||||
if (!layout.showToolbar) {
|
||||
|
||||
this.zoomToolbar.className = classes.noToolbar;
|
||||
} else if (layout.horizontalToolbar) {
|
||||
this.zoomToolbar.className = classes.topToolBar;
|
||||
this.zoomToolbar.style.left = `${contentRect.x +16}px`
|
||||
this.zoomToolbar.style.top = `${contentRect.y - 48}px`;
|
||||
this.zoomToolbar.style.width = `${contentRect.width}px`;
|
||||
} else {
|
||||
this.zoomButton.style.top = `${zoomedClientRect.y - 48}px`;
|
||||
this.zoomButton.style.left = `${zoomedClientRect.right - 64 - 12}px`;
|
||||
this.zoomToolbar.className = classes.rightToolbar;
|
||||
this.zoomToolbar.style.left = "";
|
||||
this.zoomToolbar.style.top = `${contentRect.y}px`;
|
||||
this.zoomToolbar.style.left = `${contentRect.right + 4}px`;
|
||||
this.zoomToolbar.style.width = "";
|
||||
}
|
||||
}
|
||||
if (this.zoomButton) {
|
||||
this.zoomButton.style.display = layout.showZoomButton ? "block" : "none";
|
||||
}
|
||||
|
||||
// save the position of the Mod UI in page coordinates for later zooming.
|
||||
this.zoomStartRect = zoomedClientRect.copy();
|
||||
this.zoomStartRect = layout.contentRect.copy();
|
||||
let refBounds = ref.getBoundingClientRect();
|
||||
this.zoomStartRect.x += refBounds.left;
|
||||
this.zoomStartRect.y += refBounds.top;
|
||||
} else {
|
||||
if (this.zoomToolbar) {
|
||||
this.zoomToolbar.className = classes.noToolbar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateMaximizedZoom() {
|
||||
let ref = this.maximizedRef;
|
||||
if (!this.mounted || !ref) {
|
||||
return;
|
||||
}
|
||||
let content = ref.firstElementChild as HTMLElement | null;
|
||||
|
||||
if (content) {
|
||||
let leftPad = 16; let rightPad = 16;
|
||||
let topPad = 16, bottomPad = 16;
|
||||
let bounds: Rect;
|
||||
bounds = new Rect(leftPad, topPad, ref.clientWidth - leftPad - rightPad, ref.clientHeight - topPad - bottomPad);
|
||||
|
||||
let zoomedClientRect = this.calculateZoomedClientRect(
|
||||
bounds, content.clientWidth, content.clientHeight, 48);
|
||||
content.style.left = "0px";
|
||||
content.style.top = "0px";
|
||||
content.style.transformOrigin = "0 0"; // top-left corner
|
||||
content.style.transform = `translate(${zoomedClientRect.x}px, ${zoomedClientRect.y}px) scale(${zoomedClientRect.width / content.clientWidth}, ${zoomedClientRect.height / content.clientHeight})`;
|
||||
//console.log("Zoomed to: ", zoomedClientRect.toString());
|
||||
|
||||
if (this.normalRef) {
|
||||
this.updateZoom(this.normalRef);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,20 +272,20 @@ const AutoZoom = withStyles(
|
||||
}
|
||||
this.maximizedAnimateFrameHandle = window.requestAnimationFrame(() => {
|
||||
this.maximizedAnimateFrameHandle = null;
|
||||
if (this.maximizedRef) {
|
||||
this.updateMaximizedZoom();
|
||||
}
|
||||
this.updateMaximizedZoom();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private zoomToolbar: HTMLElement | null = null;
|
||||
private zoomButton: HTMLElement | null = null;
|
||||
private normalRef: HTMLDivElement | null = null;
|
||||
|
||||
setNormalRef(ref: HTMLDivElement | null) {
|
||||
setContentRef(ref: HTMLDivElement | null) {
|
||||
this.normalRef = ref;
|
||||
if (ref) {
|
||||
this.zoomButton = ref.querySelector("#maximize-button") as HTMLDivElement | null;
|
||||
this.zoomToolbar = ref.querySelector("#zoom-toolbar") as HTMLDivElement | null;
|
||||
this.zoomButton = ref.querySelector("#zoom-button") as HTMLDivElement | null;
|
||||
this.resizeObserver = new ResizeObserver(() => {
|
||||
this.requestZoomUpdate();
|
||||
});
|
||||
@@ -261,36 +299,15 @@ const AutoZoom = withStyles(
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.disconnect();
|
||||
this.resizeObserver = null;
|
||||
this.zoomButton = null;
|
||||
this.zoomToolbar = null;
|
||||
}
|
||||
this.zoomStartRect = null;
|
||||
this.zoomToolbar = null;
|
||||
this.zoomButton = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private maximizedRef: HTMLDivElement | null = null;
|
||||
private maximedResizeObserver: ResizeObserver | null = null;
|
||||
|
||||
setMaximizedRef(ref: HTMLDivElement | null) {
|
||||
this.maximizedRef = ref;
|
||||
if (ref) {
|
||||
this.maximedResizeObserver = new ResizeObserver(() => {
|
||||
this.requestMaximizedZoomUpdate();
|
||||
});
|
||||
this.maximedResizeObserver.observe(ref);
|
||||
let child = ref.firstElementChild as HTMLElement | null;
|
||||
if (child) {
|
||||
this.maximedResizeObserver.observe(child);
|
||||
}
|
||||
this.requestMaximizedZoomUpdate();
|
||||
} else {
|
||||
if (this.maximedResizeObserver) {
|
||||
this.maximedResizeObserver.disconnect();
|
||||
this.maximedResizeObserver = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private zoomStartRect: Rect | null = null;
|
||||
|
||||
startZoom() {
|
||||
@@ -300,70 +317,77 @@ const AutoZoom = withStyles(
|
||||
this.props.setShowZoomed(true);
|
||||
}
|
||||
|
||||
render() {
|
||||
content() {
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
void classes; // suppress unused variable warning
|
||||
const landscape = this.state.screenWidth > this.state.screenHeight;
|
||||
return (<div style={{ overflow: "hidden", height: "100%", width: "100%" }} ref={(ref) => { this.setNormalRef(ref); }}>
|
||||
<div style={{
|
||||
display: "inline-block",
|
||||
position: "absolute",
|
||||
visibility: this.props.contentReady ? "visible" : "hidden",
|
||||
|
||||
}}>
|
||||
{!this.props.showZoomed && this.props.children}
|
||||
</div>
|
||||
<div id="maximize-button" style={{
|
||||
display: "none", position: "absolute", left: 0, top: 0, width: 52, height: 64, zIndex: 1101,
|
||||
borderRadius: "0px 26px 26px 0px",
|
||||
|
||||
}}
|
||||
return (
|
||||
<div ref={(ref) => { this.setContentRef(ref); }}
|
||||
style={{ width: "100%", height: "100%" }}
|
||||
>
|
||||
<IconButtonEx tooltip="Fullscreen view"
|
||||
style={{ margin: 2, background: isDarkMode() ? "#444" : "#eee" }}
|
||||
onClick={() => {
|
||||
this.startZoom();
|
||||
<div style={{
|
||||
display: "inline-block",
|
||||
position: "absolute",
|
||||
visibility: this.props.contentReady ? "visible" : "hidden"
|
||||
|
||||
}}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
<div id="zoom-toolbar" className={classes.noToolbar}
|
||||
style={{
|
||||
visibility: this.props.contentReady ? "visible" : "hidden"
|
||||
}}
|
||||
>
|
||||
<FullscreenIcon style={{ color: "white" }} />
|
||||
</IconButtonEx>
|
||||
</div>
|
||||
{this.props.showZoomed && (
|
||||
<Backdrop id="modGuiZoomBackdrop"
|
||||
sx={(theme) => ({
|
||||
zIndex: this.props.showZoomed ? 1101 : -1,
|
||||
overflow: "clip",
|
||||
background: theme.palette.mode === 'dark' ? 'rgba(0, 0, 0, 0.9)' : 'rgba(255, 255, 255, 0.9)'
|
||||
})}
|
||||
open={this.props.showZoomed}
|
||||
>
|
||||
<div ref={(ref) => { this.setMaximizedRef(ref); }}
|
||||
style={{
|
||||
position: "absolute", left: 0, top: 0, right: 0, bottom: 0, overflow: "hidden",
|
||||
visibility: this.props.contentReady ? "visible" : "hidden"
|
||||
}}>
|
||||
|
||||
<div style={{ display: "inline-block", position: "relative" }}>
|
||||
{
|
||||
this.props.showZoomed && this.props.children
|
||||
}
|
||||
</div>
|
||||
|
||||
<IconButtonEx tooltip="Exit fullscreen"
|
||||
style={{
|
||||
position: "absolute",
|
||||
right: landscape? 80: 16, // avoid potential cutout in landscape mode
|
||||
top: 16, zIndex: 1104,
|
||||
|
||||
{!this.props.showZoomed ? (
|
||||
<IconButtonEx id="zoom-button" tooltip="Fullscreen view"
|
||||
onClick={() => {
|
||||
this.startZoom();
|
||||
}}
|
||||
>
|
||||
<FullscreenIcon style={{ opacity: 0.6 }}
|
||||
/>
|
||||
</IconButtonEx>
|
||||
|
||||
) : (
|
||||
<IconButtonEx tooltip="Exit fullscreen"
|
||||
style={{}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
this.props.setShowZoomed(false);
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
<ArrowBackIcon style={{
|
||||
opacity: 0.6,
|
||||
}} />
|
||||
</IconButtonEx>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{this.props.toolbarChildren && this.props.toolbarChildren.map((child, index) => (
|
||||
<div key={index} style={{ display: "inline-block" }}>
|
||||
{child}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
void classes; // suppress unused variable warning
|
||||
|
||||
return (<div style={{ overflow: "hidden", height: "100%", width: "100%" }} >
|
||||
{!this.props.showZoomed && this.content()}
|
||||
|
||||
{this.props.showZoomed && (
|
||||
<Backdrop id="modGuiZoomBackdrop"
|
||||
className={classes.backdrop}
|
||||
open={this.props.showZoomed}
|
||||
>
|
||||
{this.content()}
|
||||
|
||||
</Backdrop>
|
||||
)}
|
||||
</div>);
|
||||
|
||||
@@ -47,6 +47,7 @@ export class Port implements Deserializable<Port> {
|
||||
this.scale_points = ScalePoint.deserialize_array(input.scale_points);
|
||||
this.is_input = input.is_input;
|
||||
this.is_output = input.is_output;
|
||||
this.is_sidechain = input.is_sidechain;
|
||||
this.is_control_port = input.is_control_port;
|
||||
this.is_audio_port = input.is_audio_port;
|
||||
this.is_atom_port = input.is_atom_port;
|
||||
@@ -80,6 +81,7 @@ export class Port implements Deserializable<Port> {
|
||||
is_output: boolean = false
|
||||
is_control_port: boolean = false;
|
||||
is_audio_port: boolean = false;
|
||||
is_sidechain: boolean = false;
|
||||
is_atom_port: boolean = false;
|
||||
is_valid: boolean = false;
|
||||
supports_midi: boolean = false;
|
||||
@@ -92,9 +94,11 @@ export class Port implements Deserializable<Port> {
|
||||
|
||||
export class PortGroup {
|
||||
deserialize(input: any): PortGroup {
|
||||
this.isA = input.isA;
|
||||
this.uri = input.uri;
|
||||
this.symbol = input.symbol;
|
||||
this.name = input.name;
|
||||
this.sideChainof = input.sideChainOf;
|
||||
this.parent_group = input.parent_group;
|
||||
this.program_list_id = input.program_list_id ?? -1;
|
||||
|
||||
@@ -108,9 +112,12 @@ export class PortGroup {
|
||||
return result;
|
||||
}
|
||||
|
||||
isA: string[] = [];
|
||||
uri: string = "";
|
||||
symbol: string = "";
|
||||
name: string = "";
|
||||
sideChainof: string = "";
|
||||
|
||||
parent_group: string = "";
|
||||
program_list_id: number = -1;
|
||||
};
|
||||
@@ -967,6 +974,8 @@ export class UiPlugin implements Deserializable<UiPlugin> {
|
||||
this.author_name = input.author_name;
|
||||
this.author_homepage = input.author_homepage;
|
||||
this.audio_inputs = input.audio_inputs;
|
||||
this.audio_side_chain_inputs = input.audio_side_chain_inputs ?? 0;
|
||||
this.audio_side_chain_title = input.audio_side_chain_title ?? "";
|
||||
this.audio_outputs = input.audio_outputs;
|
||||
this.has_midi_input = input.has_midi_input;
|
||||
this.has_midi_output = input.has_midi_output;
|
||||
@@ -1057,7 +1066,9 @@ export class UiPlugin implements Deserializable<UiPlugin> {
|
||||
author_name: string = "";
|
||||
author_homepage: string = "";
|
||||
audio_inputs: number = 0;
|
||||
audio_side_chain_inputs: number = 0;
|
||||
audio_outputs: number = 0;
|
||||
audio_side_chain_title: string = "";
|
||||
has_midi_input: number = 0;
|
||||
has_midi_output: number = 0;
|
||||
description: string = "";
|
||||
@@ -1084,6 +1095,7 @@ export function makeSplitUiPlugin(): UiPlugin {
|
||||
author_name: "",
|
||||
author_homepage: "",
|
||||
audio_inputs: 1,
|
||||
audio_side_chain_inputs: 0,
|
||||
audio_outputs: 1,
|
||||
has_midi_input: 0,
|
||||
has_midi_output: 0,
|
||||
|
||||
@@ -81,6 +81,7 @@ export class PedalboardItem implements Deserializable<PedalboardItem> {
|
||||
this.pathProperties = input.pathProperties;
|
||||
this.useModUi = input.useModUi ?? false;
|
||||
this.iconColor = input.iconColor??"";
|
||||
this.sideChainInputId = input.sideChainInputId ?? -1;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -217,6 +218,7 @@ export class PedalboardItem implements Deserializable<PedalboardItem> {
|
||||
pathProperties: {[Name: string]: string} = {};
|
||||
useModUi: boolean = false; // true if this item should use the mod-ui.
|
||||
iconColor: string = "";
|
||||
sideChainInputId: number = -1; // -1 means no sidechain input.
|
||||
};
|
||||
|
||||
export class SnapshotValue {
|
||||
@@ -392,6 +394,7 @@ export class Pedalboard implements Deserializable<Pedalboard> {
|
||||
pathProperties: {[Name: string]: string} = {};
|
||||
selectedPlugin: number = -1;
|
||||
|
||||
// yields all items in the pedalboard, including split items. Splits are yielded before their children.
|
||||
*itemsGenerator(): Generator<PedalboardItem, void, undefined> {
|
||||
let it = itemGenerator_(this.items);
|
||||
while (true)
|
||||
@@ -401,6 +404,16 @@ export class Pedalboard implements Deserializable<Pedalboard> {
|
||||
yield v.value;
|
||||
}
|
||||
}
|
||||
// same as itemsGenerator, but yields split items after their chains.
|
||||
*itemsGeneratorSplitAfter(): Generator<PedalboardItem, void, undefined> {
|
||||
let it = itemGeneratorSplitAfter_(this.items);
|
||||
while (true)
|
||||
{
|
||||
let v = it.next();
|
||||
if (v.done) break;
|
||||
yield v.value;
|
||||
}
|
||||
}
|
||||
|
||||
makeSnapshot(): Snapshot {
|
||||
let result = new Snapshot();
|
||||
@@ -791,4 +804,28 @@ function* itemGenerator_(items: PedalboardItem[]): Generator<PedalboardItem, voi
|
||||
}
|
||||
}
|
||||
|
||||
function* itemGeneratorSplitAfter_(items: PedalboardItem[]): Generator<PedalboardItem, void, undefined> {
|
||||
for (let i = 0; i < items.length; ++i) {
|
||||
let item = items[i];
|
||||
if (item.uri === SPLIT_PEDALBOARD_ITEM_URI) {
|
||||
|
||||
let splitItem = item as PedalboardSplitItem;
|
||||
|
||||
let it = itemGenerator_(splitItem.topChain);
|
||||
while (true) {
|
||||
let v = it.next();
|
||||
if (v.done) break;
|
||||
yield v.value;
|
||||
}
|
||||
it = itemGenerator_(splitItem.bottomChain);
|
||||
while (true) {
|
||||
let v = it.next();
|
||||
if (v.done) break;
|
||||
yield v.value;
|
||||
}
|
||||
}
|
||||
yield item;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -550,19 +550,16 @@ export class PiPedalModel //implements PiPedalModel
|
||||
);
|
||||
zoomedUiControl: ObservableProperty<ZoomedControlInfo | undefined> = new ObservableProperty<ZoomedControlInfo | undefined>(undefined);
|
||||
|
||||
uiPluginsByUri: Map<string, UiPlugin> = new Map<string, UiPlugin>();
|
||||
|
||||
svgImgUrl(svgImage: string): string {
|
||||
//return this.varServerUrl + "img/" + svgImage;
|
||||
// return this.varServerUrl + "img/" + svgImage;
|
||||
return "img/" + svgImage;
|
||||
}
|
||||
|
||||
|
||||
getUiPlugin(uri: string): UiPlugin | null {
|
||||
let plugins: UiPlugin[] = this.ui_plugins.get();
|
||||
|
||||
for (let plugin of plugins) {
|
||||
if (plugin.uri === uri) return plugin;
|
||||
}
|
||||
return null;
|
||||
return this.uiPluginsByUri.get(uri) ?? null;
|
||||
}
|
||||
|
||||
androidHost?: AndroidHostInterface;
|
||||
@@ -662,6 +659,7 @@ export class PiPedalModel //implements PiPedalModel
|
||||
}
|
||||
|
||||
private setModelPedalboard(pedalboard: Pedalboard) {
|
||||
this.removeInvalidSidechains(pedalboard);
|
||||
this.pedalboard.set(pedalboard);
|
||||
this.selectedSnapshot.set(pedalboard.selectedSnapshot);
|
||||
this.updateEnabledItems(pedalboard);
|
||||
@@ -1210,6 +1208,11 @@ export class PiPedalModel //implements PiPedalModel
|
||||
this.ui_plugins.set(
|
||||
UiPlugin.deserialize_array(await this.getWebSocket().request<any>("plugins"))
|
||||
);
|
||||
// index ui plugins.
|
||||
this.uiPluginsByUri = new Map<string, UiPlugin>();
|
||||
for (let i of this.ui_plugins.get()) {
|
||||
this.uiPluginsByUri.set(i.uri, i);
|
||||
}
|
||||
this.setModelPedalboard(
|
||||
new Pedalboard().deserialize(
|
||||
await this.getWebSocket().request<Pedalboard>("currentPedalboard")
|
||||
@@ -1761,13 +1764,17 @@ export class PiPedalModel //implements PiPedalModel
|
||||
}
|
||||
|
||||
let it = newPedalboard.itemsGenerator();
|
||||
let oldInstanceId = -1;
|
||||
let newInstanceId = -1;
|
||||
while (true) {
|
||||
let v = it.next();
|
||||
if (v.done) break;
|
||||
let item = v.value;
|
||||
if (item.instanceId === itemId) {
|
||||
oldInstanceId = item.instanceId;
|
||||
item.deserialize(new PedalboardItem()); // skeezy way to re-initialize.
|
||||
item.instanceId = ++newPedalboard.nextInstanceId;
|
||||
newInstanceId = item.instanceId;
|
||||
item.uri = selectedUri;
|
||||
item.pluginName = plugin.name;
|
||||
item.controlValues = this.getDefaultValues(item.uri);
|
||||
@@ -1782,6 +1789,9 @@ export class PiPedalModel //implements PiPedalModel
|
||||
item.pathProperties[fileProperty.patchProperty] = "null";
|
||||
}
|
||||
newPedalboard.selectedPlugin = item.instanceId;
|
||||
if (oldInstanceId !== -1) {
|
||||
this.updateSidechainReferences(newPedalboard, oldInstanceId, newInstanceId);
|
||||
}
|
||||
this.setModelPedalboard(newPedalboard);
|
||||
this.updateServerPedalboard()
|
||||
return item.instanceId;
|
||||
@@ -1936,6 +1946,9 @@ export class PiPedalModel //implements PiPedalModel
|
||||
newPedalboard.replaceItem(fromInstanceId, emptyItem);
|
||||
newPedalboard.replaceItem(toInstanceId, fromItem);
|
||||
newPedalboard.selectedPlugin = fromItem.instanceId;
|
||||
if (!toItem.isEmpty()) {
|
||||
this.updateSidechainReferences(newPedalboard, toItem.instanceId, fromItem.instanceId);
|
||||
}
|
||||
this.setModelPedalboard(newPedalboard);
|
||||
this.updateServerPedalboard();
|
||||
|
||||
@@ -3543,14 +3556,66 @@ export class PiPedalModel //implements PiPedalModel
|
||||
|
||||
}
|
||||
requestBankPresets(bankInstanceId: number): Promise<PresetIndexEntry[]> {
|
||||
return nullCast(this.webSocket).request<PresetIndexEntry[]>("requestBankPresets", {bankInstanceId: bankInstanceId});
|
||||
}
|
||||
return nullCast(this.webSocket).request<PresetIndexEntry[]>("requestBankPresets", { bankInstanceId: bankInstanceId });
|
||||
}
|
||||
|
||||
importPresetsFromBank(bankInstanceId: number, presets: number[]): Promise<number> {
|
||||
return nullCast(this.webSocket).request<number>("importPresetsFromBank", {bankInstanceId: bankInstanceId, presets: presets});
|
||||
return nullCast(this.webSocket).request<number>("importPresetsFromBank", { bankInstanceId: bankInstanceId, presets: presets });
|
||||
}
|
||||
copyPresetsToBank(bankInstanceId: number, presets: number[]): Promise<number> {
|
||||
return nullCast(this.webSocket).request<number>("copyPresetsToBank", {bankInstanceId: bankInstanceId, presets: presets});
|
||||
return nullCast(this.webSocket).request<number>("copyPresetsToBank", { bankInstanceId: bankInstanceId, presets: presets });
|
||||
}
|
||||
setPedalboardSideChainInput(instanceId: number, sideChainInputId: number): void {
|
||||
let pedalboard = this.pedalboard.get().clone();
|
||||
let pedalboardItem = pedalboard.getItem(instanceId);
|
||||
if (!pedalboardItem) {
|
||||
return;
|
||||
}
|
||||
pedalboardItem.sideChainInputId = sideChainInputId;
|
||||
let oldId = pedalboardItem.instanceId;
|
||||
let newId = ++pedalboard.nextInstanceId;
|
||||
|
||||
pedalboardItem.instanceId = newId; // force reload of pedalboard.
|
||||
pedalboard.selectedPlugin = pedalboardItem.instanceId;
|
||||
|
||||
this.updateSidechainReferences(pedalboard, oldId, newId);
|
||||
this.setModelPedalboard(pedalboard);
|
||||
this.updateServerPedalboard();
|
||||
|
||||
}
|
||||
|
||||
removeInvalidSidechains(pedalboard: Pedalboard) {
|
||||
let it = pedalboard.itemsGeneratorSplitAfter();
|
||||
let previousItemIds: Set<number> = new Set<number>();
|
||||
previousItemIds.add(-2); // start is visible.
|
||||
while (true) {
|
||||
let r = it.next();
|
||||
if (r.done) {
|
||||
break;
|
||||
}
|
||||
let item = r.value;
|
||||
if (item.sideChainInputId !== -1) {
|
||||
if (!previousItemIds.has(item.sideChainInputId)) {
|
||||
item.sideChainInputId = -1;
|
||||
}
|
||||
}
|
||||
if (!item.isEmpty()) { // empty items are not valid sidechain targets.
|
||||
previousItemIds.add(item.instanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
updateSidechainReferences(pedalboard: Pedalboard, oldItemId: number, newItemId: number) {
|
||||
let it = pedalboard.itemsGenerator();
|
||||
|
||||
while (true) {
|
||||
let r = it.next();
|
||||
if (r.done) break;
|
||||
let item = r.value;
|
||||
if (item.sideChainInputId === oldItemId) {
|
||||
item.sideChainInputId = newItemId;
|
||||
}
|
||||
}
|
||||
this.removeInvalidSidechains(pedalboard);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ import { Theme } from '@mui/material/styles';
|
||||
import WithStyles, { withTheme } from './WithStyles';
|
||||
import { createStyles } from './WithStyles';
|
||||
import { css } from '@emotion/react';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
|
||||
import SidechainConnectIcon from './svg/ic_sidechain_open_48.svg?react';
|
||||
|
||||
import AutoZoom from './AutoZoom';
|
||||
|
||||
@@ -37,6 +40,7 @@ import {
|
||||
} from './Pedalboard';
|
||||
import PluginControl from './PluginControl';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
import SideChainSelectControl, {SideChainSelectDialog} from './SideChainSelectControl';
|
||||
import VuMeter from './VuMeter';
|
||||
import { nullCast } from './Utility'
|
||||
import { PiPedalStateError } from './PiPedalError';
|
||||
@@ -66,6 +70,11 @@ export interface ICustomizationHost {
|
||||
isLandscapeGrid(): boolean;
|
||||
}
|
||||
|
||||
interface SideChainSelectItem {
|
||||
instanceId: number;
|
||||
title: string;
|
||||
}
|
||||
|
||||
function makeIoPluginInfo(name: string, uri: string): UiPlugin {
|
||||
let result = new UiPlugin();
|
||||
result.name = name;
|
||||
@@ -367,11 +376,12 @@ type PluginControlViewState = {
|
||||
dialogFileValue: string,
|
||||
modGuiContentReady: boolean,
|
||||
showModGuiZoomed: boolean,
|
||||
sidechainDialogOpen: boolean
|
||||
};
|
||||
|
||||
const PluginControlView =
|
||||
withTheme(withStyles(
|
||||
class extends ResizeResponsiveComponent<PluginControlViewProps, PluginControlViewState> implements ICustomizationHost {
|
||||
class extends ResizeResponsiveComponent<PluginControlViewProps, PluginControlViewState> {
|
||||
model: PiPedalModel;
|
||||
|
||||
constructor(props: PluginControlViewProps) {
|
||||
@@ -387,7 +397,8 @@ const PluginControlView =
|
||||
dialogFileProperty: new UiFileProperty(),
|
||||
dialogFileValue: "",
|
||||
modGuiContentReady: false,
|
||||
showModGuiZoomed: false
|
||||
showModGuiZoomed: false,
|
||||
sidechainDialogOpen: false
|
||||
|
||||
}
|
||||
this.onPedalboardChanged = this.onPedalboardChanged.bind(this);
|
||||
@@ -553,6 +564,68 @@ const PluginControlView =
|
||||
}
|
||||
}
|
||||
|
||||
private getSidechainSelectItems(): SideChainSelectItem[] {
|
||||
let myInstanceId = this.props.item.instanceId;
|
||||
|
||||
let items: SideChainSelectItem[] = [];
|
||||
items.push({ instanceId: -1, title: "None" });
|
||||
items.push({ instanceId: -2, title: "Input" });
|
||||
|
||||
let pedalboard = this.model.pedalboard.get();
|
||||
if (!pedalboard) return items;
|
||||
|
||||
let it = pedalboard.itemsGenerator();
|
||||
|
||||
let found = false;
|
||||
while (true) {
|
||||
let v = it.next();
|
||||
if (v.done) {
|
||||
break;
|
||||
}
|
||||
let pedalboardItem = v.value;
|
||||
if (pedalboardItem.isSplit()) {
|
||||
continue;
|
||||
}
|
||||
if (pedalboardItem.instanceId === myInstanceId) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (pedalboardItem.uri.length === 0) {
|
||||
continue;
|
||||
}
|
||||
let pluginInfo = this.model.getUiPlugin(pedalboardItem.uri);
|
||||
if (!pluginInfo) continue;
|
||||
let name = pluginInfo.name;
|
||||
if (pedalboardItem.title.length !== 0) {
|
||||
name = pedalboardItem.title;
|
||||
}
|
||||
items.push({ instanceId: pedalboardItem.instanceId, title: name });
|
||||
}
|
||||
if (!found) {
|
||||
return [{ instanceId: -1, title: "None" }];
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private makeSideChainSelect(uiPlugin: UiPlugin) {
|
||||
let items = this.getSidechainSelectItems();
|
||||
let selectedInstanceId = this.props.item.sideChainInputId;
|
||||
let title = uiPlugin.audio_side_chain_title ? uiPlugin.audio_side_chain_title : "Side chain";
|
||||
|
||||
return (
|
||||
<SideChainSelectControl
|
||||
title={title}
|
||||
selectItems={items}
|
||||
selectedInstanceId={selectedInstanceId}
|
||||
onChanged={(instanceId: number) => {
|
||||
this.model.setPedalboardSideChainInput(this.props.item.instanceId, instanceId);
|
||||
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
isLandscapeGrid(): boolean {
|
||||
return this.state.landscapeGrid;
|
||||
}
|
||||
@@ -575,7 +648,7 @@ const PluginControlView =
|
||||
}
|
||||
return (
|
||||
<div key={key} className={!isLandscapeGrid ? classes.portGroup : classes.portGroupLandscape}
|
||||
style={{ borderWidth: (controlGroup.name === "" ? 0: undefined) }}
|
||||
style={{ borderWidth: (controlGroup.name === "" ? 0 : undefined) }}
|
||||
>
|
||||
{controlGroup.name !== "" && (
|
||||
<div className={classes.portGroupTitle}>
|
||||
@@ -711,6 +784,11 @@ const PluginControlView =
|
||||
);
|
||||
}
|
||||
}
|
||||
if (plugin.audio_side_chain_inputs !== 0) {
|
||||
|
||||
result.push(this.makeSideChainSelect(plugin));
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -803,6 +881,9 @@ const PluginControlView =
|
||||
return false;
|
||||
}
|
||||
|
||||
handleSelectSidechainInput() {
|
||||
this.setState({sidechainDialogOpen: true});
|
||||
}
|
||||
|
||||
handleModGuiFileProperty(instanceId: number, filePropertyUri: string, selectedFile: string) {
|
||||
let plugin = this.model.getUiPlugin(this.props.item.uri);
|
||||
@@ -827,10 +908,30 @@ const PluginControlView =
|
||||
if (!uiPlugin) {
|
||||
return (<div />);
|
||||
}
|
||||
let toolBarChildren: ReactNode[] = [];
|
||||
if (uiPlugin.audio_side_chain_inputs > 0) {
|
||||
toolBarChildren.push(
|
||||
<IconButtonEx tooltip="Select sidechain input"
|
||||
onClick={() => {
|
||||
this.handleSelectSidechainInput();
|
||||
}}
|
||||
>
|
||||
<SidechainConnectIcon
|
||||
style={{
|
||||
opacity: 0.6,
|
||||
width: 24,
|
||||
height: 24,
|
||||
fill: this.props.theme.palette.text.primary
|
||||
}}
|
||||
/>
|
||||
</IconButtonEx>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div style={{ width: "100%", height: "100%", }}>
|
||||
<AutoZoom leftPad={36} rightPad={52} contentReady={this.state.modGuiContentReady}
|
||||
showZoomed={this.state.showModGuiZoomed}
|
||||
toolbarChildren={toolBarChildren}
|
||||
setShowZoomed={
|
||||
(value) => { this.setState({ showModGuiZoomed: value }); }
|
||||
}
|
||||
@@ -1012,6 +1113,16 @@ const PluginControlView =
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{this.state.sidechainDialogOpen && (
|
||||
<SideChainSelectDialog open={this.state.sidechainDialogOpen}
|
||||
onClose={()=>{ this.setState({sidechainDialogOpen: false});}}
|
||||
selectItems={this.getSidechainSelectItems()}
|
||||
selectedInstanceId={this.props.item.sideChainInputId}
|
||||
onChanged={(instanceId: number) => {
|
||||
this.model.setPedalboardSideChainInput(this.props.item.instanceId, instanceId);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{/* xxx: I don't think we need this anyore. */}
|
||||
<FullScreenIME uiControl={this.state.imeUiControl} value={this.state.imeValue}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Theme } from '@mui/material/styles';
|
||||
import WithStyles from './WithStyles';
|
||||
import {createStyles} from './WithStyles';
|
||||
import { createStyles } from './WithStyles';
|
||||
|
||||
import { withStyles } from "tss-react/mui";
|
||||
import { UiControl } from './Lv2Plugin';
|
||||
@@ -47,8 +47,7 @@ const defaultVuColors: VuColor[] = [
|
||||
|
||||
function getVuColor(vuColors: VuColor[], levelDb: number): string {
|
||||
let ix = 0;
|
||||
for (let i = 0; i < vuColors.length; ++i)
|
||||
{
|
||||
for (let i = 0; i < vuColors.length; ++i) {
|
||||
let c = vuColors[i];
|
||||
if (levelDb > c.minDb && levelDb <= c.maxDb) {
|
||||
ix = i;
|
||||
@@ -122,18 +121,18 @@ const styles = (theme: Theme) => createStyles({
|
||||
}),
|
||||
|
||||
titleSection: css({
|
||||
flex: "0 0 auto", alignSelf:"stretch", marginBottom: 8, marginLeft: 0, marginRight: 0
|
||||
flex: "0 0 auto", alignSelf: "stretch", marginBottom: 8, marginLeft: 0, marginRight: 0
|
||||
}),
|
||||
|
||||
midSection: css({
|
||||
flex: "1 1 1", display: "flex",flexFlow: "column nowrap",alignContent: "center",justifyContent: "center"
|
||||
flex: "1 1 1", display: "flex", flexFlow: "column nowrap", alignContent: "center", justifyContent: "center"
|
||||
}),
|
||||
|
||||
editSection: css({
|
||||
flex: "0 0 28", position: "relative", width: 60, height: 28, minHeight: 28
|
||||
}),
|
||||
editSectionNoContent: css({
|
||||
flex: "0 0 28", position: "relative", width: 1, height: 28, minHeight: 28
|
||||
flex: "0 0 28", position: "relative", width: 1, height: 28, minHeight: 28
|
||||
})
|
||||
|
||||
});
|
||||
@@ -153,12 +152,12 @@ const PluginOutputControl =
|
||||
class extends Component<PluginOutputControlProps, PluginOutputControlState> {
|
||||
|
||||
private model: PiPedalModel;
|
||||
private vuRef: React.RefObject<HTMLDivElement|null>;
|
||||
private progressRef: React.RefObject<HTMLDivElement|null>;
|
||||
private dbVuRef: React.RefObject<HTMLDivElement|null>;
|
||||
private dbVuTelltaleRef: React.RefObject<HTMLDivElement|null>;
|
||||
private dbVuTextRef: React.RefObject<HTMLDivElement|null>;
|
||||
private lampRef: React.RefObject<HTMLDivElement|null>;
|
||||
private vuRef: React.RefObject<HTMLDivElement | null>;
|
||||
private progressRef: React.RefObject<HTMLDivElement | null>;
|
||||
private dbVuRef: React.RefObject<HTMLDivElement | null>;
|
||||
private dbVuTelltaleRef: React.RefObject<HTMLDivElement | null>;
|
||||
private dbVuTextRef: React.RefObject<HTMLDivElement | null>;
|
||||
private lampRef: React.RefObject<HTMLDivElement | null>;
|
||||
|
||||
|
||||
constructor(props: PluginOutputControlProps) {
|
||||
@@ -210,7 +209,7 @@ const PluginOutputControl =
|
||||
}
|
||||
}
|
||||
|
||||
private PROGRESS_WIDTH = 40-4;
|
||||
private PROGRESS_WIDTH = 40 - 4;
|
||||
private VU_HEIGHT = 60 - 4;
|
||||
private DB_VU_HEIGHT = 60 - 4;
|
||||
private animationHandle: number | undefined = undefined;
|
||||
@@ -240,26 +239,23 @@ const PluginOutputControl =
|
||||
}
|
||||
if (this.dbVuTextRef.current) {
|
||||
let text: string;
|
||||
if (this.dbVuTelltale <= this.props.uiControl.min_value)
|
||||
{
|
||||
if (this.dbVuTelltale <= this.props.uiControl.min_value) {
|
||||
text = "-";
|
||||
} else {
|
||||
if (Math.abs(this.dbVuTelltale) >= 99.5)
|
||||
{
|
||||
if (Math.abs(this.dbVuTelltale) >= 99.5) {
|
||||
text = Math.round(this.dbVuTelltale).toString();
|
||||
} else {
|
||||
text = this.dbVuTelltale.toFixed(1);
|
||||
}
|
||||
if (!text.startsWith("-")) {
|
||||
text = '+'+text;
|
||||
}
|
||||
text = '+' + text;
|
||||
}
|
||||
}
|
||||
if (this.lastDbText !== text)
|
||||
{
|
||||
if (this.lastDbText !== text) {
|
||||
this.lastDbText = text;
|
||||
this.dbVuTextRef.current.innerText = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.animationHandle = undefined;
|
||||
this.updateDbVuTelltale();
|
||||
}
|
||||
@@ -322,7 +318,7 @@ const PluginOutputControl =
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if (this.vuRef.current) {
|
||||
let control = this.props.uiControl;
|
||||
let range = (value - control.min_value) / (control.max_value - control.min_value);
|
||||
@@ -389,8 +385,7 @@ const PluginOutputControl =
|
||||
if (value > control.max_value) {
|
||||
value = control.max_value;
|
||||
}
|
||||
if (value < control.min_value)
|
||||
{
|
||||
if (value < control.min_value) {
|
||||
value = control.min_value;
|
||||
}
|
||||
let y = (control.max_value - value) * this.DB_VU_HEIGHT / (control.max_value - control.min_value);
|
||||
@@ -439,13 +434,13 @@ const PluginOutputControl =
|
||||
} else if (control.isProgress()) {
|
||||
item_width = undefined;
|
||||
return (
|
||||
<div className={classes.controlFrame}
|
||||
<div className={classes.controlFrame}
|
||||
style={{ width: item_width }}>
|
||||
{/* TITLE SECTION */}
|
||||
<div className={classes.titleSection}
|
||||
style={{ width: "100%" }}>
|
||||
<div className={classes.titleSection}
|
||||
style={{ width: "100%" }}>
|
||||
<ControlTooltip uiControl={control}>
|
||||
<Typography variant="caption" display="block" style={{
|
||||
<Typography variant="caption" display="block" style={{
|
||||
width: "100%",
|
||||
textAlign: "center"
|
||||
}}> {control.name === "" ? "\u00A0" : control.name}</Typography>
|
||||
@@ -453,15 +448,18 @@ const PluginOutputControl =
|
||||
</div>
|
||||
{/* CONTROL SECTION */}
|
||||
|
||||
<div className={classes.midSection}
|
||||
<div className={classes.midSection}
|
||||
style={{ flex: "1 1 1", display: "flex", justifyContent: "center", alignItems: "start", flexFlow: "row nowrap" }}>
|
||||
<div style={{ width: this.PROGRESS_WIDTH+2, height: 12, marginLeft: 8, marginRight: 8, background: "#181818", }}>
|
||||
<div style={{ height: 8, width: this.PROGRESS_WIDTH, overflow: "hidden", position: "absolute",
|
||||
margin: "1px 1px 1px 1px", background: "#282828" }}>
|
||||
<div ref={this.progressRef} style={{ height: 10, width: this.PROGRESS_WIDTH, position: "absolute", marginTop: 0, background: "#0C0" }} />
|
||||
<div style={{ height: 10, width: this.PROGRESS_WIDTH, position: "absolute",
|
||||
<div style={{ width: this.PROGRESS_WIDTH + 2, height: 12, marginLeft: 8, marginRight: 8, background: "#181818", }}>
|
||||
<div style={{
|
||||
height: 8, width: this.PROGRESS_WIDTH, overflow: "hidden", position: "absolute",
|
||||
margin: "1px 1px 1px 1px", background: "#282828"
|
||||
}}>
|
||||
<div ref={this.progressRef} style={{ height: 10, width: this.PROGRESS_WIDTH, position: "absolute", marginTop: 0, background: "#0C0" }} />
|
||||
<div style={{
|
||||
height: 10, width: this.PROGRESS_WIDTH, position: "absolute",
|
||||
boxShadow: "inset 0px 2px 4px #000D", background: "transparent"
|
||||
}} />
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -478,45 +476,48 @@ const PluginOutputControl =
|
||||
let vuColors = defaultVuColors;
|
||||
|
||||
return (
|
||||
<div className={classes.controlFrame}
|
||||
<div className={classes.controlFrame}
|
||||
style={{ width: item_width }}>
|
||||
{/* TITLE SECTION */}
|
||||
<div className={classes.titleSection}
|
||||
style={{ width: "100%" }}>
|
||||
<ControlTooltip uiControl={control}>
|
||||
<Typography variant="caption" display="block" style={{
|
||||
width: "100%",
|
||||
textAlign: "center"
|
||||
}}> {control.name === "" ? "\u00A0" : control.name}</Typography>
|
||||
</ControlTooltip>
|
||||
<div className={classes.titleSection}
|
||||
style={{ width: "100%" }}>
|
||||
<Typography variant="caption" display="block" style={{
|
||||
width: "100%",
|
||||
textAlign: "center"
|
||||
}}> {control.name === "" ? "\u00A0" : control.name}</Typography>
|
||||
</div>
|
||||
{/* CONTROL SECTION */}
|
||||
|
||||
<div className={classes.midSection}
|
||||
<div className={classes.midSection}
|
||||
style={{ flex: "1 1 1", display: "flex", justifyContent: "center", alignItems: "start", flexFlow: "row nowrap" }}>
|
||||
<div style={{ width: 8, height: this.DB_VU_HEIGHT + 4, background: "#000", }}>
|
||||
<div style={{ height: this.DB_VU_HEIGHT, width: 4, overflow: "hidden", position: "absolute", margin: 2 }}>
|
||||
<div style={{ width: 4, height: this.DB_VU_HEIGHT, position: "absolute", left: 0, top: 0 }}>
|
||||
{
|
||||
vuColors.map((vuColor,ix)=>{
|
||||
let top = Math.floor(this.dbVuMap(vuColor.maxDb));
|
||||
let bottom = Math.ceil(this.dbVuMap(vuColor.minDb))+1;
|
||||
<ControlTooltip uiControl={control}>
|
||||
|
||||
return (
|
||||
<div key={ix}
|
||||
style={{ position: "absolute", width: 4, height: (bottom-top),
|
||||
top: top, left:0, background: vuColor.color }} />
|
||||
);
|
||||
})
|
||||
}
|
||||
<div style={{ width: 8, height: this.DB_VU_HEIGHT + 4, background: "#000", }}>
|
||||
<div style={{ height: this.DB_VU_HEIGHT, width: 4, overflow: "hidden", position: "absolute", margin: 2 }}>
|
||||
<div style={{ width: 4, height: this.DB_VU_HEIGHT, position: "absolute", left: 0, top: 0 }}>
|
||||
{
|
||||
vuColors.map((vuColor, ix) => {
|
||||
let top = Math.floor(this.dbVuMap(vuColor.maxDb));
|
||||
let bottom = Math.ceil(this.dbVuMap(vuColor.minDb)) + 1;
|
||||
|
||||
return (
|
||||
<div key={ix}
|
||||
style={{
|
||||
position: "absolute", width: 4, height: (bottom - top),
|
||||
top: top, left: 0, background: vuColor.color
|
||||
}} />
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
||||
<div ref={this.dbVuRef} style={{
|
||||
width: 4, position: "absolute", marginTop: 0, height: this.VU_HEIGHT, background: "#000"
|
||||
}} />
|
||||
<div ref={this.dbVuTelltaleRef} style={{ width: 4, position: "absolute", marginTop: 100, height: 3, background: "#C00" }} />
|
||||
</div>
|
||||
|
||||
<div ref={this.dbVuRef} style={{
|
||||
width: 4, position: "absolute", marginTop: 0, height: this.VU_HEIGHT, background: "#000"
|
||||
}} />
|
||||
<div ref={this.dbVuTelltaleRef} style={{ width: 4, position: "absolute", marginTop: 100, height: 3, background: "#C00" }} />
|
||||
</div>
|
||||
</div>
|
||||
</ControlTooltip>
|
||||
|
||||
</div>
|
||||
<div className={classes.editSection}>
|
||||
@@ -533,26 +534,28 @@ const PluginOutputControl =
|
||||
else if (control.isVu()) {
|
||||
item_width = undefined;
|
||||
return (
|
||||
<div className={classes.controlFrame} style={{ width: item_width}}>
|
||||
<div className={classes.controlFrame} style={{ width: item_width }}>
|
||||
{/* TITLE SECTION */}
|
||||
<div className={classes.titleSection}
|
||||
<div className={classes.titleSection}
|
||||
style={{ width: "100%" }}>
|
||||
<ControlTooltip uiControl={control}>
|
||||
<Typography noWrap display="block" variant="caption" style={{
|
||||
width: item_width,
|
||||
textAlign: "center"
|
||||
}}> {control.name === "" ? "\u00A0" : control.name}</Typography>
|
||||
</ControlTooltip>
|
||||
|
||||
<Typography noWrap display="block" variant="caption" style={{
|
||||
width: item_width,
|
||||
textAlign: "center"
|
||||
}}> {control.name === "" ? "\u00A0" : control.name}</Typography>
|
||||
|
||||
</div>
|
||||
{/* CONTROL SECTION */}
|
||||
|
||||
<div className={classes.midSection}
|
||||
style={{ display: "flex", justifyContent: "center", alignItems: "start", flexFlow: "row nowrap" }}>
|
||||
<div style={{ width: 8, height: this.VU_HEIGHT + 4, background: "#000", }}>
|
||||
<div style={{ height: this.VU_HEIGHT, overflow: "hidden", position: "absolute", margin: 2 }}>
|
||||
<div ref={this.vuRef} style={{ width: 4, height: this.VU_HEIGHT, background: "#0C0", }} />
|
||||
style={{ display: "flex", justifyContent: "center", alignItems: "start", flexFlow: "row nowrap" }}>
|
||||
<ControlTooltip uiControl={control}>
|
||||
<div style={{ width: 8, height: this.VU_HEIGHT + 4, background: "#000", }}>
|
||||
<div style={{ height: this.VU_HEIGHT, overflow: "hidden", position: "absolute", margin: 2 }}>
|
||||
<div ref={this.vuRef} style={{ width: 4, height: this.VU_HEIGHT, background: "#0C0", }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ControlTooltip>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -575,11 +578,11 @@ const PluginOutputControl =
|
||||
}
|
||||
return (
|
||||
<div className={classes.controlFrame}
|
||||
style={{width: attachedLamp? 15: item_width}}
|
||||
>
|
||||
style={{ width: attachedLamp ? 15 : item_width }}
|
||||
>
|
||||
{/* TITLE SECTION */}
|
||||
<div className={classes.titleSection}
|
||||
style={{ flex: "0 0 auto", width: "100%"}}>
|
||||
<div className={classes.titleSection}
|
||||
style={{ flex: "0 0 auto", width: "100%" }}>
|
||||
<ControlTooltip uiControl={control}>
|
||||
<Typography noWrap variant="caption" display="block" style={{
|
||||
width: "100%",
|
||||
@@ -589,7 +592,7 @@ const PluginOutputControl =
|
||||
</div>
|
||||
{/* CONTROL SECTION */}
|
||||
|
||||
<div className={classes.midSection}
|
||||
<div className={classes.midSection}
|
||||
style={{
|
||||
display: "flex", justifyContent: "center", alignItems: "center", flexFlow: "row nowrap",
|
||||
}}>
|
||||
@@ -613,23 +616,23 @@ const PluginOutputControl =
|
||||
);
|
||||
} if (control.isOutputText()) {
|
||||
return (
|
||||
<div className={classes.controlFrame}
|
||||
style={{ display: "flex", flexDirection: "column", width: item_width }}>
|
||||
<div className={classes.controlFrame}
|
||||
style={{ display: "flex", flexDirection: "column", width: item_width }}>
|
||||
{/* TITLE SECTION */}
|
||||
<div className={classes.titleSection}
|
||||
<div className={classes.titleSection}
|
||||
style={{ flex: "0 0 auto", width: "100%" }}>
|
||||
<ControlTooltip uiControl={control}>
|
||||
<Typography noWrap variant="caption" display="block" style={{
|
||||
width: "100%",
|
||||
textAlign: "left"
|
||||
}}> {control.name === "" ? "\u00A0": control.name}</Typography>
|
||||
}}> {control.name === "" ? "\u00A0" : control.name}</Typography>
|
||||
</ControlTooltip>
|
||||
</div>
|
||||
|
||||
{/* CONTROL SECTION */}
|
||||
|
||||
<div className={classes.midSection} style={{
|
||||
display: "flex", justifyContent: "center",
|
||||
display: "flex", justifyContent: "center",
|
||||
flexFlow: "row nowrap", paddingTop: 6
|
||||
|
||||
}}>
|
||||
@@ -646,10 +649,10 @@ const PluginOutputControl =
|
||||
|
||||
} else {
|
||||
return (
|
||||
<div className={classes.controlFrame}
|
||||
style={{ display: "flex", flexDirection: "column", width: item_width }}>
|
||||
<div className={classes.controlFrame}
|
||||
style={{ display: "flex", flexDirection: "column", width: item_width }}>
|
||||
{/* TITLE SECTION */}
|
||||
<div className={classes.titleSection}
|
||||
<div className={classes.titleSection}
|
||||
style={{ flex: "0 0 auto", width: "100%" }}>
|
||||
<ControlTooltip uiControl={control}>
|
||||
<Typography noWrap variant="caption" display="block" style={{
|
||||
@@ -660,7 +663,7 @@ const PluginOutputControl =
|
||||
</div>
|
||||
{/* CONTROL SECTION */}
|
||||
|
||||
<div className={classes.midSection}
|
||||
<div className={classes.midSection}
|
||||
style={{ display: "flex", justifyContent: "start", alignItems: "center", flexFlow: "row nowrap" }}>
|
||||
<Typography noWrap variant="caption" display="block" style={{ width: "100%" }}>
|
||||
{text}
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) Robin E. R. 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { Component } from 'react';
|
||||
import { Theme } from '@mui/material/styles';
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
import DialogEx from './DialogEx';
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { PiPedalModel, PiPedalModelFactory, State } from './PiPedalModel';
|
||||
import Select from '@mui/material/Select';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
|
||||
|
||||
export const StandardItemSize = { width: 80, height: 140 }
|
||||
|
||||
|
||||
import { withStyles } from "tss-react/mui";
|
||||
import WithStyles from './WithStyles';
|
||||
import { withTheme } from './WithStyles';
|
||||
|
||||
const styles = (theme: Theme) => {
|
||||
return {
|
||||
frame: css({
|
||||
position: "relative",
|
||||
margin: "12px"
|
||||
}),
|
||||
controlFrame: css({
|
||||
display: "flex", flexDirection: "column", alignItems: "start", justifyContent: "space-between", height: 116
|
||||
}),
|
||||
displayValue: css({
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 4,
|
||||
textAlign: "center",
|
||||
background: theme.mainBackground,
|
||||
color: theme.palette.text.secondary,
|
||||
// zIndex: -1,
|
||||
}),
|
||||
titleSection: css({
|
||||
flex: "0 0 auto", alignSelf: "stretch", marginBottom: 8, marginLeft: 8, marginRight: 0
|
||||
}),
|
||||
midSection: css({
|
||||
flex: "1 1 1", display: "flex", flexFlow: "column nowrap", alignContent: "center", justifyContent: "center"
|
||||
}),
|
||||
editSection: css({
|
||||
flex: "0 0 0", position: "relative", width: 60, height: 28, minHeight: 28
|
||||
})
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
export interface SideChainSelectControlProps extends WithStyles<typeof styles> {
|
||||
title: string;
|
||||
selectItems: { instanceId: number, title: string }[];
|
||||
selectedInstanceId: number;
|
||||
onChanged(instanceId: number): void;
|
||||
theme: Theme;
|
||||
}
|
||||
type SideChainSelectControlState = {
|
||||
};
|
||||
|
||||
const SideChainSelectControl =
|
||||
withTheme(withStyles(
|
||||
class extends Component<SideChainSelectControlProps, SideChainSelectControlState> {
|
||||
|
||||
model: PiPedalModel;
|
||||
|
||||
|
||||
constructor(props: SideChainSelectControlProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
};
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
this.onStateChanged = this.onStateChanged.bind(this);
|
||||
}
|
||||
|
||||
|
||||
onStateChanged(state: State) {
|
||||
}
|
||||
mounted: boolean = false;
|
||||
componentDidMount() {
|
||||
this.model.state.addOnChangedHandler(this.onStateChanged);
|
||||
this.mounted = true;
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this.mounted = false;
|
||||
this.model.state.removeOnChangedHandler(this.onStateChanged);
|
||||
}
|
||||
componentDidUpdate(prevProps: Readonly<SideChainSelectControlProps>, prevState: Readonly<SideChainSelectControlState>, snapshot?: any): void {
|
||||
}
|
||||
|
||||
render() {
|
||||
//const classes = withStyles.getClasses(this.props);
|
||||
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
|
||||
return (
|
||||
<div className={classes.controlFrame}
|
||||
style={{ width: 120 }}>
|
||||
{/* TITLE SECTION */}
|
||||
<div className={classes.titleSection}
|
||||
>
|
||||
<Typography variant="caption" display="block" noWrap style={{
|
||||
width: "100%",
|
||||
textAlign: "start"
|
||||
}}> {this.props.title}</Typography>
|
||||
</div>
|
||||
{/* CONTROL SECTION */}
|
||||
|
||||
<div className={classes.midSection} style={{ width: "100%", paddingLeft: 8 }}>
|
||||
<Select
|
||||
variant="standard"
|
||||
onChange={(event) => {
|
||||
this.props.onChanged(event.target.value as number);
|
||||
}}
|
||||
value={this.props.selectedInstanceId}
|
||||
style={{ width: "100%", fontSize: "0.8rem" }}
|
||||
>
|
||||
{this.props.selectItems.map((item) => {
|
||||
return (
|
||||
<MenuItem key={item.instanceId} value={item.instanceId}
|
||||
selected={item.instanceId === this.props.selectedInstanceId}>
|
||||
{item.title}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
|
||||
</div>
|
||||
|
||||
{/* LABEL/EDIT SECTION*/}
|
||||
<div className={classes.editSection} >
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
}
|
||||
, styles)
|
||||
)
|
||||
;
|
||||
export default SideChainSelectControl;
|
||||
|
||||
export interface SideChainSelectDialogProps {
|
||||
open: boolean,
|
||||
onClose: () => void,
|
||||
|
||||
selectItems: { instanceId: number, title: string }[];
|
||||
selectedInstanceId: number;
|
||||
onChanged(instanceId: number): void;
|
||||
|
||||
}
|
||||
export function SideChainSelectDialog(props: SideChainSelectDialogProps) {
|
||||
let { open, onClose, selectItems, selectedInstanceId, onChanged } = props;
|
||||
return (
|
||||
<DialogEx tag="nameDialog" open={open} fullWidth maxWidth="xs" onClose={onClose} aria-labelledby="Rename-dialog-title"
|
||||
style={{ userSelect: "none" }}
|
||||
onEnterKey={() => { }}
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title" style={{ userSelect: "none", padding: 0, margin: 0 }}>
|
||||
<Toolbar style={{ paddingLeft: 24, paddingRight: 24, margin: 0 }} >
|
||||
<IconButtonEx
|
||||
tooltip="Back" edge="start" color="inherit" onClick={() => { onClose(); }} aria-label="back"
|
||||
>
|
||||
<ArrowBackIcon style={{opacity: 0.6}} />
|
||||
</IconButtonEx>
|
||||
|
||||
<Typography noWrap variant="body1" style={{ flex: "1 1 auto" }}>Sidechain Input</Typography>
|
||||
</Toolbar>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent >
|
||||
<Select
|
||||
variant="standard"
|
||||
onChange={(event) => {
|
||||
onChanged(event.target.value as number);
|
||||
}}
|
||||
value={selectedInstanceId}
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{selectItems.map((item) => {
|
||||
return (
|
||||
<MenuItem key={item.instanceId} value={item.instanceId}
|
||||
selected={item.instanceId === selectedInstanceId}>
|
||||
{item.title}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</DialogContent>
|
||||
</DialogEx>
|
||||
);
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import CloseIcon from '@mui/icons-material/Close';
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import {createStyles} from './WithStyles';
|
||||
import { createStyles } from './WithStyles';
|
||||
|
||||
import { withStyles } from "tss-react/mui";
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
@@ -148,10 +148,12 @@ const SnapshotEditor = withStyles(
|
||||
|
||||
this.model_.zoomedUiControl.addOnChangedHandler(
|
||||
() => {
|
||||
this.setState({
|
||||
zoomedControlOpen: this.model_.zoomedUiControl.get() !== undefined,
|
||||
zoomedControlInfo: this.model_.zoomedUiControl.get()
|
||||
});
|
||||
if (this.mounted93) {
|
||||
this.setState({
|
||||
zoomedControlOpen: this.model_.zoomedUiControl.get() !== undefined,
|
||||
zoomedControlInfo: this.model_.zoomedUiControl.get()
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -182,9 +184,11 @@ const SnapshotEditor = withStyles(
|
||||
this.setState({ isFullScreen: !this.state.isFullScreen });
|
||||
}
|
||||
|
||||
private mounted93: boolean = false;
|
||||
componentDidMount() {
|
||||
|
||||
super.componentDidMount();
|
||||
this.mounted93 = true;
|
||||
this.model_.showStatusMonitor.addOnChangedHandler(this.showStatusMonitorHandler);
|
||||
|
||||
}
|
||||
@@ -206,6 +210,7 @@ const SnapshotEditor = withStyles(
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.mounted93 = false;
|
||||
super.componentWillUnmount();
|
||||
this.model_.banks.removeOnChangedHandler(this.showStatusMonitorHandler);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 6.35 6.35"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
id="path4452"
|
||||
d="M 3.1501953 1.1797729 C 2.3438819 1.1900494 1.6500313 1.6826477 1.3451375 2.38125 L 1.786971 2.38125 C 2.0624356 1.9000342 2.5807156 1.5761312 3.1760335 1.5761312 C 4.0608593 1.5761312 4.7733521 2.2912075 4.7733521 3.1760335 C 4.7733521 4.0608594 4.0608593 4.7733521 3.1760335 4.7733521 C 2.580068 4.7733521 2.0611725 4.450254 1.7859375 3.96875 L 1.3446208 3.96875 C 1.6524938 4.6748172 2.3583381 5.1697103 3.1760335 5.1697103 C 4.2753461 5.1697103 5.1697103 4.2753461 5.1697103 3.1760335 C 5.1697103 2.0767208 4.2753461 1.1797729 3.1760335 1.1797729 C 3.1674451 1.1797729 3.1587586 1.1796638 3.1501953 1.1797729 z " />
|
||||
<path
|
||||
id="ellipse4674"
|
||||
d="M 3.1760335 2.3300903 C 2.7944551 2.3300903 2.4622893 2.5720679 2.3455933 2.9098999 L 0.64078776 2.9098999 L 0.64078776 3.4395833 L 2.3450765 3.4395833 C 2.4612025 3.7778767 2.7937654 4.019393 3.1760335 4.0193929 C 3.651938 4.0193929 4.0488484 3.6453545 4.0488485 3.1760335 C 4.0488485 2.7067124 3.6519381 2.3300904 3.1760335 2.3300903 z M 3.1760335 2.7264486 C 3.4466224 2.7264486 3.6524902 2.9307799 3.6524902 3.1760335 C 3.6524901 3.4212871 3.4466224 3.6230347 3.1760335 3.6230347 C 2.9054446 3.6230347 2.6975099 3.4212871 2.6975098 3.1760335 C 2.6975098 2.9307799 2.9054446 2.7264486 3.1760335 2.7264486 z " />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
Reference in New Issue
Block a user