diff --git a/artifacts/Pi-Icon-Inkscape.svg b/artifacts/Pi-Icon-Inkscape.svg new file mode 100644 index 0000000..4315f57 --- /dev/null +++ b/artifacts/Pi-Icon-Inkscape.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + diff --git a/artifacts/Pi-Icon-Svg.svg b/artifacts/Pi-Icon-Svg.svg new file mode 100644 index 0000000..0fb2d6f --- /dev/null +++ b/artifacts/Pi-Icon-Svg.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + diff --git a/src/AlsaDriver.cpp b/src/AlsaDriver.cpp index b75e52d..7e999bc 100644 --- a/src/AlsaDriver.cpp +++ b/src/AlsaDriver.cpp @@ -1895,8 +1895,14 @@ namespace pipedal std::vector &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) { diff --git a/src/AlsaDriverTest.cpp b/src/AlsaDriverTest.cpp index fbe3a72..9ef3504 100644 --- a/src/AlsaDriverTest.cpp +++ b/src/AlsaDriverTest.cpp @@ -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); diff --git a/src/AudioHost.cpp b/src/AudioHost.cpp index 8a0f37c..212c327 100644 --- a/src/AudioHost.cpp +++ b/src/AudioHost.cpp @@ -533,6 +533,7 @@ private: std::vector> 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 &mainInserts, + const std::shared_ptr &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); diff --git a/src/AudioHost.hpp b/src/AudioHost.hpp index 2339b77..843e0be 100644 --- a/src/AudioHost.hpp +++ b/src/AudioHost.hpp @@ -238,6 +238,12 @@ namespace pipedal virtual void SetPedalboard(const std::shared_ptr &pedalboard) = 0; + virtual void SetChannelRoutingInserts( + const std::shared_ptr &mainInserts, + const std::shared_ptr &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; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b381956..0041d5e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/ChannelRouterSettings.cpp b/src/ChannelRouterSettings.cpp index 9410e3b..926b589 100644 --- a/src/ChannelRouterSettings.cpp +++ b/src/ChannelRouterSettings.cpp @@ -135,9 +135,20 @@ ChannelRouterSettings::ChannelRouterSettings() } +std::vector ChannelRouterPresetBank::getIndexEntries() +{ + std::vector 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() + diff --git a/src/ChannelRouterSettings.hpp b/src/ChannelRouterSettings.hpp index 0c15f3d..15af777 100644 --- a/src/ChannelRouterSettings.hpp +++ b/src/ChannelRouterSettings.hpp @@ -35,6 +35,7 @@ namespace pipedal bool configured_ = false; int64_t channelRouterPresetId_ = -1; + bool changed_ = false; std::vector mainInputChannels_ = {1, 1}; std::vector 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 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> entries_; + public: + + JSON_GETTER_SETTER(nextId) + JSON_GETTER_SETTER(version) + JSON_GETTER_SETTER_REF(entries) + + std::vector getIndexEntries(); + + DECLARE_JSON_MAP(ChannelRouterPresetBank); + }; + } \ No newline at end of file diff --git a/src/JackConfiguration.cpp b/src/JackConfiguration.cpp index da7e256..2298e70 100644 --- a/src/JackConfiguration.cpp +++ b/src/JackConfiguration.cpp @@ -149,6 +149,8 @@ void JackConfiguration::AlsaInitialize( } } catch (const std::exception& /*ignore*/) { + this->isValid_ = false; + this->errorStatus_ = "Not configured."; throw; } diff --git a/src/JackServerSettings.cpp b/src/JackServerSettings.cpp index 62aef81..74830ac 100644 --- a/src/JackServerSettings.cpp +++ b/src/JackServerSettings.cpp @@ -28,6 +28,7 @@ #include #include "Lv2Log.hpp" #include "SysExec.hpp" +#include "PiPedalAlsa.hpp" #define DRC_FILENAME "/etc/jackdrc" @@ -60,33 +61,41 @@ static std::vector SplitArgs(const char *szBuff) return result; } -static std::string GetJackStringArg(const std::vector &args, const std::string&shortOption, const std::string&longOption) +static std::string GetJackStringArg(const std::vector &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 &args, const } return strVal; } -static std::int32_t GetJackArg(const std::vector &args, const std::string&shortOption, const std::string&longOption) +static std::int32_t GetJackArg(const std::vector &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 &args, const std:: } } - -void JackServerSettings::ReadJackDaemonConfiguration() +static std::string GetAlsaDeviceName(const std::vector &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 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 precedingLines; - std::vector 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" <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 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) diff --git a/src/JackServerSettings.hpp b/src/JackServerSettings.hpp index f978d74..d1c278d 100644 --- a/src/JackServerSettings.hpp +++ b/src/JackServerSettings.hpp @@ -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); }; diff --git a/src/PiLatencyMain.cpp b/src/PiLatencyMain.cpp index 8c93f1c..d8deec9 100644 --- a/src/PiLatencyMain.cpp +++ b/src/PiLatencyMain.cpp @@ -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); diff --git a/src/PiPedalAlsa.cpp b/src/PiPedalAlsa.cpp index 06be795..874148e 100644 --- a/src/PiPedalAlsa.cpp +++ b/src/PiPedalAlsa.cpp @@ -160,11 +160,14 @@ std::vector PiPedalAlsaDevices::GetAlsaDevices() std::vector result; - int cardNum = -1; // Start with first card int err; std::vector 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 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 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 PiPedalAlsaDevices::GetAlsaDevices() if (captureOk || playbackOk) { + snd_pcm_t *hDevice = captureOk ? captureDevice : playbackDevice; snd_pcm_hw_params_t *params = nullptr; err = snd_pcm_hw_params_malloc(¶ms); @@ -637,6 +690,17 @@ AlsaMidiDeviceInfo::AlsaMidiDeviceInfo(const char *name, const char *description this->subdevice_ = subdevice; } +std::unique_ptr PiPedalAlsaDevices::instance_; + +PiPedalAlsaDevices&PiPedalAlsaDevices::instance() { + if (!instance_) + { + instance_ = std::unique_ptr(new PiPedalAlsaDevices()); + } + return (*instance_); +} + + JSON_MAP_BEGIN(AlsaDeviceInfo) JSON_MAP_REFERENCE(AlsaDeviceInfo, cardId) JSON_MAP_REFERENCE(AlsaDeviceInfo, id) diff --git a/src/PiPedalAlsa.hpp b/src/PiPedalAlsa.hpp index e9061e0..5b58b77 100644 --- a/src/PiPedalAlsa.hpp +++ b/src/PiPedalAlsa.hpp @@ -71,12 +71,16 @@ namespace pipedal { }; class PiPedalAlsaDevices { - + private: + static std::unique_ptr instance_; + PiPedalAlsaDevices() { } std::map cachedDevices; bool getCachedDevice(const std::string&name, AlsaDeviceInfo*pResult); void cacheDevice(const std::string&name, const AlsaDeviceInfo&deviceInfo); + public: + static PiPedalAlsaDevices&instance(); std::vector GetAlsaDevices(); }; diff --git a/src/PiPedalAlsaTest.cpp b/src/PiPedalAlsaTest.cpp index aa36d68..c9944b0 100644 --- a/src/PiPedalAlsaTest.cpp +++ b/src/PiPedalAlsaTest.cpp @@ -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; diff --git a/src/PiPedalModel.cpp b/src/PiPedalModel.cpp index f89b82f..1041dbe 100644 --- a/src/PiPedalModel.cpp +++ b/src/PiPedalModel.cpp @@ -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 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 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 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 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 PiPedalModel::GetAlsaSequencerPorts() return result; } - void PiPedalModel::FireChannelRouterSettingsChanged(int64_t clientId) { std::lock_guard guard{mutex}; @@ -1838,7 +1838,7 @@ void PiPedalModel::SendSetPatchProperty( { std::shared_ptr 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 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 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_mapvalues_.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 lock(mutex); + std::lock_guard lock(mutex); return storage.GetPluginPresets(pluginUri); } @@ -3376,34 +3387,30 @@ std::vector PiPedalModel::RequestBankPresets(int64_t bankInsta std::lock_guard lock(mutex); return storage.RequestBankPresets(bankInstanceId); - } int64_t PiPedalModel::ImportPresetsFromBank(int64_t bankInstanceId, const std::vector &presets) { std::lock_guard 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 &presets) { std::lock_guard 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 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 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"; } diff --git a/src/PiPedalModel.hpp b/src/PiPedalModel.hpp index 68ec4a9..07203cf 100644 --- a/src/PiPedalModel.hpp +++ b/src/PiPedalModel.hpp @@ -152,7 +152,7 @@ namespace pipedal std::unique_ptr avahiService; uint16_t webPort; - PiPedalAlsaDevices alsaDevices; + PiPedalAlsaDevices &alsaDevices = PiPedalAlsaDevices::instance(); std::recursive_mutex mutex; AdminClient adminClient; diff --git a/src/PiPedalSocket.cpp b/src/PiPedalSocket.cpp index 6f55435..2509d57 100644 --- a/src/PiPedalSocket.cpp +++ b/src/PiPedalSocket.cpp @@ -436,7 +436,7 @@ JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, clientId) JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, bankInstanceId) JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, name) JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, saveAfterInstanceId) -JSON_MAP_END() +JSON_MAP_END(); class SavePluginPresetAsBody { @@ -449,7 +449,7 @@ public: JSON_MAP_BEGIN(SavePluginPresetAsBody) JSON_MAP_REFERENCE(SavePluginPresetAsBody, instanceId) JSON_MAP_REFERENCE(SavePluginPresetAsBody, name) -JSON_MAP_END() +JSON_MAP_END(); class RenameBankBody { @@ -463,6 +463,7 @@ JSON_MAP_REFERENCE(RenameBankBody, bankId) JSON_MAP_REFERENCE(RenameBankBody, newName) JSON_MAP_END() + class RenamePresetBody { public: @@ -1186,9 +1187,921 @@ public: this->model.SetControl(message.clientId_, message.instanceId_, message.symbol_, message.value_); } - static inline MessageRegistration r_xxx{ "setControl", &PiPedalSocketHandler::handle_setControl}; REGISTER_MESSAGE_HANDLER(setControl) + void handle_previewControl(int replyTo, json_reader*pReader) { + ControlChangedBody message; + pReader->read(&message); + this->model.PreviewControl(message.clientId_, message.instanceId_, message.symbol_, message.value_); + + } + REGISTER_MESSAGE_HANDLER(previewControl) + + void handle_setInputVolume(int replyTo, json_reader*pReader) { + float value; + pReader->read(&value); + this->model.SetInputVolume(value); + + } + REGISTER_MESSAGE_HANDLER(setInputVolume) + + void handle_setOutputVolume(int replyTo, json_reader*pReader) { + float value; + pReader->read(&value); + this->model.SetOutputVolume(value); + + } + REGISTER_MESSAGE_HANDLER(setOutputVolume) + + void handle_previewInputVolume(int replyTo, json_reader*pReader) { + float value; + pReader->read(&value); + this->model.PreviewInputVolume(value); + + } + REGISTER_MESSAGE_HANDLER(previewInputVolume) + + void handle_previewOutputVolume(int replyTo, json_reader*pReader) { + float value; + pReader->read(&value); + this->model.PreviewOutputVolume(value); + + } + REGISTER_MESSAGE_HANDLER(previewOutputVolume) + + void handle_listenForMidiEvent(int replyTo, json_reader*pReader) { + ListenForMidiEventBody body; + pReader->read(&body); + this->model.ListenForMidiEvent(this->clientId, body.handle_); + + } + REGISTER_MESSAGE_HANDLER(listenForMidiEvent) + + void handle_cancelListenForMidiEvent(int replyTo, json_reader*pReader) { + uint64_t handle; + pReader->read(&handle); + this->model.CancelListenForMidiEvent(this->clientId, handle); + + } + REGISTER_MESSAGE_HANDLER(cancelListenForMidiEvent) + + void handle_monitorPatchProperty(int replyTo, json_reader*pReader) { + MonitorPatchPropertyBody body; + pReader->read(&body); + this->model.MonitorPatchProperty(this->clientId, body.clientHandle_, body.instanceId_, body.propertyUri_); + + } + REGISTER_MESSAGE_HANDLER(monitorPatchProperty) + + void handle_cancelMonitorPatchProperty(int replyTo, json_reader*pReader) { + int64_t handle; + pReader->read(&handle); + this->model.CancelMonitorPatchProperty(this->clientId, handle); + + } + REGISTER_MESSAGE_HANDLER(cancelMonitorPatchProperty) + + void handle_getUpdateStatus(int replyTo, json_reader*pReader) { + UpdateStatus updateStatus = model.GetUpdateStatus(); + this->Reply(replyTo, "getUpdateStatus", updateStatus); + + } + REGISTER_MESSAGE_HANDLER(getUpdateStatus) + + void handle_getHasWifi(int replyTo, json_reader*pReader) { + bool result = model.GetHasWifi(); + this->Reply(replyTo, "getHasWifi", result); + + } + REGISTER_MESSAGE_HANDLER(getHasWifi) + + void handle_updateNow(int replyTo, json_reader*pReader) { + std::string updateUrl; + pReader->read(&updateUrl); + model.UpdateNow(updateUrl); + bool result = true; + this->Reply(replyTo, "updateNow", result); + + } + REGISTER_MESSAGE_HANDLER(updateNow) + + void handle_getJackStatus(int replyTo, json_reader*pReader) { + JackHostStatus status = model.GetJackStatus(); + this->Reply(replyTo, "getJackStatus", status); + + } + REGISTER_MESSAGE_HANDLER(getJackStatus) + + void handle_getAlsaDevices(int replyTo, json_reader*pReader) { + std::vector devices = model.GetAlsaDevices(); + this->Reply(replyTo, "getAlsaDevices", devices); + + } + REGISTER_MESSAGE_HANDLER(getAlsaDevices) + + void handle_getKnownWifiNetworks(int replyTo, json_reader*pReader) { + std::vector channels = this->model.GetKnownWifiNetworks(); + this->Reply(replyTo, "getWifiChannels", channels); + + } + REGISTER_MESSAGE_HANDLER(getKnownWifiNetworks) + + void handle_getWifiChannels(int replyTo, json_reader*pReader) { + std::string country; + pReader->read(&country); + std::vector channels = pipedal::getWifiChannelSelectors(country.c_str()); + this->Reply(replyTo, "getWifiChannels", channels); + + } + REGISTER_MESSAGE_HANDLER(getWifiChannels) + + void handle_getPluginPresets(int replyTo, json_reader*pReader) { + std::string uri; + pReader->read(&uri); + this->Reply(replyTo, "getPluginPresets", this->model.GetPluginUiPresets(uri)); + + } + REGISTER_MESSAGE_HANDLER(getPluginPresets) + + void handle_loadPluginPreset(int replyTo, json_reader*pReader) { + LoadPluginPresetBody body; + pReader->read(&body); + this->model.LoadPluginPreset(body.pluginInstanceId_, body.presetInstanceId_); + + } + REGISTER_MESSAGE_HANDLER(loadPluginPreset) + + void handle_setJackServerSettings(int replyTo, json_reader*pReader) { + JackServerSettings jackServerSettings; + pReader->read(&jackServerSettings); + this->model.SetJackServerSettings(jackServerSettings); + this->Reply(replyTo, "setJackserverSettings"); + + } + REGISTER_MESSAGE_HANDLER(setJackServerSettings) + + void handle_setGovernorSettings(int replyTo, json_reader*pReader) { + std::string governor; + pReader->read(&governor); + std::string fromAddress = this->getFromAddress(); + // if (!IsOnLocalSubnet(fromAddress)) + // { + // throw PiPedalException("Permission denied. Not on local subnet."); + // } + this->model.SetGovernorSettings(governor); + this->Reply(replyTo, "setGovernorSettings"); + + } + REGISTER_MESSAGE_HANDLER(setGovernorSettings) + + void handle_setWifiConfigSettings(int replyTo, json_reader*pReader) { + WifiConfigSettings wifiConfigSettings; + pReader->read(&wifiConfigSettings); + if (!GetAdminClient().CanUseAdminClient()) + { + throw PiPedalException("Can't change server settings when running interactively."); + } + std::string fromAddress = this->getFromAddress(); + // if (!IsOnLocalSubnet(fromAddress)) + // { + // throw PiPedalException("Permission denied. Not on local subnet."); + // } + + this->model.SetWifiConfigSettings(wifiConfigSettings); + this->Reply(replyTo, "setWifiConfigSettings"); + + } + REGISTER_MESSAGE_HANDLER(setWifiConfigSettings) + + void handle_getWifiConfigSettings(int replyTo, json_reader*pReader) { + this->Reply(replyTo, "getWifiConfigSettings", model.GetWifiConfigSettings()); + + } + REGISTER_MESSAGE_HANDLER(getWifiConfigSettings) + + void handle_setWifiDirectConfigSettings(int replyTo, json_reader*pReader) { + WifiDirectConfigSettings wifiDirectConfigSettings; + pReader->read(&wifiDirectConfigSettings); + if (!GetAdminClient().CanUseAdminClient()) + { + throw PiPedalException("Can't change server settings when running interactively."); + } + std::string fromAddress = this->getFromAddress(); + // if (!IsOnLocalSubnet(fromAddress)) + // { + // throw PiPedalException("Permission denied. Not on local subnet."); + // } + + this->model.SetWifiDirectConfigSettings(wifiDirectConfigSettings); + this->Reply(replyTo, "setWifiDirectConfigSettings"); + + } + REGISTER_MESSAGE_HANDLER(setWifiDirectConfigSettings) + + void handle_getWifiDirectConfigSettings(int replyTo, json_reader*pReader) { + this->Reply(replyTo, "getWifiDirectConfigSettings", model.GetWifiDirectConfigSettings()); + + } + REGISTER_MESSAGE_HANDLER(getWifiDirectConfigSettings) + + void handle_getGovernorSettings(int replyTo, json_reader*pReader) { + this->Reply(replyTo, "getGovernorSettings", model.GetGovernorSettings()); + + } + REGISTER_MESSAGE_HANDLER(getGovernorSettings) + + void handle_getJackServerSettings(int replyTo, json_reader*pReader) { + this->Reply(replyTo, "getJackServerSettings", model.GetJackServerSettings()); + + } + REGISTER_MESSAGE_HANDLER(getJackServerSettings) + + void handle_getBankIndex(int replyTo, json_reader*pReader) { + BankIndex bankIndex = model.GetBankIndex(); + this->Reply(replyTo, "getBankIndex", bankIndex); + + } + REGISTER_MESSAGE_HANDLER(getBankIndex) + + void handle_getJackConfiguration(int replyTo, json_reader*pReader) { + JackConfiguration configuration = this->model.GetJackConfiguration(); + this->Reply(replyTo, "getJackConfiguration", configuration); + + } + REGISTER_MESSAGE_HANDLER(getJackConfiguration) + + void handle_getJackSettings(int replyTo, json_reader*pReader) { + JackChannelSelection selection = this->model.GetJackChannelSelection(); + this->Reply(replyTo, "getJackSettings", selection); + + } + REGISTER_MESSAGE_HANDLER(getJackSettings) + + void handle_saveCurrentPreset(int replyTo, json_reader*pReader) { + this->model.SaveCurrentPreset(this->clientId); + + } + REGISTER_MESSAGE_HANDLER(saveCurrentPreset) + + void handle_saveCurrentPresetAs(int replyTo, json_reader*pReader) { + SaveCurrentPresetAsBody body; + pReader->read(&body); + int64_t result = this->model.SaveCurrentPresetAs(this->clientId, body.bankInstanceId_,body.name_, body.saveAfterInstanceId_); + Reply(replyTo, "saveCurrentPresetsAs", result); + + } + REGISTER_MESSAGE_HANDLER(saveCurrentPresetAs) + + void handle_setSelectedPedalboardPlugin(int replyTo, json_reader*pReader) { + SetSelectedPedalboardPluginBody body; + pReader->read(&body); + this->model.SetSelectedPedalboardPlugin(body.clientId_,body.pluginInstanceId_); + + } + REGISTER_MESSAGE_HANDLER(setSelectedPedalboardPlugin) + + void handle_savePluginPresetAs(int replyTo, json_reader*pReader) { + SavePluginPresetAsBody body; + pReader->read(&body); + int64_t result = this->model.SavePluginPresetAs(body.instanceId_, body.name_); + Reply(replyTo, "saveCurrentPresetsAs", result); + + } + REGISTER_MESSAGE_HANDLER(savePluginPresetAs) + + void handle_getPresets(int replyTo, json_reader*pReader) { + PresetIndex presets; + this->model.GetPresets(&presets); + Reply(replyTo, "getPresets", presets); + + } + REGISTER_MESSAGE_HANDLER(getPresets) + + void handle_setPedalboardItemEnable(int replyTo, json_reader*pReader) { + PedalboardItemEnabledBody body; + pReader->read(&body); + model.SetPedalboardItemEnable(body.clientId_, body.instanceId_, body.enabled_); + + } + REGISTER_MESSAGE_HANDLER(setPedalboardItemEnable) + + void handle_setPedalboardItemUseModUi(int replyTo, json_reader*pReader) { + PedalboardItemUseModGuiBody body; + pReader->read(&body); + model.SetPedalboardItemUseModUi(body.clientId_, body.instanceId_, body.useModUi_); + + } + REGISTER_MESSAGE_HANDLER(setPedalboardItemUseModUi) + + void handle_updateCurrentPedalboard(int replyTo, json_reader*pReader) { + UpdateCurrentPedalboardBody body; + + pReader->read(&body); + this->model.UpdateCurrentPedalboard(body.clientId_, body.pedalboard_); + + } + REGISTER_MESSAGE_HANDLER(updateCurrentPedalboard) + + void handle_setSnapshot(int replyTo, json_reader*pReader) { + int64_t snapshotIndex = -1; + pReader->read(&snapshotIndex); + this->model.SetSnapshot(snapshotIndex); + + } + REGISTER_MESSAGE_HANDLER(setSnapshot) + + void handle_setSnapshots(int replyTo, json_reader*pReader) { + SetSnapshotsBody body; + pReader->read(&body); + this->model.SetSnapshots(body.snapshots_, body.selectedSnapshot_); + + } + REGISTER_MESSAGE_HANDLER(setSnapshots) + + void handle_currentPedalboard(int replyTo, json_reader*pReader) { + auto pedalboard = model.GetCurrentPedalboardCopy(); + Reply(replyTo, "currentPedalboard", pedalboard); + + } + REGISTER_MESSAGE_HANDLER(currentPedalboard) + + void handle_plugins(int replyTo, json_reader*pReader) { + auto ui_plugins = model.GetPluginHost().GetUiPlugins(); + Reply(replyTo, "plugins", ui_plugins); + + } + REGISTER_MESSAGE_HANDLER(plugins) + + void handle_pluginClasses(int replyTo, json_reader*pReader) { + auto classes = model.GetPluginHost().GetLv2PluginClass(); + Reply(replyTo, "pluginClasses", classes); + + } + REGISTER_MESSAGE_HANDLER(pluginClasses) + + void handle_hello(int replyTo, json_reader*pReader) { + this->model.AddNotificationSubscription(shared_from_this()); + Reply(replyTo, "ehlo", clientId); + + } + REGISTER_MESSAGE_HANDLER(hello) + + void handle_setShowStatusMonitor(int replyTo, json_reader*pReader) { + bool showStatusMonitor; + pReader->read(&showStatusMonitor); + this->model.SetShowStatusMonitor(showStatusMonitor); + + } + REGISTER_MESSAGE_HANDLER(setShowStatusMonitor) + + void handle_getShowStatusMonitor(int replyTo, json_reader*pReader) { + Reply(replyTo, "getShowStatusMonitor", this->model.GetShowStatusMonitor()); + + } + REGISTER_MESSAGE_HANDLER(getShowStatusMonitor) + + void handle_version(int replyTo, json_reader*pReader) { + PiPedalVersion version(this->model); + + Reply(replyTo, "version", version); + + } + REGISTER_MESSAGE_HANDLER(version) + + void handle_loadPreset(int replyTo, json_reader*pReader) { + int64_t instanceId = 0; + pReader->read(&instanceId); + model.LoadPreset(this->clientId, instanceId); + + } + REGISTER_MESSAGE_HANDLER(loadPreset) + + void handle_updatePresets(int replyTo, json_reader*pReader) { + PresetIndex newIndex; + pReader->read(&newIndex); + bool result = model.UpdatePresets(this->clientId, newIndex); + this->Reply(replyTo, "updatePresets", result); + + } + REGISTER_MESSAGE_HANDLER(updatePresets) + + void handle_updatePluginPresets(int replyTo, json_reader*pReader) { + PluginUiPresets pluginPresets; + pReader->read(&pluginPresets); + model.UpdatePluginPresets(pluginPresets); + this->Reply(replyTo, "updatePluginPresets", true); + + } + REGISTER_MESSAGE_HANDLER(updatePluginPresets) + + void handle_moveBank(int replyTo, json_reader*pReader) { + FromToBody body; + pReader->read(&body); + model.MoveBank(this->clientId, body.from_, body.to_); + this->Reply(replyTo, "moveBank"); + + } + REGISTER_MESSAGE_HANDLER(moveBank) + + void handle_shutdown(int replyTo, json_reader*pReader) { + model.RequestShutdown(false); + this->Reply(replyTo, "shutdown"); + + } + REGISTER_MESSAGE_HANDLER(shutdown) + + void handle_restart(int replyTo, json_reader*pReader) { + model.RequestShutdown(true); + this->Reply(replyTo, "restart"); + + } + REGISTER_MESSAGE_HANDLER(restart) + + void handle_deletePresetItems(int replyTo, json_reader*pReader) { + std::vector items; + pReader->read(&items); + int64_t result = model.DeletePresets(this->clientId, items); + this->Reply(replyTo, "deletePresetItems", result); + + } + REGISTER_MESSAGE_HANDLER(deletePresetItems) + + void handle_deleteBankItem(int replyTo, json_reader*pReader) { + int64_t instanceId = 0; + pReader->read(&instanceId); + uint64_t result = model.DeleteBank(this->clientId, instanceId); + this->Reply(replyTo, "deleteBankItem", result); + + } + REGISTER_MESSAGE_HANDLER(deleteBankItem) + + void handle_renameBank(int replyTo, json_reader*pReader) { + RenameBankBody body; + pReader->read(&body); + + std::stringstream tOut; + json_writer tWriter(tOut); + tWriter.write(body.newName_); + std::string tJson = tOut.str(); + std::stringstream tIn(tJson); + json_reader tReader(tIn); + std::string tResult; + tReader.read(&tResult); + + body.newName_ = tResult; + + try + { + model.RenameBank(this->clientId, body.bankId_, body.newName_); + this->Reply(replyTo, "renameBank"); + } + catch (const std::exception &e) + { + this->SendError(replyTo, std::string(e.what())); + } + + } + REGISTER_MESSAGE_HANDLER(renameBank) + + void handle_openBank(int replyTo, json_reader*pReader) { + int64_t bankId = -1; + pReader->read(&bankId); + try + { + model.OpenBank(this->clientId, bankId); + ; + this->Reply(replyTo, "openBank"); + } + catch (const std::exception &e) + { + this->SendError(replyTo, std::string(e.what())); + } + + } + REGISTER_MESSAGE_HANDLER(openBank) + + void handle_saveBankAs(int replyTo, json_reader*pReader) { + RenameBankBody body; + pReader->read(&body); + try + { + int64_t newId = model.SaveBankAs(this->clientId, body.bankId_, body.newName_); + this->Reply(replyTo, "saveBankAs", newId); + } + catch (const std::exception &e) + { + this->SendError(replyTo, std::string(e.what())); + } + + } + REGISTER_MESSAGE_HANDLER(saveBankAs) + + void handle_nextBank(int replyTo, json_reader*pReader) { + model.NextBank(); + + } + REGISTER_MESSAGE_HANDLER(nextBank) + + void handle_previousBank(int replyTo, json_reader*pReader) { + model.PreviousBank(); + + } + REGISTER_MESSAGE_HANDLER(previousBank) + + void handle_nextPreset(int replyTo, json_reader*pReader) { + model.NextPreset(); + + } + REGISTER_MESSAGE_HANDLER(nextPreset) + + void handle_previousPreset(int replyTo, json_reader*pReader) { + model.PreviousPreset(); + + } + REGISTER_MESSAGE_HANDLER(previousPreset) + + void handle_renamePresetItem(int replyTo, json_reader*pReader) { + RenamePresetBody body; + pReader->read(&body); + + bool result = model.RenamePreset(body.clientId_, body.instanceId_, body.name_); + this->Reply(replyTo, "renamePresetItem", result); + + } + REGISTER_MESSAGE_HANDLER(renamePresetItem) + + void handle_copyPreset(int replyTo, json_reader*pReader) { + CopyPresetBody body; + pReader->read(&body); + int64_t result = model.CopyPreset(body.clientId_, body.fromId_, body.toId_); + this->Reply(replyTo, "copyPreset", result); + + } + REGISTER_MESSAGE_HANDLER(copyPreset) + + void handle_copyPluginPreset(int replyTo, json_reader*pReader) { + CopyPluginPresetBody body; + pReader->read(&body); + uint64_t result = model.CopyPluginPreset(body.pluginUri_, body.instanceId_); + this->Reply(replyTo, "copyPluginPreset", result); + + } + REGISTER_MESSAGE_HANDLER(copyPluginPreset) + + void handle_setPatchProperty(int replyTo, json_reader*pReader) { + SetPatchPropertyBody body; + pReader->read(&body); + model.SendSetPatchProperty(clientId, body.instanceId_, body.propertyUri_, body.value_, [this, replyTo]() + { this->JsonReply(replyTo, "setPatchProperty", "true"); }, [this, replyTo](const std::string &error) + { this->SendError(replyTo, error.c_str()); }); + + } + REGISTER_MESSAGE_HANDLER(setPatchProperty) + + void handle_setPedalboardItemTitle(int replyTo, json_reader*pReader) { + SetPedalboardItemTitleBody body; + pReader->read(&body); + model.SetPedalboardItemTitle(body.instanceId_, body.title_, body.colorKey_); + + } + REGISTER_MESSAGE_HANDLER(setPedalboardItemTitle) + + void handle_getPatchProperty(int replyTo, json_reader*pReader) { + GetPatchPropertyBody body; + pReader->read(&body); + + model.SendGetPatchProperty( + this->clientId, + body.instanceId_, + body.propertyUri_, + [this, replyTo](const std::string &jsonResult) + { + this->JsonReply(replyTo, "getPatchProperty", jsonResult.c_str()); + }, + [this, replyTo](const std::string &error) + { + this->SendError(replyTo, error.c_str()); + }); + + } + REGISTER_MESSAGE_HANDLER(getPatchProperty) + + void handle_monitorPort(int replyTo, json_reader*pReader) { + MonitorPortBody body; + pReader->read(&body); + + MonitorPort(replyTo, body); + + } + REGISTER_MESSAGE_HANDLER(monitorPort) + + void handle_unmonitorPort(int replyTo, json_reader*pReader) { + int64_t subscriptionHandle; + pReader->read(&subscriptionHandle); + { + { + std::lock_guard guard(activePortMonitorsMutex); + for (auto i = this->activePortMonitors.begin(); i != this->activePortMonitors.end(); ++i) + { + if ((*i)->subscriptionHandle == subscriptionHandle) + { + auto subscription = (*i); + subscription->Close(); + + this->activePortMonitors.erase(i); + break; + } + } + } + model.UnmonitorPort(subscriptionHandle); + } + + } + REGISTER_MESSAGE_HANDLER(unmonitorPort) + + void handle_addVuSubscription(int replyTo, json_reader*pReader) { + int64_t instanceId = -1; + + pReader->read(&instanceId); + + int64_t subscriptionHandle = model.AddVuSubscription(instanceId); + + { + std::lock_guard guard(subscriptionMutex); + activeVuSubscriptions.push_back(VuSubscription{subscriptionHandle, instanceId}); + } + this->Reply(replyTo, "addVuSubscription", subscriptionHandle); + + } + REGISTER_MESSAGE_HANDLER(addVuSubscription) + + void handle_removeVuSubscription(int replyTo, json_reader*pReader) { + int64_t subscriptionHandle = -1; + pReader->read(&subscriptionHandle); + { + std::lock_guard guard(subscriptionMutex); + + for (auto i = activeVuSubscriptions.begin(); i != activeVuSubscriptions.end(); ++i) + { + if (i->subscriptionHandle == subscriptionHandle) + { + activeVuSubscriptions.erase(i); + break; + } + } + } + model.RemoveVuSubscription(subscriptionHandle); + + } + REGISTER_MESSAGE_HANDLER(removeVuSubscription) + + void handle_imageList(int replyTo, json_reader*pReader) { + this->Reply(replyTo, "imageList", imageList); + + } + REGISTER_MESSAGE_HANDLER(imageList) + + void handle_getFavorites(int replyTo, json_reader*pReader) { + std::map favorites = this->model.GetFavorites(); + this->Reply(replyTo, "getFavorites", favorites); + + } + REGISTER_MESSAGE_HANDLER(getFavorites) + + void handle_setFavorites(int replyTo, json_reader*pReader) { + std::map favorites; + pReader->read(&favorites); + this->model.SetFavorites(favorites); + + } + REGISTER_MESSAGE_HANDLER(setFavorites) + + void handle_setUpdatePolicy(int replyTo, json_reader*pReader) { + int iPolicy; + pReader->read(&iPolicy); + + this->model.SetUpdatePolicy((UpdatePolicyT)iPolicy); + + } + REGISTER_MESSAGE_HANDLER(setUpdatePolicy) + + void handle_forceUpdateCheck(int replyTo, json_reader*pReader) { + this->model.ForceUpdateCheck(); + + } + REGISTER_MESSAGE_HANDLER(forceUpdateCheck) + + void handle_setSystemMidiBindings(int replyTo, json_reader*pReader) { + std::vector bindings; + pReader->read(&bindings); + this->model.SetSystemMidiBindings(bindings); + + } + REGISTER_MESSAGE_HANDLER(setSystemMidiBindings) + + void handle_getSystemMidiBindings(int replyTo, json_reader*pReader) { + std::vector bindings = this->model.GetSystemMidiBidings(); + this->Reply(replyTo, "getSystemMidiBindings", bindings); + + } + REGISTER_MESSAGE_HANDLER(getSystemMidiBindings) + + void handle_requestFileList(int replyTo, json_reader*pReader) { + throw std::runtime_error("No longer implemented."); + + } + REGISTER_MESSAGE_HANDLER(requestFileList) + + void handle_requestFileList2(int replyTo, json_reader*pReader) { + FileRequestArgs requestArgs; + pReader->read(&requestArgs); + FileRequestResult result = this->model.GetFileList2(requestArgs.relativePath_, requestArgs.fileProperty_); + this->Reply(replyTo, "requestFileList2", result); + + } + REGISTER_MESSAGE_HANDLER(requestFileList2) + + void handle_newPreset(int replyTo, json_reader*pReader) { + int64_t presetId = this->model.CreateNewPreset(); + this->Reply(replyTo, "newPreset", presetId); + + } + REGISTER_MESSAGE_HANDLER(newPreset) + + void handle_deleteUserFile(int replyTo, json_reader*pReader) { + std::string fileName; + pReader->read(&fileName); + + this->model.DeleteSampleFile(fileName); + this->Reply(replyTo, "deleteUserFile", true); + + } + REGISTER_MESSAGE_HANDLER(deleteUserFile) + + void handle_createNewSampleDirectory(int replyTo, json_reader*pReader) { + CreateNewSampleDirectoryArgs args; + pReader->read(&args); + + std::string newFileName = this->model.CreateNewSampleDirectory(args.relativePath_, args.uiFileProperty_); + this->Reply(replyTo, "createNewSampleDirectory", newFileName); + + } + REGISTER_MESSAGE_HANDLER(createNewSampleDirectory) + + void handle_renameFilePropertyFile(int replyTo, json_reader*pReader) { + RenameSampleFileArgs args; + pReader->read(&args); + + std::string newFileName = this->model.RenameFilePropertyFile(args.oldRelativePath_, args.newRelativePath_, args.uiFileProperty_); + this->Reply(replyTo, "renameFilePropertyFile", newFileName); + + } + REGISTER_MESSAGE_HANDLER(renameFilePropertyFile) + + void handle_copyFilePropertyFile(int replyTo, json_reader*pReader) { + CopySampleFileArgs args; + pReader->read(&args); + + std::string newFileName = this->model.CopyFilePropertyFile(args.oldRelativePath_, args.newRelativePath_, args.uiFileProperty_, args.overwrite_); + this->Reply(replyTo, "copyFilePropertyFile", newFileName); + + } + REGISTER_MESSAGE_HANDLER(copyFilePropertyFile) + + void handle_getFilePropertyDirectoryTree(int replyTo, json_reader*pReader) { + GetFilePropertyDirectoryTreeArgs args; + pReader->read(&args); + FilePropertyDirectoryTree::ptr result = + model.GetFilePropertydirectoryTree( + args.fileProperty_, + args.selectedPath_); + this->Reply(replyTo, "GetFilePropertydirectoryTree", result); + + } + REGISTER_MESSAGE_HANDLER(getFilePropertyDirectoryTree) + + void handle_moveAudioFile(int replyTo, json_reader*pReader) { + MoveAudioFileArgs args; + pReader->read(&args); + this->model.MoveAudioFile(args.path_, args.from_, args.to_); + bool result = true; + this->Reply(replyTo,"moveAudioFile", result); + + } + REGISTER_MESSAGE_HANDLER(moveAudioFile) + + void handle_setOnboarding(int replyTo, json_reader*pReader) { + bool value; + pReader->read(&value); + this->model.SetOnboarding(value); + + } + REGISTER_MESSAGE_HANDLER(setOnboarding) + + void handle_getWifiRegulatoryDomains(int replyTo, json_reader*pReader) { + auto regulatoryDomains = this->model.GetWifiRegulatoryDomains(); + this->Reply(replyTo, "getWifiRegulatoryDomains", regulatoryDomains); + + } + REGISTER_MESSAGE_HANDLER(getWifiRegulatoryDomains) + + void handle_setAlsaSequencerConfiguration(int replyTo, json_reader*pReader) { + AlsaSequencerConfiguration config; + pReader->read(&config); + this->model.SetAlsaSequencerConfiguration(config); + this->Reply(replyTo, "setAlsaSequencerConfiguration"); + + } + REGISTER_MESSAGE_HANDLER(setAlsaSequencerConfiguration) + + void handle_getAlsaSequencerConfiguration(int replyTo, json_reader*pReader) { + AlsaSequencerConfiguration config = this->model.GetAlsaSequencerConfiguration(); + this->Reply(replyTo, "getAlsaSequencerConfiguration", config); + + } + REGISTER_MESSAGE_HANDLER(getAlsaSequencerConfiguration) + + void handle_getAlsaSequencerPorts(int replyTo, json_reader*pReader) { + std::vector result = model.GetAlsaSequencerPorts(); + this->Reply(replyTo,"getAlsaSequencerPorts", result); + + } + REGISTER_MESSAGE_HANDLER(getAlsaSequencerPorts) + + void handle_requestBankPresets(int replyTo, json_reader*pReader) { + RequestBankPresetsBody args; + pReader->read(&args); + auto result = this->model.RequestBankPresets(args.bankInstanceId_); + this->Reply(replyTo,"requestBankPresets",result); + + } + REGISTER_MESSAGE_HANDLER(requestBankPresets) + + void handle_importPresetsFromBank(int replyTo, json_reader*pReader) { + ImportPresetsFromBankBody args; + pReader->read(&args); + auto result = this->model.ImportPresetsFromBank(args.bankInstanceId_, args.presets_); + this->Reply(replyTo,"importPresetsFromBank",result); + + } + REGISTER_MESSAGE_HANDLER(importPresetsFromBank) + + void handle_copyPresetsToBank(int replyTo, json_reader*pReader) { + CopyPresetsToBankBody args; + pReader->read(&args); + auto result = this->model.CopyPresetsToBank(args.bankInstanceId_, args.presets_); + this->Reply(replyTo,"copyPresetsToBank",result); + + } + REGISTER_MESSAGE_HANDLER(copyPresetsToBank) + + void handle_getChannelRouterSettings(int replyTo, json_reader*pReader) { + ChannelRouterSettings::ptr result = this->model.GetChannelRouterSettings(); + this->Reply(replyTo, "getChannelRouterSettings", result); + + } + REGISTER_MESSAGE_HANDLER(getChannelRouterSettings) + + void handle_setChannelRouterSettings(int replyTo, json_reader*pReader) { + ChannelRouterSettings::ptr args; + pReader->read(&args); + this->model.SetChannelRouterSettings(this->clientId, args); + + } + REGISTER_MESSAGE_HANDLER(setChannelRouterSettings) + + void handle_downloadModelsFromTone3000(int replyTo, json_reader*pReader) { + DownloadModelsFromTone3000Body args; + pReader->read(&args); + auto result = this->model.DownloadModelsFromTone3000( + this->clientId, + StringToTone3000DownloadType(args.downloadType_), + args.downloadPath_, + args.tone3000Url_ + ); + this->Reply(replyTo,"downloadModelsFromTone3000",result); + + } + REGISTER_MESSAGE_HANDLER(downloadModelsFromTone3000) + + void handle_cancelTone3000Download(int replyTo, json_reader*pReader) { + int64_t handle = -1; + pReader->read(&handle); + model.CancelTone3000Download(clientId,handle); + + } + REGISTER_MESSAGE_HANDLER(cancelTone3000Download) + + void handle_pingTone3000Server(int replyTo, json_reader*pReader) { + std::shared_ptr this_ = shared_from_this(); + model.Post([this_, replyTo] () { + bool result = this_->PingTone3000Server(); + this_->Reply(replyTo,"pingTone3000Server",result); + }); + + } + REGISTER_MESSAGE_HANDLER(pingTone3000Server) + void handleMessage(int reply, int replyTo, const std::string &message, json_reader *pReader) { @@ -1237,740 +2150,8 @@ public: (this->*(ffHandler->second))(replyTo,pReader); return; } - if (message == "previewControl") - { - ControlChangedBody message; - pReader->read(&message); - this->model.PreviewControl(message.clientId_, message.instanceId_, message.symbol_, message.value_); - } - else if (message == "setControl") - { - ControlChangedBody message; - pReader->read(&message); - this->model.SetControl(message.clientId_, message.instanceId_, message.symbol_, message.value_); - } - else if (message == "setInputVolume") - { - float value; - pReader->read(&value); - this->model.SetInputVolume(value); - } - else if (message == "setOutputVolume") - { - float value; - pReader->read(&value); - this->model.SetOutputVolume(value); - } - else if (message == "previewInputVolume") - { - float value; - pReader->read(&value); - this->model.PreviewInputVolume(value); - } - else if (message == "previewOutputVolume") - { - float value; - pReader->read(&value); - this->model.PreviewOutputVolume(value); - } - - else if (message == "listenForMidiEvent") - { - ListenForMidiEventBody body; - pReader->read(&body); - this->model.ListenForMidiEvent(this->clientId, body.handle_); - } - else if (message == "cancelListenForMidiEvent") - { - uint64_t handle; - pReader->read(&handle); - this->model.CancelListenForMidiEvent(this->clientId, handle); - } - else if (message == "monitorPatchProperty") - { - MonitorPatchPropertyBody body; - pReader->read(&body); - this->model.MonitorPatchProperty(this->clientId, body.clientHandle_, body.instanceId_, body.propertyUri_); - } - else if (message == "cancelMonitorPatchProperty") - { - int64_t handle; - pReader->read(&handle); - this->model.CancelMonitorPatchProperty(this->clientId, handle); - } - else if (message == "getUpdateStatus") - { - UpdateStatus updateStatus = model.GetUpdateStatus(); - this->Reply(replyTo, "getUpdateStatus", updateStatus); - } - else if (message == "getHasWifi") - { - bool result = model.GetHasWifi(); - this->Reply(replyTo, "getHasWifi", result); - } - else if (message == "updateNow") - { - std::string updateUrl; - pReader->read(&updateUrl); - model.UpdateNow(updateUrl); - bool result = true; - this->Reply(replyTo, "updateNow", result); - } - else if (message == "getJackStatus") - { - JackHostStatus status = model.GetJackStatus(); - this->Reply(replyTo, "getJackStatus", status); - } - else if (message == "getAlsaDevices") - { - std::vector devices = model.GetAlsaDevices(); - this->Reply(replyTo, "getAlsaDevices", devices); - } - else if (message == "getKnownWifiNetworks") - { - std::vector channels = this->model.GetKnownWifiNetworks(); - this->Reply(replyTo, "getWifiChannels", channels); - } - else if (message == "getWifiChannels") - { - std::string country; - pReader->read(&country); - std::vector channels = pipedal::getWifiChannelSelectors(country.c_str()); - this->Reply(replyTo, "getWifiChannels", channels); - } - else if (message == "getPluginPresets") - { - std::string uri; - pReader->read(&uri); - this->Reply(replyTo, "getPluginPresets", this->model.GetPluginUiPresets(uri)); - } - else if (message == "loadPluginPreset") - { - LoadPluginPresetBody body; - pReader->read(&body); - this->model.LoadPluginPreset(body.pluginInstanceId_, body.presetInstanceId_); - } - else if (message == "setJackServerSettings") - { - JackServerSettings jackServerSettings; - pReader->read(&jackServerSettings); - this->model.SetJackServerSettings(jackServerSettings); - this->Reply(replyTo, "setJackserverSettings"); - } - else if (message == "setGovernorSettings") - { - std::string governor; - pReader->read(&governor); - std::string fromAddress = this->getFromAddress(); - // if (!IsOnLocalSubnet(fromAddress)) - // { - // throw PiPedalException("Permission denied. Not on local subnet."); - // } - this->model.SetGovernorSettings(governor); - this->Reply(replyTo, "setGovernorSettings"); - } - else if (message == "setWifiConfigSettings") - { - WifiConfigSettings wifiConfigSettings; - pReader->read(&wifiConfigSettings); - if (!GetAdminClient().CanUseAdminClient()) - { - throw PiPedalException("Can't change server settings when running interactively."); - } - std::string fromAddress = this->getFromAddress(); - // if (!IsOnLocalSubnet(fromAddress)) - // { - // throw PiPedalException("Permission denied. Not on local subnet."); - // } - - this->model.SetWifiConfigSettings(wifiConfigSettings); - this->Reply(replyTo, "setWifiConfigSettings"); - } - else if (message == "getWifiConfigSettings") - { - this->Reply(replyTo, "getWifiConfigSettings", model.GetWifiConfigSettings()); - } - else if (message == "setWifiDirectConfigSettings") - { - WifiDirectConfigSettings wifiDirectConfigSettings; - pReader->read(&wifiDirectConfigSettings); - if (!GetAdminClient().CanUseAdminClient()) - { - throw PiPedalException("Can't change server settings when running interactively."); - } - std::string fromAddress = this->getFromAddress(); - // if (!IsOnLocalSubnet(fromAddress)) - // { - // throw PiPedalException("Permission denied. Not on local subnet."); - // } - - this->model.SetWifiDirectConfigSettings(wifiDirectConfigSettings); - this->Reply(replyTo, "setWifiDirectConfigSettings"); - } - else if (message == "getWifiDirectConfigSettings") - { - this->Reply(replyTo, "getWifiDirectConfigSettings", model.GetWifiDirectConfigSettings()); - } - - else if (message == "getGovernorSettings") - { - this->Reply(replyTo, "getGovernorSettings", model.GetGovernorSettings()); - } - - else if (message == "getJackServerSettings") - { - this->Reply(replyTo, "getJackServerSettings", model.GetJackServerSettings()); - } - else if (message == "getBankIndex") - { - BankIndex bankIndex = model.GetBankIndex(); - this->Reply(replyTo, "getBankIndex", bankIndex); - } - else if (message == "getJackConfiguration") - { - JackConfiguration configuration = this->model.GetJackConfiguration(); - this->Reply(replyTo, "getJackConfiguration", configuration); - } - else if (message == "getJackSettings") - { - JackChannelSelection selection = this->model.GetJackChannelSelection(); - this->Reply(replyTo, "getJackSettings", selection); - } - else if (message == "saveCurrentPreset") - { - this->model.SaveCurrentPreset(this->clientId); - } - else if (message == "saveCurrentPresetAs") - { - SaveCurrentPresetAsBody body; - pReader->read(&body); - int64_t result = this->model.SaveCurrentPresetAs(this->clientId, body.bankInstanceId_,body.name_, body.saveAfterInstanceId_); - Reply(replyTo, "saveCurrentPresetsAs", result); - } - else if (message == "setSelectedPedalboardPlugin") - { - SetSelectedPedalboardPluginBody body; - pReader->read(&body); - this->model.SetSelectedPedalboardPlugin(body.clientId_,body.pluginInstanceId_); - } - else if (message == "savePluginPresetAs") - { - SavePluginPresetAsBody body; - pReader->read(&body); - int64_t result = this->model.SavePluginPresetAs(body.instanceId_, body.name_); - Reply(replyTo, "saveCurrentPresetsAs", result); - } - else if (message == "getPresets") - { - PresetIndex presets; - this->model.GetPresets(&presets); - Reply(replyTo, "getPresets", presets); - } - else if (message == "setPedalboardItemEnable") - { - PedalboardItemEnabledBody body; - pReader->read(&body); - model.SetPedalboardItemEnable(body.clientId_, body.instanceId_, body.enabled_); - } - else if (message == "setPedalboardItemUseModUi") { - PedalboardItemUseModGuiBody body; - pReader->read(&body); - model.SetPedalboardItemUseModUi(body.clientId_, body.instanceId_, body.useModUi_); - } - else if (message == "updateCurrentPedalboard") - { - { - UpdateCurrentPedalboardBody body; - - pReader->read(&body); - this->model.UpdateCurrentPedalboard(body.clientId_, body.pedalboard_); - } - } - else if (message == "setSnapshot") - { - int64_t snapshotIndex = -1; - pReader->read(&snapshotIndex); - this->model.SetSnapshot(snapshotIndex); - } - else if (message == "setSnapshots") - { - SetSnapshotsBody body; - pReader->read(&body); - this->model.SetSnapshots(body.snapshots_, body.selectedSnapshot_); - } - else if (message == "currentPedalboard") - { - auto pedalboard = model.GetCurrentPedalboardCopy(); - Reply(replyTo, "currentPedalboard", pedalboard); - } - else if (message == "plugins") - { - auto ui_plugins = model.GetPluginHost().GetUiPlugins(); - Reply(replyTo, "plugins", ui_plugins); - } - else if (message == "pluginClasses") - { - auto classes = model.GetPluginHost().GetLv2PluginClass(); - Reply(replyTo, "pluginClasses", classes); - } - else if (message == "hello") - { - this->model.AddNotificationSubscription(shared_from_this()); - Reply(replyTo, "ehlo", clientId); - } - else if (message == "setShowStatusMonitor") - { - bool showStatusMonitor; - pReader->read(&showStatusMonitor); - this->model.SetShowStatusMonitor(showStatusMonitor); - } - else if (message == "getShowStatusMonitor") - { - Reply(replyTo, "getShowStatusMonitor", this->model.GetShowStatusMonitor()); - } - else if (message == "version") - { - PiPedalVersion version(this->model); - - Reply(replyTo, "version", version); - } - else if (message == "loadPreset") - { - int64_t instanceId = 0; - pReader->read(&instanceId); - model.LoadPreset(this->clientId, instanceId); - } - else if (message == "updatePresets") - { - PresetIndex newIndex; - pReader->read(&newIndex); - bool result = model.UpdatePresets(this->clientId, newIndex); - this->Reply(replyTo, "updatePresets", result); - } - else if (message == "updatePluginPresets") - { - PluginUiPresets pluginPresets; - pReader->read(&pluginPresets); - model.UpdatePluginPresets(pluginPresets); - this->Reply(replyTo, "updatePluginPresets", true); - } - else if (message == "moveBank") - { - FromToBody body; - pReader->read(&body); - model.MoveBank(this->clientId, body.from_, body.to_); - this->Reply(replyTo, "moveBank"); - } - else if (message == "shutdown") - { - model.RequestShutdown(false); - this->Reply(replyTo, "shutdown"); - } - else if (message == "restart") - { - model.RequestShutdown(true); - this->Reply(replyTo, "restart"); - } - else if (message == "deletePresetItems") - { - std::vector items; - pReader->read(&items); - int64_t result = model.DeletePresets(this->clientId, items); - this->Reply(replyTo, "deletePresetItems", result); - } - else if (message == "deleteBankItem") - { - int64_t instanceId = 0; - pReader->read(&instanceId); - uint64_t result = model.DeleteBank(this->clientId, instanceId); - this->Reply(replyTo, "deleteBankItem", result); - } - else if (message == "renameBank") - { - RenameBankBody body; - pReader->read(&body); - - std::stringstream tOut; - json_writer tWriter(tOut); - tWriter.write(body.newName_); - std::string tJson = tOut.str(); - std::stringstream tIn(tJson); - json_reader tReader(tIn); - std::string tResult; - tReader.read(&tResult); - - body.newName_ = tResult; - - try - { - model.RenameBank(this->clientId, body.bankId_, body.newName_); - this->Reply(replyTo, "renameBank"); - } - catch (const std::exception &e) - { - this->SendError(replyTo, std::string(e.what())); - } - } - else if (message == "openBank") - { - int64_t bankId = -1; - pReader->read(&bankId); - try - { - model.OpenBank(this->clientId, bankId); - ; - this->Reply(replyTo, "openBank"); - } - catch (const std::exception &e) - { - this->SendError(replyTo, std::string(e.what())); - } - } - else if (message == "saveBankAs") - { - RenameBankBody body; - pReader->read(&body); - try - { - int64_t newId = model.SaveBankAs(this->clientId, body.bankId_, body.newName_); - this->Reply(replyTo, "saveBankAs", newId); - } - catch (const std::exception &e) - { - this->SendError(replyTo, std::string(e.what())); - } - } - else if (message == "nextBank") - { - model.NextBank(); - } - else if (message == "previousBank") - { - model.PreviousBank(); - } - else if (message == "nextPreset") - { - model.NextPreset(); - } - else if (message == "previousPreset") - { - model.PreviousPreset(); - } - - else if (message == "renamePresetItem") - { - RenamePresetBody body; - pReader->read(&body); - - bool result = model.RenamePreset(body.clientId_, body.instanceId_, body.name_); - this->Reply(replyTo, "renamePresetItem", result); - } - else if (message == "copyPreset") - { - CopyPresetBody body; - pReader->read(&body); - int64_t result = model.CopyPreset(body.clientId_, body.fromId_, body.toId_); - this->Reply(replyTo, "copyPreset", result); - } - else if (message == "copyPluginPreset") - { - CopyPluginPresetBody body; - pReader->read(&body); - uint64_t result = model.CopyPluginPreset(body.pluginUri_, body.instanceId_); - this->Reply(replyTo, "copyPluginPreset", result); - } - else if (message == "setPatchProperty") - { - SetPatchPropertyBody body; - pReader->read(&body); - model.SendSetPatchProperty(clientId, body.instanceId_, body.propertyUri_, body.value_, [this, replyTo]() - { this->JsonReply(replyTo, "setPatchProperty", "true"); }, [this, replyTo](const std::string &error) - { this->SendError(replyTo, error.c_str()); }); - } - else if (message == "setPedalboardItemTitle") - { - SetPedalboardItemTitleBody body; - pReader->read(&body); - model.SetPedalboardItemTitle(body.instanceId_, body.title_, body.colorKey_); - } - else if (message == "getPatchProperty") - { - GetPatchPropertyBody body; - pReader->read(&body); - - model.SendGetPatchProperty( - this->clientId, - body.instanceId_, - body.propertyUri_, - [this, replyTo](const std::string &jsonResult) - { - this->JsonReply(replyTo, "getPatchProperty", jsonResult.c_str()); - }, - [this, replyTo](const std::string &error) - { - this->SendError(replyTo, error.c_str()); - }); - } - else if (message == "monitorPort") - { - - MonitorPortBody body; - pReader->read(&body); - - MonitorPort(replyTo, body); - } - else if (message == "unmonitorPort") - { - int64_t subscriptionHandle; - pReader->read(&subscriptionHandle); - { - { - std::lock_guard guard(activePortMonitorsMutex); - for (auto i = this->activePortMonitors.begin(); i != this->activePortMonitors.end(); ++i) - { - if ((*i)->subscriptionHandle == subscriptionHandle) - { - auto subscription = (*i); - subscription->Close(); - - this->activePortMonitors.erase(i); - break; - } - } - } - model.UnmonitorPort(subscriptionHandle); - } - } - else if (message == "addVuSubscription") - { - int64_t instanceId = -1; - - pReader->read(&instanceId); - - int64_t subscriptionHandle = model.AddVuSubscription(instanceId); - - { - std::lock_guard guard(subscriptionMutex); - activeVuSubscriptions.push_back(VuSubscription{subscriptionHandle, instanceId}); - } - this->Reply(replyTo, "addVuSubscription", subscriptionHandle); - } - else if (message == "removeVuSubscription") - { - int64_t subscriptionHandle = -1; - pReader->read(&subscriptionHandle); - { - std::lock_guard guard(subscriptionMutex); - - for (auto i = activeVuSubscriptions.begin(); i != activeVuSubscriptions.end(); ++i) - { - if (i->subscriptionHandle == subscriptionHandle) - { - activeVuSubscriptions.erase(i); - break; - } - } - } - model.RemoveVuSubscription(subscriptionHandle); - } - else if (message == "imageList") - { - - this->Reply(replyTo, "imageList", imageList); - } - else if (message == "getFavorites") - { - std::map favorites = this->model.GetFavorites(); - this->Reply(replyTo, "getFavorites", favorites); - } - else if (message == "setFavorites") - { - std::map favorites; - pReader->read(&favorites); - this->model.SetFavorites(favorites); - } - else if (message == "setUpdatePolicy") - { - int iPolicy; - pReader->read(&iPolicy); - - this->model.SetUpdatePolicy((UpdatePolicyT)iPolicy); - } - else if (message == "forceUpdateCheck") - { - this->model.ForceUpdateCheck(); - } - else if (message == "setSystemMidiBindings") - { - std::vector bindings; - pReader->read(&bindings); - this->model.SetSystemMidiBindings(bindings); - } - else if (message == "getSystemMidiBindings") - { - std::vector bindings = this->model.GetSystemMidiBidings(); - this->Reply(replyTo, "getSystemMidiBindings", bindings); - } - else if (message == "requestFileList") - { - throw std::runtime_error("No longer implemented."); - } - else if (message == "requestFileList2") - { - FileRequestArgs requestArgs; - pReader->read(&requestArgs); - FileRequestResult result = this->model.GetFileList2(requestArgs.relativePath_, requestArgs.fileProperty_); - this->Reply(replyTo, "requestFileList2", result); - } - else if (message == "newPreset") - { - int64_t presetId = this->model.CreateNewPreset(); - this->Reply(replyTo, "newPreset", presetId); - } - else if (message == "deleteUserFile") - { - std::string fileName; - pReader->read(&fileName); - - this->model.DeleteSampleFile(fileName); - this->Reply(replyTo, "deleteUserFile", true); - } - else if (message == "createNewSampleDirectory") - { - CreateNewSampleDirectoryArgs args; - pReader->read(&args); - - std::string newFileName = this->model.CreateNewSampleDirectory(args.relativePath_, args.uiFileProperty_); - this->Reply(replyTo, "createNewSampleDirectory", newFileName); - } - else if (message == "renameFilePropertyFile") - { - RenameSampleFileArgs args; - pReader->read(&args); - - std::string newFileName = this->model.RenameFilePropertyFile(args.oldRelativePath_, args.newRelativePath_, args.uiFileProperty_); - this->Reply(replyTo, "renameFilePropertyFile", newFileName); - } - else if (message == "copyFilePropertyFile") - { - CopySampleFileArgs args; - pReader->read(&args); - - std::string newFileName = this->model.CopyFilePropertyFile(args.oldRelativePath_, args.newRelativePath_, args.uiFileProperty_, args.overwrite_); - this->Reply(replyTo, "copyFilePropertyFile", newFileName); - } - else if (message == "getFilePropertyDirectoryTree") - { - GetFilePropertyDirectoryTreeArgs args; - pReader->read(&args); - FilePropertyDirectoryTree::ptr result = - model.GetFilePropertydirectoryTree( - args.fileProperty_, - args.selectedPath_); - this->Reply(replyTo, "GetFilePropertydirectoryTree", result); - } - else if (message == "getFilePropertyDirectoryTree") - { - GetFilePropertyDirectoryTreeArgs args; - pReader->read(&args); - FilePropertyDirectoryTree::ptr result = - model.GetFilePropertydirectoryTree( - args.fileProperty_, - args.selectedPath_); - this->Reply(replyTo, "GetFilePropertydirectoryTree", result); - } - - else if (message == "moveAudioFile") - { - MoveAudioFileArgs args; - pReader->read(&args); - this->model.MoveAudioFile(args.path_, args.from_, args.to_); - bool result = true; - this->Reply(replyTo,"moveAudioFile", result); - } - else if (message == "setOnboarding") - { - bool value; - pReader->read(&value); - this->model.SetOnboarding(value); - } - else if (message == "getWifiRegulatoryDomains") - { - auto regulatoryDomains = this->model.GetWifiRegulatoryDomains(); - this->Reply(replyTo, "getWifiRegulatoryDomains", regulatoryDomains); - } else if (message == "setAlsaSequencerConfiguration") - { - AlsaSequencerConfiguration config; - pReader->read(&config); - this->model.SetAlsaSequencerConfiguration(config); - this->Reply(replyTo, "setAlsaSequencerConfiguration"); - } - else if (message == "getAlsaSequencerConfiguration") - { - AlsaSequencerConfiguration config = this->model.GetAlsaSequencerConfiguration(); - this->Reply(replyTo, "getAlsaSequencerConfiguration", config); - } - else if (message == "getAlsaSequencerPorts") - { - std::vector result = model.GetAlsaSequencerPorts(); - this->Reply(replyTo,"getAlsaSequencerPorts", result); - } else if (message == "requestBankPresets") { - - RequestBankPresetsBody args; - pReader->read(&args); - auto result = this->model.RequestBankPresets(args.bankInstanceId_); - this->Reply(replyTo,"requestBankPresets",result); - - } - else if (message == "importPresetsFromBank") { - ImportPresetsFromBankBody args; - pReader->read(&args); - auto result = this->model.ImportPresetsFromBank(args.bankInstanceId_, args.presets_); - this->Reply(replyTo,"importPresetsFromBank",result); - } - else if (message == "copyPresetsToBank") { - CopyPresetsToBankBody args; - pReader->read(&args); - auto result = this->model.CopyPresetsToBank(args.bankInstanceId_, args.presets_); - this->Reply(replyTo,"copyPresetsToBank",result); - } else if (message == "getChannelRouterSettings") - { - ChannelRouterSettings::ptr result = this->model.GetChannelRouterSettings(); - this->Reply(replyTo, "getChannelRouterSettings", result); - } else if (message == "setChannelRouterSettings") { - ChannelRouterSettings::ptr args; - pReader->read(&args); - this->model.SetChannelRouterSettings(this->clientId, args); - } - else if (message == "downloadModelsFromTone3000") - { - DownloadModelsFromTone3000Body args; - pReader->read(&args); - auto result = this->model.DownloadModelsFromTone3000( - this->clientId, - StringToTone3000DownloadType(args.downloadType_), - args.downloadPath_, - args.tone3000Url_ - ); - this->Reply(replyTo,"downloadModelsFromTone3000",result); - } else if (message == "cancelTone3000Download") - { - int64_t handle = -1; - pReader->read(&handle); - model.CancelTone3000Download(clientId,handle); - } else if (message == "pingTone3000Server") - { - std::shared_ptr this_ = shared_from_this(); - model.Post([this_, replyTo] () { - bool result = this_->PingTone3000Server(); - this_->Reply(replyTo,"pingTone3000Server",result); - }); - } - else - { - Lv2Log::error("Unknown message received: %s", message.c_str()); - SendError(replyTo, std::string("Unknown message: ") + message); - } + Lv2Log::error("Unknown message received: %s", message.c_str()); + SendError(replyTo, std::string("Unknown message: ") + message); } protected: diff --git a/src/RingBufferReader.hpp b/src/RingBufferReader.hpp index 990b6fa..5e684a9 100644 --- a/src/RingBufferReader.hpp +++ b/src/RingBufferReader.hpp @@ -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) { diff --git a/src/Storage.cpp b/src/Storage.cpp index 69b3302..49bc39c 100644 --- a/src/Storage.cpp +++ b/src/Storage.cpp @@ -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 &bindings) diff --git a/src/alsaCheck.cpp b/src/alsaCheck.cpp index e2147e9..25caa41 100644 --- a/src/alsaCheck.cpp +++ b/src/alsaCheck.cpp @@ -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"; diff --git a/todo.txt b/todo.txt index d0d4fdc..30ca17d 100644 --- a/todo.txt +++ b/todo.txt @@ -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? diff --git a/vite/public/favicon.ico b/vite/public/favicon.ico index 68428f3..87dab51 100644 Binary files a/vite/public/favicon.ico and b/vite/public/favicon.ico differ diff --git a/vite/public/logo192.png b/vite/public/logo192.png index b73484f..b5e5434 100644 Binary files a/vite/public/logo192.png and b/vite/public/logo192.png differ diff --git a/vite/public/logo512.png b/vite/public/logo512.png index 696e0f1..9c7691a 100644 Binary files a/vite/public/logo512.png and b/vite/public/logo512.png differ diff --git a/vite/public/manifest.json b/vite/public/manifest.json index 105f57a..e7faaad 100644 --- a/vite/public/manifest.json +++ b/vite/public/manifest.json @@ -1,24 +1,39 @@ { - "short_name": "PiPedal", - "name": "PiPedal", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" + "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" }, - { - "src": "logo192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "logo512.png", - "type": "image/png", - "sizes": "512x512" - } - ], - "start_url": ".", - "theme_color": "#000000", - "background_color": "#ffffff" -} + "homepage_url": "https://rerdavies.github.io/pipedal", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "theme_color": "#000000", + "display": "standalone", + "display_override": [ + "fullscreen", + "standalone", + "minimal-ui", + "browser" + ], + "background_color": "#000000" +} \ No newline at end of file diff --git a/vite/src/pipedal/AppThemed.css b/vite/src/pipedal/AppThemed.css index 3c0694d..d17898b 100644 --- a/vite/src/pipedal/AppThemed.css +++ b/vite/src/pipedal/AppThemed.css @@ -49,4 +49,5 @@ input[type=number] { img { user-select: none; -} \ No newline at end of file +} + diff --git a/vite/src/pipedal/ChannelRouterSettings.tsx b/vite/src/pipedal/ChannelRouterSettings.tsx index 6b5d119..3e65a42 100644 --- a/vite/src/pipedal/ChannelRouterSettings.tsx +++ b/vite/src/pipedal/ChannelRouterSettings.tsx @@ -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); + } } \ No newline at end of file diff --git a/vite/src/pipedal/ChannelRouterSettingsDialog.tsx b/vite/src/pipedal/ChannelRouterSettingsDialog.tsx index 93361ad..57ae590 100644 --- a/vite/src/pipedal/ChannelRouterSettingsDialog.tsx +++ b/vite/src/pipedal/ChannelRouterSettingsDialog.tsx @@ -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,129 +67,168 @@ interface ChannelRouterSettingsPreset { } -let channelRouterPresets: ChannelRouterSettingsPreset[] = [ - { - id: -2, name: "Left -> Left", settings: new ChannelRouterSettings().deserialize({ - configured: true, - channelRouterPresetId: -2, - mainInputChannels: [0, 0], - mainOutputChannels: [0, 0], - mainInserts: new Pedalboard(), - auxInputChannels: [-1, -1], - auxOutputChannels: [-1, -1], - auxInserts: new Pedalboard(), - sendInputChannels: [-1, -1], - sendOutputChannels: [-1, -1], - controlValues: [], - }) - }, - { - id: -3, name: "Left -> Stereo", settings: new ChannelRouterSettings().deserialize({ - configured: true, - channelRouterPresetId: -3, - mainInputChannels: [0, 0], - mainOutputChannels: [0, 1], - mainInserts: new Pedalboard(), - auxInputChannels: [-1, -1], - auxOutputChannels: [-1, -1], - auxInserts: new Pedalboard(), - sendInputChannels: [-1, -1], - sendOutputChannels: [-1, -1], - controlValues: [], - }) - }, - { - id: -4, name: "Right -> Stereo", settings: new ChannelRouterSettings().deserialize({ - configured: true, - channelRouterPresetId: -4, - mainInputChannels: [1, 1], - mainOutputChannels: [0, 1], - mainInserts: new Pedalboard(), - auxInputChannels: [-1, -1], - auxOutputChannels: [-1, -1], - auxInserts: new Pedalboard(), - sendInputChannels: [-1, -1], - sendOutputChannels: [-1, -1], - controlValues: [], - }) - }, - { - id: -5, name: "Right -> Left + re-amp", settings: new ChannelRouterSettings().deserialize({ - configured: true, - channelRouterPresetId: -5, - mainInputChannels: [1, 1], - mainOutputChannels: [0, 0], - mainInserts: new Pedalboard(), - auxInputChannels: [1, 1], - auxOutputChannels: [1, 1], - auxInserts: new Pedalboard(), - sendInputChannels: [-1, - 1], - sendOutputChannels: [-1, -1], - controlValues: [], - }) - }, - { - id: -6, name: "Left -> Left + send", settings: new ChannelRouterSettings().deserialize({ - configured: true, - channelRouterPresetId: -6, - mainInputChannels: [0, 0], - mainOutputChannels: [0, 0], - mainInserts: new Pedalboard(), - auxInputChannels: [-1, -1], - auxOutputChannels: [-1, - 1], - auxInserts: new Pedalboard(), - sendInputChannels: [1, 1], - sendOutputChannels: [1, 1], - controlValues: [], - }) - }, - { - id: -7, name: "Right -> Right + send", settings: new ChannelRouterSettings().deserialize({ - configured: true, - channelRouterPresetId: -7, - mainInputChannels: [1, 1], - mainOutputChannels: [1, 1], - mainInserts: new Pedalboard(), - auxInputChannels: [-1, -1], - auxOutputChannels: [-1, - 1], - auxInserts: new Pedalboard(), - sendInputChannels: [0, 0], - sendOutputChannels: [0, 0], - controlValues: [], - }) - }, - { - id: -8, name: "Left -> Stereo + re-amp", settings: new ChannelRouterSettings().deserialize({ - configured: true, - channelRouterPresetId: -8, - mainInputChannels: [0, 0], - mainOutputChannels: [0, 1], - mainInserts: new Pedalboard(), - auxInputChannels: [0, 0], - auxOutputChannels: [2, 2], - auxInserts: new Pedalboard(), - sendInputChannels: [-1, -1], - sendOutputChannels: [-1, -1], - controlValues: [], +let 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. - }) - }, - { - id: -9, name: "Left -> Stereo + stereo send", settings: new ChannelRouterSettings().deserialize({ - configured: true, - channelRouterPresetId: -9, - mainInputChannels: [0, 0], - mainOutputChannels: [0, 1], - mainInserts: new Pedalboard(), - auxInputChannels: [-1, -1], - auxOutputChannels: [-1, - 1], - auxInserts: new Pedalboard(), - sendInputChannels: [2, 3], - sendOutputChannels: [2, 3], - controlValues: [], - - }) - }, +let factoryRouterPresets: ChannelRouterSettings[] = [ + // Input->Output + new ChannelRouterSettings().deserialize({ + configured: true, + channelRouterPresetId: -2, + 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: [] + }) + , + // Input->Stereo out. + new ChannelRouterSettings().deserialize({ + configured: true, + channelRouterPresetId: -3, + mainInputChannels: [0, -1], + mainOutputChannels: [0, 1], + mainInserts: new Pedalboard(), + auxInputChannels: [-1, -1], + auxOutputChannels: [-1, -1], + auxInserts: new Pedalboard(), + sendInputChannels: [-1, -1], + sendOutputChannels: [-1, -1], + controlValues: [] + }) + , + // Right->Stereo out. + new ChannelRouterSettings().deserialize({ + configured: true, + channelRouterPresetId: -4, + mainInputChannels: [1,-1], + mainOutputChannels: [0, 1], + mainInserts: new Pedalboard(), + auxInputChannels: [-1, -1], + auxOutputChannels: [-1, -1], + auxInserts: new Pedalboard(), + sendInputChannels: [-1, -1], + sendOutputChannels: [-1, -1], + controlValues: [], + }) + , + new ChannelRouterSettings().deserialize({ + configured: true, + channelRouterPresetId: -5, + mainInputChannels: [1,-1], + mainOutputChannels: [0, -1], + mainInserts: new Pedalboard(), + auxInputChannels: [1,-1], + auxOutputChannels: [1,-1], + auxInserts: new Pedalboard(), + sendInputChannels: [-1, -1], + sendOutputChannels: [-1, -1], + controlValues: [], + }) + , + new ChannelRouterSettings().deserialize({ + configured: true, + channelRouterPresetId: -6, + mainInputChannels: [1,-1], + mainOutputChannels: [1,-1], + mainInserts: new Pedalboard(), + auxInputChannels: [0, -1], + auxOutputChannels: [0, -1], + auxInserts: new Pedalboard(), + sendInputChannels: [-1, -1], + sendOutputChannels: [-1, -1], + controlValues: [], + }) + , + new ChannelRouterSettings().deserialize({ + configured: true, + channelRouterPresetId: -7, + mainInputChannels: [1,-1], + mainOutputChannels: [0, -1], + mainInserts: new Pedalboard(), + auxInputChannels: [0, -1], + auxOutputChannels: [1,-1], + auxInserts: new Pedalboard(), + sendInputChannels: [-1, -1], + sendOutputChannels: [-1, -1], + controlValues: [] + }) + , + new ChannelRouterSettings().deserialize({ + configured: true, + channelRouterPresetId: -8, + mainInputChannels: [0, -1], + mainOutputChannels: [0, -1], + mainInserts: new Pedalboard(), + auxInputChannels: [1,-1], + auxOutputChannels: [1,-1], + auxInserts: new Pedalboard(), + sendInputChannels: [-1, -1], + sendOutputChannels: [-1, -1], + controlValues: [] + }) + , + new ChannelRouterSettings().deserialize({ + configured: true, + channelRouterPresetId: -9, + mainInputChannels: [0, -1], + mainOutputChannels: [0, -1], + mainInserts: new Pedalboard(), + auxInputChannels: [-1, -1], + auxOutputChannels: [-1, -1], + auxInserts: new Pedalboard(), + sendInputChannels: [1,-1], + sendOutputChannels: [1,-1], + controlValues: [] + }) + , + new ChannelRouterSettings().deserialize({ + configured: true, + channelRouterPresetId: -10, + mainInputChannels: [1,-1], + mainOutputChannels: [1,-1], + mainInserts: new Pedalboard(), + auxInputChannels: [-1, -1], + auxOutputChannels: [-1, -1], + auxInserts: new Pedalboard(), + sendInputChannels: [0, -1], + sendOutputChannels: [0, -1], + controlValues: [] + }) + , + new ChannelRouterSettings().deserialize({ + configured: true, + channelRouterPresetId: -11, + mainInputChannels: [0, -1], + mainOutputChannels: [0, 1], + mainInserts: new Pedalboard(), + auxInputChannels: [0, -1], + auxOutputChannels: [2, -1], + auxInserts: new Pedalboard(), + sendInputChannels: [-1, -1], + sendOutputChannels: [-1, -1], + controlValues: [] + }) + , + new ChannelRouterSettings().deserialize({ + configured: true, + channelRouterPresetId: -12, + mainInputChannels: [0, -1], + mainOutputChannels: [0, 1], + mainInserts: new Pedalboard(), + auxInputChannels: [-1, -1], + auxOutputChannels: [-1, -1], + auxInserts: new Pedalboard(), + sendInputChannels: [2, 3], + sendOutputChannels: [2, 3], + controlValues: [] + }) + , ]; let cellPortraitInOutDiv: React.CSSProperties = { @@ -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[] = [(None)] if (channelCount === 0) { return items; } - if (channelCount === 2) { - items.push((Left)); - items.push((Right)); + if (channelCount === 1) { + if (input) { + items.push((Input)); + } else { + items.push((Output)); + } + } else if (channelCount === 2) { + items.push((Left)); + items.push((Right)); return items; + } else { + for (let i = 0; i < channelCount; ++i) { + items.push((Ch {i + 1})); + } } - for (let i = 0; i < channelCount; ++i) { - items.push((Ch {i + 1})); + if (routeType === RouteType.Aux && input) { + items.push((Main Out L)); + items.push((Main Out R)); + } 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(model.channelRouterSettings.get()); - const [controlValues, setControlValues] = useState(model.channelRouterControlValues.get()); + const [settings, setSettings] = useState(GetDefaultChannelRouterSettings(model)); const [showHelp, setShowHelp] = useState(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,61 +448,94 @@ 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 ( - ) } @@ -437,18 +597,32 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) { return items; } + let handleSave = () => { + + }; let PresetSelect = (width: number | string | undefined) => { return (
- - +
+ { handleSave(); }} + size="large"> + + +
+ +
+ +
+ { }} />
@@ -457,6 +631,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) { let LandscapeView = () => { + let auxInsertDisabled = !settings.isAuxActive(); return (
{Vu()} - @@ -574,6 +753,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) { ); }; let PortraitView = () => { + let auxInsertDisabled = !settings.isAuxActive(); return (
{Vu()}
-
diff --git a/vite/src/pipedal/ChannelRouterSettingsHelpDialog.tsx b/vite/src/pipedal/ChannelRouterSettingsHelpDialog.tsx index f2d98c2..98ed5d2 100644 --- a/vite/src/pipedal/ChannelRouterSettingsHelpDialog.tsx +++ b/vite/src/pipedal/ChannelRouterSettingsHelpDialog.tsx @@ -105,15 +105,15 @@ export default class ChannelRouterSettingsHelpDialog extends ResizeResponsiveCom
-

The Mixer Routing Dialog determines how audio signals are processed and routed between input and output audio channels. +

The Audio Channel Routing Dialog determines how audio signals are processed and routed between input and output audio channels.

-

Guitar effects processing occurs on the Main route only. Plugins in the main PiPedal window are +

By default, guitar effects processing occurs on the Main route only. Plugins in the main PiPedal window are applied before any insert plugins that have been added to the Main 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.

-

Plugins in the main PiPedal window are not applied to the Aux route. The Aux route is intended to be +

Plugins in the main PiPedal window are not applied to the Aux route by default. The Aux route is intended to be used for

    @@ -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.
-

although you may find other creative uses for the Aux channel as well.

+

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. +

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.

diff --git a/vite/src/pipedal/JackServerSettings.tsx b/vite/src/pipedal/JackServerSettings.tsx index ab1e1e7..3ba2c0c 100644 --- a/vite/src/pipedal/JackServerSettings.tsx +++ b/vite/src/pipedal/JackServerSettings.tsx @@ -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}`; } diff --git a/vite/src/pipedal/JackServerSettingsDialog.tsx b/vite/src/pipedal/JackServerSettingsDialog.tsx index 3bb5e71..6bd0cda 100644 --- a/vite/src/pipedal/JackServerSettingsDialog.tsx +++ b/vite/src/pipedal/JackServerSettingsDialog.tsx @@ -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({