+2
-2
@@ -1,10 +1,10 @@
|
|||||||
cmake_minimum_required(VERSION 3.16.0)
|
cmake_minimum_required(VERSION 3.16.0)
|
||||||
project(pipedal
|
project(pipedal
|
||||||
VERSION 1.3.61
|
VERSION 1.3.62
|
||||||
DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi"
|
DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi"
|
||||||
HOMEPAGE_URL "https://rerdavies.github.io/pipedal"
|
HOMEPAGE_URL "https://rerdavies.github.io/pipedal"
|
||||||
)
|
)
|
||||||
set (DISPLAY_VERSION "PiPedal v1.3.61-Release")
|
set (DISPLAY_VERSION "PiPedal v1.3.62-Beta")
|
||||||
set (PACKAGE_ARCHITECTURE "arm64")
|
set (PACKAGE_ARCHITECTURE "arm64")
|
||||||
set (CMAKE_INSTALL_PREFIX "/usr/")
|
set (CMAKE_INSTALL_PREFIX "/usr/")
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,22 @@ namespace pipedal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline bool contains(const std::vector<T>&vector,const T&value)
|
||||||
|
{
|
||||||
|
for (auto i = vector.begin(); i != vector.end(); ++i)
|
||||||
|
{
|
||||||
|
if ((*i) == value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inline bool contains(const std::vector<std::string>&vector,const char*value)
|
||||||
|
{
|
||||||
|
return contains(vector,std::string(value));
|
||||||
|
}
|
||||||
|
|
||||||
class NoCopy {
|
class NoCopy {
|
||||||
public:
|
public:
|
||||||
NoCopy() { }
|
NoCopy() { }
|
||||||
|
|||||||
@@ -6,13 +6,13 @@
|
|||||||
<a href="https://rerdavies.github.io/pipedal/LicensePiPedal.html"><img src="https://img.shields.io/badge/MIT-MIT?label=license&color=%23808080"/></a>
|
<a href="https://rerdavies.github.io/pipedal/LicensePiPedal.html"><img src="https://img.shields.io/badge/MIT-MIT?label=license&color=%23808080"/></a>
|
||||||
<a href="https://github.com/rerdavies/pipedal/actions"><img src="https://img.shields.io/github/actions/workflow/status/rerdavies/pipedal/cmake.yml?branch=main"/></a>
|
<a href="https://github.com/rerdavies/pipedal/actions"><img src="https://img.shields.io/github/actions/workflow/status/rerdavies/pipedal/cmake.yml?branch=main"/></a>
|
||||||
|
|
||||||
Download: <a href='https://rerdavies.github.io/pipedal/download.html'>v1.3.61</a>
|
Download: <a href='https://rerdavies.github.io/pipedal/download.html'>v1.3.62</a>
|
||||||
Website: [https://rerdavies.github.io/pipedal](https://rerdavies.github.io/pipedal).
|
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).
|
Documentation: [https://rerdavies.github.io/pipedal/Documentation.html](https://rerdavies.github.io/pipedal/Documentation.html).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### NEW version 1.3.61 Release, providing [snapshots](https://rerdavies.github.io/pipedal/Snaphots.html), and a new Performance View. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details.
|
#### NEW version 1.3.62 Release, providing [snapshots](https://rerdavies.github.io/pipedal/Snaphots.html), and a new Performance View. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -13,17 +13,17 @@ page_icon: img/Install4.jpg
|
|||||||
|
|
||||||
Download the most recent Debian (.deb) package for your platform:
|
Download the most recent Debian (.deb) package for your platform:
|
||||||
|
|
||||||
- [Raspberry Pi OS bookworm (64-bit) v1.3.61](https://github.com/rerdavies/pipedal/releases/download/)
|
- [Raspberry Pi OS bookworm (64-bit) v1.3.62](https://github.com/rerdavies/pipedal/releases/download/)
|
||||||
- [Ubuntu/Raspberry Pi OS bullseyeye (64-bit) v1.2.31](https://github.com/rerdavies/pipedal/releases/download/v1.1.31/pipedal_1.1.31_arm64.deb)
|
- [Ubuntu/Raspberry Pi OS bullseyeye (64-bit) v1.2.31](https://github.com/rerdavies/pipedal/releases/download/v1.1.31/pipedal_1.1.31_arm64.deb)
|
||||||
|
|
||||||
Version 1.3.61 has not yet been tested on Ubuntu or Raspberry Pi OS bullseye. On these platforms, we recommend that you use version 1.1.31.
|
Version 1.3.62 has not yet been tested on Ubuntu or Raspberry Pi OS bullseye. On these platforms, we recommend that you use version 1.1.31.
|
||||||
|
|
||||||
Install the package by running
|
Install the package by running
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo apt update
|
sudo apt update
|
||||||
cd ~/Downloads
|
cd ~/Downloads
|
||||||
sudo apt-get install pipedal_1.3.61_arm64.deb
|
sudo apt-get install pipedal_1.3.62_arm64.deb
|
||||||
```
|
```
|
||||||
Adjust accordingly if you have downloaded v1.1.31.
|
Adjust accordingly if you have downloaded v1.1.31.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,31 @@
|
|||||||
# Release Notes
|
# Release Notes
|
||||||
|
|
||||||
|
## PiPedal 1.3.62 Beta
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Plugins can now share uploaded model and IR files by type. For plugins with appropriate mod:fileTypes attributes, uploaded
|
||||||
|
files will be shared by category (Nam model, IR Files, Audio Tracks, Midi Songs, etc). Sharing of ML models are also supported, although
|
||||||
|
that requires use of an extended mod:fileType (mlmodel) that MOD does not support.
|
||||||
|
- TooB Tuner stability issues fixed. The pitch detection algorithm has been improved, and the tuner now does filtering to avoid
|
||||||
|
displaying a value when the current input isn't a steady note.
|
||||||
|
- LV2 trigger controls are now displayed as buttons instead of toggle controls (maybe useful for 3rd-party looper plugins).
|
||||||
|
|
||||||
|
Known issues:
|
||||||
|
|
||||||
|
- There are a number of outstanding minor compatibility issues with Ratatouille. I intend to work with the author to address them.
|
||||||
|
|
||||||
|
### Backwards Compatibility With the Previous Upload Directory Scheme
|
||||||
|
|
||||||
|
Sharing file types poses a problem with previous versions of PiPedal which did not share uploads by file type, but instead placed uploads in a private directory for each plugin.
|
||||||
|
In order to accomodate this, Pipedal provides legacy support for this situation. If a private upload directory for a plugin exists, the file property selection dialog will show
|
||||||
|
both the new shared directories, and the old private directory. The private directory has a name that reflects the name of the plugin (e.g. "Ratatoille.lv2"). This directory only
|
||||||
|
shows up if you have used the plugin on previous versiouns of Pipedal. If you have nothing of particular value in the old private upload directory, you can delete the plugin's private directory (which can be found in `/var/pipedal/audio_uploads`), and the private directory will no longer be displayed in the PiPedal UI.
|
||||||
|
|
||||||
|
I anticipate providing a migration utility in the near future which will clean up and migrate legacy upload directories to the new directory structure (automatically uploading
|
||||||
|
presets which reference uploaded files that have moved). In the meantime, I think you will find the current occomodation for legacy upload directories perfectly functional.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## PiPedal 1.3.61 Release
|
## PiPedal 1.3.61 Release
|
||||||
|
|
||||||
Bug fixes:
|
Bug fixes:
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Download the most recent Debian (.deb) package for your platform:
|
Download the most recent Debian (.deb) package for your platform:
|
||||||
|
|
||||||
- <a href="https://github.com/rerdavies/pipedal/releases/download/v1.3.61/pipedal_1.3.61_arm64.deb">Raspberry Pi OS Bookworm (64-bit) v1.3.61</a>
|
- <a href="https://github.com/rerdavies/pipedal/releases/download/v1.3.62/pipedal_1.3.62_arm64.deb">Raspberry Pi OS Bookworm (64-bit) v1.3.62</a>
|
||||||
|
|
||||||
|
|
||||||
Install the package by running
|
Install the package by running
|
||||||
@@ -12,7 +12,7 @@ Install the package by running
|
|||||||
```
|
```
|
||||||
sudo apt update
|
sudo apt update
|
||||||
cd ~/Downloads
|
cd ~/Downloads
|
||||||
sudo apt-get install ./pipedal_1.3.61_arm64.deb
|
sudo apt-get install ./pipedal_1.3.62_arm64.deb
|
||||||
```
|
```
|
||||||
|
|
||||||
Follow the instructions in [_Configuring PiPedal After Installation_](https://rerdavies.github.io/pipedal/Configuring.html) to complete the installation.
|
Follow the instructions in [_Configuring PiPedal After Installation_](https://rerdavies.github.io/pipedal/Configuring.html) to complete the installation.
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<img src="GithubBanner.png" width="100%"/>
|
<img src="GithubBanner.png" width="100%"/>
|
||||||
<a href="Installing.html"><i>v1.3.61</i></a>
|
<a href="Installing.html"><i>v1.3.62</i></a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ To download PiPedal, click [here](download.md).
|
|||||||
To view PiPedal documentation, click [here](Documentation.md).
|
To view PiPedal documentation, click [here](Documentation.md).
|
||||||
|
|
||||||
|
|
||||||
#### NEW version 1.3.61 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details.
|
#### NEW version 1.3.62 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ cabir:impulseFile
|
|||||||
a lv2:Parameter;
|
a lv2:Parameter;
|
||||||
rdfs:label "IR";
|
rdfs:label "IR";
|
||||||
pg:group cabir:impulseFile1Group ;
|
pg:group cabir:impulseFile1Group ;
|
||||||
mod:fileTypes "wav,flac";
|
mod:fileTypes "ir,wav,flac";
|
||||||
|
|
||||||
rdfs:range atom:Path.
|
rdfs:range atom:Path.
|
||||||
|
|
||||||
@@ -63,14 +63,14 @@ cabir:impulseFile2
|
|||||||
a lv2:Parameter;
|
a lv2:Parameter;
|
||||||
rdfs:label "IR";
|
rdfs:label "IR";
|
||||||
pg:group cabir:impulseFile2Group ;
|
pg:group cabir:impulseFile2Group ;
|
||||||
mod:fileTypes "wav,flac";
|
mod:fileTypes "ir,wav,flac";
|
||||||
rdfs:range atom:Path.
|
rdfs:range atom:Path.
|
||||||
|
|
||||||
cabir:impulseFile3
|
cabir:impulseFile3
|
||||||
a lv2:Parameter;
|
a lv2:Parameter;
|
||||||
rdfs:label "IR";
|
rdfs:label "IR";
|
||||||
pg:group cabir:impulseFile3Group ;
|
pg:group cabir:impulseFile3Group ;
|
||||||
mod:fileTypes "wav,flac";
|
mod:fileTypes "ir,wav,flac";
|
||||||
rdfs:range atom:Path.
|
rdfs:range atom:Path.
|
||||||
|
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ cabir:impulseFile3
|
|||||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
rdfs:comment """
|
rdfs:comment """
|
||||||
|
|
||||||
TooB Cab IR is a convolution-based guitar cabinet impulse response simulator.
|
TooB Cab IR is a convolution-based guitar cabinet impulse response simulator.
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ toob:frequencyResponseVector
|
|||||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
|
|
||||||
mod:brand "TooB";
|
mod:brand "TooB";
|
||||||
mod:label "TooB CabSim";
|
mod:label "TooB CabSim";
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
toobimpulse:impulseFile
|
toobimpulse:impulseFile
|
||||||
a lv2:Parameter;
|
a lv2:Parameter;
|
||||||
rdfs:label "Reverb Impulse File";
|
rdfs:label "Reverb Impulse File";
|
||||||
mod:fileTypes "wav,flac";
|
mod:fileTypes "ir,wav,flac";
|
||||||
rdfs:range atom:Path.
|
rdfs:range atom:Path.
|
||||||
|
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ toobimpulse:impulseFile
|
|||||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
rdfs:comment """
|
rdfs:comment """
|
||||||
|
|
||||||
Convolution reverb is a notoriously compute-intensive effect. If you are having performance issues, use the Max T control to constrain the length of the impulse file to
|
Convolution reverb is a notoriously compute-intensive effect. If you are having performance issues, use the Max T control to constrain the length of the impulse file to
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
toobimpulse:impulseFile
|
toobimpulse:impulseFile
|
||||||
a lv2:Parameter;
|
a lv2:Parameter;
|
||||||
rdfs:label "File";
|
rdfs:label "File";
|
||||||
mod:fileTypes "wav,flac";
|
mod:fileTypes "ir,wav,flac";
|
||||||
rdfs:range atom:Path.
|
rdfs:range atom:Path.
|
||||||
|
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ toobimpulse:impulseFile
|
|||||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
rdfs:comment """
|
rdfs:comment """
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ inputStage:filterGroup
|
|||||||
doap:license <https://two-play.com/TooB/licenses/isc> ;
|
doap:license <https://two-play.com/TooB/licenses/isc> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
|
|
||||||
mod:brand "TooB";
|
mod:brand "TooB";
|
||||||
mod:label "TooB Input";
|
mod:label "TooB Input";
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ pstage:stage3
|
|||||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
|
|
||||||
mod:brand "TooB";
|
mod:brand "TooB";
|
||||||
mod:label "Power Stage";
|
mod:label "Power Stage";
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
rdfs:comment "TooB spectrum analyzer" ;
|
rdfs:comment "TooB spectrum analyzer" ;
|
||||||
|
|
||||||
mod:brand "TooB";
|
mod:brand "TooB";
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ tonestack:eqGroup
|
|||||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
|
|
||||||
uiext:ui <http://two-play.com/plugins/toob-tone-stack-ui>;
|
uiext:ui <http://two-play.com/plugins/toob-tone-stack-ui>;
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -40,7 +40,7 @@
|
|||||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
rdfs:comment """
|
rdfs:comment """
|
||||||
Emulation of a Boss CE-2 Chorus.
|
Emulation of a Boss CE-2 Chorus.
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
rdfs:comment """
|
rdfs:comment """
|
||||||
A straightforward no-frills digital delay.
|
A straightforward no-frills digital delay.
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
rdfs:comment """
|
rdfs:comment """
|
||||||
Emulation of a Boss BF-2 Flanger, based on circuit analysis and simulation of the original.
|
Emulation of a Boss BF-2 Flanger, based on circuit analysis and simulation of the original.
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
rdfs:comment """
|
rdfs:comment """
|
||||||
Digital emulation of a Boss BF-2 Flanger.
|
Digital emulation of a Boss BF-2 Flanger.
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
rdfs:comment """
|
rdfs:comment """
|
||||||
Toob Freeverb is an Lv2 implementation of the famous Freeverb reverb effect. FreeVerb delivers a well-balanced reverb with very little tonal coloration.
|
Toob Freeverb is an Lv2 implementation of the famous Freeverb reverb effect. FreeVerb delivers a well-balanced reverb with very little tonal coloration.
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
toobml:modelFile
|
toobml:modelFile
|
||||||
a lv2:Parameter;
|
a lv2:Parameter;
|
||||||
rdfs:label "Model";
|
rdfs:label "Model";
|
||||||
mod:fileTypes "nam,nammodel";
|
mod:fileTypes "json,mlmodels";
|
||||||
rdfs:range atom:Path.
|
rdfs:range atom:Path.
|
||||||
|
|
||||||
toobml:filterGroup
|
toobml:filterGroup
|
||||||
@@ -65,7 +65,7 @@ toobml:sagGroup
|
|||||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
rdfs:comment """
|
rdfs:comment """
|
||||||
The TooB ML Amplifier plugin provides emulation of a variety of amplifiers and overdrive pedals that are implemented
|
The TooB ML Amplifier plugin provides emulation of a variety of amplifiers and overdrive pedals that are implemented
|
||||||
using neural-network-based machine learning models of real amplifiers.
|
using neural-network-based machine learning models of real amplifiers.
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ toobNam:eqGroup
|
|||||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
rdfs:comment """
|
rdfs:comment """
|
||||||
A port of Steven Atkinson's Neural Amp Modeler to LV2.
|
A port of Steven Atkinson's Neural Amp Modeler to LV2.
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
|
||||||
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
doap:maintainer <http://two-play.com/rerdavies#me> ;
|
||||||
lv2:minorVersion 0 ;
|
lv2:minorVersion 0 ;
|
||||||
lv2:microVersion 51 ;
|
lv2:microVersion 52 ;
|
||||||
rdfs:comment """
|
rdfs:comment """
|
||||||
TooB Tuner is a chromatic guitar tuner.
|
TooB Tuner is a chromatic guitar tuner.
|
||||||
""" ;
|
""" ;
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ class DialogEx extends React.Component<DialogExProps,DialogExState> implements I
|
|||||||
{
|
{
|
||||||
this.onEnterKey();
|
this.onEnterKey();
|
||||||
}
|
}
|
||||||
|
evt.stopPropagation();
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
let { tag,onClose,...extra} = this.props;
|
let { tag,onClose,...extra} = this.props;
|
||||||
|
|||||||
+706
-661
File diff suppressed because it is too large
Load Diff
@@ -38,28 +38,18 @@ import { isDarkMode } from './DarkMode';
|
|||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
|
||||||
|
|
||||||
function pathConcat(l: string, r: string): string
|
|
||||||
{
|
|
||||||
if (l.length === 0) return r;
|
|
||||||
if (r.length === 0) return l;
|
|
||||||
return l +'/' + r;
|
|
||||||
}
|
|
||||||
|
|
||||||
class DirectoryTree {
|
class DirectoryTree {
|
||||||
name: string = "";
|
name: string = "";
|
||||||
path: string = "";
|
path: string = "";
|
||||||
expanded: boolean = false;
|
expanded: boolean = false;
|
||||||
|
isProtected: boolean = true;
|
||||||
children: DirectoryTree[] = [];
|
children: DirectoryTree[] = [];
|
||||||
|
|
||||||
constructor(tree: FilePropertyDirectoryTree, parentTree: (DirectoryTree | null) = null) {
|
constructor(tree: FilePropertyDirectoryTree, parentTree: (DirectoryTree | null) = null) {
|
||||||
this.name = tree.directoryName;
|
this.name = tree.directoryName;
|
||||||
if (parentTree) {
|
this.path = tree.directoryName;
|
||||||
this.path = pathConcat(parentTree.path,tree.directoryName);
|
this.name = tree.displayName;
|
||||||
} else {
|
this.isProtected = tree.isProtected;
|
||||||
this.name = "Home";
|
|
||||||
this.path = tree.directoryName;
|
|
||||||
this.expanded = true;
|
|
||||||
}
|
|
||||||
for (var treeChild of tree.children) {
|
for (var treeChild of tree.children) {
|
||||||
this.children.push(new DirectoryTree(treeChild, this));
|
this.children.push(new DirectoryTree(treeChild, this));
|
||||||
}
|
}
|
||||||
@@ -143,6 +133,7 @@ export interface FilePropertyDirectorySelectDialogState {
|
|||||||
selectedPath: string;
|
selectedPath: string;
|
||||||
directoryTree?: DirectoryTree;
|
directoryTree?: DirectoryTree;
|
||||||
hasSelection: boolean;
|
hasSelection: boolean;
|
||||||
|
isProtected: boolean;
|
||||||
directoryTreeInvalidatecount: number;
|
directoryTreeInvalidatecount: number;
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -160,6 +151,7 @@ export default class FilePropertyDirectorySelectDialog extends ResizeResponsiveC
|
|||||||
selectedPath: props.defaultPath,
|
selectedPath: props.defaultPath,
|
||||||
directoryTree: undefined,
|
directoryTree: undefined,
|
||||||
hasSelection: false,
|
hasSelection: false,
|
||||||
|
isProtected: true,
|
||||||
directoryTreeInvalidatecount: 0
|
directoryTreeInvalidatecount: 0
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -192,14 +184,19 @@ export default class FilePropertyDirectorySelectDialog extends ResizeResponsiveC
|
|||||||
this.model.getFilePropertyDirectoryTree(this.props.uiFileProperty)
|
this.model.getFilePropertyDirectoryTree(this.props.uiFileProperty)
|
||||||
.then((filePropertyDirectoryTree) => {
|
.then((filePropertyDirectoryTree) => {
|
||||||
let myTree = new DirectoryTree(filePropertyDirectoryTree);
|
let myTree = new DirectoryTree(filePropertyDirectoryTree);
|
||||||
if (this.props.excludeDirectory)
|
if (this.props.excludeDirectory.length !== 0)
|
||||||
{
|
{
|
||||||
myTree.remove(this.props.excludeDirectory);
|
myTree.remove(this.props.excludeDirectory);
|
||||||
}
|
}
|
||||||
myTree.expand(this.state.selectedPath);
|
myTree.expand(this.state.selectedPath);
|
||||||
|
|
||||||
let hasSelection = myTree.find(this.state.selectedPath) != null;
|
let selection = myTree.find(this.state.selectedPath);
|
||||||
this.setState({ directoryTree: myTree, hasSelection: hasSelection });
|
let hasSelection = !!selection;
|
||||||
|
this.setState({
|
||||||
|
directoryTree: myTree,
|
||||||
|
hasSelection: hasSelection,
|
||||||
|
isProtected: selection ? selection.isProtected : false
|
||||||
|
});
|
||||||
this.requestScroll = true;
|
this.requestScroll = true;
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
@@ -223,6 +220,9 @@ export default class FilePropertyDirectorySelectDialog extends ResizeResponsiveC
|
|||||||
|
|
||||||
|
|
||||||
onOK() {
|
onOK() {
|
||||||
|
if (this.state.isProtected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.state.hasSelection) {
|
if (this.state.hasSelection) {
|
||||||
this.props.onOk(this.state.selectedPath);
|
this.props.onOk(this.state.selectedPath);
|
||||||
}
|
}
|
||||||
@@ -238,7 +238,11 @@ export default class FilePropertyDirectorySelectDialog extends ResizeResponsiveC
|
|||||||
private onTreeClick(directoryTree: DirectoryTree) {
|
private onTreeClick(directoryTree: DirectoryTree) {
|
||||||
if (!this.state.directoryTree) return;
|
if (!this.state.directoryTree) return;
|
||||||
this.state.directoryTree.expand(directoryTree.path);
|
this.state.directoryTree.expand(directoryTree.path);
|
||||||
this.setState({ selectedPath: directoryTree.path, hasSelection: true, directoryTreeInvalidatecount: this.state.directoryTreeInvalidatecount + 1 });
|
this.setState({
|
||||||
|
selectedPath: directoryTree.path,
|
||||||
|
hasSelection: true,
|
||||||
|
isProtected: directoryTree.isProtected,
|
||||||
|
directoryTreeInvalidatecount: this.state.directoryTreeInvalidatecount + 1 });
|
||||||
}
|
}
|
||||||
private renderTree_(directoryTree: DirectoryTree) {
|
private renderTree_(directoryTree: DirectoryTree) {
|
||||||
let ref: ((element: HTMLButtonElement | null) => void) | undefined = undefined;
|
let ref: ((element: HTMLButtonElement | null) => void) | undefined = undefined;
|
||||||
@@ -338,7 +342,8 @@ export default class FilePropertyDirectorySelectDialog extends ResizeResponsiveC
|
|||||||
<Button variant="dialogSecondary" onClick={() => { this.onClose(); }} color="primary">
|
<Button variant="dialogSecondary" onClick={() => { this.onClose(); }} color="primary">
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="dialogPrimary" onClick={() => { this.onOK(); }} color="secondary" disabled={!this.state.hasSelection} >
|
<Button variant="dialogPrimary" onClick={() => { this.onOK(); }} color="secondary"
|
||||||
|
disabled={(!this.state.hasSelection) || this.state.isProtected} >
|
||||||
OK
|
OK
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
export default class FilePropertyDirectoryTree {
|
export default class FilePropertyDirectoryTree {
|
||||||
deserialize(input: any) : FilePropertyDirectoryTree {
|
deserialize(input: any) : FilePropertyDirectoryTree {
|
||||||
this.directoryName = input.directoryName;
|
this.directoryName = input.directoryName;
|
||||||
|
this.displayName = input.displayName;
|
||||||
|
this.isProtected = input.isProtected;
|
||||||
this.children = FilePropertyDirectoryTree.deserialize_array(input.children);
|
this.children = FilePropertyDirectoryTree.deserialize_array(input.children);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -35,5 +37,7 @@ export default class FilePropertyDirectoryTree {
|
|||||||
|
|
||||||
|
|
||||||
directoryName: string = "";
|
directoryName: string = "";
|
||||||
|
displayName: string = "";
|
||||||
|
isProtected: boolean = false;
|
||||||
children: FilePropertyDirectoryTree[] = [];
|
children: FilePropertyDirectoryTree[] = [];
|
||||||
}
|
}
|
||||||
+15
-3
@@ -246,6 +246,8 @@ export class UiFileProperty {
|
|||||||
this.index = input.index;
|
this.index = input.index;
|
||||||
this.portGroup = input.portGroup;
|
this.portGroup = input.portGroup;
|
||||||
this.resourceDirectory = input.resourceDirectory ?? "";
|
this.resourceDirectory = input.resourceDirectory ?? "";
|
||||||
|
this.modDirectories = input.modDirectories;
|
||||||
|
this.useLegacyModDirectory = input.useLegacyModDirectory;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
static deserialize_array(input: any): UiFileProperty[] {
|
static deserialize_array(input: any): UiFileProperty[] {
|
||||||
@@ -297,6 +299,8 @@ export class UiFileProperty {
|
|||||||
index: number = -1;
|
index: number = -1;
|
||||||
portGroup: string = "";
|
portGroup: string = "";
|
||||||
resourceDirectory: string = "";
|
resourceDirectory: string = "";
|
||||||
|
modDirectories: string[] = [];
|
||||||
|
useLegacyModDirectory: boolean = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
export class Lv2Plugin implements Deserializable<Lv2Plugin> {
|
export class Lv2Plugin implements Deserializable<Lv2Plugin> {
|
||||||
@@ -436,6 +440,7 @@ export enum ControlType {
|
|||||||
OnOffSwitch,
|
OnOffSwitch,
|
||||||
ABSwitch,
|
ABSwitch,
|
||||||
Select,
|
Select,
|
||||||
|
Trigger,
|
||||||
|
|
||||||
Tuner,
|
Tuner,
|
||||||
Vu,
|
Vu,
|
||||||
@@ -444,7 +449,7 @@ export enum ControlType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class UiControl implements Deserializable<UiControl> {
|
export class UiControl implements Deserializable<UiControl> {
|
||||||
deserialize(input: any): UiControl {
|
deserialize(input: any): UiControl {
|
||||||
this.symbol = input.symbol;
|
this.symbol = input.symbol;
|
||||||
this.name = input.name;
|
this.name = input.name;
|
||||||
this.index = input.index;
|
this.index = input.index;
|
||||||
@@ -458,7 +463,7 @@ export class UiControl implements Deserializable<UiControl> {
|
|||||||
this.integer_property = input.integer_property;
|
this.integer_property = input.integer_property;
|
||||||
this.enumeration_property = input.enumeration_property;
|
this.enumeration_property = input.enumeration_property;
|
||||||
this.toggled_property = input.toggled_property;
|
this.toggled_property = input.toggled_property;
|
||||||
this.trigger = input.trigger;
|
this.trigger_property = input.trigger_property;
|
||||||
this.not_on_gui = input.not_on_gui;
|
this.not_on_gui = input.not_on_gui;
|
||||||
this.scale_points = ScalePoint.deserialize_array(input.scale_points);
|
this.scale_points = ScalePoint.deserialize_array(input.scale_points);
|
||||||
this.port_group = input.port_group;
|
this.port_group = input.port_group;
|
||||||
@@ -499,6 +504,10 @@ export class UiControl implements Deserializable<UiControl> {
|
|||||||
this.controlType = ControlType.OnOffSwitch;
|
this.controlType = ControlType.OnOffSwitch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.is_input && this.trigger_property)
|
||||||
|
{
|
||||||
|
this.controlType = ControlType.Trigger;
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
applyProperties(properties: Partial<UiControl>): UiControl {
|
applyProperties(properties: Partial<UiControl>): UiControl {
|
||||||
@@ -546,7 +555,7 @@ export class UiControl implements Deserializable<UiControl> {
|
|||||||
range_steps: number = 0;
|
range_steps: number = 0;
|
||||||
integer_property: boolean = false;
|
integer_property: boolean = false;
|
||||||
enumeration_property: boolean = false;
|
enumeration_property: boolean = false;
|
||||||
trigger: boolean = false;
|
trigger_property: boolean = false;
|
||||||
not_on_gui: boolean = false;
|
not_on_gui: boolean = false;
|
||||||
toggled_property: boolean = false;
|
toggled_property: boolean = false;
|
||||||
scale_points: ScalePoint[] = [];
|
scale_points: ScalePoint[] = [];
|
||||||
@@ -590,6 +599,9 @@ export class UiControl implements Deserializable<UiControl> {
|
|||||||
isSelect(): boolean {
|
isSelect(): boolean {
|
||||||
return this.controlType === ControlType.Select;
|
return this.controlType === ControlType.Select;
|
||||||
}
|
}
|
||||||
|
isTrigger(): boolean {
|
||||||
|
return this.controlType === ControlType.Trigger;
|
||||||
|
}
|
||||||
isOutputSelect(): boolean {
|
isOutputSelect(): boolean {
|
||||||
return !this.is_input && this.controlType === ControlType.OutputSelect;
|
return !this.is_input && this.controlType === ControlType.OutputSelect;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ const MidiBindingView =
|
|||||||
isBinaryControl = (port.isAbToggle() || port.isOnOffSwitch());
|
isBinaryControl = (port.isAbToggle() || port.isOnOffSwitch());
|
||||||
if (midiBinding.bindingType !== MidiBinding.BINDING_TYPE_NONE) {
|
if (midiBinding.bindingType !== MidiBinding.BINDING_TYPE_NONE) {
|
||||||
canLatch = isBinaryControl;
|
canLatch = isBinaryControl;
|
||||||
canTrigger = port.trigger;
|
canTrigger = port.trigger_property;
|
||||||
showLinearControlTypeSelect = !(canLatch || canTrigger);
|
showLinearControlTypeSelect = !(canLatch || canTrigger);
|
||||||
showLinearRange = showLinearControlTypeSelect && midiBinding.linearControlType === MidiBinding.LINEAR_CONTROL_TYPE;
|
showLinearRange = showLinearControlTypeSelect && midiBinding.linearControlType === MidiBinding.LINEAR_CONTROL_TYPE;
|
||||||
canRotaryScale = showLinearControlTypeSelect && midiBinding.linearControlType === MidiBinding.CIRCULAR_CONTROL_TYPE;
|
canRotaryScale = showLinearControlTypeSelect && midiBinding.linearControlType === MidiBinding.CIRCULAR_CONTROL_TYPE;
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright (c) 2022 Robin Davies
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export interface ModDirectory {
|
||||||
|
modType: string;
|
||||||
|
pipedalPath: string;
|
||||||
|
displayName: string;
|
||||||
|
defaultFileExtensions: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const modDirectories: ModDirectory[] = [
|
||||||
|
{modType: "audioloop", pipedalPath: "shared/audio/Loops",displayName: "Loops", defaultFileExtensions: ["audio/*"]}, // Audio Loops, meant to be used for looper-style plugins
|
||||||
|
//"audiorecording","shared/audio/Audio Recordings", defaultFileExtensions: ["audio/*"]}, : Audio Recordings, triggered by plugins and stored in the unit
|
||||||
|
{modType: "audiosample", pipedalPath: "shared/audio/Samples", displayName: "Samples", defaultFileExtensions: ["audio/*"]}, // One-shot Audio Samples, meant to be used for sampler-style plugins
|
||||||
|
{modType: "audiotrack", pipedalPath: "shared/audio/Tracks",displayName: "Tracks", defaultFileExtensions: ["audio/*"]}, // Audio Tracks, meant to be used as full-performance/song or backtrack
|
||||||
|
{modType: "cabsim", pipedalPath: "CabIR", displayName: "Cab IRs", defaultFileExtensions: ["audio/*"]}, // Speaker Cabinets, meant as small IR audio files
|
||||||
|
|
||||||
|
/// - h2drumkit: Hydrogen Drumkits, must use h2drumkit file extension
|
||||||
|
{modType: "ir", pipedalPath: "ReverbImpulseFiles", displayName: "Impulse Responses", defaultFileExtensions: ["audio/*"]}, // Impulse Responses
|
||||||
|
{modType: "midiclip", pipedalPath: "shared/midiClips",displayName: "MIDI Clips", defaultFileExtensions: [".mid", ".midi"]}, // MIDI Clips, to be used in sync with host tempo, must have mid or midi file extension
|
||||||
|
{modType: "midisong", pipedalPath: "shared/midiSongs", displayName: "MIDI Songs", defaultFileExtensions: [".mid", ".midi"]}, // MIDI Songs, meant to be used as full-performance/song or backtrack
|
||||||
|
{modType: "sf2", pipedalPath: "shared/sf2",displayName: "Sound Fonts", defaultFileExtensions: ["sf2", "sf3"]}, // SF2 Instruments, must have sf2 or sf3 file extension
|
||||||
|
{modType: "sfz", pipedalPath: "shared/sfz",displayName: "Sfz Files", defaultFileExtensions: ["sfz"]}, // SFZ Instruments, must have sfz file extension
|
||||||
|
// extensions observed in the field.
|
||||||
|
{modType: "audio", pipedalPath: "shared/audio", displayName: "Audio", defaultFileExtensions: ["audio/*"]}, // all audio files (Ratatoille)
|
||||||
|
{modType: "nammodel", pipedalPath: "NeuralAmpModels", displayName: "Neural Amp Models", defaultFileExtensions: [".nam"]}, // Ratatoille, Mike's NAM.
|
||||||
|
{modType: "aidadspmodel", pipedalPath: "shared/aidaaix", displayName: "AIDA IAX Models", defaultFileExtensions: [".json", ".aidaiax"]}, // Ratatoille
|
||||||
|
{modType: "mlmodel", pipedalPath: "ToobMlModels", displayName: "ML Models", defaultFileExtensions: [".json"]}, //
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
export function getModDirectory(modFileType: string): ModDirectory | undefined {
|
||||||
|
for (let i = 0; i < modDirectories.length; ++i)
|
||||||
|
{
|
||||||
|
if (modDirectories[i].modType === modFileType)
|
||||||
|
{
|
||||||
|
return modDirectories[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
@@ -73,8 +73,20 @@ export enum ReconnectReason {
|
|||||||
export type ControlValueChangedHandler = (key: string, value: number) => void;
|
export type ControlValueChangedHandler = (key: string, value: number) => void;
|
||||||
|
|
||||||
export interface FileEntry {
|
export interface FileEntry {
|
||||||
filename: string;
|
pathname: string;
|
||||||
|
displayName: string;
|
||||||
isDirectory: boolean;
|
isDirectory: boolean;
|
||||||
|
isProtected: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface BreadcrumbEntry {
|
||||||
|
pathname: string;
|
||||||
|
displayName: string;
|
||||||
|
};
|
||||||
|
export class FileRequestResult {
|
||||||
|
files: FileEntry[] = [];
|
||||||
|
isProtected: boolean = false;
|
||||||
|
breadcrumbs: BreadcrumbEntry[] = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PluginPresetsChangedHandler = (pluginUri: string) => void;
|
export type PluginPresetsChangedHandler = (pluginUri: string) => void;
|
||||||
@@ -1989,9 +2001,9 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
return nullCast(this.webSocket)
|
return nullCast(this.webSocket)
|
||||||
.request<string[]>('requestFileList', piPedalFileProperty);
|
.request<string[]>('requestFileList', piPedalFileProperty);
|
||||||
}
|
}
|
||||||
requestFileList2(relativeDirectoryPath: string, piPedalFileProperty: UiFileProperty): Promise<FileEntry[]> {
|
requestFileList2(relativeDirectoryPath: string, piPedalFileProperty: UiFileProperty): Promise<FileRequestResult> {
|
||||||
return nullCast(this.webSocket)
|
return nullCast(this.webSocket)
|
||||||
.request<FileEntry[]>('requestFileList2',
|
.request<FileRequestResult>('requestFileList2',
|
||||||
{ relativePath: relativeDirectoryPath, fileProperty: piPedalFileProperty }
|
{ relativePath: relativeDirectoryPath, fileProperty: piPedalFileProperty }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2498,7 +2510,7 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
link.click();
|
link.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadFile(uploadPage: string, file: File, contentType: string = "application/octet-stream", abortController?: AbortController): Promise<string> {
|
uploadUserFile(uploadPage: string, file: File, contentType: string = "application/octet-stream", abortController?: AbortController): Promise<string> {
|
||||||
let result = new Promise<string>((resolve, reject) => {
|
let result = new Promise<string>((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
if (file.size > this.maxFileUploadSize) {
|
if (file.size > this.maxFileUploadSize) {
|
||||||
@@ -2539,10 +2551,20 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then((json) => {
|
.then((json) => {
|
||||||
resolve(json as string);
|
let response = json as {errorMessage: string, path: string};
|
||||||
|
if (response.errorMessage !== "")
|
||||||
|
{
|
||||||
|
throw new Error(response.errorMessage);
|
||||||
|
}
|
||||||
|
resolve(response.path);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
reject("Upload failed. " + error);
|
if (error instanceof Error)
|
||||||
|
{
|
||||||
|
reject("Upload failed. " + (error as Error).message);
|
||||||
|
} else {
|
||||||
|
reject("Upload failed. " + error);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
;
|
;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
+127
-117
@@ -18,6 +18,7 @@
|
|||||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
import React, { TouchEvent, PointerEvent, ReactNode, Component, SyntheticEvent } from 'react';
|
import React, { TouchEvent, PointerEvent, ReactNode, Component, SyntheticEvent } from 'react';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
import { Theme } from '@mui/material/styles';
|
import { Theme } from '@mui/material/styles';
|
||||||
import { WithStyles } from '@mui/styles';
|
import { WithStyles } from '@mui/styles';
|
||||||
import createStyles from '@mui/styles/createStyles';
|
import createStyles from '@mui/styles/createStyles';
|
||||||
@@ -27,10 +28,11 @@ import Typography from '@mui/material/Typography';
|
|||||||
import Input from '@mui/material/Input';
|
import Input from '@mui/material/Input';
|
||||||
import Select from '@mui/material/Select';
|
import Select from '@mui/material/Select';
|
||||||
import Switch from '@mui/material/Switch';
|
import Switch from '@mui/material/Switch';
|
||||||
import Utility, {nullCast} from './Utility';
|
import Utility, { nullCast } from './Utility';
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
import {PiPedalModel,PiPedalModelFactory} from './PiPedalModel';
|
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||||
import {ReactComponent as DialIcon} from './svg/fx_dial.svg';
|
import { ReactComponent as DialIcon } from './svg/fx_dial.svg';
|
||||||
|
import { isDarkMode } from './DarkMode';
|
||||||
|
|
||||||
const MIN_ANGLE = -135;
|
const MIN_ANGLE = -135;
|
||||||
const MAX_ANGLE = 135;
|
const MAX_ANGLE = 135;
|
||||||
@@ -94,8 +96,7 @@ type PluginControlState = {
|
|||||||
|
|
||||||
const PluginControl =
|
const PluginControl =
|
||||||
withStyles(styles, { withTheme: true })(
|
withStyles(styles, { withTheme: true })(
|
||||||
class extends Component<PluginControlProps, PluginControlState>
|
class extends Component<PluginControlProps, PluginControlState> {
|
||||||
{
|
|
||||||
|
|
||||||
frameRef: React.RefObject<HTMLDivElement>;
|
frameRef: React.RefObject<HTMLDivElement>;
|
||||||
imgRef: React.RefObject<SVGSVGElement>;
|
imgRef: React.RefObject<SVGSVGElement>;
|
||||||
@@ -140,23 +141,18 @@ const PluginControl =
|
|||||||
return Utility.needsZoomedControls();
|
return Utility.needsZoomedControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
showZoomedControl()
|
showZoomedControl() {
|
||||||
{
|
if (this.props.uiControl && this.frameRef.current) {
|
||||||
if (this.props.uiControl && this.frameRef.current)
|
this.model.zoomUiControl(this.frameRef.current, this.props.instanceId, this.props.uiControl);
|
||||||
{
|
|
||||||
this.model.zoomUiControl(this.frameRef.current,this.props.instanceId,this.props.uiControl);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hideZoomedControl()
|
hideZoomedControl() {
|
||||||
{
|
if (this.frameRef.current && this.model.zoomedUiControl.get()?.source === this.frameRef.current) {
|
||||||
if (this.frameRef.current && this.model.zoomedUiControl.get()?.source === this.frameRef.current)
|
|
||||||
{
|
|
||||||
this.model.clearZoomedControl();
|
this.model.clearZoomedControl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount()
|
componentWillUnmount() {
|
||||||
{
|
|
||||||
this.hideZoomedControl();
|
this.hideZoomedControl();
|
||||||
}
|
}
|
||||||
inputChanged: boolean = false;
|
inputChanged: boolean = false;
|
||||||
@@ -172,12 +168,11 @@ const PluginControl =
|
|||||||
}
|
}
|
||||||
onInputFocus(event: SyntheticEvent): void {
|
onInputFocus(event: SyntheticEvent): void {
|
||||||
this.displayValueRef.current!.style.display = "none";
|
this.displayValueRef.current!.style.display = "none";
|
||||||
if (Utility.hasIMEKeyboard())
|
if (Utility.hasIMEKeyboard()) {
|
||||||
{
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
this.inputRef.current?.blur();
|
this.inputRef.current?.blur();
|
||||||
this.props.requestIMEEdit(nullCast(this.props.uiControl),this.props.value)
|
this.props.requestIMEEdit(nullCast(this.props.uiControl), this.props.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onInputKeyPress(e: any): void {
|
onInputKeyPress(e: any): void {
|
||||||
@@ -221,7 +216,7 @@ const PluginControl =
|
|||||||
// clamp and quantize.
|
// clamp and quantize.
|
||||||
let range = this.valueToRange(result);
|
let range = this.valueToRange(result);
|
||||||
result = this.rangeToValue(range);
|
result = this.rangeToValue(range);
|
||||||
let displayVal = this.props.uiControl?.formatShortValue(result)??"";
|
let displayVal = this.props.uiControl?.formatShortValue(result) ?? "";
|
||||||
if (event.currentTarget) {
|
if (event.currentTarget) {
|
||||||
event.currentTarget.value = displayVal;
|
event.currentTarget.value = displayVal;
|
||||||
}
|
}
|
||||||
@@ -270,11 +265,9 @@ const PluginControl =
|
|||||||
|
|
||||||
capturedPointers: number[] = [];
|
capturedPointers: number[] = [];
|
||||||
|
|
||||||
onBodyPointerDownCapture(e_: any): any
|
onBodyPointerDownCapture(e_: any): any {
|
||||||
{
|
|
||||||
let e = e_ as PointerEvent;
|
let e = e_ as PointerEvent;
|
||||||
if (this.isExtraTouch(e))
|
if (this.isExtraTouch(e)) {
|
||||||
{
|
|
||||||
this.isTap = false;
|
this.isTap = false;
|
||||||
this.captureElement!.setPointerCapture(e.pointerId);
|
this.captureElement!.setPointerCapture(e.pointerId);
|
||||||
this.capturedPointers.push(e.pointerId);
|
this.capturedPointers.push(e.pointerId);
|
||||||
@@ -305,10 +298,8 @@ const PluginControl =
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
if (this.isTouchDevice())
|
if (this.isTouchDevice()) {
|
||||||
{
|
if (this.props.uiControl?.isDial() ?? false) {
|
||||||
if (this.props.uiControl?.isDial()??false)
|
|
||||||
{
|
|
||||||
this.isTap = false;
|
this.isTap = false;
|
||||||
this.showZoomedControl();
|
this.showZoomedControl();
|
||||||
return;
|
return;
|
||||||
@@ -318,8 +309,7 @@ const PluginControl =
|
|||||||
++this.pointersDown;
|
++this.pointersDown;
|
||||||
|
|
||||||
this.mouseDown = true;
|
this.mouseDown = true;
|
||||||
if (this.pointersDown === 1)
|
if (this.pointersDown === 1) {
|
||||||
{
|
|
||||||
this.isTap = true;
|
this.isTap = true;
|
||||||
this.tapStartMs = Date.now();
|
this.tapStartMs = Date.now();
|
||||||
} else {
|
} else {
|
||||||
@@ -340,8 +330,8 @@ const PluginControl =
|
|||||||
this.captureElement = img;
|
this.captureElement = img;
|
||||||
document.body.addEventListener(
|
document.body.addEventListener(
|
||||||
"pointerdown",
|
"pointerdown",
|
||||||
this.onBodyPointerDownCapture,true
|
this.onBodyPointerDownCapture, true
|
||||||
);
|
);
|
||||||
|
|
||||||
img.setPointerCapture(e.pointerId);
|
img.setPointerCapture(e.pointerId);
|
||||||
if (img.style) {
|
if (img.style) {
|
||||||
@@ -350,11 +340,10 @@ const PluginControl =
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (this.isExtraTouch(e))
|
if (this.isExtraTouch(e)) {
|
||||||
{
|
|
||||||
++this.pointersDown;
|
++this.pointersDown;
|
||||||
this.isTap = false;
|
this.isTap = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -367,7 +356,7 @@ const PluginControl =
|
|||||||
if (this.isCapturedPointer(e)) {
|
if (this.isCapturedPointer(e)) {
|
||||||
--this.pointersDown;
|
--this.pointersDown;
|
||||||
this.isTap = false;
|
this.isTap = false;
|
||||||
|
|
||||||
|
|
||||||
this.releaseCapture(e);
|
this.releaseCapture(e);
|
||||||
}
|
}
|
||||||
@@ -378,21 +367,16 @@ const PluginControl =
|
|||||||
|
|
||||||
let ultraHigh = false;
|
let ultraHigh = false;
|
||||||
let high = false;
|
let high = false;
|
||||||
if (e.ctrlKey)
|
if (e.ctrlKey) {
|
||||||
{
|
|
||||||
ultraHigh = true;
|
ultraHigh = true;
|
||||||
}
|
}
|
||||||
if (e.shiftKey)
|
if (e.shiftKey) {
|
||||||
{
|
|
||||||
high = true;
|
high = true;
|
||||||
}
|
}
|
||||||
if (e.pointerType === "touch")
|
if (e.pointerType === "touch") {
|
||||||
{
|
if (this.pointersDown >= 3) {
|
||||||
if (this.pointersDown >= 3)
|
|
||||||
{
|
|
||||||
ultraHigh = true;
|
ultraHigh = true;
|
||||||
} else if (this.pointersDown === 2)
|
} else if (this.pointersDown === 2) {
|
||||||
{
|
|
||||||
high = true;
|
high = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -411,28 +395,24 @@ const PluginControl =
|
|||||||
}
|
}
|
||||||
private lastTapMs = 0;
|
private lastTapMs = 0;
|
||||||
|
|
||||||
resetToDefaultValue(uiControl: UiControl): void
|
resetToDefaultValue(uiControl: UiControl): void {
|
||||||
{
|
|
||||||
let value = uiControl.default_value;
|
let value = uiControl.default_value;
|
||||||
this.model.setPedalboardControl(this.props.instanceId,uiControl.symbol,value);
|
this.model.setPedalboardControl(this.props.instanceId, uiControl.symbol, value);
|
||||||
}
|
}
|
||||||
onPointerDoubleTap() {
|
onPointerDoubleTap() {
|
||||||
let uiControl = this.props.uiControl;
|
let uiControl = this.props.uiControl;
|
||||||
if (uiControl)
|
if (uiControl) {
|
||||||
{
|
if (uiControl.isDial()) {
|
||||||
if (uiControl.isDial())
|
|
||||||
{
|
|
||||||
this.resetToDefaultValue(uiControl);
|
this.resetToDefaultValue(uiControl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onPointerTap() {
|
onPointerTap() {
|
||||||
let tapTime = Date.now();
|
let tapTime = Date.now();
|
||||||
let dT = tapTime-this.lastTapMs;
|
let dT = tapTime - this.lastTapMs;
|
||||||
this.lastTapMs = tapTime;
|
this.lastTapMs = tapTime;
|
||||||
|
|
||||||
if (dT < 500)
|
if (dT < 500) {
|
||||||
{
|
|
||||||
this.onPointerDoubleTap();
|
this.onPointerDoubleTap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -442,32 +422,29 @@ const PluginControl =
|
|||||||
|
|
||||||
if (this.isCapturedPointer(e)) {
|
if (this.isCapturedPointer(e)) {
|
||||||
--this.pointersDown;
|
--this.pointersDown;
|
||||||
|
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let dRange = this.updateRange(e)
|
let dRange = this.updateRange(e)
|
||||||
this.previewRange(dRange, true);
|
this.previewRange(dRange, true);
|
||||||
|
|
||||||
this.releaseCapture(e);
|
this.releaseCapture(e);
|
||||||
if (this.isTap)
|
if (this.isTap) {
|
||||||
{
|
let ms = Date.now() - this.tapStartMs;
|
||||||
let ms = Date.now()-this.tapStartMs;
|
if (ms < 200) {
|
||||||
if (ms < 200)
|
|
||||||
{
|
|
||||||
this.onPointerTap();
|
this.onPointerTap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
--this.pointersDown;
|
--this.pointersDown;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
releaseCapture(e: PointerEvent<SVGSVGElement>)
|
releaseCapture(e: PointerEvent<SVGSVGElement>) {
|
||||||
{
|
|
||||||
let img = this.imgRef.current;
|
let img = this.imgRef.current;
|
||||||
|
|
||||||
if (img && img.style) {
|
if (img && img.style) {
|
||||||
img.releasePointerCapture(e.pointerId);
|
img.releasePointerCapture(e.pointerId);
|
||||||
img.style.opacity = "" + DEFAULT_OPACITY;
|
img.style.opacity = "" + DEFAULT_OPACITY;
|
||||||
@@ -480,14 +457,14 @@ const PluginControl =
|
|||||||
}
|
}
|
||||||
document.body.removeEventListener(
|
document.body.removeEventListener(
|
||||||
"pointerdown",
|
"pointerdown",
|
||||||
this.onBodyPointerDownCapture,true
|
this.onBodyPointerDownCapture, true
|
||||||
);
|
);
|
||||||
|
|
||||||
this.mouseDown = false;
|
this.mouseDown = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clickSlop() {
|
clickSlop() {
|
||||||
return 3.5; // maybe larger on touch devices.
|
return 3.5; // maybe larger on touch devices.
|
||||||
}
|
}
|
||||||
onPointerMove(e: PointerEvent<SVGSVGElement>): void {
|
onPointerMove(e: PointerEvent<SVGSVGElement>): void {
|
||||||
@@ -499,15 +476,27 @@ const PluginControl =
|
|||||||
let x = e.clientX;
|
let x = e.clientX;
|
||||||
let y = e.clientY;
|
let y = e.clientY;
|
||||||
let dx = x - this.startX;
|
let dx = x - this.startX;
|
||||||
let dy = y-this.startY;
|
let dy = y - this.startY;
|
||||||
let distance = Math.sqrt(dx*dx+dy*dy);
|
let distance = Math.sqrt(dx * dx + dy * dy);
|
||||||
if (distance >= this.clickSlop())
|
if (distance >= this.clickSlop()) {
|
||||||
{
|
|
||||||
this.isTap = false;
|
this.isTap = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
handleTriggerMouseDown() {
|
||||||
|
let uiControl = this.props.uiControl;
|
||||||
|
if (uiControl)
|
||||||
|
{
|
||||||
|
this.model.setPedalboardControl(this.props.instanceId,uiControl.symbol,1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleTriggerMouseUp() {
|
||||||
|
let uiControl = this.props.uiControl;
|
||||||
|
if (uiControl)
|
||||||
|
{
|
||||||
|
this.model.setPedalboardControl(this.props.instanceId,uiControl.symbol,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
previewInputValue(value: number, commitValue: boolean) {
|
previewInputValue(value: number, commitValue: boolean) {
|
||||||
let range = this.valueToRange(value);
|
let range = this.valueToRange(value);
|
||||||
value = this.rangeToValue(range);
|
value = this.rangeToValue(range);
|
||||||
@@ -547,13 +536,12 @@ const PluginControl =
|
|||||||
}
|
}
|
||||||
let inputElement = this.inputRef.current;
|
let inputElement = this.inputRef.current;
|
||||||
if (inputElement) {
|
if (inputElement) {
|
||||||
let v = this.props.uiControl?.formatShortValue(value)??"";
|
let v = this.props.uiControl?.formatShortValue(value) ?? "";
|
||||||
inputElement.value = v;
|
inputElement.value = v;
|
||||||
}
|
}
|
||||||
let displayValue = this.displayValueRef.current;
|
let displayValue = this.displayValueRef.current;
|
||||||
if (displayValue)
|
if (displayValue) {
|
||||||
{
|
let v = this.formatDisplayValue(this.props.uiControl, value);
|
||||||
let v = this.formatDisplayValue(this.props.uiControl,value);
|
|
||||||
displayValue.childNodes[0].textContent = v;
|
displayValue.childNodes[0].textContent = v;
|
||||||
}
|
}
|
||||||
let selectElement = this.selectRef.current;
|
let selectElement = this.selectRef.current;
|
||||||
@@ -577,9 +565,9 @@ const PluginControl =
|
|||||||
}
|
}
|
||||||
onSelectChanged(e: any, value: any) {
|
onSelectChanged(e: any, value: any) {
|
||||||
let target = e.target;
|
let target = e.target;
|
||||||
setTimeout(()=> {
|
setTimeout(() => {
|
||||||
this.props.onChange(target.value);
|
this.props.onChange(target.value);
|
||||||
},0);
|
}, 0);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -610,7 +598,7 @@ const PluginControl =
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Select variant="standard"
|
<Select variant="standard"
|
||||||
ref={this.selectRef}
|
ref={this.selectRef}
|
||||||
value={control.clampSelectValue(value)}
|
value={control.clampSelectValue(value)}
|
||||||
onChange={this.onSelectChanged}
|
onChange={this.onSelectChanged}
|
||||||
@@ -619,7 +607,7 @@ const PluginControl =
|
|||||||
id: 'id' + control.symbol,
|
id: 'id' + control.symbol,
|
||||||
style: { fontSize: FONT_SIZE }
|
style: { fontSize: FONT_SIZE }
|
||||||
}}
|
}}
|
||||||
style={{ marginLeft: 4, marginRight: 4, width: 140,fontSize: 14 }}
|
style={{ marginLeft: 4, marginRight: 4, width: 140, fontSize: 14 }}
|
||||||
>
|
>
|
||||||
{control.scale_points.map((scale_point: ScalePoint) => (
|
{control.scale_points.map((scale_point: ScalePoint) => (
|
||||||
<MenuItem key={scale_point.value} value={scale_point.value}>{scale_point.label}</MenuItem>
|
<MenuItem key={scale_point.value} value={scale_point.value}>{scale_point.label}</MenuItem>
|
||||||
@@ -645,11 +633,10 @@ const PluginControl =
|
|||||||
value = Math.round(value);
|
value = Math.round(value);
|
||||||
}
|
}
|
||||||
let range: number;
|
let range: number;
|
||||||
if (uiControl.is_logarithmic)
|
if (uiControl.is_logarithmic) {
|
||||||
{
|
range = Math.log(value / uiControl.min_value) / Math.log(uiControl.max_value / uiControl.min_value);
|
||||||
range = Math.log(value/uiControl.min_value)/Math.log(uiControl.max_value/uiControl.min_value);
|
if (uiControl.range_steps > 1) {
|
||||||
if (uiControl.range_steps > 1) {
|
range = Math.round(range * (uiControl.range_steps - 1)) / (uiControl.range_steps - 1);
|
||||||
range = Math.round(range*(uiControl.range_steps-1))/(uiControl.range_steps-1);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
range = (value - uiControl.min_value) / (uiControl.max_value - uiControl.min_value);
|
range = (value - uiControl.min_value) / (uiControl.max_value - uiControl.min_value);
|
||||||
@@ -666,17 +653,14 @@ const PluginControl =
|
|||||||
let uiControl = this.props.uiControl;
|
let uiControl = this.props.uiControl;
|
||||||
if (uiControl) {
|
if (uiControl) {
|
||||||
if (uiControl.range_steps > 1) {
|
if (uiControl.range_steps > 1) {
|
||||||
range = Math.round(range * (uiControl.range_steps-1)) / (uiControl.range_steps-1);
|
range = Math.round(range * (uiControl.range_steps - 1)) / (uiControl.range_steps - 1);
|
||||||
}
|
}
|
||||||
let value: number;
|
let value: number;
|
||||||
if (uiControl.min_value === uiControl.max_value)
|
if (uiControl.min_value === uiControl.max_value) {
|
||||||
{
|
|
||||||
value = uiControl.min_value;
|
value = uiControl.min_value;
|
||||||
} else
|
} else {
|
||||||
{
|
if (uiControl.is_logarithmic) {
|
||||||
if (uiControl.is_logarithmic)
|
value = uiControl.min_value * Math.pow(uiControl.max_value / uiControl.min_value, range);
|
||||||
{
|
|
||||||
value = uiControl.min_value*Math.pow(uiControl.max_value/uiControl.min_value,range);
|
|
||||||
} else {
|
} else {
|
||||||
value = range * (uiControl.max_value - uiControl.min_value) + uiControl.min_value;
|
value = range * (uiControl.max_value - uiControl.min_value) + uiControl.min_value;
|
||||||
}
|
}
|
||||||
@@ -724,45 +708,71 @@ const PluginControl =
|
|||||||
let isSelect = control.isSelect();
|
let isSelect = control.isSelect();
|
||||||
let isAbSwitch = control.isAbToggle();
|
let isAbSwitch = control.isAbToggle();
|
||||||
let isOnOffSwitch = control.isOnOffSwitch();
|
let isOnOffSwitch = control.isOnOffSwitch();
|
||||||
|
let isTrigger = control.isTrigger();
|
||||||
|
|
||||||
if (isAbSwitch) {
|
if (isAbSwitch) {
|
||||||
switchText = control.scale_points[0].value === value ? control.scale_points[0].label : control.scale_points[1].label;
|
switchText = control.scale_points[0].value === value ? control.scale_points[0].label : control.scale_points[1].label;
|
||||||
}
|
}
|
||||||
|
|
||||||
let item_width = isSelect ? 160 : 80;
|
let item_width: number | undefined = isSelect ? 160 : 80;
|
||||||
|
if (isTrigger)
|
||||||
|
{
|
||||||
|
item_width = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={this.frameRef} style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "flex-start", width: item_width, margin: 8 }}>
|
<div ref={this.frameRef} style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "flex-start", width: item_width, margin: 8 }}>
|
||||||
{/* TITLE SECTION */}
|
{/* TITLE SECTION */}
|
||||||
<div style={{ flex: "0 0 auto", width: "100%", marginBottom: 8,marginLeft: isSelect? 16: 0, marginRight: 0 }}>
|
<div style={{ flex: "0 0 auto", alignSelf:"stretch", marginBottom: 8, marginLeft: isSelect ? 16 : 0, marginRight: 0 }}>
|
||||||
<Typography variant="caption" display="block" noWrap style={{
|
<Typography variant="caption" display="block" noWrap style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
textAlign: isSelect ? "left" : "center"
|
textAlign: isSelect ? "left" : "center"
|
||||||
}}> {control.name}</Typography>
|
}}> {isTrigger ? "\u00A0" : control.name}</Typography>
|
||||||
</div>
|
</div>
|
||||||
{/* CONTROL SECTION */}
|
{/* CONTROL SECTION */}
|
||||||
|
|
||||||
<div style={{ flex: "0 0 auto" }}>
|
<div style={{ flex: "0 0 auto" }}>
|
||||||
{(isSelect || isAbSwitch || isOnOffSwitch) ? (
|
{isTrigger ? (
|
||||||
this.makeSelect(control, value)
|
<Button variant="contained" color="primary" size="small"
|
||||||
) : (
|
onMouseDown={
|
||||||
<div style={{ flex: "0 1 auto" }}>
|
(evt)=> { this.handleTriggerMouseDown(); }
|
||||||
<DialIcon ref={this.imgRef}
|
}
|
||||||
style={{ overscrollBehavior: "none", touchAction: "none", fill: dialColor,
|
onMouseUp={
|
||||||
width: 36, height: 36, opacity: DEFAULT_OPACITY, transform: this.getRotationTransform() }}
|
(evt)=> { this.handleTriggerMouseUp(); }
|
||||||
onTouchStart={this.onTouchStart} onTouchMove={this.onTouchMove}
|
}
|
||||||
onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} onPointerMoveCapture={this.onPointerMove} onDrag={this.onDrag}
|
style={{
|
||||||
|
textTransform: "none",
|
||||||
/>
|
background: (isDarkMode() ? "#6750A4" : undefined),
|
||||||
</div>
|
marginLeft: 8, marginRight: 8,minWidth:60
|
||||||
|
}}
|
||||||
|
|
||||||
|
>
|
||||||
|
{control.name}
|
||||||
|
</Button>
|
||||||
)
|
)
|
||||||
|
: ((isSelect || isAbSwitch || isOnOffSwitch) ? (
|
||||||
|
this.makeSelect(control, value)
|
||||||
|
) : (
|
||||||
|
<div style={{ flex: "0 1 auto" }}>
|
||||||
|
<DialIcon ref={this.imgRef}
|
||||||
|
style={{
|
||||||
|
overscrollBehavior: "none", touchAction: "none", fill: dialColor,
|
||||||
|
width: 36, height: 36, opacity: DEFAULT_OPACITY, transform: this.getRotationTransform()
|
||||||
|
}}
|
||||||
|
onTouchStart={this.onTouchStart} onTouchMove={this.onTouchMove}
|
||||||
|
onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} onPointerMoveCapture={this.onPointerMove} onDrag={this.onDrag}
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* LABEL/EDIT SECTION*/}
|
{/* LABEL/EDIT SECTION*/}
|
||||||
<div style={{ flex: "0 0 auto", position: "relative", width: 60 }}>
|
<div style={{ flex: "0 0 auto", position: "relative", width: 60 }}>
|
||||||
{(!(isSelect || isOnOffSwitch)) &&
|
{(!(isSelect || isOnOffSwitch || isTrigger)) &&
|
||||||
(
|
(
|
||||||
(isAbSwitch) ? (
|
(isAbSwitch) ? (
|
||||||
<Typography variant="caption" display="block" noWrap style={{
|
<Typography variant="caption" display="block" noWrap style={{
|
||||||
@@ -770,7 +780,7 @@ const PluginControl =
|
|||||||
}}> {switchText} </Typography>
|
}}> {switchText} </Typography>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<Input key={value}
|
<Input key={value}
|
||||||
type="number"
|
type="number"
|
||||||
defaultValue={control.formatShortValue(value)}
|
defaultValue={control.formatShortValue(value)}
|
||||||
error={this.state.error}
|
error={this.state.error}
|
||||||
@@ -785,10 +795,10 @@ const PluginControl =
|
|||||||
|
|
||||||
onKeyPress={this.onInputKeyPress} />
|
onKeyPress={this.onInputKeyPress} />
|
||||||
<div className={classes.displayValue} ref={this.displayValueRef} onClick={(e) => { this.inputRef.current!.focus(); }} >
|
<div className={classes.displayValue} ref={this.displayValueRef} onClick={(e) => { this.inputRef.current!.focus(); }} >
|
||||||
<Typography noWrap color="inherit" style={{ fontSize: "12.8px", paddingTop:4, paddingBottom: 6 }}
|
<Typography noWrap color="inherit" style={{ fontSize: "12.8px", paddingTop: 4, paddingBottom: 6 }}
|
||||||
|
|
||||||
>
|
>
|
||||||
{this.formatDisplayValue(control, value)}</Typography>
|
{this.formatDisplayValue(control, value)}</Typography>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export const SplitTypeControl: UiControl = new UiControl().deserialize({
|
|||||||
enumeration_property: true,
|
enumeration_property: true,
|
||||||
not_on_gui: false,
|
not_on_gui: false,
|
||||||
toggled_property: false,
|
toggled_property: false,
|
||||||
|
trigger_property: false,
|
||||||
scale_points: ScalePoint.deserialize_array(
|
scale_points: ScalePoint.deserialize_array(
|
||||||
[
|
[
|
||||||
{ value: 0, label: "A/B" },
|
{ value: 0, label: "A/B" },
|
||||||
@@ -60,6 +61,7 @@ export const SplitAbControl: UiControl = new UiControl().deserialize({
|
|||||||
enumeration_property: true,
|
enumeration_property: true,
|
||||||
not_on_gui: false,
|
not_on_gui: false,
|
||||||
toggled_property: false,
|
toggled_property: false,
|
||||||
|
trigger_property: false,
|
||||||
scale_points: ScalePoint.deserialize_array(
|
scale_points: ScalePoint.deserialize_array(
|
||||||
[
|
[
|
||||||
{ value: 0, label: "A" },
|
{ value: 0, label: "A" },
|
||||||
@@ -83,6 +85,8 @@ export const SplitMixControl: UiControl = new UiControl().deserialize({
|
|||||||
enumeration_property: false,
|
enumeration_property: false,
|
||||||
not_on_gui: false,
|
not_on_gui: false,
|
||||||
toggled_property: false,
|
toggled_property: false,
|
||||||
|
trigger_property: false,
|
||||||
|
|
||||||
scale_points: []
|
scale_points: []
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -100,6 +104,8 @@ export const SplitPanLeftControl: UiControl = new UiControl().deserialize({
|
|||||||
enumeration_property: false,
|
enumeration_property: false,
|
||||||
not_on_gui: false,
|
not_on_gui: false,
|
||||||
toggled_property: false,
|
toggled_property: false,
|
||||||
|
trigger_property: false,
|
||||||
|
|
||||||
scale_points: []
|
scale_points: []
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -117,6 +123,8 @@ export const SplitVolLeftControl: UiControl = new UiControl().deserialize({
|
|||||||
enumeration_property: false,
|
enumeration_property: false,
|
||||||
not_on_gui: false,
|
not_on_gui: false,
|
||||||
toggled_property: false,
|
toggled_property: false,
|
||||||
|
trigger_property: false,
|
||||||
|
|
||||||
units: Units.db,
|
units: Units.db,
|
||||||
scale_points: [
|
scale_points: [
|
||||||
new ScalePoint().deserialize({
|
new ScalePoint().deserialize({
|
||||||
@@ -141,6 +149,8 @@ export const SplitPanRightControl: UiControl = new UiControl().deserialize({
|
|||||||
enumeration_property: false,
|
enumeration_property: false,
|
||||||
not_on_gui: false,
|
not_on_gui: false,
|
||||||
toggled_property: false,
|
toggled_property: false,
|
||||||
|
trigger_property: false,
|
||||||
|
|
||||||
scale_points: []
|
scale_points: []
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -158,6 +168,8 @@ export const SplitVolRightControl: UiControl = new UiControl().deserialize({
|
|||||||
enumeration_property: false,
|
enumeration_property: false,
|
||||||
not_on_gui: false,
|
not_on_gui: false,
|
||||||
toggled_property: false,
|
toggled_property: false,
|
||||||
|
trigger_property: false,
|
||||||
|
|
||||||
units: Units.db,
|
units: Units.db,
|
||||||
scale_points: [
|
scale_points: [
|
||||||
new ScalePoint().deserialize({
|
new ScalePoint().deserialize({
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ export default class UploadFileDialog extends ResizeResponsiveComponent<UploadFi
|
|||||||
{
|
{
|
||||||
throw new Error("Invalid file extension.");
|
throw new Error("Invalid file extension.");
|
||||||
}
|
}
|
||||||
let filename = await this.model.uploadFile(
|
let filename = await this.model.uploadUserFile(
|
||||||
this.props.uploadPage,
|
this.props.uploadPage,
|
||||||
this.uploadList[i].file,
|
this.uploadList[i].file,
|
||||||
"application/octet-stream",
|
"application/octet-stream",
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set (PIPEDAL_SOURCES
|
set (PIPEDAL_SOURCES
|
||||||
|
ModFileTypes.cpp ModFileTypes.hpp
|
||||||
PatchPropertyWriter.hpp
|
PatchPropertyWriter.hpp
|
||||||
PresetBundle.cpp PresetBundle.hpp
|
PresetBundle.cpp PresetBundle.hpp
|
||||||
RealtimeMidiEventType.hpp
|
RealtimeMidiEventType.hpp
|
||||||
@@ -637,6 +638,7 @@ add_executable(pipedalconfig
|
|||||||
CommandLineParser.hpp
|
CommandLineParser.hpp
|
||||||
PiPedalException.hpp
|
PiPedalException.hpp
|
||||||
ConfigMain.cpp
|
ConfigMain.cpp
|
||||||
|
ModFileTypes.cpp ModFileTypes.hpp
|
||||||
PiPedalConfiguration.hpp PiPedalConfiguration.cpp
|
PiPedalConfiguration.hpp PiPedalConfiguration.cpp
|
||||||
JackServerSettings.hpp JackServerSettings.cpp
|
JackServerSettings.hpp JackServerSettings.cpp
|
||||||
SystemConfigFile.hpp SystemConfigFile.cpp
|
SystemConfigFile.hpp SystemConfigFile.cpp
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "CommandLineParser.hpp"
|
#include "CommandLineParser.hpp"
|
||||||
#include "SystemConfigFile.hpp"
|
#include "SystemConfigFile.hpp"
|
||||||
|
#include "ModFileTypes.hpp"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -951,6 +952,7 @@ void Install(const fs::path &programPrefix, const std::string endpointAddress)
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
// apply policy changes we dropped into the polkit configuration files (allows pipedal_d to use NetworkManager dbus apis).
|
// apply policy changes we dropped into the polkit configuration files (allows pipedal_d to use NetworkManager dbus apis).
|
||||||
silentSysExec(SYSTEMCTL_BIN " restart polkit.service");
|
silentSysExec(SYSTEMCTL_BIN " restart polkit.service");
|
||||||
|
|
||||||
@@ -1158,6 +1160,8 @@ void Install(const fs::path &programPrefix, const std::string endpointAddress)
|
|||||||
sysExec(SYSTEMCTL_BIN " daemon-reload");
|
sysExec(SYSTEMCTL_BIN " daemon-reload");
|
||||||
|
|
||||||
FixPermissions();
|
FixPermissions();
|
||||||
|
ModFileTypes::CreateDefaultDirectories("/var/pipedal/audio_uploads");
|
||||||
|
|
||||||
|
|
||||||
StopService(false);
|
StopService(false);
|
||||||
AvahiInstall();
|
AvahiInstall();
|
||||||
|
|||||||
+15
-2
@@ -22,7 +22,20 @@
|
|||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
|
|
||||||
JSON_MAP_BEGIN(FileEntry)
|
JSON_MAP_BEGIN(FileEntry)
|
||||||
JSON_MAP_REFERENCE(FileEntry,filename)
|
JSON_MAP_REFERENCE(FileEntry,pathname)
|
||||||
|
JSON_MAP_REFERENCE(FileEntry,displayName)
|
||||||
|
JSON_MAP_REFERENCE(FileEntry,isProtected)
|
||||||
JSON_MAP_REFERENCE(FileEntry,isDirectory)
|
JSON_MAP_REFERENCE(FileEntry,isDirectory)
|
||||||
|
JSON_MAP_END()
|
||||||
|
|
||||||
|
JSON_MAP_BEGIN(BreadcrumbEntry)
|
||||||
|
JSON_MAP_REFERENCE(BreadcrumbEntry,pathname)
|
||||||
|
JSON_MAP_REFERENCE(BreadcrumbEntry,displayName)
|
||||||
|
JSON_MAP_END()
|
||||||
|
|
||||||
|
|
||||||
|
JSON_MAP_BEGIN(FileRequestResult)
|
||||||
|
JSON_MAP_REFERENCE(FileRequestResult,files)
|
||||||
|
JSON_MAP_REFERENCE(FileRequestResult,isProtected)
|
||||||
|
JSON_MAP_REFERENCE(FileRequestResult,breadcrumbs)
|
||||||
JSON_MAP_END()
|
JSON_MAP_END()
|
||||||
|
|||||||
+21
-3
@@ -26,15 +26,33 @@ namespace pipedal {
|
|||||||
class FileEntry {
|
class FileEntry {
|
||||||
public:
|
public:
|
||||||
FileEntry() { }
|
FileEntry() { }
|
||||||
FileEntry(std::string filename,bool isDirectory)
|
FileEntry(const std::string&pathname,const std::string &displayName,bool isDirectory, bool isProtected = false)
|
||||||
:filename_(filename), isDirectory_(isDirectory)
|
:pathname_(pathname), displayName_(displayName), isDirectory_(isDirectory),isProtected_(isProtected)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
std::string filename_;
|
std::string pathname_;
|
||||||
|
std::string displayName_;
|
||||||
bool isDirectory_ = false;
|
bool isDirectory_ = false;
|
||||||
|
bool isProtected_ = false;
|
||||||
|
|
||||||
DECLARE_JSON_MAP(FileEntry);
|
DECLARE_JSON_MAP(FileEntry);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
class BreadcrumbEntry {
|
||||||
|
public:
|
||||||
|
std::string pathname_;
|
||||||
|
std::string displayName_;
|
||||||
|
|
||||||
|
DECLARE_JSON_MAP(BreadcrumbEntry);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileRequestResult {
|
||||||
|
public:
|
||||||
|
std::vector<FileEntry> files_;
|
||||||
|
bool isProtected_ = false;
|
||||||
|
std::vector<BreadcrumbEntry> breadcrumbs_;
|
||||||
|
DECLARE_JSON_MAP(FileRequestResult);
|
||||||
|
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -20,16 +20,26 @@
|
|||||||
#include "FilePropertyDirectoryTree.hpp"
|
#include "FilePropertyDirectoryTree.hpp"
|
||||||
|
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
FilePropertyDirectoryTree::FilePropertyDirectoryTree()
|
FilePropertyDirectoryTree::FilePropertyDirectoryTree()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
FilePropertyDirectoryTree::FilePropertyDirectoryTree(const std::string &directoryName)
|
FilePropertyDirectoryTree::FilePropertyDirectoryTree(const std::string &directoryName)
|
||||||
: directoryName_(directoryName)
|
: directoryName_(directoryName),
|
||||||
|
displayName_(fs::path(directoryName).filename().string())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FilePropertyDirectoryTree::FilePropertyDirectoryTree(const std::string &directoryName, const std::string &displayName)
|
||||||
|
: directoryName_(directoryName),
|
||||||
|
displayName_(displayName)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_MAP_BEGIN(FilePropertyDirectoryTree)
|
JSON_MAP_BEGIN(FilePropertyDirectoryTree)
|
||||||
JSON_MAP_REFERENCE(FilePropertyDirectoryTree, directoryName)
|
JSON_MAP_REFERENCE(FilePropertyDirectoryTree, directoryName)
|
||||||
|
JSON_MAP_REFERENCE(FilePropertyDirectoryTree, displayName)
|
||||||
|
JSON_MAP_REFERENCE(FilePropertyDirectoryTree, isProtected)
|
||||||
JSON_MAP_REFERENCE(FilePropertyDirectoryTree, children)
|
JSON_MAP_REFERENCE(FilePropertyDirectoryTree, children)
|
||||||
JSON_MAP_END()
|
JSON_MAP_END()
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ namespace pipedal {
|
|||||||
|
|
||||||
FilePropertyDirectoryTree();
|
FilePropertyDirectoryTree();
|
||||||
FilePropertyDirectoryTree(const std::string&directoryName);
|
FilePropertyDirectoryTree(const std::string&directoryName);
|
||||||
|
FilePropertyDirectoryTree(const std::string&directoryName,const std::string&displayName);
|
||||||
std::string directoryName_;
|
std::string directoryName_;
|
||||||
|
std::string displayName_;
|
||||||
|
bool isProtected_ = false;
|
||||||
std::vector<std::unique_ptr<FilePropertyDirectoryTree>> children_;
|
std::vector<std::unique_ptr<FilePropertyDirectoryTree>> children_;
|
||||||
|
|
||||||
DECLARE_JSON_MAP(FilePropertyDirectoryTree);
|
DECLARE_JSON_MAP(FilePropertyDirectoryTree);
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
// Copyright (c) 2024 Robin Davies
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "ModFileTypes.hpp"
|
||||||
|
#include "util.hpp"
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <iostream>
|
||||||
|
#include "ss.hpp"
|
||||||
|
|
||||||
|
using namespace pipedal;
|
||||||
|
|
||||||
|
const std::vector<ModFileTypes::ModDirectory> ModFileTypes::ModDirectories =
|
||||||
|
{
|
||||||
|
{"audioloop", "shared/audio/Loops", "Loops", {"audio/*"}}, // Audio Loops, meant to be used for looper-style plugins
|
||||||
|
//"audiorecording","shared/audio/Audio Recordings", {"audio/*"}}, : Audio Recordings, triggered by plugins and stored in the unit
|
||||||
|
{"audiosample", "shared/audio/Samples", "Samples", {"audio/*"}}, // One-shot Audio Samples, meant to be used for sampler-style plugins
|
||||||
|
{"audiotrack", "shared/audio/Tracks", "Tracks", {"audio/*"}}, // Audio Tracks, meant to be used as full-performance/song or backtrack
|
||||||
|
{"cabsim", "CabIR", "Cab IRs", {"audio/*"}}, // Speaker Cabinets, meant as small IR audio files
|
||||||
|
|
||||||
|
/// - h2drumkit: Hydrogen Drumkits, must use h2drumkit file extension
|
||||||
|
{"ir", "ReverbImpulseFiles", "Impulse Responses", {"audio/*"}}, // Impulse Responses
|
||||||
|
{"midiclip", "shared/midiClips", "MIDI Clips", {".mid", ".midi"}}, // MIDI Clips, to be used in sync with host tempo, must have mid or midi file extension
|
||||||
|
{"midisong", "shared/midiSongs", "MIDI Songs", {".mid", ".midi"}}, // MIDI Songs, meant to be used as full-performance/song or backtrack
|
||||||
|
{"sf2", "shared/sf2", "Sound Fonts", {"sf2", "sf3"}}, // SF2 Instruments, must have sf2 or sf3 file extension
|
||||||
|
{"sfz", "shared/sfz", "Sfz Files", {"sfz"}}, // SFZ Instruments, must have sfz file extension
|
||||||
|
// extensions observed in the field.
|
||||||
|
{"audio", "shared/audio", "Audio", {"audio/*"}}, // all audio files (Ratatoille)
|
||||||
|
{"nammodel", "NeuralAmpModels", "Neural Amp Models", {".nam"}}, // Ratatoille, Mike's NAM.
|
||||||
|
{"aidadspmodel", "shared/aidaaix", "AIDA IAX Models", {".json", ".aidaiax"}}, // Ratatoille
|
||||||
|
{"mlmodel", "ToobMlModels", "ML Models", {".json"}}, //
|
||||||
|
//
|
||||||
|
};
|
||||||
|
|
||||||
|
const ModFileTypes::ModDirectory *ModFileTypes::GetModDirectory(const std::string &type)
|
||||||
|
{
|
||||||
|
for (const ModFileTypes::ModDirectory &modType : ModFileTypes::ModDirectories)
|
||||||
|
{
|
||||||
|
if (modType.modType == type)
|
||||||
|
{
|
||||||
|
return &modType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModFileTypes::ModFileTypes(const std::string &fileTypes)
|
||||||
|
{
|
||||||
|
std::vector<std::string> types = split(fileTypes, ',');
|
||||||
|
for (const auto &type : types)
|
||||||
|
{
|
||||||
|
const ModDirectory *wellKnownType = GetModDirectory(type);
|
||||||
|
if (wellKnownType)
|
||||||
|
{
|
||||||
|
rootDirectories_.push_back(type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fileTypes_.push_back(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// for Ratatoille.lv2.
|
||||||
|
// If rootDirectories contains "nammodel" and fileTypes contains "json", add "mlmodel" too.
|
||||||
|
// if (contains(rootDirectories_,"nammodel") && contains(fileTypes_,"json"))
|
||||||
|
// {
|
||||||
|
// if (!contains(rootDirectories_,"mlmodel"))
|
||||||
|
// {
|
||||||
|
// rootDirectories_.push_back("mlmodel");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
if (fileTypes_.empty())
|
||||||
|
{
|
||||||
|
for (const auto &type : types)
|
||||||
|
{
|
||||||
|
const ModDirectory *wellKnownType = GetModDirectory(type);
|
||||||
|
if (wellKnownType)
|
||||||
|
{
|
||||||
|
for (const std::string &newType : wellKnownType->defaultFileExtensions)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
for (const std::string &oldType : fileTypes_)
|
||||||
|
{
|
||||||
|
if (newType == oldType)
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
fileTypes_.push_back(newType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::filesystem::path getModDirectoryPath(
|
||||||
|
const std::filesystem::path &rootDirectory,
|
||||||
|
const std::string &modType)
|
||||||
|
{
|
||||||
|
auto wellKnownType = ModFileTypes::GetModDirectory(modType);
|
||||||
|
if (!wellKnownType)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(SS("Can't find modFileType " << modType));
|
||||||
|
}
|
||||||
|
return rootDirectory / wellKnownType->pipedalPath;
|
||||||
|
}
|
||||||
|
void ModFileTypes::CreateDefaultDirectories(const std::filesystem::path &rootDirectory)
|
||||||
|
{
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
using namespace std;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (const auto &modType : ModDirectories)
|
||||||
|
{
|
||||||
|
fs::path path = rootDirectory / modType.pipedalPath;
|
||||||
|
|
||||||
|
fs::create_directories(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs::exists(rootDirectory / "shared" / "audio" / "Cab IR Files"))
|
||||||
|
{
|
||||||
|
fs::create_symlink(
|
||||||
|
getModDirectoryPath(rootDirectory, "cabsim"),
|
||||||
|
rootDirectory / "shared" / "audio" / "Cab IR Files");
|
||||||
|
}
|
||||||
|
if (!fs::exists(rootDirectory / "shared" / "audio" / "Impulse Responses"))
|
||||||
|
{
|
||||||
|
fs::create_symlink(
|
||||||
|
getModDirectoryPath(rootDirectory, "ir"),
|
||||||
|
rootDirectory / "shared" / "audio" / "Impulse Responses");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
cout << "Can't create default MOD directories. " << e.what() << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) 2024 Robin Davies
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to
|
||||||
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace pipedal {
|
||||||
|
class ModFileTypes {
|
||||||
|
|
||||||
|
public:
|
||||||
|
ModFileTypes(const std::string&fileTypes);
|
||||||
|
const std::vector<std::string> &rootDirectories() { return rootDirectories_; }
|
||||||
|
const std::vector<std::string> &fileTypes() { return fileTypes_;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::string> rootDirectories_;
|
||||||
|
std::vector<std::string> fileTypes_;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
class ModDirectory {
|
||||||
|
public:
|
||||||
|
const std::string modType;
|
||||||
|
const std::string pipedalPath;
|
||||||
|
const std::string displayName;
|
||||||
|
const std::vector<std::string> defaultFileExtensions;
|
||||||
|
};
|
||||||
|
|
||||||
|
const static std::vector<ModDirectory> ModDirectories;
|
||||||
|
static const ModDirectory* GetModDirectory(const std::string&modType);
|
||||||
|
|
||||||
|
static void CreateDefaultDirectories(const std::filesystem::path&rootDirectory);
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2296,7 +2296,7 @@ std::vector<std::string> PiPedalModel::GetFileList(const UiFileProperty &filePro
|
|||||||
return std::vector<std::string>(); // don't disclose to users what the problem is.
|
return std::vector<std::string>(); // don't disclose to users what the problem is.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::vector<FileEntry> PiPedalModel::GetFileList2(const std::string &relativePath, const UiFileProperty &fileProperty)
|
FileRequestResult PiPedalModel::GetFileList2(const std::string &relativePath, const UiFileProperty &fileProperty)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -2305,7 +2305,7 @@ std::vector<FileEntry> PiPedalModel::GetFileList2(const std::string &relativePat
|
|||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
Lv2Log::warning("GetFileList() failed: (%s)", e.what());
|
Lv2Log::warning("GetFileList() failed: (%s)", e.what());
|
||||||
return std::vector<FileEntry>(); // don't disclose to users what the problem is.
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ namespace pipedal
|
|||||||
void SetUpdatePolicy(UpdatePolicyT updatePolicy);
|
void SetUpdatePolicy(UpdatePolicyT updatePolicy);
|
||||||
void ForceUpdateCheck();
|
void ForceUpdateCheck();
|
||||||
std::vector<std::string> GetFileList(const UiFileProperty &fileProperty);
|
std::vector<std::string> GetFileList(const UiFileProperty &fileProperty);
|
||||||
std::vector<FileEntry> GetFileList2(const std::string &relativePath, const UiFileProperty &fileProperty);
|
FileRequestResult GetFileList2(const std::string &relativePath, const UiFileProperty &fileProperty);
|
||||||
|
|
||||||
void DeleteSampleFile(const std::filesystem::path &fileName);
|
void DeleteSampleFile(const std::filesystem::path &fileName);
|
||||||
std::string CreateNewSampleDirectory(const std::string &relativePath, const UiFileProperty &uiFileProperty);
|
std::string CreateNewSampleDirectory(const std::string &relativePath, const UiFileProperty &uiFileProperty);
|
||||||
|
|||||||
@@ -1589,8 +1589,8 @@ public:
|
|||||||
{
|
{
|
||||||
FileRequestArgs requestArgs;
|
FileRequestArgs requestArgs;
|
||||||
pReader->read(&requestArgs);
|
pReader->read(&requestArgs);
|
||||||
std::vector<FileEntry> list = this->model.GetFileList2(requestArgs.relativePath_, requestArgs.fileProperty_);
|
FileRequestResult result = this->model.GetFileList2(requestArgs.relativePath_, requestArgs.fileProperty_);
|
||||||
this->Reply(replyTo, "requestFileList2", list);
|
this->Reply(replyTo, "requestFileList2", result);
|
||||||
}
|
}
|
||||||
else if (message == "newPreset")
|
else if (message == "newPreset")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -465,6 +465,8 @@ JSON_MAP_REFERENCE(UiFileProperty, patchProperty)
|
|||||||
JSON_MAP_REFERENCE(UiFileProperty, fileTypes)
|
JSON_MAP_REFERENCE(UiFileProperty, fileTypes)
|
||||||
JSON_MAP_REFERENCE(UiFileProperty, portGroup)
|
JSON_MAP_REFERENCE(UiFileProperty, portGroup)
|
||||||
JSON_MAP_REFERENCE(UiFileProperty, resourceDirectory)
|
JSON_MAP_REFERENCE(UiFileProperty, resourceDirectory)
|
||||||
|
JSON_MAP_REFERENCE(UiFileProperty, modDirectories)
|
||||||
|
JSON_MAP_REFERENCE(UiFileProperty, useLegacyModDirectory)
|
||||||
JSON_MAP_END()
|
JSON_MAP_END()
|
||||||
|
|
||||||
JSON_MAP_BEGIN(UiFrequencyPlot)
|
JSON_MAP_BEGIN(UiFrequencyPlot)
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ namespace pipedal {
|
|||||||
std::string patchProperty_;
|
std::string patchProperty_;
|
||||||
std::string portGroup_;
|
std::string portGroup_;
|
||||||
std::string resourceDirectory_;
|
std::string resourceDirectory_;
|
||||||
|
std::vector<std::string> modDirectories_;
|
||||||
|
bool useLegacyModDirectory_= false;
|
||||||
public:
|
public:
|
||||||
using ptr = std::shared_ptr<UiFileProperty>;
|
using ptr = std::shared_ptr<UiFileProperty>;
|
||||||
UiFileProperty() { }
|
UiFileProperty() { }
|
||||||
@@ -121,9 +123,17 @@ namespace pipedal {
|
|||||||
UiFileProperty(const std::string&name, const std::string&patchProperty,const std::string &directory);
|
UiFileProperty(const std::string&name, const std::string&patchProperty,const std::string &directory);
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<std::string>& modDirectories() { return modDirectories_; }
|
||||||
|
const std::vector<std::string>& modDirectories() const { return modDirectories_; }
|
||||||
|
|
||||||
|
bool useLegacyModDirectory() const { return useLegacyModDirectory_; }
|
||||||
|
void useLegacyModDirectory(bool value) { useLegacyModDirectory_ = value; }
|
||||||
const std::string &label() const { return label_; }
|
const std::string &label() const { return label_; }
|
||||||
int32_t index() const { return index_; }
|
int32_t index() const { return index_; }
|
||||||
|
|
||||||
const std::string &directory() const { return directory_; }
|
const std::string &directory() const { return directory_; }
|
||||||
|
void directory(const std::string &path) { directory_ = path; }
|
||||||
|
|
||||||
const std::string&portGroup() const { return portGroup_; }
|
const std::string&portGroup() const { return portGroup_; }
|
||||||
|
|
||||||
const std::vector<UiFileType> &fileTypes() const { return fileTypes_; }
|
const std::vector<UiFileType> &fileTypes() const { return fileTypes_; }
|
||||||
|
|||||||
+92
-54
@@ -46,6 +46,7 @@
|
|||||||
#include "PiPedalException.hpp"
|
#include "PiPedalException.hpp"
|
||||||
#include "StdErrorCapture.hpp"
|
#include "StdErrorCapture.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
|
#include "ModFileTypes.hpp"
|
||||||
|
|
||||||
#include "Locale.hpp"
|
#include "Locale.hpp"
|
||||||
|
|
||||||
@@ -118,6 +119,7 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld)
|
|||||||
core__toggled = lilv_new_uri(pWorld, LV2_CORE__toggled);
|
core__toggled = lilv_new_uri(pWorld, LV2_CORE__toggled);
|
||||||
core__connectionOptional = lilv_new_uri(pWorld, LV2_CORE__connectionOptional);
|
core__connectionOptional = lilv_new_uri(pWorld, LV2_CORE__connectionOptional);
|
||||||
portprops__not_on_gui_property_uri = lilv_new_uri(pWorld, LV2_PORT_PROPS__notOnGUI);
|
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);
|
midi__event = lilv_new_uri(pWorld, LV2_MIDI__MidiEvent);
|
||||||
core__designation = lilv_new_uri(pWorld, LV2_CORE__designation);
|
core__designation = lilv_new_uri(pWorld, LV2_CORE__designation);
|
||||||
portgroups__group = lilv_new_uri(pWorld, LV2_PORT_GROUPS__group);
|
portgroups__group = lilv_new_uri(pWorld, LV2_PORT_GROUPS__group);
|
||||||
@@ -191,8 +193,7 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld)
|
|||||||
|
|
||||||
dc__format = lilv_new_uri(pWorld, "http://purl.org/dc/terms/format");
|
dc__format = lilv_new_uri(pWorld, "http://purl.org/dc/terms/format");
|
||||||
|
|
||||||
mod__fileTypes = lilv_new_uri(pWorld,"http://moddevices.com/ns/mod#fileTypes");
|
mod__fileTypes = lilv_new_uri(pWorld, "http://moddevices.com/ns/mod#fileTypes");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PluginHost::LilvUris::Free()
|
void PluginHost::LilvUris::Free()
|
||||||
@@ -477,8 +478,7 @@ void PluginHost::Load(const char *lv2Path)
|
|||||||
const char *pb1 = left->name().c_str();
|
const char *pb1 = left->name().c_str();
|
||||||
const char *pb2 = right->name().c_str();
|
const char *pb2 = right->name().c_str();
|
||||||
return collator->Compare(
|
return collator->Compare(
|
||||||
left->name(),right->name())
|
left->name(), right->name()) < 0;
|
||||||
< 0;
|
|
||||||
};
|
};
|
||||||
std::sort(this->plugins_.begin(), this->plugins_.end(), compare);
|
std::sort(this->plugins_.begin(), this->plugins_.end(), compare);
|
||||||
|
|
||||||
@@ -624,15 +624,47 @@ std::shared_ptr<PiPedalUI> Lv2PluginInfo::FindWritablePathProperties(PluginHost
|
|||||||
strLabel, propertyUri.AsUri(), lv2DirectoryName);
|
strLabel, propertyUri.AsUri(), lv2DirectoryName);
|
||||||
|
|
||||||
AutoLilvNodes mod__fileTypes = lilv_world_find_nodes(pWorld, propertyUri, lv2Host->lilvUris->mod__fileTypes, nullptr);
|
AutoLilvNodes mod__fileTypes = lilv_world_find_nodes(pWorld, propertyUri, lv2Host->lilvUris->mod__fileTypes, nullptr);
|
||||||
LILV_FOREACH(nodes,i,mod__fileTypes)
|
LILV_FOREACH(nodes, i, mod__fileTypes)
|
||||||
{
|
{
|
||||||
// "nam,nammodel"
|
// "nam,nammodel"
|
||||||
AutoLilvNode lilvfileType {lilv_nodes_get(mod__fileTypes, i)};
|
AutoLilvNode lilvfileType{lilv_nodes_get(mod__fileTypes, i)};
|
||||||
std::string fileTypes = lilvfileType.AsString();
|
std::string fileTypes = lilvfileType.AsString();
|
||||||
|
ModFileTypes modFileTypes(fileTypes);
|
||||||
|
|
||||||
for (std::string&type: split(fileTypes,','))
|
for (const std::string &rootDirectory : modFileTypes.rootDirectories())
|
||||||
{
|
{
|
||||||
fileProperty->fileTypes().push_back(UiFileType(SS(type << " file"),SS('.' << type)));
|
fileProperty->modDirectories().push_back(rootDirectory);
|
||||||
|
}
|
||||||
|
for (const std::string &type : modFileTypes.fileTypes())
|
||||||
|
{
|
||||||
|
fileProperty->fileTypes().push_back(UiFileType(SS(type << " file"), SS('.' << type)));
|
||||||
|
}
|
||||||
|
// Legacy case: audio_uploads/<plugin_directory> exists.
|
||||||
|
|
||||||
|
std::filesystem::path bundleDirectoryName = std::filesystem::path(bundle_path()).parent_path().filename();
|
||||||
|
std::filesystem::path legacyUploadPath = lv2Host->MapPath(bundleDirectoryName.string());
|
||||||
|
|
||||||
|
if (std::filesystem::exists(legacyUploadPath))
|
||||||
|
{
|
||||||
|
if (!std::filesystem::exists(legacyUploadPath / ".migrated"))
|
||||||
|
{
|
||||||
|
fileProperty->useLegacyModDirectory(true);
|
||||||
|
fileProperty->directory(bundleDirectoryName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fileProperty->modDirectories().size() == 0)
|
||||||
|
{
|
||||||
|
fileProperty->directory(bundleDirectoryName);
|
||||||
|
}
|
||||||
|
else if (fileProperty->modDirectories().size() == 1 && !fileProperty->useLegacyModDirectory()) // no synthetic root.
|
||||||
|
{
|
||||||
|
auto modType = ModFileTypes::GetModDirectory(fileProperty->modDirectories()[0]);
|
||||||
|
if (modType)
|
||||||
|
{
|
||||||
|
fileProperty->directory(modType->pipedalPath);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// handled at request time.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!mod__fileTypes)
|
if (!mod__fileTypes)
|
||||||
@@ -932,6 +964,7 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP
|
|||||||
this->toggled_property_ = lilv_port_has_property(plugin, pPort, host->lilvUris->core__toggled);
|
this->toggled_property_ = lilv_port_has_property(plugin, pPort, host->lilvUris->core__toggled);
|
||||||
this->not_on_gui_ = lilv_port_has_property(plugin, pPort, host->lilvUris->portprops__not_on_gui_property_uri);
|
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->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);
|
||||||
|
|
||||||
LilvScalePoints *pScalePoints = lilv_port_get_scale_points(plugin, pPort);
|
LilvScalePoints *pScalePoints = lilv_port_get_scale_points(plugin, pPort);
|
||||||
LILV_FOREACH(scale_points, iSP, pScalePoints)
|
LILV_FOREACH(scale_points, iSP, pScalePoints)
|
||||||
@@ -1387,33 +1420,36 @@ std::shared_ptr<HostWorkerThread> PluginHost::GetHostWorkerThread()
|
|||||||
return pHostWorkerThread;
|
return pHostWorkerThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ResourceInfo {
|
class ResourceInfo
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
ResourceInfo(const std::string&filePropertyDirectory,const std::string&resourceDirectory)
|
ResourceInfo(const std::string &filePropertyDirectory, const std::string &resourceDirectory)
|
||||||
:filePropertyDirectory(filePropertyDirectory), resourceDirectory(resourceDirectory) {}
|
: filePropertyDirectory(filePropertyDirectory), resourceDirectory(resourceDirectory) {}
|
||||||
|
|
||||||
std::string filePropertyDirectory;
|
std::string filePropertyDirectory;
|
||||||
std::string resourceDirectory;
|
std::string resourceDirectory;
|
||||||
bool operator==(const ResourceInfo&other) const {
|
bool operator==(const ResourceInfo &other) const
|
||||||
|
{
|
||||||
return this->filePropertyDirectory == other.filePropertyDirectory && this->resourceDirectory == other.resourceDirectory;
|
return this->filePropertyDirectory == other.filePropertyDirectory && this->resourceDirectory == other.resourceDirectory;
|
||||||
|
|
||||||
}
|
}
|
||||||
bool operator<(const ResourceInfo&other) const {
|
bool operator<(const ResourceInfo &other) const
|
||||||
|
{
|
||||||
if (this->filePropertyDirectory < other.filePropertyDirectory)
|
if (this->filePropertyDirectory < other.filePropertyDirectory)
|
||||||
return true;
|
return true;
|
||||||
if (this->filePropertyDirectory > other.filePropertyDirectory)
|
if (this->filePropertyDirectory > other.filePropertyDirectory)
|
||||||
return false;
|
return false;
|
||||||
return this->resourceDirectory < other.resourceDirectory;
|
return this->resourceDirectory < other.resourceDirectory;
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool anyTargetFilesExist(const std::filesystem::path &sourceDirectory, const std::filesystem::path&targetDirectory)
|
static bool anyTargetFilesExist(const std::filesystem::path &sourceDirectory, const std::filesystem::path &targetDirectory)
|
||||||
{
|
{
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
try {
|
try
|
||||||
if (!fs::exists(targetDirectory)) return false;
|
{
|
||||||
for (auto&directoryEntry : fs::directory_iterator(sourceDirectory))
|
if (!fs::exists(targetDirectory))
|
||||||
|
return false;
|
||||||
|
for (auto &directoryEntry : fs::directory_iterator(sourceDirectory))
|
||||||
{
|
{
|
||||||
fs::path thisPath = directoryEntry.path();
|
fs::path thisPath = directoryEntry.path();
|
||||||
if (directoryEntry.is_directory())
|
if (directoryEntry.is_directory())
|
||||||
@@ -1421,11 +1457,13 @@ static bool anyTargetFilesExist(const std::filesystem::path &sourceDirectory, co
|
|||||||
auto name = thisPath.filename();
|
auto name = thisPath.filename();
|
||||||
fs::path childSource = sourceDirectory / name;
|
fs::path childSource = sourceDirectory / name;
|
||||||
fs::path childTarget = targetDirectory / name;
|
fs::path childTarget = targetDirectory / name;
|
||||||
if (anyTargetFilesExist(childSource,childTarget))
|
if (anyTargetFilesExist(childSource, childTarget))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
fs::path targetPath = targetDirectory / thisPath.filename();
|
fs::path targetPath = targetDirectory / thisPath.filename();
|
||||||
if (fs::exists(targetPath))
|
if (fs::exists(targetPath))
|
||||||
{
|
{
|
||||||
@@ -1433,9 +1471,9 @@ static bool anyTargetFilesExist(const std::filesystem::path &sourceDirectory, co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (const std::exception&) {
|
catch (const std::exception &)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1450,48 +1488,52 @@ static void createTargetLinks(const std::filesystem::path &sourceDirectory, cons
|
|||||||
{
|
{
|
||||||
fs::path childSource = dirEntry.path();
|
fs::path childSource = dirEntry.path();
|
||||||
fs::path childTarget = targetDirectory / childSource.filename();
|
fs::path childTarget = targetDirectory / childSource.filename();
|
||||||
if (dirEntry.is_directory()) {
|
if (dirEntry.is_directory())
|
||||||
createTargetLinks(childSource,childTarget);
|
{
|
||||||
} else {
|
createTargetLinks(childSource, childTarget);
|
||||||
fs::create_symlink(childSource,childTarget);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fs::create_symlink(childSource, childTarget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void PluginHost::CheckForResourceInitialization(const std::string &pluginUri,const std::filesystem::path&pluginUploadDirectory)
|
void PluginHost::CheckForResourceInitialization(const std::string &pluginUri, const std::filesystem::path &pluginUploadDirectory)
|
||||||
{
|
{
|
||||||
|
|
||||||
auto plugin = GetPluginInfo(pluginUri);
|
auto plugin = GetPluginInfo(pluginUri);
|
||||||
if (plugin)
|
if (plugin)
|
||||||
{
|
{
|
||||||
std::filesystem::path bundlePath = plugin->bundle_path();
|
std::filesystem::path bundlePath = plugin->bundle_path();
|
||||||
if (!plugin->piPedalUI())
|
if (!plugin->piPedalUI())
|
||||||
return;
|
return;
|
||||||
const auto& fileProperties = plugin->piPedalUI()->fileProperties();
|
const auto &fileProperties = plugin->piPedalUI()->fileProperties();
|
||||||
if (fileProperties.size() != 0 && !pluginsThatHaveBeenCheckedForResources.contains(pluginUri))
|
if (fileProperties.size() != 0 && !pluginsThatHaveBeenCheckedForResources.contains(pluginUri))
|
||||||
{
|
{
|
||||||
pluginsThatHaveBeenCheckedForResources.insert(pluginUri);
|
pluginsThatHaveBeenCheckedForResources.insert(pluginUri);
|
||||||
|
|
||||||
// eliminate duplicates.
|
// eliminate duplicates.
|
||||||
std::set<ResourceInfo> resourceInfoSet;
|
std::set<ResourceInfo> resourceInfoSet;
|
||||||
for (const auto&fileProperty: fileProperties)
|
for (const auto &fileProperty : fileProperties)
|
||||||
{
|
{
|
||||||
if (!fileProperty->resourceDirectory().empty() && !fileProperty->directory().empty())
|
if (!fileProperty->resourceDirectory().empty() && !fileProperty->directory().empty())
|
||||||
{
|
{
|
||||||
resourceInfoSet.insert(ResourceInfo(fileProperty->directory(),fileProperty->resourceDirectory()));
|
resourceInfoSet.insert(ResourceInfo(fileProperty->directory(), fileProperty->resourceDirectory()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try
|
||||||
for (const ResourceInfo&resourceInfo: resourceInfoSet)
|
|
||||||
{
|
{
|
||||||
std::filesystem::path sourcePath = bundlePath / resourceInfo.resourceDirectory;
|
for (const ResourceInfo &resourceInfo : resourceInfoSet)
|
||||||
std::filesystem::path targetPath = pluginUploadDirectory / resourceInfo.filePropertyDirectory;
|
|
||||||
if (!anyTargetFilesExist(sourcePath,targetPath))
|
|
||||||
{
|
{
|
||||||
createTargetLinks(sourcePath,targetPath);
|
std::filesystem::path sourcePath = bundlePath / resourceInfo.resourceDirectory;
|
||||||
|
std::filesystem::path targetPath = pluginUploadDirectory / resourceInfo.filePropertyDirectory;
|
||||||
|
if (!anyTargetFilesExist(sourcePath, targetPath))
|
||||||
|
{
|
||||||
|
createTargetLinks(sourcePath, targetPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
Lv2Log::error(SS("CheckForResourceInitialization: " << e.what()));
|
Lv2Log::error(SS("CheckForResourceInitialization: " << e.what()));
|
||||||
}
|
}
|
||||||
@@ -1499,20 +1541,17 @@ void PluginHost::CheckForResourceInitialization(const std::string &pluginUri,con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
json_variant PluginHost::MapPath(const json_variant&json)
|
json_variant PluginHost::MapPath(const json_variant &json)
|
||||||
{
|
{
|
||||||
AtomConverter converter(GetMapFeature());
|
AtomConverter converter(GetMapFeature());
|
||||||
return converter.MapPath(json,GetPluginStoragePath());
|
return converter.MapPath(json, GetPluginStoragePath());
|
||||||
|
|
||||||
}
|
}
|
||||||
json_variant PluginHost::AbstractPath(const json_variant&json)
|
json_variant PluginHost::AbstractPath(const json_variant &json)
|
||||||
{
|
{
|
||||||
AtomConverter converter(GetMapFeature());
|
AtomConverter converter(GetMapFeature());
|
||||||
return converter.AbstractPath(json,GetPluginStoragePath());
|
return converter.AbstractPath(json, GetPluginStoragePath());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string PluginHost::MapPath(const std::string &abstractPath)
|
std::string PluginHost::MapPath(const std::string &abstractPath)
|
||||||
{
|
{
|
||||||
auto storagePath = GetPluginStoragePath();
|
auto storagePath = GetPluginStoragePath();
|
||||||
@@ -1521,10 +1560,8 @@ std::string PluginHost::MapPath(const std::string &abstractPath)
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return SS(storagePath << '/' << abstractPath);
|
return SS(storagePath << '/' << abstractPath);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
std::string PluginHost::AbstractPath(const std::string&path)
|
std::string PluginHost::AbstractPath(const std::string &path)
|
||||||
{
|
{
|
||||||
auto storagePath = GetPluginStoragePath();
|
auto storagePath = GetPluginStoragePath();
|
||||||
if (path.starts_with(storagePath))
|
if (path.starts_with(storagePath))
|
||||||
@@ -1593,7 +1630,7 @@ json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{
|
|||||||
MAP_REF(Lv2PortInfo, is_logarithmic),
|
MAP_REF(Lv2PortInfo, is_logarithmic),
|
||||||
MAP_REF(Lv2PortInfo, display_priority),
|
MAP_REF(Lv2PortInfo, display_priority),
|
||||||
MAP_REF(Lv2PortInfo, range_steps),
|
MAP_REF(Lv2PortInfo, range_steps),
|
||||||
MAP_REF(Lv2PortInfo, trigger),
|
MAP_REF(Lv2PortInfo, trigger_property),
|
||||||
MAP_REF(Lv2PortInfo, integer_property),
|
MAP_REF(Lv2PortInfo, integer_property),
|
||||||
MAP_REF(Lv2PortInfo, enumeration_property),
|
MAP_REF(Lv2PortInfo, enumeration_property),
|
||||||
MAP_REF(Lv2PortInfo, toggled_property),
|
MAP_REF(Lv2PortInfo, toggled_property),
|
||||||
@@ -1662,6 +1699,7 @@ json_map::storage_type<Lv2PluginUiPort> Lv2PluginUiPort::jmap{{
|
|||||||
MAP_REF(Lv2PluginUiPort, enumeration_property),
|
MAP_REF(Lv2PluginUiPort, enumeration_property),
|
||||||
MAP_REF(Lv2PluginUiPort, not_on_gui),
|
MAP_REF(Lv2PluginUiPort, not_on_gui),
|
||||||
MAP_REF(Lv2PluginUiPort, toggled_property),
|
MAP_REF(Lv2PluginUiPort, toggled_property),
|
||||||
|
MAP_REF(Lv2PluginUiPort, trigger_property),
|
||||||
MAP_REF(Lv2PluginUiPort, scale_points),
|
MAP_REF(Lv2PluginUiPort, scale_points),
|
||||||
MAP_REF(Lv2PluginUiPort, port_group),
|
MAP_REF(Lv2PluginUiPort, port_group),
|
||||||
|
|
||||||
|
|||||||
+6
-2
@@ -214,7 +214,7 @@ namespace pipedal
|
|||||||
bool is_logarithmic_ = false;
|
bool is_logarithmic_ = false;
|
||||||
int display_priority_ = -1;
|
int display_priority_ = -1;
|
||||||
int range_steps_ = 0;
|
int range_steps_ = 0;
|
||||||
bool trigger_ = false;
|
bool trigger_property_ = false;
|
||||||
bool integer_property_ = false;
|
bool integer_property_ = false;
|
||||||
bool enumeration_property_ = false;
|
bool enumeration_property_ = false;
|
||||||
bool toggled_property_ = false;
|
bool toggled_property_ = false;
|
||||||
@@ -285,7 +285,7 @@ namespace pipedal
|
|||||||
LV2_PROPERTY_GETSET_SCALAR(is_logarithmic);
|
LV2_PROPERTY_GETSET_SCALAR(is_logarithmic);
|
||||||
LV2_PROPERTY_GETSET_SCALAR(display_priority);
|
LV2_PROPERTY_GETSET_SCALAR(display_priority);
|
||||||
LV2_PROPERTY_GETSET_SCALAR(range_steps);
|
LV2_PROPERTY_GETSET_SCALAR(range_steps);
|
||||||
LV2_PROPERTY_GETSET_SCALAR(trigger);
|
LV2_PROPERTY_GETSET_SCALAR(trigger_property);
|
||||||
LV2_PROPERTY_GETSET_SCALAR(integer_property);
|
LV2_PROPERTY_GETSET_SCALAR(integer_property);
|
||||||
LV2_PROPERTY_GETSET_SCALAR(enumeration_property);
|
LV2_PROPERTY_GETSET_SCALAR(enumeration_property);
|
||||||
LV2_PROPERTY_GETSET_SCALAR(toggled_property);
|
LV2_PROPERTY_GETSET_SCALAR(toggled_property);
|
||||||
@@ -496,6 +496,7 @@ namespace pipedal
|
|||||||
default_value_(pPort->default_value()), range_steps_(pPort->range_steps()), display_priority_(pPort->display_priority()),
|
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()), enumeration_property_(pPort->enumeration_property()),
|
is_logarithmic_(pPort->is_logarithmic()), integer_property_(pPort->integer_property()), enumeration_property_(pPort->enumeration_property()),
|
||||||
toggled_property_(pPort->toggled_property()), not_on_gui_(pPort->not_on_gui()), scale_points_(pPort->scale_points()),
|
toggled_property_(pPort->toggled_property()), not_on_gui_(pPort->not_on_gui()), scale_points_(pPort->scale_points()),
|
||||||
|
trigger_property_(pPort->trigger_property()),
|
||||||
comment_(pPort->comment()), units_(pPort->units()),
|
comment_(pPort->comment()), units_(pPort->units()),
|
||||||
connection_optional_(pPort->connection_optional())
|
connection_optional_(pPort->connection_optional())
|
||||||
{
|
{
|
||||||
@@ -536,6 +537,7 @@ namespace pipedal
|
|||||||
bool enumeration_property_ = false;
|
bool enumeration_property_ = false;
|
||||||
bool not_on_gui_ = false;
|
bool not_on_gui_ = false;
|
||||||
bool toggled_property_ = false;
|
bool toggled_property_ = false;
|
||||||
|
bool trigger_property_ = false;
|
||||||
std::vector<Lv2ScalePoint> scale_points_;
|
std::vector<Lv2ScalePoint> scale_points_;
|
||||||
std::string port_group_;
|
std::string port_group_;
|
||||||
|
|
||||||
@@ -561,6 +563,7 @@ namespace pipedal
|
|||||||
LV2_PROPERTY_GETSET_SCALAR(integer_property);
|
LV2_PROPERTY_GETSET_SCALAR(integer_property);
|
||||||
LV2_PROPERTY_GETSET_SCALAR(enumeration_property);
|
LV2_PROPERTY_GETSET_SCALAR(enumeration_property);
|
||||||
LV2_PROPERTY_GETSET_SCALAR(toggled_property);
|
LV2_PROPERTY_GETSET_SCALAR(toggled_property);
|
||||||
|
LV2_PROPERTY_GETSET_SCALAR(trigger_property);
|
||||||
LV2_PROPERTY_GETSET_SCALAR(not_on_gui);
|
LV2_PROPERTY_GETSET_SCALAR(not_on_gui);
|
||||||
LV2_PROPERTY_GETSET(scale_points);
|
LV2_PROPERTY_GETSET(scale_points);
|
||||||
LV2_PROPERTY_GETSET(units);
|
LV2_PROPERTY_GETSET(units);
|
||||||
@@ -664,6 +667,7 @@ namespace pipedal
|
|||||||
AutoLilvNode core__toggled;
|
AutoLilvNode core__toggled;
|
||||||
AutoLilvNode core__connectionOptional;
|
AutoLilvNode core__connectionOptional;
|
||||||
AutoLilvNode portprops__not_on_gui_property_uri;
|
AutoLilvNode portprops__not_on_gui_property_uri;
|
||||||
|
AutoLilvNode portprops__trigger;
|
||||||
AutoLilvNode midi__event;
|
AutoLilvNode midi__event;
|
||||||
AutoLilvNode core__designation;
|
AutoLilvNode core__designation;
|
||||||
AutoLilvNode portgroups__group;
|
AutoLilvNode portgroups__group;
|
||||||
|
|||||||
+276
-69
@@ -22,6 +22,7 @@
|
|||||||
#include "Storage.hpp"
|
#include "Storage.hpp"
|
||||||
#include "AudioConfig.hpp"
|
#include "AudioConfig.hpp"
|
||||||
#include "PiPedalException.hpp"
|
#include "PiPedalException.hpp"
|
||||||
|
#include <stdexcept>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -32,14 +33,50 @@
|
|||||||
#include "PluginHost.hpp"
|
#include "PluginHost.hpp"
|
||||||
#include "ss.hpp"
|
#include "ss.hpp"
|
||||||
#include "ofstream_synced.hpp"
|
#include "ofstream_synced.hpp"
|
||||||
|
#include "ModFileTypes.hpp"
|
||||||
|
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
const char *BANK_EXTENSION = ".bank";
|
const char *BANK_EXTENSION = ".bank";
|
||||||
const char *BANKS_FILENAME = "index.banks";
|
const char *BANKS_FILENAME = "index.banks";
|
||||||
|
|
||||||
#define USER_SETTINGS_FILENAME "userSettings.json";
|
#define USER_SETTINGS_FILENAME "userSettings.json";
|
||||||
|
|
||||||
|
|
||||||
|
static bool isSubdirectory(const fs::path&path, const fs::path&basePath)
|
||||||
|
{
|
||||||
|
auto iPath = path.begin();
|
||||||
|
for (auto i = basePath.begin(); i != basePath.end(); ++i)
|
||||||
|
{
|
||||||
|
if (iPath == path.end())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((*i) != (*iPath))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++iPath;
|
||||||
|
}
|
||||||
|
while (iPath != path.end())
|
||||||
|
{
|
||||||
|
if (iPath->string() == "..")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++iPath;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hasSyntheticModRoot(const UiFileProperty &fileProperty)
|
||||||
|
{
|
||||||
|
return (fileProperty.modDirectories().size() > 1 || (fileProperty.modDirectories().size() == 1 && fileProperty.useLegacyModDirectory()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Storage::Storage()
|
Storage::Storage()
|
||||||
{
|
{
|
||||||
SetConfigRoot("~/var/Config");
|
SetConfigRoot("~/var/Config");
|
||||||
@@ -1648,63 +1685,190 @@ static bool ensureNoDotDot(const std::filesystem::path&path)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void AddFilesToResult(
|
||||||
std::vector<FileEntry> Storage::GetFileList2(const std::string &relativePath,const UiFileProperty &fileProperty)
|
FileRequestResult&result,
|
||||||
|
const UiFileProperty &fileProperty,
|
||||||
|
const fs::path&rootPath)
|
||||||
{
|
{
|
||||||
if (!ensureNoDotDot(relativePath))
|
if (!fs::exists(rootPath))
|
||||||
{
|
{
|
||||||
ThrowPermissionDeniedError();
|
return; // silently without error.
|
||||||
}
|
}
|
||||||
if (!UiFileProperty::IsDirectoryNameValid(fileProperty.directory()))
|
auto & resultFiles = result.files_;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
ThrowPermissionDeniedError();
|
for (auto const &dir_entry : std::filesystem::directory_iterator(rootPath))
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<FileEntry> result;
|
|
||||||
|
|
||||||
// if fileProperty has a user-accessible directory, push the entire file path.
|
|
||||||
if (fileProperty.directory().size() != 0)
|
|
||||||
{
|
|
||||||
std::filesystem::path audioFileDirectory = this->GetPluginUploadDirectory() / fileProperty.directory() / relativePath;
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
for (auto const &dir_entry : std::filesystem::directory_iterator(audioFileDirectory))
|
const auto &path = dir_entry.path();
|
||||||
|
auto name = path.filename().string();
|
||||||
|
if (dir_entry.is_regular_file())
|
||||||
{
|
{
|
||||||
if (dir_entry.is_regular_file())
|
if (name.length() > 0 && name[0] != '.') // don't show hidden files.
|
||||||
{
|
{
|
||||||
auto &path = dir_entry.path();
|
if (fileProperty.IsValidExtension(path.extension().string()))
|
||||||
auto name = path.filename().string();
|
|
||||||
if (name.length() > 0 && name[0] != '.') // don't show hidden files.
|
|
||||||
{
|
{
|
||||||
if (fileProperty.IsValidExtension(path.extension().string()))
|
resultFiles.push_back(
|
||||||
{
|
FileEntry(path,name,false,false)
|
||||||
// a relative path!
|
);
|
||||||
result.push_back(FileEntry(path,false));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (dir_entry.is_directory()) {
|
|
||||||
result.push_back(FileEntry(dir_entry.path(),true));
|
|
||||||
}
|
}
|
||||||
|
} else if (dir_entry.is_directory()) {
|
||||||
|
resultFiles.push_back(FileEntry{path,name,true,fs::is_symlink(path)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception &error)
|
}
|
||||||
{
|
catch (const std::exception &error)
|
||||||
throw std::logic_error("GetFileList failed. Directory not found: " + audioFileDirectory.string());
|
{
|
||||||
}
|
throw std::logic_error("GetFileList failed. Directory not found: " + rootPath.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort lexicographically
|
// sort lexicographically
|
||||||
|
|
||||||
auto collator = Locale::GetInstance()->GetCollator();
|
auto collator = Locale::GetInstance()->GetCollator();
|
||||||
|
|
||||||
std::sort(result.begin(), result.end(),[&collator](const FileEntry&l, const FileEntry&r) {
|
std::sort(resultFiles.begin(), resultFiles.end(),[&collator](const FileEntry&l, const FileEntry&r) {
|
||||||
if (l.isDirectory_ != r.isDirectory_)
|
if (l.isDirectory_ != r.isDirectory_)
|
||||||
{
|
{
|
||||||
return l.isDirectory_ > r.isDirectory_;
|
return l.isDirectory_ > r.isDirectory_;
|
||||||
}
|
}
|
||||||
return collator->Compare(l.filename_,r.filename_) < 0;
|
return collator->Compare(l.displayName_,r.displayName_) < 0;
|
||||||
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
FileRequestResult Storage::GetModFileList2(const std::string &relativePath,const UiFileProperty &fileProperty)
|
||||||
|
{
|
||||||
|
FileRequestResult result;
|
||||||
|
fs::path uploadsDirectory = GetPluginUploadDirectory();
|
||||||
|
|
||||||
|
if (relativePath.empty())
|
||||||
|
{
|
||||||
|
// return the synthetic root.
|
||||||
|
result.isProtected_ = true;
|
||||||
|
|
||||||
|
for (const auto&modDirectory: fileProperty.modDirectories())
|
||||||
|
{
|
||||||
|
const auto directoryInfo = ModFileTypes::GetModDirectory(modDirectory);
|
||||||
|
if (directoryInfo)
|
||||||
|
{
|
||||||
|
result.files_.push_back(
|
||||||
|
FileEntry(uploadsDirectory / directoryInfo->pipedalPath,directoryInfo->displayName,true,true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (fileProperty.useLegacyModDirectory())
|
||||||
|
{
|
||||||
|
result.files_.push_back(
|
||||||
|
FileEntry(uploadsDirectory / fileProperty.directory(),fs::path(fileProperty.directory()).filename().string(),true,true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
result.breadcrumbs_.push_back({"","Home"});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
fs::path modDirectoryPath;
|
||||||
|
|
||||||
|
result.breadcrumbs_.push_back({"","Home"});
|
||||||
|
fs::path fsRelativePath { relativePath};
|
||||||
|
|
||||||
|
for (const auto &modDirectory: fileProperty.modDirectories())
|
||||||
|
{
|
||||||
|
const ModFileTypes::ModDirectory*modDirectoryInfo = ModFileTypes::GetModDirectory(modDirectory);
|
||||||
|
if (modDirectoryInfo)
|
||||||
|
{
|
||||||
|
if (isSubdirectory(fsRelativePath, uploadsDirectory / modDirectoryInfo->pipedalPath))
|
||||||
|
{
|
||||||
|
modDirectoryPath = uploadsDirectory / modDirectoryInfo->pipedalPath;
|
||||||
|
result.breadcrumbs_.push_back({modDirectoryPath.string(),modDirectoryInfo->displayName});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (modDirectoryPath.empty() && fileProperty.useLegacyModDirectory())
|
||||||
|
{
|
||||||
|
if (isSubdirectory(fsRelativePath, uploadsDirectory / fileProperty.directory()))
|
||||||
|
{
|
||||||
|
modDirectoryPath = uploadsDirectory / fileProperty.directory();
|
||||||
|
result.breadcrumbs_.push_back({modDirectoryPath.string(),fs::path(fileProperty.directory()).filename().string()});
|
||||||
|
} else {
|
||||||
|
ThrowPermissionDeniedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add remaing path segements as breadcrumbs.
|
||||||
|
{
|
||||||
|
fs::path modPath {modDirectoryPath};
|
||||||
|
fs::path rp { relativePath};
|
||||||
|
auto iRp = rp.begin();
|
||||||
|
// skip past one or more segments in the modDiretoryPath.
|
||||||
|
for (auto iModPath = modPath.begin(); iModPath != modPath.end(); ++iModPath)
|
||||||
|
{
|
||||||
|
if (iRp != rp.end()) {
|
||||||
|
++iRp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (iRp != rp.end())
|
||||||
|
{
|
||||||
|
result.breadcrumbs_.push_back({*iRp,*iRp});
|
||||||
|
++iRp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddFilesToResult(result,fileProperty,relativePath);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FileRequestResult Storage::GetFileList2(const std::string &relativePath_,const UiFileProperty &fileProperty)
|
||||||
|
{
|
||||||
|
std::string absolutePath = relativePath_;
|
||||||
|
if (!ensureNoDotDot(absolutePath))
|
||||||
|
{
|
||||||
|
ThrowPermissionDeniedError();
|
||||||
|
}
|
||||||
|
if (hasSyntheticModRoot(fileProperty))
|
||||||
|
{
|
||||||
|
return Storage::GetModFileList2(absolutePath,fileProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileRequestResult result;
|
||||||
|
|
||||||
|
if (fileProperty.directory().empty())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("fileProperty.directory() not specified.");
|
||||||
|
}
|
||||||
|
std::filesystem::path pluginRootDirectory = this->GetPluginUploadDirectory() / fileProperty.directory();
|
||||||
|
if (absolutePath == "")
|
||||||
|
{
|
||||||
|
absolutePath = pluginRootDirectory.string();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
result.breadcrumbs_.push_back({"","Home"});
|
||||||
|
fs::path fsAbsolutePath {absolutePath};
|
||||||
|
auto iAbsolutePath = fsAbsolutePath.begin();
|
||||||
|
for (auto i = pluginRootDirectory.begin(); i != pluginRootDirectory.end(); ++i)
|
||||||
|
{
|
||||||
|
if (iAbsolutePath == fsAbsolutePath.end() || (*i) != *iAbsolutePath)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Directory is not a subdirectory of the plugin root directory.");
|
||||||
|
}
|
||||||
|
++iAbsolutePath;
|
||||||
|
}
|
||||||
|
auto cumulativePath = pluginRootDirectory;
|
||||||
|
|
||||||
|
while (iAbsolutePath != fsAbsolutePath.end())
|
||||||
|
{
|
||||||
|
cumulativePath /= (*iAbsolutePath);
|
||||||
|
result.breadcrumbs_.push_back({cumulativePath.string(),iAbsolutePath->string()});
|
||||||
|
++iAbsolutePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isSubdirectory(absolutePath,pluginRootDirectory))
|
||||||
|
{
|
||||||
|
throw std::runtime_error(SS("Improper location. " << absolutePath));
|
||||||
|
}
|
||||||
|
AddFilesToResult(result,fileProperty,absolutePath);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1715,29 +1879,32 @@ bool Storage::IsValidSampleFileName(const std::filesystem::path &fileName)
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ensureNoDotDot(fileName))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::filesystem::path audioFilePath = this->GetPluginUploadDirectory();
|
std::filesystem::path audioFilePath = this->GetPluginUploadDirectory();
|
||||||
|
|
||||||
std::filesystem::path parentDirectory = fileName.parent_path();
|
std::filesystem::path parentDirectory = fileName.parent_path();
|
||||||
while (true)
|
|
||||||
|
auto iTarget = parentDirectory.begin();
|
||||||
|
|
||||||
|
for (auto i = audioFilePath.begin(); i != audioFilePath.end(); ++i)
|
||||||
{
|
{
|
||||||
if (!parentDirectory.has_parent_path())
|
if (iTarget == parentDirectory.end()) {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::string name = parentDirectory.filename().string();
|
if (*i != *iTarget) {
|
||||||
if (parentDirectory == audioFilePath)
|
|
||||||
return true;
|
|
||||||
parentDirectory = parentDirectory.parent_path();
|
|
||||||
if (parentDirectory.string().length() < audioFilePath.string().length())
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
++iTarget;
|
||||||
}
|
}
|
||||||
|
while (iTarget != parentDirectory.end())
|
||||||
|
{
|
||||||
|
if (iTarget->string() == "..")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++iTarget;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
void Storage::DeleteSampleFile(const std::filesystem::path &fileName)
|
void Storage::DeleteSampleFile(const std::filesystem::path &fileName)
|
||||||
{
|
{
|
||||||
@@ -1753,9 +1920,18 @@ void Storage::DeleteSampleFile(const std::filesystem::path &fileName)
|
|||||||
{
|
{
|
||||||
if (std::filesystem::is_directory(fileName))
|
if (std::filesystem::is_directory(fileName))
|
||||||
{
|
{
|
||||||
if (fileName.string().length() > 1) // guard against rm -rf / (bitter experience)
|
if (fileName.string().length() <= 1) // guard against rm -rf / (bitter experience)
|
||||||
{
|
{
|
||||||
std::filesystem::remove_all(fileName);
|
throw std::logic_error("Invalid filename.");
|
||||||
|
}
|
||||||
|
if (std::filesystem::is_symlink(fileName))
|
||||||
|
{
|
||||||
|
std::filesystem::remove(fileName);
|
||||||
|
} else {
|
||||||
|
if (fileName.string().length() > 1) // guard against rm -rf / (bitter experience)
|
||||||
|
{
|
||||||
|
std::filesystem::remove_all(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -1769,13 +1945,13 @@ void Storage::DeleteSampleFile(const std::filesystem::path &fileName)
|
|||||||
}
|
}
|
||||||
std::filesystem::path Storage::MakeUserFilePath(const std::string &directory, const std::string &filename)
|
std::filesystem::path Storage::MakeUserFilePath(const std::string &directory, const std::string &filename)
|
||||||
{
|
{
|
||||||
if (!ensureNoDotDot(directory))
|
|
||||||
{
|
|
||||||
throw std::logic_error(SS("Invalide filename: " << filename));
|
|
||||||
}
|
|
||||||
std::filesystem::path filePath{filename};
|
std::filesystem::path filePath{filename};
|
||||||
|
|
||||||
std::filesystem::path result = this->GetPluginUploadDirectory() / directory / filename;
|
std::filesystem::path result = fs::path(directory) / filename;
|
||||||
|
if (!result.is_absolute())
|
||||||
|
{
|
||||||
|
result = this->GetPluginUploadDirectory() / result;
|
||||||
|
}
|
||||||
if (!this->IsValidSampleFileName(result))
|
if (!this->IsValidSampleFileName(result))
|
||||||
{
|
{
|
||||||
throw std::logic_error(SS("Invalid upload path: " << result));
|
throw std::logic_error(SS("Invalid upload path: " << result));
|
||||||
@@ -1898,8 +2074,8 @@ void Storage::FillSampleDirectoryTree(FilePropertyDirectoryTree*node, const std:
|
|||||||
{
|
{
|
||||||
if (child.is_directory())
|
if (child.is_directory())
|
||||||
{
|
{
|
||||||
const auto& childPath = child.path();
|
const auto& childPath = child.path();
|
||||||
FilePropertyDirectoryTree::ptr childTree = std::make_unique<FilePropertyDirectoryTree>(childPath.filename());
|
FilePropertyDirectoryTree::ptr childTree = std::make_unique<FilePropertyDirectoryTree>(childPath);
|
||||||
FillSampleDirectoryTree(childTree.get(),childPath);
|
FillSampleDirectoryTree(childTree.get(),childPath);
|
||||||
node->children_.push_back(std::move(childTree));
|
node->children_.push_back(std::move(childTree));
|
||||||
}
|
}
|
||||||
@@ -1911,22 +2087,53 @@ void Storage::FillSampleDirectoryTree(FilePropertyDirectoryTree*node, const std:
|
|||||||
return collator->Compare(left->directoryName_,right->directoryName_) < 0;
|
return collator->Compare(left->directoryName_,right->directoryName_) < 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
FilePropertyDirectoryTree::ptr Storage::GetFilePropertydirectoryTree(const UiFileProperty&uiFileProperty) const
|
FilePropertyDirectoryTree::ptr Storage::GetFilePropertydirectoryTree(const UiFileProperty&uiFileProperty)
|
||||||
{
|
{
|
||||||
FilePropertyDirectoryTree::ptr result = std::make_unique<FilePropertyDirectoryTree>("");
|
fs::path uploadDirectory = this->GetPluginUploadDirectory();
|
||||||
if (uiFileProperty.directory().empty())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Invalid uiFileProperty");
|
|
||||||
}
|
|
||||||
if (!ensureNoDotDot(uiFileProperty.directory()))
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Invalid uiFileProperty");
|
|
||||||
}
|
|
||||||
std::filesystem::path rootDirectory = this->GetPluginUploadDirectory() / uiFileProperty.directory();
|
|
||||||
|
|
||||||
FillSampleDirectoryTree(result.get(),rootDirectory);
|
if (hasSyntheticModRoot(uiFileProperty))
|
||||||
|
{
|
||||||
|
FilePropertyDirectoryTree::ptr result = std::make_unique<FilePropertyDirectoryTree>("","Home");
|
||||||
|
result->isProtected_ = true;
|
||||||
|
for (const auto& modDirectory: uiFileProperty.modDirectories())
|
||||||
|
{
|
||||||
|
auto modDirectoryInfo = ModFileTypes::GetModDirectory(modDirectory);
|
||||||
|
if (modDirectoryInfo)
|
||||||
|
{
|
||||||
|
auto childPath = uploadDirectory / modDirectoryInfo->pipedalPath;
|
||||||
|
FilePropertyDirectoryTree::ptr child = std::make_unique<FilePropertyDirectoryTree>(
|
||||||
|
childPath,modDirectoryInfo->displayName
|
||||||
|
);
|
||||||
|
FillSampleDirectoryTree(child.get(),childPath);
|
||||||
|
result->children_.push_back(std::move(child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (uiFileProperty.useLegacyModDirectory()) {
|
||||||
|
auto childPath = uploadDirectory / uiFileProperty.directory();
|
||||||
|
FilePropertyDirectoryTree::ptr child = std::make_unique<FilePropertyDirectoryTree>(
|
||||||
|
childPath
|
||||||
|
);
|
||||||
|
FillSampleDirectoryTree(child.get(),childPath);
|
||||||
|
result->children_.push_back(std::move(child));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (uiFileProperty.directory().empty())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Invalid uiFileProperty");
|
||||||
|
}
|
||||||
|
if (!ensureNoDotDot(uiFileProperty.directory()))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Invalid uiFileProperty");
|
||||||
|
}
|
||||||
|
std::filesystem::path rootDirectory = uploadDirectory / uiFileProperty.directory();
|
||||||
|
FilePropertyDirectoryTree::ptr result = std::make_unique<FilePropertyDirectoryTree>(rootDirectory.string(),"Home");
|
||||||
|
|
||||||
|
FillSampleDirectoryTree(result.get(),rootDirectory);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -152,7 +152,9 @@ public:
|
|||||||
int64_t DeleteBank(int64_t bankId);
|
int64_t DeleteBank(int64_t bankId);
|
||||||
|
|
||||||
std::vector<std::string> GetFileList(const UiFileProperty&fileProperty);
|
std::vector<std::string> GetFileList(const UiFileProperty&fileProperty);
|
||||||
std::vector<FileEntry> GetFileList2(const std::string&relativePath,const UiFileProperty&fileProperty);
|
FileRequestResult GetFileList2(const std::string&relativePath,const UiFileProperty&fileProperty);
|
||||||
|
|
||||||
|
FileRequestResult GetModFileList2(const std::string &relativePath,const UiFileProperty &fileProperty);
|
||||||
|
|
||||||
|
|
||||||
void SetJackChannelSelection(const JackChannelSelection&channelSelection);
|
void SetJackChannelSelection(const JackChannelSelection&channelSelection);
|
||||||
@@ -224,7 +226,7 @@ public:
|
|||||||
const std::string&oldRelativePath,
|
const std::string&oldRelativePath,
|
||||||
const std::string&newRelativePath,
|
const std::string&newRelativePath,
|
||||||
const UiFileProperty&uiFileProperty);
|
const UiFileProperty&uiFileProperty);
|
||||||
FilePropertyDirectoryTree::ptr GetFilePropertydirectoryTree(const UiFileProperty&uiFileProperty) const;
|
FilePropertyDirectoryTree::ptr GetFilePropertydirectoryTree(const UiFileProperty&uiFileProperty);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+152
-119
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2024 Robin Davies
|
// Copyright (c) 2024 Robin Davies
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
// 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
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
#include "UpdaterSecurity.hpp"
|
#include "UpdaterSecurity.hpp"
|
||||||
#include "TemporaryFile.hpp"
|
#include "TemporaryFile.hpp"
|
||||||
#include "PresetBundle.hpp"
|
#include "PresetBundle.hpp"
|
||||||
|
#include "json.hpp"
|
||||||
|
|
||||||
#define OLD_PRESET_EXTENSION ".piPreset"
|
#define OLD_PRESET_EXTENSION ".piPreset"
|
||||||
#define PRESET_EXTENSION ".piPreset"
|
#define PRESET_EXTENSION ".piPreset"
|
||||||
@@ -50,40 +50,52 @@ using namespace pipedal;
|
|||||||
using namespace boost::system;
|
using namespace boost::system;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
class UserUploadResponse
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string errorMessage_;
|
||||||
|
std::string path_;
|
||||||
|
DECLARE_JSON_MAP(UserUploadResponse);
|
||||||
|
};
|
||||||
|
JSON_MAP_BEGIN(UserUploadResponse)
|
||||||
|
JSON_MAP_REFERENCE(UserUploadResponse, errorMessage)
|
||||||
|
JSON_MAP_REFERENCE(UserUploadResponse, path)
|
||||||
|
JSON_MAP_END()
|
||||||
|
|
||||||
static bool IsZipFile(const std::filesystem::path &path)
|
static bool IsZipFile(const std::filesystem::path &path)
|
||||||
{
|
{
|
||||||
std::ifstream f(path);
|
std::ifstream f(path);
|
||||||
if (!f.is_open()) return false;
|
if (!f.is_open())
|
||||||
|
return false;
|
||||||
|
|
||||||
char c[4];
|
char c[4];
|
||||||
memset(c,0,sizeof(c));
|
memset(c, 0, sizeof(c));
|
||||||
|
|
||||||
f >> c[0] >> c[1] >> c[2] >> c[3];
|
f >> c[0] >> c[1] >> c[2] >> c[3];
|
||||||
|
|
||||||
// official file header according to PKware documetnation.
|
// official file header according to PKware documetnation.
|
||||||
return c[0] == 0x50 && c[1] == 0x4B && c[2] == 0x03 && c[3] == 0x04;
|
return c[0] == 0x50 && c[1] == 0x4B && c[2] == 0x03 && c[3] == 0x04;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExtensionChecker {
|
class ExtensionChecker
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
ExtensionChecker(const std::string&extensionList)
|
ExtensionChecker(const std::string &extensionList)
|
||||||
:extensions(split(extensionList,','))
|
: extensions(split(extensionList, ','))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
bool IsValidExtension(const std::string&extension)
|
bool IsValidExtension(const std::string &extension)
|
||||||
{
|
{
|
||||||
if (extensions.size() == 0)
|
if (extensions.size() == 0)
|
||||||
return true;
|
return true;
|
||||||
for (const auto&ext: extensions)
|
for (const auto &ext : extensions)
|
||||||
{
|
{
|
||||||
if (ext == extension)
|
if (ext == extension)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::string> extensions;
|
std::vector<std::string> extensions;
|
||||||
};
|
};
|
||||||
@@ -133,7 +145,8 @@ public:
|
|||||||
else if (segment == "uploadBank")
|
else if (segment == "uploadBank")
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
} else if (segment == "uploadUserFile")
|
}
|
||||||
|
else if (segment == "uploadUserFile")
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -159,7 +172,7 @@ public:
|
|||||||
PluginPresets pluginPresets = model->GetPluginPresets(pluginUri);
|
PluginPresets pluginPresets = model->GetPluginPresets(pluginUri);
|
||||||
|
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
json_writer writer(s,false);
|
json_writer writer(s, false);
|
||||||
writer.write(pluginPresets);
|
writer.write(pluginPresets);
|
||||||
*pContent = s.str();
|
*pContent = s.str();
|
||||||
}
|
}
|
||||||
@@ -176,7 +189,7 @@ public:
|
|||||||
file.selectedPreset(newInstanceId);
|
file.selectedPreset(newInstanceId);
|
||||||
|
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
json_writer writer(s,false);
|
json_writer writer(s, false);
|
||||||
writer.write(file);
|
writer.write(file);
|
||||||
*pContent = s.str();
|
*pContent = s.str();
|
||||||
*pName = pedalboard.name();
|
*pName = pedalboard.name();
|
||||||
@@ -210,12 +223,11 @@ public:
|
|||||||
std::string content;
|
std::string content;
|
||||||
GetPluginPresets(request_uri, &name, &content);
|
GetPluginPresets(request_uri, &name, &content);
|
||||||
|
|
||||||
TemporaryFile tmpFile { WEB_TEMP_DIR};
|
TemporaryFile tmpFile{WEB_TEMP_DIR};
|
||||||
PresetBundleWriter::ptr presetbundleWriter = PresetBundleWriter::CreatePluginPresetsFile(*(this->model),content);
|
PresetBundleWriter::ptr presetbundleWriter = PresetBundleWriter::CreatePluginPresetsFile(*(this->model), content);
|
||||||
presetbundleWriter->WriteToFile(tmpFile.Path());
|
presetbundleWriter->WriteToFile(tmpFile.Path());
|
||||||
size_t contentLength = std::filesystem::file_size(tmpFile.Path());
|
size_t contentLength = std::filesystem::file_size(tmpFile.Path());
|
||||||
|
|
||||||
|
|
||||||
res.set(HttpField::content_type, PLUGIN_PRESETS_MIME_TYPE);
|
res.set(HttpField::content_type, PLUGIN_PRESETS_MIME_TYPE);
|
||||||
res.set(HttpField::cache_control, "no-cache");
|
res.set(HttpField::cache_control, "no-cache");
|
||||||
res.setContentLength(content.length());
|
res.setContentLength(content.length());
|
||||||
@@ -228,8 +240,8 @@ public:
|
|||||||
std::string content;
|
std::string content;
|
||||||
GetPreset(request_uri, &name, &content);
|
GetPreset(request_uri, &name, &content);
|
||||||
|
|
||||||
TemporaryFile tmpFile { WEB_TEMP_DIR};
|
TemporaryFile tmpFile{WEB_TEMP_DIR};
|
||||||
PresetBundleWriter::ptr presetbundleWriter = PresetBundleWriter::CreatePresetsFile(*(this->model),content);
|
PresetBundleWriter::ptr presetbundleWriter = PresetBundleWriter::CreatePresetsFile(*(this->model), content);
|
||||||
presetbundleWriter->WriteToFile(tmpFile.Path());
|
presetbundleWriter->WriteToFile(tmpFile.Path());
|
||||||
size_t contentLength = std::filesystem::file_size(tmpFile.Path());
|
size_t contentLength = std::filesystem::file_size(tmpFile.Path());
|
||||||
|
|
||||||
@@ -245,14 +257,11 @@ public:
|
|||||||
std::string content;
|
std::string content;
|
||||||
GetBank(request_uri, &name, &content);
|
GetBank(request_uri, &name, &content);
|
||||||
|
|
||||||
TemporaryFile tmpFile { WEB_TEMP_DIR};
|
TemporaryFile tmpFile{WEB_TEMP_DIR};
|
||||||
PresetBundleWriter::ptr presetbundleWriter = PresetBundleWriter::CreatePresetsFile(*(this->model),content);
|
PresetBundleWriter::ptr presetbundleWriter = PresetBundleWriter::CreatePresetsFile(*(this->model), content);
|
||||||
presetbundleWriter->WriteToFile(tmpFile.Path());
|
presetbundleWriter->WriteToFile(tmpFile.Path());
|
||||||
size_t contentLength = std::filesystem::file_size(tmpFile.Path());
|
size_t contentLength = std::filesystem::file_size(tmpFile.Path());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
res.set(HttpField::content_type, BANK_MIME_TYPE);
|
res.set(HttpField::content_type, BANK_MIME_TYPE);
|
||||||
res.set(HttpField::cache_control, "no-cache");
|
res.set(HttpField::cache_control, "no-cache");
|
||||||
res.set(HttpField::content_disposition, GetContentDispositionHeader(name, BANK_EXTENSION));
|
res.set(HttpField::content_disposition, GetContentDispositionHeader(name, BANK_EXTENSION));
|
||||||
@@ -290,12 +299,11 @@ public:
|
|||||||
std::string content;
|
std::string content;
|
||||||
GetPluginPresets(request_uri, &name, &content);
|
GetPluginPresets(request_uri, &name, &content);
|
||||||
|
|
||||||
std::shared_ptr<TemporaryFile> tmpFile =std::make_shared<TemporaryFile>(WEB_TEMP_DIR);
|
std::shared_ptr<TemporaryFile> tmpFile = std::make_shared<TemporaryFile>(WEB_TEMP_DIR);
|
||||||
PresetBundleWriter::ptr presetbundleWriter = PresetBundleWriter::CreatePluginPresetsFile(*(this->model),content);
|
PresetBundleWriter::ptr presetbundleWriter = PresetBundleWriter::CreatePluginPresetsFile(*(this->model), content);
|
||||||
presetbundleWriter->WriteToFile(tmpFile->Path());
|
presetbundleWriter->WriteToFile(tmpFile->Path());
|
||||||
size_t contentLength = std::filesystem::file_size(tmpFile->Path());
|
size_t contentLength = std::filesystem::file_size(tmpFile->Path());
|
||||||
|
|
||||||
|
|
||||||
res.set(HttpField::content_type, PLUGIN_PRESETS_MIME_TYPE);
|
res.set(HttpField::content_type, PLUGIN_PRESETS_MIME_TYPE);
|
||||||
res.set(HttpField::cache_control, "no-cache");
|
res.set(HttpField::cache_control, "no-cache");
|
||||||
res.setContentLength(contentLength);
|
res.setContentLength(contentLength);
|
||||||
@@ -308,12 +316,11 @@ public:
|
|||||||
std::string content;
|
std::string content;
|
||||||
GetPreset(request_uri, &name, &content);
|
GetPreset(request_uri, &name, &content);
|
||||||
|
|
||||||
std::shared_ptr<TemporaryFile> tmpFile =std::make_shared<TemporaryFile>(WEB_TEMP_DIR);
|
std::shared_ptr<TemporaryFile> tmpFile = std::make_shared<TemporaryFile>(WEB_TEMP_DIR);
|
||||||
PresetBundleWriter::ptr presetbundleWriter = PresetBundleWriter::CreatePresetsFile(*(this->model),content);
|
PresetBundleWriter::ptr presetbundleWriter = PresetBundleWriter::CreatePresetsFile(*(this->model), content);
|
||||||
presetbundleWriter->WriteToFile(tmpFile->Path());
|
presetbundleWriter->WriteToFile(tmpFile->Path());
|
||||||
size_t contentLength = std::filesystem::file_size(tmpFile->Path());
|
size_t contentLength = std::filesystem::file_size(tmpFile->Path());
|
||||||
|
|
||||||
|
|
||||||
res.set(HttpField::content_type, PRESET_MIME_TYPE);
|
res.set(HttpField::content_type, PRESET_MIME_TYPE);
|
||||||
res.set(HttpField::cache_control, "no-cache");
|
res.set(HttpField::cache_control, "no-cache");
|
||||||
res.setContentLength(contentLength);
|
res.setContentLength(contentLength);
|
||||||
@@ -326,13 +333,11 @@ public:
|
|||||||
std::string content;
|
std::string content;
|
||||||
GetBank(request_uri, &name, &content);
|
GetBank(request_uri, &name, &content);
|
||||||
|
|
||||||
std::shared_ptr<TemporaryFile> tmpFile =std::make_shared<TemporaryFile>(WEB_TEMP_DIR);
|
std::shared_ptr<TemporaryFile> tmpFile = std::make_shared<TemporaryFile>(WEB_TEMP_DIR);
|
||||||
PresetBundleWriter::ptr presetbundleWriter = PresetBundleWriter::CreatePresetsFile(*(this->model),content);
|
PresetBundleWriter::ptr presetbundleWriter = PresetBundleWriter::CreatePresetsFile(*(this->model), content);
|
||||||
presetbundleWriter->WriteToFile(tmpFile->Path());
|
presetbundleWriter->WriteToFile(tmpFile->Path());
|
||||||
size_t contentLength = std::filesystem::file_size(tmpFile->Path());
|
size_t contentLength = std::filesystem::file_size(tmpFile->Path());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
res.set(HttpField::content_type, BANK_MIME_TYPE);
|
res.set(HttpField::content_type, BANK_MIME_TYPE);
|
||||||
res.set(HttpField::cache_control, "no-cache");
|
res.set(HttpField::cache_control, "no-cache");
|
||||||
res.setContentLength(contentLength);
|
res.setContentLength(contentLength);
|
||||||
@@ -356,40 +361,49 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static std::string GetFirstFolderOrFile(const std::vector<std::string>&fileNames)
|
static std::string GetFirstFolderOrFile(const std::vector<std::string> &fileNames)
|
||||||
{
|
{
|
||||||
for (const auto &fileName: fileNames)
|
for (const auto &fileName : fileNames)
|
||||||
{
|
{
|
||||||
size_t nPos = fileName.find('/');
|
size_t nPos = fileName.find('/');
|
||||||
if (nPos != std::string::npos) {
|
if (nPos != std::string::npos)
|
||||||
return fileName.substr(0,nPos);
|
{
|
||||||
|
return fileName.substr(0, nPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fileNames.size() == 0)
|
if (fileNames.size() == 0)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
return fileNames[0];
|
return fileNames[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static bool HasSingleRootDirectory(ZipFileReader&zipFile) {
|
static bool HasSingleRootDirectory(ZipFileReader &zipFile)
|
||||||
|
{
|
||||||
bool hasDirectory = false;
|
bool hasDirectory = false;
|
||||||
std::string previousDirectory;
|
std::string previousDirectory;
|
||||||
for (auto& file: zipFile.GetFiles())
|
for (auto &file : zipFile.GetFiles())
|
||||||
{
|
{
|
||||||
auto pos = file.find('/');
|
auto pos = file.find('/');
|
||||||
if (pos == std::string::npos) {
|
if (pos == std::string::npos)
|
||||||
|
{
|
||||||
// a file in the root.
|
// a file in the root.
|
||||||
return false;
|
return false;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
std::string currentRootDirectory = file.substr(0,pos);
|
std::string currentRootDirectory = file.substr(0, pos);
|
||||||
if (hasDirectory) {
|
if (hasDirectory)
|
||||||
|
{
|
||||||
if (currentRootDirectory != previousDirectory)
|
if (currentRootDirectory != previousDirectory)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
hasDirectory = true;
|
hasDirectory = true;
|
||||||
previousDirectory = currentRootDirectory;
|
previousDirectory = currentRootDirectory;
|
||||||
}
|
}
|
||||||
@@ -412,26 +426,26 @@ public:
|
|||||||
{
|
{
|
||||||
PluginPresets presets;
|
PluginPresets presets;
|
||||||
fs::path filePath = req.get_body_temporary_file();
|
fs::path filePath = req.get_body_temporary_file();
|
||||||
if (filePath.empty())
|
if (filePath.empty())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Unexpected.");
|
throw std::runtime_error("Unexpected.");
|
||||||
}
|
}
|
||||||
if (IsZipFile(filePath))
|
if (IsZipFile(filePath))
|
||||||
{
|
{
|
||||||
auto presetReader = PresetBundleReader::LoadPluginPresetsFile(*(this->model),filePath);
|
auto presetReader = PresetBundleReader::LoadPluginPresetsFile(*(this->model), filePath);
|
||||||
presetReader->ExtractMediaFiles();
|
presetReader->ExtractMediaFiles();
|
||||||
|
|
||||||
std::stringstream ss(presetReader->GetPluginPresetsJson());
|
std::stringstream ss(presetReader->GetPluginPresetsJson());
|
||||||
json_reader reader(ss);
|
json_reader reader(ss);
|
||||||
reader.read(&presets);
|
reader.read(&presets);
|
||||||
|
}
|
||||||
} else {
|
else
|
||||||
|
{
|
||||||
json_reader reader(req.get_body_input_stream());
|
json_reader reader(req.get_body_input_stream());
|
||||||
reader.read(&presets);
|
reader.read(&presets);
|
||||||
}
|
}
|
||||||
model->UploadPluginPresets(presets);
|
model->UploadPluginPresets(presets);
|
||||||
|
|
||||||
|
|
||||||
res.set(HttpField::content_type, "application/json");
|
res.set(HttpField::content_type, "application/json");
|
||||||
res.set(HttpField::cache_control, "no-cache");
|
res.set(HttpField::cache_control, "no-cache");
|
||||||
std::stringstream sResult;
|
std::stringstream sResult;
|
||||||
@@ -447,19 +461,21 @@ public:
|
|||||||
BankFile bankFile;
|
BankFile bankFile;
|
||||||
|
|
||||||
fs::path filePath = req.get_body_temporary_file();
|
fs::path filePath = req.get_body_temporary_file();
|
||||||
if (filePath.empty())
|
if (filePath.empty())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Unexpected.");
|
throw std::runtime_error("Unexpected.");
|
||||||
}
|
}
|
||||||
if (IsZipFile(filePath))
|
if (IsZipFile(filePath))
|
||||||
{
|
{
|
||||||
auto presetReader = PresetBundleReader::LoadPresetsFile(*(this->model),filePath);
|
auto presetReader = PresetBundleReader::LoadPresetsFile(*(this->model), filePath);
|
||||||
presetReader->ExtractMediaFiles();
|
presetReader->ExtractMediaFiles();
|
||||||
|
|
||||||
std::stringstream ss(presetReader->GetPresetJson());
|
std::stringstream ss(presetReader->GetPresetJson());
|
||||||
json_reader reader(ss);
|
json_reader reader(ss);
|
||||||
reader.read(&bankFile);
|
reader.read(&bankFile);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// legacy json format, no zip, no media files.
|
// legacy json format, no zip, no media files.
|
||||||
json_reader reader(req.get_body_input_stream());
|
json_reader reader(req.get_body_input_stream());
|
||||||
reader.read(&bankFile);
|
reader.read(&bankFile);
|
||||||
@@ -487,19 +503,21 @@ public:
|
|||||||
BankFile bankFile;
|
BankFile bankFile;
|
||||||
|
|
||||||
fs::path filePath = req.get_body_temporary_file();
|
fs::path filePath = req.get_body_temporary_file();
|
||||||
if (filePath.empty())
|
if (filePath.empty())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Unexpected.");
|
throw std::runtime_error("Unexpected.");
|
||||||
}
|
}
|
||||||
if (IsZipFile(filePath))
|
if (IsZipFile(filePath))
|
||||||
{
|
{
|
||||||
auto presetReader = PresetBundleReader::LoadPresetsFile(*(this->model),filePath);
|
auto presetReader = PresetBundleReader::LoadPresetsFile(*(this->model), filePath);
|
||||||
presetReader->ExtractMediaFiles();
|
presetReader->ExtractMediaFiles();
|
||||||
|
|
||||||
std::stringstream ss(presetReader->GetPresetJson());
|
std::stringstream ss(presetReader->GetPresetJson());
|
||||||
json_reader reader(ss);
|
json_reader reader(ss);
|
||||||
reader.read(&bankFile);
|
reader.read(&bankFile);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// legacy json format, no zip, no media files.
|
// legacy json format, no zip, no media files.
|
||||||
json_reader reader(req.get_body_input_stream());
|
json_reader reader(req.get_body_input_stream());
|
||||||
reader.read(&bankFile);
|
reader.read(&bankFile);
|
||||||
@@ -522,70 +540,88 @@ public:
|
|||||||
res.setContentLength(result.length());
|
res.setContentLength(result.length());
|
||||||
|
|
||||||
res.setBody(result);
|
res.setBody(result);
|
||||||
} else if (segment == "uploadUserFile")
|
}
|
||||||
|
else if (segment == "uploadUserFile")
|
||||||
{
|
{
|
||||||
res.set(HttpField::content_type, "application/json");
|
UserUploadResponse result;
|
||||||
res.set(HttpField::cache_control, "no-cache");
|
try
|
||||||
std::string instanceId = request_uri.query("id");
|
|
||||||
std::string directory = request_uri.query("directory");
|
|
||||||
std::string filename = request_uri.query("filename");
|
|
||||||
std::string patchProperty = request_uri.query("property");
|
|
||||||
|
|
||||||
|
|
||||||
if (patchProperty.length() == 0 && directory.length() == 0)
|
|
||||||
{
|
{
|
||||||
throw PiPedalException("Malformed request.");
|
|
||||||
|
|
||||||
}
|
res.set(HttpField::content_type, "application/json");
|
||||||
|
res.set(HttpField::cache_control, "no-cache");
|
||||||
|
std::string instanceId = request_uri.query("id");
|
||||||
|
std::string directory = request_uri.query("directory");
|
||||||
|
std::string filename = request_uri.query("filename");
|
||||||
|
std::string patchProperty = request_uri.query("property");
|
||||||
|
|
||||||
res.set(HttpField::content_type, "application/json");
|
if (patchProperty.length() == 0 && directory.length() == 0)
|
||||||
res.set(HttpField::cache_control, "no-cache");
|
{
|
||||||
|
throw PiPedalException("Malformed request.");
|
||||||
|
}
|
||||||
|
|
||||||
std::string outputFileName = std::filesystem::path(directory) / filename;
|
res.set(HttpField::content_type, "application/json");
|
||||||
|
res.set(HttpField::cache_control, "no-cache");
|
||||||
|
|
||||||
if (filename.ends_with(".zip"))
|
fs::path outputFileName = std::filesystem::path(directory) / filename;
|
||||||
{
|
|
||||||
ExtensionChecker extensionChecker { request_uri.query("ext") };
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
try {
|
if (filename.ends_with(".zip"))
|
||||||
auto zipFile = ZipFileReader::Create(req.get_body_temporary_file());
|
{
|
||||||
std::vector<std::string> files = zipFile->GetFiles();
|
ExtensionChecker extensionChecker{request_uri.query("ext")};
|
||||||
bool hasSingleRootDirectory = HasSingleRootDirectory(*zipFile);
|
namespace fs = std::filesystem;
|
||||||
if (!hasSingleRootDirectory) {
|
|
||||||
directory = (fs::path(directory) / fs::path(filename).filename().replace_extension("")).string();
|
try
|
||||||
}
|
|
||||||
for (const auto&inputFile : files)
|
|
||||||
{
|
{
|
||||||
if (!inputFile.ends_with("/")) // don't process directory entries.
|
auto zipFile = ZipFileReader::Create(req.get_body_temporary_file());
|
||||||
|
std::vector<std::string> files = zipFile->GetFiles();
|
||||||
|
bool hasSingleRootDirectory = HasSingleRootDirectory(*zipFile);
|
||||||
|
if (!hasSingleRootDirectory)
|
||||||
{
|
{
|
||||||
fs::path inputPath { inputFile};
|
directory = (fs::path(directory) / fs::path(filename).filename().replace_extension("")).string();
|
||||||
std::string extension = inputPath.extension();
|
}
|
||||||
if (extensionChecker.IsValidExtension(extension))
|
for (const auto &inputFile : files)
|
||||||
|
{
|
||||||
|
if (!inputFile.ends_with("/")) // don't process directory entries.
|
||||||
{
|
{
|
||||||
auto si = zipFile->GetFileInputStream(inputFile);
|
fs::path inputPath{inputFile};
|
||||||
std::string path = this->model->UploadUserFile(directory,patchProperty,inputFile, si,zipFile->GetFileSize(inputFile));
|
std::string extension = inputPath.extension();
|
||||||
|
if (extensionChecker.IsValidExtension(extension))
|
||||||
|
{
|
||||||
|
auto si = zipFile->GetFileInputStream(inputFile);
|
||||||
|
std::string path = this->model->UploadUserFile(directory, patchProperty, inputFile, si, zipFile->GetFileSize(inputFile));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// set outputPath to the file or folder we would like focus to go to.
|
||||||
|
// almost always a single folder in the root.
|
||||||
|
std::string returnPath = GetFirstFolderOrFile(files);
|
||||||
|
outputFileName = fs::path(directory) / returnPath;
|
||||||
}
|
}
|
||||||
// set outputPath to the file or folder we would like focus to go to.
|
catch (const std::exception &e)
|
||||||
// almost always a single folder in the root.
|
{
|
||||||
std::string returnPath = GetFirstFolderOrFile(files);
|
Lv2Log::error(SS("Unzip failed. " << e.what()));
|
||||||
outputFileName = this->model->GetPluginUploadDirectory() / directory / returnPath;
|
throw;
|
||||||
} catch (const std::exception &e)
|
}
|
||||||
{
|
FileSystemSync();
|
||||||
Lv2Log::error(SS("Unzip failed. " << e.what()));
|
}
|
||||||
throw;
|
else
|
||||||
|
{
|
||||||
|
outputFileName = this->model->UploadUserFile(directory, patchProperty, filename, req.get_body_input_stream(), req.content_length());
|
||||||
}
|
}
|
||||||
FileSystemSync();
|
|
||||||
|
|
||||||
} else {
|
if (outputFileName.is_relative())
|
||||||
outputFileName = this->model->UploadUserFile(directory,patchProperty,filename,req.get_body_input_stream(), req.content_length());
|
{
|
||||||
|
outputFileName = this->model->GetPluginUploadDirectory() / outputFileName;
|
||||||
|
}
|
||||||
|
result.path_ = outputFileName.string();
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
result.errorMessage_ = e.what();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
json_writer writer(ss);
|
json_writer writer(ss);
|
||||||
writer.write(outputFileName);
|
writer.write(result);
|
||||||
std::string response = ss.str();
|
std::string response = ss.str();
|
||||||
|
|
||||||
res.setContentLength(response.length());
|
res.setContentLength(response.length());
|
||||||
@@ -598,7 +634,7 @@ public:
|
|||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
Lv2Log::error(SS("Error uploading file: " << e.what()) );
|
Lv2Log::error(SS("Error uploading file: " << e.what()));
|
||||||
if (strcmp(e.what(), "Not found") == 0)
|
if (strcmp(e.what(), "Not found") == 0)
|
||||||
{
|
{
|
||||||
ec = boost::system::errc::make_error_code(boost::system::errc::no_such_file_or_directory);
|
ec = boost::system::errc::make_error_code(boost::system::errc::no_such_file_or_directory);
|
||||||
@@ -611,7 +647,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static std::string StripPortNumber(const std::string &fromAddress)
|
static std::string StripPortNumber(const std::string &fromAddress)
|
||||||
{
|
{
|
||||||
std::string address = fromAddress;
|
std::string address = fromAddress;
|
||||||
@@ -663,10 +698,10 @@ public:
|
|||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
|
|
||||||
s << "{ \"socket_server_port\": " << portNumber
|
s << "{ \"socket_server_port\": " << portNumber
|
||||||
<< ", \"socket_server_address\": \"" << webSocketAddress
|
<< ", \"socket_server_address\": \"" << webSocketAddress
|
||||||
<< "\", \"ui_plugins\": [ ], \"max_upload_size\": " << maxUploadSize
|
<< "\", \"ui_plugins\": [ ], \"max_upload_size\": " << maxUploadSize
|
||||||
<< ", \"enable_auto_update\": " << (ENABLE_AUTO_UPDATE ? " true": "false")
|
<< ", \"enable_auto_update\": " << (ENABLE_AUTO_UPDATE ? " true" : "false")
|
||||||
<< " }";
|
<< " }";
|
||||||
|
|
||||||
return s.str();
|
return s.str();
|
||||||
}
|
}
|
||||||
@@ -720,16 +755,14 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
void pipedal::ConfigureWebServer(
|
void pipedal::ConfigureWebServer(
|
||||||
WebServer&server,
|
WebServer &server,
|
||||||
PiPedalModel&model,
|
PiPedalModel &model,
|
||||||
int port,
|
int port,
|
||||||
size_t maxUploadSize)
|
size_t maxUploadSize)
|
||||||
{
|
{
|
||||||
std::shared_ptr<RequestHandler> interceptConfig{new InterceptConfig(port, maxUploadSize)};
|
std::shared_ptr<RequestHandler> interceptConfig{new InterceptConfig(port, maxUploadSize)};
|
||||||
server.AddRequestHandler(interceptConfig);
|
server.AddRequestHandler(interceptConfig);
|
||||||
|
|
||||||
std::shared_ptr<DownloadIntercept> downloadIntercept = std::make_shared<DownloadIntercept>(&model);
|
|
||||||
server.AddRequestHandler(downloadIntercept);
|
|
||||||
|
|
||||||
|
|
||||||
|
std::shared_ptr<DownloadIntercept> downloadIntercept = std::make_shared<DownloadIntercept>(&model);
|
||||||
|
server.AddRequestHandler(downloadIntercept);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user