refactor service.conf
This commit is contained in:
+5
-5
@@ -210,13 +210,13 @@ bool setJackConfiguration(JackServerSettings serverSettings)
|
||||
|
||||
silentSysExec("/usr/bin/systemctl unmask jack");
|
||||
silentSysExec("/usr/bin/systemctl enable jack");
|
||||
#else
|
||||
// otherwise,
|
||||
// the config was written before invoking admin main. but we still need to restart the service.
|
||||
#endif
|
||||
|
||||
std::thread delayedRestartThread(delayedRestartProc);
|
||||
delayedRestartThread.detach();
|
||||
#else
|
||||
// otherwise,
|
||||
// the config was written before invoking admin main and we don't need to to restart the service.
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+30
-7
@@ -268,6 +268,15 @@ namespace pipedal
|
||||
snd_pcm_sw_params_free(playbackSwParams);
|
||||
playbackSwParams = nullptr;
|
||||
}
|
||||
for (auto *midiState: this->midiStates)
|
||||
{
|
||||
if (midiState != nullptr)
|
||||
{
|
||||
midiState->Close();
|
||||
delete midiState;
|
||||
}
|
||||
}
|
||||
midiStates.resize(0);
|
||||
}
|
||||
|
||||
std::string discover_alsa_using_apps()
|
||||
@@ -573,9 +582,14 @@ namespace pipedal
|
||||
this->channelSelection = channelSelection;
|
||||
|
||||
open = true;
|
||||
|
||||
OpenMidi(jackServerSettings, channelSelection);
|
||||
OpenAudio(jackServerSettings, channelSelection);
|
||||
try {
|
||||
OpenMidi(jackServerSettings, channelSelection);
|
||||
OpenAudio(jackServerSettings, channelSelection);
|
||||
} catch (const std::exception&e)
|
||||
{
|
||||
Close();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenAudio(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
|
||||
@@ -1512,7 +1526,7 @@ namespace pipedal
|
||||
try
|
||||
{
|
||||
int err;
|
||||
for (int retry = 0; retry < 5; ++retry)
|
||||
for (int retry = 0; retry < 15; ++retry)
|
||||
{
|
||||
err = snd_pcm_open(&playbackHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
||||
if (err == -EBUSY)
|
||||
@@ -1524,13 +1538,22 @@ namespace pipedal
|
||||
}
|
||||
if (err < 0)
|
||||
{
|
||||
throw PiPedalStateException(SS(alsaDeviceName << " not found. "
|
||||
throw PiPedalStateException(SS(alsaDeviceName << " playback device not found. "
|
||||
<< "(" << snd_strerror(err) << ")"));
|
||||
}
|
||||
|
||||
err = snd_pcm_open(&captureHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
|
||||
for (int retry = 0; retry < 15; ++retry)
|
||||
{
|
||||
err = snd_pcm_open(&captureHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
|
||||
if (err == -EBUSY)
|
||||
{
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (err < 0)
|
||||
throw PiPedalStateException(SS(alsaDeviceName << " not found."));
|
||||
throw PiPedalStateException(SS(alsaDeviceName << " capture device not found."));
|
||||
|
||||
if (snd_pcm_hw_params_malloc(&playbackHwParams) < 0)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,217 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
// avoid breaking changes from 1.64 to.71
|
||||
// Note that raspbian only prvides 1.64; ubuntu studio only provides 1.71
|
||||
#define BOOST_BEAST_ALLOW_DEPRECATED
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/beast/http.hpp>
|
||||
#include <boost/asio/ip/network_v4.hpp>
|
||||
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include "Uri.hpp"
|
||||
#include <string_view>
|
||||
#include <filesystem>
|
||||
|
||||
|
||||
// forward declaration.
|
||||
|
||||
class websocket_session;
|
||||
|
||||
namespace pipedal {
|
||||
using HttpRequest =boost::beast::http::request<boost::beast::http::string_body>;
|
||||
|
||||
|
||||
std::string last_modified(const std::filesystem::path& path);
|
||||
|
||||
class SocketHandler {
|
||||
public:
|
||||
|
||||
class IWriteCallback {
|
||||
public:
|
||||
virtual void close() = 0;
|
||||
|
||||
virtual void writeCallback(const std::string& text) = 0;
|
||||
virtual std::string getFromAddress() const = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
friend class ::websocket_session;
|
||||
|
||||
|
||||
IWriteCallback *writeCallback_;
|
||||
|
||||
void setWriteCallback(IWriteCallback *writeCallback) {
|
||||
writeCallback_ = writeCallback;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void onReceive(const std::string_view&text) = 0;
|
||||
public:
|
||||
std::string getFromAddress() const { return writeCallback_->getFromAddress(); }
|
||||
void receive(const std::string_view&text) {
|
||||
onReceive(text);
|
||||
}
|
||||
void send(const std::string &text) {
|
||||
if (writeCallback_ != nullptr)
|
||||
{
|
||||
writeCallback_->writeCallback(text);
|
||||
}
|
||||
}
|
||||
virtual void Close()
|
||||
{
|
||||
writeCallback_->close();
|
||||
}
|
||||
|
||||
virtual ~SocketHandler() = default;
|
||||
|
||||
virtual void onAttach() { }
|
||||
|
||||
};
|
||||
|
||||
class ISocketFactory {
|
||||
public:
|
||||
virtual bool wants(const uri &request) = 0;
|
||||
virtual std::shared_ptr<SocketHandler> CreateHandler(const uri& request) = 0;
|
||||
};
|
||||
|
||||
class RequestHandler {
|
||||
private:
|
||||
uri target_url_;
|
||||
public:
|
||||
RequestHandler(const char*target_url)
|
||||
: target_url_(target_url)
|
||||
{
|
||||
|
||||
}
|
||||
~RequestHandler() = default;
|
||||
|
||||
virtual bool wantsRedirect(const uri& requestUri)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uri GetRedirect(const uri& requestUri)
|
||||
{
|
||||
return requestUri;
|
||||
}
|
||||
|
||||
virtual bool wants(boost::beast::http::verb verb,const uri &request_uri) const {
|
||||
if (request_uri.segment_count() < target_url_.segment_count())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < target_url_.segment_count(); ++i)
|
||||
{
|
||||
if (request_uri.segment(i) != target_url_.segment(i))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void head_response(
|
||||
const uri&request_uri,
|
||||
const boost::beast::http::request<boost::beast::http::string_body> &req,
|
||||
boost::beast::http::response<boost::beast::http::empty_body> &res,
|
||||
boost::beast::error_code &ec) = 0;
|
||||
|
||||
virtual void get_response(
|
||||
const uri&request_uri,
|
||||
const boost::beast::http::request<boost::beast::http::string_body> &req,
|
||||
boost::beast::http::response<boost::beast::http::string_body> &res,
|
||||
boost::beast::error_code &ec) = 0;
|
||||
|
||||
virtual void post_response(
|
||||
const uri&request_uri,
|
||||
const boost::beast::http::request<boost::beast::http::string_body> &req,
|
||||
boost::beast::http::response<boost::beast::http::string_body> &res,
|
||||
boost::beast::error_code &ec)
|
||||
{
|
||||
get_response(request_uri,req,res,ec);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
class BeastServer {
|
||||
private:
|
||||
boost::asio::ip::address address;
|
||||
unsigned short port;
|
||||
std::string doc_root;
|
||||
int threads;
|
||||
std::mutex log_mutex;
|
||||
std::vector<std::shared_ptr<RequestHandler> > request_handlers;
|
||||
std::vector<std::shared_ptr<ISocketFactory> > socket_factories;
|
||||
std::mutex io_mutex;
|
||||
boost::asio::io_context *pIoContext = nullptr;
|
||||
void*pListener = nullptr;
|
||||
std::thread*pBgThread = nullptr;
|
||||
|
||||
bool logHttpRequests = false;
|
||||
|
||||
bool hasAccessPointGateway = false;
|
||||
boost::asio::ip::network_v4 accessPointGateway;
|
||||
std::string accessPointServerAddress;
|
||||
bool stopping = false;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
BeastServer(
|
||||
const boost::asio::ip::address& address,
|
||||
int port,
|
||||
const char*rootPath,
|
||||
int threads = 10);
|
||||
|
||||
void Run();
|
||||
void RunInBackground();
|
||||
bool Stopping() const { return this->stopping; }
|
||||
void Stop();
|
||||
void Join();
|
||||
|
||||
void SetLogHttpRequests(bool logRequests) { this->logHttpRequests = logRequests; }
|
||||
bool LogHttpRequests() const { return this->logHttpRequests; }
|
||||
boost::asio::io_context& IoContext() {
|
||||
return *pIoContext;
|
||||
}
|
||||
|
||||
void AddRequestHandler(std::shared_ptr<RequestHandler> requestHandler)
|
||||
{
|
||||
request_handlers.push_back(requestHandler);
|
||||
}
|
||||
|
||||
void AddSocketFactory(const std::shared_ptr<ISocketFactory> &socketHandler)
|
||||
{
|
||||
socket_factories.push_back(socketHandler);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<RequestHandler> > &RequestHandlers() { return request_handlers; }
|
||||
|
||||
const std::string&GetDocRoot() { return doc_root; }
|
||||
|
||||
virtual ~BeastServer();
|
||||
|
||||
std::shared_ptr<ISocketFactory> GetSocketFactory(const uri &request_uri);
|
||||
|
||||
void SetAccessPointGateway(const boost::asio::ip::network_v4 &gateway, const std::string&accessPointServerAddress)
|
||||
{
|
||||
this->accessPointGateway = gateway;
|
||||
this->hasAccessPointGateway = true;
|
||||
this->accessPointServerAddress = accessPointServerAddress;
|
||||
}
|
||||
bool HasAccessPointGateway() const { return hasAccessPointGateway; }
|
||||
boost::asio::ip::network_v4 &GetAccessPointGateway() { return this->accessPointGateway; }
|
||||
const std::string & GetAccessPointServerAddress() const { return this->accessPointServerAddress; }
|
||||
|
||||
public:
|
||||
virtual void onError(boost::system::error_code ec, char const* what);
|
||||
virtual void onWarning(boost::system::error_code ec, char const* what);
|
||||
virtual void onInfo(char const* what);
|
||||
|
||||
virtual void onListenerClosed();
|
||||
};
|
||||
|
||||
} // namespace pipedal;
|
||||
+8
-4
@@ -89,9 +89,10 @@ set (PIPEDAL_SOURCES
|
||||
CpuUse.hpp CpuUse.cpp
|
||||
P2pConfigFiles.hpp
|
||||
AvahiService.cpp AvahiService.hpp
|
||||
DeviceIdFile.cpp DeviceIdFile.hpp
|
||||
ServiceConfiguration.cpp ServiceConfiguration.hpp
|
||||
WriteTemplateFile.cpp WriteTemplateFile.hpp
|
||||
|
||||
ConfigUtil.hpp ConfigUtil.cpp
|
||||
ConfigSerializer.h ConfigSerializer.cpp
|
||||
StdErrorCapture.hpp StdErrorCapture.cpp
|
||||
Ipv6Helpers.cpp Ipv6Helpers.hpp
|
||||
PluginPreset.cpp PluginPreset.hpp
|
||||
@@ -236,12 +237,13 @@ add_test(NAME DevTest COMMAND pipedaltest "[Dev]")
|
||||
|
||||
add_executable(pipedalconfig
|
||||
PrettyPrinter.hpp
|
||||
DeviceIdFile.cpp DeviceIdFile.hpp
|
||||
ServiceConfiguration.cpp ServiceConfiguration.hpp
|
||||
json.cpp json.hpp
|
||||
HtmlHelper.cpp HtmlHelper.hpp
|
||||
CommandLineParser.hpp
|
||||
WriteTemplateFile.hpp WriteTemplateFile.cpp
|
||||
PiPedalException.hpp
|
||||
ConfigSerializer.h ConfigSerializer.cpp
|
||||
ConfigMain.cpp
|
||||
WifiConfigSettings.cpp WifiConfigSettings.hpp
|
||||
WifiDirectConfigSettings.cpp WifiDirectConfigSettings.hpp
|
||||
@@ -283,7 +285,9 @@ target_link_libraries(pipedal_latency_test PRIVATE pthread asound
|
||||
|
||||
add_executable(pipedaladmind AdminMain.cpp CommandLineParser.hpp
|
||||
UnixSocket.cpp UnixSocket.hpp
|
||||
DeviceIdFile.cpp DeviceIdFile.hpp
|
||||
ServiceConfiguration.cpp ServiceConfiguration.hpp
|
||||
ConfigSerializer.h ConfigSerializer.cpp
|
||||
autoptr_vector.h
|
||||
|
||||
JackServerSettings.hpp JackServerSettings.cpp
|
||||
json.hpp json.cpp HtmlHelper.hpp HtmlHelper.cpp Lv2Log.hpp Lv2Log.cpp
|
||||
|
||||
+59
-26
@@ -33,7 +33,7 @@
|
||||
#include "JackServerSettings.hpp"
|
||||
#include "P2pConfigFiles.hpp"
|
||||
#include "PrettyPrinter.hpp"
|
||||
#include "DeviceIdFile.hpp"
|
||||
#include "ServiceConfiguration.hpp"
|
||||
#include <uuid/uuid.h>
|
||||
#include <random>
|
||||
#include "AudioConfig.hpp"
|
||||
@@ -366,6 +366,7 @@ void InstallLimits()
|
||||
}
|
||||
|
||||
|
||||
#if JACK_HOST
|
||||
void MaybeStartJackService()
|
||||
{
|
||||
std::filesystem::path drcFile = "/etc/jackdrc";
|
||||
@@ -379,7 +380,9 @@ void MaybeStartJackService()
|
||||
silentSysExec(SYSTEMCTL_BIN " mask jack");
|
||||
}
|
||||
}
|
||||
void InstallJackService()
|
||||
#endif
|
||||
|
||||
void InstallAudioService()
|
||||
{
|
||||
InstallLimits();
|
||||
InstallPamEnv();
|
||||
@@ -435,6 +438,28 @@ int SudoExec(char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsP2pServiceEnabled()
|
||||
{
|
||||
WifiDirectConfigSettings settings;
|
||||
settings.Load();
|
||||
return settings.enable_;
|
||||
}
|
||||
|
||||
static void RestartP2pdService()
|
||||
{
|
||||
if (IsP2pServiceEnabled())
|
||||
{
|
||||
silentSysExec(SYSTEMCTL_BIN " restart " PIPEDAL_P2PD_SERVICE);
|
||||
}
|
||||
}
|
||||
|
||||
static void UninstallP2pdService()
|
||||
{
|
||||
silentSysExec("systemctl stop " PIPEDAL_P2PD_SERVICE);
|
||||
silentSysExec("systemctl disable " PIPEDAL_P2PD_SERVICE);
|
||||
}
|
||||
|
||||
|
||||
void Uninstall()
|
||||
{
|
||||
try
|
||||
@@ -444,9 +469,13 @@ void Uninstall()
|
||||
|
||||
StopService();
|
||||
DisableService();
|
||||
UninstallP2pdService();
|
||||
|
||||
#if UNINSTALL_JACK_SERVICE
|
||||
silentSysExec(SYSTEMCTL_BIN " stop jack");
|
||||
silentSysExec(SYSTEMCTL_BIN " disable jack");
|
||||
OnWifiUninstall();
|
||||
#endif
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
@@ -545,33 +574,33 @@ static std::string RandomChars(int nchars)
|
||||
return s.str();
|
||||
}
|
||||
|
||||
static void PrepareDeviceidFile()
|
||||
static void PrepareServiceConfigurationFile(uint16_t portNumber)
|
||||
{
|
||||
if (!std::filesystem::exists(DeviceIdFile::DEVICEID_FILE_NAME))
|
||||
ServiceConfiguration serviceConfiguration;
|
||||
try {
|
||||
serviceConfiguration.Load();
|
||||
} catch (const std::exception &)
|
||||
{
|
||||
DeviceIdFile deviceIdFile;
|
||||
|
||||
deviceIdFile.deviceName = "PiPedal";
|
||||
deviceIdFile.uuid = MakeUuid();
|
||||
deviceIdFile.Save();
|
||||
} else {
|
||||
DeviceIdFile deviceIdFile;
|
||||
if (deviceIdFile.deviceName == "" || deviceIdFile.uuid == "")
|
||||
{
|
||||
if (deviceIdFile.deviceName == "")
|
||||
{
|
||||
deviceIdFile.deviceName = "PiPedal";
|
||||
}
|
||||
if (deviceIdFile.uuid == "")
|
||||
{
|
||||
deviceIdFile.uuid = MakeUuid();
|
||||
}
|
||||
deviceIdFile.Save();
|
||||
}
|
||||
|
||||
}
|
||||
if (serviceConfiguration.deviceName == "" || serviceConfiguration.uuid == ""
|
||||
|| portNumber != serviceConfiguration.server_port
|
||||
)
|
||||
{
|
||||
if (serviceConfiguration.deviceName == "")
|
||||
{
|
||||
serviceConfiguration.deviceName = "PiPedal";
|
||||
}
|
||||
if (serviceConfiguration.uuid == "")
|
||||
{
|
||||
serviceConfiguration.uuid = MakeUuid();
|
||||
}
|
||||
serviceConfiguration.server_port = portNumber;
|
||||
serviceConfiguration.Save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Install(const std::filesystem::path &programPrefix, const std::string endpointAddress)
|
||||
{
|
||||
if (sysExec(GROUPADD_BIN " -f " AUDIO_SERVICE_GROUP_NAME) != EXIT_SUCCESS)
|
||||
@@ -583,8 +612,7 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
|
||||
throw PiPedalException("Failed to create pipedald service group.");
|
||||
}
|
||||
|
||||
PrepareDeviceidFile();
|
||||
InstallJackService();
|
||||
InstallAudioService();
|
||||
auto endpos = endpointAddress.find_last_of(':');
|
||||
if (endpos == string::npos)
|
||||
{
|
||||
@@ -610,6 +638,9 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
|
||||
s << "Invalid port number: " << strPort;
|
||||
throw PiPedalException(s.str());
|
||||
}
|
||||
PrepareServiceConfigurationFile(port);
|
||||
|
||||
|
||||
|
||||
bool authBindRequired = port < 512;
|
||||
|
||||
@@ -744,6 +775,8 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
|
||||
RestartService(false);
|
||||
EnableService();
|
||||
|
||||
RestartP2pdService();
|
||||
|
||||
cout << "Complete"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "ConfigSerializer.h"
|
||||
|
||||
|
||||
using namespace config_serializer;
|
||||
using namespace config_serializer::detail;
|
||||
using namespace std;
|
||||
|
||||
std::string config_serializer::detail::trim(const std::string &v)
|
||||
{
|
||||
size_t start = v.find_first_not_of(' ');
|
||||
size_t end = v.find_last_not_of(' ');
|
||||
if (start >= end+1)
|
||||
return "";
|
||||
return v.substr(start, end+1 - start);
|
||||
}
|
||||
|
||||
std::vector<std::string> config_serializer::detail::split(const std::string &value, char delimiter)
|
||||
{
|
||||
size_t start = 0;
|
||||
std::vector<std::string> result;
|
||||
while (start < value.length())
|
||||
{
|
||||
size_t pos = value.find_first_of(delimiter, start);
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
result.push_back(value.substr(start));
|
||||
break;
|
||||
}
|
||||
result.push_back(value.substr(start, pos - start));
|
||||
start = pos + 1;
|
||||
if (start == value.length())
|
||||
{
|
||||
// ends with delimieter? Then there's an empty-length value at the end.
|
||||
result.push_back("");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string config_serializer::detail::EncodeString(const std::string &s)
|
||||
{
|
||||
bool requiresEncoding = false;
|
||||
|
||||
for (char c : s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\0':
|
||||
break;
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\\':
|
||||
case ' ':
|
||||
case '\"':
|
||||
requiresEncoding = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!requiresEncoding)
|
||||
return s;
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
ss << '"';
|
||||
for (char c : s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\0':
|
||||
// discard.
|
||||
break;
|
||||
case '\r':
|
||||
ss << "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
ss << "\\t";
|
||||
break;
|
||||
case '\\':
|
||||
ss << "\\\\";
|
||||
break;
|
||||
case '\"':
|
||||
ss << "\\\"";
|
||||
break;
|
||||
default:
|
||||
ss << c;
|
||||
}
|
||||
}
|
||||
ss << '"';
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string config_serializer::detail::DecodeString(const std::string &value)
|
||||
{
|
||||
std::stringstream s;
|
||||
const char *p = value.c_str();
|
||||
char quoteChar;
|
||||
if (*p == '\'' || *p == '\"')
|
||||
{
|
||||
quoteChar = *p;
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
if (*p != quoteChar)
|
||||
return value;
|
||||
if (value.at(value.length()-1) != quoteChar)
|
||||
{
|
||||
throw std::invalid_argument("Invalid quoted string.");
|
||||
}
|
||||
++p;
|
||||
while (*p != 0 && *p != quoteChar)
|
||||
{
|
||||
char c = *p++;
|
||||
if (c == '\\')
|
||||
{
|
||||
c = *p++;
|
||||
switch (c)
|
||||
{
|
||||
case 'r':
|
||||
s << '\r';
|
||||
break;
|
||||
case 't':
|
||||
s << '\t';
|
||||
break;
|
||||
case 'n':
|
||||
s << '\n';
|
||||
break;
|
||||
case '\0':
|
||||
throw std::invalid_argument("Invalid quoted string.");
|
||||
break;
|
||||
default:
|
||||
s << c;
|
||||
}
|
||||
if (c != 0)
|
||||
{
|
||||
s << c;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s << c;
|
||||
}
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include "autoptr_vector.h"
|
||||
#include <unordered_map>
|
||||
#include <fstream>
|
||||
|
||||
namespace config_serializer
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
std::string trim(const std::string &v);
|
||||
|
||||
std::vector<std::string> split(const std::string &v, char seperator);
|
||||
|
||||
/**
|
||||
* @brief Convert string to config file format.
|
||||
*
|
||||
* Adds quotes and escapes, but only if neccessary.
|
||||
*
|
||||
* Source text is assumed to be UTF-8. Escapes for the
|
||||
* following values only: \r \n \t \" \\.
|
||||
*
|
||||
*
|
||||
* @param s
|
||||
* @return std::string Encoded string.
|
||||
*/
|
||||
std::string EncodeString(const std::string &s);
|
||||
|
||||
/**
|
||||
* @brief Config file format to string.
|
||||
*
|
||||
* Decodes quotes and escapes, but only if neccessary.
|
||||
*
|
||||
* Source text is assumed to be UTF-8. Escapes for the
|
||||
* following values only: \r \n \t \" \\.
|
||||
*
|
||||
* @param s
|
||||
* @return std::string
|
||||
*/
|
||||
std::string DecodeString(const std::string &s);
|
||||
|
||||
/**
|
||||
* @brief Convert a string to an integer.
|
||||
*
|
||||
* @tparam T A signed or unsigned integral type.
|
||||
* @param value The string to parse.
|
||||
* @return T The parsed result.
|
||||
* @throws std::invalid_argument if the supplied string is not a valid integer.
|
||||
*/
|
||||
template <class T>
|
||||
T ToInt(const std::string &value)
|
||||
{
|
||||
std::stringstream s(value);
|
||||
T result;
|
||||
s >> result;
|
||||
if (s.fail())
|
||||
{
|
||||
throw std::invalid_argument("Invalid integer value.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using namespace detail;
|
||||
|
||||
template <class OBJ>
|
||||
class ConfigSerializerBase
|
||||
{
|
||||
protected:
|
||||
ConfigSerializerBase(const std::string &name, const std::string &comment)
|
||||
: name(name),
|
||||
comment(comment)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
using Obj = OBJ;
|
||||
virtual ~ConfigSerializerBase() {}
|
||||
|
||||
const std::string &GetName() const { return name; }
|
||||
const std::string &GetComment() const { return comment; }
|
||||
virtual void SetValue(Obj &configurable, const std::string &value) const = 0;
|
||||
virtual const std::string GetValue(Obj &configurable) const = 0;
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
std::string comment;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class ConfigConverter
|
||||
{
|
||||
public:
|
||||
static T FromString(const std::string &value)
|
||||
{
|
||||
T result;
|
||||
std::stringstream s(value);
|
||||
s >> result;
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string ToString(const T &value)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << value;
|
||||
return s.str();
|
||||
}
|
||||
};
|
||||
|
||||
// string specializations.
|
||||
template <>
|
||||
inline std::string ConfigConverter<std::string>::FromString(const std::string &value)
|
||||
{
|
||||
return DecodeString(value);
|
||||
}
|
||||
template <>
|
||||
inline std::string ConfigConverter<std::string>::ToString(const std::string &config)
|
||||
{
|
||||
return EncodeString(config);
|
||||
}
|
||||
// bool specializations.
|
||||
template <>
|
||||
inline bool ConfigConverter<bool>::FromString(const std::string &value)
|
||||
{
|
||||
bool result = false;
|
||||
if (value == "")
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
else if (value == "true")
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
else if (value == "false")
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
int t = ToInt<int>(value);
|
||||
result = (t != 0);
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
throw std::invalid_argument("Expecting true or false.");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
template <>
|
||||
inline std::string ConfigConverter<bool>::ToString(const bool &value)
|
||||
{
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
template <class OBJ, class T>
|
||||
class ConfigSerializer : public ConfigSerializerBase<OBJ>
|
||||
{
|
||||
public:
|
||||
using Obj = OBJ;
|
||||
using pointer_type = T Obj::*;
|
||||
using base = ConfigSerializerBase<Obj>;
|
||||
using converter = ConfigConverter<T>;
|
||||
|
||||
ConfigSerializer(const std::string &name,
|
||||
pointer_type pMember,
|
||||
const std::string &comment)
|
||||
: base(name, comment),
|
||||
pMember(pMember)
|
||||
{
|
||||
}
|
||||
virtual void SetValue(Obj &configurable, const std::string &value) const
|
||||
{
|
||||
configurable.*pMember = ConfigConverter<T>::FromString(value);
|
||||
}
|
||||
virtual const std::string GetValue(Obj &configurable) const
|
||||
{
|
||||
return ConfigConverter<T>::ToString(configurable.*pMember);
|
||||
}
|
||||
|
||||
private:
|
||||
pointer_type pMember;
|
||||
};
|
||||
|
||||
template <class OBJ>
|
||||
class ConfigSerializable
|
||||
{
|
||||
private:
|
||||
using Obj = OBJ;
|
||||
|
||||
using serializer_t = ConfigSerializerBase<OBJ>;
|
||||
using serializers_t = p2p::autoptr_vector<serializer_t>;
|
||||
|
||||
const serializers_t &serializers;
|
||||
|
||||
protected:
|
||||
ConfigSerializable(const serializers_t &serializers)
|
||||
: serializers(serializers)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void Save(std::ostream &f)
|
||||
{
|
||||
bool firstLine = true;
|
||||
for (const serializer_t *serializer : serializers)
|
||||
{
|
||||
if (serializer->GetComment().length() != 0)
|
||||
{
|
||||
if (!firstLine)
|
||||
{
|
||||
f << std::endl;
|
||||
}
|
||||
firstLine = false;
|
||||
std::vector<std::string> comments = split(serializer->GetComment(), '\n');
|
||||
|
||||
if (comments.size() != 0)
|
||||
for (const std::string &comment : comments)
|
||||
{
|
||||
f << "# " << comment << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
f << serializer->GetName() << '=' << serializer->GetValue((Obj &)*this) << std::endl;
|
||||
}
|
||||
}
|
||||
void Save(const std::string &path)
|
||||
{
|
||||
std::ofstream f;
|
||||
f.open(path);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw std::invalid_argument("Can't write to file " + path);
|
||||
}
|
||||
try
|
||||
{
|
||||
Save(f);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
throw std::invalid_argument("Error writing to '" + path + "'. " + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Load(std::istream &f)
|
||||
{
|
||||
std::unordered_map<std::string, serializer_t *> index;
|
||||
|
||||
for (serializer_t *serializer : serializers)
|
||||
{
|
||||
index[serializer->GetName()] = serializer;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
int nLine = 0;
|
||||
while (true)
|
||||
{
|
||||
if (f.eof())
|
||||
break;
|
||||
getline(f, line);
|
||||
++nLine;
|
||||
|
||||
// remove comments.
|
||||
auto npos = line.find_first_of('#');
|
||||
if (npos != std::string::npos)
|
||||
{
|
||||
line = line.substr(0, npos);
|
||||
}
|
||||
size_t start = 0;
|
||||
while (start < line.size() && line[start] == ' ')
|
||||
{
|
||||
++start;
|
||||
}
|
||||
if (start == line.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto pos = line.find_first_of('=', start);
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
std::stringstream msg;
|
||||
|
||||
msg << "(" << nLine << "," << (start + 1) << "): Syntax error. Expecting '='.";
|
||||
throw std::logic_error(msg.str());
|
||||
}
|
||||
std::string label = trim(line.substr(start, pos - start));
|
||||
std::string value = trim(line.substr(pos + 1));
|
||||
|
||||
if (index.contains(label))
|
||||
{
|
||||
serializer_t *serializer = index[label];
|
||||
try
|
||||
{
|
||||
serializer->SetValue((Obj &)*this, value);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "(" << nLine << ',' << (pos + 1) << "): " << e.what();
|
||||
throw std::logic_error(msg.str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "(" << nLine << ',' << (start) << "): "
|
||||
<< "Invalid property: " + label;
|
||||
throw std::logic_error(
|
||||
msg.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Load(const std::string &path, bool throwIfNotFound = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::ifstream f;
|
||||
f.open(path);
|
||||
if (!f.is_open())
|
||||
{
|
||||
if (throwIfNotFound)
|
||||
{
|
||||
throw std::logic_error("Can't open file.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
Load(f);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "Error: " << path << " " << e.what();
|
||||
throw std::logic_error(msg.str());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+8
-1
@@ -161,6 +161,12 @@ private:
|
||||
|
||||
isOpen = false;
|
||||
StopReaderThread();
|
||||
|
||||
if (realtimeMonitorPortSubscriptions != nullptr)
|
||||
{
|
||||
delete realtimeMonitorPortSubscriptions;
|
||||
realtimeMonitorPortSubscriptions = nullptr;
|
||||
}
|
||||
if (active)
|
||||
{
|
||||
audioDriver->Deactivate();
|
||||
@@ -933,8 +939,9 @@ public:
|
||||
Lv2Log::info("Audio started.");
|
||||
|
||||
}
|
||||
catch (PiPedalException &e)
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error(SS("Failed to start audio. " << e.what()));
|
||||
Close();
|
||||
active = false;
|
||||
throw;
|
||||
|
||||
@@ -785,6 +785,9 @@ Lv2PortInfo::Lv2PortInfo(Lv2Host *host, const LilvPlugin *plugin, const LilvPort
|
||||
NodeAutoFree unitsValueUri = lilv_port_get(plugin, pPort, host->lilvUris.unitsUri);
|
||||
this->units_ = UriToUnits(nodeAsString(unitsValueUri));
|
||||
|
||||
NodeAutoFree commentNode = lilv_port_get(plugin,pPort,host->lilvUris.rdfsComment);
|
||||
this->comment_ = nodeAsString(commentNode);
|
||||
|
||||
NodeAutoFree bufferType = lilv_port_get(plugin, pPort, host->lilvUris.bufferType_uri);
|
||||
|
||||
this->buffer_type_ = "";
|
||||
@@ -1167,6 +1170,7 @@ json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{{
|
||||
MAP_REF(Lv2PortInfo, buffer_type),
|
||||
MAP_REF(Lv2PortInfo, port_group),
|
||||
json_map::enum_reference("units", &Lv2PortInfo::units_, get_units_enum_converter()),
|
||||
MAP_REF(Lv2PortInfo, comment)
|
||||
}};
|
||||
|
||||
json_map::storage_type<Lv2PortGroup> Lv2PortGroup::jmap{{
|
||||
@@ -1229,6 +1233,7 @@ json_map::storage_type<Lv2PluginUiControlPort> Lv2PluginUiControlPort::jmap{{
|
||||
MAP_REF(Lv2PluginUiControlPort, port_group),
|
||||
|
||||
json_map::enum_reference("units", &Lv2PluginUiControlPort::units_, get_units_enum_converter()),
|
||||
MAP_REF(Lv2PluginUiControlPort, comment),
|
||||
|
||||
}};
|
||||
|
||||
|
||||
@@ -205,6 +205,7 @@ private:
|
||||
|
||||
std::string designation_;
|
||||
Units units_ = Units::none;
|
||||
std::string comment_;
|
||||
public:
|
||||
bool IsSwitch() const {
|
||||
return min_value_ == 0 && max_value_ == 1
|
||||
@@ -261,6 +262,7 @@ public:
|
||||
LV2_PROPERTY_GETSET_SCALAR(toggled_property);
|
||||
LV2_PROPERTY_GETSET_SCALAR(not_on_gui);
|
||||
LV2_PROPERTY_GETSET(port_group);
|
||||
LV2_PROPERTY_GETSET(comment);
|
||||
LV2_PROPERTY_GETSET_SCALAR(units);
|
||||
|
||||
LV2_PROPERTY_GETSET(buffer_type);
|
||||
@@ -417,6 +419,7 @@ public:
|
||||
, toggled_property_(pPort->toggled_property())
|
||||
, not_on_gui_(pPort->not_on_gui())
|
||||
, scale_points_(pPort->scale_points())
|
||||
, comment_(pPort->comment())
|
||||
, units_(pPort->units())
|
||||
{
|
||||
// Use symbols to index port groups, instead of uris.
|
||||
@@ -450,6 +453,7 @@ private:
|
||||
std::vector<Lv2ScalePoint> scale_points_;
|
||||
std::string port_group_;
|
||||
Units units_ = Units::none;
|
||||
std::string comment_;
|
||||
public:
|
||||
LV2_PROPERTY_GETSET(symbol);
|
||||
LV2_PROPERTY_GETSET_SCALAR(index);
|
||||
@@ -466,6 +470,7 @@ public:
|
||||
LV2_PROPERTY_GETSET_SCALAR(not_on_gui);
|
||||
LV2_PROPERTY_GETSET(scale_points);
|
||||
LV2_PROPERTY_GETSET(units);
|
||||
LV2_PROPERTY_GETSET(comment);
|
||||
|
||||
public:
|
||||
static json_map::storage_type<Lv2PluginUiControlPort> jmap;
|
||||
|
||||
+168
-97
@@ -18,8 +18,9 @@
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "pch.h"
|
||||
#include "DeviceIdFile.hpp"
|
||||
#include "ServiceConfiguration.hpp"
|
||||
#include "AudioConfig.hpp"
|
||||
#include "ConfigUtil.hpp"
|
||||
#include <sched.h>
|
||||
#include "PiPedalModel.hpp"
|
||||
#include "JackHost.hpp"
|
||||
@@ -55,8 +56,6 @@ PiPedalModel::PiPedalModel()
|
||||
#else
|
||||
this->jackServerSettings = this->storage.GetJackServerSettings();
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PiPedalModel::Close()
|
||||
@@ -84,37 +83,40 @@ void PiPedalModel::Close()
|
||||
|
||||
PiPedalModel::~PiPedalModel()
|
||||
{
|
||||
try {
|
||||
adminClient.UnmonitorGovernor();
|
||||
} catch (...) // noexcept here!
|
||||
try
|
||||
{
|
||||
adminClient.UnmonitorGovernor();
|
||||
}
|
||||
catch (...) // noexcept here!
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
CurrentPreset currentPreset;
|
||||
currentPreset.modified_ = this->hasPresetChanged;
|
||||
currentPreset.preset_ = this->pedalBoard;
|
||||
storage.SaveCurrentPreset(currentPreset);
|
||||
} catch (...)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (jackHost)
|
||||
{
|
||||
jackHost->Close();
|
||||
}
|
||||
} catch (...)
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#include <fstream>
|
||||
|
||||
void PiPedalModel::Init(const PiPedalConfiguration&configuration)
|
||||
void PiPedalModel::Init(const PiPedalConfiguration &configuration)
|
||||
{
|
||||
this->configuration = configuration;
|
||||
storage.SetConfigRoot(configuration.GetDocRoot());
|
||||
@@ -122,7 +124,6 @@ void PiPedalModel::Init(const PiPedalConfiguration&configuration)
|
||||
storage.Initialize();
|
||||
|
||||
this->jackServerSettings.ReadJackConfiguration();
|
||||
|
||||
}
|
||||
|
||||
void PiPedalModel::LoadLv2PluginInfo()
|
||||
@@ -147,14 +148,14 @@ void PiPedalModel::LoadLv2PluginInfo()
|
||||
// Copy all presets out of Lilv data to json files
|
||||
// so that we can close lilv while we're actually
|
||||
// running.
|
||||
for (const auto&plugin: lv2Host.GetPlugins())
|
||||
for (const auto &plugin : lv2Host.GetPlugins())
|
||||
{
|
||||
if (plugin->has_factory_presets())
|
||||
{
|
||||
if (!storage.HasPluginPresets(plugin->uri()))
|
||||
{
|
||||
PluginPresets pluginPresets = lv2Host.GetFactoryPluginPresets(plugin->uri());
|
||||
storage.SavePluginPresets(plugin->uri(),pluginPresets);
|
||||
storage.SavePluginPresets(plugin->uri(), pluginPresets);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,7 +166,6 @@ void PiPedalModel::Load()
|
||||
this->webRoot = configuration.GetWebRoot();
|
||||
this->webPort = (uint16_t)configuration.GetSocketServerPort();
|
||||
|
||||
|
||||
adminClient.MonitorGovernor(storage.GetGovernorSettings());
|
||||
|
||||
// lv2Host.Load(configuration.GetLv2Path().c_str());
|
||||
@@ -200,8 +200,8 @@ void PiPedalModel::Load()
|
||||
|
||||
struct sched_param scheduler_params;
|
||||
scheduler_params.sched_priority = 10;
|
||||
memset(&scheduler_params,0,sizeof(sched_param));
|
||||
sched_setscheduler(0,SCHED_RR,&scheduler_params);
|
||||
memset(&scheduler_params, 0, sizeof(sched_param));
|
||||
sched_setscheduler(0, SCHED_RR, &scheduler_params);
|
||||
#if JACK_HOST
|
||||
this->jackConfiguration = this->jackConfiguration.JackInitialize();
|
||||
#else
|
||||
@@ -216,7 +216,7 @@ void PiPedalModel::Load()
|
||||
this->lv2Host.OnConfigurationChanged(jackConfiguration, selection);
|
||||
try
|
||||
{
|
||||
jackHost->Open(this->jackServerSettings,selection);
|
||||
jackHost->Open(this->jackServerSettings, selection);
|
||||
|
||||
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
|
||||
this->lv2PedalBoard = lv2PedalBoard;
|
||||
@@ -226,7 +226,9 @@ void PiPedalModel::Load()
|
||||
{
|
||||
Lv2Log::error("Failed to load initial plugin. (%s)", e.what());
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
Lv2Log::error("Audio device not configured.");
|
||||
}
|
||||
}
|
||||
@@ -453,7 +455,7 @@ void PiPedalModel::FirePresetsChanged(int64_t clientId)
|
||||
delete[] t;
|
||||
}
|
||||
}
|
||||
void PiPedalModel::FirePluginPresetsChanged(const std::string & pluginUri)
|
||||
void PiPedalModel::FirePluginPresetsChanged(const std::string &pluginUri)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||
{
|
||||
@@ -473,7 +475,6 @@ void PiPedalModel::FirePluginPresetsChanged(const std::string & pluginUri)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PiPedalModel::SaveCurrentPreset(int64_t clientId)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||
@@ -482,31 +483,31 @@ void PiPedalModel::SaveCurrentPreset(int64_t clientId)
|
||||
this->SetPresetChanged(clientId, false);
|
||||
}
|
||||
|
||||
uint64_t PiPedalModel::CopyPluginPreset(const std::string&pluginUri,uint64_t presetId)
|
||||
uint64_t PiPedalModel::CopyPluginPreset(const std::string &pluginUri, uint64_t presetId)
|
||||
{
|
||||
uint64_t result = storage.CopyPluginPreset(pluginUri,presetId);
|
||||
uint64_t result = storage.CopyPluginPreset(pluginUri, presetId);
|
||||
FirePluginPresetsChanged(pluginUri);
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
void PiPedalModel::UpdatePluginPresets(const PluginUiPresets&pluginPresets)
|
||||
void PiPedalModel::UpdatePluginPresets(const PluginUiPresets &pluginPresets)
|
||||
{
|
||||
storage.UpdatePluginPresets(pluginPresets);
|
||||
FirePluginPresetsChanged(pluginPresets.pluginUri_);
|
||||
}
|
||||
int64_t PiPedalModel::SavePluginPresetAs(int64_t instanceId, const std::string&name)
|
||||
int64_t PiPedalModel::SavePluginPresetAs(int64_t instanceId, const std::string &name)
|
||||
{
|
||||
auto *item = this->pedalBoard.GetItem(instanceId);
|
||||
if (!item)
|
||||
{
|
||||
throw PiPedalException("Plugin not found.");
|
||||
}
|
||||
std::map<std::string,float> values;
|
||||
for (const auto & controlValue: item->controlValues())
|
||||
std::map<std::string, float> values;
|
||||
for (const auto &controlValue : item->controlValues())
|
||||
{
|
||||
values[controlValue.key()] = controlValue.value();
|
||||
}
|
||||
uint64_t presetId = storage.SavePluginPreset(item->uri(),name,values);
|
||||
uint64_t presetId = storage.SavePluginPreset(item->uri(), name, values);
|
||||
FirePluginPresetsChanged(item->uri());
|
||||
return presetId;
|
||||
}
|
||||
@@ -522,16 +523,15 @@ int64_t PiPedalModel::SaveCurrentPresetAs(int64_t clientId, const std::string &n
|
||||
return result;
|
||||
}
|
||||
|
||||
void PiPedalModel::UploadPluginPresets(const PluginPresets&pluginPresets)
|
||||
void PiPedalModel::UploadPluginPresets(const PluginPresets &pluginPresets)
|
||||
{
|
||||
if (pluginPresets.pluginUri_.length() == 0)
|
||||
{
|
||||
throw PiPedalException("Invalid plugin presets.");
|
||||
}
|
||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||
storage.SavePluginPresets(pluginPresets.pluginUri_,pluginPresets);
|
||||
storage.SavePluginPresets(pluginPresets.pluginUri_, pluginPresets);
|
||||
FirePluginPresetsChanged(pluginPresets.pluginUri_);
|
||||
|
||||
}
|
||||
int64_t PiPedalModel::UploadPreset(const BankFile &bankFile, int64_t uploadAfter)
|
||||
{
|
||||
@@ -623,7 +623,7 @@ int64_t PiPedalModel::DeletePreset(int64_t clientId, int64_t instanceId)
|
||||
-1, // can't use cached version.
|
||||
newSelection);
|
||||
}
|
||||
return newSelection;
|
||||
return newSelection;
|
||||
}
|
||||
bool PiPedalModel::RenamePreset(int64_t clientId, int64_t instanceId, const std::string &name)
|
||||
{
|
||||
@@ -649,13 +649,13 @@ GovernorSettings PiPedalModel::GetGovernorSettings()
|
||||
return result;
|
||||
}
|
||||
}
|
||||
void PiPedalModel::SetGovernorSettings(const std::string& governor)
|
||||
void PiPedalModel::SetGovernorSettings(const std::string &governor)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
adminClient.SetGovernorSettings(governor);
|
||||
|
||||
this->storage.SetGovernorSettings(governor);
|
||||
|
||||
|
||||
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
|
||||
{
|
||||
for (size_t i = 0; i < subscribers.size(); ++i)
|
||||
@@ -669,9 +669,8 @@ void PiPedalModel::SetGovernorSettings(const std::string& governor)
|
||||
}
|
||||
delete[] t;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
@@ -698,22 +697,46 @@ void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSet
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static std::string GetP2pdName()
|
||||
{
|
||||
std::string name = "/etc/pipedal/config/pipedal_p2pd.conf";
|
||||
|
||||
std::string result;
|
||||
|
||||
if (ConfigUtil::GetConfigLine(name,"p2p_device_name",&result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void PiPedalModel::UpdateDnsSd()
|
||||
{
|
||||
avahiService.Unannounce();
|
||||
|
||||
DeviceIdFile deviceIdFile;
|
||||
ServiceConfiguration deviceIdFile;
|
||||
deviceIdFile.Load();
|
||||
|
||||
|
||||
std::string p2pdName = GetP2pdName();
|
||||
if (p2pdName != "") {
|
||||
deviceIdFile.deviceName = p2pdName;
|
||||
}
|
||||
|
||||
if (deviceIdFile.deviceName != "" && deviceIdFile.uuid != "")
|
||||
{
|
||||
avahiService.Announce(webPort,deviceIdFile.deviceName,deviceIdFile.uuid,"pipedal");
|
||||
} else {
|
||||
avahiService.Announce(webPort, deviceIdFile.deviceName, deviceIdFile.uuid, "pipedal");
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// device_uuid file is written at install time. This warning is harmless if you're debugging.
|
||||
// Without it, we can't pulblish the website via dnsDS.
|
||||
// Run "pipedalconfig --install" to create the file.
|
||||
Lv2Log::warning("Cant read device_uuid file. dnsSD announcement skipped.");
|
||||
Lv2Log::warning("Cant read device_uuid file from service.conf file. dnsSD announcement skipped.");
|
||||
}
|
||||
|
||||
}
|
||||
void PiPedalModel::SetWifiDirectConfigSettings(const WifiDirectConfigSettings &wifiDirectConfigSettings)
|
||||
{
|
||||
@@ -741,11 +764,7 @@ void PiPedalModel::SetWifiDirectConfigSettings(const WifiDirectConfigSettings &w
|
||||
|
||||
// update NSD-SD announement.
|
||||
UpdateDnsSd();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WifiConfigSettings PiPedalModel::GetWifiConfigSettings()
|
||||
@@ -786,7 +805,6 @@ bool PiPedalModel::GetShowStatusMonitor()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex); // copy atomically.
|
||||
return storage.GetShowStatusMonitor();
|
||||
|
||||
}
|
||||
|
||||
JackConfiguration PiPedalModel::GetJackConfiguration()
|
||||
@@ -795,37 +813,82 @@ JackConfiguration PiPedalModel::GetJackConfiguration()
|
||||
return this->jackConfiguration;
|
||||
}
|
||||
|
||||
void PiPedalModel::SetJackChannelSelection(int64_t clientId, const JackChannelSelection &channelSelection)
|
||||
void PiPedalModel::RestartAudio()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex); // copy atomically.
|
||||
this->storage.SetJackChannelSelection(channelSelection);
|
||||
this->FireChannelSelectionChanged(clientId);
|
||||
|
||||
this->lv2Host.OnConfigurationChanged(jackConfiguration, channelSelection);
|
||||
if (this->jackHost->IsOpen())
|
||||
{
|
||||
|
||||
this->jackHost->Close();
|
||||
#ifdef JUNK
|
||||
// restarting is a bit dodgy. It was impossible with Jack, but
|
||||
// now very plausible with the ALSA audio stack.
|
||||
|
||||
}
|
||||
// restarting is a bit dodgy. It was impossible with Jack, but
|
||||
// now very plausible with the ALSA audio stack.
|
||||
|
||||
// Still bugs wrt/ restarting the circular buffers for the audio thread.
|
||||
|
||||
// for the meantime, just rely on the fact that the admin service will restart
|
||||
// for the meantime, just rely on the fact that the admin service will restart
|
||||
// the process.
|
||||
//...
|
||||
|
||||
// do a complete reload.
|
||||
|
||||
this->jackHost->SetPedalBoard(nullptr);
|
||||
this->jackHost->Open(this->jackServerSettings,channelSelection);
|
||||
this->jackHost->SetPedalBoard(nullptr);
|
||||
|
||||
this->jackConfiguration.AlsaInitialize(this->jackServerSettings);
|
||||
|
||||
if (this->jackConfiguration.isValid())
|
||||
{
|
||||
JackChannelSelection selection = storage.GetJackChannelSelection(jackConfiguration);
|
||||
selection = selection.RemoveInvalidChannels(jackConfiguration);
|
||||
|
||||
} else {
|
||||
jackConfiguration.SetErrorStatus("Error");
|
||||
}
|
||||
|
||||
FireJackConfigurationChanged(jackConfiguration);
|
||||
|
||||
|
||||
if (!jackServerSettings.IsValid() || !jackConfiguration.isValid())
|
||||
{
|
||||
Lv2Log::error("Audio configuration not valid.");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
JackChannelSelection channelSelection = GetJackChannelSelection();
|
||||
if (!channelSelection.isValid())
|
||||
{
|
||||
Lv2Log::error("Audio configuration not valid.");
|
||||
return;
|
||||
}
|
||||
this->jackHost->Open(this->jackServerSettings, channelSelection);
|
||||
|
||||
this->lv2Host.OnConfigurationChanged(jackConfiguration, channelSelection);
|
||||
|
||||
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
|
||||
this->lv2PedalBoard = lv2PedalBoard;
|
||||
jackHost->SetPedalBoard(lv2PedalBoard);
|
||||
this->UpdateRealtimeVuSubscriptions();
|
||||
#endif
|
||||
UpdateRealtimeMonitorPortSubscriptions();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error(SS("Failed to start audio. " << e.what()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void PiPedalModel::SetJackChannelSelection(int64_t clientId, const JackChannelSelection &channelSelection)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex); // copy atomically.
|
||||
this->storage.SetJackChannelSelection(channelSelection);
|
||||
|
||||
this->lv2Host.OnConfigurationChanged(jackConfiguration, channelSelection);
|
||||
}
|
||||
|
||||
RestartAudio(); // no lock to avoid mutex deadlock when reader thread is sending notifications..
|
||||
|
||||
this->FireChannelSelectionChanged(clientId);
|
||||
}
|
||||
|
||||
void PiPedalModel::FireChannelSelectionChanged(int64_t clientId)
|
||||
@@ -1024,7 +1087,6 @@ void PiPedalModel::UnmonitorPort(int64_t subscriptionHandle)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PiPedalModel::OnNotifyMonitorPort(const MonitorPortUpdate &update)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
@@ -1095,7 +1157,7 @@ void PiPedalModel::GetLv2Parameter(
|
||||
|
||||
BankIndex PiPedalModel::GetBankIndex() const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(const_cast<std::recursive_mutex&>(mutex));
|
||||
std::lock_guard<std::recursive_mutex> guard(const_cast<std::recursive_mutex &>(mutex));
|
||||
return storage.GetBanks();
|
||||
}
|
||||
|
||||
@@ -1137,16 +1199,17 @@ JackServerSettings PiPedalModel::GetJackServerSettings()
|
||||
|
||||
void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSettings)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
std::unique_lock<std::recursive_mutex> guard(mutex);
|
||||
|
||||
#if JACK_HOST
|
||||
if (!adminClient.CanUseShutdownClient())
|
||||
{
|
||||
throw PiPedalException("Can't change server settings when running a debug server.");
|
||||
}
|
||||
#endif
|
||||
|
||||
this->jackServerSettings = jackServerSettings;
|
||||
|
||||
|
||||
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us)
|
||||
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
|
||||
for (size_t i = 0; i < subscribers.size(); ++i)
|
||||
@@ -1160,19 +1223,25 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
|
||||
}
|
||||
delete[] t;
|
||||
|
||||
|
||||
#if ALSA_HOST
|
||||
storage.SetJackServerSettings(jackServerSettings);
|
||||
|
||||
FireJackConfigurationChanged(this->jackConfiguration);
|
||||
|
||||
guard.unlock();
|
||||
RestartAudio();
|
||||
|
||||
#endif
|
||||
#if JACK_HOST
|
||||
if (adminClient.CanUseShutdownClient())
|
||||
{
|
||||
|
||||
#if ALSA_HOST
|
||||
storage.SetJackServerSettings(jackServerSettings);
|
||||
#endif
|
||||
|
||||
// save the current (edited) preset now in case the service shutdown isn't clean.
|
||||
CurrentPreset currentPreset;
|
||||
currentPreset.modified_ = this->hasPresetChanged;
|
||||
currentPreset.preset_ = this->pedalBoard;
|
||||
storage.SaveCurrentPreset(currentPreset);
|
||||
|
||||
|
||||
this->jackConfiguration.SetIsRestarting(true);
|
||||
FireJackConfigurationChanged(this->jackConfiguration);
|
||||
this->jackHost->UpdateServerConfiguration(
|
||||
@@ -1195,8 +1264,8 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
|
||||
}
|
||||
else
|
||||
{
|
||||
// we now do a complete restart of the services,
|
||||
// so just sit tight and wait for the restart.
|
||||
// we now do a complete restart of the services,
|
||||
// so just sit tight and wait for the restart.
|
||||
#ifdef JUNK
|
||||
this->jackConfiguration.SetErrorStatus("");
|
||||
FireJackConfigurationChanged(this->jackConfiguration);
|
||||
@@ -1211,7 +1280,9 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
|
||||
#endif
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PiPedalModel::UpdateDefaults(PedalBoardItem *pedalBoardItem)
|
||||
@@ -1259,15 +1330,14 @@ void PiPedalModel::UpdateDefaults(PedalBoard *pedalBoard)
|
||||
}
|
||||
}
|
||||
|
||||
PluginPresets PiPedalModel::GetPluginPresets(const std::string& pluginUri)
|
||||
PluginPresets PiPedalModel::GetPluginPresets(const std::string &pluginUri)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
return storage.GetPluginPresets(pluginUri);
|
||||
|
||||
}
|
||||
|
||||
PluginUiPresets PiPedalModel::GetPluginUiPresets(const std::string& pluginUri)
|
||||
PluginUiPresets PiPedalModel::GetPluginUiPresets(const std::string &pluginUri)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
@@ -1332,7 +1402,7 @@ void PiPedalModel::DeleteMidiListeners(int64_t clientId)
|
||||
jackHost->SetListenForMidiEvent(midiEventListeners.size() != 0);
|
||||
}
|
||||
|
||||
void PiPedalModel::OnNotifyAtomOutput(uint64_t instanceId, const std::string&atomType,const std::string&atomJson)
|
||||
void PiPedalModel::OnNotifyAtomOutput(uint64_t instanceId, const std::string &atomType, const std::string &atomJson)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
@@ -1344,19 +1414,18 @@ void PiPedalModel::OnNotifyAtomOutput(uint64_t instanceId, const std::string&ato
|
||||
auto subscriber = this->GetNotificationSubscriber(listener.clientId);
|
||||
if (subscriber)
|
||||
{
|
||||
subscriber->OnNotifyAtomOutput(listener.clientHandle,instanceId,atomType,atomJson);
|
||||
} else {
|
||||
subscriber->OnNotifyAtomOutput(listener.clientHandle, instanceId, atomType, atomJson);
|
||||
}
|
||||
else
|
||||
{
|
||||
atomOutputListeners.erase(atomOutputListeners.begin() + i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
jackHost->SetListenForAtomOutput(atomOutputListeners.size() != 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
@@ -1370,7 +1439,9 @@ void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl)
|
||||
if (subscriber)
|
||||
{
|
||||
subscriber->OnNotifyMidiListener(listener.clientHandle, isNote, noteOrControl);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
midiEventListeners.erase(midiEventListeners.begin() + i);
|
||||
--i;
|
||||
}
|
||||
@@ -1400,7 +1471,7 @@ void PiPedalModel::CancelListenForMidiEvent(int64_t clientId, int64_t clientHand
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
for (size_t i = 0; i < midiEventListeners.size(); ++i)
|
||||
{
|
||||
const auto& listener = midiEventListeners[i];
|
||||
const auto &listener = midiEventListeners[i];
|
||||
if (listener.clientId == clientId && listener.clientHandle == clientHandle)
|
||||
{
|
||||
midiEventListeners.erase(midiEventListeners.begin() + i);
|
||||
@@ -1417,7 +1488,7 @@ void PiPedalModel::CancelListenForAtomOutputs(int64_t clientId, int64_t clientHa
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
for (size_t i = 0; i < atomOutputListeners.size(); ++i)
|
||||
{
|
||||
const auto&listener = atomOutputListeners[i];
|
||||
const auto &listener = atomOutputListeners[i];
|
||||
if (listener.clientId == clientId && listener.clientHandle == clientHandle)
|
||||
{
|
||||
atomOutputListeners.erase(atomOutputListeners.begin() + i);
|
||||
@@ -1434,24 +1505,24 @@ std::vector<AlsaDeviceInfo> PiPedalModel::GetAlsaDevices()
|
||||
return this->alsaDevices.GetAlsaDevices();
|
||||
}
|
||||
|
||||
const std::filesystem::path& PiPedalModel::GetWebRoot() const
|
||||
const std::filesystem::path &PiPedalModel::GetWebRoot() const
|
||||
{
|
||||
return webRoot;
|
||||
}
|
||||
|
||||
std::map<std::string,bool> PiPedalModel::GetFavorites() const
|
||||
std::map<std::string, bool> PiPedalModel::GetFavorites() const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(const_cast<std::recursive_mutex&>(mutex));
|
||||
|
||||
std::lock_guard<std::recursive_mutex> guard(const_cast<std::recursive_mutex &>(mutex));
|
||||
|
||||
return storage.GetFavorites();
|
||||
}
|
||||
void PiPedalModel::SetFavorites(const std::map<std::string,bool> &favorites)
|
||||
void PiPedalModel::SetFavorites(const std::map<std::string, bool> &favorites)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
storage.SetFavorites(favorites);
|
||||
|
||||
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us)
|
||||
std::vector<IPiPedalModelSubscriber*> t;
|
||||
std::vector<IPiPedalModelSubscriber *> t;
|
||||
t.reserve(this->subscribers.size());
|
||||
|
||||
for (size_t i = 0; i < subscribers.size(); ++i)
|
||||
|
||||
@@ -142,6 +142,8 @@ private:
|
||||
void UpdateRealtimeMonitorPortSubscriptions();
|
||||
void OnVuUpdate(const std::vector<VuUpdate>& updates);
|
||||
|
||||
void RestartAudio();
|
||||
|
||||
std::vector<RealtimeParameterRequest*> outstandingParameterRequests;
|
||||
|
||||
IPiPedalModelSubscriber*GetNotificationSubscriber(int64_t clientId);
|
||||
|
||||
+12
-12
@@ -836,10 +836,10 @@ public:
|
||||
std::string governor;
|
||||
pReader->read(&governor);
|
||||
std::string fromAddress = this->getFromAddress();
|
||||
if (!IsOnLocalSubnet(fromAddress))
|
||||
{
|
||||
throw PiPedalException("Permission denied. Not on local subnet.");
|
||||
}
|
||||
// if (!IsOnLocalSubnet(fromAddress))
|
||||
// {
|
||||
// throw PiPedalException("Permission denied. Not on local subnet.");
|
||||
// }
|
||||
this->model.SetGovernorSettings(governor);
|
||||
this->Reply(replyTo, "setGovernorSettings");
|
||||
}
|
||||
@@ -852,10 +852,10 @@ public:
|
||||
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.");
|
||||
}
|
||||
// if (!IsOnLocalSubnet(fromAddress))
|
||||
// {
|
||||
// throw PiPedalException("Permission denied. Not on local subnet.");
|
||||
// }
|
||||
|
||||
this->model.SetWifiConfigSettings(wifiConfigSettings);
|
||||
this->Reply(replyTo, "setWifiConfigSettings");
|
||||
@@ -873,10 +873,10 @@ public:
|
||||
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.");
|
||||
}
|
||||
// if (!IsOnLocalSubnet(fromAddress))
|
||||
// {
|
||||
// throw PiPedalException("Permission denied. Not on local subnet.");
|
||||
// }
|
||||
|
||||
this->model.SetWifiDirectConfigSettings(wifiDirectConfigSettings);
|
||||
this->Reply(replyTo, "setWifiDirectConfigSettings");
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "DeviceIdFile.hpp"
|
||||
#include "ServiceConfiguration.hpp"
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <grp.h>
|
||||
@@ -33,44 +33,45 @@
|
||||
using namespace pipedal;
|
||||
|
||||
using namespace std;
|
||||
using namespace config_serializer;
|
||||
|
||||
|
||||
const char DeviceIdFile::DEVICEID_FILE_NAME[] = "/etc/pipedal/config/device_uuid";
|
||||
const char ServiceConfiguration::DEVICEID_FILE_NAME[] = "/etc/pipedal/config/service.conf";
|
||||
|
||||
void DeviceIdFile::Load()
|
||||
#define SERIALIZER_ENTRY(MEMBER_NAME) \
|
||||
new ConfigSerializer<ServiceConfiguration,decltype(ServiceConfiguration::MEMBER_NAME)>(#MEMBER_NAME, &ServiceConfiguration::MEMBER_NAME, "")
|
||||
|
||||
|
||||
static p2p::autoptr_vector<ConfigSerializerBase<ServiceConfiguration>>
|
||||
deviceIdSerializers
|
||||
{
|
||||
SERIALIZER_ENTRY(uuid),
|
||||
SERIALIZER_ENTRY(deviceName),
|
||||
SERIALIZER_ENTRY(server_port)
|
||||
};
|
||||
|
||||
|
||||
ServiceConfiguration::ServiceConfiguration()
|
||||
: base(deviceIdSerializers)
|
||||
{
|
||||
{
|
||||
ifstream f;
|
||||
|
||||
f.open(DEVICEID_FILE_NAME);
|
||||
if (f.is_open())
|
||||
{
|
||||
std::getline(f, uuid);
|
||||
std::getline(f, deviceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
void DeviceIdFile::Save()
|
||||
{
|
||||
{
|
||||
std::string path = DEVICEID_FILE_NAME;
|
||||
ofstream f;
|
||||
f.open(path, ios_base::trunc);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw invalid_argument("Can't write to file " + path);
|
||||
}
|
||||
|
||||
void ServiceConfiguration::Load()
|
||||
{
|
||||
base::Load(DEVICEID_FILE_NAME,false);
|
||||
}
|
||||
void ServiceConfiguration::Save()
|
||||
{
|
||||
base::Save(DEVICEID_FILE_NAME);
|
||||
{
|
||||
struct group *group_;
|
||||
group_ = getgrnam("pipedal_d");
|
||||
if (group_ == nullptr)
|
||||
{
|
||||
throw logic_error("Group not found: pipedal_d");
|
||||
}
|
||||
std::ignore = chown(path.c_str(), -1, group_->gr_gid);
|
||||
std::ignore = chmod(path.c_str(), S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
|
||||
|
||||
f << uuid << endl;
|
||||
f << deviceName << endl;
|
||||
std::ignore = chown(DEVICEID_FILE_NAME,-1,group_->gr_gid);
|
||||
std::ignore = chmod(DEVICEID_FILE_NAME, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
|
||||
}
|
||||
}
|
||||
@@ -25,10 +25,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "ConfigSerializer.h"
|
||||
|
||||
namespace pipedal {
|
||||
class DeviceIdFile {
|
||||
using namespace config_serializer;
|
||||
|
||||
class ServiceConfiguration: protected ConfigSerializable<ServiceConfiguration> {
|
||||
public:
|
||||
using base = ConfigSerializable<ServiceConfiguration>;
|
||||
|
||||
ServiceConfiguration();
|
||||
|
||||
static const char DEVICEID_FILE_NAME[];
|
||||
|
||||
void Load();
|
||||
@@ -36,6 +43,7 @@ namespace pipedal {
|
||||
|
||||
|
||||
std::string uuid;
|
||||
std::string deviceName;
|
||||
std::string deviceName = "PiPedal";
|
||||
uint32_t server_port = 80;
|
||||
};
|
||||
}
|
||||
+13
-6
@@ -21,7 +21,7 @@
|
||||
#include "ss.hpp"
|
||||
|
||||
#include "P2pConfigFiles.hpp"
|
||||
#include "DeviceIdFile.hpp"
|
||||
#include "ServiceConfiguration.hpp"
|
||||
#include "SetWifiConfig.hpp"
|
||||
#include "PiPedalException.hpp"
|
||||
#include "SystemConfigFile.hpp"
|
||||
@@ -410,6 +410,16 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
|
||||
{
|
||||
cout << "Disabling P2P" << endl;
|
||||
UninstallP2p();
|
||||
|
||||
ServiceConfiguration deviceIdFile;
|
||||
deviceIdFile.Load();
|
||||
deviceIdFile.deviceName = settings.hotspotName_;
|
||||
deviceIdFile.Save();
|
||||
|
||||
|
||||
// Announce new mDNS service with potentially new deviceName.
|
||||
sysExec(SYSTEMCTL_BIN " restart pipedald");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -417,7 +427,7 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
|
||||
settings.Save();
|
||||
// ******************* device_uuid
|
||||
|
||||
DeviceIdFile deviceIdFile;
|
||||
ServiceConfiguration deviceIdFile;
|
||||
deviceIdFile.Load();
|
||||
deviceIdFile.deviceName = settings.hotspotName_;
|
||||
deviceIdFile.Save();
|
||||
@@ -502,12 +512,9 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
|
||||
sysExec(SYSTEMCTL_BIN " daemon-reload");
|
||||
|
||||
sysExec(SYSTEMCTL_BIN " stop pipedal_p2pd");
|
||||
sysExec(SYSTEMCTL_BIN " stop dnsmasq");
|
||||
sysExec(SYSTEMCTL_BIN " stop wpa_supplicant");
|
||||
sysExec(SYSTEMCTL_BIN " restart dhcpcd");
|
||||
sysExec(SYSTEMCTL_BIN " unmask wpa_supplicant");
|
||||
sysExec(SYSTEMCTL_BIN " start wpa_supplicant");
|
||||
sysExec(SYSTEMCTL_BIN " enable wpa_supplicant");
|
||||
sysExec(SYSTEMCTL_BIN " enable wpa_supplicant"); // ?? Not sure that's still right. :-/
|
||||
sysExec(SYSTEMCTL_BIN " start dnsmasq");
|
||||
sysExec(SYSTEMCTL_BIN " enable dnsmasq");
|
||||
sysExec(SYSTEMCTL_BIN " start pipedal_p2pd");
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <stdexcept>
|
||||
#include <random>
|
||||
#include "ConfigUtil.hpp"
|
||||
#include "DeviceIdFile.hpp"
|
||||
#include "ServiceConfiguration.hpp"
|
||||
#include "WriteTemplateFile.hpp"
|
||||
#include "ss.hpp"
|
||||
|
||||
@@ -169,7 +169,7 @@ void WifiDirectConfigSettings::Load()
|
||||
this->channel_ = "6";
|
||||
}
|
||||
try {
|
||||
DeviceIdFile deviceIdFile;
|
||||
ServiceConfiguration deviceIdFile;
|
||||
deviceIdFile.Load();
|
||||
if (deviceIdFile.deviceName == "")
|
||||
{
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
namespace p2p {
|
||||
|
||||
template <typename T> class autoptr_vector
|
||||
{
|
||||
std::vector<T*> v;
|
||||
private:
|
||||
// no copy no move.
|
||||
autoptr_vector(const autoptr_vector<T> &) = delete;
|
||||
autoptr_vector(autoptr_vector<T> && ) = delete;
|
||||
public:
|
||||
using iterator = std::vector<T*>::const_iterator;
|
||||
|
||||
autoptr_vector() { }
|
||||
autoptr_vector(std::initializer_list<T*> l)
|
||||
:v(l)
|
||||
{
|
||||
|
||||
}
|
||||
~autoptr_vector() {
|
||||
for (T *i: v)
|
||||
{
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
void push_back(T*value) { v.push_back(value);}
|
||||
|
||||
iterator begin() const { return v.begin(); }
|
||||
iterator end() const { return v.end(); }
|
||||
|
||||
T*operator[](size_t index) const { return v[index]; }
|
||||
|
||||
size_t size() const { return v.size(); }
|
||||
};
|
||||
}
|
||||
+2
-2
@@ -23,7 +23,7 @@
|
||||
#include "BeastServer.hpp"
|
||||
#include <iostream>
|
||||
#include "Lv2Log.hpp"
|
||||
#include "DeviceIdFile.hpp"
|
||||
#include "ServiceConfiguration.hpp"
|
||||
#include "AvahiService.hpp"
|
||||
|
||||
#include "PiPedalSocket.hpp"
|
||||
@@ -428,7 +428,7 @@ public:
|
||||
#define LINK_LOCAL_WEB_SOCKET 1
|
||||
#if LINK_LOCAL_WEB_SOCKET
|
||||
std::string webSocketAddress = GetLinkLocalAddress(fromAddress);
|
||||
Lv2Log::info(SS("Web Socket Adddress: " << webSocketAddress << ":" << portNumber));
|
||||
Lv2Log::info(SS("Web Socket Address: " << webSocketAddress << ":" << portNumber));
|
||||
#else
|
||||
std::string webSocketAddress = "*";
|
||||
#endif
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
[Unit]
|
||||
Description=PiPedal P2P Session Manager
|
||||
After=network.target
|
||||
After=nss-lookup.target
|
||||
After=wpa_supplicant.service
|
||||
After=dhcpcd.service
|
||||
StartLimitIntervalSec=300
|
||||
StartLimitBurst=8
|
||||
|
||||
[Service]
|
||||
ExecStart=${COMMAND} --systemd --log-level debug
|
||||
Type=notify
|
||||
WorkingDirectory=/var/pipedal
|
||||
TimeoutStopSec=5
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -7,6 +7,7 @@ After=local-fs.target
|
||||
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
Type=notify
|
||||
LimitMEMLOCK=infinity
|
||||
LimitRTPRIO=95
|
||||
|
||||
Reference in New Issue
Block a user