Manifest.json, Channel Router Sync. Alsa device enumeration.

This commit is contained in:
Robin E.R. Davies
2026-02-13 16:06:10 -05:00
parent cf7f499994
commit 564ed914cc
33 changed files with 2001 additions and 1191 deletions
+91
View File
@@ -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

+91
View File
@@ -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

+17 -1
View File
@@ -1895,8 +1895,14 @@ namespace pipedal
std::vector<float*> &channelBuffers
) {
size_t nChannels = channelSelection.size();
channelBuffers.resize(nChannels);
if (nChannels == 0)
{
channelBuffers.resize(1);
channelBuffers[0] = zeroInputBuffer;
return;
}
channelBuffers.resize(nChannels);
for (size_t i = 0; i < nChannels; ++i)
{
int64_t deviceChannel = channelSelection[i];
@@ -1932,6 +1938,16 @@ namespace pipedal
) {
size_t nChannels = channelSelection.size();
if (nChannels == 0)
{
channelBuffers.resize(1);
if (discardOutputBuffer != nullptr)
{
discardOutputBuffer = AllocateAudioBuffer();
}
channelBuffers[0] = discardOutputBuffer;
return;
}
channelBuffers.resize(nChannels);
for (size_t i = 0; i < nChannels; ++i)
{
+1 -1
View File
@@ -87,7 +87,7 @@ public:
AlsaFormatEncodeDecodeTest(this);
JackServerSettings serverSettings("hw:M2", "hw:M2", 48000, 32, 3);
JackServerSettings serverSettings("hw:M2","hw:M2", 48000, 32, 3);
JackConfiguration jackConfiguration;
jackConfiguration.AlsaInitialize(serverSettings);
+75
View File
@@ -533,6 +533,7 @@ private:
std::vector<std::shared_ptr<Lv2Pedalboard>> activePedalboards; // pedalboards that have been sent to the audio queue.
Lv2Pedalboard *realtimeActivePedalboard = nullptr;
Lv2RoutingInserts realtimeRoutingInserts;
uint32_t sampleRate = 0;
uint64_t currentSample = 0;
@@ -894,6 +895,47 @@ private:
return false; // signal to caller that the effect has been replaced, and processing needs to start again.
}
case RingBufferCommand::ReplaceRoutingInserts:
{
Lv2RoutingInserts body;
realtimeReader.readComplete(&body);
auto oldValue = this->realtimeRoutingInserts;
this->realtimeRoutingInserts = body;
realtimeWriter.RoutingInsertsReplaced(oldValue);
// invalidate the possibly no-good subscriptions. Model will update them shortly.
freeRealtimeVuConfiguration();
freeRealtimeMonitorPortSubscriptions();
cancelParameterRequests();
Lv2Pedalboard *mainInserts = body.mainInserts;
if (mainInserts)
{
mainInserts->ResetAtomBuffers();
// issue patch gets for all writable path properties.
for (auto pEffect : mainInserts->GetEffects())
{
pEffect->RequestAllPathPatchProperties();
}
mainInserts->UpdateAudioPorts();
}
Lv2Pedalboard *auxInserts = body.auxInserts;
if (auxInserts)
{
auxInserts->ResetAtomBuffers();
// issue patch gets for all writable path properties.
for (auto pEffect : auxInserts->GetEffects())
{
pEffect->RequestAllPathPatchProperties();
}
auxInserts->UpdateAudioPorts();
}
reEntered = false;
return false; // signal to caller that the effect has been replaced, and processing needs to start again.
}
default:
throw PiPedalStateException("Unknown Ringbuffer command.");
}
@@ -1549,6 +1591,20 @@ public:
hostReader.read(&body);
OnActivePedalboardReleased(body.oldEffect);
}
else if (command == RingBufferCommand::RoutingInsertsReplaced)
{
Lv2RoutingInserts body;
hostReader.read(&body);
if (body.mainInserts)
{
OnActivePedalboardReleased(body.mainInserts);
}
if (body.auxInserts)
{
OnActivePedalboardReleased(body.auxInserts);
}
}
else if (command == RingBufferCommand::FreeSnapshot)
{
IndexedSnapshot *snapshot;
@@ -1782,6 +1838,25 @@ public:
}
}
virtual void SetChannelRoutingInserts(
const std::shared_ptr<Lv2Pedalboard> &mainInserts,
const std::shared_ptr<Lv2Pedalboard> &auxInserts
) override {
if (active && mainInserts)
{
mainInserts->Activate();
this->activePedalboards.push_back(mainInserts);
}
if (active && auxInserts)
{
auxInserts->Activate();
this->activePedalboards.push_back(auxInserts);
}
Lv2RoutingInserts routingInserts { mainInserts: mainInserts.get(), auxInserts: auxInserts.get()};
hostWriter.ReplaceRoutingInserts(routingInserts);
}
virtual void SetBypass(uint64_t instanceId, bool enabled)
{
std::lock_guard guard(mutex);
+6
View File
@@ -238,6 +238,12 @@ namespace pipedal
virtual void SetPedalboard(const std::shared_ptr<Lv2Pedalboard> &pedalboard) = 0;
virtual void SetChannelRoutingInserts(
const std::shared_ptr<Lv2Pedalboard> &mainInserts,
const std::shared_ptr<Lv2Pedalboard> &auxInserts
) = 0;
virtual void SetControlValue(uint64_t instanceId, const std::string &symbol, float value) = 0;
virtual void SetInputVolume(float value) = 0;
virtual void SetOutputVolume(float value) = 0;
+3 -1
View File
@@ -740,6 +740,7 @@ add_executable(pipedalconfig
ModFileTypes.cpp ModFileTypes.hpp
MimeTypes.cpp MimeTypes.hpp
PiPedalConfiguration.hpp PiPedalConfiguration.cpp
PiPedalAlsa.hpp PiPedalAlsa.cpp
JackServerSettings.hpp JackServerSettings.cpp
SystemConfigFile.hpp SystemConfigFile.cpp
WifiChannelSelectors.cpp WifiChannelSelectors.hpp
@@ -817,6 +818,7 @@ add_executable(pipedaladmind AdminMain.cpp CommandLineParser.hpp
SetWifiConfig.cpp SetWifiConfig.hpp
JackServerSettings.hpp JackServerSettings.cpp
PiPedalAlsa.hpp PiPedalAlsa.cpp
Lv2SystemdLogger.hpp Lv2SystemdLogger.cpp
SystemConfigFile.hpp SystemConfigFile.cpp
@@ -824,7 +826,7 @@ add_executable(pipedaladmind AdminMain.cpp CommandLineParser.hpp
asan_options.cpp
)
target_link_libraries(pipedaladmind PRIVATE PiPedalCommon pthread atomic stdc++fs systemd )
target_link_libraries(pipedaladmind PRIVATE PiPedalCommon pthread atomic stdc++fs systemd asound)
add_executable(processcopyrights copyrightMain.cpp
CommandLineParser.hpp
+30
View File
@@ -135,9 +135,20 @@ ChannelRouterSettings::ChannelRouterSettings()
}
std::vector<ChannelRouterPresetIndexEntry> ChannelRouterPresetBank::getIndexEntries()
{
std::vector<ChannelRouterPresetIndexEntry> result;
for (auto &entry: entries_)
{
result.push_back(ChannelRouterPresetIndexEntry{entry->id(), entry->name()});
}
return result;
}
JSON_MAP_BEGIN(ChannelRouterSettings)
JSON_MAP_REFERENCE(ChannelRouterSettings, configured)
JSON_MAP_REFERENCE(ChannelRouterSettings, changed)
JSON_MAP_REFERENCE(ChannelRouterSettings, channelRouterPresetId)
JSON_MAP_REFERENCE(ChannelRouterSettings, mainInputChannels)
JSON_MAP_REFERENCE(ChannelRouterSettings, mainOutputChannels)
@@ -149,3 +160,22 @@ JSON_MAP_REFERENCE(ChannelRouterSettings, sendInputChannels)
JSON_MAP_REFERENCE(ChannelRouterSettings, sendOutputChannels)
JSON_MAP_REFERENCE(ChannelRouterSettings, controlValues)
JSON_MAP_END()
JSON_MAP_BEGIN(ChannelRouterPresetIndexEntry)
JSON_MAP_REFERENCE(ChannelRouterPresetIndexEntry, id)
JSON_MAP_REFERENCE(ChannelRouterPresetIndexEntry, name)
JSON_MAP_END()
JSON_MAP_BEGIN(ChannelRouterPresetBankEntry)
JSON_MAP_REFERENCE(ChannelRouterPresetBankEntry, id)
JSON_MAP_REFERENCE(ChannelRouterPresetBankEntry, name)
JSON_MAP_REFERENCE(ChannelRouterPresetBankEntry, channelRouterSettings)
JSON_MAP_END()
JSON_MAP_BEGIN(ChannelRouterPresetBank)
JSON_MAP_REFERENCE(ChannelRouterPresetBank, nextId)
JSON_MAP_REFERENCE(ChannelRouterPresetBank, version)
JSON_MAP_REFERENCE(ChannelRouterPresetBank, entries)
JSON_MAP_END()
+54
View File
@@ -35,6 +35,7 @@ namespace pipedal
bool configured_ = false;
int64_t channelRouterPresetId_ = -1;
bool changed_ = false;
std::vector<int64_t> mainInputChannels_ = {1, 1};
std::vector<int64_t> mainOutputChannels_ = {0, 1};
@@ -60,6 +61,7 @@ namespace pipedal
JSON_GETTER_SETTER(configured)
JSON_GETTER_SETTER(channelRouterPresetId)
JSON_GETTER_SETTER(changed)
JSON_GETTER_SETTER_REF(mainInputChannels)
JSON_GETTER_SETTER_REF(mainOutputChannels)
JSON_GETTER_SETTER_REF(mainInserts)
@@ -110,4 +112,56 @@ namespace pipedal
std::vector<int64_t> sendOutputChannels_;
};
class ChannelRouterPresetIndexEntry {
private:
int64_t id_ = -1;
std::string name_;
public:
ChannelRouterPresetIndexEntry() = default;
ChannelRouterPresetIndexEntry(const ChannelRouterPresetIndexEntry&other) = default;
ChannelRouterPresetIndexEntry(ChannelRouterPresetIndexEntry&&other) = default;
ChannelRouterPresetIndexEntry&operator=(const ChannelRouterPresetIndexEntry&other) = default;
ChannelRouterPresetIndexEntry&operator=(ChannelRouterPresetIndexEntry&&other) = default;
ChannelRouterPresetIndexEntry(int64_t id, const std::string&name)
: id_(id),name_(name) { }
JSON_GETTER_SETTER(id)
JSON_GETTER_SETTER_REF(name)
DECLARE_JSON_MAP(ChannelRouterPresetIndexEntry);
};
class ChannelRouterPresetBankEntry {
private:
int64_t id_;
std::string name_;
ChannelRouterSettings channelRouterSettings_;
public:
JSON_GETTER_SETTER(id)
JSON_GETTER_SETTER_REF(name)
JSON_GETTER_SETTER_REF(channelRouterSettings)
DECLARE_JSON_MAP(ChannelRouterPresetBankEntry);
};
class ChannelRouterPresetBank {
private:
int64_t nextId_ = 1;
int64_t version_ = 1;
std::vector<std::shared_ptr<ChannelRouterPresetBankEntry>> entries_;
public:
JSON_GETTER_SETTER(nextId)
JSON_GETTER_SETTER(version)
JSON_GETTER_SETTER_REF(entries)
std::vector<ChannelRouterPresetIndexEntry> getIndexEntries();
DECLARE_JSON_MAP(ChannelRouterPresetBank);
};
}
+2
View File
@@ -149,6 +149,8 @@ void JackConfiguration::AlsaInitialize(
}
} catch (const std::exception& /*ignore*/)
{
this->isValid_ = false;
this->errorStatus_ = "Not configured.";
throw;
}
+45 -136
View File
@@ -28,6 +28,7 @@
#include <filesystem>
#include "Lv2Log.hpp"
#include "SysExec.hpp"
#include "PiPedalAlsa.hpp"
#define DRC_FILENAME "/etc/jackdrc"
@@ -60,33 +61,41 @@ static std::vector<std::string> SplitArgs(const char *szBuff)
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;
for (size_t i = 1; i < args.size(); ++i)
{
auto pos = args[i].rfind(longOption,0);
if (pos != std::string::npos) {
auto pos = args[i].rfind(longOption, 0);
if (pos != std::string::npos)
{
if (args[i].length() == longOption.length())
{
if (i == args.size()-1) {
if (i == args.size() - 1)
{
throw PiPedalException("Can't read Jack configuration.");
}
strVal = args[i+1];
} else {
strVal = args[i + 1];
}
else
{
strVal = args[i].substr(longOption.length());
}
break;
}
pos = args[i].rfind(shortOption,0);
if (pos != std::string::npos) {
pos = args[i].rfind(shortOption, 0);
if (pos != std::string::npos)
{
if (args[i].length() == shortOption.length())
{
if (i == args.size()-1) {
if (i == args.size() - 1)
{
throw PiPedalException("Can't read Jack configuration.");
}
strVal = args[i+1];
} else {
strVal = args[i + 1];
}
else
{
strVal = args[i].substr(shortOption.length());
}
break;
@@ -94,9 +103,9 @@ static std::string GetJackStringArg(const std::vector<std::string> &args, const
}
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
{
unsigned long value = std::stoul(strVal);
@@ -108,144 +117,42 @@ static std::int32_t GetJackArg(const std::vector<std::string> &args, const std::
}
}
void JackServerSettings::ReadJackDaemonConfiguration()
static std::string GetAlsaDeviceName(const std::vector<AlsaDeviceInfo> &availableDevices, const std::string &id)
{
#if !JACK_HOST
return;
#endif
this->valid_ = false;
std::string lastLine;
if (id.empty())
return "";
std::string name;
for (const auto &availableDevice : availableDevices)
{
ifstream input(DRC_FILENAME);
if (!input.is_open())
if (availableDevice.id_ == id)
{
return;
}
while (true)
{
std::string line;
std::getline(input,line);
if (line.length() != 0) {
lastLine = line;
}
if (input.eof()) {
name = availableDevice.name_;
break;
}
}
try
if (name.empty())
{
std::vector<std::string> argv = SplitArgs(lastLine.c_str());
for (auto i = argv.begin(); i != argv.end(); ++i)
auto pos = id.find(":");
if (pos != std::string::npos)
{
if ((*i) == "-dalsa") {
argv.erase(i);
break;
}
}
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);
name = id.substr(pos + 1);
}
}
return name;
}
void JackServerSettings::WriteDaemonConfig()
void JackServerSettings::FixUpDeviceNames()
{
#if JACK_HOST
this->valid_ = false;
if (
((!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;
PiPedalAlsaDevices& alsaDevices = PiPedalAlsaDevices::instance();
std::vector<AlsaDeviceInfo> availableDevices = alsaDevices.GetAlsaDevices();
if (std::filesystem::exists(DRC_FILENAME)) {
ifstream input(DRC_FILENAME);
if (!input.is_open())
{
return;
this->alsaInputDeviceName_ = GetAlsaDeviceName(availableDevices, this->alsaInputDevice_);
this->alsaOutputDeviceName_ = GetAlsaDeviceName(availableDevices, this->alsaOutputDevice_);
}
while (true)
{
if (input.eof())
{
break;
}
std::getline(input,lastLine);
precedingLines.push_back(lastLine);
if (input.eof())
{
break;
}
}
// erase blank lines at the end.
while (precedingLines.size() != 0 && precedingLines[precedingLines.size()-1] == "")
{
precedingLines.erase(precedingLines.begin()+precedingLines.size()-1);
}
// erase the last line, which should contain the command invocation.
if (precedingLines.size() != 0)
{
precedingLines.erase(precedingLines.begin()+precedingLines.size()-1);
}
}
// write to the output.
try
{
ofstream output(DRC_FILENAME);
if (!output.is_open())
{
throw PiPedalException("Can't write " DRC_FILENAME);
}
if (precedingLines.size() == 0)
{
// jack1 incantation for promiscuous servers.
output << "#!/bin/sh" <<endl;
output << "export JACK_PROMISCUOUS_SERVER=audio" << endl;
output << "export JACK_NO_AUDIO_RESERVATION=1" << endl;
output << "umask 0" << endl;
}
for (auto line : precedingLines)
{
output << line << endl;
}
// the style used by qjackctl. :-/
// Lower to -P70 in order to allow the USB soft-irq to run at higher priority than JACK (it runs at 80).
output << "/usr/bin/jackd "
<< "-R -P70 --silent"
<< " -dalsa -d" << this->alsaDevice_
<< " -r" << this->sampleRate_
<< " -p" << this->bufferSize_
<< " -n" << this->numberOfBuffers_ << " -Xseq"
<< endl;
if (silentSysExec("/usr/bin/chmod 755 " DRC_FILENAME) != 0)
{
Lv2Log::error("Failed to set permissions on /etc/jackdrc");
}
}
catch (const std::exception &e)
{
}
#else
throw PiPedalStateException("JACK_HOST not enabled at compile time.");
#endif
}
JSON_MAP_BEGIN(JackServerSettings)
@@ -256,6 +163,8 @@ JSON_MAP_REFERENCE(JackServerSettings, isJackAudio)
JSON_MAP_REFERENCE(JackServerSettings, alsaDevice) // legacy field
JSON_MAP_REFERENCE(JackServerSettings, alsaInputDevice)
JSON_MAP_REFERENCE(JackServerSettings, alsaOutputDevice)
JSON_MAP_REFERENCE(JackServerSettings, alsaInputDeviceName)
JSON_MAP_REFERENCE(JackServerSettings, alsaOutputDeviceName)
JSON_MAP_REFERENCE(JackServerSettings, sampleRate)
JSON_MAP_REFERENCE(JackServerSettings, bufferSize)
JSON_MAP_REFERENCE(JackServerSettings, numberOfBuffers)
+11 -5
View File
@@ -33,7 +33,9 @@ namespace pipedal
bool isJackAudio_ = JACK_HOST ? true : false;
bool rebootRequired_ = false;
std::string alsaInputDevice_;
std::string alsaInputDeviceName_;
std::string alsaOutputDevice_;
std::string alsaOutputDeviceName_;
std::string alsaDevice_; // legacy
uint64_t sampleRate_ = 0;
uint32_t bufferSize_ = 64;
@@ -44,6 +46,7 @@ namespace pipedal
JackServerSettings(
const std::string &alsaInputDevice,
const std::string &alsaOutputDevice,
uint64_t sampleRate,
uint32_t bufferSize,
uint32_t numberOfBuffers)
@@ -55,6 +58,7 @@ namespace pipedal
numberOfBuffers_(numberOfBuffers),
isOnboarding_(false)
{
FixUpDeviceNames();
}
uint64_t GetSampleRate() const { return sampleRate_; }
@@ -62,11 +66,13 @@ namespace pipedal
uint32_t GetBufferSize() const { return bufferSize_; }
uint32_t GetNumberOfBuffers() const { return numberOfBuffers_; }
const std::string &GetAlsaInputDevice() const { return alsaInputDevice_; }
const std::string &GetAlsaInputDeviceName() const { return alsaInputDeviceName_; }
const std::string &GetAlsaOutputDevice() const { return alsaOutputDevice_; }
const std::string &GetAlsaOutputDeviceName() const { return alsaOutputDeviceName_; }
const std::string &GetLegacyAlsaDevice() const { return alsaDevice_; } //legacy
void SetAlsaInputDevice(const std::string &d){ alsaInputDevice_ = d; }
void SetAlsaOutputDevice(const std::string &d){ alsaOutputDevice_ = d; }
void SetAlsaInputDevice(const std::string &id, const std::string&name){ alsaInputDevice_ = id; alsaInputDeviceName_ = name; }
void SetAlsaOutputDevice(const std::string &id, const std::string&name){ alsaOutputDevice_ = id; alsaOutputDeviceName_ = name; }
void SetLegacyAlsaDevice(const std::string &d) { alsaDevice_ = d; }
void UseDummyAudioDevice() {
@@ -74,7 +80,9 @@ namespace pipedal
if (sampleRate_ == 0) sampleRate_ = 48000;
this->alsaDevice_ = "dummy:channels_2";
this->alsaInputDevice_ = "dummy:channels_2";
this->alsaInputDeviceName_ = "Stopped";
this->alsaOutputDevice_ = "dummy:channels_2";
this->alsaOutputDeviceName_ = "Stopped";
}
bool IsDummyAudioDevice() const {
return
@@ -83,11 +91,8 @@ namespace pipedal
|| this->alsaInputDevice_.starts_with("dummy:");
}
void ReadJackDaemonConfiguration();
bool IsValid() const { return valid_; }
void WriteDaemonConfig(); // requires root perms.
void SetRebootRequired(bool value)
{
rebootRequired_ = value;
@@ -106,6 +111,7 @@ namespace pipedal
this->bufferSize_ == other.bufferSize_ &&
this->numberOfBuffers_ == other.numberOfBuffers_;
}
void FixUpDeviceNames();
DECLARE_JSON_MAP(JackServerSettings);
};
+2 -3
View File
@@ -103,8 +103,7 @@ void PrintHelp()
void ListDevices()
{
PiPedalAlsaDevices alsaDevices;
auto devices = alsaDevices.GetAlsaDevices();
auto devices = PiPedalAlsaDevices::instance().GetAlsaDevices();
PrettyPrinter pp;
if (devices.size() == 0)
@@ -199,7 +198,7 @@ public:
TestResult result;
try
{
JackServerSettings serverSettings(inputDeviceId, outputDeviceId, sampleRate, bufferSize, buffers);
JackServerSettings serverSettings(inputDeviceId,outputDeviceId, sampleRate, bufferSize, buffers);
JackConfiguration jackConfiguration;
jackConfiguration.AlsaInitialize(serverSettings);
+67 -3
View File
@@ -160,11 +160,14 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
std::vector<AlsaDeviceInfo> result;
int cardNum = -1; // Start with first card
int err;
std::vector<ProcAlsaDevice> procAlsaDevices = getProcAlsaDevices();
snd_pcm_info_t *pcminfo = nullptr;
snd_pcm_info_alloca(&pcminfo);
for (const auto &procAlsaDevice: procAlsaDevices)
{
std::stringstream ss;
@@ -198,13 +201,60 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
err = snd_ctl_card_info(hDevice, alsaInfo);
if (err == 0)
{
AlsaDeviceInfo info;
info.cardId_ = cardNum;
info.cardId_ = procAlsaDevice.cardId;
info.id_ = std::string("hw:") + snd_ctl_card_info_get_id(alsaInfo);
const char *driver = snd_ctl_card_info_get_driver(alsaInfo);
(void)driver;
info.name_ = snd_ctl_card_info_get_name(alsaInfo);
snd_pcm_info_set_device(pcminfo, procAlsaDevice.subdeviceId);
snd_pcm_info_set_subdevice(pcminfo, 0);
snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK);
if ((err = snd_ctl_pcm_info(hDevice, pcminfo)) < 0) {
snd_pcm_info_set_device(pcminfo, procAlsaDevice.subdeviceId);
snd_pcm_info_set_subdevice(pcminfo, 0);
snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_CAPTURE);
if ((err = snd_ctl_pcm_info(hDevice, pcminfo)) < 0) {
if (err != -ENOENT)
Lv2Log::warning("control digital audio info (%i): %s", info.cardId_, snd_strerror(err));
continue;
}
}
/*
Names and IDs are f-ed up in ALSA. id seems to be vaguely reliable. names are garbage!
Sample data from Unbuntu 24.04.
snd_ctl_card_info_get_id(info) snd_ctl_card_info_get_name(alsaInfo) subdevice snd_pcm_info_get_id(pcminfo) snd_pcm_info_get_name(pcminfo)
Generic HD-Audio Generic 3 HDMI 0 Acer S231HL
Generic HD-Audio Generic 3 HDMI 0 ASUS MG28U
Generic_1 HD-Audio Generic 0 CX11970 Analog CX11970 Analog
M2 M2 USB Audio USB Audio
CODEC USB AUDIO CODEC USB Audio USB Audio
*/
std::string name;
{
// let's assume (without evidence) that names get localized, but ids do not. :-/
std::string cardId = snd_ctl_card_info_get_id(alsaInfo);
std::string cardName = snd_ctl_card_info_get_name(alsaInfo);
std::string pcmId = snd_pcm_info_get_id(pcminfo);
std::string pcmName = snd_pcm_info_get_name(pcminfo);
std::string driver = snd_ctl_card_info_get_driver(alsaInfo);
if (driver == "USB-Audio")
{
name = SS("USB - " << cardId);
}
else if (pcmId == pcmName)
{
name = pcmId;
} else {
name = SS(pcmId << '[' << pcmName << ']');
}
}
info.name_ = name;
info.longName_ = snd_ctl_card_info_get_longname(alsaInfo);
// we can't read our own device if it's open so use data that gets
@@ -219,6 +269,8 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
result.push_back(cachedInfo);
continue;
}
snd_pcm_t *captureDevice = nullptr;
snd_pcm_t *playbackDevice = nullptr;
auto rc = snd_pcm_open(&captureDevice, cardId.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
@@ -248,6 +300,7 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
if (captureOk || playbackOk)
{
snd_pcm_t *hDevice = captureOk ? captureDevice : playbackDevice;
snd_pcm_hw_params_t *params = nullptr;
err = snd_pcm_hw_params_malloc(&params);
@@ -637,6 +690,17 @@ AlsaMidiDeviceInfo::AlsaMidiDeviceInfo(const char *name, const char *description
this->subdevice_ = subdevice;
}
std::unique_ptr<PiPedalAlsaDevices> PiPedalAlsaDevices::instance_;
PiPedalAlsaDevices&PiPedalAlsaDevices::instance() {
if (!instance_)
{
instance_ = std::unique_ptr<PiPedalAlsaDevices>(new PiPedalAlsaDevices());
}
return (*instance_);
}
JSON_MAP_BEGIN(AlsaDeviceInfo)
JSON_MAP_REFERENCE(AlsaDeviceInfo, cardId)
JSON_MAP_REFERENCE(AlsaDeviceInfo, id)
+5 -1
View File
@@ -71,12 +71,16 @@ namespace pipedal {
};
class PiPedalAlsaDevices {
private:
static std::unique_ptr<PiPedalAlsaDevices> instance_;
PiPedalAlsaDevices() { }
std::map<std::string,AlsaDeviceInfo> cachedDevices;
bool getCachedDevice(const std::string&name, AlsaDeviceInfo*pResult);
void cacheDevice(const std::string&name, const AlsaDeviceInfo&deviceInfo);
public:
static PiPedalAlsaDevices&instance();
std::vector<AlsaDeviceInfo> GetAlsaDevices();
};
+1 -1
View File
@@ -34,7 +34,7 @@ using namespace std;
static void DiscoveryTest()
{
cout << "--- Discovery" << endl;
PiPedalAlsaDevices devices;
PiPedalAlsaDevices &devices = PiPedalAlsaDevices::instance();
auto result = devices.GetAlsaDevices();
std::cout << result.size() << " ALSA devices found." << std::endl;
+57 -53
View File
@@ -83,12 +83,9 @@ PiPedalModel::PiPedalModel()
this->updater->Start();
this->currentUpdateStatus = updater->GetCurrentStatus();
this->pedalboard = Pedalboard::MakeDefault(Pedalboard::PedalboardType::MainPedalboard);
#if JACK_HOST
this->jackServerSettings = this->storage.GetJackServerSettings(); // to get onboarding flag.
this->jackServerSettings.ReadJackDaemonConfiguration();
#else
this->jackServerSettings = this->storage.GetJackServerSettings();
#endif
updater->SetUpdateListener(
[this](const UpdateStatus &updateStatus)
{
@@ -224,21 +221,22 @@ void PiPedalModel::Init(const PiPedalConfiguration &configuration)
this->systemMidiBindings = storage.GetSystemMidiBindings();
#if JACK_HOST
this->jackConfiguration = this->jackConfiguration.JackInitialize();
#else
this->jackServerSettings = storage.GetJackServerSettings();
try
{
this->jackConfiguration.AlsaInitialize(jackServerSettings);
#endif
}
catch (const std::exception &)
{
//
}
this->channelRouterSettings = storage.GetChannelRouterSettings();
pluginHost.OnConfigurationChanged(
jackConfiguration,
storage.GetChannelSelection());
}
int64_t PiPedalModel::DownloadModelsFromTone3000(
int64_t clientId,
Tone3000DownloadType downloadType,
@@ -271,14 +269,12 @@ int64_t PiPedalModel::DownloadModelsFromTone3000(
return tone3000Downloader->RequestDownload(
downloadType,
downloadPath,
tone3000Url
);
tone3000Url);
}
void PiPedalModel::CancelTone3000Download(
int64_t clientId,
int64_t downloadHandle
)
int64_t downloadHandle)
{
std::lock_guard<std::recursive_mutex> lock(mutex);
@@ -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);
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());
storage.SavePluginPresets(plugin->uri(), pluginPresets);
} else {
}
else
{
if (pluginPresetIndexVersion == 0)
{
if (plugin->uri() == "http://two-play.com/plugins/toob-convolution-reverb" || plugin->uri() == "http://two-play.com/plugins/toob-convolution-reverb-stereo")
{
// overwrite previous factory presets!
PluginPresets pluginPresets = pluginHost.GetFactoryPluginPresets(plugin->uri());
for (auto & pluginPreset: pluginPresets.presets_)
for (auto &pluginPreset : pluginPresets.presets_)
{
storage.SavePluginPreset(
plugin->uri(),
pluginPreset);
}
}
}
}
@@ -772,13 +769,17 @@ void PiPedalModel::SetPedalboardItemEnable(int64_t clientId, int64_t pedalItemId
std::lock_guard<std::recursive_mutex> guard{mutex};
{
this->pedalboard.SetItemEnabled(pedalItemId, enabled);
PedalboardItem * pPedalboardItem = this->pedalboard.GetItem(pedalItemId);
if (pPedalboardItem) {
PedalboardItem *pPedalboardItem = this->pedalboard.GetItem(pedalItemId);
if (pPedalboardItem)
{
Lv2PluginInfo::ptr pluginInfo = GetPluginInfo(pPedalboardItem->uri());
if (pluginInfo) {
for (auto &port: pluginInfo->ports()) {
if (port->is_bypass()) {
pPedalboardItem->SetControlValue(port->symbol(), enabled? 1.0: 0.0f);
if (pluginInfo)
{
for (auto &port : pluginInfo->ports())
{
if (port->is_bypass())
{
pPedalboardItem->SetControlValue(port->symbol(), enabled ? 1.0 : 0.0f);
}
}
}
@@ -999,7 +1000,7 @@ int64_t PiPedalModel::SavePluginPresetAs(int64_t instanceId, const std::string &
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};
@@ -1523,7 +1524,7 @@ void PiPedalModel::RestartAudio(bool useDummyAudioDriver)
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);
@@ -1625,7 +1626,6 @@ std::vector<AlsaSequencerPortSelection> PiPedalModel::GetAlsaSequencerPorts()
return result;
}
void PiPedalModel::FireChannelRouterSettingsChanged(int64_t clientId)
{
std::lock_guard<std::recursive_mutex> guard{mutex};
@@ -2129,8 +2129,7 @@ std::shared_ptr<Lv2PluginInfo> PiPedalModel::GetPluginInfo(const std::string &ur
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());
if (!pPlugin)
@@ -2145,10 +2144,10 @@ void PiPedalModel::UpdateDefaults(SnapshotValue&snapshotValue, const PedalboardI
// Fix incorrect bypass settings presint in Pipedal < 1.5.96
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())
{
ControlValue* pValue = snapshotValue.GetControlValue(port->symbol());
ControlValue *pValue = snapshotValue.GetControlValue(port->symbol());
float value = snapshotValue.isEnabled_ ? 1.0f : 0.0f;
if (pValue == nullptr)
@@ -2166,7 +2165,8 @@ void PiPedalModel::UpdateDefaults(SnapshotValue&snapshotValue, const PedalboardI
if (pPlugin->uri() == "http://two-play.com/plugins/toob-nam")
{
ControlValue *pVersion = snapshotValue.GetControlValue("version");
if (pVersion == nullptr) {
if (pVersion == nullptr)
{
ControlValue *pValue = snapshotValue.GetControlValue("inputCalibrationMode");
if (pValue == nullptr)
{
@@ -2175,13 +2175,16 @@ void PiPedalModel::UpdateDefaults(SnapshotValue&snapshotValue, const PedalboardI
}
// convert old gate threshold to new gate threshold.
ControlValue *pGateValue = snapshotValue.GetControlValue("gate");
if (pGateValue) {
if (pGateValue)
{
float value = pGateValue->value();
// Is the gate disabled?
if (value <= -100.0f)
{
value = -120.0f; // "disabled in new range."
} else {
}
else
{
value = value * 0.5; // correct the bug in original implementation.
}
snapshotValue.SetControlValue("gate", value);
@@ -2245,7 +2248,8 @@ void PiPedalModel::UpdateDefaults(PedalboardItem *pedalboardItem, std::unordered
if (pPlugin->uri() == "http://two-play.com/plugins/toob-nam")
{
ControlValue *pVersion = pedalboardItem->GetControlValue("version");
if (pVersion == nullptr) {
if (pVersion == nullptr)
{
ControlValue *pValue = pedalboardItem->GetControlValue("inputCalibrationMode");
if (pValue == nullptr)
{
@@ -2254,13 +2258,16 @@ void PiPedalModel::UpdateDefaults(PedalboardItem *pedalboardItem, std::unordered
}
// convert old gate threshold to new gate threshold.
ControlValue *pGateValue = pedalboardItem->GetControlValue("gate");
if (pGateValue) {
if (pGateValue)
{
float value = pGateValue->value();
// Is the gate disabled?
if (value <= -100.0f)
{
value = -120.0f; // "disabled in new range."
} else {
}
else
{
value = value * 0.5; // correct the bug in original implementation.
}
pedalboardItem->SetControlValue("gate", value);
@@ -2278,18 +2285,20 @@ void PiPedalModel::UpdateDefaults(PedalboardItem *pedalboardItem, std::unordered
{
// retroactively correct bug in version of PiPedal prior to 1.5.96.
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)
{
pedalboardItem->controlValues().push_back(
pipedal::ControlValue(port->symbol().c_str(), value));
} else {
}
else
{
pValue->value(value);
}
} else {
}
else
{
ControlValue *pValue = pedalboardItem->GetControlValue(port->symbol());
if (pValue == nullptr)
{
@@ -2357,7 +2366,9 @@ void PiPedalModel::UpdateDefaults(Snapshot *snapshot, std::unordered_map<int64_t
// plugin is no longer present. Remove from the snapshot.
snapshot->values_.erase(snapshot->values_.begin() + i);
--i;
} else {
}
else
{
UpdateDefaults(value, f->second);
}
}
@@ -3376,7 +3387,6 @@ std::vector<PresetIndexEntry> PiPedalModel::RequestBankPresets(int64_t bankInsta
std::lock_guard<std::recursive_mutex> lock(mutex);
return storage.RequestBankPresets(bankInstanceId);
}
int64_t PiPedalModel::ImportPresetsFromBank(int64_t bankInstanceId, const std::vector<int64_t> &presets)
@@ -3386,24 +3396,21 @@ int64_t PiPedalModel::ImportPresetsFromBank(int64_t bankInstanceId, const std::v
FirePresetsChanged(-1);
return lastAdded;
}
int64_t PiPedalModel::CopyPresetsToBank(int64_t bankInstanceId, const std::vector<int64_t> &presets)
{
std::lock_guard<std::recursive_mutex> lock(mutex);
uint64_t lastAdded = storage.CopyPresetsToBank(bankInstanceId, presets);
return lastAdded;
}
ChannelRouterSettings::ptr PiPedalModel::GetChannelRouterSettings()
{
std::lock_guard<std::recursive_mutex> lock(mutex);
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);
@@ -3415,11 +3422,8 @@ void PiPedalModel::SetChannelRouterSettings(int64_t clientId,ChannelRouterSettin
RestartAudio(); // no lock to avoid mutex deadlock when reader thread is sending notifications..
this->FireChannelRouterSettingsChanged(clientId);
}
std::string PiPedalModel::Tone3000ThumbnailDirectory()
{
return "/var/pipedal/tone3000_thumbnails";
+1 -1
View File
@@ -152,7 +152,7 @@ namespace pipedal
std::unique_ptr<AvahiService> avahiService;
uint16_t webPort;
PiPedalAlsaDevices alsaDevices;
PiPedalAlsaDevices &alsaDevices = PiPedalAlsaDevices::instance();
std::recursive_mutex mutex;
AdminClient adminClient;
+916 -735
View File
File diff suppressed because it is too large Load Diff
+24 -4
View File
@@ -45,11 +45,19 @@ namespace pipedal
uint8_t cc2_;
};
struct Lv2RoutingInserts {
Lv2Pedalboard*mainInserts = nullptr;
Lv2Pedalboard*auxInserts = nullptr;;
};
enum class RingBufferCommand : int64_t
{
Invalid = 0,
ReplaceEffect,
EffectReplaced,
ReplaceRoutingInserts,
RoutingInsertsReplaced,
SetValue,
SetBypass,
// AudioStopped,
@@ -539,16 +547,28 @@ namespace pipedal
write(RingBufferCommand::ReplaceEffect, pedalboard);
}
void EffectReplaced(Lv2Pedalboard *pedalboard)
{
write(RingBufferCommand::EffectReplaced, pedalboard);
}
void ReplaceRoutingInserts(Lv2RoutingInserts routingInserts)
{
write(RingBufferCommand::ReplaceRoutingInserts, routingInserts);
}
void RoutingInsertsReplaced(Lv2RoutingInserts routingInserts)
{
write(RingBufferCommand::ReplaceRoutingInserts, routingInserts);
}
void AudioTerminatedAbnormally()
{
AudioStoppedBody body;
write(RingBufferCommand::AudioTerminatedAbnormally, body);
}
void EffectReplaced(Lv2Pedalboard *pedalboard)
{
write(RingBufferCommand::EffectReplaced, pedalboard);
}
void FreeSnapshot(IndexedSnapshot *snapshot)
{
+4 -8
View File
@@ -1931,14 +1931,13 @@ pipedal::JackServerSettings Storage::GetJackServerSettings()
result.GetAlsaInputDevice().empty() &&
result.GetAlsaOutputDevice().empty())
{
result.SetAlsaInputDevice(result.GetLegacyAlsaDevice());
result.SetAlsaOutputDevice(result.GetLegacyAlsaDevice());
std::string legacyDeviceId = result.GetLegacyAlsaDevice();
result.SetAlsaInputDevice(legacyDeviceId, "");
result.SetAlsaOutputDevice(legacyDeviceId, "");
result.SetLegacyAlsaDevice("");
}
result.FixUpDeviceNames();
}
#if JACK_HOST
result.Initialize();
#endif
return result;
}
@@ -1952,9 +1951,6 @@ void Storage::SetJackServerSettings(const pipedal::JackServerSettings &jackConfi
json_writer writer(f, false);
writer.write(jackConfiguration);
}
#if JACK_HOST
jackConfiguration.Write();
#endif
}
void Storage::SetSystemMidiBindings(const std::vector<MidiBinding> &bindings)
+1
View File
@@ -206,6 +206,7 @@ static void print_device_info(snd_ctl_t* handle, int card, int device) {
// Print basic info
std::cout << "\n Subdevice: " << name << " - " << device_name << std::endl;
std::cout << " ID: " << snd_pcm_info_get_id(pcminfo) << std::endl;
std::cout << " Name: " << snd_pcm_info_get_name(pcminfo) << std::endl;
std::cout << " Capabilities:";
if (has_playback) std::cout << " PLAYBACK";
if (has_capture) std::cout << " CAPTURE";
+8 -5
View File
@@ -1,20 +1,23 @@
A way to force input to mono?
Cab I/Rs: gear="ir"
summaries for Mono audio adapaters.
Check PWA on windows, edge and chrome.
Make sure Pi 4 default audio device is still hidden when selecting devices!
A final solution for rate-limiting on Tone3000.
A way to force input to mono?
// yyy: only if the property changed!.
Tooltip on File Property Select: add to release notes.
sAFEfILEnAMES: RENAME, &C. Safe bank names?
NEW BANK
WINDOW SIZE CALCUALTIONS ARE WRONG ON LINUX HIDEF DISPLAYS.
ls
Carla Project icons.
check reload after change of LV2 plugins.
Exactly one underrun per seek in Toob Player
json "skip" code doesn't work with complex objects (MidiChannelBinding specifically).
json nan/inf is fatal.
- pipewire aux in?
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 27 KiB

+16 -1
View File
@@ -1,6 +1,14 @@
{
"manifest_version": 3,
"short_name": "PiPedal",
"name": "PiPedal",
"author": "Robin E.R. Davies",
"description": "A web-based client for the PiPedal guitar effects processor.",
"developer": {
"name": "Robin E.R. Davies",
"url": "https://rerdavies.github.io"
},
"homepage_url": "https://rerdavies.github.io/pipedal",
"icons": [
{
"src": "favicon.ico",
@@ -20,5 +28,12 @@
],
"start_url": ".",
"theme_color": "#000000",
"background_color": "#ffffff"
"display": "standalone",
"display_override": [
"fullscreen",
"standalone",
"minimal-ui",
"browser"
],
"background_color": "#000000"
}
+1
View File
@@ -50,3 +50,4 @@ input[type=number] {
img {
user-select: none;
}
+51 -10
View File
@@ -3,24 +3,34 @@ import { Pedalboard, ControlValue } from "./Pedalboard.tsx";
import JackConfiguration from "./Jack.tsx";
export const NONE_CHANNEL: number = -1;
// valid for Aux channel input only.
export const MAIN_OUT_LEFT_CHANNEL: number = -2;
// valid for Aux channel input only.
export const MAIN_OUT_RIGHT_CHANNEL: number = -3;
function isActiveChannel(channels: number[]): boolean {
if (channels.length < 2) return false;
return channels[1] >= 0 || channels[0] >= 0;
return channels[1] !== -1 || channels[0] !== -1;
}
function chName(ch: number): string {
if (ch === -1) {
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) {
return "Invalid";
}
if (channels[0] === -1 && channels[1] === -1) {
return "None";
}
if (maxChannels === 1) {
return input ? "Input": "Output";
}
if (maxChannels === 2) {
if (channels[0] === 0 && channels[1] === 1) {
@@ -36,13 +46,28 @@ function channelPairName(channels: number[], maxChannels: number): string {
return "Right,Left";
}
}
return chName(channels[0]) + "," + chName(channels[1]);
if (channels[0] === channels[1]) {
return chName(channels[0]);
}
return '[' + chName(channels[0]) + " " + chName(channels[1]) + "]";
}
function arrayEquals(a: number[], b: number[]): boolean {
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
export default class ChannelRouterSettings {
configured: boolean = false;
modified: boolean = false;
channelRouterPresetId: number = -1;
mainInputChannels: number[] = [1, 1];
@@ -61,6 +86,7 @@ export default class ChannelRouterSettings {
deserialize(obj: any): ChannelRouterSettings {
this.configured = obj.configured;
this.modified = obj.modified;
this.channelRouterPresetId = obj.channelRouterPresetId ? obj.channelRouterPresetId : -1;
this.mainInputChannels = obj.mainInputChannels.slice();
this.mainOutputChannels = obj.mainOutputChannels.slice();
@@ -73,6 +99,9 @@ export default class ChannelRouterSettings {
this.controlValues = ControlValue.deserializeArray(obj.controlValues);
return this;
}
clone() : ChannelRouterSettings {
return new ChannelRouterSettings().deserialize(this);
}
getControlValue(symbol: string): number | null {
for (let control of this.controlValues) {
if (control.key === symbol) {
@@ -111,16 +140,21 @@ export default class ChannelRouterSettings {
}
let nInputs = jackConfiguration.inputAudioPorts.length;
let nOutputs = jackConfiguration.outputAudioPorts.length;
let description = channelPairName(this.mainInputChannels, nInputs) + " -> " +
channelPairName(this.mainOutputChannels, nOutputs);
let description = channelPairName(this.mainInputChannels, nInputs,true) + " -> " +
channelPairName(this.mainOutputChannels, nOutputs,false);
if (isActiveChannel(this.auxInputChannels) && isActiveChannel(this.auxOutputChannels))
{
description += " " + "+ re-amp: " + channelPairName(this.auxInputChannels, nInputs) + " -> " +
channelPairName(this.auxOutputChannels, nOutputs);
if (arrayEquals(this.mainInputChannels, this.auxInputChannels))
{
description += ", re-amp -> " + channelPairName(this.auxOutputChannels,nOutputs, false);
} else {
description += " " + ", aux: " + channelPairName(this.auxInputChannels, nInputs,true) + " -> " +
channelPairName(this.auxOutputChannels, nOutputs,false);
}
}
if (isActiveChannel(this.sendInputChannels) && isActiveChannel(this.sendOutputChannels)) {
description += " " + "+ send: " +channelPairName(this.sendOutputChannels, nOutputs)
+ " -> " + channelPairName(this.sendInputChannels, nInputs);
description += " " + ", send: " +channelPairName(this.sendOutputChannels, nOutputs, false)
+ " -> " + channelPairName(this.sendInputChannels, nInputs,true);
}
return description;
}
@@ -128,6 +162,7 @@ export default class ChannelRouterSettings {
return jackConfiguration.isValid;
}
isValid(jackConfiguration: JackConfiguration): boolean {
if (!this.configured) {
return false;
@@ -176,5 +211,11 @@ export default class ChannelRouterSettings {
}
return true;
}
isAuxActive() : boolean {
return isActiveChannel(this.auxInputChannels) && isActiveChannel(this.auxOutputChannels);
}
isSendActive() : boolean{
return isActiveChannel(this.sendInputChannels) && isActiveChannel(this.sendOutputChannels);
}
}
+294 -110
View File
@@ -31,20 +31,22 @@ import MenuItem from '@mui/material/MenuItem';
import ChannelRouterSettingsHelpDialog from './ChannelRouterSettingsHelpDialog';
import SpeakerIcon from '@mui/icons-material/Speaker';
import MicIcon from '@mui/icons-material/Mic';
import SaveIconOutline from '@mui/icons-material/Save';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import Typography from '@mui/material/Typography';
import ChannelRouterSettings from './ChannelRouterSettings';
import { Pedalboard, ControlValue } from './Pedalboard';
import ChannelRouterSettings, { MAIN_OUT_LEFT_CHANNEL, MAIN_OUT_RIGHT_CHANNEL } from './ChannelRouterSettings';
import { Pedalboard } from './Pedalboard';
import DialogEx from './DialogEx';
import { PiPedalModelFactory } from './PiPedalModel';
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
import { JackConfiguration } from './Jack';
let debugInputChannels: number | null = 2;
let debugOutputChannels: number | null = 4;
let debugInputChannels: number | null = 1;
let debugOutputChannels: number | null = 1;
export interface ChannelRouterSettingsDialogProps {
@@ -65,27 +67,31 @@ interface ChannelRouterSettingsPreset {
}
let channelRouterPresets: ChannelRouterSettingsPreset[] = [
{
id: -2, name: "Left -> Left", settings: new ChannelRouterSettings().deserialize({
let DEFAULT_MONO_TO_MONO_ROUTING_PRESET = -2; // corresponds to Input->Output.
let DEFAULT_LEFT_TO_STEREO_ROUTING_PRESET = -3; // corresponds to Input->Stereo out.
let DEFAULT_RIGHT_TO_STEREO_ROUTING_PRESET = -4; // corresponds to Right->Stereo out.
let factoryRouterPresets: ChannelRouterSettings[] = [
// Input->Output
new ChannelRouterSettings().deserialize({
configured: true,
channelRouterPresetId: -2,
mainInputChannels: [0, 0],
mainOutputChannels: [0, 0],
mainInputChannels: [0, -1],
mainOutputChannels: [0, -1],
mainInserts: new Pedalboard(),
auxInputChannels: [-1, -1],
auxOutputChannels: [-1, -1],
auxInserts: new Pedalboard(),
sendInputChannels: [-1, -1],
sendOutputChannels: [-1, -1],
controlValues: [],
controlValues: []
})
},
{
id: -3, name: "Left -> Stereo", settings: new ChannelRouterSettings().deserialize({
,
// Input->Stereo out.
new ChannelRouterSettings().deserialize({
configured: true,
channelRouterPresetId: -3,
mainInputChannels: [0, 0],
mainInputChannels: [0, -1],
mainOutputChannels: [0, 1],
mainInserts: new Pedalboard(),
auxInputChannels: [-1, -1],
@@ -93,14 +99,14 @@ let channelRouterPresets: ChannelRouterSettingsPreset[] = [
auxInserts: new Pedalboard(),
sendInputChannels: [-1, -1],
sendOutputChannels: [-1, -1],
controlValues: [],
controlValues: []
})
},
{
id: -4, name: "Right -> Stereo", settings: new ChannelRouterSettings().deserialize({
,
// Right->Stereo out.
new ChannelRouterSettings().deserialize({
configured: true,
channelRouterPresetId: -4,
mainInputChannels: [1, 1],
mainInputChannels: [1,-1],
mainOutputChannels: [0, 1],
mainInserts: new Pedalboard(),
auxInputChannels: [-1, -1],
@@ -110,84 +116,119 @@ let channelRouterPresets: ChannelRouterSettingsPreset[] = [
sendOutputChannels: [-1, -1],
controlValues: [],
})
},
{
id: -5, name: "Right -> Left + re-amp", settings: new ChannelRouterSettings().deserialize({
,
new ChannelRouterSettings().deserialize({
configured: true,
channelRouterPresetId: -5,
mainInputChannels: [1, 1],
mainOutputChannels: [0, 0],
mainInputChannels: [1,-1],
mainOutputChannels: [0, -1],
mainInserts: new Pedalboard(),
auxInputChannels: [1, 1],
auxOutputChannels: [1, 1],
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],
auxInputChannels: [1,-1],
auxOutputChannels: [1,-1],
auxInserts: new Pedalboard(),
sendInputChannels: [-1, -1],
sendOutputChannels: [-1, -1],
controlValues: [],
})
},
{
id: -9, name: "Left -> Stereo + stereo send", settings: new ChannelRouterSettings().deserialize({
,
new ChannelRouterSettings().deserialize({
configured: true,
channelRouterPresetId: -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, 0],
mainInputChannels: [0, -1],
mainOutputChannels: [0, -1],
mainInserts: new Pedalboard(),
auxInputChannels: [-1, -1],
auxOutputChannels: [-1, -1],
auxInserts: new Pedalboard(),
sendInputChannels: [1,-1],
sendOutputChannels: [1,-1],
controlValues: []
})
,
new ChannelRouterSettings().deserialize({
configured: true,
channelRouterPresetId: -10,
mainInputChannels: [1,-1],
mainOutputChannels: [1,-1],
mainInserts: new Pedalboard(),
auxInputChannels: [-1, -1],
auxOutputChannels: [-1, -1],
auxInserts: new Pedalboard(),
sendInputChannels: [0, -1],
sendOutputChannels: [0, -1],
controlValues: []
})
,
new ChannelRouterSettings().deserialize({
configured: true,
channelRouterPresetId: -11,
mainInputChannels: [0, -1],
mainOutputChannels: [0, 1],
mainInserts: new Pedalboard(),
auxInputChannels: [0, -1],
auxOutputChannels: [2, -1],
auxInserts: new Pedalboard(),
sendInputChannels: [-1, -1],
sendOutputChannels: [-1, -1],
controlValues: []
})
,
new ChannelRouterSettings().deserialize({
configured: true,
channelRouterPresetId: -12,
mainInputChannels: [0, -1],
mainOutputChannels: [0, 1],
mainInserts: new Pedalboard(),
auxInputChannels: [-1, -1],
auxOutputChannels: [-1, - 1],
auxOutputChannels: [-1, -1],
auxInserts: new Pedalboard(),
sendInputChannels: [2, 3],
sendOutputChannels: [2, 3],
controlValues: [],
controlValues: []
})
},
,
];
let cellPortraitInOutDiv: React.CSSProperties = {
@@ -248,41 +289,132 @@ let cellPortraitInOut: React.CSSProperties = {
verticalAlign: "middle"
};
function MakeChannelMenu(channelCount: number): React.ReactElement[] {
function normalizeChannelSelection(channelSelection: number[]) {
if (channelSelection.length === 2) {
if (channelSelection[0] === -1) {
channelSelection[0] = channelSelection[1];
channelSelection[1] = -1;
} else {
if (channelSelection[0] === channelSelection[1]) {
channelSelection[1] = -1;
}
}
}
}
function makeRoutingPresets(audioConfig: JackConfiguration): ChannelRouterSettingsPreset[] {
let result: ChannelRouterSettingsPreset[] = [];
for (let preset of factoryRouterPresets) {
if (preset.isValid(audioConfig)) {
normalizeChannelSelection(preset.mainInputChannels);
normalizeChannelSelection(preset.mainOutputChannels);
normalizeChannelSelection(preset.auxInputChannels);
normalizeChannelSelection(preset.auxOutputChannels);
normalizeChannelSelection(preset.sendInputChannels);
normalizeChannelSelection(preset.sendOutputChannels);
let newPreset = {
id: preset.channelRouterPresetId,
name: preset.getDescription(audioConfig),
settings: preset
};
result.push(newPreset);
}
}
return result;
}
function MakeChannelMenu(channelCount: number, routeType: RouteType, input: boolean, disallowedSelections: number[]): React.ReactElement[] {
let items: React.ReactElement[] = [(<MenuItem key={-1} value={-1}>None</MenuItem>)]
if (channelCount === 0) {
return items;
}
if (channelCount === 2) {
items.push((<MenuItem key={0} value={0}>Left</MenuItem>));
items.push((<MenuItem key={1} value={1}>Right</MenuItem>));
return items;
if (channelCount === 1) {
if (input) {
items.push((<MenuItem key={0} value={0} disabled={disallowedSelections.includes(0)}>Input</MenuItem>));
} else {
items.push((<MenuItem key={0} value={0} disabled={disallowedSelections.includes(0)}>Output</MenuItem>));
}
} else if (channelCount === 2) {
items.push((<MenuItem key={0} value={0} disabled={disallowedSelections.includes(0)}>Left</MenuItem>));
items.push((<MenuItem key={1} value={1} disabled={disallowedSelections.includes(1)}>Right</MenuItem>));
return items;
} else {
for (let i = 0; i < channelCount; ++i) {
items.push((<MenuItem key={i} value={i}>Ch {i + 1}</MenuItem>));
items.push((<MenuItem key={i} value={i} disabled={disallowedSelections.includes(i)}>Ch {i + 1}</MenuItem>));
}
}
if (routeType === RouteType.Aux && input) {
items.push((<MenuItem key={MAIN_OUT_LEFT_CHANNEL} value={MAIN_OUT_LEFT_CHANNEL} >Main Out L</MenuItem>));
items.push((<MenuItem key={MAIN_OUT_RIGHT_CHANNEL} value={MAIN_OUT_RIGHT_CHANNEL} >Main Out R</MenuItem>));
}
return items;
}
function makeDebugConfig(baseConfig: JackConfiguration): JackConfiguration {
let config = new JackConfiguration().deserialize(baseConfig); // clone.
for (let i = config.inputAudioPorts.length; i < (debugInputChannels ?? 0); ++i) {
config.inputAudioPorts.push("ch" + (i + 1));
}
if (config.inputAudioPorts.length > (debugInputChannels ?? 0)) {
config.inputAudioPorts = config.inputAudioPorts.slice(0, debugInputChannels ?? 0);
}
for (let i = config.outputAudioPorts.length; i < (debugOutputChannels ?? 0); ++i) {
config.outputAudioPorts.push("ch" + (i + 1));
}
if (config.outputAudioPorts.length > (debugOutputChannels ?? 0)) {
config.outputAudioPorts = config.outputAudioPorts.slice(0, debugOutputChannels ?? 0);
}
return config;
}
function getChannelRouterSettingsPresetById(presetId: number): ChannelRouterSettings {
for (let preset of factoryRouterPresets) {
if (preset.channelRouterPresetId === presetId) {
return preset.clone();
}
}
throw new Error("Error setting defaults. Preset not found: " + presetId);
}
function GetDefaultChannelRouterSettings(model: PiPedalModel) : ChannelRouterSettings {
let currentSettings = model.channelRouterSettings.get();
if (currentSettings.configured) {
return currentSettings;
}
// we're onboarding the user, so set an appropriate default channel routing preset based on the number of ports on the audio interface.
let jackSettings = model.jackConfiguration.get();
let numInputs = jackSettings.inputAudioPorts.length;
let numOutputs = jackSettings.outputAudioPorts.length;
if (numInputs < 2 || numOutputs < 2) {
currentSettings = getChannelRouterSettingsPresetById(DEFAULT_MONO_TO_MONO_ROUTING_PRESET);
} else if (numInputs === 2 && numOutputs === 2) {
currentSettings = getChannelRouterSettingsPresetById(DEFAULT_RIGHT_TO_STEREO_ROUTING_PRESET);
} else {
currentSettings = getChannelRouterSettingsPresetById(DEFAULT_LEFT_TO_STEREO_ROUTING_PRESET);
}
model.setChannelRouterSettings(currentSettings);
return model.channelRouterSettings.get();
}
function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
//const classes = useStyles();
const { open, onClose } = props;
let model = PiPedalModelFactory.getInstance();
let config = model.jackConfiguration.get();
if (debugInputChannels != null || debugOutputChannels != null) {
config = makeDebugConfig(config);
}
let channelRouterPresets = makeRoutingPresets(config);
const [settings, setSettings] = useState<ChannelRouterSettings>(model.channelRouterSettings.get());
const [controlValues, setControlValues] = useState<ControlValue[]>(model.channelRouterControlValues.get());
const [settings, setSettings] = useState<ChannelRouterSettings>(GetDefaultChannelRouterSettings(model));
const [showHelp, setShowHelp] = useState<boolean>(false);
if (settings) {
}
if (controlValues) {
}
React.useEffect(() => {
if (open) {
@@ -290,13 +422,8 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
setSettings(model.channelRouterSettings.get());
};
model.channelRouterSettings.addOnChangedHandler(handleSettingsChanged);
let handleControlsChanged = () => {
setControlValues(model.channelRouterControlValues.get());
};
model.channelRouterControlValues.addOnChangedHandler(handleControlsChanged);
return () => {
model.channelRouterSettings.removeOnChangedHandler(handleSettingsChanged);
model.channelRouterControlValues.removeOnChangedHandler(handleControlsChanged);
}
} else {
return () => { };
@@ -321,28 +448,50 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
let value: number;
let disallowedSelections: number[] = [];
let channelSelection: number[] = [];
switch (routeType) {
case RouteType.Main:
value = input ? settings.mainInputChannels[channelIndex] : settings.mainOutputChannels[channelIndex];
channelSelection = input ? settings.mainInputChannels : settings.mainOutputChannels;
break;
case RouteType.Aux:
value = input ? settings.auxInputChannels[channelIndex] : settings.auxOutputChannels[channelIndex];
channelSelection = input ? settings.auxInputChannels : settings.auxOutputChannels;
break;
case RouteType.Send:
value = input ? settings.sendInputChannels[channelIndex] : settings.sendOutputChannels[channelIndex];
channelSelection = input ? settings.sendInputChannels : settings.sendOutputChannels;
break;
}
value = channelSelection[channelIndex];
if (value >= channelCount) {
value = -1;
}
if (routeType === RouteType.Send) {
if (input) {
disallowedSelections = settings.mainInputChannels.concat(settings.auxInputChannels);
} else {
disallowedSelections = settings.mainOutputChannels.concat(settings.auxOutputChannels);
}
}
if (channelIndex === 1) {
if (channelSelection[0] !== -1) {
disallowedSelections.push(channelSelection[0]);
}
}
let error = false;
if (routeType === RouteType.Main && channelSelection[0] === -1 && channelSelection[1] === -1) {
error = true;
}
return (
<Select variant="standard" style={{ width: 90 }} value={value}
<Select variant="standard" style={{ width: icon ? 130 : 110 }} value={value}
startAdornment={
icon ? (
input ? (<MicIcon style={{ opacity: 0.6, width: 16, height: 16, marginRight: 4 }} />) :
(<SpeakerIcon style={{ opacity: 0.6, width: 16, height: 16, marginRight: 4 }} />)
) : undefined
}
error={error}
onChange={(event) => {
let newValue = event.target.value as number;
let newSettings = new ChannelRouterSettings().deserialize(settings);
@@ -350,32 +499,43 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
case RouteType.Main:
if (input) {
newSettings.mainInputChannels[channelIndex] = newValue;
normalizeChannelSelection(newSettings.mainInputChannels);
} else {
newSettings.mainOutputChannels[channelIndex] = newValue;
normalizeChannelSelection(newSettings.mainOutputChannels);
}
break;
case RouteType.Aux:
if (input) {
newSettings.auxInputChannels[channelIndex] = newValue;
normalizeChannelSelection(newSettings.auxInputChannels);
} else {
newSettings.auxOutputChannels[channelIndex] = newValue;
normalizeChannelSelection(newSettings.auxOutputChannels);
}
break;
case RouteType.Send:
if (input) {
newSettings.sendInputChannels[channelIndex] = newValue;
normalizeChannelSelection(newSettings.sendInputChannels);
} else {
newSettings.sendOutputChannels[channelIndex] = newValue;
normalizeChannelSelection(newSettings.sendOutputChannels);
}
break;
}
if (newSettings.channelRouterPresetId >= 0) {
newSettings.modified = true; // custom.
} else {
newSettings.modified = true;
newSettings.channelRouterPresetId = -1; // custom.
}
newSettings.configured = true;
model.setChannelRouterSettings(newSettings);
}}
>
{MakeChannelMenu(channelCount)}
{MakeChannelMenu(channelCount, routeType, input, disallowedSelections)}
</Select>
)
}
@@ -437,18 +597,32 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
return items;
}
let handleSave = () => {
};
let PresetSelect = (width: number | string | undefined) => {
return (
<div style={{ display: "flex", flexDirection: "row", marginTop: 2, alignItems: "center" }}>
<Select variant="standard" style={{ width: width }} value={settings.channelRouterPresetId}
<div style={{ flex: "0 0 auto" }}>
<IconButtonEx tooltip="Save current preset"
style={{ flex: "0 0 auto", color: "#FFFFFF" }}
onClick={(e) => { handleSave(); }}
size="large">
<SaveIconOutline style={{ opacity: 0.75 }} color="inherit" />
</IconButtonEx>
</div>
<div style={{ flex: "1 1", display: "relative" }}>
<Select variant="standard" style={{ width: "100%" }} value={settings.channelRouterPresetId}
onChange={(event) => {
ApplyPresetId(event.target.value);
}}
>
{GeneratePresetMenuItems()}
</Select>
<IconButtonEx tooltip="Presets" aria-label="more-presets">
</div>
<IconButtonEx tooltip="Presets" aria-label="more-presets" style={{ marginRight: 24 }}>
<MoreVertIcon style={{ opacity: 0.6 }} onClick={() => { }} />
</IconButtonEx>
</div>
@@ -457,6 +631,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
let LandscapeView = () => {
let auxInsertDisabled = !settings.isAuxActive();
return (
<div style={{
display: "flex", flexFlow: "row", alignItems: "center", justifyContent: "center",
@@ -518,7 +693,11 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
</td>
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
<td rowSpan={2} style={cellLeft}>
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
<Button variant="outlined" style={{
textTransform: "none", borderRadius: 24,
}}
disabled={auxInsertDisabled}>
Inserts
</Button>
</td>
@@ -574,6 +753,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
);
};
let PortraitView = () => {
let auxInsertDisabled = !settings.isAuxActive();
return (
<div style={{
display: "flex", flexFlow: "row", alignItems: "center", justifyContent: "center",
@@ -685,7 +865,11 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
<td style={cellPortraitControlStrip}>{Vu()}</td>
<td style={cellPortraitControlStrip}>
<div style={{ marginLeft: 8, marginRight: 8 }}>
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
<Button variant="outlined" style={{
textTransform: "none", borderRadius: 24,
}}
disabled={auxInsertDisabled}>
Inserts
</Button>
</div>
@@ -105,15 +105,15 @@ export default class ChannelRouterSettingsHelpDialog extends ResizeResponsiveCom
<DialogContent style={{ minHeight: 96 }}>
<div style={{ fontSize: "0.9em", lineHeight: "18pt" }}>
<p> The Mixer Routing Dialog determines how audio signals are processed and routed between input and output audio channels.
<p> The Audio Channel Routing Dialog determines how audio signals are processed and routed between input and output audio channels.
</p>
<p>Guitar effects processing occurs on the <i>Main</i> route only. Plugins in the main PiPedal window are
<p>By default, guitar effects processing occurs on the <i>Main</i> route only. Plugins in the main PiPedal window are
applied before any insert plugins that have been added to the <i>Main</i> route. Main insert plugins are
applied globally, regardless of the selected preset or effects in the main PiPedal window. You might
want to add an EQ or reverb plugin here, to allow your presets to be globally adjusted to suit the room
in which you are currently performing.
</p>
<p>Plugins in the main PiPedal window are not applied to the <i>Aux</i> route. The Aux route is intended to be
<p>Plugins in the main PiPedal window are not applied to the <i>Aux</i> route by default. The Aux route is intended to be
used for
</p>
<ul>
@@ -128,7 +128,13 @@ export default class ChannelRouterSettingsHelpDialog extends ResizeResponsiveCom
mixing it with the processed guitar signal, or passing it out on a dedicated output channel.
</li>
</ul>
<p>although you may find other creative uses for the Aux channel as well.</p>
<p>However, if "Main Out L" or "Main Out R" is selected as an Aux input channel, then the Aux route will receive the
output of the processed Main route. Main route insrts are not applied
to the Aux route input signal, so you can have an independent set of inserts applied to the Main and Aux output signals.
An example of how one might use this feature: sending the output of the Main route to
a soundboard or PA system without reverb or EQ applied; and then applying reverb and EQ only to the Aux route whose
outputs will be used as inputs to a stage monitor or headphones for the performer.
</p>
<p>If the Main and Aux routes share output channels, then the results of the Main and Output signals are
summed together on those output channels. For stereo mixing, assign the same left and right output
channels to both the Main and Aux routes. If you have a 2x4 or 4x4 audio interface with two guitar input channels
@@ -142,7 +148,8 @@ export default class ChannelRouterSettingsHelpDialog extends ResizeResponsiveCom
TooB Send plugin will be sent to the external hardware effect, and the output the external hardware
effect will be returned to PiPedal at the output of the TooB Send plugin. PiPedal currently supports
only one Send plugin instance per preset; and the Send and Return channels may not be shared with the Main
or Aux routes.
or Aux routes. Note that you may only use one instance of the TooB Send plugin per preset. The TooB Send plugin
may not be used as a Main or Aux insert effect.
</p>
</div>
+9 -12
View File
@@ -27,6 +27,8 @@ export default class JackServerSettings {
this.rebootRequired = input.rebootRequired;
this.alsaInputDevice = input.alsaInputDevice ?? "";
this.alsaOutputDevice = input.alsaOutputDevice ?? "";
this.alsaInputDeviceName = input.alsaInputDeviceName ?? this.alsaInputDevice;
this.alsaOutputDeviceName = input.alsaOutputDeviceName ?? this.alsaOutputDevice;
this.sampleRate = input.sampleRate;
this.bufferSize = input.bufferSize;
this.numberOfBuffers = input.numberOfBuffers;
@@ -50,6 +52,8 @@ export default class JackServerSettings {
isJackAudio = false;
alsaInputDevice: string = "";
alsaOutputDevice: string = "";
alsaInputDeviceName: string = "";
alsaOutputDeviceName: string = "";
sampleRate = 48000;
bufferSize = 64;
numberOfBuffers = 3;
@@ -74,19 +78,12 @@ export default class JackServerSettings {
return "Not selected";
}
let inDev = this.alsaInputDevice.startsWith("hw:")
? this.alsaInputDevice.substring(3)
: this.alsaInputDevice;
let outDev = this.alsaOutputDevice.startsWith("hw:")
? this.alsaOutputDevice.substring(3)
: this.alsaOutputDevice;
let device: string;
if (inDev === outDev) {
device = inDev;
} else {
device = inDev + "-> " + outDev;
if (this.alsaInputDevice === this.alsaOutputDevice) {
device = this.alsaInputDeviceName;;
}
else {
device = this.alsaInputDeviceName + " -> " + this.alsaOutputDeviceName;
}
return `${device} ${this.sampleRate} ${this.bufferSize}x${this.numberOfBuffers}`;
}
@@ -580,10 +580,20 @@ const JackServerSettingsDialog = withStyles(
this.setState({ suppressDeviceWarning: checked });
localStorage.setItem("suppressSeparateDeviceWarning", checked ? "1" : "0");
}
getDeviceName(deviceId: string): string {
if (!this.state.alsaDevices) return deviceId;
for (let i = 0; i < this.state.alsaDevices.length; ++i) {
if (this.state.alsaDevices[i].id === deviceId) {
return this.state.alsaDevices[i].name;
}
}
return deviceId;
}
handleInputDeviceChanged(e: any) {
const d = e.target.value as string;
let s = this.state.jackServerSettings.clone();
s.alsaInputDevice = d;
s.alsaInputDeviceName = this.getDeviceName(d);
s.valid = false;
let settings = this.applyAlsaDevices(s, this.state.alsaDevices);
this.setState({
@@ -597,6 +607,7 @@ const JackServerSettingsDialog = withStyles(
const d = e.target.value as string;
let s = this.state.jackServerSettings.clone();
s.alsaOutputDevice = d;
s.alsaOutputDeviceName = this.getDeviceName(d);
s.valid = false;
let settings = this.applyAlsaDevices(s, this.state.alsaDevices);
this.setState({