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
+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;
}
+53 -144
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) {
throw PiPedalException("Can't read Jack configuration.");
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) {
throw PiPedalException("Can't read Jack configuration.");
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()) {
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);
name = availableDevice.name_;
break;
}
}
if (name.empty())
{
auto pos = id.find(":");
if (pos != std::string::npos)
{
name = id.substr(pos + 1);
}
}
return name;
}
void JackServerSettings::WriteDaemonConfig()
void JackServerSettings::FixUpDeviceNames()
{
#if JACK_HOST
this->valid_ = false;
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
if (
((!this->alsaInputDevice_.empty()) && this->alsaInputDeviceName_.empty()) ||
(!this->alsaOutputDevice_.empty()) && this->alsaOutputDeviceName_.empty())
{
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");
}
PiPedalAlsaDevices& alsaDevices = PiPedalAlsaDevices::instance();
std::vector<AlsaDeviceInfo> availableDevices = alsaDevices.GetAlsaDevices();
this->alsaInputDeviceName_ = GetAlsaDeviceName(availableDevices, this->alsaInputDevice_);
this->alsaOutputDeviceName_ = GetAlsaDeviceName(availableDevices, this->alsaOutputDevice_);
}
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;
+66 -62
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();
this->jackConfiguration.AlsaInitialize(jackServerSettings);
#endif
try
{
this->jackConfiguration.AlsaInitialize(jackServerSettings);
}
catch (const std::exception &)
{
//
}
this->channelRouterSettings = storage.GetChannelRouterSettings();
pluginHost.OnConfigurationChanged(
jackConfiguration,
storage.GetChannelSelection());
}
int64_t PiPedalModel::DownloadModelsFromTone3000(
int64_t clientId,
Tone3000DownloadType downloadType,
@@ -253,7 +251,7 @@ int64_t PiPedalModel::DownloadModelsFromTone3000(
{
throw PiPedalException("Invalid path: not in uploads directory.");
}
// Check for ".." in path components (security check)
for (const auto &component : path)
{
@@ -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,16 +769,20 @@ 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);
}
}
}
}
}
// Notify clients.
@@ -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};
@@ -1838,7 +1838,7 @@ void PiPedalModel::SendSetPatchProperty(
{
std::shared_ptr<Lv2PluginInfo> pluginInfo = GetPluginInfo(pedalboardItem->uri_);
auto pipedalUi = pluginInfo->piPedalUI();
if (pipedalUi)
if (pipedalUi)
{
auto fileProperty = pipedalUi->GetFileProperty(propertyUri);
if (fileProperty)
@@ -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,11 +2144,11 @@ 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());
float value = snapshotValue.isEnabled_ ? 1.0f : 0.0f;
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);
}
}
@@ -2387,7 +2398,7 @@ void PiPedalModel::UpdateDefaults(Pedalboard *pedalboard)
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);
}
@@ -3376,34 +3387,30 @@ 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)
{
std::lock_guard<std::recursive_mutex> lock(mutex);
uint64_t lastAdded = storage.ImportPresetsFromBank(bankInstanceId, presets);
uint64_t lastAdded = storage.ImportPresetsFromBank(bankInstanceId, presets);
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);
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,12 +3422,9 @@ 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()
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;
+918 -737
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";