Manifest.json, Channel Router Sync. Alsa device enumeration.
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 16.933332 16.933334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:export-filename="logo192.png"
|
||||
inkscape:export-xdpi="288"
|
||||
inkscape:export-ydpi="288"
|
||||
inkscape:version="1.4.3 (1:1.4.3+202512261035+0d15f75042)"
|
||||
sodipodi:docname="Pi-Icon-Inkscape.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="true"
|
||||
inkscape:lockguides="true"
|
||||
inkscape:zoom="14.609375"
|
||||
inkscape:cx="32"
|
||||
inkscape:cy="32"
|
||||
inkscape:window-width="2141"
|
||||
inkscape:window-height="1161"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1">
|
||||
<inkscape:grid
|
||||
id="grid2"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="1.0583333"
|
||||
spacingy="1.0583333"
|
||||
empcolor="#000000"
|
||||
empopacity="0.30196078"
|
||||
color="#000000"
|
||||
opacity="0.14901961"
|
||||
empspacing="4"
|
||||
enabled="true"
|
||||
visible="true"
|
||||
dotted="true" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
style="display:inline">
|
||||
<circle
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;fill:#5e2edf;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3"
|
||||
cx="8.4666653"
|
||||
cy="8.4666653"
|
||||
r="8.4666653" />
|
||||
<g
|
||||
id="g5"
|
||||
transform="matrix(1.1241207,0,0,1.1241207,-1.0580763,-1.0508884)">
|
||||
<circle
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.5875;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4"
|
||||
cx="8.4666662"
|
||||
cy="8.4666653"
|
||||
r="4.8967609" />
|
||||
<ellipse
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.396875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path5"
|
||||
cx="8.4666662"
|
||||
cy="8.4666653"
|
||||
rx="4.2097802"
|
||||
ry="4.2333322" />
|
||||
<path
|
||||
id="path4-9"
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;baseline-shift:baseline;display:inline;overflow:visible;opacity:1;fill:#ffffff;fill-opacity:1;stroke:#5e2edf;stroke-width:2.11667;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||
d="M 12.164115,4.1470337 7.9597209,7.8558512 8.9829142,9.0702474 13.214697,5.3371419 C 12.920982,4.8932514 12.566824,4.4925879 12.164115,4.1470337 Z" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 16.933332 16.933334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:export-filename="../vite/public/logo512.png"
|
||||
inkscape:export-xdpi="768"
|
||||
inkscape:export-ydpi="768"
|
||||
inkscape:version="1.4.3 (1:1.4.3+202512261035+0d15f75042)"
|
||||
sodipodi:docname="Pi-Icon-Inkscape.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="true"
|
||||
inkscape:lockguides="true"
|
||||
inkscape:zoom="14.609375"
|
||||
inkscape:cx="32"
|
||||
inkscape:cy="32"
|
||||
inkscape:window-width="2141"
|
||||
inkscape:window-height="1161"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1">
|
||||
<inkscape:grid
|
||||
id="grid2"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="1.0583333"
|
||||
spacingy="1.0583333"
|
||||
empcolor="#000000"
|
||||
empopacity="0.30196078"
|
||||
color="#000000"
|
||||
opacity="0.14901961"
|
||||
empspacing="4"
|
||||
enabled="true"
|
||||
visible="true"
|
||||
dotted="true" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
style="display:inline">
|
||||
<circle
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;fill:#5e2edf;fill-opacity:1;stroke:none;stroke-width:2.07592;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3"
|
||||
cx="8.4666653"
|
||||
cy="8.4666653"
|
||||
r="8.3036709" />
|
||||
<g
|
||||
id="g5"
|
||||
transform="matrix(1.0057192,0,0,1.0057192,-0.04986822,-0.04842228)">
|
||||
<circle
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.5875;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4"
|
||||
cx="8.4666662"
|
||||
cy="8.4666653"
|
||||
r="4.8967609" />
|
||||
<ellipse
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.774042;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path5"
|
||||
cx="8.4666662"
|
||||
cy="8.4666653"
|
||||
rx="4.2097802"
|
||||
ry="4.2333322" />
|
||||
<path
|
||||
id="path4-9"
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;baseline-shift:baseline;display:inline;overflow:visible;opacity:1;fill:#ffffff;fill-opacity:1;stroke:#5e2edf;stroke-width:2.11667;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||
d="M 12.164115,4.1470337 7.9597209,7.8558512 8.9829142,9.0702474 13.214697,5.3371419 C 12.920982,4.8932514 12.566824,4.4925879 12.164115,4.1470337 Z" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
@@ -1895,8 +1895,14 @@ namespace pipedal
|
||||
std::vector<float*> &channelBuffers
|
||||
) {
|
||||
size_t nChannels = channelSelection.size();
|
||||
channelBuffers.resize(nChannels);
|
||||
if (nChannels == 0)
|
||||
{
|
||||
channelBuffers.resize(1);
|
||||
channelBuffers[0] = zeroInputBuffer;
|
||||
return;
|
||||
}
|
||||
|
||||
channelBuffers.resize(nChannels);
|
||||
for (size_t i = 0; i < nChannels; ++i)
|
||||
{
|
||||
int64_t deviceChannel = channelSelection[i];
|
||||
@@ -1932,6 +1938,16 @@ namespace pipedal
|
||||
) {
|
||||
size_t nChannels = channelSelection.size();
|
||||
|
||||
if (nChannels == 0)
|
||||
{
|
||||
channelBuffers.resize(1);
|
||||
if (discardOutputBuffer != nullptr)
|
||||
{
|
||||
discardOutputBuffer = AllocateAudioBuffer();
|
||||
}
|
||||
channelBuffers[0] = discardOutputBuffer;
|
||||
return;
|
||||
}
|
||||
channelBuffers.resize(nChannels);
|
||||
for (size_t i = 0; i < nChannels; ++i)
|
||||
{
|
||||
|
||||
@@ -533,6 +533,7 @@ private:
|
||||
|
||||
std::vector<std::shared_ptr<Lv2Pedalboard>> activePedalboards; // pedalboards that have been sent to the audio queue.
|
||||
Lv2Pedalboard *realtimeActivePedalboard = nullptr;
|
||||
Lv2RoutingInserts realtimeRoutingInserts;
|
||||
|
||||
uint32_t sampleRate = 0;
|
||||
uint64_t currentSample = 0;
|
||||
@@ -894,6 +895,47 @@ private:
|
||||
|
||||
return false; // signal to caller that the effect has been replaced, and processing needs to start again.
|
||||
}
|
||||
case RingBufferCommand::ReplaceRoutingInserts:
|
||||
{
|
||||
Lv2RoutingInserts body;
|
||||
realtimeReader.readComplete(&body);
|
||||
|
||||
auto oldValue = this->realtimeRoutingInserts;
|
||||
this->realtimeRoutingInserts = body;
|
||||
|
||||
realtimeWriter.RoutingInsertsReplaced(oldValue);
|
||||
|
||||
// invalidate the possibly no-good subscriptions. Model will update them shortly.
|
||||
freeRealtimeVuConfiguration();
|
||||
freeRealtimeMonitorPortSubscriptions();
|
||||
cancelParameterRequests();
|
||||
|
||||
Lv2Pedalboard *mainInserts = body.mainInserts;
|
||||
if (mainInserts)
|
||||
{
|
||||
mainInserts->ResetAtomBuffers();
|
||||
// issue patch gets for all writable path properties.
|
||||
for (auto pEffect : mainInserts->GetEffects())
|
||||
{
|
||||
pEffect->RequestAllPathPatchProperties();
|
||||
}
|
||||
mainInserts->UpdateAudioPorts();
|
||||
}
|
||||
Lv2Pedalboard *auxInserts = body.auxInserts;
|
||||
if (auxInserts)
|
||||
{
|
||||
auxInserts->ResetAtomBuffers();
|
||||
// issue patch gets for all writable path properties.
|
||||
for (auto pEffect : auxInserts->GetEffects())
|
||||
{
|
||||
pEffect->RequestAllPathPatchProperties();
|
||||
}
|
||||
auxInserts->UpdateAudioPorts();
|
||||
}
|
||||
reEntered = false;
|
||||
|
||||
return false; // signal to caller that the effect has been replaced, and processing needs to start again.
|
||||
}
|
||||
default:
|
||||
throw PiPedalStateException("Unknown Ringbuffer command.");
|
||||
}
|
||||
@@ -1549,6 +1591,20 @@ public:
|
||||
hostReader.read(&body);
|
||||
OnActivePedalboardReleased(body.oldEffect);
|
||||
}
|
||||
else if (command == RingBufferCommand::RoutingInsertsReplaced)
|
||||
{
|
||||
Lv2RoutingInserts body;
|
||||
hostReader.read(&body);
|
||||
if (body.mainInserts)
|
||||
{
|
||||
OnActivePedalboardReleased(body.mainInserts);
|
||||
}
|
||||
if (body.auxInserts)
|
||||
{
|
||||
OnActivePedalboardReleased(body.auxInserts);
|
||||
}
|
||||
|
||||
}
|
||||
else if (command == RingBufferCommand::FreeSnapshot)
|
||||
{
|
||||
IndexedSnapshot *snapshot;
|
||||
@@ -1782,6 +1838,25 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetChannelRoutingInserts(
|
||||
const std::shared_ptr<Lv2Pedalboard> &mainInserts,
|
||||
const std::shared_ptr<Lv2Pedalboard> &auxInserts
|
||||
) override {
|
||||
if (active && mainInserts)
|
||||
{
|
||||
mainInserts->Activate();
|
||||
this->activePedalboards.push_back(mainInserts);
|
||||
}
|
||||
if (active && auxInserts)
|
||||
{
|
||||
auxInserts->Activate();
|
||||
this->activePedalboards.push_back(auxInserts);
|
||||
}
|
||||
Lv2RoutingInserts routingInserts { mainInserts: mainInserts.get(), auxInserts: auxInserts.get()};
|
||||
hostWriter.ReplaceRoutingInserts(routingInserts);
|
||||
}
|
||||
|
||||
|
||||
virtual void SetBypass(uint64_t instanceId, bool enabled)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
|
||||
@@ -238,6 +238,12 @@ namespace pipedal
|
||||
|
||||
virtual void SetPedalboard(const std::shared_ptr<Lv2Pedalboard> &pedalboard) = 0;
|
||||
|
||||
virtual void SetChannelRoutingInserts(
|
||||
const std::shared_ptr<Lv2Pedalboard> &mainInserts,
|
||||
const std::shared_ptr<Lv2Pedalboard> &auxInserts
|
||||
) = 0;
|
||||
|
||||
|
||||
virtual void SetControlValue(uint64_t instanceId, const std::string &symbol, float value) = 0;
|
||||
virtual void SetInputVolume(float value) = 0;
|
||||
virtual void SetOutputVolume(float value) = 0;
|
||||
|
||||
@@ -740,6 +740,7 @@ add_executable(pipedalconfig
|
||||
ModFileTypes.cpp ModFileTypes.hpp
|
||||
MimeTypes.cpp MimeTypes.hpp
|
||||
PiPedalConfiguration.hpp PiPedalConfiguration.cpp
|
||||
PiPedalAlsa.hpp PiPedalAlsa.cpp
|
||||
JackServerSettings.hpp JackServerSettings.cpp
|
||||
SystemConfigFile.hpp SystemConfigFile.cpp
|
||||
WifiChannelSelectors.cpp WifiChannelSelectors.hpp
|
||||
@@ -817,6 +818,7 @@ add_executable(pipedaladmind AdminMain.cpp CommandLineParser.hpp
|
||||
|
||||
SetWifiConfig.cpp SetWifiConfig.hpp
|
||||
JackServerSettings.hpp JackServerSettings.cpp
|
||||
PiPedalAlsa.hpp PiPedalAlsa.cpp
|
||||
Lv2SystemdLogger.hpp Lv2SystemdLogger.cpp
|
||||
|
||||
SystemConfigFile.hpp SystemConfigFile.cpp
|
||||
@@ -824,7 +826,7 @@ add_executable(pipedaladmind AdminMain.cpp CommandLineParser.hpp
|
||||
asan_options.cpp
|
||||
|
||||
)
|
||||
target_link_libraries(pipedaladmind PRIVATE PiPedalCommon pthread atomic stdc++fs systemd )
|
||||
target_link_libraries(pipedaladmind PRIVATE PiPedalCommon pthread atomic stdc++fs systemd asound)
|
||||
|
||||
add_executable(processcopyrights copyrightMain.cpp
|
||||
CommandLineParser.hpp
|
||||
|
||||
@@ -135,9 +135,20 @@ ChannelRouterSettings::ChannelRouterSettings()
|
||||
|
||||
}
|
||||
|
||||
std::vector<ChannelRouterPresetIndexEntry> ChannelRouterPresetBank::getIndexEntries()
|
||||
{
|
||||
std::vector<ChannelRouterPresetIndexEntry> result;
|
||||
for (auto &entry: entries_)
|
||||
{
|
||||
result.push_back(ChannelRouterPresetIndexEntry{entry->id(), entry->name()});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
JSON_MAP_BEGIN(ChannelRouterSettings)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, configured)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, changed)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, channelRouterPresetId)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, mainInputChannels)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, mainOutputChannels)
|
||||
@@ -149,3 +160,22 @@ JSON_MAP_REFERENCE(ChannelRouterSettings, sendInputChannels)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, sendOutputChannels)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, controlValues)
|
||||
JSON_MAP_END()
|
||||
|
||||
JSON_MAP_BEGIN(ChannelRouterPresetIndexEntry)
|
||||
JSON_MAP_REFERENCE(ChannelRouterPresetIndexEntry, id)
|
||||
JSON_MAP_REFERENCE(ChannelRouterPresetIndexEntry, name)
|
||||
JSON_MAP_END()
|
||||
|
||||
|
||||
JSON_MAP_BEGIN(ChannelRouterPresetBankEntry)
|
||||
JSON_MAP_REFERENCE(ChannelRouterPresetBankEntry, id)
|
||||
JSON_MAP_REFERENCE(ChannelRouterPresetBankEntry, name)
|
||||
JSON_MAP_REFERENCE(ChannelRouterPresetBankEntry, channelRouterSettings)
|
||||
JSON_MAP_END()
|
||||
|
||||
JSON_MAP_BEGIN(ChannelRouterPresetBank)
|
||||
JSON_MAP_REFERENCE(ChannelRouterPresetBank, nextId)
|
||||
JSON_MAP_REFERENCE(ChannelRouterPresetBank, version)
|
||||
JSON_MAP_REFERENCE(ChannelRouterPresetBank, entries)
|
||||
JSON_MAP_END()
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace pipedal
|
||||
|
||||
bool configured_ = false;
|
||||
int64_t channelRouterPresetId_ = -1;
|
||||
bool changed_ = false;
|
||||
|
||||
std::vector<int64_t> mainInputChannels_ = {1, 1};
|
||||
std::vector<int64_t> mainOutputChannels_ = {0, 1};
|
||||
@@ -60,6 +61,7 @@ namespace pipedal
|
||||
|
||||
JSON_GETTER_SETTER(configured)
|
||||
JSON_GETTER_SETTER(channelRouterPresetId)
|
||||
JSON_GETTER_SETTER(changed)
|
||||
JSON_GETTER_SETTER_REF(mainInputChannels)
|
||||
JSON_GETTER_SETTER_REF(mainOutputChannels)
|
||||
JSON_GETTER_SETTER_REF(mainInserts)
|
||||
@@ -110,4 +112,56 @@ namespace pipedal
|
||||
std::vector<int64_t> sendOutputChannels_;
|
||||
};
|
||||
|
||||
class ChannelRouterPresetIndexEntry {
|
||||
private:
|
||||
int64_t id_ = -1;
|
||||
std::string name_;
|
||||
public:
|
||||
ChannelRouterPresetIndexEntry() = default;
|
||||
ChannelRouterPresetIndexEntry(const ChannelRouterPresetIndexEntry&other) = default;
|
||||
ChannelRouterPresetIndexEntry(ChannelRouterPresetIndexEntry&&other) = default;
|
||||
ChannelRouterPresetIndexEntry&operator=(const ChannelRouterPresetIndexEntry&other) = default;
|
||||
ChannelRouterPresetIndexEntry&operator=(ChannelRouterPresetIndexEntry&&other) = default;
|
||||
|
||||
ChannelRouterPresetIndexEntry(int64_t id, const std::string&name)
|
||||
: id_(id),name_(name) { }
|
||||
|
||||
|
||||
JSON_GETTER_SETTER(id)
|
||||
JSON_GETTER_SETTER_REF(name)
|
||||
|
||||
DECLARE_JSON_MAP(ChannelRouterPresetIndexEntry);
|
||||
};
|
||||
|
||||
|
||||
class ChannelRouterPresetBankEntry {
|
||||
private:
|
||||
int64_t id_;
|
||||
std::string name_;
|
||||
ChannelRouterSettings channelRouterSettings_;
|
||||
public:
|
||||
JSON_GETTER_SETTER(id)
|
||||
JSON_GETTER_SETTER_REF(name)
|
||||
JSON_GETTER_SETTER_REF(channelRouterSettings)
|
||||
|
||||
DECLARE_JSON_MAP(ChannelRouterPresetBankEntry);
|
||||
|
||||
};
|
||||
|
||||
class ChannelRouterPresetBank {
|
||||
private:
|
||||
int64_t nextId_ = 1;
|
||||
int64_t version_ = 1;
|
||||
std::vector<std::shared_ptr<ChannelRouterPresetBankEntry>> entries_;
|
||||
public:
|
||||
|
||||
JSON_GETTER_SETTER(nextId)
|
||||
JSON_GETTER_SETTER(version)
|
||||
JSON_GETTER_SETTER_REF(entries)
|
||||
|
||||
std::vector<ChannelRouterPresetIndexEntry> getIndexEntries();
|
||||
|
||||
DECLARE_JSON_MAP(ChannelRouterPresetBank);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -149,6 +149,8 @@ void JackConfiguration::AlsaInitialize(
|
||||
}
|
||||
} catch (const std::exception& /*ignore*/)
|
||||
{
|
||||
this->isValid_ = false;
|
||||
this->errorStatus_ = "Not configured.";
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <filesystem>
|
||||
#include "Lv2Log.hpp"
|
||||
#include "SysExec.hpp"
|
||||
#include "PiPedalAlsa.hpp"
|
||||
|
||||
#define DRC_FILENAME "/etc/jackdrc"
|
||||
|
||||
@@ -66,27 +67,35 @@ static std::string GetJackStringArg(const std::vector<std::string> &args, const
|
||||
for (size_t i = 1; i < args.size(); ++i)
|
||||
{
|
||||
auto pos = args[i].rfind(longOption, 0);
|
||||
if (pos != std::string::npos) {
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
if (args[i].length() == longOption.length())
|
||||
{
|
||||
if (i == args.size()-1) {
|
||||
if (i == args.size() - 1)
|
||||
{
|
||||
throw PiPedalException("Can't read Jack configuration.");
|
||||
}
|
||||
strVal = args[i + 1];
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
strVal = args[i].substr(longOption.length());
|
||||
}
|
||||
break;
|
||||
}
|
||||
pos = args[i].rfind(shortOption, 0);
|
||||
if (pos != std::string::npos) {
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
if (args[i].length() == shortOption.length())
|
||||
{
|
||||
if (i == args.size()-1) {
|
||||
if (i == args.size() - 1)
|
||||
{
|
||||
throw PiPedalException("Can't read Jack configuration.");
|
||||
}
|
||||
strVal = args[i + 1];
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
strVal = args[i].substr(shortOption.length());
|
||||
}
|
||||
break;
|
||||
@@ -108,144 +117,42 @@ static std::int32_t GetJackArg(const std::vector<std::string> &args, const std::
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JackServerSettings::ReadJackDaemonConfiguration()
|
||||
static std::string GetAlsaDeviceName(const std::vector<AlsaDeviceInfo> &availableDevices, const std::string &id)
|
||||
{
|
||||
#if !JACK_HOST
|
||||
return;
|
||||
#endif
|
||||
this->valid_ = false;
|
||||
|
||||
std::string lastLine;
|
||||
if (id.empty())
|
||||
return "";
|
||||
std::string name;
|
||||
for (const auto &availableDevice : availableDevices)
|
||||
{
|
||||
ifstream input(DRC_FILENAME);
|
||||
|
||||
if (!input.is_open())
|
||||
if (availableDevice.id_ == id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::string line;
|
||||
std::getline(input,line);
|
||||
if (line.length() != 0) {
|
||||
lastLine = line;
|
||||
}
|
||||
if (input.eof()) {
|
||||
name = availableDevice.name_;
|
||||
break;
|
||||
}
|
||||
}
|
||||
try
|
||||
if (name.empty())
|
||||
{
|
||||
std::vector<std::string> argv = SplitArgs(lastLine.c_str());
|
||||
for (auto i = argv.begin(); i != argv.end(); ++i)
|
||||
auto pos = id.find(":");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
if ((*i) == "-dalsa") {
|
||||
argv.erase(i);
|
||||
break;
|
||||
name = id.substr(pos + 1);
|
||||
}
|
||||
}
|
||||
this->bufferSize_ = GetJackArg(argv, "-p", "--period");
|
||||
this->numberOfBuffers_ = (uint32_t)GetJackArg(argv, "-n", "--nperiods");
|
||||
this->sampleRate_ = (uint32_t)GetJackArg(argv, "-r", "--rate");
|
||||
// read new dual device flags, fallback on old -d/--device
|
||||
std::string capDev = GetJackStringArg(argv, "-C", "--capture");
|
||||
std::string playDev = GetJackStringArg(argv, "-P", "--playback");
|
||||
std::string dev = "";
|
||||
try { dev = GetJackStringArg(argv, "-d", "--device"); } catch(...) {}
|
||||
this->alsaInputDevice_ = capDev.empty() ? dev : capDev;
|
||||
this->alsaOutputDevice_ = playDev.empty() ? dev : playDev;
|
||||
this->valid_ = true;
|
||||
return name;
|
||||
}
|
||||
catch (std::exception &)
|
||||
void JackServerSettings::FixUpDeviceNames()
|
||||
{
|
||||
if (
|
||||
((!this->alsaInputDevice_.empty()) && this->alsaInputDeviceName_.empty()) ||
|
||||
(!this->alsaOutputDevice_.empty()) && this->alsaOutputDeviceName_.empty())
|
||||
{
|
||||
//Lv2Log::error("Can't parse " DRC_FILENAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JackServerSettings::WriteDaemonConfig()
|
||||
{
|
||||
#if JACK_HOST
|
||||
this->valid_ = false;
|
||||
PiPedalAlsaDevices& alsaDevices = PiPedalAlsaDevices::instance();
|
||||
std::vector<AlsaDeviceInfo> availableDevices = alsaDevices.GetAlsaDevices();
|
||||
|
||||
std::vector<std::string> precedingLines;
|
||||
std::vector<std::string> argv;
|
||||
std::string lastLine;
|
||||
|
||||
if (std::filesystem::exists(DRC_FILENAME)) {
|
||||
ifstream input(DRC_FILENAME);
|
||||
|
||||
if (!input.is_open())
|
||||
{
|
||||
return;
|
||||
this->alsaInputDeviceName_ = GetAlsaDeviceName(availableDevices, this->alsaInputDevice_);
|
||||
this->alsaOutputDeviceName_ = GetAlsaDeviceName(availableDevices, this->alsaOutputDevice_);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (input.eof())
|
||||
{
|
||||
break;
|
||||
}
|
||||
std::getline(input,lastLine);
|
||||
precedingLines.push_back(lastLine);
|
||||
if (input.eof())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// erase blank lines at the end.
|
||||
while (precedingLines.size() != 0 && precedingLines[precedingLines.size()-1] == "")
|
||||
{
|
||||
precedingLines.erase(precedingLines.begin()+precedingLines.size()-1);
|
||||
}
|
||||
// erase the last line, which should contain the command invocation.
|
||||
if (precedingLines.size() != 0)
|
||||
{
|
||||
precedingLines.erase(precedingLines.begin()+precedingLines.size()-1);
|
||||
}
|
||||
}
|
||||
// write to the output.
|
||||
try
|
||||
{
|
||||
ofstream output(DRC_FILENAME);
|
||||
if (!output.is_open())
|
||||
{
|
||||
throw PiPedalException("Can't write " DRC_FILENAME);
|
||||
}
|
||||
if (precedingLines.size() == 0)
|
||||
{
|
||||
// jack1 incantation for promiscuous servers.
|
||||
output << "#!/bin/sh" <<endl;
|
||||
output << "export JACK_PROMISCUOUS_SERVER=audio" << endl;
|
||||
output << "export JACK_NO_AUDIO_RESERVATION=1" << endl;
|
||||
output << "umask 0" << endl;
|
||||
}
|
||||
for (auto line : precedingLines)
|
||||
{
|
||||
output << line << endl;
|
||||
}
|
||||
// the style used by qjackctl. :-/
|
||||
// Lower to -P70 in order to allow the USB soft-irq to run at higher priority than JACK (it runs at 80).
|
||||
output << "/usr/bin/jackd "
|
||||
<< "-R -P70 --silent"
|
||||
<< " -dalsa -d" << this->alsaDevice_
|
||||
<< " -r" << this->sampleRate_
|
||||
<< " -p" << this->bufferSize_
|
||||
<< " -n" << this->numberOfBuffers_ << " -Xseq"
|
||||
<< endl;
|
||||
if (silentSysExec("/usr/bin/chmod 755 " DRC_FILENAME) != 0)
|
||||
{
|
||||
Lv2Log::error("Failed to set permissions on /etc/jackdrc");
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
}
|
||||
#else
|
||||
throw PiPedalStateException("JACK_HOST not enabled at compile time.");
|
||||
#endif
|
||||
}
|
||||
|
||||
JSON_MAP_BEGIN(JackServerSettings)
|
||||
@@ -256,6 +163,8 @@ JSON_MAP_REFERENCE(JackServerSettings, isJackAudio)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, alsaDevice) // legacy field
|
||||
JSON_MAP_REFERENCE(JackServerSettings, alsaInputDevice)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, alsaOutputDevice)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, alsaInputDeviceName)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, alsaOutputDeviceName)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, sampleRate)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, bufferSize)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, numberOfBuffers)
|
||||
|
||||
@@ -33,7 +33,9 @@ namespace pipedal
|
||||
bool isJackAudio_ = JACK_HOST ? true : false;
|
||||
bool rebootRequired_ = false;
|
||||
std::string alsaInputDevice_;
|
||||
std::string alsaInputDeviceName_;
|
||||
std::string alsaOutputDevice_;
|
||||
std::string alsaOutputDeviceName_;
|
||||
std::string alsaDevice_; // legacy
|
||||
uint64_t sampleRate_ = 0;
|
||||
uint32_t bufferSize_ = 64;
|
||||
@@ -44,6 +46,7 @@ namespace pipedal
|
||||
JackServerSettings(
|
||||
const std::string &alsaInputDevice,
|
||||
const std::string &alsaOutputDevice,
|
||||
|
||||
uint64_t sampleRate,
|
||||
uint32_t bufferSize,
|
||||
uint32_t numberOfBuffers)
|
||||
@@ -55,6 +58,7 @@ namespace pipedal
|
||||
numberOfBuffers_(numberOfBuffers),
|
||||
isOnboarding_(false)
|
||||
{
|
||||
FixUpDeviceNames();
|
||||
}
|
||||
|
||||
uint64_t GetSampleRate() const { return sampleRate_; }
|
||||
@@ -62,11 +66,13 @@ namespace pipedal
|
||||
uint32_t GetBufferSize() const { return bufferSize_; }
|
||||
uint32_t GetNumberOfBuffers() const { return numberOfBuffers_; }
|
||||
const std::string &GetAlsaInputDevice() const { return alsaInputDevice_; }
|
||||
const std::string &GetAlsaInputDeviceName() const { return alsaInputDeviceName_; }
|
||||
const std::string &GetAlsaOutputDevice() const { return alsaOutputDevice_; }
|
||||
const std::string &GetAlsaOutputDeviceName() const { return alsaOutputDeviceName_; }
|
||||
const std::string &GetLegacyAlsaDevice() const { return alsaDevice_; } //legacy
|
||||
|
||||
void SetAlsaInputDevice(const std::string &d){ alsaInputDevice_ = d; }
|
||||
void SetAlsaOutputDevice(const std::string &d){ alsaOutputDevice_ = d; }
|
||||
void SetAlsaInputDevice(const std::string &id, const std::string&name){ alsaInputDevice_ = id; alsaInputDeviceName_ = name; }
|
||||
void SetAlsaOutputDevice(const std::string &id, const std::string&name){ alsaOutputDevice_ = id; alsaOutputDeviceName_ = name; }
|
||||
void SetLegacyAlsaDevice(const std::string &d) { alsaDevice_ = d; }
|
||||
|
||||
void UseDummyAudioDevice() {
|
||||
@@ -74,7 +80,9 @@ namespace pipedal
|
||||
if (sampleRate_ == 0) sampleRate_ = 48000;
|
||||
this->alsaDevice_ = "dummy:channels_2";
|
||||
this->alsaInputDevice_ = "dummy:channels_2";
|
||||
this->alsaInputDeviceName_ = "Stopped";
|
||||
this->alsaOutputDevice_ = "dummy:channels_2";
|
||||
this->alsaOutputDeviceName_ = "Stopped";
|
||||
}
|
||||
bool IsDummyAudioDevice() const {
|
||||
return
|
||||
@@ -83,11 +91,8 @@ namespace pipedal
|
||||
|| this->alsaInputDevice_.starts_with("dummy:");
|
||||
}
|
||||
|
||||
void ReadJackDaemonConfiguration();
|
||||
|
||||
bool IsValid() const { return valid_; }
|
||||
|
||||
void WriteDaemonConfig(); // requires root perms.
|
||||
void SetRebootRequired(bool value)
|
||||
{
|
||||
rebootRequired_ = value;
|
||||
@@ -106,6 +111,7 @@ namespace pipedal
|
||||
this->bufferSize_ == other.bufferSize_ &&
|
||||
this->numberOfBuffers_ == other.numberOfBuffers_;
|
||||
}
|
||||
void FixUpDeviceNames();
|
||||
|
||||
DECLARE_JSON_MAP(JackServerSettings);
|
||||
};
|
||||
|
||||
@@ -103,8 +103,7 @@ void PrintHelp()
|
||||
|
||||
void ListDevices()
|
||||
{
|
||||
PiPedalAlsaDevices alsaDevices;
|
||||
auto devices = alsaDevices.GetAlsaDevices();
|
||||
auto devices = PiPedalAlsaDevices::instance().GetAlsaDevices();
|
||||
|
||||
PrettyPrinter pp;
|
||||
if (devices.size() == 0)
|
||||
|
||||
@@ -160,11 +160,14 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
|
||||
|
||||
std::vector<AlsaDeviceInfo> result;
|
||||
|
||||
int cardNum = -1; // Start with first card
|
||||
int err;
|
||||
|
||||
std::vector<ProcAlsaDevice> procAlsaDevices = getProcAlsaDevices();
|
||||
|
||||
|
||||
snd_pcm_info_t *pcminfo = nullptr;
|
||||
snd_pcm_info_alloca(&pcminfo);
|
||||
|
||||
for (const auto &procAlsaDevice: procAlsaDevices)
|
||||
{
|
||||
std::stringstream ss;
|
||||
@@ -198,13 +201,60 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
|
||||
err = snd_ctl_card_info(hDevice, alsaInfo);
|
||||
if (err == 0)
|
||||
{
|
||||
|
||||
AlsaDeviceInfo info;
|
||||
info.cardId_ = cardNum;
|
||||
info.cardId_ = procAlsaDevice.cardId;
|
||||
info.id_ = std::string("hw:") + snd_ctl_card_info_get_id(alsaInfo);
|
||||
const char *driver = snd_ctl_card_info_get_driver(alsaInfo);
|
||||
(void)driver;
|
||||
|
||||
info.name_ = snd_ctl_card_info_get_name(alsaInfo);
|
||||
snd_pcm_info_set_device(pcminfo, procAlsaDevice.subdeviceId);
|
||||
snd_pcm_info_set_subdevice(pcminfo, 0);
|
||||
snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK);
|
||||
|
||||
if ((err = snd_ctl_pcm_info(hDevice, pcminfo)) < 0) {
|
||||
snd_pcm_info_set_device(pcminfo, procAlsaDevice.subdeviceId);
|
||||
snd_pcm_info_set_subdevice(pcminfo, 0);
|
||||
snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_CAPTURE);
|
||||
|
||||
if ((err = snd_ctl_pcm_info(hDevice, pcminfo)) < 0) {
|
||||
if (err != -ENOENT)
|
||||
Lv2Log::warning("control digital audio info (%i): %s", info.cardId_, snd_strerror(err));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/*
|
||||
Names and IDs are f-ed up in ALSA. id seems to be vaguely reliable. names are garbage!
|
||||
Sample data from Unbuntu 24.04.
|
||||
|
||||
snd_ctl_card_info_get_id(info) snd_ctl_card_info_get_name(alsaInfo) subdevice snd_pcm_info_get_id(pcminfo) snd_pcm_info_get_name(pcminfo)
|
||||
Generic HD-Audio Generic 3 HDMI 0 Acer S231HL
|
||||
Generic HD-Audio Generic 3 HDMI 0 ASUS MG28U
|
||||
Generic_1 HD-Audio Generic 0 CX11970 Analog CX11970 Analog
|
||||
M2 M2 USB Audio USB Audio
|
||||
CODEC USB AUDIO CODEC USB Audio USB Audio
|
||||
|
||||
*/
|
||||
std::string name;
|
||||
{
|
||||
// let's assume (without evidence) that names get localized, but ids do not. :-/
|
||||
std::string cardId = snd_ctl_card_info_get_id(alsaInfo);
|
||||
std::string cardName = snd_ctl_card_info_get_name(alsaInfo);
|
||||
std::string pcmId = snd_pcm_info_get_id(pcminfo);
|
||||
std::string pcmName = snd_pcm_info_get_name(pcminfo);
|
||||
std::string driver = snd_ctl_card_info_get_driver(alsaInfo);
|
||||
if (driver == "USB-Audio")
|
||||
{
|
||||
name = SS("USB - " << cardId);
|
||||
}
|
||||
else if (pcmId == pcmName)
|
||||
{
|
||||
name = pcmId;
|
||||
} else {
|
||||
name = SS(pcmId << '[' << pcmName << ']');
|
||||
}
|
||||
}
|
||||
info.name_ = name;
|
||||
info.longName_ = snd_ctl_card_info_get_longname(alsaInfo);
|
||||
|
||||
// we can't read our own device if it's open so use data that gets
|
||||
@@ -219,6 +269,8 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
|
||||
result.push_back(cachedInfo);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
snd_pcm_t *captureDevice = nullptr;
|
||||
snd_pcm_t *playbackDevice = nullptr;
|
||||
auto rc = snd_pcm_open(&captureDevice, cardId.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
|
||||
@@ -248,6 +300,7 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
|
||||
|
||||
if (captureOk || playbackOk)
|
||||
{
|
||||
|
||||
snd_pcm_t *hDevice = captureOk ? captureDevice : playbackDevice;
|
||||
snd_pcm_hw_params_t *params = nullptr;
|
||||
err = snd_pcm_hw_params_malloc(¶ms);
|
||||
@@ -637,6 +690,17 @@ AlsaMidiDeviceInfo::AlsaMidiDeviceInfo(const char *name, const char *description
|
||||
this->subdevice_ = subdevice;
|
||||
}
|
||||
|
||||
std::unique_ptr<PiPedalAlsaDevices> PiPedalAlsaDevices::instance_;
|
||||
|
||||
PiPedalAlsaDevices&PiPedalAlsaDevices::instance() {
|
||||
if (!instance_)
|
||||
{
|
||||
instance_ = std::unique_ptr<PiPedalAlsaDevices>(new PiPedalAlsaDevices());
|
||||
}
|
||||
return (*instance_);
|
||||
}
|
||||
|
||||
|
||||
JSON_MAP_BEGIN(AlsaDeviceInfo)
|
||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, cardId)
|
||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, id)
|
||||
|
||||
@@ -71,12 +71,16 @@ namespace pipedal {
|
||||
};
|
||||
|
||||
class PiPedalAlsaDevices {
|
||||
|
||||
private:
|
||||
static std::unique_ptr<PiPedalAlsaDevices> instance_;
|
||||
PiPedalAlsaDevices() { }
|
||||
std::map<std::string,AlsaDeviceInfo> cachedDevices;
|
||||
|
||||
bool getCachedDevice(const std::string&name, AlsaDeviceInfo*pResult);
|
||||
void cacheDevice(const std::string&name, const AlsaDeviceInfo&deviceInfo);
|
||||
|
||||
public:
|
||||
static PiPedalAlsaDevices&instance();
|
||||
|
||||
std::vector<AlsaDeviceInfo> GetAlsaDevices();
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ using namespace std;
|
||||
static void DiscoveryTest()
|
||||
{
|
||||
cout << "--- Discovery" << endl;
|
||||
PiPedalAlsaDevices devices;
|
||||
PiPedalAlsaDevices &devices = PiPedalAlsaDevices::instance();
|
||||
auto result = devices.GetAlsaDevices();
|
||||
std::cout << result.size() << " ALSA devices found." << std::endl;
|
||||
|
||||
|
||||
@@ -83,12 +83,9 @@ PiPedalModel::PiPedalModel()
|
||||
this->updater->Start();
|
||||
this->currentUpdateStatus = updater->GetCurrentStatus();
|
||||
this->pedalboard = Pedalboard::MakeDefault(Pedalboard::PedalboardType::MainPedalboard);
|
||||
#if JACK_HOST
|
||||
this->jackServerSettings = this->storage.GetJackServerSettings(); // to get onboarding flag.
|
||||
this->jackServerSettings.ReadJackDaemonConfiguration();
|
||||
#else
|
||||
|
||||
this->jackServerSettings = this->storage.GetJackServerSettings();
|
||||
#endif
|
||||
|
||||
updater->SetUpdateListener(
|
||||
[this](const UpdateStatus &updateStatus)
|
||||
{
|
||||
@@ -224,21 +221,22 @@ void PiPedalModel::Init(const PiPedalConfiguration &configuration)
|
||||
|
||||
this->systemMidiBindings = storage.GetSystemMidiBindings();
|
||||
|
||||
#if JACK_HOST
|
||||
this->jackConfiguration = this->jackConfiguration.JackInitialize();
|
||||
#else
|
||||
this->jackServerSettings = storage.GetJackServerSettings();
|
||||
try
|
||||
{
|
||||
this->jackConfiguration.AlsaInitialize(jackServerSettings);
|
||||
#endif
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
this->channelRouterSettings = storage.GetChannelRouterSettings();
|
||||
pluginHost.OnConfigurationChanged(
|
||||
jackConfiguration,
|
||||
storage.GetChannelSelection());
|
||||
|
||||
}
|
||||
|
||||
|
||||
int64_t PiPedalModel::DownloadModelsFromTone3000(
|
||||
int64_t clientId,
|
||||
Tone3000DownloadType downloadType,
|
||||
@@ -271,14 +269,12 @@ int64_t PiPedalModel::DownloadModelsFromTone3000(
|
||||
return tone3000Downloader->RequestDownload(
|
||||
downloadType,
|
||||
downloadPath,
|
||||
tone3000Url
|
||||
);
|
||||
tone3000Url);
|
||||
}
|
||||
|
||||
void PiPedalModel::CancelTone3000Download(
|
||||
int64_t clientId,
|
||||
int64_t downloadHandle
|
||||
)
|
||||
int64_t downloadHandle)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
|
||||
@@ -358,7 +354,9 @@ void PiPedalModel::LoadLv2PluginInfo()
|
||||
{
|
||||
PluginPresets pluginPresets = pluginHost.GetFactoryPluginPresets(plugin->uri());
|
||||
storage.SavePluginPresets(plugin->uri(), pluginPresets);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pluginPresetIndexVersion == 0)
|
||||
{
|
||||
if (plugin->uri() == "http://two-play.com/plugins/toob-convolution-reverb" || plugin->uri() == "http://two-play.com/plugins/toob-convolution-reverb-stereo")
|
||||
@@ -371,7 +369,6 @@ void PiPedalModel::LoadLv2PluginInfo()
|
||||
plugin->uri(),
|
||||
pluginPreset);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -773,11 +770,15 @@ void PiPedalModel::SetPedalboardItemEnable(int64_t clientId, int64_t pedalItemId
|
||||
{
|
||||
this->pedalboard.SetItemEnabled(pedalItemId, enabled);
|
||||
PedalboardItem *pPedalboardItem = this->pedalboard.GetItem(pedalItemId);
|
||||
if (pPedalboardItem) {
|
||||
if (pPedalboardItem)
|
||||
{
|
||||
Lv2PluginInfo::ptr pluginInfo = GetPluginInfo(pPedalboardItem->uri());
|
||||
if (pluginInfo) {
|
||||
for (auto &port: pluginInfo->ports()) {
|
||||
if (port->is_bypass()) {
|
||||
if (pluginInfo)
|
||||
{
|
||||
for (auto &port : pluginInfo->ports())
|
||||
{
|
||||
if (port->is_bypass())
|
||||
{
|
||||
pPedalboardItem->SetControlValue(port->symbol(), enabled ? 1.0 : 0.0f);
|
||||
}
|
||||
}
|
||||
@@ -1625,7 +1626,6 @@ std::vector<AlsaSequencerPortSelection> PiPedalModel::GetAlsaSequencerPorts()
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void PiPedalModel::FireChannelRouterSettingsChanged(int64_t clientId)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||
@@ -2129,7 +2129,6 @@ std::shared_ptr<Lv2PluginInfo> PiPedalModel::GetPluginInfo(const std::string &ur
|
||||
return pluginHost.GetPluginInfo(uri);
|
||||
}
|
||||
|
||||
|
||||
void PiPedalModel::UpdateDefaults(SnapshotValue &snapshotValue, const PedalboardItem *pedalboardItem_)
|
||||
{
|
||||
std::shared_ptr<Lv2PluginInfo> pPlugin = pluginHost.GetPluginInfo(pedalboardItem_->uri());
|
||||
@@ -2166,7 +2165,8 @@ void PiPedalModel::UpdateDefaults(SnapshotValue&snapshotValue, const PedalboardI
|
||||
if (pPlugin->uri() == "http://two-play.com/plugins/toob-nam")
|
||||
{
|
||||
ControlValue *pVersion = snapshotValue.GetControlValue("version");
|
||||
if (pVersion == nullptr) {
|
||||
if (pVersion == nullptr)
|
||||
{
|
||||
ControlValue *pValue = snapshotValue.GetControlValue("inputCalibrationMode");
|
||||
if (pValue == nullptr)
|
||||
{
|
||||
@@ -2175,13 +2175,16 @@ void PiPedalModel::UpdateDefaults(SnapshotValue&snapshotValue, const PedalboardI
|
||||
}
|
||||
// convert old gate threshold to new gate threshold.
|
||||
ControlValue *pGateValue = snapshotValue.GetControlValue("gate");
|
||||
if (pGateValue) {
|
||||
if (pGateValue)
|
||||
{
|
||||
float value = pGateValue->value();
|
||||
// Is the gate disabled?
|
||||
if (value <= -100.0f)
|
||||
{
|
||||
value = -120.0f; // "disabled in new range."
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
value = value * 0.5; // correct the bug in original implementation.
|
||||
}
|
||||
snapshotValue.SetControlValue("gate", value);
|
||||
@@ -2245,7 +2248,8 @@ void PiPedalModel::UpdateDefaults(PedalboardItem *pedalboardItem, std::unordered
|
||||
if (pPlugin->uri() == "http://two-play.com/plugins/toob-nam")
|
||||
{
|
||||
ControlValue *pVersion = pedalboardItem->GetControlValue("version");
|
||||
if (pVersion == nullptr) {
|
||||
if (pVersion == nullptr)
|
||||
{
|
||||
ControlValue *pValue = pedalboardItem->GetControlValue("inputCalibrationMode");
|
||||
if (pValue == nullptr)
|
||||
{
|
||||
@@ -2254,13 +2258,16 @@ void PiPedalModel::UpdateDefaults(PedalboardItem *pedalboardItem, std::unordered
|
||||
}
|
||||
// convert old gate threshold to new gate threshold.
|
||||
ControlValue *pGateValue = pedalboardItem->GetControlValue("gate");
|
||||
if (pGateValue) {
|
||||
if (pGateValue)
|
||||
{
|
||||
float value = pGateValue->value();
|
||||
// Is the gate disabled?
|
||||
if (value <= -100.0f)
|
||||
{
|
||||
value = -120.0f; // "disabled in new range."
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
value = value * 0.5; // correct the bug in original implementation.
|
||||
}
|
||||
pedalboardItem->SetControlValue("gate", value);
|
||||
@@ -2284,12 +2291,14 @@ void PiPedalModel::UpdateDefaults(PedalboardItem *pedalboardItem, std::unordered
|
||||
{
|
||||
pedalboardItem->controlValues().push_back(
|
||||
pipedal::ControlValue(port->symbol().c_str(), value));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
pValue->value(value);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ControlValue *pValue = pedalboardItem->GetControlValue(port->symbol());
|
||||
if (pValue == nullptr)
|
||||
{
|
||||
@@ -2357,7 +2366,9 @@ void PiPedalModel::UpdateDefaults(Snapshot *snapshot, std::unordered_map<int64_t
|
||||
// plugin is no longer present. Remove from the snapshot.
|
||||
snapshot->values_.erase(snapshot->values_.begin() + i);
|
||||
--i;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateDefaults(value, f->second);
|
||||
}
|
||||
}
|
||||
@@ -3376,7 +3387,6 @@ std::vector<PresetIndexEntry> PiPedalModel::RequestBankPresets(int64_t bankInsta
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
|
||||
return storage.RequestBankPresets(bankInstanceId);
|
||||
|
||||
}
|
||||
|
||||
int64_t PiPedalModel::ImportPresetsFromBank(int64_t bankInstanceId, const std::vector<int64_t> &presets)
|
||||
@@ -3386,17 +3396,14 @@ int64_t PiPedalModel::ImportPresetsFromBank(int64_t bankInstanceId, const std::v
|
||||
|
||||
FirePresetsChanged(-1);
|
||||
return lastAdded;
|
||||
|
||||
}
|
||||
int64_t PiPedalModel::CopyPresetsToBank(int64_t bankInstanceId, const std::vector<int64_t> &presets)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
uint64_t lastAdded = storage.CopyPresetsToBank(bankInstanceId, presets);
|
||||
return lastAdded;
|
||||
|
||||
}
|
||||
|
||||
|
||||
ChannelRouterSettings::ptr PiPedalModel::GetChannelRouterSettings()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
@@ -3415,11 +3422,8 @@ void PiPedalModel::SetChannelRouterSettings(int64_t clientId,ChannelRouterSettin
|
||||
RestartAudio(); // no lock to avoid mutex deadlock when reader thread is sending notifications..
|
||||
|
||||
this->FireChannelRouterSettingsChanged(clientId);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string PiPedalModel::Tone3000ThumbnailDirectory()
|
||||
{
|
||||
return "/var/pipedal/tone3000_thumbnails";
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace pipedal
|
||||
std::unique_ptr<AvahiService> avahiService;
|
||||
uint16_t webPort;
|
||||
|
||||
PiPedalAlsaDevices alsaDevices;
|
||||
PiPedalAlsaDevices &alsaDevices = PiPedalAlsaDevices::instance();
|
||||
std::recursive_mutex mutex;
|
||||
|
||||
AdminClient adminClient;
|
||||
|
||||
@@ -45,11 +45,19 @@ namespace pipedal
|
||||
uint8_t cc2_;
|
||||
};
|
||||
|
||||
struct Lv2RoutingInserts {
|
||||
|
||||
Lv2Pedalboard*mainInserts = nullptr;
|
||||
Lv2Pedalboard*auxInserts = nullptr;;
|
||||
};
|
||||
|
||||
enum class RingBufferCommand : int64_t
|
||||
{
|
||||
Invalid = 0,
|
||||
ReplaceEffect,
|
||||
EffectReplaced,
|
||||
ReplaceRoutingInserts,
|
||||
RoutingInsertsReplaced,
|
||||
SetValue,
|
||||
SetBypass,
|
||||
// AudioStopped,
|
||||
@@ -539,16 +547,28 @@ namespace pipedal
|
||||
write(RingBufferCommand::ReplaceEffect, pedalboard);
|
||||
}
|
||||
|
||||
void EffectReplaced(Lv2Pedalboard *pedalboard)
|
||||
{
|
||||
write(RingBufferCommand::EffectReplaced, pedalboard);
|
||||
}
|
||||
|
||||
void ReplaceRoutingInserts(Lv2RoutingInserts routingInserts)
|
||||
{
|
||||
write(RingBufferCommand::ReplaceRoutingInserts, routingInserts);
|
||||
}
|
||||
|
||||
void RoutingInsertsReplaced(Lv2RoutingInserts routingInserts)
|
||||
{
|
||||
write(RingBufferCommand::ReplaceRoutingInserts, routingInserts);
|
||||
}
|
||||
|
||||
|
||||
void AudioTerminatedAbnormally()
|
||||
{
|
||||
AudioStoppedBody body;
|
||||
write(RingBufferCommand::AudioTerminatedAbnormally, body);
|
||||
}
|
||||
|
||||
void EffectReplaced(Lv2Pedalboard *pedalboard)
|
||||
{
|
||||
write(RingBufferCommand::EffectReplaced, pedalboard);
|
||||
}
|
||||
|
||||
void FreeSnapshot(IndexedSnapshot *snapshot)
|
||||
{
|
||||
|
||||
@@ -1931,14 +1931,13 @@ pipedal::JackServerSettings Storage::GetJackServerSettings()
|
||||
result.GetAlsaInputDevice().empty() &&
|
||||
result.GetAlsaOutputDevice().empty())
|
||||
{
|
||||
result.SetAlsaInputDevice(result.GetLegacyAlsaDevice());
|
||||
result.SetAlsaOutputDevice(result.GetLegacyAlsaDevice());
|
||||
std::string legacyDeviceId = result.GetLegacyAlsaDevice();
|
||||
result.SetAlsaInputDevice(legacyDeviceId, "");
|
||||
result.SetAlsaOutputDevice(legacyDeviceId, "");
|
||||
result.SetLegacyAlsaDevice("");
|
||||
}
|
||||
result.FixUpDeviceNames();
|
||||
}
|
||||
#if JACK_HOST
|
||||
result.Initialize();
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1952,9 +1951,6 @@ void Storage::SetJackServerSettings(const pipedal::JackServerSettings &jackConfi
|
||||
json_writer writer(f, false);
|
||||
writer.write(jackConfiguration);
|
||||
}
|
||||
#if JACK_HOST
|
||||
jackConfiguration.Write();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Storage::SetSystemMidiBindings(const std::vector<MidiBinding> &bindings)
|
||||
|
||||
@@ -206,6 +206,7 @@ static void print_device_info(snd_ctl_t* handle, int card, int device) {
|
||||
// Print basic info
|
||||
std::cout << "\n Subdevice: " << name << " - " << device_name << std::endl;
|
||||
std::cout << " ID: " << snd_pcm_info_get_id(pcminfo) << std::endl;
|
||||
std::cout << " Name: " << snd_pcm_info_get_name(pcminfo) << std::endl;
|
||||
std::cout << " Capabilities:";
|
||||
if (has_playback) std::cout << " PLAYBACK";
|
||||
if (has_capture) std::cout << " CAPTURE";
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
A way to force input to mono?
|
||||
Cab I/Rs: gear="ir"
|
||||
summaries for Mono audio adapaters.
|
||||
Check PWA on windows, edge and chrome.
|
||||
Make sure Pi 4 default audio device is still hidden when selecting devices!
|
||||
|
||||
A final solution for rate-limiting on Tone3000.
|
||||
|
||||
A way to force input to mono?
|
||||
|
||||
|
||||
// yyy: only if the property changed!.
|
||||
Tooltip on File Property Select: add to release notes.
|
||||
sAFEfILEnAMES: RENAME, &C. Safe bank names?
|
||||
NEW BANK
|
||||
WINDOW SIZE CALCUALTIONS ARE WRONG ON LINUX HIDEF DISPLAYS.
|
||||
|
||||
ls
|
||||
Carla Project icons.
|
||||
check reload after change of LV2 plugins.
|
||||
|
||||
Exactly one underrun per seek in Toob Player
|
||||
|
||||
|
||||
json "skip" code doesn't work with complex objects (MidiChannelBinding specifically).
|
||||
json nan/inf is fatal.
|
||||
|
||||
- pipewire aux in?
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 401 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 27 KiB |
@@ -1,6 +1,14 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"short_name": "PiPedal",
|
||||
"name": "PiPedal",
|
||||
"author": "Robin E.R. Davies",
|
||||
"description": "A web-based client for the PiPedal guitar effects processor.",
|
||||
"developer": {
|
||||
"name": "Robin E.R. Davies",
|
||||
"url": "https://rerdavies.github.io"
|
||||
},
|
||||
"homepage_url": "https://rerdavies.github.io/pipedal",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
@@ -20,5 +28,12 @@
|
||||
],
|
||||
"start_url": ".",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
"display": "standalone",
|
||||
"display_override": [
|
||||
"fullscreen",
|
||||
"standalone",
|
||||
"minimal-ui",
|
||||
"browser"
|
||||
],
|
||||
"background_color": "#000000"
|
||||
}
|
||||
@@ -50,3 +50,4 @@ input[type=number] {
|
||||
img {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,16 @@ import { Pedalboard, ControlValue } from "./Pedalboard.tsx";
|
||||
import JackConfiguration from "./Jack.tsx";
|
||||
|
||||
|
||||
export const NONE_CHANNEL: number = -1;
|
||||
// valid for Aux channel input only.
|
||||
export const MAIN_OUT_LEFT_CHANNEL: number = -2;
|
||||
// valid for Aux channel input only.
|
||||
export const MAIN_OUT_RIGHT_CHANNEL: number = -3;
|
||||
|
||||
|
||||
function isActiveChannel(channels: number[]): boolean {
|
||||
if (channels.length < 2) return false;
|
||||
return channels[1] >= 0 || channels[0] >= 0;
|
||||
return channels[1] !== -1 || channels[0] !== -1;
|
||||
}
|
||||
|
||||
function chName(ch: number): string {
|
||||
@@ -14,13 +21,16 @@ function chName(ch: number): string {
|
||||
}
|
||||
return "Ch" + (ch + 1);
|
||||
}
|
||||
function channelPairName(channels: number[], maxChannels: number): string {
|
||||
function channelPairName(channels: number[], maxChannels: number, input: boolean): string {
|
||||
if (channels.length !== 2) {
|
||||
return "Invalid";
|
||||
}
|
||||
if (channels[0] === -1 && channels[1] === -1) {
|
||||
return "None";
|
||||
}
|
||||
if (maxChannels === 1) {
|
||||
return input ? "Input": "Output";
|
||||
}
|
||||
|
||||
if (maxChannels === 2) {
|
||||
if (channels[0] === 0 && channels[1] === 1) {
|
||||
@@ -36,13 +46,28 @@ function channelPairName(channels: number[], maxChannels: number): string {
|
||||
return "Right,Left";
|
||||
}
|
||||
}
|
||||
return chName(channels[0]) + "," + chName(channels[1]);
|
||||
if (channels[0] === channels[1]) {
|
||||
return chName(channels[0]);
|
||||
}
|
||||
return '[' + chName(channels[0]) + " " + chName(channels[1]) + "]";
|
||||
}
|
||||
|
||||
|
||||
function arrayEquals(a: number[], b: number[]): boolean {
|
||||
if (a.length !== b.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export default class ChannelRouterSettings {
|
||||
configured: boolean = false;
|
||||
modified: boolean = false;
|
||||
channelRouterPresetId: number = -1;
|
||||
|
||||
mainInputChannels: number[] = [1, 1];
|
||||
@@ -61,6 +86,7 @@ export default class ChannelRouterSettings {
|
||||
|
||||
deserialize(obj: any): ChannelRouterSettings {
|
||||
this.configured = obj.configured;
|
||||
this.modified = obj.modified;
|
||||
this.channelRouterPresetId = obj.channelRouterPresetId ? obj.channelRouterPresetId : -1;
|
||||
this.mainInputChannels = obj.mainInputChannels.slice();
|
||||
this.mainOutputChannels = obj.mainOutputChannels.slice();
|
||||
@@ -73,6 +99,9 @@ export default class ChannelRouterSettings {
|
||||
this.controlValues = ControlValue.deserializeArray(obj.controlValues);
|
||||
return this;
|
||||
}
|
||||
clone() : ChannelRouterSettings {
|
||||
return new ChannelRouterSettings().deserialize(this);
|
||||
}
|
||||
getControlValue(symbol: string): number | null {
|
||||
for (let control of this.controlValues) {
|
||||
if (control.key === symbol) {
|
||||
@@ -111,16 +140,21 @@ export default class ChannelRouterSettings {
|
||||
}
|
||||
let nInputs = jackConfiguration.inputAudioPorts.length;
|
||||
let nOutputs = jackConfiguration.outputAudioPorts.length;
|
||||
let description = channelPairName(this.mainInputChannels, nInputs) + " -> " +
|
||||
channelPairName(this.mainOutputChannels, nOutputs);
|
||||
let description = channelPairName(this.mainInputChannels, nInputs,true) + " -> " +
|
||||
channelPairName(this.mainOutputChannels, nOutputs,false);
|
||||
if (isActiveChannel(this.auxInputChannels) && isActiveChannel(this.auxOutputChannels))
|
||||
{
|
||||
description += " " + "+ re-amp: " + channelPairName(this.auxInputChannels, nInputs) + " -> " +
|
||||
channelPairName(this.auxOutputChannels, nOutputs);
|
||||
if (arrayEquals(this.mainInputChannels, this.auxInputChannels))
|
||||
{
|
||||
description += ", re-amp -> " + channelPairName(this.auxOutputChannels,nOutputs, false);
|
||||
} else {
|
||||
description += " " + ", aux: " + channelPairName(this.auxInputChannels, nInputs,true) + " -> " +
|
||||
channelPairName(this.auxOutputChannels, nOutputs,false);
|
||||
}
|
||||
}
|
||||
if (isActiveChannel(this.sendInputChannels) && isActiveChannel(this.sendOutputChannels)) {
|
||||
description += " " + "+ send: " +channelPairName(this.sendOutputChannels, nOutputs)
|
||||
+ " -> " + channelPairName(this.sendInputChannels, nInputs);
|
||||
description += " " + ", send: " +channelPairName(this.sendOutputChannels, nOutputs, false)
|
||||
+ " -> " + channelPairName(this.sendInputChannels, nInputs,true);
|
||||
}
|
||||
return description;
|
||||
}
|
||||
@@ -128,6 +162,7 @@ export default class ChannelRouterSettings {
|
||||
return jackConfiguration.isValid;
|
||||
}
|
||||
|
||||
|
||||
isValid(jackConfiguration: JackConfiguration): boolean {
|
||||
if (!this.configured) {
|
||||
return false;
|
||||
@@ -176,5 +211,11 @@ export default class ChannelRouterSettings {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
isAuxActive() : boolean {
|
||||
return isActiveChannel(this.auxInputChannels) && isActiveChannel(this.auxOutputChannels);
|
||||
}
|
||||
isSendActive() : boolean{
|
||||
return isActiveChannel(this.sendInputChannels) && isActiveChannel(this.sendOutputChannels);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,20 +31,22 @@ import MenuItem from '@mui/material/MenuItem';
|
||||
import ChannelRouterSettingsHelpDialog from './ChannelRouterSettingsHelpDialog';
|
||||
import SpeakerIcon from '@mui/icons-material/Speaker';
|
||||
import MicIcon from '@mui/icons-material/Mic';
|
||||
import SaveIconOutline from '@mui/icons-material/Save';
|
||||
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import ChannelRouterSettings from './ChannelRouterSettings';
|
||||
import { Pedalboard, ControlValue } from './Pedalboard';
|
||||
import ChannelRouterSettings, { MAIN_OUT_LEFT_CHANNEL, MAIN_OUT_RIGHT_CHANNEL } from './ChannelRouterSettings';
|
||||
import { Pedalboard } from './Pedalboard';
|
||||
|
||||
import DialogEx from './DialogEx';
|
||||
|
||||
import { PiPedalModelFactory } from './PiPedalModel';
|
||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
import { JackConfiguration } from './Jack';
|
||||
|
||||
|
||||
let debugInputChannels: number | null = 2;
|
||||
let debugOutputChannels: number | null = 4;
|
||||
let debugInputChannels: number | null = 1;
|
||||
let debugOutputChannels: number | null = 1;
|
||||
|
||||
|
||||
export interface ChannelRouterSettingsDialogProps {
|
||||
@@ -65,27 +67,31 @@ interface ChannelRouterSettingsPreset {
|
||||
}
|
||||
|
||||
|
||||
let channelRouterPresets: ChannelRouterSettingsPreset[] = [
|
||||
{
|
||||
id: -2, name: "Left -> Left", settings: new ChannelRouterSettings().deserialize({
|
||||
let DEFAULT_MONO_TO_MONO_ROUTING_PRESET = -2; // corresponds to Input->Output.
|
||||
let DEFAULT_LEFT_TO_STEREO_ROUTING_PRESET = -3; // corresponds to Input->Stereo out.
|
||||
let DEFAULT_RIGHT_TO_STEREO_ROUTING_PRESET = -4; // corresponds to Right->Stereo out.
|
||||
|
||||
let factoryRouterPresets: ChannelRouterSettings[] = [
|
||||
// Input->Output
|
||||
new ChannelRouterSettings().deserialize({
|
||||
configured: true,
|
||||
channelRouterPresetId: -2,
|
||||
mainInputChannels: [0, 0],
|
||||
mainOutputChannels: [0, 0],
|
||||
mainInputChannels: [0, -1],
|
||||
mainOutputChannels: [0, -1],
|
||||
mainInserts: new Pedalboard(),
|
||||
auxInputChannels: [-1, -1],
|
||||
auxOutputChannels: [-1, -1],
|
||||
auxInserts: new Pedalboard(),
|
||||
sendInputChannels: [-1, -1],
|
||||
sendOutputChannels: [-1, -1],
|
||||
controlValues: [],
|
||||
controlValues: []
|
||||
})
|
||||
},
|
||||
{
|
||||
id: -3, name: "Left -> Stereo", settings: new ChannelRouterSettings().deserialize({
|
||||
,
|
||||
// Input->Stereo out.
|
||||
new ChannelRouterSettings().deserialize({
|
||||
configured: true,
|
||||
channelRouterPresetId: -3,
|
||||
mainInputChannels: [0, 0],
|
||||
mainInputChannels: [0, -1],
|
||||
mainOutputChannels: [0, 1],
|
||||
mainInserts: new Pedalboard(),
|
||||
auxInputChannels: [-1, -1],
|
||||
@@ -93,14 +99,14 @@ let channelRouterPresets: ChannelRouterSettingsPreset[] = [
|
||||
auxInserts: new Pedalboard(),
|
||||
sendInputChannels: [-1, -1],
|
||||
sendOutputChannels: [-1, -1],
|
||||
controlValues: [],
|
||||
controlValues: []
|
||||
})
|
||||
},
|
||||
{
|
||||
id: -4, name: "Right -> Stereo", settings: new ChannelRouterSettings().deserialize({
|
||||
,
|
||||
// Right->Stereo out.
|
||||
new ChannelRouterSettings().deserialize({
|
||||
configured: true,
|
||||
channelRouterPresetId: -4,
|
||||
mainInputChannels: [1, 1],
|
||||
mainInputChannels: [1,-1],
|
||||
mainOutputChannels: [0, 1],
|
||||
mainInserts: new Pedalboard(),
|
||||
auxInputChannels: [-1, -1],
|
||||
@@ -110,73 +116,109 @@ let channelRouterPresets: ChannelRouterSettingsPreset[] = [
|
||||
sendOutputChannels: [-1, -1],
|
||||
controlValues: [],
|
||||
})
|
||||
},
|
||||
{
|
||||
id: -5, name: "Right -> Left + re-amp", settings: new ChannelRouterSettings().deserialize({
|
||||
,
|
||||
new ChannelRouterSettings().deserialize({
|
||||
configured: true,
|
||||
channelRouterPresetId: -5,
|
||||
mainInputChannels: [1, 1],
|
||||
mainOutputChannels: [0, 0],
|
||||
mainInputChannels: [1,-1],
|
||||
mainOutputChannels: [0, -1],
|
||||
mainInserts: new Pedalboard(),
|
||||
auxInputChannels: [1, 1],
|
||||
auxOutputChannels: [1, 1],
|
||||
auxInputChannels: [1,-1],
|
||||
auxOutputChannels: [1,-1],
|
||||
auxInserts: new Pedalboard(),
|
||||
sendInputChannels: [-1, -1],
|
||||
sendOutputChannels: [-1, -1],
|
||||
controlValues: [],
|
||||
})
|
||||
},
|
||||
{
|
||||
id: -6, name: "Left -> Left + send", settings: new ChannelRouterSettings().deserialize({
|
||||
,
|
||||
new ChannelRouterSettings().deserialize({
|
||||
configured: true,
|
||||
channelRouterPresetId: -6,
|
||||
mainInputChannels: [0, 0],
|
||||
mainOutputChannels: [0, 0],
|
||||
mainInputChannels: [1,-1],
|
||||
mainOutputChannels: [1,-1],
|
||||
mainInserts: new Pedalboard(),
|
||||
auxInputChannels: [-1, -1],
|
||||
auxOutputChannels: [-1, - 1],
|
||||
auxInserts: new Pedalboard(),
|
||||
sendInputChannels: [1, 1],
|
||||
sendOutputChannels: [1, 1],
|
||||
controlValues: [],
|
||||
})
|
||||
},
|
||||
{
|
||||
id: -7, name: "Right -> Right + send", settings: new ChannelRouterSettings().deserialize({
|
||||
configured: true,
|
||||
channelRouterPresetId: -7,
|
||||
mainInputChannels: [1, 1],
|
||||
mainOutputChannels: [1, 1],
|
||||
mainInserts: new Pedalboard(),
|
||||
auxInputChannels: [-1, -1],
|
||||
auxOutputChannels: [-1, - 1],
|
||||
auxInserts: new Pedalboard(),
|
||||
sendInputChannels: [0, 0],
|
||||
sendOutputChannels: [0, 0],
|
||||
controlValues: [],
|
||||
})
|
||||
},
|
||||
{
|
||||
id: -8, name: "Left -> Stereo + re-amp", settings: new ChannelRouterSettings().deserialize({
|
||||
configured: true,
|
||||
channelRouterPresetId: -8,
|
||||
mainInputChannels: [0, 0],
|
||||
mainOutputChannels: [0, 1],
|
||||
mainInserts: new Pedalboard(),
|
||||
auxInputChannels: [0, 0],
|
||||
auxOutputChannels: [2, 2],
|
||||
auxInputChannels: [0, -1],
|
||||
auxOutputChannels: [0, -1],
|
||||
auxInserts: new Pedalboard(),
|
||||
sendInputChannels: [-1, -1],
|
||||
sendOutputChannels: [-1, -1],
|
||||
controlValues: [],
|
||||
|
||||
})
|
||||
},
|
||||
{
|
||||
id: -9, name: "Left -> Stereo + stereo send", settings: new ChannelRouterSettings().deserialize({
|
||||
,
|
||||
new ChannelRouterSettings().deserialize({
|
||||
configured: true,
|
||||
channelRouterPresetId: -7,
|
||||
mainInputChannels: [1,-1],
|
||||
mainOutputChannels: [0, -1],
|
||||
mainInserts: new Pedalboard(),
|
||||
auxInputChannels: [0, -1],
|
||||
auxOutputChannels: [1,-1],
|
||||
auxInserts: new Pedalboard(),
|
||||
sendInputChannels: [-1, -1],
|
||||
sendOutputChannels: [-1, -1],
|
||||
controlValues: []
|
||||
})
|
||||
,
|
||||
new ChannelRouterSettings().deserialize({
|
||||
configured: true,
|
||||
channelRouterPresetId: -8,
|
||||
mainInputChannels: [0, -1],
|
||||
mainOutputChannels: [0, -1],
|
||||
mainInserts: new Pedalboard(),
|
||||
auxInputChannels: [1,-1],
|
||||
auxOutputChannels: [1,-1],
|
||||
auxInserts: new Pedalboard(),
|
||||
sendInputChannels: [-1, -1],
|
||||
sendOutputChannels: [-1, -1],
|
||||
controlValues: []
|
||||
})
|
||||
,
|
||||
new ChannelRouterSettings().deserialize({
|
||||
configured: true,
|
||||
channelRouterPresetId: -9,
|
||||
mainInputChannels: [0, 0],
|
||||
mainInputChannels: [0, -1],
|
||||
mainOutputChannels: [0, -1],
|
||||
mainInserts: new Pedalboard(),
|
||||
auxInputChannels: [-1, -1],
|
||||
auxOutputChannels: [-1, -1],
|
||||
auxInserts: new Pedalboard(),
|
||||
sendInputChannels: [1,-1],
|
||||
sendOutputChannels: [1,-1],
|
||||
controlValues: []
|
||||
})
|
||||
,
|
||||
new ChannelRouterSettings().deserialize({
|
||||
configured: true,
|
||||
channelRouterPresetId: -10,
|
||||
mainInputChannels: [1,-1],
|
||||
mainOutputChannels: [1,-1],
|
||||
mainInserts: new Pedalboard(),
|
||||
auxInputChannels: [-1, -1],
|
||||
auxOutputChannels: [-1, -1],
|
||||
auxInserts: new Pedalboard(),
|
||||
sendInputChannels: [0, -1],
|
||||
sendOutputChannels: [0, -1],
|
||||
controlValues: []
|
||||
})
|
||||
,
|
||||
new ChannelRouterSettings().deserialize({
|
||||
configured: true,
|
||||
channelRouterPresetId: -11,
|
||||
mainInputChannels: [0, -1],
|
||||
mainOutputChannels: [0, 1],
|
||||
mainInserts: new Pedalboard(),
|
||||
auxInputChannels: [0, -1],
|
||||
auxOutputChannels: [2, -1],
|
||||
auxInserts: new Pedalboard(),
|
||||
sendInputChannels: [-1, -1],
|
||||
sendOutputChannels: [-1, -1],
|
||||
controlValues: []
|
||||
})
|
||||
,
|
||||
new ChannelRouterSettings().deserialize({
|
||||
configured: true,
|
||||
channelRouterPresetId: -12,
|
||||
mainInputChannels: [0, -1],
|
||||
mainOutputChannels: [0, 1],
|
||||
mainInserts: new Pedalboard(),
|
||||
auxInputChannels: [-1, -1],
|
||||
@@ -184,10 +226,9 @@ let channelRouterPresets: ChannelRouterSettingsPreset[] = [
|
||||
auxInserts: new Pedalboard(),
|
||||
sendInputChannels: [2, 3],
|
||||
sendOutputChannels: [2, 3],
|
||||
controlValues: [],
|
||||
|
||||
controlValues: []
|
||||
})
|
||||
},
|
||||
,
|
||||
];
|
||||
|
||||
let cellPortraitInOutDiv: React.CSSProperties = {
|
||||
@@ -248,41 +289,132 @@ let cellPortraitInOut: React.CSSProperties = {
|
||||
verticalAlign: "middle"
|
||||
};
|
||||
|
||||
function MakeChannelMenu(channelCount: number): React.ReactElement[] {
|
||||
|
||||
function normalizeChannelSelection(channelSelection: number[]) {
|
||||
if (channelSelection.length === 2) {
|
||||
if (channelSelection[0] === -1) {
|
||||
channelSelection[0] = channelSelection[1];
|
||||
channelSelection[1] = -1;
|
||||
} else {
|
||||
if (channelSelection[0] === channelSelection[1]) {
|
||||
channelSelection[1] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function makeRoutingPresets(audioConfig: JackConfiguration): ChannelRouterSettingsPreset[] {
|
||||
let result: ChannelRouterSettingsPreset[] = [];
|
||||
|
||||
for (let preset of factoryRouterPresets) {
|
||||
if (preset.isValid(audioConfig)) {
|
||||
normalizeChannelSelection(preset.mainInputChannels);
|
||||
normalizeChannelSelection(preset.mainOutputChannels);
|
||||
normalizeChannelSelection(preset.auxInputChannels);
|
||||
normalizeChannelSelection(preset.auxOutputChannels);
|
||||
normalizeChannelSelection(preset.sendInputChannels);
|
||||
normalizeChannelSelection(preset.sendOutputChannels);
|
||||
let newPreset = {
|
||||
id: preset.channelRouterPresetId,
|
||||
name: preset.getDescription(audioConfig),
|
||||
settings: preset
|
||||
};
|
||||
result.push(newPreset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function MakeChannelMenu(channelCount: number, routeType: RouteType, input: boolean, disallowedSelections: number[]): React.ReactElement[] {
|
||||
let items: React.ReactElement[] = [(<MenuItem key={-1} value={-1}>None</MenuItem>)]
|
||||
if (channelCount === 0) {
|
||||
return items;
|
||||
}
|
||||
if (channelCount === 2) {
|
||||
items.push((<MenuItem key={0} value={0}>Left</MenuItem>));
|
||||
items.push((<MenuItem key={1} value={1}>Right</MenuItem>));
|
||||
return items;
|
||||
if (channelCount === 1) {
|
||||
if (input) {
|
||||
items.push((<MenuItem key={0} value={0} disabled={disallowedSelections.includes(0)}>Input</MenuItem>));
|
||||
} else {
|
||||
items.push((<MenuItem key={0} value={0} disabled={disallowedSelections.includes(0)}>Output</MenuItem>));
|
||||
}
|
||||
} else if (channelCount === 2) {
|
||||
items.push((<MenuItem key={0} value={0} disabled={disallowedSelections.includes(0)}>Left</MenuItem>));
|
||||
items.push((<MenuItem key={1} value={1} disabled={disallowedSelections.includes(1)}>Right</MenuItem>));
|
||||
return items;
|
||||
} else {
|
||||
for (let i = 0; i < channelCount; ++i) {
|
||||
items.push((<MenuItem key={i} value={i}>Ch {i + 1}</MenuItem>));
|
||||
items.push((<MenuItem key={i} value={i} disabled={disallowedSelections.includes(i)}>Ch {i + 1}</MenuItem>));
|
||||
}
|
||||
}
|
||||
if (routeType === RouteType.Aux && input) {
|
||||
items.push((<MenuItem key={MAIN_OUT_LEFT_CHANNEL} value={MAIN_OUT_LEFT_CHANNEL} >Main Out L</MenuItem>));
|
||||
items.push((<MenuItem key={MAIN_OUT_RIGHT_CHANNEL} value={MAIN_OUT_RIGHT_CHANNEL} >Main Out R</MenuItem>));
|
||||
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
function makeDebugConfig(baseConfig: JackConfiguration): JackConfiguration {
|
||||
let config = new JackConfiguration().deserialize(baseConfig); // clone.
|
||||
for (let i = config.inputAudioPorts.length; i < (debugInputChannels ?? 0); ++i) {
|
||||
config.inputAudioPorts.push("ch" + (i + 1));
|
||||
}
|
||||
if (config.inputAudioPorts.length > (debugInputChannels ?? 0)) {
|
||||
config.inputAudioPorts = config.inputAudioPorts.slice(0, debugInputChannels ?? 0);
|
||||
}
|
||||
for (let i = config.outputAudioPorts.length; i < (debugOutputChannels ?? 0); ++i) {
|
||||
config.outputAudioPorts.push("ch" + (i + 1));
|
||||
}
|
||||
if (config.outputAudioPorts.length > (debugOutputChannels ?? 0)) {
|
||||
config.outputAudioPorts = config.outputAudioPorts.slice(0, debugOutputChannels ?? 0);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
function getChannelRouterSettingsPresetById(presetId: number): ChannelRouterSettings {
|
||||
for (let preset of factoryRouterPresets) {
|
||||
if (preset.channelRouterPresetId === presetId) {
|
||||
return preset.clone();
|
||||
}
|
||||
}
|
||||
throw new Error("Error setting defaults. Preset not found: " + presetId);
|
||||
}
|
||||
|
||||
function GetDefaultChannelRouterSettings(model: PiPedalModel) : ChannelRouterSettings {
|
||||
let currentSettings = model.channelRouterSettings.get();
|
||||
if (currentSettings.configured) {
|
||||
return currentSettings;
|
||||
}
|
||||
// we're onboarding the user, so set an appropriate default channel routing preset based on the number of ports on the audio interface.
|
||||
|
||||
let jackSettings = model.jackConfiguration.get();
|
||||
let numInputs = jackSettings.inputAudioPorts.length;
|
||||
let numOutputs = jackSettings.outputAudioPorts.length;
|
||||
if (numInputs < 2 || numOutputs < 2) {
|
||||
currentSettings = getChannelRouterSettingsPresetById(DEFAULT_MONO_TO_MONO_ROUTING_PRESET);
|
||||
} else if (numInputs === 2 && numOutputs === 2) {
|
||||
currentSettings = getChannelRouterSettingsPresetById(DEFAULT_RIGHT_TO_STEREO_ROUTING_PRESET);
|
||||
} else {
|
||||
currentSettings = getChannelRouterSettingsPresetById(DEFAULT_LEFT_TO_STEREO_ROUTING_PRESET);
|
||||
}
|
||||
model.setChannelRouterSettings(currentSettings);
|
||||
return model.channelRouterSettings.get();
|
||||
}
|
||||
|
||||
function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
//const classes = useStyles();
|
||||
const { open, onClose } = props;
|
||||
let model = PiPedalModelFactory.getInstance();
|
||||
let config = model.jackConfiguration.get();
|
||||
if (debugInputChannels != null || debugOutputChannels != null) {
|
||||
config = makeDebugConfig(config);
|
||||
}
|
||||
let channelRouterPresets = makeRoutingPresets(config);
|
||||
|
||||
|
||||
const [settings, setSettings] = useState<ChannelRouterSettings>(model.channelRouterSettings.get());
|
||||
const [controlValues, setControlValues] = useState<ControlValue[]>(model.channelRouterControlValues.get());
|
||||
const [settings, setSettings] = useState<ChannelRouterSettings>(GetDefaultChannelRouterSettings(model));
|
||||
const [showHelp, setShowHelp] = useState<boolean>(false);
|
||||
|
||||
if (settings) {
|
||||
}
|
||||
if (controlValues) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
if (open) {
|
||||
@@ -290,13 +422,8 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
setSettings(model.channelRouterSettings.get());
|
||||
};
|
||||
model.channelRouterSettings.addOnChangedHandler(handleSettingsChanged);
|
||||
let handleControlsChanged = () => {
|
||||
setControlValues(model.channelRouterControlValues.get());
|
||||
};
|
||||
model.channelRouterControlValues.addOnChangedHandler(handleControlsChanged);
|
||||
return () => {
|
||||
model.channelRouterSettings.removeOnChangedHandler(handleSettingsChanged);
|
||||
model.channelRouterControlValues.removeOnChangedHandler(handleControlsChanged);
|
||||
}
|
||||
} else {
|
||||
return () => { };
|
||||
@@ -321,28 +448,50 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
|
||||
let value: number;
|
||||
|
||||
let disallowedSelections: number[] = [];
|
||||
|
||||
let channelSelection: number[] = [];
|
||||
switch (routeType) {
|
||||
case RouteType.Main:
|
||||
value = input ? settings.mainInputChannels[channelIndex] : settings.mainOutputChannels[channelIndex];
|
||||
channelSelection = input ? settings.mainInputChannels : settings.mainOutputChannels;
|
||||
break;
|
||||
case RouteType.Aux:
|
||||
value = input ? settings.auxInputChannels[channelIndex] : settings.auxOutputChannels[channelIndex];
|
||||
channelSelection = input ? settings.auxInputChannels : settings.auxOutputChannels;
|
||||
break;
|
||||
case RouteType.Send:
|
||||
value = input ? settings.sendInputChannels[channelIndex] : settings.sendOutputChannels[channelIndex];
|
||||
channelSelection = input ? settings.sendInputChannels : settings.sendOutputChannels;
|
||||
break;
|
||||
}
|
||||
value = channelSelection[channelIndex];
|
||||
|
||||
if (value >= channelCount) {
|
||||
value = -1;
|
||||
}
|
||||
if (routeType === RouteType.Send) {
|
||||
if (input) {
|
||||
disallowedSelections = settings.mainInputChannels.concat(settings.auxInputChannels);
|
||||
} else {
|
||||
disallowedSelections = settings.mainOutputChannels.concat(settings.auxOutputChannels);
|
||||
}
|
||||
}
|
||||
if (channelIndex === 1) {
|
||||
if (channelSelection[0] !== -1) {
|
||||
disallowedSelections.push(channelSelection[0]);
|
||||
}
|
||||
}
|
||||
let error = false;
|
||||
if (routeType === RouteType.Main && channelSelection[0] === -1 && channelSelection[1] === -1) {
|
||||
error = true;
|
||||
}
|
||||
return (
|
||||
<Select variant="standard" style={{ width: 90 }} value={value}
|
||||
<Select variant="standard" style={{ width: icon ? 130 : 110 }} value={value}
|
||||
startAdornment={
|
||||
icon ? (
|
||||
input ? (<MicIcon style={{ opacity: 0.6, width: 16, height: 16, marginRight: 4 }} />) :
|
||||
(<SpeakerIcon style={{ opacity: 0.6, width: 16, height: 16, marginRight: 4 }} />)
|
||||
) : undefined
|
||||
}
|
||||
error={error}
|
||||
onChange={(event) => {
|
||||
let newValue = event.target.value as number;
|
||||
let newSettings = new ChannelRouterSettings().deserialize(settings);
|
||||
@@ -350,32 +499,43 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
case RouteType.Main:
|
||||
if (input) {
|
||||
newSettings.mainInputChannels[channelIndex] = newValue;
|
||||
normalizeChannelSelection(newSettings.mainInputChannels);
|
||||
} else {
|
||||
newSettings.mainOutputChannels[channelIndex] = newValue;
|
||||
normalizeChannelSelection(newSettings.mainOutputChannels);
|
||||
}
|
||||
break;
|
||||
case RouteType.Aux:
|
||||
if (input) {
|
||||
newSettings.auxInputChannels[channelIndex] = newValue;
|
||||
normalizeChannelSelection(newSettings.auxInputChannels);
|
||||
} else {
|
||||
newSettings.auxOutputChannels[channelIndex] = newValue;
|
||||
normalizeChannelSelection(newSettings.auxOutputChannels);
|
||||
}
|
||||
break;
|
||||
case RouteType.Send:
|
||||
if (input) {
|
||||
newSettings.sendInputChannels[channelIndex] = newValue;
|
||||
normalizeChannelSelection(newSettings.sendInputChannels);
|
||||
|
||||
} else {
|
||||
newSettings.sendOutputChannels[channelIndex] = newValue;
|
||||
normalizeChannelSelection(newSettings.sendOutputChannels);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (newSettings.channelRouterPresetId >= 0) {
|
||||
newSettings.modified = true; // custom.
|
||||
} else {
|
||||
newSettings.modified = true;
|
||||
newSettings.channelRouterPresetId = -1; // custom.
|
||||
}
|
||||
newSettings.configured = true;
|
||||
model.setChannelRouterSettings(newSettings);
|
||||
}}
|
||||
>
|
||||
{MakeChannelMenu(channelCount)}
|
||||
{MakeChannelMenu(channelCount, routeType, input, disallowedSelections)}
|
||||
</Select>
|
||||
)
|
||||
}
|
||||
@@ -437,18 +597,32 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
|
||||
return items;
|
||||
}
|
||||
let handleSave = () => {
|
||||
|
||||
};
|
||||
|
||||
let PresetSelect = (width: number | string | undefined) => {
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "row", marginTop: 2, alignItems: "center" }}>
|
||||
<Select variant="standard" style={{ width: width }} value={settings.channelRouterPresetId}
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<IconButtonEx tooltip="Save current preset"
|
||||
style={{ flex: "0 0 auto", color: "#FFFFFF" }}
|
||||
onClick={(e) => { handleSave(); }}
|
||||
size="large">
|
||||
<SaveIconOutline style={{ opacity: 0.75 }} color="inherit" />
|
||||
</IconButtonEx>
|
||||
</div>
|
||||
|
||||
<div style={{ flex: "1 1", display: "relative" }}>
|
||||
<Select variant="standard" style={{ width: "100%" }} value={settings.channelRouterPresetId}
|
||||
onChange={(event) => {
|
||||
ApplyPresetId(event.target.value);
|
||||
}}
|
||||
>
|
||||
{GeneratePresetMenuItems()}
|
||||
</Select>
|
||||
<IconButtonEx tooltip="Presets" aria-label="more-presets">
|
||||
</div>
|
||||
<IconButtonEx tooltip="Presets" aria-label="more-presets" style={{ marginRight: 24 }}>
|
||||
<MoreVertIcon style={{ opacity: 0.6 }} onClick={() => { }} />
|
||||
</IconButtonEx>
|
||||
</div>
|
||||
@@ -457,6 +631,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
|
||||
|
||||
let LandscapeView = () => {
|
||||
let auxInsertDisabled = !settings.isAuxActive();
|
||||
return (
|
||||
<div style={{
|
||||
display: "flex", flexFlow: "row", alignItems: "center", justifyContent: "center",
|
||||
@@ -518,7 +693,11 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||
<td rowSpan={2} style={cellLeft}>
|
||||
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
||||
<Button variant="outlined" style={{
|
||||
textTransform: "none", borderRadius: 24,
|
||||
|
||||
}}
|
||||
disabled={auxInsertDisabled}>
|
||||
Inserts
|
||||
</Button>
|
||||
</td>
|
||||
@@ -574,6 +753,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
);
|
||||
};
|
||||
let PortraitView = () => {
|
||||
let auxInsertDisabled = !settings.isAuxActive();
|
||||
return (
|
||||
<div style={{
|
||||
display: "flex", flexFlow: "row", alignItems: "center", justifyContent: "center",
|
||||
@@ -685,7 +865,11 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||
<td style={cellPortraitControlStrip}>
|
||||
<div style={{ marginLeft: 8, marginRight: 8 }}>
|
||||
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
||||
<Button variant="outlined" style={{
|
||||
textTransform: "none", borderRadius: 24,
|
||||
|
||||
}}
|
||||
disabled={auxInsertDisabled}>
|
||||
Inserts
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -105,15 +105,15 @@ export default class ChannelRouterSettingsHelpDialog extends ResizeResponsiveCom
|
||||
|
||||
<DialogContent style={{ minHeight: 96 }}>
|
||||
<div style={{ fontSize: "0.9em", lineHeight: "18pt" }}>
|
||||
<p> The Mixer Routing Dialog determines how audio signals are processed and routed between input and output audio channels.
|
||||
<p> The Audio Channel Routing Dialog determines how audio signals are processed and routed between input and output audio channels.
|
||||
</p>
|
||||
<p>Guitar effects processing occurs on the <i>Main</i> route only. Plugins in the main PiPedal window are
|
||||
<p>By default, guitar effects processing occurs on the <i>Main</i> route only. Plugins in the main PiPedal window are
|
||||
applied before any insert plugins that have been added to the <i>Main</i> route. Main insert plugins are
|
||||
applied globally, regardless of the selected preset or effects in the main PiPedal window. You might
|
||||
want to add an EQ or reverb plugin here, to allow your presets to be globally adjusted to suit the room
|
||||
in which you are currently performing.
|
||||
</p>
|
||||
<p>Plugins in the main PiPedal window are not applied to the <i>Aux</i> route. The Aux route is intended to be
|
||||
<p>Plugins in the main PiPedal window are not applied to the <i>Aux</i> route by default. The Aux route is intended to be
|
||||
used for
|
||||
</p>
|
||||
<ul>
|
||||
@@ -128,7 +128,13 @@ export default class ChannelRouterSettingsHelpDialog extends ResizeResponsiveCom
|
||||
mixing it with the processed guitar signal, or passing it out on a dedicated output channel.
|
||||
</li>
|
||||
</ul>
|
||||
<p>although you may find other creative uses for the Aux channel as well.</p>
|
||||
<p>However, if "Main Out L" or "Main Out R" is selected as an Aux input channel, then the Aux route will receive the
|
||||
output of the processed Main route. Main route insrts are not applied
|
||||
to the Aux route input signal, so you can have an independent set of inserts applied to the Main and Aux output signals.
|
||||
An example of how one might use this feature: sending the output of the Main route to
|
||||
a soundboard or PA system without reverb or EQ applied; and then applying reverb and EQ only to the Aux route whose
|
||||
outputs will be used as inputs to a stage monitor or headphones for the performer.
|
||||
</p>
|
||||
<p>If the Main and Aux routes share output channels, then the results of the Main and Output signals are
|
||||
summed together on those output channels. For stereo mixing, assign the same left and right output
|
||||
channels to both the Main and Aux routes. If you have a 2x4 or 4x4 audio interface with two guitar input channels
|
||||
@@ -142,7 +148,8 @@ export default class ChannelRouterSettingsHelpDialog extends ResizeResponsiveCom
|
||||
TooB Send plugin will be sent to the external hardware effect, and the output the external hardware
|
||||
effect will be returned to PiPedal at the output of the TooB Send plugin. PiPedal currently supports
|
||||
only one Send plugin instance per preset; and the Send and Return channels may not be shared with the Main
|
||||
or Aux routes.
|
||||
or Aux routes. Note that you may only use one instance of the TooB Send plugin per preset. The TooB Send plugin
|
||||
may not be used as a Main or Aux insert effect.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ export default class JackServerSettings {
|
||||
this.rebootRequired = input.rebootRequired;
|
||||
this.alsaInputDevice = input.alsaInputDevice ?? "";
|
||||
this.alsaOutputDevice = input.alsaOutputDevice ?? "";
|
||||
this.alsaInputDeviceName = input.alsaInputDeviceName ?? this.alsaInputDevice;
|
||||
this.alsaOutputDeviceName = input.alsaOutputDeviceName ?? this.alsaOutputDevice;
|
||||
this.sampleRate = input.sampleRate;
|
||||
this.bufferSize = input.bufferSize;
|
||||
this.numberOfBuffers = input.numberOfBuffers;
|
||||
@@ -50,6 +52,8 @@ export default class JackServerSettings {
|
||||
isJackAudio = false;
|
||||
alsaInputDevice: string = "";
|
||||
alsaOutputDevice: string = "";
|
||||
alsaInputDeviceName: string = "";
|
||||
alsaOutputDeviceName: string = "";
|
||||
sampleRate = 48000;
|
||||
bufferSize = 64;
|
||||
numberOfBuffers = 3;
|
||||
@@ -74,19 +78,12 @@ export default class JackServerSettings {
|
||||
return "Not selected";
|
||||
}
|
||||
|
||||
let inDev = this.alsaInputDevice.startsWith("hw:")
|
||||
? this.alsaInputDevice.substring(3)
|
||||
: this.alsaInputDevice;
|
||||
let outDev = this.alsaOutputDevice.startsWith("hw:")
|
||||
? this.alsaOutputDevice.substring(3)
|
||||
: this.alsaOutputDevice;
|
||||
|
||||
let device: string;
|
||||
if (inDev === outDev) {
|
||||
device = inDev;
|
||||
|
||||
} else {
|
||||
device = inDev + "-> " + outDev;
|
||||
if (this.alsaInputDevice === this.alsaOutputDevice) {
|
||||
device = this.alsaInputDeviceName;;
|
||||
}
|
||||
else {
|
||||
device = this.alsaInputDeviceName + " -> " + this.alsaOutputDeviceName;
|
||||
}
|
||||
return `${device} ${this.sampleRate} ${this.bufferSize}x${this.numberOfBuffers}`;
|
||||
}
|
||||
|
||||
@@ -580,10 +580,20 @@ const JackServerSettingsDialog = withStyles(
|
||||
this.setState({ suppressDeviceWarning: checked });
|
||||
localStorage.setItem("suppressSeparateDeviceWarning", checked ? "1" : "0");
|
||||
}
|
||||
getDeviceName(deviceId: string): string {
|
||||
if (!this.state.alsaDevices) return deviceId;
|
||||
for (let i = 0; i < this.state.alsaDevices.length; ++i) {
|
||||
if (this.state.alsaDevices[i].id === deviceId) {
|
||||
return this.state.alsaDevices[i].name;
|
||||
}
|
||||
}
|
||||
return deviceId;
|
||||
}
|
||||
handleInputDeviceChanged(e: any) {
|
||||
const d = e.target.value as string;
|
||||
let s = this.state.jackServerSettings.clone();
|
||||
s.alsaInputDevice = d;
|
||||
s.alsaInputDeviceName = this.getDeviceName(d);
|
||||
s.valid = false;
|
||||
let settings = this.applyAlsaDevices(s, this.state.alsaDevices);
|
||||
this.setState({
|
||||
@@ -597,6 +607,7 @@ const JackServerSettingsDialog = withStyles(
|
||||
const d = e.target.value as string;
|
||||
let s = this.state.jackServerSettings.clone();
|
||||
s.alsaOutputDevice = d;
|
||||
s.alsaOutputDeviceName = this.getDeviceName(d);
|
||||
s.valid = false;
|
||||
let settings = this.applyAlsaDevices(s, this.state.alsaDevices);
|
||||
this.setState({
|
||||
|
||||