Jack configuration (alpha)
This commit is contained in:
+4
-2
@@ -118,6 +118,7 @@ set (PIPEDAL_SOURCES
|
||||
WifiChannels.hpp
|
||||
WifiChannels.cpp
|
||||
RegDb.cpp RegDb.hpp
|
||||
PiPedalAlsa.hpp PiPedalAlsa.cpp
|
||||
)
|
||||
|
||||
configure_file(config.hpp.in config.hpp)
|
||||
@@ -136,7 +137,7 @@ target_include_directories(pipedald PRIVATE
|
||||
${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${LIBNL3_INCLUDE_DIRS} ${LVDEV_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(pipedald PRIVATE pthread atomic stdc++fs
|
||||
target_link_libraries(pipedald PRIVATE pthread atomic stdc++fs asound
|
||||
${LILV_0_LIBRARIES} ${JACK_LIBRARIES} ${LIBNL3_LIBRARIES} systemd
|
||||
)
|
||||
|
||||
@@ -144,6 +145,7 @@ target_link_libraries(pipedald PRIVATE pthread atomic stdc++fs
|
||||
add_executable(pipedaltest ${PIPEDAL_SOURCES} testMain.cpp
|
||||
jsonTest.cpp
|
||||
WifiChannelsTest.cpp
|
||||
PiPedalAlsaTest.cpp
|
||||
|
||||
SystemConfigFile.hpp SystemConfigFile.cpp
|
||||
SystemConfigFileTest.cpp
|
||||
@@ -160,7 +162,7 @@ target_include_directories(pipedaltest PRIVATE
|
||||
${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${LIBNL3_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(pipedaltest PRIVATE pthread atomic stdc++fs
|
||||
target_link_libraries(pipedaltest PRIVATE pthread atomic stdc++fs asound
|
||||
${LILV_0_LIBRARIES} ${JACK_LIBRARIES} ${LIBNL3_LIBRARIES} systemd
|
||||
)
|
||||
|
||||
|
||||
+128
-26
@@ -18,6 +18,8 @@
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include "CommandLineParser.hpp"
|
||||
#include "PiPedalException.hpp"
|
||||
#include <filesystem>
|
||||
@@ -27,27 +29,29 @@
|
||||
#include "SetWifiConfig.hpp"
|
||||
#include "SysExec.hpp"
|
||||
#include <sys/wait.h>
|
||||
#include <pwd.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace pipedal;
|
||||
|
||||
#define SERVICE_ACCOUNT_NAME "pipedal_d"
|
||||
#define SERVICE_GROUP_NAME "pipedal_d"
|
||||
#define JACK_SERVICE_ACCOUNT_NAME "jack"
|
||||
#define JACK_SERVICE_GROUP_NAME "jack"
|
||||
#define AUDIO_SERVICE_GROUP_NAME "audio"
|
||||
|
||||
#define SYSTEMCTL_BIN "/usr/bin/systemctl"
|
||||
#define GROUPADD_BIN "/usr/sbin/groupadd"
|
||||
#define USERADD_BIN "/usr/sbin/useradd"
|
||||
#define USERADD_BIN "/usr/sbin/useradd"
|
||||
#define USERMOD_BIN "/usr/sbin/usermod"
|
||||
#define CHGRP_BIN "/usr/bin/chgrp"
|
||||
#define CHOWN_BIN "/usr/bin/chown"
|
||||
#define CHMOD_BIN "/usr/bin/chmod"
|
||||
|
||||
|
||||
#define SERVICE_PATH "/usr/lib/systemd/system"
|
||||
#define NATIVE_SERVICE "pipedald"
|
||||
#define SHUTDOWN_SERVICE "pipedalshutdownd"
|
||||
// #define REACT_SERVICE "pipedal_react"
|
||||
|
||||
#define JACK_SERVICE "jack"
|
||||
|
||||
std::filesystem::path GetServiceFileName(const std::string &serviceName)
|
||||
{
|
||||
@@ -113,6 +117,14 @@ void StopService()
|
||||
cout << "Error: Failed to stop the " SHUTDOWN_SERVICE " service.";
|
||||
}
|
||||
}
|
||||
|
||||
void Uninstall() {
|
||||
StopService();
|
||||
DisableService();
|
||||
system(SYSTEMCTL_BIN " stop jack");
|
||||
system(SYSTEMCTL_BIN " disable jack");
|
||||
}
|
||||
|
||||
void StartService()
|
||||
{
|
||||
|
||||
@@ -132,8 +144,98 @@ void RestartService()
|
||||
StartService();
|
||||
}
|
||||
|
||||
static bool userExists(const char *userName)
|
||||
{
|
||||
struct passwd pwd;
|
||||
struct passwd *result;
|
||||
char *buf;
|
||||
size_t bufsize;
|
||||
int s;
|
||||
|
||||
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
if (bufsize == -1) /* Value was indeterminate */
|
||||
bufsize = 16384; /* Should be more than enough */
|
||||
|
||||
buf = (char *)malloc(bufsize);
|
||||
if (buf == NULL)
|
||||
{
|
||||
perror("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
s = getpwnam_r(userName, &pwd, buf, bufsize, &result);
|
||||
if (result == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void InstallLimits()
|
||||
{
|
||||
if (SysExec(GROUPADD_BIN " -f " AUDIO_SERVICE_GROUP_NAME) != EXIT_SUCCESS)
|
||||
{
|
||||
throw PiPedalException("Failed to create audio service group.");
|
||||
}
|
||||
|
||||
std::filesystem::path limitsConfig = "/usr/security/audio.conf";
|
||||
|
||||
if (!std::filesystem::exists(limitsConfig))
|
||||
{
|
||||
ofstream output(limitsConfig);
|
||||
output << "# Realtime priority group used by pipedal/jack daemon" << endl;
|
||||
output << "@audio - rtprio 95" << endl;
|
||||
output << "@audio - memlock unlimited" << endl;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void MaybeStartJackService() {
|
||||
std::filesystem::path drcFile = "/etc/jackdrc";
|
||||
|
||||
if (std::filesystem::exists(drcFile) && std::filesystem::file_size(drcFile) != 0) {
|
||||
system(SYSTEMCTL_BIN " start jack");
|
||||
} else {
|
||||
system(SYSTEMCTL_BIN " mask jack");
|
||||
}
|
||||
}
|
||||
void InstallJackService()
|
||||
{
|
||||
InstallLimits();
|
||||
if (SysExec(GROUPADD_BIN " -f " JACK_SERVICE_GROUP_NAME) != EXIT_SUCCESS)
|
||||
{
|
||||
throw PiPedalException("Failed to create jack service group.");
|
||||
}
|
||||
if (!userExists(JACK_SERVICE_ACCOUNT_NAME))
|
||||
{
|
||||
if (SysExec(USERADD_BIN " " JACK_SERVICE_ACCOUNT_NAME " -g " JACK_SERVICE_GROUP_NAME " -M -N -r") != EXIT_SUCCESS)
|
||||
{
|
||||
// throw PiPedalException("Failed to create service account.");
|
||||
}
|
||||
// lock account for login.
|
||||
SysExec("passwd -l " JACK_SERVICE_ACCOUNT_NAME);
|
||||
}
|
||||
|
||||
// Add to audio groups.
|
||||
SysExec(USERMOD_BIN " -a -G " JACK_SERVICE_GROUP_NAME " " JACK_SERVICE_ACCOUNT_NAME);
|
||||
SysExec(USERMOD_BIN " -a -G" AUDIO_SERVICE_GROUP_NAME " " JACK_SERVICE_ACCOUNT_NAME);
|
||||
|
||||
// deploy the systemd service file
|
||||
std::map<std::string,std::string> map; // nothing to customize.
|
||||
|
||||
WriteTemplateFile(map, std::filesystem::path("/etc/pipedal/templateJack.service"), GetServiceFileName(JACK_SERVICE));
|
||||
|
||||
|
||||
MaybeStartJackService();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Install(const std::filesystem::path &programPrefix, const std::string endpointAddress)
|
||||
{
|
||||
|
||||
InstallJackService();
|
||||
auto endpos = endpointAddress.find_last_of(':');
|
||||
if (endpos == string::npos)
|
||||
{
|
||||
@@ -168,13 +270,15 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
|
||||
{
|
||||
throw PiPedalException("Failed to create service group.");
|
||||
}
|
||||
if (SysExec(USERADD_BIN " " SERVICE_ACCOUNT_NAME " -g " SERVICE_GROUP_NAME " -M -N -r") != EXIT_SUCCESS)
|
||||
if (!userExists(SERVICE_ACCOUNT_NAME))
|
||||
{
|
||||
// throw PiPedalException("Failed to create service account.");
|
||||
if (SysExec(USERADD_BIN " " SERVICE_ACCOUNT_NAME " -g " SERVICE_GROUP_NAME " -M -N -r") != EXIT_SUCCESS)
|
||||
{
|
||||
// throw PiPedalException("Failed to create service account.");
|
||||
}
|
||||
// lock account for login.
|
||||
SysExec("passwd -l " SERVICE_ACCOUNT_NAME);
|
||||
}
|
||||
// lock account for login.
|
||||
SysExec("passwd -l " SERVICE_ACCOUNT_NAME);
|
||||
|
||||
|
||||
// Add to audio groups.
|
||||
SysExec(USERMOD_BIN " -a -G jack " SERVICE_ACCOUNT_NAME);
|
||||
@@ -278,8 +382,9 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
|
||||
cout << "Complete" << endl;
|
||||
}
|
||||
|
||||
int SudoExec(char**argv) {
|
||||
int pbPid;
|
||||
int SudoExec(char **argv)
|
||||
{
|
||||
int pbPid;
|
||||
int returnValue = 0;
|
||||
|
||||
if ((pbPid = fork()) == 0)
|
||||
@@ -295,7 +400,6 @@ int SudoExec(char**argv) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CommandLineParser parser;
|
||||
@@ -328,7 +432,7 @@ int main(int argc, char **argv)
|
||||
{
|
||||
parser.Parse(argc, (const char **)argv);
|
||||
|
||||
int actionCount = install + uninstall + stop + start + enable + disable + enable_ap + disable_ap;
|
||||
int actionCount = install + uninstall + stop + start + enable + disable + enable_ap + disable_ap + restart;
|
||||
if (actionCount > 1)
|
||||
{
|
||||
throw PiPedalException("Please provide only one action.");
|
||||
@@ -364,7 +468,8 @@ int main(int argc, char **argv)
|
||||
|
||||
if (help || helpError)
|
||||
{
|
||||
if (helpError) cout << endl;
|
||||
if (helpError)
|
||||
cout << endl;
|
||||
|
||||
cout << "pipedalconfig - Post-install configuration for pipdeal" << endl
|
||||
<< "Copyright (c) 2021 Robin Davies. All rights reserved." << endl
|
||||
@@ -409,7 +514,7 @@ int main(int argc, char **argv)
|
||||
<< "Description:" << endl
|
||||
<< " pipedalconfig performs post-install configuration of pipedal." << endl
|
||||
<< endl;
|
||||
exit(helpError? EXIT_FAILURE: EXIT_SUCCESS);
|
||||
exit(helpError ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
if (portOption.size() != 0 && !install)
|
||||
{
|
||||
@@ -421,16 +526,16 @@ int main(int argc, char **argv)
|
||||
if (uid != 0 && !nopkexec)
|
||||
{
|
||||
// re-execute with PKEXEC in order to prompt form SUDO credentials.
|
||||
std::vector<char*> args;
|
||||
std::vector<char *> args;
|
||||
std::string pkexec = "/usr/bin/pkexec"; // staged because "ISO C++ forbids converting a string constant to std::vector<char*>::value_type"(!)
|
||||
args.push_back((char*)(pkexec.c_str()));
|
||||
args.push_back((char *)(pkexec.c_str()));
|
||||
|
||||
std::filesystem::path path = std::filesystem::absolute(argv[0]);
|
||||
std::string sPath = path;
|
||||
args.push_back((char*)path.c_str());
|
||||
args.push_back((char *)path.c_str());
|
||||
for (int arg = 1; arg < argc; ++arg)
|
||||
{
|
||||
args.push_back((char*)argv[arg]);
|
||||
args.push_back((char *)argv[arg]);
|
||||
}
|
||||
|
||||
std::string prefixArg; // lifetime management for the prefix arguments
|
||||
@@ -440,18 +545,14 @@ int main(int argc, char **argv)
|
||||
{
|
||||
std::filesystem::path prefix = std::filesystem::path(argv[0]).parent_path().parent_path();
|
||||
prefixOptionArg = "-prefix";
|
||||
args.push_back((char*)(prefixOptionArg.c_str()));
|
||||
args.push_back((char *)(prefixOptionArg.c_str()));
|
||||
prefixArg = prefix;
|
||||
args.push_back((char*)prefixArg.c_str());
|
||||
args.push_back((char *)prefixArg.c_str());
|
||||
}
|
||||
args.push_back(nullptr);
|
||||
|
||||
char**rawArgv = &args[0];
|
||||
char **rawArgv = &args[0];
|
||||
return SudoExec(rawArgv);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
try
|
||||
@@ -488,6 +589,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
else if (uninstall)
|
||||
{
|
||||
Uninstall();
|
||||
}
|
||||
else if (stop)
|
||||
{
|
||||
|
||||
@@ -55,6 +55,7 @@ namespace pipedal
|
||||
size_t GetBlockLength() const { return blockLength_; }
|
||||
size_t GetMidiBufferSize() const { return midiBufferSize_;}
|
||||
double GetMaxAllowedMidiDelta() const { return maxAllowedMidiDelta_; }
|
||||
void SetErrorStatus(const std::string&message) { this->errorStatus_ = message; }
|
||||
|
||||
const std::vector<std::string> &GetInputAudioPorts() const { return inputAudioPorts_; }
|
||||
const std::vector<std::string> &GetOutputAudioPorts() const { return outputAudioPorts_; }
|
||||
|
||||
+15
-6
@@ -68,6 +68,7 @@ namespace pipedal
|
||||
class JackHostImpl : public JackHost
|
||||
{
|
||||
private:
|
||||
|
||||
std::recursive_mutex mutex;
|
||||
int64_t overrunGracePeriodSamples = 0;
|
||||
|
||||
@@ -145,7 +146,7 @@ private:
|
||||
auto port = outputPorts[i];
|
||||
if (port != nullptr)
|
||||
{
|
||||
jack_disconnect(client, channelSelection.getOutputAudioPorts()[i].c_str(), jack_port_name(port));
|
||||
jack_disconnect(client, jack_port_name(port),channelSelection.getOutputAudioPorts()[i].c_str());
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < midiInputPorts.size(); ++i)
|
||||
@@ -906,6 +907,7 @@ public:
|
||||
|
||||
virtual void Open(const JackChannelSelection &channelSelection)
|
||||
{
|
||||
|
||||
std::lock_guard guard(mutex);
|
||||
|
||||
this->currentSample = 0;
|
||||
@@ -1262,11 +1264,18 @@ private:
|
||||
{
|
||||
this_->restarting = true;
|
||||
this_->Close();
|
||||
ShutdownClient::SetJackServerConfiguration(jackServerSettings);
|
||||
this_->Open(this_->channelSelection);
|
||||
this_->restarting = false;
|
||||
onComplete(true, "");
|
||||
isComplete = true;
|
||||
try {
|
||||
ShutdownClient::SetJackServerConfiguration(jackServerSettings);
|
||||
this_->Open(this_->channelSelection);
|
||||
this_->restarting = false;
|
||||
onComplete(true, "");
|
||||
isComplete = true;
|
||||
} catch (const std::exception &e)
|
||||
{
|
||||
onComplete(false,e.what());
|
||||
this_->restarting = false;
|
||||
isComplete = true;
|
||||
}
|
||||
}
|
||||
static void ThreadProc_(RestartThread *this_)
|
||||
{
|
||||
|
||||
+1
-1
@@ -27,6 +27,7 @@
|
||||
#include "json.hpp"
|
||||
#include "JackServerSettings.hpp"
|
||||
#include <functional>
|
||||
#include "PiPedalAlsa.hpp"
|
||||
|
||||
namespace pipedal {
|
||||
|
||||
@@ -118,7 +119,6 @@ public:
|
||||
class IHost;
|
||||
|
||||
class JackHost {
|
||||
|
||||
protected:
|
||||
JackHost() { }
|
||||
public:
|
||||
|
||||
+110
-79
@@ -23,6 +23,9 @@
|
||||
#include "JackServerSettings.hpp"
|
||||
#include <fstream>
|
||||
#include "PiPedalException.hpp"
|
||||
#include <string>
|
||||
#include "unistd.h"
|
||||
#include <filesystem>
|
||||
|
||||
#define DRC_FILENAME "/etc/jackdrc"
|
||||
|
||||
@@ -55,24 +58,46 @@ static std::vector<std::string> SplitArgs(const char *szBuff)
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint64_t GetJackArg(const std::vector<std::string> &args, const char *shortOption, const char *longOption)
|
||||
static std::string GetJackStringArg(const std::vector<std::string> &args, const std::string&shortOption, const std::string&longOption)
|
||||
{
|
||||
int pos = -1;
|
||||
std::string strVal;
|
||||
for (size_t i = 1; i < args.size(); ++i)
|
||||
{
|
||||
if (args[i] == shortOption || args[i] == longOption)
|
||||
{
|
||||
pos = i;
|
||||
auto pos = args[i].rfind(longOption,0);
|
||||
if (pos != std::string::npos) {
|
||||
if (args[i].length() == longOption.length())
|
||||
{
|
||||
if (i == args.size()-1) {
|
||||
throw PiPedalException("Can't read Jack configuration.");
|
||||
}
|
||||
strVal = args[i+1];
|
||||
} else {
|
||||
strVal = args[i].substr(longOption.length());
|
||||
}
|
||||
break;
|
||||
}
|
||||
pos = args[i].rfind(shortOption,0);
|
||||
if (pos != std::string::npos) {
|
||||
if (args[i].length() == shortOption.length())
|
||||
{
|
||||
if (i == args.size()-1) {
|
||||
throw PiPedalException("Can't read Jack configuration.");
|
||||
}
|
||||
strVal = args[i+1];
|
||||
} else {
|
||||
strVal = args[i].substr(shortOption.length());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pos == -1 || pos == args.size() - 1)
|
||||
{
|
||||
throw PiPedalException("Can't read Jack configuration.");
|
||||
}
|
||||
return strVal;
|
||||
}
|
||||
static std::int32_t GetJackArg(const std::vector<std::string> &args, const std::string&shortOption, const std::string&longOption)
|
||||
{
|
||||
std::string strVal = GetJackStringArg(args,shortOption,longOption);
|
||||
try
|
||||
{
|
||||
unsigned long value = std::stoul(args[pos + 1]);
|
||||
unsigned long value = std::stoul(strVal);
|
||||
return value;
|
||||
}
|
||||
catch (const std::exception &)
|
||||
@@ -80,35 +105,13 @@ static uint64_t GetJackArg(const std::vector<std::string> &args, const char *sho
|
||||
throw PiPedalException("Can't read Jack configuration.");
|
||||
}
|
||||
}
|
||||
static void SetJackArg(std::vector<std::string> &args, const char *shortOption, const char *longOption,uint64_t value)
|
||||
{
|
||||
int pos = -1;
|
||||
for (size_t i = 1; i < args.size(); ++i)
|
||||
{
|
||||
if (args[i] == shortOption || args[i] == longOption)
|
||||
{
|
||||
pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pos == -1 || pos == args.size() - 1)
|
||||
{
|
||||
throw PiPedalException("Can't read Jack configuration.");
|
||||
}
|
||||
stringstream s;
|
||||
s << value;
|
||||
args[pos+1] = s.str();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void JackServerSettings::ReadJackConfiguration()
|
||||
{
|
||||
this->valid_ = false;
|
||||
|
||||
char firstLine[1024];
|
||||
|
||||
std::string lastLine;
|
||||
{
|
||||
ifstream input(DRC_FILENAME);
|
||||
|
||||
@@ -120,22 +123,32 @@ void JackServerSettings::ReadJackConfiguration()
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (input.eof())
|
||||
{
|
||||
Lv2Log::error("Premature end of file in " DRC_FILENAME);
|
||||
return;
|
||||
std::string line;
|
||||
std::getline(input,line);
|
||||
if (line.length() != 0) {
|
||||
lastLine = line;
|
||||
}
|
||||
input.getline(firstLine, sizeof(firstLine));
|
||||
if (firstLine[0] != '#')
|
||||
if (input.eof()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
std::vector < std::string> argv = SplitArgs(firstLine);
|
||||
this->bufferSize_ = GetJackArg(argv, "-p", "-period");
|
||||
this->numberOfBuffers_ = (uint32_t)GetJackArg(argv, "-n", "-nperiods");
|
||||
this->sampleRate_ = (uint32_t)GetJackArg(argv, "-r", "-rate");
|
||||
try
|
||||
{
|
||||
std::vector<std::string> argv = SplitArgs(lastLine.c_str());
|
||||
for (auto i = argv.begin(); i != argv.end(); ++i)
|
||||
{
|
||||
if ((*i) == "-dalsa") {
|
||||
argv.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->bufferSize_ = GetJackArg(argv, "-p", "--period");
|
||||
this->numberOfBuffers_ = (uint32_t)GetJackArg(argv, "-n", "--nperiods");
|
||||
this->sampleRate_ = (uint32_t)GetJackArg(argv, "-r", "--rate");
|
||||
this->alsaDevice_ = GetJackStringArg(argv,"-d", "--device");
|
||||
valid_ = true;
|
||||
} catch (std::exception &)
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
Lv2Log::error("Can't parse " DRC_FILENAME);
|
||||
}
|
||||
@@ -147,10 +160,10 @@ void JackServerSettings::Write()
|
||||
this->valid_ = false;
|
||||
|
||||
std::vector<std::string> precedingLines;
|
||||
std::vector < std::string> argv;
|
||||
std::vector<std::string> argv;
|
||||
std::string lastLine;
|
||||
|
||||
{
|
||||
char firstLine[1024];
|
||||
if (std::filesystem::exists(DRC_FILENAME)) {
|
||||
ifstream input(DRC_FILENAME);
|
||||
|
||||
if (!input.is_open())
|
||||
@@ -164,46 +177,64 @@ void JackServerSettings::Write()
|
||||
if (input.eof())
|
||||
{
|
||||
Lv2Log::error("Premature end of file in " DRC_FILENAME);
|
||||
return;
|
||||
}
|
||||
input.getline(firstLine, sizeof(firstLine));
|
||||
if (firstLine[0] != '#')
|
||||
break;
|
||||
precedingLines.push_back(firstLine);
|
||||
}
|
||||
std::getline(input,lastLine);
|
||||
precedingLines.push_back(lastLine);
|
||||
if (input.eof())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// set new values for arguments.
|
||||
argv = SplitArgs(firstLine);
|
||||
try {
|
||||
SetJackArg(argv, "-p", "-period", this->bufferSize_);
|
||||
SetJackArg(argv, "-n", "-nperiods", this->numberOfBuffers_);
|
||||
SetJackArg(argv, "-r", "-rate",this->sampleRate_);
|
||||
} catch (std::exception &)
|
||||
// erase blank lines at the end.
|
||||
while (precedingLines.size() != 0 && precedingLines[precedingLines.size()-1] == "")
|
||||
{
|
||||
Lv2Log::error("Can't parse " DRC_FILENAME);
|
||||
return;
|
||||
precedingLines.erase(precedingLines.begin()+precedingLines.size()-1);
|
||||
}
|
||||
}
|
||||
// erase the last line, which should contain the command invocation.
|
||||
if (precedingLines.size() != 0)
|
||||
{
|
||||
precedingLines.erase(precedingLines.begin()+precedingLines.size()-1);
|
||||
}
|
||||
}
|
||||
// write to the output.
|
||||
try {
|
||||
try
|
||||
{
|
||||
ofstream output(DRC_FILENAME);
|
||||
if (!output.is_open())
|
||||
{
|
||||
throw PiPedalException("Can't write " DRC_FILENAME);
|
||||
}
|
||||
for (auto line: precedingLines)
|
||||
if (precedingLines.size() == 0)
|
||||
{
|
||||
// jack1 incantation for promiscuous servers.
|
||||
output << "#!/bin/sh" <<endl;
|
||||
output << "export JACK_PROMISCUOUS_SERVER=" << endl;
|
||||
output << "export JACK_NO_AUDIO_RESERVATION=" << endl;
|
||||
output << "umask 0" << endl;
|
||||
}
|
||||
for (auto line : precedingLines)
|
||||
{
|
||||
output << line << endl;
|
||||
}
|
||||
for (size_t i = 0; i < argv.size(); ++i)
|
||||
// the style used by qjackctl. :-/
|
||||
output << "/usr/bin/jackd "
|
||||
<< "-R -P90"
|
||||
<< " -driver alsa -d" << this->alsaDevice_
|
||||
<< " -r" << this->sampleRate_
|
||||
<< " -p" << this->bufferSize_
|
||||
<< " -n" << this->numberOfBuffers_ << " -Xseq"
|
||||
<< endl;
|
||||
system("/usr/bin/chmod 755 " DRC_FILENAME);
|
||||
system("pulseaudio --kill");
|
||||
system("/usr/bin/systemctl enable jack");
|
||||
if (system("/usr/bin/systemctl restart jack") != 0)
|
||||
{
|
||||
if (i != 0) {
|
||||
output << " ";
|
||||
}
|
||||
output << argv[i];
|
||||
throw PiPedalException("Failed to start jack audio service.");
|
||||
}
|
||||
output << endl;
|
||||
} catch (const std::exception &e) {
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
stringstream s;
|
||||
s << "jack - " << e.what();
|
||||
Lv2Log::error(s.str());
|
||||
@@ -211,10 +242,10 @@ void JackServerSettings::Write()
|
||||
}
|
||||
|
||||
JSON_MAP_BEGIN(JackServerSettings)
|
||||
JSON_MAP_REFERENCE(JackServerSettings,valid)
|
||||
JSON_MAP_REFERENCE(JackServerSettings,rebootRequired)
|
||||
JSON_MAP_REFERENCE(JackServerSettings,sampleRate)
|
||||
JSON_MAP_REFERENCE(JackServerSettings,bufferSize)
|
||||
JSON_MAP_REFERENCE(JackServerSettings,numberOfBuffers)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, valid)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, rebootRequired)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, alsaDevice)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, sampleRate)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, bufferSize)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, numberOfBuffers)
|
||||
JSON_MAP_END()
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace pipedal {
|
||||
class JackServerSettings {
|
||||
bool valid_ = false;
|
||||
bool rebootRequired_ = false;
|
||||
std::string alsaDevice_;
|
||||
uint64_t sampleRate_ = 0;
|
||||
uint32_t bufferSize_ = 0;
|
||||
uint32_t numberOfBuffers_ = 0;
|
||||
@@ -36,6 +37,7 @@ namespace pipedal {
|
||||
uint64_t GetSampleRate() const { return sampleRate_; }
|
||||
uint32_t GetBufferSize() const { return bufferSize_; }
|
||||
uint32_t GetNumberOfBuffers() const { return numberOfBuffers_; }
|
||||
const std::string&GetAlsaDevice() const { return alsaDevice_; }
|
||||
|
||||
void ReadJackConfiguration();
|
||||
|
||||
|
||||
+20
-2
@@ -22,6 +22,7 @@
|
||||
#include "Locale.hpp"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "Lv2Log.hpp"
|
||||
|
||||
|
||||
// Must be UNICODE. Should reflect system locale. (.e.g en-US.UTF8, de-de.UTF8)
|
||||
@@ -36,12 +37,29 @@ static const char*getLocale(const char*localeEnvironmentVariable)
|
||||
{
|
||||
result = getenv("LC_ALL");
|
||||
}
|
||||
|
||||
if (result == nullptr) {
|
||||
result = "en_US.UTF-8";
|
||||
}
|
||||
std::stringstream s;
|
||||
s << "Locale: " << result;
|
||||
Lv2Log::error(s.str());
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::locale collationLocale(getLocale("LC_COLLATION"));
|
||||
const std::collate<char>& Locale::collation = std::use_facet<std::collate<char> >(collationLocale);
|
||||
|
||||
void Locale::setDefaultLocale() {
|
||||
const char* locale = getLocale("LC_ALL");
|
||||
try {
|
||||
setlocale(LC_ALL,locale);
|
||||
} catch (const std::exception&)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "Failed to set default locale (" << locale << "). Defaulting to en_US.";
|
||||
Lv2Log::error(s.str());
|
||||
|
||||
std::setlocale(LC_ALL,"en_US.UTF-8");
|
||||
}
|
||||
}
|
||||
const std::collate<char>& Locale::collation() { return std::use_facet<std::collate<char> >(std::locale()); }
|
||||
|
||||
|
||||
+3
-1
@@ -25,6 +25,8 @@ namespace pipedal {
|
||||
|
||||
class Locale {
|
||||
public:
|
||||
static const std::collate<char>& collation;
|
||||
static void setDefaultLocale();
|
||||
|
||||
static const std::collate<char>& collation();
|
||||
};
|
||||
}
|
||||
|
||||
+7
-4
@@ -394,13 +394,14 @@ void Lv2Host::Load(const char *lv2Path)
|
||||
}
|
||||
}
|
||||
|
||||
auto compare = [](
|
||||
const auto &collation = std::use_facet<std::collate<char> >(std::locale());
|
||||
auto compare = [&collation](
|
||||
const std::shared_ptr<Lv2PluginInfo> &left,
|
||||
const std::shared_ptr<Lv2PluginInfo> &right)
|
||||
{
|
||||
const char *pb1 = left->name().c_str();
|
||||
const char *pb2 = right->name().c_str();
|
||||
return Locale::collation.compare(
|
||||
return collation.compare(
|
||||
pb1, pb1 + left->name().size(),
|
||||
pb2, pb2 + right->name().size()) < 0;
|
||||
};
|
||||
@@ -1005,9 +1006,11 @@ std::vector<Lv2PluginPreset> Lv2Host::GetPluginPresets(const std::string &plugin
|
||||
}
|
||||
lilv_nodes_free(presets);
|
||||
|
||||
auto compare = [] (const Lv2PluginPreset&left, const Lv2PluginPreset&right)
|
||||
auto& collation = std::use_facet<std::collate<char> >(std::locale());
|
||||
|
||||
auto compare = [&collation] (const Lv2PluginPreset&left, const Lv2PluginPreset&right)
|
||||
{
|
||||
return Locale::collation.compare(
|
||||
return collation.compare(
|
||||
left.name_.c_str(),
|
||||
left.name_.c_str()+left.name_.size(),
|
||||
right.name_.c_str(),
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
// Copyright (c) 2021 Robin 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 "pch.h"
|
||||
#include "PiPedalAlsa.hpp"
|
||||
#include "alsa/asoundlib.h"
|
||||
#include "Lv2Log.hpp"
|
||||
#include <mutex>
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
static uint32_t RATES[] = {
|
||||
44100, 48000, 44100 * 2, 48000 * 2, 44100 * 4, 48000 * 4};
|
||||
|
||||
|
||||
std::mutex alsaMutex;
|
||||
|
||||
std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAvailableAlsaDevices()
|
||||
{
|
||||
std::lock_guard guard{alsaMutex};
|
||||
|
||||
std::vector<AlsaDeviceInfo> result;
|
||||
|
||||
int cardNum = -1; // Start with first card
|
||||
int err;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if ((err = snd_card_next(&cardNum)) < 0)
|
||||
{
|
||||
Lv2Log::error("Unexpected error enumerating ALSA devices.");
|
||||
break;
|
||||
}
|
||||
if (cardNum < 0)
|
||||
// No more cards
|
||||
break;
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "hw:" << cardNum;
|
||||
std::string cardId = ss.str();
|
||||
|
||||
snd_ctl_t *hDevice = nullptr;
|
||||
|
||||
if ((err = snd_ctl_open(&hDevice, cardId.c_str(), 0)) < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
snd_ctl_card_info_t *alsaInfo;
|
||||
if (snd_ctl_card_info_malloc(&alsaInfo) != 0)
|
||||
{
|
||||
Lv2Log::error("Failed to allocate ALSA card info");
|
||||
snd_ctl_close(hDevice);
|
||||
continue;
|
||||
}
|
||||
|
||||
err = snd_ctl_card_info(hDevice, alsaInfo);
|
||||
if (err == 0)
|
||||
{
|
||||
AlsaDeviceInfo info;
|
||||
info.cardId_ = cardNum;
|
||||
info.id_ = std::string("hw:") + snd_ctl_card_info_get_id(alsaInfo);
|
||||
const char* driver = snd_ctl_card_info_get_driver(alsaInfo);
|
||||
|
||||
info.name_ = snd_ctl_card_info_get_name(alsaInfo);
|
||||
info.longName_ = snd_ctl_card_info_get_longname(alsaInfo);
|
||||
|
||||
|
||||
snd_pcm_t *hDevice = nullptr;
|
||||
|
||||
// must support capture AND playback
|
||||
err = snd_pcm_open(&hDevice, cardId.c_str(), SND_PCM_STREAM_CAPTURE, 0);
|
||||
if (err == 0) {
|
||||
snd_pcm_close(hDevice);
|
||||
}
|
||||
if (err == 0)
|
||||
{
|
||||
err = snd_pcm_open(&hDevice, cardId.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
|
||||
}
|
||||
if (err == 0)
|
||||
{
|
||||
snd_pcm_hw_params_t *params = nullptr;
|
||||
err = snd_pcm_hw_params_malloc(¶ms);
|
||||
if (err == 0)
|
||||
{
|
||||
err = snd_pcm_hw_params_any(hDevice, params);
|
||||
if (err == 0)
|
||||
{
|
||||
unsigned int minRate = 0, maxRate = 0;
|
||||
snd_pcm_uframes_t minBufferSize,maxBufferSize;
|
||||
int dir;
|
||||
err = snd_pcm_hw_params_get_rate_min(params, &minRate, &dir);
|
||||
if (err == 0)
|
||||
{
|
||||
err = snd_pcm_hw_params_get_rate_max(params, &maxRate, &dir);
|
||||
}
|
||||
if (err == 0)
|
||||
{
|
||||
err = snd_pcm_hw_params_get_buffer_size_min(params,&minBufferSize);
|
||||
}
|
||||
if (err == 0)
|
||||
{
|
||||
err = snd_pcm_hw_params_get_buffer_size_max(params,&maxBufferSize);
|
||||
}
|
||||
if (err == 0)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(RATES) / sizeof(RATES[0]); ++i)
|
||||
{
|
||||
uint32_t rate = RATES[i];
|
||||
if (rate >= minRate && rate <= maxRate)
|
||||
{
|
||||
info.sampleRates_.push_back(rate);
|
||||
}
|
||||
}
|
||||
info.minBufferSize_ = (uint32_t)minBufferSize;
|
||||
info.maxBufferSize_ = (uint32_t)maxBufferSize;
|
||||
result.push_back(std::move(info));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (params != nullptr)
|
||||
snd_pcm_hw_params_free(params);
|
||||
snd_pcm_close(hDevice);
|
||||
}
|
||||
}
|
||||
snd_ctl_card_info_free(alsaInfo);
|
||||
snd_ctl_close(hDevice);
|
||||
}
|
||||
}
|
||||
snd_config_update_free_global();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void PiPedalAlsaDevices::PreLoadJackDevice(const std::string&deviceName)
|
||||
{
|
||||
// save the device info before we start the jack server, because
|
||||
// we won't be able to do it later.
|
||||
std::vector<AlsaDeviceInfo> devices = GetAvailableAlsaDevices();
|
||||
this->hasJackDevice = false;
|
||||
for (auto &device : devices)
|
||||
{
|
||||
if (device.id_ == deviceName) {
|
||||
this->currentJackDevice = device;
|
||||
this->hasJackDevice = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
|
||||
{
|
||||
std::vector<AlsaDeviceInfo> devices = GetAvailableAlsaDevices();
|
||||
if (this->hasJackDevice)
|
||||
{
|
||||
bool found = false;
|
||||
for (auto &device: devices)
|
||||
{
|
||||
if (device.id_ == this->currentJackDevice.id_)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
devices.push_back(this->currentJackDevice);
|
||||
}
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
|
||||
|
||||
JSON_MAP_BEGIN(AlsaDeviceInfo)
|
||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, cardId)
|
||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, id)
|
||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, name)
|
||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, longName)
|
||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, sampleRates)
|
||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, minBufferSize)
|
||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, maxBufferSize)
|
||||
JSON_MAP_END()
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2021 Robin 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 "json.hpp"
|
||||
|
||||
namespace pipedal {
|
||||
class AlsaDeviceInfo {
|
||||
public:
|
||||
int cardId_ = -1;
|
||||
std::string id_;
|
||||
std::string name_;
|
||||
std::string longName_;
|
||||
std::vector<uint32_t> sampleRates_;
|
||||
uint32_t minBufferSize_ = 0,maxBufferSize_ = 0;
|
||||
|
||||
DECLARE_JSON_MAP(AlsaDeviceInfo);
|
||||
|
||||
};
|
||||
|
||||
class PiPedalAlsaDevices {
|
||||
|
||||
bool hasJackDevice = false;
|
||||
AlsaDeviceInfo currentJackDevice;
|
||||
std::vector<AlsaDeviceInfo> GetAvailableAlsaDevices();
|
||||
public:
|
||||
void PreLoadJackDevice(const std::string&deviceName);
|
||||
|
||||
std::vector<AlsaDeviceInfo> GetAlsaDevices();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2021 Robin 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 "pch.h"
|
||||
#include "catch.hpp"
|
||||
#include <sstream>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "PiPedalAlsa.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
TEST_CASE( "ALSA Test", "[pipedal_alsa_test]" ) {
|
||||
|
||||
PiPedalAlsaDevices devices;
|
||||
auto result = devices.GetAlsaDevices();
|
||||
REQUIRE(result.size() >= 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+46
-25
@@ -87,6 +87,8 @@ void PiPedalModel::Load(const PiPedalConfiguration &configuration)
|
||||
|
||||
this->jackServerSettings.ReadJackConfiguration();
|
||||
|
||||
alsaDevices.PreLoadJackDevice(this->jackServerSettings.GetAlsaDevice());
|
||||
|
||||
storage.SetDataRoot(configuration.GetLocalStoragePath().c_str());
|
||||
storage.Initialize();
|
||||
|
||||
@@ -332,7 +334,7 @@ PedalBoard PiPedalModel::getPreset(int64_t instanceId)
|
||||
std::lock_guard guard(mutex);
|
||||
return this->storage.GetPreset(instanceId);
|
||||
}
|
||||
void PiPedalModel::getBank(int64_t instanceId, BankFile*pResult)
|
||||
void PiPedalModel::getBank(int64_t instanceId, BankFile *pResult)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
this->storage.GetBankFile(instanceId, pResult);
|
||||
@@ -855,13 +857,19 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
|
||||
this->jackServerSettings = jackServerSettings;
|
||||
|
||||
if (!ShutdownClient::CanUseShutdownClient())
|
||||
{
|
||||
throw PiPedalException("Can't change server settings when running interactively.");
|
||||
throw PiPedalException("Can't change server settings when running a debug server.");
|
||||
}
|
||||
|
||||
if (this->jackServerSettings.GetAlsaDevice() != jackServerSettings.GetAlsaDevice())
|
||||
{
|
||||
this->alsaDevices.PreLoadJackDevice(jackServerSettings.GetAlsaDevice());
|
||||
}
|
||||
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)
|
||||
@@ -879,29 +887,38 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
|
||||
{
|
||||
this->jackConfiguration.SetIsRestarting(true);
|
||||
fireJackConfigurationChanged(this->jackConfiguration);
|
||||
this->jackHost->UpdateServerConfiguration(jackServerSettings,
|
||||
[this](bool success, const std::string &errorMessage)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
if (!success)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "UpdateServerconfiguration failed: " << errorMessage;
|
||||
Lv2Log::error(s.str().c_str());
|
||||
}
|
||||
// Update jack server status.
|
||||
this->jackConfiguration.SetIsRestarting(false);
|
||||
fireJackConfigurationChanged(this->jackConfiguration);
|
||||
this->jackHost->UpdateServerConfiguration(
|
||||
jackServerSettings,
|
||||
[this](bool success, const std::string &errorMessage)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
if (!success)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "UpdateServerconfiguration failed: " << errorMessage;
|
||||
Lv2Log::error(s.str().c_str());
|
||||
}
|
||||
// Update jack server status.
|
||||
this->jackConfiguration.SetIsRestarting(false);
|
||||
if (!success)
|
||||
{
|
||||
this->jackConfiguration.SetErrorStatus(errorMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->jackConfiguration.SetErrorStatus("");
|
||||
fireJackConfigurationChanged(this->jackConfiguration);
|
||||
|
||||
// restart the pedalboard on a new instance.
|
||||
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
|
||||
this->lv2PedalBoard = lv2PedalBoard;
|
||||
// restart the pedalboard on a new instance.
|
||||
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
|
||||
this->lv2PedalBoard = lv2PedalBoard;
|
||||
|
||||
jackHost->SetPedalBoard(lv2PedalBoard);
|
||||
updateRealtimeVuSubscriptions();
|
||||
updateRealtimeMonitorPortSubscriptions();
|
||||
});
|
||||
}
|
||||
jackHost->SetPedalBoard(lv2PedalBoard);
|
||||
updateRealtimeVuSubscriptions();
|
||||
updateRealtimeMonitorPortSubscriptions();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void PiPedalModel::updateDefaults(PedalBoardItem *pedalBoardItem)
|
||||
@@ -1040,3 +1057,7 @@ void PiPedalModel::cancelListenForMidiEvent(int64_t clientId, int64_t clientHand
|
||||
jackHost->SetListenForMidiEvent(false);
|
||||
}
|
||||
}
|
||||
std::vector<AlsaDeviceInfo> PiPedalModel::GetAlsaDevices()
|
||||
{
|
||||
return this->alsaDevices.GetAlsaDevices();
|
||||
}
|
||||
@@ -60,6 +60,8 @@ public:
|
||||
|
||||
class PiPedalModel: private IJackHostCallbacks {
|
||||
private:
|
||||
PiPedalAlsaDevices alsaDevices;
|
||||
|
||||
|
||||
class MidiListener {
|
||||
public:
|
||||
@@ -202,6 +204,7 @@ public:
|
||||
|
||||
void listenForMidiEvent(int64_t clientId, int64_t clientHandle, bool listenForControlsOnly);
|
||||
void cancelListenForMidiEvent(int64_t clientId, int64_t clientHandled);
|
||||
std::vector<AlsaDeviceInfo> GetAlsaDevices();
|
||||
|
||||
};
|
||||
} // namespace pipedal.
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "WifiConfigSettings.hpp"
|
||||
#include "WifiChannels.hpp"
|
||||
#include "SysExec.hpp"
|
||||
#include "PiPedalAlsa.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace pipedal;
|
||||
@@ -689,6 +690,12 @@ public:
|
||||
{
|
||||
JackHostStatus status = model.getJackStatus();
|
||||
this->Reply(replyTo,"getJackStatus",status);
|
||||
} else if (message == "getAlsaDevices")
|
||||
{
|
||||
std::vector<AlsaDeviceInfo> devices = model.GetAlsaDevices();
|
||||
this->Reply(replyTo,"getAlsaDevices",devices);
|
||||
|
||||
|
||||
} else if (message == "getWifiChannels")
|
||||
{
|
||||
std::string country;
|
||||
|
||||
@@ -109,7 +109,8 @@ bool ShutdownClient::WriteMessage(const char*message) {
|
||||
while (!eolFound)
|
||||
{
|
||||
ssize_t nRead = read(sock,pWrite,available);
|
||||
if (nRead == 0) {
|
||||
if (nRead == -1)
|
||||
{
|
||||
*pWrite = 0;
|
||||
break;
|
||||
}
|
||||
@@ -156,12 +157,11 @@ bool ShutdownClient::WriteMessage(const char*message) {
|
||||
bool ShutdownClient::SetJackServerConfiguration(const JackServerSettings & jackServerSettings)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "setJackConfiguration "
|
||||
<< jackServerSettings.GetSampleRate()
|
||||
<< " " << jackServerSettings.GetBufferSize()
|
||||
<< " " << jackServerSettings.GetNumberOfBuffers()
|
||||
<< "\n";
|
||||
|
||||
s << "setJackConfiguration ";
|
||||
|
||||
json_writer writer(s,true);
|
||||
writer.write(jackServerSettings);
|
||||
s << std::endl;
|
||||
return WriteMessage(s.str().c_str());
|
||||
}
|
||||
|
||||
|
||||
+13
-24
@@ -80,27 +80,12 @@ static void ReleaseAccessPoint(const std::string gatewayAddress)
|
||||
}
|
||||
|
||||
|
||||
bool setJackConfiguration(uint32_t sampleRate,uint32_t bufferSize,uint32_t numberOfBuffers)
|
||||
bool setJackConfiguration(JackServerSettings serverSettings)
|
||||
{
|
||||
bool success = true;
|
||||
JackServerSettings serverSettings(sampleRate,bufferSize,numberOfBuffers);
|
||||
|
||||
try {
|
||||
serverSettings.Write();
|
||||
} catch (const std::exception &e) {
|
||||
std::stringstream s;
|
||||
s << "Failed to write jackdrc settings. " << e.what();
|
||||
Lv2Log::error(s.str());
|
||||
success = false;
|
||||
}
|
||||
::system("pulseaudio --kill");
|
||||
|
||||
if (::system("systemctl restart jack") != 0)
|
||||
{
|
||||
Lv2Log::error("Failed to restart jack server.");
|
||||
success = false;
|
||||
}
|
||||
return false;
|
||||
serverSettings.Write();
|
||||
return true;
|
||||
|
||||
|
||||
}
|
||||
@@ -245,24 +230,28 @@ private:
|
||||
} else if (startsWith(s,"setJackConfiguration "))
|
||||
{
|
||||
auto remainder = s.substr(strlen("setJackConfiguration "));
|
||||
|
||||
// xxx delete me
|
||||
Lv2Log::error("setJackConfiguration: " + remainder);
|
||||
|
||||
std::stringstream input(remainder);
|
||||
uint32_t sampleRate;
|
||||
uint32_t bufferSize;
|
||||
uint32_t numberOfBuffers;
|
||||
input >> sampleRate >> bufferSize >> numberOfBuffers;
|
||||
JackServerSettings serverSettings;
|
||||
json_reader reader(input);
|
||||
|
||||
reader.read(&serverSettings);
|
||||
|
||||
if (input.fail())
|
||||
{
|
||||
result = -1;
|
||||
} else {
|
||||
result = setJackConfiguration(sampleRate,bufferSize,numberOfBuffers) ? 0: -1;
|
||||
result = setJackConfiguration(serverSettings) ? 0: -1;
|
||||
}
|
||||
|
||||
}
|
||||
} catch (const std::exception &e)
|
||||
{
|
||||
std::stringstream t;
|
||||
t << "-2 " << e.what();
|
||||
t << "-2 " << e.what() << "\n";
|
||||
this->response = t.str();
|
||||
this->writePosition = 0;
|
||||
WriteSome();
|
||||
|
||||
@@ -418,6 +418,8 @@ uint16_t g_ShutdownPort = 0;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
|
||||
sem_init(&signalSemaphore, 0, 0);
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
@@ -5,6 +5,7 @@ After=network.target
|
||||
BindsTo=jack.service
|
||||
|
||||
|
||||
|
||||
[Service]
|
||||
LimitMEMLOCK=infinity
|
||||
LimitRTPRIO=95
|
||||
@@ -12,11 +13,11 @@ ExecStart=${COMMAND}
|
||||
User=pipedal_d
|
||||
Group=pipedal_d
|
||||
Restart=always
|
||||
RestartSec=15
|
||||
RestartSec=25
|
||||
TimeoutStopSec=10
|
||||
WorkingDirectory=/var/pipedal
|
||||
Environment=JACK_PROMISCUOUS_SERVER=jack
|
||||
Environment=JACK_START_SERVER=1
|
||||
Environment=JACK_PROMISCUOUS_SERVER=
|
||||
Environment=JACK_STJACK_NO_START_SERVER=
|
||||
|
||||
|
||||
[Install]
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
[Unit]
|
||||
Description=Jack Audio Daemon
|
||||
After=sound.target
|
||||
After=local-fs.target
|
||||
After=dbus.socket
|
||||
|
||||
|
||||
[Service]
|
||||
LimitMEMLOCK=infinity
|
||||
LimitRTTIME=infinity
|
||||
LimitRTPRIO=95
|
||||
ExecStartPre=/bin/sleep 3
|
||||
ExecStart=/etc/jackdrc
|
||||
User=jack
|
||||
Group=jack
|
||||
Restart=always
|
||||
RestartSec=15
|
||||
|
||||
Environment=JACK_PROMISCUOUS_SERVER=jack
|
||||
Environment=JACK_NO_AUDIO_RESERVATION=1
|
||||
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user