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
|
std::vector<float*> &channelBuffers
|
||||||
) {
|
) {
|
||||||
size_t nChannels = channelSelection.size();
|
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)
|
for (size_t i = 0; i < nChannels; ++i)
|
||||||
{
|
{
|
||||||
int64_t deviceChannel = channelSelection[i];
|
int64_t deviceChannel = channelSelection[i];
|
||||||
@@ -1932,6 +1938,16 @@ namespace pipedal
|
|||||||
) {
|
) {
|
||||||
size_t nChannels = channelSelection.size();
|
size_t nChannels = channelSelection.size();
|
||||||
|
|
||||||
|
if (nChannels == 0)
|
||||||
|
{
|
||||||
|
channelBuffers.resize(1);
|
||||||
|
if (discardOutputBuffer != nullptr)
|
||||||
|
{
|
||||||
|
discardOutputBuffer = AllocateAudioBuffer();
|
||||||
|
}
|
||||||
|
channelBuffers[0] = discardOutputBuffer;
|
||||||
|
return;
|
||||||
|
}
|
||||||
channelBuffers.resize(nChannels);
|
channelBuffers.resize(nChannels);
|
||||||
for (size_t i = 0; i < nChannels; ++i)
|
for (size_t i = 0; i < nChannels; ++i)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ public:
|
|||||||
|
|
||||||
AlsaFormatEncodeDecodeTest(this);
|
AlsaFormatEncodeDecodeTest(this);
|
||||||
|
|
||||||
JackServerSettings serverSettings("hw:M2", "hw:M2", 48000, 32, 3);
|
JackServerSettings serverSettings("hw:M2","hw:M2", 48000, 32, 3);
|
||||||
|
|
||||||
JackConfiguration jackConfiguration;
|
JackConfiguration jackConfiguration;
|
||||||
jackConfiguration.AlsaInitialize(serverSettings);
|
jackConfiguration.AlsaInitialize(serverSettings);
|
||||||
|
|||||||
@@ -533,6 +533,7 @@ private:
|
|||||||
|
|
||||||
std::vector<std::shared_ptr<Lv2Pedalboard>> activePedalboards; // pedalboards that have been sent to the audio queue.
|
std::vector<std::shared_ptr<Lv2Pedalboard>> activePedalboards; // pedalboards that have been sent to the audio queue.
|
||||||
Lv2Pedalboard *realtimeActivePedalboard = nullptr;
|
Lv2Pedalboard *realtimeActivePedalboard = nullptr;
|
||||||
|
Lv2RoutingInserts realtimeRoutingInserts;
|
||||||
|
|
||||||
uint32_t sampleRate = 0;
|
uint32_t sampleRate = 0;
|
||||||
uint64_t currentSample = 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.
|
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:
|
default:
|
||||||
throw PiPedalStateException("Unknown Ringbuffer command.");
|
throw PiPedalStateException("Unknown Ringbuffer command.");
|
||||||
}
|
}
|
||||||
@@ -1549,6 +1591,20 @@ public:
|
|||||||
hostReader.read(&body);
|
hostReader.read(&body);
|
||||||
OnActivePedalboardReleased(body.oldEffect);
|
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)
|
else if (command == RingBufferCommand::FreeSnapshot)
|
||||||
{
|
{
|
||||||
IndexedSnapshot *snapshot;
|
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)
|
virtual void SetBypass(uint64_t instanceId, bool enabled)
|
||||||
{
|
{
|
||||||
std::lock_guard guard(mutex);
|
std::lock_guard guard(mutex);
|
||||||
|
|||||||
@@ -238,6 +238,12 @@ namespace pipedal
|
|||||||
|
|
||||||
virtual void SetPedalboard(const std::shared_ptr<Lv2Pedalboard> &pedalboard) = 0;
|
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 SetControlValue(uint64_t instanceId, const std::string &symbol, float value) = 0;
|
||||||
virtual void SetInputVolume(float value) = 0;
|
virtual void SetInputVolume(float value) = 0;
|
||||||
virtual void SetOutputVolume(float value) = 0;
|
virtual void SetOutputVolume(float value) = 0;
|
||||||
|
|||||||
@@ -740,6 +740,7 @@ add_executable(pipedalconfig
|
|||||||
ModFileTypes.cpp ModFileTypes.hpp
|
ModFileTypes.cpp ModFileTypes.hpp
|
||||||
MimeTypes.cpp MimeTypes.hpp
|
MimeTypes.cpp MimeTypes.hpp
|
||||||
PiPedalConfiguration.hpp PiPedalConfiguration.cpp
|
PiPedalConfiguration.hpp PiPedalConfiguration.cpp
|
||||||
|
PiPedalAlsa.hpp PiPedalAlsa.cpp
|
||||||
JackServerSettings.hpp JackServerSettings.cpp
|
JackServerSettings.hpp JackServerSettings.cpp
|
||||||
SystemConfigFile.hpp SystemConfigFile.cpp
|
SystemConfigFile.hpp SystemConfigFile.cpp
|
||||||
WifiChannelSelectors.cpp WifiChannelSelectors.hpp
|
WifiChannelSelectors.cpp WifiChannelSelectors.hpp
|
||||||
@@ -817,6 +818,7 @@ add_executable(pipedaladmind AdminMain.cpp CommandLineParser.hpp
|
|||||||
|
|
||||||
SetWifiConfig.cpp SetWifiConfig.hpp
|
SetWifiConfig.cpp SetWifiConfig.hpp
|
||||||
JackServerSettings.hpp JackServerSettings.cpp
|
JackServerSettings.hpp JackServerSettings.cpp
|
||||||
|
PiPedalAlsa.hpp PiPedalAlsa.cpp
|
||||||
Lv2SystemdLogger.hpp Lv2SystemdLogger.cpp
|
Lv2SystemdLogger.hpp Lv2SystemdLogger.cpp
|
||||||
|
|
||||||
SystemConfigFile.hpp SystemConfigFile.cpp
|
SystemConfigFile.hpp SystemConfigFile.cpp
|
||||||
@@ -824,7 +826,7 @@ add_executable(pipedaladmind AdminMain.cpp CommandLineParser.hpp
|
|||||||
asan_options.cpp
|
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
|
add_executable(processcopyrights copyrightMain.cpp
|
||||||
CommandLineParser.hpp
|
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_BEGIN(ChannelRouterSettings)
|
||||||
JSON_MAP_REFERENCE(ChannelRouterSettings, configured)
|
JSON_MAP_REFERENCE(ChannelRouterSettings, configured)
|
||||||
|
JSON_MAP_REFERENCE(ChannelRouterSettings, changed)
|
||||||
JSON_MAP_REFERENCE(ChannelRouterSettings, channelRouterPresetId)
|
JSON_MAP_REFERENCE(ChannelRouterSettings, channelRouterPresetId)
|
||||||
JSON_MAP_REFERENCE(ChannelRouterSettings, mainInputChannels)
|
JSON_MAP_REFERENCE(ChannelRouterSettings, mainInputChannels)
|
||||||
JSON_MAP_REFERENCE(ChannelRouterSettings, mainOutputChannels)
|
JSON_MAP_REFERENCE(ChannelRouterSettings, mainOutputChannels)
|
||||||
@@ -149,3 +160,22 @@ JSON_MAP_REFERENCE(ChannelRouterSettings, sendInputChannels)
|
|||||||
JSON_MAP_REFERENCE(ChannelRouterSettings, sendOutputChannels)
|
JSON_MAP_REFERENCE(ChannelRouterSettings, sendOutputChannels)
|
||||||
JSON_MAP_REFERENCE(ChannelRouterSettings, controlValues)
|
JSON_MAP_REFERENCE(ChannelRouterSettings, controlValues)
|
||||||
JSON_MAP_END()
|
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;
|
bool configured_ = false;
|
||||||
int64_t channelRouterPresetId_ = -1;
|
int64_t channelRouterPresetId_ = -1;
|
||||||
|
bool changed_ = false;
|
||||||
|
|
||||||
std::vector<int64_t> mainInputChannels_ = {1, 1};
|
std::vector<int64_t> mainInputChannels_ = {1, 1};
|
||||||
std::vector<int64_t> mainOutputChannels_ = {0, 1};
|
std::vector<int64_t> mainOutputChannels_ = {0, 1};
|
||||||
@@ -60,6 +61,7 @@ namespace pipedal
|
|||||||
|
|
||||||
JSON_GETTER_SETTER(configured)
|
JSON_GETTER_SETTER(configured)
|
||||||
JSON_GETTER_SETTER(channelRouterPresetId)
|
JSON_GETTER_SETTER(channelRouterPresetId)
|
||||||
|
JSON_GETTER_SETTER(changed)
|
||||||
JSON_GETTER_SETTER_REF(mainInputChannels)
|
JSON_GETTER_SETTER_REF(mainInputChannels)
|
||||||
JSON_GETTER_SETTER_REF(mainOutputChannels)
|
JSON_GETTER_SETTER_REF(mainOutputChannels)
|
||||||
JSON_GETTER_SETTER_REF(mainInserts)
|
JSON_GETTER_SETTER_REF(mainInserts)
|
||||||
@@ -110,4 +112,56 @@ namespace pipedal
|
|||||||
std::vector<int64_t> sendOutputChannels_;
|
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*/)
|
} catch (const std::exception& /*ignore*/)
|
||||||
{
|
{
|
||||||
|
this->isValid_ = false;
|
||||||
|
this->errorStatus_ = "Not configured.";
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include "Lv2Log.hpp"
|
#include "Lv2Log.hpp"
|
||||||
#include "SysExec.hpp"
|
#include "SysExec.hpp"
|
||||||
|
#include "PiPedalAlsa.hpp"
|
||||||
|
|
||||||
#define DRC_FILENAME "/etc/jackdrc"
|
#define DRC_FILENAME "/etc/jackdrc"
|
||||||
|
|
||||||
@@ -60,33 +61,41 @@ static std::vector<std::string> SplitArgs(const char *szBuff)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string GetJackStringArg(const std::vector<std::string> &args, const std::string&shortOption, const std::string&longOption)
|
static std::string GetJackStringArg(const std::vector<std::string> &args, const std::string &shortOption, const std::string &longOption)
|
||||||
{
|
{
|
||||||
std::string strVal;
|
std::string strVal;
|
||||||
for (size_t i = 1; i < args.size(); ++i)
|
for (size_t i = 1; i < args.size(); ++i)
|
||||||
{
|
{
|
||||||
auto pos = args[i].rfind(longOption,0);
|
auto pos = args[i].rfind(longOption, 0);
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos)
|
||||||
|
{
|
||||||
if (args[i].length() == longOption.length())
|
if (args[i].length() == longOption.length())
|
||||||
{
|
{
|
||||||
if (i == args.size()-1) {
|
if (i == args.size() - 1)
|
||||||
throw PiPedalException("Can't read Jack configuration.");
|
{
|
||||||
|
throw PiPedalException("Can't read Jack configuration.");
|
||||||
}
|
}
|
||||||
strVal = args[i+1];
|
strVal = args[i + 1];
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
strVal = args[i].substr(longOption.length());
|
strVal = args[i].substr(longOption.length());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pos = args[i].rfind(shortOption,0);
|
pos = args[i].rfind(shortOption, 0);
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos)
|
||||||
|
{
|
||||||
if (args[i].length() == shortOption.length())
|
if (args[i].length() == shortOption.length())
|
||||||
{
|
{
|
||||||
if (i == args.size()-1) {
|
if (i == args.size() - 1)
|
||||||
throw PiPedalException("Can't read Jack configuration.");
|
{
|
||||||
|
throw PiPedalException("Can't read Jack configuration.");
|
||||||
}
|
}
|
||||||
strVal = args[i+1];
|
strVal = args[i + 1];
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
strVal = args[i].substr(shortOption.length());
|
strVal = args[i].substr(shortOption.length());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -94,9 +103,9 @@ static std::string GetJackStringArg(const std::vector<std::string> &args, const
|
|||||||
}
|
}
|
||||||
return strVal;
|
return strVal;
|
||||||
}
|
}
|
||||||
static std::int32_t GetJackArg(const std::vector<std::string> &args, const std::string&shortOption, const std::string&longOption)
|
static std::int32_t GetJackArg(const std::vector<std::string> &args, const std::string &shortOption, const std::string &longOption)
|
||||||
{
|
{
|
||||||
std::string strVal = GetJackStringArg(args,shortOption,longOption);
|
std::string strVal = GetJackStringArg(args, shortOption, longOption);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
unsigned long value = std::stoul(strVal);
|
unsigned long value = std::stoul(strVal);
|
||||||
@@ -108,144 +117,42 @@ static std::int32_t GetJackArg(const std::vector<std::string> &args, const std::
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string GetAlsaDeviceName(const std::vector<AlsaDeviceInfo> &availableDevices, const std::string &id)
|
||||||
void JackServerSettings::ReadJackDaemonConfiguration()
|
|
||||||
{
|
{
|
||||||
#if !JACK_HOST
|
if (id.empty())
|
||||||
return;
|
return "";
|
||||||
#endif
|
std::string name;
|
||||||
this->valid_ = false;
|
for (const auto &availableDevice : availableDevices)
|
||||||
|
|
||||||
std::string lastLine;
|
|
||||||
{
|
{
|
||||||
ifstream input(DRC_FILENAME);
|
if (availableDevice.id_ == id)
|
||||||
|
|
||||||
if (!input.is_open())
|
|
||||||
{
|
{
|
||||||
return;
|
name = availableDevice.name_;
|
||||||
}
|
break;
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
std::string line;
|
|
||||||
std::getline(input,line);
|
|
||||||
if (line.length() != 0) {
|
|
||||||
lastLine = line;
|
|
||||||
}
|
|
||||||
if (input.eof()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::vector<std::string> argv = SplitArgs(lastLine.c_str());
|
|
||||||
for (auto i = argv.begin(); i != argv.end(); ++i)
|
|
||||||
{
|
|
||||||
if ((*i) == "-dalsa") {
|
|
||||||
argv.erase(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
catch (std::exception &)
|
|
||||||
{
|
|
||||||
//Lv2Log::error("Can't parse " DRC_FILENAME);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (name.empty())
|
||||||
|
{
|
||||||
|
auto pos = id.find(":");
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
{
|
||||||
|
name = id.substr(pos + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
}
|
}
|
||||||
|
void JackServerSettings::FixUpDeviceNames()
|
||||||
void JackServerSettings::WriteDaemonConfig()
|
|
||||||
{
|
{
|
||||||
#if JACK_HOST
|
if (
|
||||||
this->valid_ = false;
|
((!this->alsaInputDevice_.empty()) && this->alsaInputDeviceName_.empty()) ||
|
||||||
|
(!this->alsaOutputDevice_.empty()) && this->alsaOutputDeviceName_.empty())
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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())
|
PiPedalAlsaDevices& alsaDevices = PiPedalAlsaDevices::instance();
|
||||||
{
|
std::vector<AlsaDeviceInfo> availableDevices = alsaDevices.GetAlsaDevices();
|
||||||
throw PiPedalException("Can't write " DRC_FILENAME);
|
|
||||||
}
|
this->alsaInputDeviceName_ = GetAlsaDeviceName(availableDevices, this->alsaInputDevice_);
|
||||||
if (precedingLines.size() == 0)
|
this->alsaOutputDeviceName_ = GetAlsaDeviceName(availableDevices, this->alsaOutputDevice_);
|
||||||
{
|
|
||||||
// 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)
|
JSON_MAP_BEGIN(JackServerSettings)
|
||||||
@@ -256,6 +163,8 @@ JSON_MAP_REFERENCE(JackServerSettings, isJackAudio)
|
|||||||
JSON_MAP_REFERENCE(JackServerSettings, alsaDevice) // legacy field
|
JSON_MAP_REFERENCE(JackServerSettings, alsaDevice) // legacy field
|
||||||
JSON_MAP_REFERENCE(JackServerSettings, alsaInputDevice)
|
JSON_MAP_REFERENCE(JackServerSettings, alsaInputDevice)
|
||||||
JSON_MAP_REFERENCE(JackServerSettings, alsaOutputDevice)
|
JSON_MAP_REFERENCE(JackServerSettings, alsaOutputDevice)
|
||||||
|
JSON_MAP_REFERENCE(JackServerSettings, alsaInputDeviceName)
|
||||||
|
JSON_MAP_REFERENCE(JackServerSettings, alsaOutputDeviceName)
|
||||||
JSON_MAP_REFERENCE(JackServerSettings, sampleRate)
|
JSON_MAP_REFERENCE(JackServerSettings, sampleRate)
|
||||||
JSON_MAP_REFERENCE(JackServerSettings, bufferSize)
|
JSON_MAP_REFERENCE(JackServerSettings, bufferSize)
|
||||||
JSON_MAP_REFERENCE(JackServerSettings, numberOfBuffers)
|
JSON_MAP_REFERENCE(JackServerSettings, numberOfBuffers)
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ namespace pipedal
|
|||||||
bool isJackAudio_ = JACK_HOST ? true : false;
|
bool isJackAudio_ = JACK_HOST ? true : false;
|
||||||
bool rebootRequired_ = false;
|
bool rebootRequired_ = false;
|
||||||
std::string alsaInputDevice_;
|
std::string alsaInputDevice_;
|
||||||
|
std::string alsaInputDeviceName_;
|
||||||
std::string alsaOutputDevice_;
|
std::string alsaOutputDevice_;
|
||||||
|
std::string alsaOutputDeviceName_;
|
||||||
std::string alsaDevice_; // legacy
|
std::string alsaDevice_; // legacy
|
||||||
uint64_t sampleRate_ = 0;
|
uint64_t sampleRate_ = 0;
|
||||||
uint32_t bufferSize_ = 64;
|
uint32_t bufferSize_ = 64;
|
||||||
@@ -44,6 +46,7 @@ namespace pipedal
|
|||||||
JackServerSettings(
|
JackServerSettings(
|
||||||
const std::string &alsaInputDevice,
|
const std::string &alsaInputDevice,
|
||||||
const std::string &alsaOutputDevice,
|
const std::string &alsaOutputDevice,
|
||||||
|
|
||||||
uint64_t sampleRate,
|
uint64_t sampleRate,
|
||||||
uint32_t bufferSize,
|
uint32_t bufferSize,
|
||||||
uint32_t numberOfBuffers)
|
uint32_t numberOfBuffers)
|
||||||
@@ -55,6 +58,7 @@ namespace pipedal
|
|||||||
numberOfBuffers_(numberOfBuffers),
|
numberOfBuffers_(numberOfBuffers),
|
||||||
isOnboarding_(false)
|
isOnboarding_(false)
|
||||||
{
|
{
|
||||||
|
FixUpDeviceNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t GetSampleRate() const { return sampleRate_; }
|
uint64_t GetSampleRate() const { return sampleRate_; }
|
||||||
@@ -62,11 +66,13 @@ namespace pipedal
|
|||||||
uint32_t GetBufferSize() const { return bufferSize_; }
|
uint32_t GetBufferSize() const { return bufferSize_; }
|
||||||
uint32_t GetNumberOfBuffers() const { return numberOfBuffers_; }
|
uint32_t GetNumberOfBuffers() const { return numberOfBuffers_; }
|
||||||
const std::string &GetAlsaInputDevice() const { return alsaInputDevice_; }
|
const std::string &GetAlsaInputDevice() const { return alsaInputDevice_; }
|
||||||
|
const std::string &GetAlsaInputDeviceName() const { return alsaInputDeviceName_; }
|
||||||
const std::string &GetAlsaOutputDevice() const { return alsaOutputDevice_; }
|
const std::string &GetAlsaOutputDevice() const { return alsaOutputDevice_; }
|
||||||
|
const std::string &GetAlsaOutputDeviceName() const { return alsaOutputDeviceName_; }
|
||||||
const std::string &GetLegacyAlsaDevice() const { return alsaDevice_; } //legacy
|
const std::string &GetLegacyAlsaDevice() const { return alsaDevice_; } //legacy
|
||||||
|
|
||||||
void SetAlsaInputDevice(const std::string &d){ alsaInputDevice_ = d; }
|
void SetAlsaInputDevice(const std::string &id, const std::string&name){ alsaInputDevice_ = id; alsaInputDeviceName_ = name; }
|
||||||
void SetAlsaOutputDevice(const std::string &d){ alsaOutputDevice_ = d; }
|
void SetAlsaOutputDevice(const std::string &id, const std::string&name){ alsaOutputDevice_ = id; alsaOutputDeviceName_ = name; }
|
||||||
void SetLegacyAlsaDevice(const std::string &d) { alsaDevice_ = d; }
|
void SetLegacyAlsaDevice(const std::string &d) { alsaDevice_ = d; }
|
||||||
|
|
||||||
void UseDummyAudioDevice() {
|
void UseDummyAudioDevice() {
|
||||||
@@ -74,7 +80,9 @@ namespace pipedal
|
|||||||
if (sampleRate_ == 0) sampleRate_ = 48000;
|
if (sampleRate_ == 0) sampleRate_ = 48000;
|
||||||
this->alsaDevice_ = "dummy:channels_2";
|
this->alsaDevice_ = "dummy:channels_2";
|
||||||
this->alsaInputDevice_ = "dummy:channels_2";
|
this->alsaInputDevice_ = "dummy:channels_2";
|
||||||
|
this->alsaInputDeviceName_ = "Stopped";
|
||||||
this->alsaOutputDevice_ = "dummy:channels_2";
|
this->alsaOutputDevice_ = "dummy:channels_2";
|
||||||
|
this->alsaOutputDeviceName_ = "Stopped";
|
||||||
}
|
}
|
||||||
bool IsDummyAudioDevice() const {
|
bool IsDummyAudioDevice() const {
|
||||||
return
|
return
|
||||||
@@ -83,11 +91,8 @@ namespace pipedal
|
|||||||
|| this->alsaInputDevice_.starts_with("dummy:");
|
|| this->alsaInputDevice_.starts_with("dummy:");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadJackDaemonConfiguration();
|
|
||||||
|
|
||||||
bool IsValid() const { return valid_; }
|
bool IsValid() const { return valid_; }
|
||||||
|
|
||||||
void WriteDaemonConfig(); // requires root perms.
|
|
||||||
void SetRebootRequired(bool value)
|
void SetRebootRequired(bool value)
|
||||||
{
|
{
|
||||||
rebootRequired_ = value;
|
rebootRequired_ = value;
|
||||||
@@ -106,6 +111,7 @@ namespace pipedal
|
|||||||
this->bufferSize_ == other.bufferSize_ &&
|
this->bufferSize_ == other.bufferSize_ &&
|
||||||
this->numberOfBuffers_ == other.numberOfBuffers_;
|
this->numberOfBuffers_ == other.numberOfBuffers_;
|
||||||
}
|
}
|
||||||
|
void FixUpDeviceNames();
|
||||||
|
|
||||||
DECLARE_JSON_MAP(JackServerSettings);
|
DECLARE_JSON_MAP(JackServerSettings);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -103,8 +103,7 @@ void PrintHelp()
|
|||||||
|
|
||||||
void ListDevices()
|
void ListDevices()
|
||||||
{
|
{
|
||||||
PiPedalAlsaDevices alsaDevices;
|
auto devices = PiPedalAlsaDevices::instance().GetAlsaDevices();
|
||||||
auto devices = alsaDevices.GetAlsaDevices();
|
|
||||||
|
|
||||||
PrettyPrinter pp;
|
PrettyPrinter pp;
|
||||||
if (devices.size() == 0)
|
if (devices.size() == 0)
|
||||||
@@ -199,7 +198,7 @@ public:
|
|||||||
TestResult result;
|
TestResult result;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
JackServerSettings serverSettings(inputDeviceId, outputDeviceId, sampleRate, bufferSize, buffers);
|
JackServerSettings serverSettings(inputDeviceId,outputDeviceId, sampleRate, bufferSize, buffers);
|
||||||
|
|
||||||
JackConfiguration jackConfiguration;
|
JackConfiguration jackConfiguration;
|
||||||
jackConfiguration.AlsaInitialize(serverSettings);
|
jackConfiguration.AlsaInitialize(serverSettings);
|
||||||
|
|||||||
@@ -160,11 +160,14 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
|
|||||||
|
|
||||||
std::vector<AlsaDeviceInfo> result;
|
std::vector<AlsaDeviceInfo> result;
|
||||||
|
|
||||||
int cardNum = -1; // Start with first card
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
std::vector<ProcAlsaDevice> procAlsaDevices = getProcAlsaDevices();
|
std::vector<ProcAlsaDevice> procAlsaDevices = getProcAlsaDevices();
|
||||||
|
|
||||||
|
|
||||||
|
snd_pcm_info_t *pcminfo = nullptr;
|
||||||
|
snd_pcm_info_alloca(&pcminfo);
|
||||||
|
|
||||||
for (const auto &procAlsaDevice: procAlsaDevices)
|
for (const auto &procAlsaDevice: procAlsaDevices)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
@@ -198,13 +201,60 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
|
|||||||
err = snd_ctl_card_info(hDevice, alsaInfo);
|
err = snd_ctl_card_info(hDevice, alsaInfo);
|
||||||
if (err == 0)
|
if (err == 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
AlsaDeviceInfo info;
|
AlsaDeviceInfo info;
|
||||||
info.cardId_ = cardNum;
|
info.cardId_ = procAlsaDevice.cardId;
|
||||||
info.id_ = std::string("hw:") + snd_ctl_card_info_get_id(alsaInfo);
|
info.id_ = std::string("hw:") + snd_ctl_card_info_get_id(alsaInfo);
|
||||||
const char *driver = snd_ctl_card_info_get_driver(alsaInfo);
|
const char *driver = snd_ctl_card_info_get_driver(alsaInfo);
|
||||||
(void)driver;
|
(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);
|
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
|
// 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);
|
result.push_back(cachedInfo);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
snd_pcm_t *captureDevice = nullptr;
|
snd_pcm_t *captureDevice = nullptr;
|
||||||
snd_pcm_t *playbackDevice = nullptr;
|
snd_pcm_t *playbackDevice = nullptr;
|
||||||
auto rc = snd_pcm_open(&captureDevice, cardId.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
|
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)
|
if (captureOk || playbackOk)
|
||||||
{
|
{
|
||||||
|
|
||||||
snd_pcm_t *hDevice = captureOk ? captureDevice : playbackDevice;
|
snd_pcm_t *hDevice = captureOk ? captureDevice : playbackDevice;
|
||||||
snd_pcm_hw_params_t *params = nullptr;
|
snd_pcm_hw_params_t *params = nullptr;
|
||||||
err = snd_pcm_hw_params_malloc(¶ms);
|
err = snd_pcm_hw_params_malloc(¶ms);
|
||||||
@@ -637,6 +690,17 @@ AlsaMidiDeviceInfo::AlsaMidiDeviceInfo(const char *name, const char *description
|
|||||||
this->subdevice_ = subdevice;
|
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_BEGIN(AlsaDeviceInfo)
|
||||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, cardId)
|
JSON_MAP_REFERENCE(AlsaDeviceInfo, cardId)
|
||||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, id)
|
JSON_MAP_REFERENCE(AlsaDeviceInfo, id)
|
||||||
|
|||||||
@@ -71,12 +71,16 @@ namespace pipedal {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class PiPedalAlsaDevices {
|
class PiPedalAlsaDevices {
|
||||||
|
private:
|
||||||
|
static std::unique_ptr<PiPedalAlsaDevices> instance_;
|
||||||
|
PiPedalAlsaDevices() { }
|
||||||
std::map<std::string,AlsaDeviceInfo> cachedDevices;
|
std::map<std::string,AlsaDeviceInfo> cachedDevices;
|
||||||
|
|
||||||
bool getCachedDevice(const std::string&name, AlsaDeviceInfo*pResult);
|
bool getCachedDevice(const std::string&name, AlsaDeviceInfo*pResult);
|
||||||
void cacheDevice(const std::string&name, const AlsaDeviceInfo&deviceInfo);
|
void cacheDevice(const std::string&name, const AlsaDeviceInfo&deviceInfo);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static PiPedalAlsaDevices&instance();
|
||||||
|
|
||||||
std::vector<AlsaDeviceInfo> GetAlsaDevices();
|
std::vector<AlsaDeviceInfo> GetAlsaDevices();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ using namespace std;
|
|||||||
static void DiscoveryTest()
|
static void DiscoveryTest()
|
||||||
{
|
{
|
||||||
cout << "--- Discovery" << endl;
|
cout << "--- Discovery" << endl;
|
||||||
PiPedalAlsaDevices devices;
|
PiPedalAlsaDevices &devices = PiPedalAlsaDevices::instance();
|
||||||
auto result = devices.GetAlsaDevices();
|
auto result = devices.GetAlsaDevices();
|
||||||
std::cout << result.size() << " ALSA devices found." << std::endl;
|
std::cout << result.size() << " ALSA devices found." << std::endl;
|
||||||
|
|
||||||
|
|||||||
@@ -83,12 +83,9 @@ PiPedalModel::PiPedalModel()
|
|||||||
this->updater->Start();
|
this->updater->Start();
|
||||||
this->currentUpdateStatus = updater->GetCurrentStatus();
|
this->currentUpdateStatus = updater->GetCurrentStatus();
|
||||||
this->pedalboard = Pedalboard::MakeDefault(Pedalboard::PedalboardType::MainPedalboard);
|
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();
|
this->jackServerSettings = this->storage.GetJackServerSettings();
|
||||||
#endif
|
|
||||||
updater->SetUpdateListener(
|
updater->SetUpdateListener(
|
||||||
[this](const UpdateStatus &updateStatus)
|
[this](const UpdateStatus &updateStatus)
|
||||||
{
|
{
|
||||||
@@ -224,21 +221,22 @@ void PiPedalModel::Init(const PiPedalConfiguration &configuration)
|
|||||||
|
|
||||||
this->systemMidiBindings = storage.GetSystemMidiBindings();
|
this->systemMidiBindings = storage.GetSystemMidiBindings();
|
||||||
|
|
||||||
#if JACK_HOST
|
|
||||||
this->jackConfiguration = this->jackConfiguration.JackInitialize();
|
|
||||||
#else
|
|
||||||
this->jackServerSettings = storage.GetJackServerSettings();
|
this->jackServerSettings = storage.GetJackServerSettings();
|
||||||
this->jackConfiguration.AlsaInitialize(jackServerSettings);
|
try
|
||||||
#endif
|
{
|
||||||
|
this->jackConfiguration.AlsaInitialize(jackServerSettings);
|
||||||
|
}
|
||||||
|
catch (const std::exception &)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
this->channelRouterSettings = storage.GetChannelRouterSettings();
|
this->channelRouterSettings = storage.GetChannelRouterSettings();
|
||||||
pluginHost.OnConfigurationChanged(
|
pluginHost.OnConfigurationChanged(
|
||||||
jackConfiguration,
|
jackConfiguration,
|
||||||
storage.GetChannelSelection());
|
storage.GetChannelSelection());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int64_t PiPedalModel::DownloadModelsFromTone3000(
|
int64_t PiPedalModel::DownloadModelsFromTone3000(
|
||||||
int64_t clientId,
|
int64_t clientId,
|
||||||
Tone3000DownloadType downloadType,
|
Tone3000DownloadType downloadType,
|
||||||
@@ -253,7 +251,7 @@ int64_t PiPedalModel::DownloadModelsFromTone3000(
|
|||||||
{
|
{
|
||||||
throw PiPedalException("Invalid path: not in uploads directory.");
|
throw PiPedalException("Invalid path: not in uploads directory.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for ".." in path components (security check)
|
// Check for ".." in path components (security check)
|
||||||
for (const auto &component : path)
|
for (const auto &component : path)
|
||||||
{
|
{
|
||||||
@@ -271,14 +269,12 @@ int64_t PiPedalModel::DownloadModelsFromTone3000(
|
|||||||
return tone3000Downloader->RequestDownload(
|
return tone3000Downloader->RequestDownload(
|
||||||
downloadType,
|
downloadType,
|
||||||
downloadPath,
|
downloadPath,
|
||||||
tone3000Url
|
tone3000Url);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PiPedalModel::CancelTone3000Download(
|
void PiPedalModel::CancelTone3000Download(
|
||||||
int64_t clientId,
|
int64_t clientId,
|
||||||
int64_t downloadHandle
|
int64_t downloadHandle)
|
||||||
)
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
|
|
||||||
@@ -307,12 +303,12 @@ void PiPedalModel::OnTone3000Progress(const Tone3000DownloadProgress &progress)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PiPedalModel::OnTone3000DownloadComplete(int64_t handle, const std::string&resultPath)
|
void PiPedalModel::OnTone3000DownloadComplete(int64_t handle, const std::string &resultPath)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
for (auto subscriber : this->subscribers)
|
for (auto subscriber : this->subscribers)
|
||||||
{
|
{
|
||||||
subscriber->OnTone3000DownloadComplete(handle,resultPath);
|
subscriber->OnTone3000DownloadComplete(handle, resultPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,20 +354,21 @@ void PiPedalModel::LoadLv2PluginInfo()
|
|||||||
{
|
{
|
||||||
PluginPresets pluginPresets = pluginHost.GetFactoryPluginPresets(plugin->uri());
|
PluginPresets pluginPresets = pluginHost.GetFactoryPluginPresets(plugin->uri());
|
||||||
storage.SavePluginPresets(plugin->uri(), pluginPresets);
|
storage.SavePluginPresets(plugin->uri(), pluginPresets);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if (pluginPresetIndexVersion == 0)
|
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")
|
if (plugin->uri() == "http://two-play.com/plugins/toob-convolution-reverb" || plugin->uri() == "http://two-play.com/plugins/toob-convolution-reverb-stereo")
|
||||||
{
|
{
|
||||||
// overwrite previous factory presets!
|
// overwrite previous factory presets!
|
||||||
PluginPresets pluginPresets = pluginHost.GetFactoryPluginPresets(plugin->uri());
|
PluginPresets pluginPresets = pluginHost.GetFactoryPluginPresets(plugin->uri());
|
||||||
for (auto & pluginPreset: pluginPresets.presets_)
|
for (auto &pluginPreset : pluginPresets.presets_)
|
||||||
{
|
{
|
||||||
storage.SavePluginPreset(
|
storage.SavePluginPreset(
|
||||||
plugin->uri(),
|
plugin->uri(),
|
||||||
pluginPreset);
|
pluginPreset);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -772,16 +769,20 @@ void PiPedalModel::SetPedalboardItemEnable(int64_t clientId, int64_t pedalItemId
|
|||||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||||
{
|
{
|
||||||
this->pedalboard.SetItemEnabled(pedalItemId, enabled);
|
this->pedalboard.SetItemEnabled(pedalItemId, enabled);
|
||||||
PedalboardItem * pPedalboardItem = this->pedalboard.GetItem(pedalItemId);
|
PedalboardItem *pPedalboardItem = this->pedalboard.GetItem(pedalItemId);
|
||||||
if (pPedalboardItem) {
|
if (pPedalboardItem)
|
||||||
|
{
|
||||||
Lv2PluginInfo::ptr pluginInfo = GetPluginInfo(pPedalboardItem->uri());
|
Lv2PluginInfo::ptr pluginInfo = GetPluginInfo(pPedalboardItem->uri());
|
||||||
if (pluginInfo) {
|
if (pluginInfo)
|
||||||
for (auto &port: pluginInfo->ports()) {
|
{
|
||||||
if (port->is_bypass()) {
|
for (auto &port : pluginInfo->ports())
|
||||||
pPedalboardItem->SetControlValue(port->symbol(), enabled? 1.0: 0.0f);
|
{
|
||||||
|
if (port->is_bypass())
|
||||||
|
{
|
||||||
|
pPedalboardItem->SetControlValue(port->symbol(), enabled ? 1.0 : 0.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify clients.
|
// Notify clients.
|
||||||
@@ -999,7 +1000,7 @@ int64_t PiPedalModel::SavePluginPresetAs(int64_t instanceId, const std::string &
|
|||||||
return presetId;
|
return presetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t PiPedalModel::SaveCurrentPresetAs(int64_t clientId, int64_t bankInstanceId,const std::string &name, int64_t saveAfterInstanceId)
|
int64_t PiPedalModel::SaveCurrentPresetAs(int64_t clientId, int64_t bankInstanceId, const std::string &name, int64_t saveAfterInstanceId)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||||
|
|
||||||
@@ -1523,7 +1524,7 @@ void PiPedalModel::RestartAudio(bool useDummyAudioDriver)
|
|||||||
throw std::runtime_error("Audio configuration not valid.");
|
throw std::runtime_error("Audio configuration not valid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto & channelSelection = this->storage.GetChannelSelection();
|
const auto &channelSelection = this->storage.GetChannelSelection();
|
||||||
|
|
||||||
this->audioHost->Open(jackServerSettings, channelSelection);
|
this->audioHost->Open(jackServerSettings, channelSelection);
|
||||||
|
|
||||||
@@ -1625,7 +1626,6 @@ std::vector<AlsaSequencerPortSelection> PiPedalModel::GetAlsaSequencerPorts()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PiPedalModel::FireChannelRouterSettingsChanged(int64_t clientId)
|
void PiPedalModel::FireChannelRouterSettingsChanged(int64_t clientId)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||||
@@ -1838,7 +1838,7 @@ void PiPedalModel::SendSetPatchProperty(
|
|||||||
{
|
{
|
||||||
std::shared_ptr<Lv2PluginInfo> pluginInfo = GetPluginInfo(pedalboardItem->uri_);
|
std::shared_ptr<Lv2PluginInfo> pluginInfo = GetPluginInfo(pedalboardItem->uri_);
|
||||||
auto pipedalUi = pluginInfo->piPedalUI();
|
auto pipedalUi = pluginInfo->piPedalUI();
|
||||||
if (pipedalUi)
|
if (pipedalUi)
|
||||||
{
|
{
|
||||||
auto fileProperty = pipedalUi->GetFileProperty(propertyUri);
|
auto fileProperty = pipedalUi->GetFileProperty(propertyUri);
|
||||||
if (fileProperty)
|
if (fileProperty)
|
||||||
@@ -2129,8 +2129,7 @@ std::shared_ptr<Lv2PluginInfo> PiPedalModel::GetPluginInfo(const std::string &ur
|
|||||||
return pluginHost.GetPluginInfo(uri);
|
return pluginHost.GetPluginInfo(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PiPedalModel::UpdateDefaults(SnapshotValue &snapshotValue, const PedalboardItem *pedalboardItem_)
|
||||||
void PiPedalModel::UpdateDefaults(SnapshotValue&snapshotValue, const PedalboardItem*pedalboardItem_)
|
|
||||||
{
|
{
|
||||||
std::shared_ptr<Lv2PluginInfo> pPlugin = pluginHost.GetPluginInfo(pedalboardItem_->uri());
|
std::shared_ptr<Lv2PluginInfo> pPlugin = pluginHost.GetPluginInfo(pedalboardItem_->uri());
|
||||||
if (!pPlugin)
|
if (!pPlugin)
|
||||||
@@ -2145,11 +2144,11 @@ void PiPedalModel::UpdateDefaults(SnapshotValue&snapshotValue, const PedalboardI
|
|||||||
// Fix incorrect bypass settings presint in Pipedal < 1.5.96
|
// Fix incorrect bypass settings presint in Pipedal < 1.5.96
|
||||||
for (size_t i = 0; i < pPlugin->ports().size(); ++i)
|
for (size_t i = 0; i < pPlugin->ports().size(); ++i)
|
||||||
{
|
{
|
||||||
auto& port = pPlugin->ports()[i];
|
auto &port = pPlugin->ports()[i];
|
||||||
if (port->is_bypass() && port->is_control_port() && port->is_input())
|
if (port->is_bypass() && port->is_control_port() && port->is_input())
|
||||||
{
|
{
|
||||||
ControlValue* pValue = snapshotValue.GetControlValue(port->symbol());
|
ControlValue *pValue = snapshotValue.GetControlValue(port->symbol());
|
||||||
float value = snapshotValue.isEnabled_ ? 1.0f : 0.0f;
|
float value = snapshotValue.isEnabled_ ? 1.0f : 0.0f;
|
||||||
|
|
||||||
if (pValue == nullptr)
|
if (pValue == nullptr)
|
||||||
{
|
{
|
||||||
@@ -2166,7 +2165,8 @@ void PiPedalModel::UpdateDefaults(SnapshotValue&snapshotValue, const PedalboardI
|
|||||||
if (pPlugin->uri() == "http://two-play.com/plugins/toob-nam")
|
if (pPlugin->uri() == "http://two-play.com/plugins/toob-nam")
|
||||||
{
|
{
|
||||||
ControlValue *pVersion = snapshotValue.GetControlValue("version");
|
ControlValue *pVersion = snapshotValue.GetControlValue("version");
|
||||||
if (pVersion == nullptr) {
|
if (pVersion == nullptr)
|
||||||
|
{
|
||||||
ControlValue *pValue = snapshotValue.GetControlValue("inputCalibrationMode");
|
ControlValue *pValue = snapshotValue.GetControlValue("inputCalibrationMode");
|
||||||
if (pValue == nullptr)
|
if (pValue == nullptr)
|
||||||
{
|
{
|
||||||
@@ -2175,13 +2175,16 @@ void PiPedalModel::UpdateDefaults(SnapshotValue&snapshotValue, const PedalboardI
|
|||||||
}
|
}
|
||||||
// convert old gate threshold to new gate threshold.
|
// convert old gate threshold to new gate threshold.
|
||||||
ControlValue *pGateValue = snapshotValue.GetControlValue("gate");
|
ControlValue *pGateValue = snapshotValue.GetControlValue("gate");
|
||||||
if (pGateValue) {
|
if (pGateValue)
|
||||||
|
{
|
||||||
float value = pGateValue->value();
|
float value = pGateValue->value();
|
||||||
// Is the gate disabled?
|
// Is the gate disabled?
|
||||||
if (value <= -100.0f)
|
if (value <= -100.0f)
|
||||||
{
|
{
|
||||||
value = -120.0f; // "disabled in new range."
|
value = -120.0f; // "disabled in new range."
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
value = value * 0.5; // correct the bug in original implementation.
|
value = value * 0.5; // correct the bug in original implementation.
|
||||||
}
|
}
|
||||||
snapshotValue.SetControlValue("gate", value);
|
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")
|
if (pPlugin->uri() == "http://two-play.com/plugins/toob-nam")
|
||||||
{
|
{
|
||||||
ControlValue *pVersion = pedalboardItem->GetControlValue("version");
|
ControlValue *pVersion = pedalboardItem->GetControlValue("version");
|
||||||
if (pVersion == nullptr) {
|
if (pVersion == nullptr)
|
||||||
|
{
|
||||||
ControlValue *pValue = pedalboardItem->GetControlValue("inputCalibrationMode");
|
ControlValue *pValue = pedalboardItem->GetControlValue("inputCalibrationMode");
|
||||||
if (pValue == nullptr)
|
if (pValue == nullptr)
|
||||||
{
|
{
|
||||||
@@ -2254,13 +2258,16 @@ void PiPedalModel::UpdateDefaults(PedalboardItem *pedalboardItem, std::unordered
|
|||||||
}
|
}
|
||||||
// convert old gate threshold to new gate threshold.
|
// convert old gate threshold to new gate threshold.
|
||||||
ControlValue *pGateValue = pedalboardItem->GetControlValue("gate");
|
ControlValue *pGateValue = pedalboardItem->GetControlValue("gate");
|
||||||
if (pGateValue) {
|
if (pGateValue)
|
||||||
|
{
|
||||||
float value = pGateValue->value();
|
float value = pGateValue->value();
|
||||||
// Is the gate disabled?
|
// Is the gate disabled?
|
||||||
if (value <= -100.0f)
|
if (value <= -100.0f)
|
||||||
{
|
{
|
||||||
value = -120.0f; // "disabled in new range."
|
value = -120.0f; // "disabled in new range."
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
value = value * 0.5; // correct the bug in original implementation.
|
value = value * 0.5; // correct the bug in original implementation.
|
||||||
}
|
}
|
||||||
pedalboardItem->SetControlValue("gate", value);
|
pedalboardItem->SetControlValue("gate", value);
|
||||||
@@ -2278,18 +2285,20 @@ void PiPedalModel::UpdateDefaults(PedalboardItem *pedalboardItem, std::unordered
|
|||||||
{
|
{
|
||||||
// retroactively correct bug in version of PiPedal prior to 1.5.96.
|
// retroactively correct bug in version of PiPedal prior to 1.5.96.
|
||||||
ControlValue *pValue = pedalboardItem->GetControlValue(port->symbol());
|
ControlValue *pValue = pedalboardItem->GetControlValue(port->symbol());
|
||||||
float value = pedalboardItem->isEnabled() ? 1.0f: 0.0f;
|
float value = pedalboardItem->isEnabled() ? 1.0f : 0.0f;
|
||||||
|
|
||||||
if (pValue == nullptr)
|
if (pValue == nullptr)
|
||||||
{
|
{
|
||||||
pedalboardItem->controlValues().push_back(
|
pedalboardItem->controlValues().push_back(
|
||||||
pipedal::ControlValue(port->symbol().c_str(), value));
|
pipedal::ControlValue(port->symbol().c_str(), value));
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
pValue->value(value);
|
pValue->value(value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
} else {
|
{
|
||||||
ControlValue *pValue = pedalboardItem->GetControlValue(port->symbol());
|
ControlValue *pValue = pedalboardItem->GetControlValue(port->symbol());
|
||||||
if (pValue == nullptr)
|
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.
|
// plugin is no longer present. Remove from the snapshot.
|
||||||
snapshot->values_.erase(snapshot->values_.begin() + i);
|
snapshot->values_.erase(snapshot->values_.begin() + i);
|
||||||
--i;
|
--i;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
UpdateDefaults(value, f->second);
|
UpdateDefaults(value, f->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2387,7 +2398,7 @@ void PiPedalModel::UpdateDefaults(Pedalboard *pedalboard)
|
|||||||
|
|
||||||
PluginPresets PiPedalModel::GetPluginPresets(const std::string &pluginUri)
|
PluginPresets PiPedalModel::GetPluginPresets(const std::string &pluginUri)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
|
|
||||||
return storage.GetPluginPresets(pluginUri);
|
return storage.GetPluginPresets(pluginUri);
|
||||||
}
|
}
|
||||||
@@ -3376,34 +3387,30 @@ std::vector<PresetIndexEntry> PiPedalModel::RequestBankPresets(int64_t bankInsta
|
|||||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
|
|
||||||
return storage.RequestBankPresets(bankInstanceId);
|
return storage.RequestBankPresets(bankInstanceId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t PiPedalModel::ImportPresetsFromBank(int64_t bankInstanceId, const std::vector<int64_t> &presets)
|
int64_t PiPedalModel::ImportPresetsFromBank(int64_t bankInstanceId, const std::vector<int64_t> &presets)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
uint64_t lastAdded = storage.ImportPresetsFromBank(bankInstanceId, presets);
|
uint64_t lastAdded = storage.ImportPresetsFromBank(bankInstanceId, presets);
|
||||||
|
|
||||||
FirePresetsChanged(-1);
|
FirePresetsChanged(-1);
|
||||||
return lastAdded;
|
return lastAdded;
|
||||||
|
|
||||||
}
|
}
|
||||||
int64_t PiPedalModel::CopyPresetsToBank(int64_t bankInstanceId, const std::vector<int64_t> &presets)
|
int64_t PiPedalModel::CopyPresetsToBank(int64_t bankInstanceId, const std::vector<int64_t> &presets)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
uint64_t lastAdded = storage.CopyPresetsToBank(bankInstanceId, presets);
|
uint64_t lastAdded = storage.CopyPresetsToBank(bankInstanceId, presets);
|
||||||
return lastAdded;
|
return lastAdded;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ChannelRouterSettings::ptr PiPedalModel::GetChannelRouterSettings()
|
ChannelRouterSettings::ptr PiPedalModel::GetChannelRouterSettings()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
return this->channelRouterSettings;
|
return this->channelRouterSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PiPedalModel::SetChannelRouterSettings(int64_t clientId,ChannelRouterSettings::ptr &settings)
|
void PiPedalModel::SetChannelRouterSettings(int64_t clientId, ChannelRouterSettings::ptr &settings)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
@@ -3415,12 +3422,9 @@ void PiPedalModel::SetChannelRouterSettings(int64_t clientId,ChannelRouterSettin
|
|||||||
RestartAudio(); // no lock to avoid mutex deadlock when reader thread is sending notifications..
|
RestartAudio(); // no lock to avoid mutex deadlock when reader thread is sending notifications..
|
||||||
|
|
||||||
this->FireChannelRouterSettingsChanged(clientId);
|
this->FireChannelRouterSettingsChanged(clientId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string PiPedalModel::Tone3000ThumbnailDirectory()
|
||||||
|
|
||||||
std::string PiPedalModel::Tone3000ThumbnailDirectory()
|
|
||||||
{
|
{
|
||||||
return "/var/pipedal/tone3000_thumbnails";
|
return "/var/pipedal/tone3000_thumbnails";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ namespace pipedal
|
|||||||
std::unique_ptr<AvahiService> avahiService;
|
std::unique_ptr<AvahiService> avahiService;
|
||||||
uint16_t webPort;
|
uint16_t webPort;
|
||||||
|
|
||||||
PiPedalAlsaDevices alsaDevices;
|
PiPedalAlsaDevices &alsaDevices = PiPedalAlsaDevices::instance();
|
||||||
std::recursive_mutex mutex;
|
std::recursive_mutex mutex;
|
||||||
|
|
||||||
AdminClient adminClient;
|
AdminClient adminClient;
|
||||||
|
|||||||
@@ -45,11 +45,19 @@ namespace pipedal
|
|||||||
uint8_t cc2_;
|
uint8_t cc2_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Lv2RoutingInserts {
|
||||||
|
|
||||||
|
Lv2Pedalboard*mainInserts = nullptr;
|
||||||
|
Lv2Pedalboard*auxInserts = nullptr;;
|
||||||
|
};
|
||||||
|
|
||||||
enum class RingBufferCommand : int64_t
|
enum class RingBufferCommand : int64_t
|
||||||
{
|
{
|
||||||
Invalid = 0,
|
Invalid = 0,
|
||||||
ReplaceEffect,
|
ReplaceEffect,
|
||||||
EffectReplaced,
|
EffectReplaced,
|
||||||
|
ReplaceRoutingInserts,
|
||||||
|
RoutingInsertsReplaced,
|
||||||
SetValue,
|
SetValue,
|
||||||
SetBypass,
|
SetBypass,
|
||||||
// AudioStopped,
|
// AudioStopped,
|
||||||
@@ -539,16 +547,28 @@ namespace pipedal
|
|||||||
write(RingBufferCommand::ReplaceEffect, pedalboard);
|
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()
|
void AudioTerminatedAbnormally()
|
||||||
{
|
{
|
||||||
AudioStoppedBody body;
|
AudioStoppedBody body;
|
||||||
write(RingBufferCommand::AudioTerminatedAbnormally, body);
|
write(RingBufferCommand::AudioTerminatedAbnormally, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectReplaced(Lv2Pedalboard *pedalboard)
|
|
||||||
{
|
|
||||||
write(RingBufferCommand::EffectReplaced, pedalboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeSnapshot(IndexedSnapshot *snapshot)
|
void FreeSnapshot(IndexedSnapshot *snapshot)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1931,14 +1931,13 @@ pipedal::JackServerSettings Storage::GetJackServerSettings()
|
|||||||
result.GetAlsaInputDevice().empty() &&
|
result.GetAlsaInputDevice().empty() &&
|
||||||
result.GetAlsaOutputDevice().empty())
|
result.GetAlsaOutputDevice().empty())
|
||||||
{
|
{
|
||||||
result.SetAlsaInputDevice(result.GetLegacyAlsaDevice());
|
std::string legacyDeviceId = result.GetLegacyAlsaDevice();
|
||||||
result.SetAlsaOutputDevice(result.GetLegacyAlsaDevice());
|
result.SetAlsaInputDevice(legacyDeviceId, "");
|
||||||
|
result.SetAlsaOutputDevice(legacyDeviceId, "");
|
||||||
result.SetLegacyAlsaDevice("");
|
result.SetLegacyAlsaDevice("");
|
||||||
}
|
}
|
||||||
|
result.FixUpDeviceNames();
|
||||||
}
|
}
|
||||||
#if JACK_HOST
|
|
||||||
result.Initialize();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1952,9 +1951,6 @@ void Storage::SetJackServerSettings(const pipedal::JackServerSettings &jackConfi
|
|||||||
json_writer writer(f, false);
|
json_writer writer(f, false);
|
||||||
writer.write(jackConfiguration);
|
writer.write(jackConfiguration);
|
||||||
}
|
}
|
||||||
#if JACK_HOST
|
|
||||||
jackConfiguration.Write();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Storage::SetSystemMidiBindings(const std::vector<MidiBinding> &bindings)
|
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
|
// Print basic info
|
||||||
std::cout << "\n Subdevice: " << name << " - " << device_name << std::endl;
|
std::cout << "\n Subdevice: " << name << " - " << device_name << std::endl;
|
||||||
std::cout << " ID: " << snd_pcm_info_get_id(pcminfo) << 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:";
|
std::cout << " Capabilities:";
|
||||||
if (has_playback) std::cout << " PLAYBACK";
|
if (has_playback) std::cout << " PLAYBACK";
|
||||||
if (has_capture) std::cout << " CAPTURE";
|
if (has_capture) std::cout << " CAPTURE";
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
A way to force input to mono?
|
summaries for Mono audio adapaters.
|
||||||
Cab I/Rs: gear="ir"
|
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.
|
Tooltip on File Property Select: add to release notes.
|
||||||
sAFEfILEnAMES: RENAME, &C. Safe bank names?
|
sAFEfILEnAMES: RENAME, &C. Safe bank names?
|
||||||
NEW BANK
|
NEW BANK
|
||||||
WINDOW SIZE CALCUALTIONS ARE WRONG ON LINUX HIDEF DISPLAYS.
|
WINDOW SIZE CALCUALTIONS ARE WRONG ON LINUX HIDEF DISPLAYS.
|
||||||
|
|
||||||
ls
|
|
||||||
Carla Project icons.
|
Carla Project icons.
|
||||||
check reload after change of LV2 plugins.
|
check reload after change of LV2 plugins.
|
||||||
|
|
||||||
Exactly one underrun per seek in Toob Player
|
Exactly one underrun per seek in Toob Player
|
||||||
|
|
||||||
|
|
||||||
json "skip" code doesn't work with complex objects (MidiChannelBinding specifically).
|
|
||||||
json nan/inf is fatal.
|
json nan/inf is fatal.
|
||||||
|
|
||||||
- pipewire aux in?
|
- 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,24 +1,39 @@
|
|||||||
{
|
{
|
||||||
"short_name": "PiPedal",
|
"manifest_version": 3,
|
||||||
"name": "PiPedal",
|
"short_name": "PiPedal",
|
||||||
"icons": [
|
"name": "PiPedal",
|
||||||
{
|
"author": "Robin E.R. Davies",
|
||||||
"src": "favicon.ico",
|
"description": "A web-based client for the PiPedal guitar effects processor.",
|
||||||
"sizes": "64x64 32x32 24x24 16x16",
|
"developer": {
|
||||||
"type": "image/x-icon"
|
"name": "Robin E.R. Davies",
|
||||||
|
"url": "https://rerdavies.github.io"
|
||||||
},
|
},
|
||||||
{
|
"homepage_url": "https://rerdavies.github.io/pipedal",
|
||||||
"src": "logo192.png",
|
"icons": [
|
||||||
"type": "image/png",
|
{
|
||||||
"sizes": "192x192"
|
"src": "favicon.ico",
|
||||||
},
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
{
|
"type": "image/x-icon"
|
||||||
"src": "logo512.png",
|
},
|
||||||
"type": "image/png",
|
{
|
||||||
"sizes": "512x512"
|
"src": "logo192.png",
|
||||||
}
|
"type": "image/png",
|
||||||
],
|
"sizes": "192x192"
|
||||||
"start_url": ".",
|
},
|
||||||
"theme_color": "#000000",
|
{
|
||||||
"background_color": "#ffffff"
|
"src": "logo512.png",
|
||||||
}
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"display": "standalone",
|
||||||
|
"display_override": [
|
||||||
|
"fullscreen",
|
||||||
|
"standalone",
|
||||||
|
"minimal-ui",
|
||||||
|
"browser"
|
||||||
|
],
|
||||||
|
"background_color": "#000000"
|
||||||
|
}
|
||||||
@@ -49,4 +49,5 @@ input[type=number] {
|
|||||||
|
|
||||||
img {
|
img {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,24 +3,34 @@ import { Pedalboard, ControlValue } from "./Pedalboard.tsx";
|
|||||||
import JackConfiguration from "./Jack.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 {
|
function isActiveChannel(channels: number[]): boolean {
|
||||||
if (channels.length < 2) return false;
|
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 {
|
function chName(ch: number): string {
|
||||||
if (ch === -1) {
|
if (ch === -1) {
|
||||||
return "None";
|
return "None";
|
||||||
}
|
}
|
||||||
return "Ch " + (ch + 1);
|
return "Ch" + (ch + 1);
|
||||||
}
|
}
|
||||||
function channelPairName(channels: number[], maxChannels: number): string {
|
function channelPairName(channels: number[], maxChannels: number, input: boolean): string {
|
||||||
if (channels.length !== 2) {
|
if (channels.length !== 2) {
|
||||||
return "Invalid";
|
return "Invalid";
|
||||||
}
|
}
|
||||||
if (channels[0] === -1 && channels[1] === -1) {
|
if (channels[0] === -1 && channels[1] === -1) {
|
||||||
return "None";
|
return "None";
|
||||||
}
|
}
|
||||||
|
if (maxChannels === 1) {
|
||||||
|
return input ? "Input": "Output";
|
||||||
|
}
|
||||||
|
|
||||||
if (maxChannels === 2) {
|
if (maxChannels === 2) {
|
||||||
if (channels[0] === 0 && channels[1] === 1) {
|
if (channels[0] === 0 && channels[1] === 1) {
|
||||||
@@ -36,13 +46,28 @@ function channelPairName(channels: number[], maxChannels: number): string {
|
|||||||
return "Right,Left";
|
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 {
|
export default class ChannelRouterSettings {
|
||||||
configured: boolean = false;
|
configured: boolean = false;
|
||||||
|
modified: boolean = false;
|
||||||
channelRouterPresetId: number = -1;
|
channelRouterPresetId: number = -1;
|
||||||
|
|
||||||
mainInputChannels: number[] = [1, 1];
|
mainInputChannels: number[] = [1, 1];
|
||||||
@@ -61,6 +86,7 @@ export default class ChannelRouterSettings {
|
|||||||
|
|
||||||
deserialize(obj: any): ChannelRouterSettings {
|
deserialize(obj: any): ChannelRouterSettings {
|
||||||
this.configured = obj.configured;
|
this.configured = obj.configured;
|
||||||
|
this.modified = obj.modified;
|
||||||
this.channelRouterPresetId = obj.channelRouterPresetId ? obj.channelRouterPresetId : -1;
|
this.channelRouterPresetId = obj.channelRouterPresetId ? obj.channelRouterPresetId : -1;
|
||||||
this.mainInputChannels = obj.mainInputChannels.slice();
|
this.mainInputChannels = obj.mainInputChannels.slice();
|
||||||
this.mainOutputChannels = obj.mainOutputChannels.slice();
|
this.mainOutputChannels = obj.mainOutputChannels.slice();
|
||||||
@@ -73,6 +99,9 @@ export default class ChannelRouterSettings {
|
|||||||
this.controlValues = ControlValue.deserializeArray(obj.controlValues);
|
this.controlValues = ControlValue.deserializeArray(obj.controlValues);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
clone() : ChannelRouterSettings {
|
||||||
|
return new ChannelRouterSettings().deserialize(this);
|
||||||
|
}
|
||||||
getControlValue(symbol: string): number | null {
|
getControlValue(symbol: string): number | null {
|
||||||
for (let control of this.controlValues) {
|
for (let control of this.controlValues) {
|
||||||
if (control.key === symbol) {
|
if (control.key === symbol) {
|
||||||
@@ -111,16 +140,21 @@ export default class ChannelRouterSettings {
|
|||||||
}
|
}
|
||||||
let nInputs = jackConfiguration.inputAudioPorts.length;
|
let nInputs = jackConfiguration.inputAudioPorts.length;
|
||||||
let nOutputs = jackConfiguration.outputAudioPorts.length;
|
let nOutputs = jackConfiguration.outputAudioPorts.length;
|
||||||
let description = channelPairName(this.mainInputChannels, nInputs) + " -> " +
|
let description = channelPairName(this.mainInputChannels, nInputs,true) + " -> " +
|
||||||
channelPairName(this.mainOutputChannels, nOutputs);
|
channelPairName(this.mainOutputChannels, nOutputs,false);
|
||||||
if (isActiveChannel(this.auxInputChannels) && isActiveChannel(this.auxOutputChannels))
|
if (isActiveChannel(this.auxInputChannels) && isActiveChannel(this.auxOutputChannels))
|
||||||
{
|
{
|
||||||
description += " " + "+ re-amp: " + channelPairName(this.auxInputChannels, nInputs) + " -> " +
|
if (arrayEquals(this.mainInputChannels, this.auxInputChannels))
|
||||||
channelPairName(this.auxOutputChannels, nOutputs);
|
{
|
||||||
|
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)) {
|
if (isActiveChannel(this.sendInputChannels) && isActiveChannel(this.sendOutputChannels)) {
|
||||||
description += " " + "+ send: " +channelPairName(this.sendOutputChannels, nOutputs)
|
description += " " + ", send: " +channelPairName(this.sendOutputChannels, nOutputs, false)
|
||||||
+ " -> " + channelPairName(this.sendInputChannels, nInputs);
|
+ " -> " + channelPairName(this.sendInputChannels, nInputs,true);
|
||||||
}
|
}
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
@@ -128,6 +162,7 @@ export default class ChannelRouterSettings {
|
|||||||
return jackConfiguration.isValid;
|
return jackConfiguration.isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
isValid(jackConfiguration: JackConfiguration): boolean {
|
isValid(jackConfiguration: JackConfiguration): boolean {
|
||||||
if (!this.configured) {
|
if (!this.configured) {
|
||||||
return false;
|
return false;
|
||||||
@@ -176,5 +211,11 @@ export default class ChannelRouterSettings {
|
|||||||
}
|
}
|
||||||
return true;
|
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 ChannelRouterSettingsHelpDialog from './ChannelRouterSettingsHelpDialog';
|
||||||
import SpeakerIcon from '@mui/icons-material/Speaker';
|
import SpeakerIcon from '@mui/icons-material/Speaker';
|
||||||
import MicIcon from '@mui/icons-material/Mic';
|
import MicIcon from '@mui/icons-material/Mic';
|
||||||
|
import SaveIconOutline from '@mui/icons-material/Save';
|
||||||
|
|
||||||
import DialogTitle from '@mui/material/DialogTitle';
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
import DialogContent from '@mui/material/DialogContent';
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import ChannelRouterSettings from './ChannelRouterSettings';
|
import ChannelRouterSettings, { MAIN_OUT_LEFT_CHANNEL, MAIN_OUT_RIGHT_CHANNEL } from './ChannelRouterSettings';
|
||||||
import { Pedalboard, ControlValue } from './Pedalboard';
|
import { Pedalboard } from './Pedalboard';
|
||||||
|
|
||||||
import DialogEx from './DialogEx';
|
import DialogEx from './DialogEx';
|
||||||
|
|
||||||
import { PiPedalModelFactory } from './PiPedalModel';
|
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||||
|
import { JackConfiguration } from './Jack';
|
||||||
|
|
||||||
|
|
||||||
let debugInputChannels: number | null = 2;
|
let debugInputChannels: number | null = 1;
|
||||||
let debugOutputChannels: number | null = 4;
|
let debugOutputChannels: number | null = 1;
|
||||||
|
|
||||||
|
|
||||||
export interface ChannelRouterSettingsDialogProps {
|
export interface ChannelRouterSettingsDialogProps {
|
||||||
@@ -65,129 +67,168 @@ interface ChannelRouterSettingsPreset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let channelRouterPresets: ChannelRouterSettingsPreset[] = [
|
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.
|
||||||
id: -2, name: "Left -> Left", settings: new ChannelRouterSettings().deserialize({
|
let DEFAULT_RIGHT_TO_STEREO_ROUTING_PRESET = -4; // corresponds to Right->Stereo out.
|
||||||
configured: true,
|
|
||||||
channelRouterPresetId: -2,
|
|
||||||
mainInputChannels: [0, 0],
|
|
||||||
mainOutputChannels: [0, 0],
|
|
||||||
mainInserts: new Pedalboard(),
|
|
||||||
auxInputChannels: [-1, -1],
|
|
||||||
auxOutputChannels: [-1, -1],
|
|
||||||
auxInserts: new Pedalboard(),
|
|
||||||
sendInputChannels: [-1, -1],
|
|
||||||
sendOutputChannels: [-1, -1],
|
|
||||||
controlValues: [],
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: -3, name: "Left -> Stereo", settings: new ChannelRouterSettings().deserialize({
|
|
||||||
configured: true,
|
|
||||||
channelRouterPresetId: -3,
|
|
||||||
mainInputChannels: [0, 0],
|
|
||||||
mainOutputChannels: [0, 1],
|
|
||||||
mainInserts: new Pedalboard(),
|
|
||||||
auxInputChannels: [-1, -1],
|
|
||||||
auxOutputChannels: [-1, -1],
|
|
||||||
auxInserts: new Pedalboard(),
|
|
||||||
sendInputChannels: [-1, -1],
|
|
||||||
sendOutputChannels: [-1, -1],
|
|
||||||
controlValues: [],
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: -4, name: "Right -> Stereo", settings: new ChannelRouterSettings().deserialize({
|
|
||||||
configured: true,
|
|
||||||
channelRouterPresetId: -4,
|
|
||||||
mainInputChannels: [1, 1],
|
|
||||||
mainOutputChannels: [0, 1],
|
|
||||||
mainInserts: new Pedalboard(),
|
|
||||||
auxInputChannels: [-1, -1],
|
|
||||||
auxOutputChannels: [-1, -1],
|
|
||||||
auxInserts: new Pedalboard(),
|
|
||||||
sendInputChannels: [-1, -1],
|
|
||||||
sendOutputChannels: [-1, -1],
|
|
||||||
controlValues: [],
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: -5, name: "Right -> Left + re-amp", settings: new ChannelRouterSettings().deserialize({
|
|
||||||
configured: true,
|
|
||||||
channelRouterPresetId: -5,
|
|
||||||
mainInputChannels: [1, 1],
|
|
||||||
mainOutputChannels: [0, 0],
|
|
||||||
mainInserts: new Pedalboard(),
|
|
||||||
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({
|
|
||||||
configured: true,
|
|
||||||
channelRouterPresetId: -6,
|
|
||||||
mainInputChannels: [0, 0],
|
|
||||||
mainOutputChannels: [0, 0],
|
|
||||||
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],
|
|
||||||
auxInserts: new Pedalboard(),
|
|
||||||
sendInputChannels: [-1, -1],
|
|
||||||
sendOutputChannels: [-1, -1],
|
|
||||||
controlValues: [],
|
|
||||||
|
|
||||||
})
|
let factoryRouterPresets: ChannelRouterSettings[] = [
|
||||||
},
|
// Input->Output
|
||||||
{
|
new ChannelRouterSettings().deserialize({
|
||||||
id: -9, name: "Left -> Stereo + stereo send", settings: new ChannelRouterSettings().deserialize({
|
configured: true,
|
||||||
configured: true,
|
channelRouterPresetId: -2,
|
||||||
channelRouterPresetId: -9,
|
mainInputChannels: [0, -1],
|
||||||
mainInputChannels: [0, 0],
|
mainOutputChannels: [0, -1],
|
||||||
mainOutputChannels: [0, 1],
|
mainInserts: new Pedalboard(),
|
||||||
mainInserts: new Pedalboard(),
|
auxInputChannels: [-1, -1],
|
||||||
auxInputChannels: [-1, -1],
|
auxOutputChannels: [-1, -1],
|
||||||
auxOutputChannels: [-1, - 1],
|
auxInserts: new Pedalboard(),
|
||||||
auxInserts: new Pedalboard(),
|
sendInputChannels: [-1, -1],
|
||||||
sendInputChannels: [2, 3],
|
sendOutputChannels: [-1, -1],
|
||||||
sendOutputChannels: [2, 3],
|
controlValues: []
|
||||||
controlValues: [],
|
})
|
||||||
|
,
|
||||||
})
|
// Input->Stereo out.
|
||||||
},
|
new ChannelRouterSettings().deserialize({
|
||||||
|
configured: true,
|
||||||
|
channelRouterPresetId: -3,
|
||||||
|
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: []
|
||||||
|
})
|
||||||
|
,
|
||||||
|
// Right->Stereo out.
|
||||||
|
new ChannelRouterSettings().deserialize({
|
||||||
|
configured: true,
|
||||||
|
channelRouterPresetId: -4,
|
||||||
|
mainInputChannels: [1,-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: -5,
|
||||||
|
mainInputChannels: [1,-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: -6,
|
||||||
|
mainInputChannels: [1,-1],
|
||||||
|
mainOutputChannels: [1,-1],
|
||||||
|
mainInserts: new Pedalboard(),
|
||||||
|
auxInputChannels: [0, -1],
|
||||||
|
auxOutputChannels: [0, -1],
|
||||||
|
auxInserts: new Pedalboard(),
|
||||||
|
sendInputChannels: [-1, -1],
|
||||||
|
sendOutputChannels: [-1, -1],
|
||||||
|
controlValues: [],
|
||||||
|
})
|
||||||
|
,
|
||||||
|
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, -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],
|
||||||
|
auxOutputChannels: [-1, -1],
|
||||||
|
auxInserts: new Pedalboard(),
|
||||||
|
sendInputChannels: [2, 3],
|
||||||
|
sendOutputChannels: [2, 3],
|
||||||
|
controlValues: []
|
||||||
|
})
|
||||||
|
,
|
||||||
];
|
];
|
||||||
|
|
||||||
let cellPortraitInOutDiv: React.CSSProperties = {
|
let cellPortraitInOutDiv: React.CSSProperties = {
|
||||||
@@ -248,41 +289,132 @@ let cellPortraitInOut: React.CSSProperties = {
|
|||||||
verticalAlign: "middle"
|
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>)]
|
let items: React.ReactElement[] = [(<MenuItem key={-1} value={-1}>None</MenuItem>)]
|
||||||
if (channelCount === 0) {
|
if (channelCount === 0) {
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
if (channelCount === 2) {
|
if (channelCount === 1) {
|
||||||
items.push((<MenuItem key={0} value={0}>Left</MenuItem>));
|
if (input) {
|
||||||
items.push((<MenuItem key={1} value={1}>Right</MenuItem>));
|
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;
|
return items;
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < channelCount; ++i) {
|
||||||
|
items.push((<MenuItem key={i} value={i} disabled={disallowedSelections.includes(i)}>Ch {i + 1}</MenuItem>));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (let i = 0; i < channelCount; ++i) {
|
if (routeType === RouteType.Aux && input) {
|
||||||
items.push((<MenuItem key={i} value={i}>Ch {i + 1}</MenuItem>));
|
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;
|
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) {
|
function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||||
//const classes = useStyles();
|
//const classes = useStyles();
|
||||||
const { open, onClose } = props;
|
const { open, onClose } = props;
|
||||||
let model = PiPedalModelFactory.getInstance();
|
let model = PiPedalModelFactory.getInstance();
|
||||||
let config = model.jackConfiguration.get();
|
let config = model.jackConfiguration.get();
|
||||||
|
if (debugInputChannels != null || debugOutputChannels != null) {
|
||||||
|
config = makeDebugConfig(config);
|
||||||
|
}
|
||||||
|
let channelRouterPresets = makeRoutingPresets(config);
|
||||||
|
|
||||||
|
const [settings, setSettings] = useState<ChannelRouterSettings>(GetDefaultChannelRouterSettings(model));
|
||||||
const [settings, setSettings] = useState<ChannelRouterSettings>(model.channelRouterSettings.get());
|
|
||||||
const [controlValues, setControlValues] = useState<ControlValue[]>(model.channelRouterControlValues.get());
|
|
||||||
const [showHelp, setShowHelp] = useState<boolean>(false);
|
const [showHelp, setShowHelp] = useState<boolean>(false);
|
||||||
|
|
||||||
if (settings) {
|
|
||||||
}
|
|
||||||
if (controlValues) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
@@ -290,13 +422,8 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
|||||||
setSettings(model.channelRouterSettings.get());
|
setSettings(model.channelRouterSettings.get());
|
||||||
};
|
};
|
||||||
model.channelRouterSettings.addOnChangedHandler(handleSettingsChanged);
|
model.channelRouterSettings.addOnChangedHandler(handleSettingsChanged);
|
||||||
let handleControlsChanged = () => {
|
|
||||||
setControlValues(model.channelRouterControlValues.get());
|
|
||||||
};
|
|
||||||
model.channelRouterControlValues.addOnChangedHandler(handleControlsChanged);
|
|
||||||
return () => {
|
return () => {
|
||||||
model.channelRouterSettings.removeOnChangedHandler(handleSettingsChanged);
|
model.channelRouterSettings.removeOnChangedHandler(handleSettingsChanged);
|
||||||
model.channelRouterControlValues.removeOnChangedHandler(handleControlsChanged);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return () => { };
|
return () => { };
|
||||||
@@ -321,61 +448,94 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
|||||||
|
|
||||||
let value: number;
|
let value: number;
|
||||||
|
|
||||||
|
let disallowedSelections: number[] = [];
|
||||||
|
|
||||||
|
let channelSelection: number[] = [];
|
||||||
switch (routeType) {
|
switch (routeType) {
|
||||||
case RouteType.Main:
|
case RouteType.Main:
|
||||||
value = input ? settings.mainInputChannels[channelIndex] : settings.mainOutputChannels[channelIndex];
|
channelSelection = input ? settings.mainInputChannels : settings.mainOutputChannels;
|
||||||
break;
|
break;
|
||||||
case RouteType.Aux:
|
case RouteType.Aux:
|
||||||
value = input ? settings.auxInputChannels[channelIndex] : settings.auxOutputChannels[channelIndex];
|
channelSelection = input ? settings.auxInputChannels : settings.auxOutputChannels;
|
||||||
break;
|
break;
|
||||||
case RouteType.Send:
|
case RouteType.Send:
|
||||||
value = input ? settings.sendInputChannels[channelIndex] : settings.sendOutputChannels[channelIndex];
|
channelSelection = input ? settings.sendInputChannels : settings.sendOutputChannels;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
value = channelSelection[channelIndex];
|
||||||
|
|
||||||
if (value >= channelCount) {
|
if (value >= channelCount) {
|
||||||
value = -1;
|
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 (
|
return (
|
||||||
<Select variant="standard" style={{ width: 90 }} value={value}
|
<Select variant="standard" style={{ width: icon ? 130 : 110 }} value={value}
|
||||||
startAdornment={
|
startAdornment={
|
||||||
icon ? (
|
icon ? (
|
||||||
input ? (<MicIcon style={{ opacity: 0.6, width: 16, height: 16, marginRight: 4 }} />) :
|
input ? (<MicIcon style={{ opacity: 0.6, width: 16, height: 16, marginRight: 4 }} />) :
|
||||||
(<SpeakerIcon style={{ opacity: 0.6, width: 16, height: 16, marginRight: 4 }} />)
|
(<SpeakerIcon style={{ opacity: 0.6, width: 16, height: 16, marginRight: 4 }} />)
|
||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
|
error={error}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
let newValue = event.target.value as number;
|
let newValue = event.target.value as number;
|
||||||
let newSettings = new ChannelRouterSettings().deserialize(settings);
|
let newSettings = new ChannelRouterSettings().deserialize(settings);
|
||||||
switch (routeType) {
|
switch (routeType) {
|
||||||
case RouteType.Main:
|
case RouteType.Main:
|
||||||
if (input) {
|
if (input) {
|
||||||
newSettings.mainInputChannels[channelIndex] = newValue;
|
newSettings.mainInputChannels[channelIndex] = newValue;
|
||||||
|
normalizeChannelSelection(newSettings.mainInputChannels);
|
||||||
} else {
|
} else {
|
||||||
newSettings.mainOutputChannels[channelIndex] = newValue;
|
newSettings.mainOutputChannels[channelIndex] = newValue;
|
||||||
|
normalizeChannelSelection(newSettings.mainOutputChannels);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RouteType.Aux:
|
case RouteType.Aux:
|
||||||
if (input) {
|
if (input) {
|
||||||
newSettings.auxInputChannels[channelIndex] = newValue;
|
newSettings.auxInputChannels[channelIndex] = newValue;
|
||||||
|
normalizeChannelSelection(newSettings.auxInputChannels);
|
||||||
} else {
|
} else {
|
||||||
newSettings.auxOutputChannels[channelIndex] = newValue;
|
newSettings.auxOutputChannels[channelIndex] = newValue;
|
||||||
|
normalizeChannelSelection(newSettings.auxOutputChannels);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RouteType.Send:
|
case RouteType.Send:
|
||||||
if (input) {
|
if (input) {
|
||||||
newSettings.sendInputChannels[channelIndex] = newValue;
|
newSettings.sendInputChannels[channelIndex] = newValue;
|
||||||
|
normalizeChannelSelection(newSettings.sendInputChannels);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
newSettings.sendOutputChannels[channelIndex] = newValue;
|
newSettings.sendOutputChannels[channelIndex] = newValue;
|
||||||
|
normalizeChannelSelection(newSettings.sendOutputChannels);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
newSettings.channelRouterPresetId = -1; // custom.
|
if (newSettings.channelRouterPresetId >= 0) {
|
||||||
|
newSettings.modified = true; // custom.
|
||||||
|
} else {
|
||||||
|
newSettings.modified = true;
|
||||||
|
newSettings.channelRouterPresetId = -1; // custom.
|
||||||
|
}
|
||||||
newSettings.configured = true;
|
newSettings.configured = true;
|
||||||
model.setChannelRouterSettings(newSettings);
|
model.setChannelRouterSettings(newSettings);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{MakeChannelMenu(channelCount)}
|
{MakeChannelMenu(channelCount, routeType, input, disallowedSelections)}
|
||||||
</Select>
|
</Select>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -437,18 +597,32 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
|||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
let handleSave = () => {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
let PresetSelect = (width: number | string | undefined) => {
|
let PresetSelect = (width: number | string | undefined) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ display: "flex", flexDirection: "row", marginTop: 2, alignItems: "center" }}>
|
<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" }}>
|
||||||
onChange={(event) => {
|
<IconButtonEx tooltip="Save current preset"
|
||||||
ApplyPresetId(event.target.value);
|
style={{ flex: "0 0 auto", color: "#FFFFFF" }}
|
||||||
}}
|
onClick={(e) => { handleSave(); }}
|
||||||
>
|
size="large">
|
||||||
{GeneratePresetMenuItems()}
|
<SaveIconOutline style={{ opacity: 0.75 }} color="inherit" />
|
||||||
</Select>
|
</IconButtonEx>
|
||||||
<IconButtonEx tooltip="Presets" aria-label="more-presets">
|
</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>
|
||||||
|
</div>
|
||||||
|
<IconButtonEx tooltip="Presets" aria-label="more-presets" style={{ marginRight: 24 }}>
|
||||||
<MoreVertIcon style={{ opacity: 0.6 }} onClick={() => { }} />
|
<MoreVertIcon style={{ opacity: 0.6 }} onClick={() => { }} />
|
||||||
</IconButtonEx>
|
</IconButtonEx>
|
||||||
</div>
|
</div>
|
||||||
@@ -457,6 +631,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
|||||||
|
|
||||||
|
|
||||||
let LandscapeView = () => {
|
let LandscapeView = () => {
|
||||||
|
let auxInsertDisabled = !settings.isAuxActive();
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<div style={{
|
||||||
display: "flex", flexFlow: "row", alignItems: "center", justifyContent: "center",
|
display: "flex", flexFlow: "row", alignItems: "center", justifyContent: "center",
|
||||||
@@ -518,7 +693,11 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
|||||||
</td>
|
</td>
|
||||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||||
<td rowSpan={2} style={cellLeft}>
|
<td rowSpan={2} style={cellLeft}>
|
||||||
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
<Button variant="outlined" style={{
|
||||||
|
textTransform: "none", borderRadius: 24,
|
||||||
|
|
||||||
|
}}
|
||||||
|
disabled={auxInsertDisabled}>
|
||||||
Inserts
|
Inserts
|
||||||
</Button>
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
@@ -574,6 +753,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
let PortraitView = () => {
|
let PortraitView = () => {
|
||||||
|
let auxInsertDisabled = !settings.isAuxActive();
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<div style={{
|
||||||
display: "flex", flexFlow: "row", alignItems: "center", justifyContent: "center",
|
display: "flex", flexFlow: "row", alignItems: "center", justifyContent: "center",
|
||||||
@@ -685,7 +865,11 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
|||||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||||
<td style={cellPortraitControlStrip}>
|
<td style={cellPortraitControlStrip}>
|
||||||
<div style={{ marginLeft: 8, marginRight: 8 }}>
|
<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
|
Inserts
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -105,15 +105,15 @@ export default class ChannelRouterSettingsHelpDialog extends ResizeResponsiveCom
|
|||||||
|
|
||||||
<DialogContent style={{ minHeight: 96 }}>
|
<DialogContent style={{ minHeight: 96 }}>
|
||||||
<div style={{ fontSize: "0.9em", lineHeight: "18pt" }}>
|
<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>
|
||||||
<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 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
|
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
|
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.
|
in which you are currently performing.
|
||||||
</p>
|
</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
|
used for
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<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.
|
mixing it with the processed guitar signal, or passing it out on a dedicated output channel.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</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
|
<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
|
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
|
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
|
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
|
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
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ export default class JackServerSettings {
|
|||||||
this.rebootRequired = input.rebootRequired;
|
this.rebootRequired = input.rebootRequired;
|
||||||
this.alsaInputDevice = input.alsaInputDevice ?? "";
|
this.alsaInputDevice = input.alsaInputDevice ?? "";
|
||||||
this.alsaOutputDevice = input.alsaOutputDevice ?? "";
|
this.alsaOutputDevice = input.alsaOutputDevice ?? "";
|
||||||
|
this.alsaInputDeviceName = input.alsaInputDeviceName ?? this.alsaInputDevice;
|
||||||
|
this.alsaOutputDeviceName = input.alsaOutputDeviceName ?? this.alsaOutputDevice;
|
||||||
this.sampleRate = input.sampleRate;
|
this.sampleRate = input.sampleRate;
|
||||||
this.bufferSize = input.bufferSize;
|
this.bufferSize = input.bufferSize;
|
||||||
this.numberOfBuffers = input.numberOfBuffers;
|
this.numberOfBuffers = input.numberOfBuffers;
|
||||||
@@ -50,6 +52,8 @@ export default class JackServerSettings {
|
|||||||
isJackAudio = false;
|
isJackAudio = false;
|
||||||
alsaInputDevice: string = "";
|
alsaInputDevice: string = "";
|
||||||
alsaOutputDevice: string = "";
|
alsaOutputDevice: string = "";
|
||||||
|
alsaInputDeviceName: string = "";
|
||||||
|
alsaOutputDeviceName: string = "";
|
||||||
sampleRate = 48000;
|
sampleRate = 48000;
|
||||||
bufferSize = 64;
|
bufferSize = 64;
|
||||||
numberOfBuffers = 3;
|
numberOfBuffers = 3;
|
||||||
@@ -74,19 +78,12 @@ export default class JackServerSettings {
|
|||||||
return "Not selected";
|
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;
|
let device: string;
|
||||||
if (inDev === outDev) {
|
if (this.alsaInputDevice === this.alsaOutputDevice) {
|
||||||
device = inDev;
|
device = this.alsaInputDeviceName;;
|
||||||
|
}
|
||||||
} else {
|
else {
|
||||||
device = inDev + "-> " + outDev;
|
device = this.alsaInputDeviceName + " -> " + this.alsaOutputDeviceName;
|
||||||
}
|
}
|
||||||
return `${device} ${this.sampleRate} ${this.bufferSize}x${this.numberOfBuffers}`;
|
return `${device} ${this.sampleRate} ${this.bufferSize}x${this.numberOfBuffers}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -580,10 +580,20 @@ const JackServerSettingsDialog = withStyles(
|
|||||||
this.setState({ suppressDeviceWarning: checked });
|
this.setState({ suppressDeviceWarning: checked });
|
||||||
localStorage.setItem("suppressSeparateDeviceWarning", checked ? "1" : "0");
|
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) {
|
handleInputDeviceChanged(e: any) {
|
||||||
const d = e.target.value as string;
|
const d = e.target.value as string;
|
||||||
let s = this.state.jackServerSettings.clone();
|
let s = this.state.jackServerSettings.clone();
|
||||||
s.alsaInputDevice = d;
|
s.alsaInputDevice = d;
|
||||||
|
s.alsaInputDeviceName = this.getDeviceName(d);
|
||||||
s.valid = false;
|
s.valid = false;
|
||||||
let settings = this.applyAlsaDevices(s, this.state.alsaDevices);
|
let settings = this.applyAlsaDevices(s, this.state.alsaDevices);
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -597,6 +607,7 @@ const JackServerSettingsDialog = withStyles(
|
|||||||
const d = e.target.value as string;
|
const d = e.target.value as string;
|
||||||
let s = this.state.jackServerSettings.clone();
|
let s = this.state.jackServerSettings.clone();
|
||||||
s.alsaOutputDevice = d;
|
s.alsaOutputDevice = d;
|
||||||
|
s.alsaOutputDeviceName = this.getDeviceName(d);
|
||||||
s.valid = false;
|
s.valid = false;
|
||||||
let settings = this.applyAlsaDevices(s, this.state.alsaDevices);
|
let settings = this.applyAlsaDevices(s, this.state.alsaDevices);
|
||||||
this.setState({
|
this.setState({
|
||||||
|
|||||||