Hotspot: no scanning if not required.

This commit is contained in:
Robin E. R. Davies
2025-06-29 20:27:43 -04:00
parent 03cb18e065
commit 79aa1441c3
11 changed files with 553 additions and 206 deletions
+232 -7
View File
@@ -34,6 +34,7 @@
#include "Finally.hpp" #include "Finally.hpp"
#include "Lv2Log.hpp" #include "Lv2Log.hpp"
#include <mutex> #include <mutex>
#include <thread>
// enumerate alsa sequencer ports // enumerate alsa sequencer ports
@@ -96,6 +97,26 @@ namespace pipedal
int queueId = -1; // Queue for real-time timestamps int queueId = -1; // Queue for real-time timestamps
}; };
class AlsaSequencerDeviceMonitorImpl : public AlsaSequencerDeviceMonitor
{
public:
virtual ~AlsaSequencerDeviceMonitorImpl() override;
virtual void StartMonitoring(
Callback &&onChangeCallback) override;
virtual void StopMonitoring() override;
private:
int CreateInputQueue(snd_seq_t *seqHandle, int inPort);
bool started = false;
void ServiceProc();
std::unique_ptr<std::jthread> serviceThread;
std::atomic<bool> terminateThread{false};
Callback callback;
};
}; };
using namespace impl; using namespace impl;
@@ -247,9 +268,10 @@ namespace pipedal
size_t inputBufferSize = snd_seq_get_input_buffer_size(seqHandle); size_t inputBufferSize = snd_seq_get_input_buffer_size(seqHandle);
(void)inputBufferSize; (void)inputBufferSize;
rc = snd_seq_set_input_buffer_size(seqHandle, 128*1024); rc = snd_seq_set_input_buffer_size(seqHandle, 128 * 1024);
if (rc < 0) { if (rc < 0)
Lv2Log::warning("Failed resize the ALSA sequencer input buffer: %s",snd_strerror(rc)); {
Lv2Log::warning("Failed resize the ALSA sequencer input buffer: %s", snd_strerror(rc));
} }
snd_seq_set_client_name(seqHandle, "PiPedal"); snd_seq_set_client_name(seqHandle, "PiPedal");
@@ -584,10 +606,10 @@ namespace pipedal
message.Set(0xF8); // MIDI Real Time Clock Tick message.Set(0xF8); // MIDI Real Time Clock Tick
break; break;
#ifndef NDEBUG #ifndef NDEBUG
#define MSG_DEBUG_LOG(x) \ #define MSG_DEBUG_LOG(x) \
case x:\ case x: \
Lv2Log::debug("ALSA Sequencer Message" #x);\ Lv2Log::debug("ALSA Sequencer Message" #x); \
break; break;
#else #else
#define MSG_DEBUG_LOG(x) #define MSG_DEBUG_LOG(x)
#endif #endif
@@ -733,6 +755,7 @@ namespace pipedal
Finally seq_finally([seq]() Finally seq_finally([seq]()
{ snd_seq_close(seq); }); { snd_seq_close(seq); });
snd_seq_addr_t sender, dest; snd_seq_addr_t sender, dest;
dest.client = myClientId; dest.client = myClientId;
dest.port = 0; dest.port = 0;
@@ -797,6 +820,208 @@ namespace pipedal
} }
} }
AlsaSequencerDeviceMonitor::ptr AlsaSequencerDeviceMonitor::Create()
{
return std::make_shared<AlsaSequencerDeviceMonitorImpl>();
}
void AlsaSequencerDeviceMonitorImpl::StartMonitoring(
Callback &&onChangeCallback)
{
started = true;
this->callback = std::move(onChangeCallback);
this->serviceThread = std::make_unique<std::jthread>(
[this]()
{
ServiceProc();
});
}
void AlsaSequencerDeviceMonitorImpl::ServiceProc()
{
int err;
snd_seq_t *seqHandle;
err = snd_seq_open(&seqHandle, "default", SND_SEQ_OPEN_DUPLEX, 0);
Finally seq_finally(
[seqHandle]()
{
snd_seq_close(seqHandle);
});
if (err < 0)
{
Lv2Log::error("Error opening ALSA Device Monitor sequencer: %s", snd_strerror(err));
return;
}
snd_seq_set_client_name(seqHandle, "Device Monitor");
int inPort = snd_seq_create_simple_port(
seqHandle, "PiPedal:portMonitor",
SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
SND_SEQ_PORT_TYPE_APPLICATION);
if (inPort < 0)
{
Lv2Log::error("Error creating ALSA Device Monitor port: %s", snd_strerror(inPort));
return;
}
Finally inPort_finaly {
[seqHandle, inPort]()
{
snd_seq_delete_port(seqHandle, inPort);
}
};
// Set client name
int queueId = CreateInputQueue(seqHandle,inPort);
if (queueId < 0)
{
Lv2Log::error("Error creating ALSA Device Monitor queue: %s", snd_strerror(queueId));
return;
}
Finally queue_finally(
[seqHandle, queueId]()
{
snd_seq_free_queue(seqHandle, queueId);
});
// Subscribe to system announcements
snd_seq_port_subscribe_t *subscription;
snd_seq_port_subscribe_alloca(&subscription);
snd_seq_addr_t sender, dest;
sender.client = SND_SEQ_CLIENT_SYSTEM;
sender.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
dest.client = snd_seq_client_id(seqHandle);
dest.port = inPort;
snd_seq_port_subscribe_set_sender(subscription, &sender);
snd_seq_port_subscribe_set_dest(subscription, &dest);
err = snd_seq_subscribe_port(seqHandle, subscription);
if (err < 0)
{
Lv2Log::error("Failed to subscribe to ALSA sequencer announcements: %s", snd_strerror(err));
return;
}
// Create poll descriptors
std::vector<struct pollfd> pollFds;
snd_seq_nonblock(seqHandle, 1); // Set sequencer to non-blocking mode
while (!terminateThread)
{
int nPollFds = snd_seq_poll_descriptors_count(seqHandle, POLLIN);
pollFds.resize(nPollFds);
snd_seq_poll_descriptors(seqHandle, pollFds.data(), nPollFds, POLLIN);
// Poll for events
if (poll(pollFds.data(), nPollFds, 100) > 0)
{
snd_seq_event_t *event;
while (snd_seq_event_input(seqHandle, &event) > 0)
{
if (event->type == SND_SEQ_EVENT_CLIENT_START)
{
// Get the client name for logging/debugging
snd_seq_client_info_t *client_info;
snd_seq_client_info_alloca(&client_info);
if (snd_seq_get_any_client_info(seqHandle, event->data.addr.client, client_info) >= 0) {
std::string clientName = snd_seq_client_info_get_name(client_info);
callback(MonitorAction::DeviceAdded, event->data.addr.client, clientName);
}
}
else if (event->type == SND_SEQ_EVENT_CLIENT_EXIT)
{
callback(MonitorAction::DeviceRemoved, event->data.addr.client,"");
}
snd_seq_free_event(event);
}
}
}
return;
}
void AlsaSequencerDeviceMonitorImpl::StopMonitoring()
{
if (started)
{
started = false;
terminateThread = true;
serviceThread = nullptr; // (joins)
}
}
AlsaSequencerDeviceMonitorImpl::~AlsaSequencerDeviceMonitorImpl()
{
StopMonitoring();
}
int AlsaSequencerDeviceMonitorImpl::CreateInputQueue(snd_seq_t *seqHandle, int inPort)
{
if (!seqHandle)
{
throw std::runtime_error("ALSA sequencer not initialized");
}
// Create a new queue if we don't have one yet
int queueId = -1;
{
queueId = snd_seq_alloc_named_queue(seqHandle, "PiPedal Device Monitor Queue");
if (queueId < 0)
{
throw std::runtime_error(SS("Failed to create ALSA queue: " << snd_strerror(queueId)));
}
// Set queue timing to real-time mode
snd_seq_queue_tempo_t *tempo;
snd_seq_queue_tempo_alloca(&tempo);
snd_seq_queue_tempo_set_tempo(tempo, 120); // 120 BPM default
snd_seq_queue_tempo_set_ppq(tempo, 96); // 96 ticks per quarter note
int rc = snd_seq_set_queue_tempo(seqHandle, queueId, tempo);
if (rc < 0)
{
snd_seq_free_queue(seqHandle, queueId);
throw std::runtime_error(SS("Failed to set queue tempo: " << snd_strerror(rc)));
}
// Start the queue
rc = snd_seq_start_queue(seqHandle, queueId, nullptr);
if (rc < 0)
{
snd_seq_free_queue(seqHandle, queueId);
throw std::runtime_error(SS("Failed to start queue: " << snd_strerror(rc)));
}
// Set the queue for input timestamping
snd_seq_port_info_t *port_info;
snd_seq_port_info_alloca(&port_info);
rc = snd_seq_get_port_info(seqHandle, inPort, port_info);
if (rc < 0)
{
snd_seq_free_queue(seqHandle, queueId);
throw std::runtime_error(SS("Failed to get port info: " << snd_strerror(rc)));
}
// Enable timestamping on the input port
snd_seq_port_info_set_timestamping(port_info, 1);
snd_seq_port_info_set_timestamp_real(port_info, 1);
snd_seq_port_info_set_timestamp_queue(port_info, queueId);
rc = snd_seq_set_port_info(seqHandle, inPort, port_info);
if (rc < 0)
{
snd_seq_free_queue(seqHandle, queueId);
throw std::runtime_error(SS("Failed to set port timestamping: " << snd_strerror(rc)));
}
return queueId;
}
return queueId;
}
JSON_MAP_BEGIN(AlsaSequencerPortSelection) JSON_MAP_BEGIN(AlsaSequencerPortSelection)
JSON_MAP_REFERENCE(AlsaSequencerPortSelection, id) JSON_MAP_REFERENCE(AlsaSequencerPortSelection, id)
JSON_MAP_REFERENCE(AlsaSequencerPortSelection, name) JSON_MAP_REFERENCE(AlsaSequencerPortSelection, name)
+97 -77
View File
@@ -89,7 +89,6 @@ bool WifiConfigSettings::ValidateCountryCode(const std::string &text)
return std::isalpha(text[0]) && std::isalpha(text[1]); return std::isalpha(text[0]) && std::isalpha(text[1]);
} }
void WifiConfigSettings::Load() void WifiConfigSettings::Load()
{ {
try try
@@ -132,13 +131,16 @@ static void openWithPerms(
f.open(path); f.open(path);
f.close(); f.close();
} }
try { try
{
// set the perms. // set the perms.
std::filesystem::permissions( std::filesystem::permissions(
path, path,
perms, perms,
std::filesystem::perm_options::replace); std::filesystem::perm_options::replace);
} catch (const std::exception&) { }
catch (const std::exception &)
{
Lv2Log::warning(SS("Failed to set permissions on" << path << ".")); Lv2Log::warning(SS("Failed to set permissions on" << path << "."));
} }
} }
@@ -149,7 +151,7 @@ static void openWithPerms(
void WifiConfigSettings::Save() void WifiConfigSettings::Save()
{ {
WifiConfigSettings newSettings {*this}; WifiConfigSettings newSettings{*this};
// sync legacy settings, just in case i don't know what. // sync legacy settings, just in case i don't know what.
newSettings.mdnsName_ = newSettings.hotspotName_; newSettings.mdnsName_ = newSettings.hotspotName_;
@@ -167,8 +169,8 @@ void WifiConfigSettings::Save()
try try
{ {
ofstream_synced f; ofstream_synced f;
openWithPerms(f,CONFIG_PATH); openWithPerms(f, CONFIG_PATH);
json_writer writer(f,false); json_writer writer(f, false);
writer.write(&newSettings); writer.write(&newSettings);
} }
catch (const std::exception &e) catch (const std::exception &e)
@@ -300,17 +302,14 @@ namespace pipedal::priv
connection["ipv4"]["method"] = sdbus::Variant(std::string("shared")); connection["ipv4"]["method"] = sdbus::Variant(std::string("shared"));
connection["ipv6"]["method"] = sdbus::Variant(std::string("ignore")); connection["ipv6"]["method"] = sdbus::Variant(std::string("ignore"));
connection["ipv4"]["address-data"] = sdbus::Variant(std::vector<std::map<std::string, sdbus::Variant>>{{{"address", sdbus::Variant("192.168.4.1")},
connection["ipv4"]["address-data"] = sdbus::Variant(std::vector<std::map<std::string, sdbus::Variant>>{{ {"prefix", sdbus::Variant(uint32_t(24))}}});
{"address", sdbus::Variant("192.168.4.1")},
{"prefix", sdbus::Variant(uint32_t(24))}
}});
connection["ipv4"]["dhcp-send-hostname"] = sdbus::Variant(true); connection["ipv4"]["dhcp-send-hostname"] = sdbus::Variant(true);
connection["ipv4"]["dhcp-hostname"] = sdbus::Variant("raspberrypi"); connection["ipv4"]["dhcp-hostname"] = sdbus::Variant("raspberrypi");
connection["ipv4"]["dhcp-options"] = sdbus::Variant(std::vector<std::string>{ connection["ipv4"]["dhcp-options"] = sdbus::Variant(std::vector<std::string>{
"option:classless-static-route,192.168.4.0/24,0.0.0.0,0.0.0.0/0,192.168.4.1" "option:classless-static-route,192.168.4.0/24,0.0.0.0,0.0.0.0/0,192.168.4.1"
//"option:classless-static-route,192.168.4.0/24,192.168.4.1" //"option:classless-static-route,192.168.4.0/24,192.168.4.1"
}); });
auto connection_path = AddConnection(connection); auto connection_path = AddConnection(connection);
return connection_path; return connection_path;
@@ -326,7 +325,6 @@ namespace pipedal::priv
return active_connection_path; return active_connection_path;
} }
void RemoveConnection(const std::string &connection) void RemoveConnection(const std::string &connection)
{ {
auto connection_proxy = sdbus::createProxy( auto connection_proxy = sdbus::createProxy(
@@ -429,7 +427,6 @@ namespace pipedal::priv
}; };
} }
JSON_MAP_BEGIN(WifiConfigSettings) JSON_MAP_BEGIN(WifiConfigSettings)
// v0 // v0
JSON_MAP_REFERENCE(WifiConfigSettings, valid) JSON_MAP_REFERENCE(WifiConfigSettings, valid)
@@ -560,48 +557,47 @@ bool WifiConfigSettings::ValidateChannel(const std::string &countryCode, const s
return true; return true;
} }
static const char* trueValues[] { static const char *trueValues[]{
"true", "true",
"on", "on",
"yes" "yes"
"1", "1",
nullptr nullptr};
}; static const char *falseValues[]{
static const char* falseValues[] {
"false", "false",
"off", "off",
"no", "no",
"0", "0",
nullptr nullptr};
};
static bool Matches(const std::string&value, const char**matches) static bool Matches(const std::string &value, const char **matches)
{ {
while(*matches) while (*matches)
{ {
if (value == *matches) return true; if (value == *matches)
return true;
++matches; ++matches;
} }
return false; return false;
} }
static bool TryStringToBool(const std::string &value, bool*outputValue) static bool TryStringToBool(const std::string &value, bool *outputValue)
{ {
if (Matches(value,trueValues)) if (Matches(value, trueValues))
{ {
*outputValue = true; *outputValue = true;
return true; return true;
} }
if (Matches(value, falseValues)) { if (Matches(value, falseValues))
{
*outputValue = false; *outputValue = false;
return true; return true;
} }
*outputValue = false; *outputValue = false;
return false; return false;
} }
static WifiConfigSettings::ssid_t readSsid(std::istream&ss) static WifiConfigSettings::ssid_t readSsid(std::istream &ss)
{ {
using ssid_t = WifiConfigSettings::ssid_t; using ssid_t = WifiConfigSettings::ssid_t;
char c; char c;
@@ -624,28 +620,33 @@ static WifiConfigSettings::ssid_t readSsid(std::istream&ss)
break; break;
} }
ss >> c; ss >> c;
switch (c) { switch (c)
case 'n': {
result.push_back((uint8_t)'\n'); case 'n':
break; result.push_back((uint8_t)'\n');
case 'r': break;
result.push_back((uint8_t)'\r'); case 'r':
break; result.push_back((uint8_t)'\r');
case 't': break;
result.push_back((uint8_t)'\t'); case 't':
break; result.push_back((uint8_t)'\t');
case 'b': break;
result.push_back((uint8_t)'\b'); case 'b':
break; result.push_back((uint8_t)'\b');
default: break;
result.push_back((uint8_t)c); default:
break; result.push_back((uint8_t)c);
break;
} }
} else { }
else
{
result.push_back((uint8_t)c); result.push_back((uint8_t)c);
} }
} }
} else { }
else
{
while (!ss.eof()) while (!ss.eof())
{ {
if (c == ':') if (c == ':')
@@ -662,7 +663,7 @@ static WifiConfigSettings::ssid_t readSsid(std::istream&ss)
} }
return result; return result;
} }
static std::vector<WifiConfigSettings::ssid_t> stringToSsidArray(const std::string&value) static std::vector<WifiConfigSettings::ssid_t> stringToSsidArray(const std::string &value)
{ {
using ssid_t = WifiConfigSettings::ssid_t; using ssid_t = WifiConfigSettings::ssid_t;
std::istringstream ss(value); std::istringstream ss(value);
@@ -683,16 +684,15 @@ static std::vector<WifiConfigSettings::ssid_t> stringToSsidArray(const std::stri
} }
} }
return result; return result;
} }
void WifiConfigSettings::ParseArguments( void WifiConfigSettings::ParseArguments(
const std::vector<std::string> &argv, const std::vector<std::string> &argv,
HotspotAutoStartMode startMode, HotspotAutoStartMode startMode,
const std::string homeNetworkSsid const std::string homeNetworkSsid
) )
{ {
this->valid_ = false ; this->valid_ = false;
if (argv.size() != 4) if (argv.size() != 4)
{ {
throw invalid_argument("Invalid number of arguments."); throw invalid_argument("Invalid number of arguments.");
@@ -714,7 +714,6 @@ void WifiConfigSettings::ParseArguments(
this->hasPassword_ = this->password_.length() != 0; this->hasPassword_ = this->password_.length() != 0;
this->hasSavedPassword_ = oldSettings.hasSavedPassword_; this->hasSavedPassword_ = oldSettings.hasSavedPassword_;
if (!ValidateCountryCode(this->countryCode_)) if (!ValidateCountryCode(this->countryCode_))
{ {
throw invalid_argument("Invalid country code."); throw invalid_argument("Invalid country code.");
@@ -742,12 +741,12 @@ void WifiConfigSettings::ParseArguments(
this->valid_ = true; this->valid_ = true;
} }
bool WifiConfigSettings::ConfigurationChanged(const WifiConfigSettings&other) const bool WifiConfigSettings::ConfigurationChanged(const WifiConfigSettings &other) const
{ {
return !( return !(
this->valid_ == other.valid_ && this->valid_ == other.valid_ &&
//this->rebootRequired_ == other.rebootRequired_ && // this->rebootRequired_ == other.rebootRequired_ &&
//this->enable_ == other.enable_ && // this->enable_ == other.enable_ &&
this->countryCode_ == other.countryCode_ && this->countryCode_ == other.countryCode_ &&
this->hotspotName_ == other.hotspotName_ && this->hotspotName_ == other.hotspotName_ &&
this->hasPassword_ == other.hasPassword_ && this->hasPassword_ == other.hasPassword_ &&
@@ -756,18 +755,16 @@ bool WifiConfigSettings::ConfigurationChanged(const WifiConfigSettings&other) co
this->homeNetwork_ == other.homeNetwork_ && this->homeNetwork_ == other.homeNetwork_ &&
this->autoStartMode_ == other.autoStartMode_ && this->autoStartMode_ == other.autoStartMode_ &&
this->hasSavedPassword_ == other.hasSavedPassword_ this->hasSavedPassword_ == other.hasSavedPassword_);
);
} }
bool WifiConfigSettings::operator==(const WifiConfigSettings&other) const bool WifiConfigSettings::operator==(const WifiConfigSettings &other) const
{ {
return !ConfigurationChanged(other) && this->wifiWarningGiven_ == other.wifiWarningGiven_; return !ConfigurationChanged(other) && this->wifiWarningGiven_ == other.wifiWarningGiven_;
} }
static bool CanSeeHomeNetwork(const std::string&home, const std::vector<std::string>&availableNetworks) static bool CanSeeHomeNetwork(const std::string &home, const std::vector<std::string> &availableNetworks)
{ {
for (const auto &availableNetwork: availableNetworks) for (const auto &availableNetwork : availableNetworks)
{ {
if (availableNetwork == home) if (availableNetwork == home)
{ {
@@ -789,27 +786,28 @@ bool WifiConfigSettings::WantsHotspot(
switch (autoStartMode) switch (autoStartMode)
{ {
case HotspotAutoStartMode::Never: case HotspotAutoStartMode::Never:
default: default:
return false; return false;
case HotspotAutoStartMode::NoEthernetConnection: case HotspotAutoStartMode::NoEthernetConnection:
return !ethernetConnected; return !ethernetConnected;
case HotspotAutoStartMode::NotAtHome: case HotspotAutoStartMode::NotAtHome:
return !CanSeeHomeNetwork(this->homeNetwork_, availableNetworks); return !CanSeeHomeNetwork(this->homeNetwork_, availableNetworks);
case HotspotAutoStartMode::NoRememberedWifiConections: case HotspotAutoStartMode::NoRememberedWifiConections:
return availableRememberedNetworks.size() == 0; return availableRememberedNetworks.size() == 0;
case HotspotAutoStartMode::Always: case HotspotAutoStartMode::Always:
return true; return true;
} }
} }
std::string pipedal::ssidToString(const std::vector<uint8_t> &ssid) std::string pipedal::ssidToString(const std::vector<uint8_t> &ssid)
{ {
std::stringstream s; std::stringstream s;
for (auto v: ssid) for (auto v : ssid)
{ {
if (v == 0) break; // breaks for some arguably legal ssids, but I can live with that. if (v == 0)
break; // breaks for some arguably legal ssids, but I can live with that.
s << (char)v; s << (char)v;
} }
return s.str(); return s.str();
@@ -819,7 +817,7 @@ std::vector<std::string> pipedal::ssidToStringVector(const std::vector<std::vect
{ {
std::vector<std::string> result; std::vector<std::string> result;
result.reserve(ssids.size()); result.reserve(ssids.size());
for (const std::vector<uint8_t> &ssid: ssids) for (const std::vector<uint8_t> &ssid : ssids)
{ {
result.push_back(ssidToString(ssid)); result.push_back(ssidToString(ssid));
} }
@@ -828,12 +826,34 @@ std::vector<std::string> pipedal::ssidToStringVector(const std::vector<std::vect
bool WifiConfigSettings::WantsHotspot( bool WifiConfigSettings::WantsHotspot(
bool ethernetConnected, bool ethernetConnected,
const std::vector<std::vector<uint8_t>> &availableRememberedNetworks, // remembered networks that are currently visible const std::vector<std::vector<uint8_t>> &availableRememberedNetworks, // remembered networks that are currently visible
const std::vector<std::vector<uint8_t>> &availableNetworks // all visible networks. const std::vector<std::vector<uint8_t>> &availableNetworks // all visible networks.
) )
{ {
std::vector<std::string> sAvailableRememberedNetworks = ssidToStringVector(availableRememberedNetworks); std::vector<std::string> sAvailableRememberedNetworks = ssidToStringVector(availableRememberedNetworks);
std::vector<std::string> sAvailableNetworks = ssidToStringVector(availableNetworks); std::vector<std::string> sAvailableNetworks = ssidToStringVector(availableNetworks);
return WantsHotspot(ethernetConnected,sAvailableRememberedNetworks,sAvailableNetworks); return WantsHotspot(ethernetConnected, sAvailableRememberedNetworks, sAvailableNetworks);
}
bool WifiConfigSettings::NeedsWifi() const
{
HotspotAutoStartMode autoStartMode = (HotspotAutoStartMode)this->autoStartMode_;
return autoStartMode != HotspotAutoStartMode::Never;
} }
bool WifiConfigSettings::NeedsScan() const
{
HotspotAutoStartMode autoStartMode = (HotspotAutoStartMode)this->autoStartMode_;
switch (autoStartMode)
{
case HotspotAutoStartMode::Never:
case HotspotAutoStartMode::NoEthernetConnection:
case HotspotAutoStartMode::Always:
default:
return false;
case HotspotAutoStartMode::NotAtHome:
case HotspotAutoStartMode::NoRememberedWifiConections:
return true;
}
}
@@ -26,6 +26,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <memory> #include <memory>
#include <functional>
#include "json.hpp" #include "json.hpp"
namespace pipedal namespace pipedal
@@ -331,6 +332,26 @@ namespace pipedal
virtual void RemoveAllConnections() = 0; virtual void RemoveAllConnections() = 0;
}; };
class AlsaSequencerDeviceMonitor {
protected:
AlsaSequencerDeviceMonitor() {}
public:
virtual ~AlsaSequencerDeviceMonitor() {}
using self = AlsaSequencerDeviceMonitor;
using ptr = std::shared_ptr<self>;
static ptr Create();
enum class MonitorAction {
DeviceAdded,
DeviceRemoved
};
using Callback = std::function<void(MonitorAction action, int client, const std::string& clientName)>;
virtual void StartMonitoring(Callback &&onChangeCallback) = 0;
virtual void StopMonitoring() = 0;
};
std::string RawMidiIdToSequencerId(const std::vector<AlsaSequencerPort> &seqDevices, const std::string &rawMidiId); std::string RawMidiIdToSequencerId(const std::vector<AlsaSequencerPort> &seqDevices, const std::string &rawMidiId);
} }
@@ -95,6 +95,10 @@ namespace pipedal {
bool operator==(const WifiConfigSettings&other) const; bool operator==(const WifiConfigSettings&other) const;
bool ConfigurationChanged(const WifiConfigSettings&other) const; bool ConfigurationChanged(const WifiConfigSettings&other) const;
bool WantsHotspot(bool ethernetConnected)
{
return WantsHotspot(ethernetConnected, std::vector<std::string>{}, std::vector<std::string>{});
}
bool WantsHotspot( bool WantsHotspot(
bool ethernetConnected, bool ethernetConnected,
const std::vector<std::string> &availableRememberedNetworks, // remembered networks that are currently visible const std::vector<std::string> &availableRememberedNetworks, // remembered networks that are currently visible
@@ -105,7 +109,8 @@ namespace pipedal {
const std::vector<std::vector<uint8_t>> &availableRememberedNetworks, // remembered networks that are currently visible const std::vector<std::vector<uint8_t>> &availableRememberedNetworks, // remembered networks that are currently visible
const std::vector<std::vector<uint8_t>> &availableNetworks // all visible networks. const std::vector<std::vector<uint8_t>> &availableNetworks // all visible networks.
); );
bool NeedsScan() const;
bool NeedsWifi() const;
public: public:
DECLARE_JSON_MAP(WifiConfigSettings); DECLARE_JSON_MAP(WifiConfigSettings);
}; };
+28 -1
View File
@@ -390,6 +390,7 @@ class AudioHostImpl : public AudioHost, private AudioDriverHost, private IPatchW
{ {
private: private:
AlsaSequencer::ptr alsaSequencer; AlsaSequencer::ptr alsaSequencer;
AlsaSequencerDeviceMonitor::ptr alsaDeviceMonitor;
void OnWritePatchPropertyBuffer( void OnWritePatchPropertyBuffer(
PatchPropertyWriter::Buffer *); PatchPropertyWriter::Buffer *);
@@ -1252,9 +1253,21 @@ public:
cpuTemperatureMonitor = CpuTemperatureMonitor::Get(); cpuTemperatureMonitor = CpuTemperatureMonitor::Get();
this->alsaSequencer = AlsaSequencer::Create(); this->alsaSequencer = AlsaSequencer::Create();
this->alsaDeviceMonitor = AlsaSequencerDeviceMonitor::Create();
this->alsaDeviceMonitor->StartMonitoring(
[this](
AlsaSequencerDeviceMonitor::MonitorAction action,
int client,
const std::string &clientName)
{
HandleAlsaSequencerDevicesChanged(action, client, clientName);
}
);
} }
virtual ~AudioHostImpl() virtual ~AudioHostImpl()
{ {
alsaDeviceMonitor->StopMonitoring();
Close(); Close();
CleanRestartThreads(true); CleanRestartThreads(true);
audioDriver = nullptr; audioDriver = nullptr;
@@ -1273,7 +1286,21 @@ public:
{ {
return this->sampleRate; return this->sampleRate;
} }
void HandleAlsaSequencerDevicesChanged(
AlsaSequencerDeviceMonitor::MonitorAction action, int client, const std::string &clientName)
{
if (pNotifyCallbacks)
{
if (action == AlsaSequencerDeviceMonitor::MonitorAction::DeviceRemoved)
{
pNotifyCallbacks->OnAlsaSequencerDeviceRemoved(client);
}
else if (action == AlsaSequencerDeviceMonitor::MonitorAction::DeviceAdded)
{
pNotifyCallbacks->OnAlsaSequencerDeviceAdded(client,clientName);
}
}
}
void HandleAudioTerminatedAbnormally() void HandleAudioTerminatedAbnormally()
{ {
Lv2Log::error("Audio processing terminated unexpectedly."); Lv2Log::error("Audio processing terminated unexpectedly.");
+2
View File
@@ -179,6 +179,8 @@ namespace pipedal
virtual void OnNotifyMidiRealtimeSnapshotRequest(int32_t snapshotIndex,int64_t snapshotRequestId) = 0; virtual void OnNotifyMidiRealtimeSnapshotRequest(int32_t snapshotIndex,int64_t snapshotRequestId) = 0;
virtual void OnAlsaDriverTerminatedAbnormally() = 0; virtual void OnAlsaDriverTerminatedAbnormally() = 0;
virtual void OnAlsaSequencerDeviceAdded(int client, const std::string &clientName) = 0;
virtual void OnAlsaSequencerDeviceRemoved(int client) = 0;
}; };
class JackHostStatus class JackHostStatus
+37 -18
View File
@@ -80,7 +80,7 @@ namespace pipedal::impl
bool hasWifi = false; bool hasWifi = false;
HasWifiListener hasWifiListener; HasWifiListener hasWifiListener;
void SetHasWifi(bool hasWifi); void SetHasWifi(bool hasWifi);
virtual bool GetHasWifi() override ; virtual bool GetHasWifi() override;
void onClose(); void onClose();
void onError(const std::string &message); void onError(const std::string &message);
@@ -347,7 +347,7 @@ void HotspotManagerImpl::onStartMonitoring()
this->networkManager = NetworkManager::Create(dbusDispatcher); this->networkManager = NetworkManager::Create(dbusDispatcher);
if (!networkManager->WirelessEnabled()) if (!networkManager->WirelessEnabled() && wifiConfigSettings.NeedsWifi())
{ {
networkManager->WirelessEnabled(true); networkManager->WirelessEnabled(true);
} }
@@ -483,6 +483,7 @@ void HotspotManagerImpl::onReload()
// force a reload. // force a reload.
StopHotspot(); StopHotspot();
SetState(State::Monitoring); SetState(State::Monitoring);
StartScanTimer(); // or stop it as the case may be.
MaybeStartHotspot(); MaybeStartHotspot();
return; return;
} }
@@ -758,15 +759,25 @@ void HotspotManagerImpl::MaybeStartHotspot()
// devices are transitioning. Do nothing. // devices are transitioning. Do nothing.
return; return;
} }
std::vector<AccessPoint::ptr> allAccessPoints = GetAllAccessPoints();
std::vector<std::vector<uint8_t>> allAccessPointSsids = GetAccessPointSsids(allAccessPoints);
std::vector<std::vector<uint8_t>> connectableSsids = GetKnownVisibleAccessPoints(allAccessPointSsids); // all the ssids currently visible for which we have credentials.
this->UpdateKnownNetworks(connectableSsids, allAccessPoints); bool wantsHotspot;
bool wantsHotspot = this->wifiConfigSettings.WantsHotspot( if (wifiConfigSettings.NeedsScan()) {
this->ethernetConnected, connectableSsids, allAccessPointSsids); std::vector<AccessPoint::ptr> allAccessPoints = GetAllAccessPoints();
std::vector<std::vector<uint8_t>> allAccessPointSsids = GetAccessPointSsids(allAccessPoints);
std::vector<std::vector<uint8_t>> connectableSsids = GetKnownVisibleAccessPoints(allAccessPointSsids); // all the ssids currently visible for which we have credentials.
this->UpdateKnownNetworks(connectableSsids, allAccessPoints);
wantsHotspot = this->wifiConfigSettings.WantsHotspot(
this->ethernetConnected, connectableSsids, allAccessPointSsids);
} else {
wantsHotspot = this->wifiConfigSettings.WantsHotspot(
this->ethernetConnected);
}
if (this->state == State::Monitoring && wantsHotspot) if (this->state == State::Monitoring && wantsHotspot)
{ {
@@ -996,8 +1007,14 @@ void HotspotManagerImpl::ScanNow()
try try
{ {
Lv2Log::debug("Scanning"); if (this->wifiConfigSettings.NeedsScan())
wlanWirelessDevice->RequestScan(options); {
Lv2Log::debug("Scanning");
wlanWirelessDevice->RequestScan(options);
} else {
return;
}
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
@@ -1036,7 +1053,8 @@ void HotspotManagerImpl::SetHasWifiListener(HasWifiListener &&listener)
this->hasWifiListener(this->hasWifi); this->hasWifiListener(this->hasWifi);
} }
} }
bool HotspotManagerImpl::GetHasWifi() { bool HotspotManagerImpl::GetHasWifi()
{
std::lock_guard<std::recursive_mutex> lock{this->networkChangingListenerMutex}; std::lock_guard<std::recursive_mutex> lock{this->networkChangingListenerMutex};
return hasWifi; return hasWifi;
} }
@@ -1112,17 +1130,20 @@ static bool IsNetworkManagerRunning()
return gUsingNetworkManager; return gUsingNetworkManager;
} }
bool bResult = false; bool bResult = false;
auto result = sysExecForOutput("systemctl","is-active NetworkManager"); auto result = sysExecForOutput("systemctl", "is-active NetworkManager");
if (result.exitCode == EXIT_SUCCESS) if (result.exitCode == EXIT_SUCCESS)
{ {
std::string text = result.output.erase(result.output.find_last_not_of(" \n\r\t")+1); std::string text = result.output.erase(result.output.find_last_not_of(" \n\r\t") + 1);
if (text == "active") if (text == "active")
{ {
bResult = true; bResult = true;
} else if (text == "inactive") }
else if (text == "inactive")
{ {
bResult = false; bResult = false;
} else { }
else
{
throw std::runtime_error(SS("UsingNetworkManager: unexpected result (" << text << ")")); throw std::runtime_error(SS("UsingNetworkManager: unexpected result (" << text << ")"));
} }
gNetworkManagerTestExecuted = true; gNetworkManagerTestExecuted = true;
@@ -1134,11 +1155,10 @@ static bool IsNetworkManagerRunning()
return false; return false;
} }
bool HotspotManager::HasWifiDevice() bool HotspotManager::HasWifiDevice()
{ {
// use procfs to decide this, as NetworkManager may not be available yet. // use procfs to decide this, as NetworkManager may not be available yet.
if ( !(get_wireless_interfaces_sysfs().empty()) ) if (!(get_wireless_interfaces_sysfs().empty()))
{ {
return false; return false;
} }
@@ -1148,5 +1168,4 @@ bool HotspotManager::HasWifiDevice()
return false; return false;
} }
return true; return true;
} }
+91 -68
View File
@@ -292,16 +292,12 @@ void PiPedalModel::Load()
this->audioHost->SetNotificationCallbacks(this); this->audioHost->SetNotificationCallbacks(this);
this->systemMidiBindings = storage.GetSystemMidiBindings(); this->systemMidiBindings = storage.GetSystemMidiBindings();
this->audioHost->SetSystemMidiBindings(this->systemMidiBindings); this->audioHost->SetSystemMidiBindings(this->systemMidiBindings);
audioHost->SetAlsaSequencerConfiguration(storage.GetAlsaSequencerConfiguration()); audioHost->SetAlsaSequencerConfiguration(storage.GetAlsaSequencerConfiguration());
if (configuration.GetMLock()) if (configuration.GetMLock())
{ {
#ifndef NO_MLOCK #ifndef NO_MLOCK
@@ -515,7 +511,6 @@ void PiPedalModel::FireBanksChanged(int64_t clientId)
} }
} }
void PiPedalModel::FirePedalboardChanged(int64_t clientId, bool loadAudioThread) void PiPedalModel::FirePedalboardChanged(int64_t clientId, bool loadAudioThread)
{ {
if (loadAudioThread) if (loadAudioThread)
@@ -601,12 +596,9 @@ void PiPedalModel::UpdateCurrentPedalboard(int64_t clientId, Pedalboard &pedalbo
UpdateVst3Settings(pedalboard); UpdateVst3Settings(pedalboard);
Lv2PedalboardErrorList errorMessages; Lv2PedalboardErrorList errorMessages;
std::shared_ptr<Lv2Pedalboard> lv2Pedalboard{ std::shared_ptr<Lv2Pedalboard> lv2Pedalboard{
this->pluginHost.UpdateLv2PedalboardStructure(pedalboard, this->lv2Pedalboard.get(), errorMessages) this->pluginHost.UpdateLv2PedalboardStructure(pedalboard, this->lv2Pedalboard.get(), errorMessages)};
};
this->lv2Pedalboard = lv2Pedalboard; this->lv2Pedalboard = lv2Pedalboard;
// apply the error messages to the lv2Pedalboard. // apply the error messages to the lv2Pedalboard.
@@ -620,9 +612,7 @@ void PiPedalModel::UpdateCurrentPedalboard(int64_t clientId, Pedalboard &pedalbo
UpdateRealtimeVuSubscriptions(); UpdateRealtimeVuSubscriptions();
UpdateRealtimeMonitorPortSubscriptions(); UpdateRealtimeMonitorPortSubscriptions();
this->FirePedalboardChanged(clientId, false);
this->FirePedalboardChanged(clientId,false);
this->SetPresetChanged(clientId, true); this->SetPresetChanged(clientId, true);
} }
} }
@@ -1406,6 +1396,38 @@ void PiPedalModel::RestartAudio(bool useDummyAudioDriver)
} }
} }
void PiPedalModel::OnAlsaSequencerDeviceAdded(int client, const std::string &clientName)
{
std::lock_guard<std::recursive_mutex> lock(mutex);
auto alsaSequencerConfiguration = this->storage.GetAlsaSequencerConfiguration();
std::string key = "seq:" + clientName;
bool interested = false;
for (const auto &port : alsaSequencerConfiguration.connections())
{
if (port.id().starts_with(key))
{
interested = true;
break;
}
}
if (interested)
{
Post(
[this]
{
// reconfigure connections.
std::lock_guard<std::recursive_mutex> lock(this->mutex);
if (this->audioHost) {
this->audioHost->SetAlsaSequencerConfiguration(this->storage.GetAlsaSequencerConfiguration());
}
});
}
}
void PiPedalModel::OnAlsaSequencerDeviceRemoved(int client)
{
// no action required.
}
void PiPedalModel::SetAlsaSequencerConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration) void PiPedalModel::SetAlsaSequencerConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex); std::lock_guard<std::recursive_mutex> lock(mutex);
@@ -1424,7 +1446,6 @@ void PiPedalModel::SetAlsaSequencerConfiguration(const AlsaSequencerConfiguratio
subscriber->OnAlsaSequencerConfigurationChanged(alsaSequencerConfiguration); subscriber->OnAlsaSequencerConfigurationChanged(alsaSequencerConfiguration);
} }
} }
} }
AlsaSequencerConfiguration PiPedalModel::GetAlsaSequencerConfiguration() AlsaSequencerConfiguration PiPedalModel::GetAlsaSequencerConfiguration()
{ {
@@ -1441,14 +1462,11 @@ std::vector<AlsaSequencerPortSelection> PiPedalModel::GetAlsaSequencerPorts()
result.push_back(AlsaSequencerPortSelection{ result.push_back(AlsaSequencerPortSelection{
port.id, port.id,
port.name, port.name,
port.displaySortOrder port.displaySortOrder});
});
} }
return result; return result;
} }
void PiPedalModel::SetJackChannelSelection(int64_t clientId, const JackChannelSelection &channelSelection) void PiPedalModel::SetJackChannelSelection(int64_t clientId, const JackChannelSelection &channelSelection)
{ {
{ {
@@ -1678,7 +1696,8 @@ void PiPedalModel::SendSetPatchProperty(
std::shared_ptr<Lv2PluginInfo> pluginInfo = GetPluginInfo(pedalboardItem->uri_); std::shared_ptr<Lv2PluginInfo> pluginInfo = GetPluginInfo(pedalboardItem->uri_);
auto pipedalUi = pluginInfo->piPedalUI(); auto pipedalUi = pluginInfo->piPedalUI();
auto fileProperty = pipedalUi->GetFileProperty(propertyUri); auto fileProperty = pipedalUi->GetFileProperty(propertyUri);
if (fileProperty && value.is_string()) { if (fileProperty && value.is_string())
{
json_variant abstractPath = pluginHost.AbstractPath(value); json_variant abstractPath = pluginHost.AbstractPath(value);
std::ostringstream ss; std::ostringstream ss;
@@ -1729,12 +1748,11 @@ void PiPedalModel::SendSetPatchProperty(
}}; }};
LV2_URID urid = this->pluginHost.GetLv2Urid(propertyUri.c_str()); LV2_URID urid = this->pluginHost.GetLv2Urid(propertyUri.c_str());
size_t sampleTimeout = 0.5*audioHost->GetSampleRate(); size_t sampleTimeout = 0.5 * audioHost->GetSampleRate();
RealtimePatchPropertyRequest *request = new RealtimePatchPropertyRequest( RealtimePatchPropertyRequest *request = new RealtimePatchPropertyRequest(
onRequestComplete, onRequestComplete,
clientId, instanceId, urid, atomValue, nullptr, onError, clientId, instanceId, urid, atomValue, nullptr, onError,
sampleTimeout sampleTimeout);
);
outstandingParameterRequests.push_back(request); outstandingParameterRequests.push_back(request);
if (this->audioHost) if (this->audioHost)
@@ -1802,10 +1820,10 @@ void PiPedalModel::SendGetPatchProperty(
{ {
onError("Audio stopped."); onError("Audio stopped.");
} }
size_t sampleTimeout = 0.3*audioHost->GetSampleRate(); size_t sampleTimeout = 0.3 * audioHost->GetSampleRate();
RealtimePatchPropertyRequest *request = new RealtimePatchPropertyRequest( RealtimePatchPropertyRequest *request = new RealtimePatchPropertyRequest(
onRequestComplete, onRequestComplete,
clientId, instanceId, urid, onSuccess, onError,sampleTimeout); clientId, instanceId, urid, onSuccess, onError, sampleTimeout);
outstandingParameterRequests.push_back(request); outstandingParameterRequests.push_back(request);
this->audioHost->sendRealtimeParameterRequest(request); this->audioHost->sendRealtimeParameterRequest(request);
@@ -1969,7 +1987,9 @@ void PiPedalModel::UpdateDefaults(PedalboardItem *pedalboardItem, std::unordered
{ {
pedalboardItem->midiChannelBinding(MidiChannelBinding::DefaultForMissingValue()); pedalboardItem->midiChannelBinding(MidiChannelBinding::DefaultForMissingValue());
} }
} else { }
else
{
if (pedalboardItem->midiChannelBinding()) if (pedalboardItem->midiChannelBinding())
{ {
pedalboardItem->midiChannelBinding(std::optional<MidiChannelBinding>()); // clear it. pedalboardItem->midiChannelBinding(std::optional<MidiChannelBinding>()); // clear it.
@@ -2245,22 +2265,23 @@ void PiPedalModel::OnNotifyMidiListen(uint8_t cc0, uint8_t cc1, uint8_t cc2)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex); std::lock_guard<std::recursive_mutex> lock(mutex);
bool isNote = (cc0 & 0xF0) == 0x90; // Note On bool isNote = (cc0 & 0xF0) == 0x90; // Note On
if (isNote && cc2 == 0) { if (isNote && cc2 == 0)
{
return; // Note off. Oopsie. return; // Note off. Oopsie.
} }
bool isControl = (cc0 & 0xF0) == 0xB0; // Control Change bool isControl = (cc0 & 0xF0) == 0xB0; // Control Change
if (!isNote && !isControl) { if (!isNote && !isControl)
{
return; // Not a note on or control change. return; // Not a note on or control change.
} }
for (int i = 0; i < midiEventListeners.size(); ++i) for (int i = 0; i < midiEventListeners.size(); ++i)
{ {
auto &listener = midiEventListeners[i]; auto &listener = midiEventListeners[i];
auto subscriber = this->GetNotificationSubscriber(listener.clientId); auto subscriber = this->GetNotificationSubscriber(listener.clientId);
if (subscriber) if (subscriber)
{ {
subscriber->OnNotifyMidiListener(listener.clientHandle, cc0,cc1,cc2); subscriber->OnNotifyMidiListener(listener.clientHandle, cc0, cc1, cc2);
} }
else else
{ {
@@ -2345,6 +2366,7 @@ void PiPedalModel::CancelMonitorPatchProperty(int64_t clientId, int64_t clientHa
audioHost->SetListenForMidiEvent(false); audioHost->SetListenForMidiEvent(false);
} }
} }
std::vector<AlsaDeviceInfo> PiPedalModel::GetAlsaDevices() std::vector<AlsaDeviceInfo> PiPedalModel::GetAlsaDevices()
{ {
std::vector<AlsaDeviceInfo> result = this->alsaDevices.GetAlsaDevices(); std::vector<AlsaDeviceInfo> result = this->alsaDevices.GetAlsaDevices();
@@ -2402,9 +2424,9 @@ void PiPedalModel::SetSystemMidiBindings(std::vector<MidiBinding> &bindings)
} }
} }
PedalboardItem*PiPedalModel::GetPedalboardItemForFileProperty(const UiFileProperty& fileProperty) PedalboardItem *PiPedalModel::GetPedalboardItemForFileProperty(const UiFileProperty &fileProperty)
{ {
for (PedalboardItem*pedalboardItem: this->pedalboard.GetAllPlugins()) for (PedalboardItem *pedalboardItem : this->pedalboard.GetAllPlugins())
{ {
if (pedalboardItem->pathProperties_.contains(fileProperty.patchProperty())) if (pedalboardItem->pathProperties_.contains(fileProperty.patchProperty()))
{ {
@@ -2421,7 +2443,6 @@ FileRequestResult PiPedalModel::GetFileList2(const std::string &relativePath_, c
if (!storage.IsInUploadsDirectory(relativePath)) if (!storage.IsInUploadsDirectory(relativePath))
{ {
// if relativePath is in a resource directory of the plugin, then we have loaded a factory preset or are using a default property. // if relativePath is in a resource directory of the plugin, then we have loaded a factory preset or are using a default property.
// map the resource path to the corresponding file in the uploads directory. // map the resource path to the corresponding file in the uploads directory.
// :-( // :-(
@@ -2429,13 +2450,13 @@ FileRequestResult PiPedalModel::GetFileList2(const std::string &relativePath_, c
if (pedalboardItem) if (pedalboardItem)
{ {
auto pluginInfo = GetPluginInfo(pedalboardItem->uri()); auto pluginInfo = GetPluginInfo(pedalboardItem->uri());
if (pluginInfo) { if (pluginInfo)
std::filesystem::path resourcePath = fs::path(pluginInfo->bundle_path()) {
/ fileProperty.resourceDirectory(); std::filesystem::path resourcePath = fs::path(pluginInfo->bundle_path()) / fileProperty.resourceDirectory();
if (IsSubdirectory(relativePath,resourcePath)) if (IsSubdirectory(relativePath, resourcePath))
{ {
fs::path t = MakeRelativePath(relativePath,resourcePath); fs::path t = MakeRelativePath(relativePath, resourcePath);
t = fileProperty.directory() / t; t = fileProperty.directory() / t;
if (fs::exists(t)) if (fs::exists(t))
{ {
relativePath = t; relativePath = t;
@@ -2469,10 +2490,9 @@ std::string PiPedalModel::CopyFilePropertyFile(
bool overwrite) bool overwrite)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex); std::lock_guard<std::recursive_mutex> lock(mutex);
return storage.CopyFilePropertyFile(oldRelativePath, newRelativePath, uiFileProperty,overwrite); return storage.CopyFilePropertyFile(oldRelativePath, newRelativePath, uiFileProperty, overwrite);
} }
void PiPedalModel::DeleteSampleFile(const std::filesystem::path &fileName) void PiPedalModel::DeleteSampleFile(const std::filesystem::path &fileName)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex); std::lock_guard<std::recursive_mutex> lock(mutex);
@@ -2484,28 +2504,29 @@ std::string PiPedalModel::CreateNewSampleDirectory(const std::string &relativePa
std::lock_guard<std::recursive_mutex> lock(mutex); std::lock_guard<std::recursive_mutex> lock(mutex);
return storage.CreateNewSampleDirectory(relativePath, uiFileProperty); return storage.CreateNewSampleDirectory(relativePath, uiFileProperty);
} }
FilePropertyDirectoryTree::ptr PiPedalModel::GetFilePropertydirectoryTree(const UiFileProperty &uiFileProperty,const std::string&selectedPath) FilePropertyDirectoryTree::ptr PiPedalModel::GetFilePropertydirectoryTree(const UiFileProperty &uiFileProperty, const std::string &selectedPath)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex); std::lock_guard<std::recursive_mutex> lock(mutex);
return storage.GetFilePropertydirectoryTree(uiFileProperty,selectedPath); return storage.GetFilePropertydirectoryTree(uiFileProperty, selectedPath);
} }
UiFileProperty::ptr PiPedalModel::FindLoadedPatchProperty(int64_t instanceId,const std::string&patchPropertyUri) UiFileProperty::ptr PiPedalModel::FindLoadedPatchProperty(int64_t instanceId, const std::string &patchPropertyUri)
{ {
auto pedalboardItems = pedalboard.GetAllPlugins(); auto pedalboardItems = pedalboard.GetAllPlugins();
for (const auto&pedalboardItem: pedalboardItems) { for (const auto &pedalboardItem : pedalboardItems)
{
if (pedalboardItem->instanceId() == instanceId) if (pedalboardItem->instanceId() == instanceId)
{ {
Lv2PluginInfo::ptr pluginInfo = GetPluginInfo(pedalboardItem->uri()); Lv2PluginInfo::ptr pluginInfo = GetPluginInfo(pedalboardItem->uri());
if (pluginInfo && pluginInfo->piPedalUI()) if (pluginInfo && pluginInfo->piPedalUI())
{ {
for (const auto&fileProperty: pluginInfo->piPedalUI()->fileProperties()) for (const auto &fileProperty : pluginInfo->piPedalUI()->fileProperties())
if (fileProperty->patchProperty() == patchPropertyUri) if (fileProperty->patchProperty() == patchPropertyUri)
{ {
return fileProperty; return fileProperty;
} }
} }
} }
} }
@@ -2513,9 +2534,9 @@ UiFileProperty::ptr PiPedalModel::FindLoadedPatchProperty(int64_t instanceId,con
throw std::runtime_error("Permission denied. Plugin not currently loaded."); throw std::runtime_error("Permission denied. Plugin not currently loaded.");
} }
std::string PiPedalModel::UploadUserFile(const std::string &directory, int64_t instanceId,const std::string &patchProperty, const std::string &filename, std::istream &stream, size_t contentLength) std::string PiPedalModel::UploadUserFile(const std::string &directory, int64_t instanceId, const std::string &patchProperty, const std::string &filename, std::istream &stream, size_t contentLength)
{ {
UiFileProperty::ptr fileProperty = FindLoadedPatchProperty(instanceId,patchProperty); UiFileProperty::ptr fileProperty = FindLoadedPatchProperty(instanceId, patchProperty);
if (!fileProperty) if (!fileProperty)
{ {
Lv2Log::error(SS("Upload fle: Permission denied. No currently-loaded plugin provides that patch property: " << patchProperty)); Lv2Log::error(SS("Upload fle: Permission denied. No currently-loaded plugin provides that patch property: " << patchProperty));
@@ -2905,20 +2926,23 @@ bool PiPedalModel::GetHasWifi()
return hasWifi; return hasWifi;
} }
std::map<std::string,std::string> PiPedalModel::GetWifiRegulatoryDomains() std::map<std::string, std::string> PiPedalModel::GetWifiRegulatoryDomains()
{ {
std::map<std::string,std::string> result; std::map<std::string, std::string> result;
try { try
auto& regDb = RegDb::GetInstance(); {
auto &regDb = RegDb::GetInstance();
result = regDb.getRegulatoryDomains(storage.GetConfigRoot() / "iso_codes.json"); result = regDb.getRegulatoryDomains(storage.GetConfigRoot() / "iso_codes.json");
} catch (const std::exception&e) }
catch (const std::exception &e)
{ {
Lv2Log::warning(SS("Unable to query Wifi Regulatory domains. " << e.what())); Lv2Log::warning(SS("Unable to query Wifi Regulatory domains. " << e.what()));
} }
return result; return result;
} }
void PiPedalModel::CancelAudioRetry() { void PiPedalModel::CancelAudioRetry()
{
std::lock_guard<std::recursive_mutex> lock(mutex); std::lock_guard<std::recursive_mutex> lock(mutex);
if (audioRetryPostHandle) if (audioRetryPostHandle)
{ {
@@ -2926,14 +2950,16 @@ void PiPedalModel::CancelAudioRetry() {
this->CancelPost(audioRetryPostHandle); this->CancelPost(audioRetryPostHandle);
audioRetryPostHandle = 0; audioRetryPostHandle = 0;
} }
} }
void PiPedalModel::OnAlsaDriverTerminatedAbnormally() {
void PiPedalModel::OnAlsaDriverTerminatedAbnormally()
{
// notification from the realtime thread, via the audiohost that the // notification from the realtime thread, via the audiohost that the
// ALSA stream has broken. We want to restart. // ALSA stream has broken. We want to restart.
// get off the service thread as promptly as possible // get off the service thread as promptly as possible
this->Post([&]() { this->Post([&]()
{
std::lock_guard<std::recursive_mutex> lock(mutex); std::lock_guard<std::recursive_mutex> lock(mutex);
if (closed) return; if (closed) return;
@@ -2981,8 +3007,7 @@ void PiPedalModel::OnAlsaDriverTerminatedAbnormally() {
} else { } else {
Lv2Log::error(SS("Unable to reastart audio.")); Lv2Log::error(SS("Unable to reastart audio."));
} } });
});
} }
bool PiPedalModel::IsInUploadsDirectory(const std::string &path) bool PiPedalModel::IsInUploadsDirectory(const std::string &path)
@@ -2993,8 +3018,8 @@ bool PiPedalModel::IsInUploadsDirectory(const std::string &path)
void PiPedalModel::MoveAudioFile( void PiPedalModel::MoveAudioFile(
const std::string &directory, const std::string &directory,
int32_t fromPosition, int32_t fromPosition,
int32_t toPosition int32_t toPosition)
) { {
if (directory.empty()) if (directory.empty())
{ {
throw std::runtime_error("Directory is empty."); throw std::runtime_error("Directory is empty.");
@@ -3005,13 +3030,11 @@ void PiPedalModel::MoveAudioFile(
void PiPedalModel::SetPedalboardItemTitle(int64_t instanceId, const std::string &title) void PiPedalModel::SetPedalboardItemTitle(int64_t instanceId, const std::string &title)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex); std::lock_guard<std::recursive_mutex> lock(mutex);
if (!this->pedalboard.SetItemTitle(instanceId,title)) if (!this->pedalboard.SetItemTitle(instanceId, title))
{ {
return; return;
} }
// no need to reload the pedalboard, but we do need to notify subscribers. // no need to reload the pedalboard, but we do need to notify subscribers.
this->SetPresetChanged(-1, true); this->SetPresetChanged(-1, true);
this->FirePedalboardChanged(-1,false); this->FirePedalboardChanged(-1, false);
} }
+2
View File
@@ -240,6 +240,8 @@ namespace pipedal
virtual void OnNotifyMidiRealtimeEvent(RealtimeMidiEventType eventType) override; virtual void OnNotifyMidiRealtimeEvent(RealtimeMidiEventType eventType) override;
virtual void OnNotifyMidiRealtimeSnapshotRequest(int32_t snapshotIndex,int64_t snapshotRequestId) override; virtual void OnNotifyMidiRealtimeSnapshotRequest(int32_t snapshotIndex,int64_t snapshotRequestId) override;
virtual void OnAlsaDriverTerminatedAbnormally() override; virtual void OnAlsaDriverTerminatedAbnormally() override;
virtual void OnAlsaSequencerDeviceAdded(int client, const std::string &clientName) override;
virtual void OnAlsaSequencerDeviceRemoved(int client) override;
void OnNotifyPathPatchPropertyReceived( void OnNotifyPathPatchPropertyReceived(
int64_t instanceId, int64_t instanceId,
+1 -3
View File
@@ -1,8 +1,6 @@
VU decay on a timer. Test autohotspot detection.
Midi handling in the dummy audio connector.
PiPedal sequencer not visible. Is it missing duplex somewhere?
check filetime conversion in gcc 12.2! (missing c++ 20 time conversion functions) check filetime conversion in gcc 12.2! (missing c++ 20 time conversion functions)
AudioFiles.cpp fileTimeToInt64 AudioFiles.cpp fileTimeToInt64
@@ -54,19 +54,23 @@ function SelectMidiChannelsDialog(props: SelectMidiChannelsDialogProps) {
const [allPorts, setAllPorts] = useState<DialogItem[] | null>(null); const [allPorts, setAllPorts] = useState<DialogItem[] | null>(null);
const [model] = useState<PiPedalModel>(PiPedalModelFactory.getInstance()); const [model] = useState<PiPedalModel>(PiPedalModelFactory.getInstance());
const [changed, setChanged] = useState<boolean>(false); const [changed, setChanged] = useState<boolean>(false);
const [ readyToDisplay, setReadyToDisplay ] = useState<boolean>(false);
React.useEffect(() => { React.useEffect(() => {
if (open) { if (open) {
setReadyToDisplay(false);
model.getAlsaSequencerPorts().then((ports) => { model.getAlsaSequencerPorts().then((ports) => {
setAvailablePorts(ports); setAvailablePorts(ports);
}).catch((error) => { }).catch((error) => {
model.showAlert(error); model.showAlert(error);
setReadyToDisplay(true);
setAvailablePorts(null); setAvailablePorts(null);
}); });
model.getAlsaSequencerConfiguration().then((config) => { model.getAlsaSequencerConfiguration().then((config) => {
setConfiguration(config); setConfiguration(config);
}).catch((error) => { }).catch((error) => {
model.showAlert(error); model.showAlert(error);
setReadyToDisplay(true);
setConfiguration(null); setConfiguration(null);
}); });
return () => { return () => {
@@ -78,7 +82,7 @@ function SelectMidiChannelsDialog(props: SelectMidiChannelsDialogProps) {
React.useEffect(() => { React.useEffect(() => {
if (availablePorts !== null && configuration !== null) { if (availablePorts !== null && configuration !== null) {
let result: DialogItem[] = []; let result: DialogItem[] = [];
setReadyToDisplay(true);
for (let port of availablePorts) { for (let port of availablePorts) {
result.push({ result.push({
id: port.id, id: port.id,
@@ -159,7 +163,8 @@ function SelectMidiChannelsDialog(props: SelectMidiChannelsDialogProps) {
return ( return (
<DialogEx tag="midiChannels" onClose={handleClose} aria-labelledby="select-midi-inputs" open={open} <DialogEx tag="midiChannels" onClose={handleClose} aria-labelledby="select-midi-inputs"
open={open && readyToDisplay}
fullWidth maxWidth="xs" fullWidth maxWidth="xs"
onEnterKey={handleClose} onEnterKey={handleClose}
> >