Re-order service startup (Jack Server Settings)

This commit is contained in:
Robin Davies
2021-08-29 21:20:42 -04:00
parent 5ec349f17b
commit 57ecc0d9ef
14 changed files with 239 additions and 124 deletions
+1 -1
View File
@@ -25,6 +25,6 @@
/* Provide access point capture redirects on this gateway. */ /* Provide access point capture redirects on this gateway. */
"accessPointGateway": "192.168.0.0/24", "accessPointGateway": "192.168.0.0/24",
"accessPointServerAddress" : "192.168.0.23:8080" "accessPointServerAddress" : "192.168.0.26:8080"
} }
+1 -2
View File
@@ -33,7 +33,6 @@ import FormControl from '@material-ui/core/FormControl';
import Select from '@material-ui/core/Select'; import Select from '@material-ui/core/Select';
import DialogContent from '@material-ui/core/DialogContent'; import DialogContent from '@material-ui/core/DialogContent';
import MenuItem from '@material-ui/core/MenuItem'; import MenuItem from '@material-ui/core/MenuItem';
import { nullCast } from './Utility';
import Typography from '@material-ui/core/Typography'; import Typography from '@material-ui/core/Typography';
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
@@ -178,7 +177,7 @@ const JackServerSettingsDialog = withStyles(styles)(
let settings = this.state.jackServerSettings.clone(); let settings = this.state.jackServerSettings.clone();
settings.alsaDevice = device; settings.alsaDevice = device;
settings.sampleRate = selectedDevice.closestSampleRate(settings.sampleRate); settings.sampleRate = selectedDevice.closestSampleRate(settings.sampleRate);
settings.bufferSize = selectedDevice.closestSampleRate(settings.bufferSize); settings.bufferSize = selectedDevice.closestBufferSize(settings.bufferSize);
this.setState({ this.setState({
jackServerSettings: settings, jackServerSettings: settings,
+16 -2
View File
@@ -20,7 +20,7 @@
import React, { SyntheticEvent, Component } from 'react'; import React, { SyntheticEvent, Component } from 'react';
import IconButton from '@material-ui/core/IconButton'; import IconButton from '@material-ui/core/IconButton';
import Typography from '@material-ui/core/Typography'; import Typography from '@material-ui/core/Typography';
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; import { PiPedalModel, PiPedalModelFactory, State } from './PiPedalModel';
import ButtonBase from "@material-ui/core/ButtonBase"; import ButtonBase from "@material-ui/core/ButtonBase";
import { TransitionProps } from '@material-ui/core/transitions/transition'; import { TransitionProps } from '@material-ui/core/transitions/transition';
import Slide from '@material-ui/core/Slide'; import Slide from '@material-ui/core/Slide';
@@ -159,9 +159,20 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
this.handleJackSettingsChanged = this.handleJackSettingsChanged.bind(this); this.handleJackSettingsChanged = this.handleJackSettingsChanged.bind(this);
this.handleJackServerSettingsChanged = this.handleJackServerSettingsChanged.bind(this); this.handleJackServerSettingsChanged = this.handleJackServerSettingsChanged.bind(this);
this.handleWifiConfigSettingsChanged = this.handleWifiConfigSettingsChanged.bind(this); this.handleWifiConfigSettingsChanged = this.handleWifiConfigSettingsChanged.bind(this);
this.handleConnectionStateChanged = this.handleConnectionStateChanged.bind(this);
} }
handleConnectionStateChanged() : void {
if (this.model.state.get() === State.Ready)
{
if (this.state.shuttingDown || this.state.restarting)
{
this.setState({shuttingDown: false, restarting: false});
}
}
}
handleApplyWifiConfig(wifiConfigSettings: WifiConfigSettings): void { handleApplyWifiConfig(wifiConfigSettings: WifiConfigSettings): void {
this.setState({showWifiConfigDialog: false}); this.setState({showWifiConfigDialog: false});
this.model.setWifiConfigSettings(wifiConfigSettings) this.model.setWifiConfigSettings(wifiConfigSettings)
@@ -218,6 +229,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
if (active !== this.active) { if (active !== this.active) {
this.active = active; this.active = active;
if (active) { if (active) {
this.model.state.addOnChangedHandler(this.handleConnectionStateChanged);
this.model.jackSettings.addOnChangedHandler(this.handleJackSettingsChanged); this.model.jackSettings.addOnChangedHandler(this.handleJackSettingsChanged);
this.model.jackConfiguration.addOnChangedHandler(this.handleJackConfigurationChanged); this.model.jackConfiguration.addOnChangedHandler(this.handleJackConfigurationChanged);
this.model.jackServerSettings.addOnChangedHandler(this.handleJackServerSettingsChanged); this.model.jackServerSettings.addOnChangedHandler(this.handleJackServerSettingsChanged);
@@ -235,15 +247,17 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
jackStatus: undefined jackStatus: undefined
}) })
}); });
this.handleConnectionStateChanged();
this.handleJackConfigurationChanged(); this.handleJackConfigurationChanged();
this.handleJackSettingsChanged(); this.handleJackSettingsChanged();
this.handleJackServerSettingsChanged(); this.handleJackServerSettingsChanged();
this.handleWifiConfigSettingsChanged(); this.handleWifiConfigSettingsChanged();
// xxx UNCOMMENT ME! this.timerHandle = setInterval(() => this.tick(), 1000); this.timerHandle = setInterval(() => this.tick(), 1000);
} else { } else {
if (this.timerHandle) { if (this.timerHandle) {
clearInterval(this.timerHandle); clearInterval(this.timerHandle);
} }
this.model.state.removeOnChangedHandler(this.handleConnectionStateChanged);
this.model.jackConfiguration.removeOnChangedHandler(this.handleJackConfigurationChanged); this.model.jackConfiguration.removeOnChangedHandler(this.handleJackConfigurationChanged);
this.model.jackSettings.removeOnChangedHandler(this.handleJackSettingsChanged); this.model.jackSettings.removeOnChangedHandler(this.handleJackSettingsChanged);
this.model.jackServerSettings.removeOnChangedHandler(this.handleJackServerSettingsChanged); this.model.jackServerSettings.removeOnChangedHandler(this.handleJackServerSettingsChanged);
+1
View File
@@ -176,6 +176,7 @@ add_executable(pipedalconfig json.cpp json.hpp
PiPedalException.hpp PiPedalException.hpp
ConfigMain.cpp ConfigMain.cpp
WifiConfigSettings.cpp WifiConfigSettings.hpp WifiConfigSettings.cpp WifiConfigSettings.hpp
JackServerSettings.hpp JackServerSettings.cpp
SetWifiConfig.hpp SetWifiConfig.cpp SetWifiConfig.hpp SetWifiConfig.cpp
SystemConfigFile.hpp SystemConfigFile.cpp SystemConfigFile.hpp SystemConfigFile.cpp
SysExec.hpp SysExec.cpp SysExec.hpp SysExec.cpp
+17 -2
View File
@@ -30,6 +30,7 @@
#include "SysExec.hpp" #include "SysExec.hpp"
#include <sys/wait.h> #include <sys/wait.h>
#include <pwd.h> #include <pwd.h>
#include "JackServerSettings.hpp"
using namespace std; using namespace std;
using namespace pipedal; using namespace pipedal;
@@ -116,6 +117,10 @@ void StopService()
{ {
cout << "Error: Failed to stop the " SHUTDOWN_SERVICE " service."; cout << "Error: Failed to stop the " SHUTDOWN_SERVICE " service.";
} }
if (SysExec(SYSTEMCTL_BIN " stop " JACK_SERVICE ".service") != EXIT_SUCCESS)
{
throw PiPedalException("Failed to stop the " JACK_SERVICE " service.");
}
} }
void Uninstall() { void Uninstall() {
@@ -128,13 +133,23 @@ void Uninstall() {
void StartService() void StartService()
{ {
SilentSysExec("/usr/bin/pulseaudio --kill"); // interferes with Jack audio service startup.
if (SysExec(SYSTEMCTL_BIN " start " SHUTDOWN_SERVICE ".service") != EXIT_SUCCESS)
{
throw PiPedalException("Failed to start the " SHUTDOWN_SERVICE " service.");
}
if (SysExec(SYSTEMCTL_BIN " start " NATIVE_SERVICE ".service") != EXIT_SUCCESS) if (SysExec(SYSTEMCTL_BIN " start " NATIVE_SERVICE ".service") != EXIT_SUCCESS)
{ {
throw PiPedalException("Failed to start the " NATIVE_SERVICE " service."); throw PiPedalException("Failed to start the " NATIVE_SERVICE " service.");
} }
if (SysExec(SYSTEMCTL_BIN " start " SHUTDOWN_SERVICE ".service") != EXIT_SUCCESS) JackServerSettings serverSettings;
serverSettings.ReadJackConfiguration();
if (serverSettings.IsValid())
{ {
throw PiPedalException("Failed to start the " SHUTDOWN_SERVICE " service."); if (SysExec(SYSTEMCTL_BIN " start " JACK_SERVICE ".service") != EXIT_SUCCESS)
{
throw PiPedalException("Failed to start the " JACK_SERVICE " service.");
}
} }
} }
void RestartService() void RestartService()
+15 -1
View File
@@ -905,6 +905,8 @@ public:
return result; return result;
} }
static int jackInstanceId;
virtual void Open(const JackChannelSelection &channelSelection) virtual void Open(const JackChannelSelection &channelSelection)
{ {
@@ -929,14 +931,24 @@ public:
jack_status_t status; jack_status_t status;
try try
{ {
// need a unique instance name every timme.
std::stringstream s;
s << "PiPedal";
if (jackInstanceId != 0) {
s << jackInstanceId;
}
++jackInstanceId;
client = jack_client_open("PiPedal", JackNullOption, &status); std::string instanceName = s.str();
client = jack_client_open(instanceName.c_str(), JackNullOption, &status);
if (client == nullptr || status & JackFailure) if (client == nullptr || status & JackFailure)
{ {
if (client) if (client)
{ {
jack_client_close(client); jack_client_close(client);
client = nullptr;
} }
std::string error = GetJackErrorMessage(status); std::string error = GetJackErrorMessage(status);
Lv2Log::error(error); Lv2Log::error(error);
@@ -1389,6 +1401,8 @@ public:
} }
}; };
int JackHostImpl::jackInstanceId = 0;
JackHost *JackHost::CreateInstance(IHost *pHost) JackHost *JackHost::CreateInstance(IHost *pHost)
{ {
return new JackHostImpl(pHost); return new JackHostImpl(pHost);
+5 -12
View File
@@ -19,7 +19,6 @@
#include "pch.h" #include "pch.h"
#include "Lv2Log.hpp"
#include "JackServerSettings.hpp" #include "JackServerSettings.hpp"
#include <fstream> #include <fstream>
#include "PiPedalException.hpp" #include "PiPedalException.hpp"
@@ -117,7 +116,6 @@ void JackServerSettings::ReadJackConfiguration()
if (!input.is_open()) if (!input.is_open())
{ {
Lv2Log::error("Can't read " DRC_FILENAME);
return; return;
} }
@@ -146,11 +144,11 @@ void JackServerSettings::ReadJackConfiguration()
this->numberOfBuffers_ = (uint32_t)GetJackArg(argv, "-n", "--nperiods"); this->numberOfBuffers_ = (uint32_t)GetJackArg(argv, "-n", "--nperiods");
this->sampleRate_ = (uint32_t)GetJackArg(argv, "-r", "--rate"); this->sampleRate_ = (uint32_t)GetJackArg(argv, "-r", "--rate");
this->alsaDevice_ = GetJackStringArg(argv,"-d", "--device"); this->alsaDevice_ = GetJackStringArg(argv,"-d", "--device");
valid_ = true; this->valid_ = true;
} }
catch (std::exception &) catch (std::exception &)
{ {
Lv2Log::error("Can't parse " DRC_FILENAME); //Lv2Log::error("Can't parse " DRC_FILENAME);
} }
} }
} }
@@ -168,7 +166,6 @@ void JackServerSettings::Write()
if (!input.is_open()) if (!input.is_open())
{ {
Lv2Log::error("Can't read " DRC_FILENAME);
return; return;
} }
@@ -176,7 +173,6 @@ void JackServerSettings::Write()
{ {
if (input.eof()) if (input.eof())
{ {
Lv2Log::error("Premature end of file in " DRC_FILENAME);
break; break;
} }
std::getline(input,lastLine); std::getline(input,lastLine);
@@ -209,8 +205,8 @@ void JackServerSettings::Write()
{ {
// jack1 incantation for promiscuous servers. // jack1 incantation for promiscuous servers.
output << "#!/bin/sh" <<endl; output << "#!/bin/sh" <<endl;
output << "export JACK_PROMISCUOUS_SERVER=" << endl; output << "export JACK_PROMISCUOUS_SERVER=jack" << endl;
output << "export JACK_NO_AUDIO_RESERVATION=" << endl; output << "export JACK_NO_AUDIO_RESERVATION=1" << endl;
output << "umask 0" << endl; output << "umask 0" << endl;
} }
for (auto line : precedingLines) for (auto line : precedingLines)
@@ -220,7 +216,7 @@ void JackServerSettings::Write()
// the style used by qjackctl. :-/ // the style used by qjackctl. :-/
output << "/usr/bin/jackd " output << "/usr/bin/jackd "
<< "-R -P90" << "-R -P90"
<< " -driver alsa -d" << this->alsaDevice_ << " -dalsa -d" << this->alsaDevice_
<< " -r" << this->sampleRate_ << " -r" << this->sampleRate_
<< " -p" << this->bufferSize_ << " -p" << this->bufferSize_
<< " -n" << this->numberOfBuffers_ << " -Xseq" << " -n" << this->numberOfBuffers_ << " -Xseq"
@@ -235,9 +231,6 @@ void JackServerSettings::Write()
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
stringstream s;
s << "jack - " << e.what();
Lv2Log::error(s.str());
} }
} }
+34 -53
View File
@@ -26,12 +26,26 @@
using namespace pipedal; using namespace pipedal;
static uint32_t RATES[] = { static uint32_t RATES[] = {
44100, 48000, 44100 * 2, 48000 * 2, 44100 * 4, 48000 * 4}; 22050, 24000, 44100, 48000, 44100 * 2, 48000 * 2, 44100 * 4, 48000 * 4};
std::mutex alsaMutex; std::mutex alsaMutex;
std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAvailableAlsaDevices() bool PiPedalAlsaDevices::getCachedDevice(const std::string &name, AlsaDeviceInfo *pResult)
{
auto it = cachedDevices.find(name);
if (it != cachedDevices.end())
{
*pResult = it->second;
return true;
}
return false;
}
void PiPedalAlsaDevices::cacheDevice(const std::string &name, const AlsaDeviceInfo &deviceInfo)
{
cachedDevices[name] = deviceInfo;
}
std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
{ {
std::lock_guard guard{alsaMutex}; std::lock_guard guard{alsaMutex};
@@ -50,7 +64,7 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAvailableAlsaDevices()
if (cardNum < 0) if (cardNum < 0)
// No more cards // No more cards
break; break;
{ {
std::stringstream ss; std::stringstream ss;
ss << "hw:" << cardNum; ss << "hw:" << cardNum;
@@ -77,17 +91,17 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAvailableAlsaDevices()
AlsaDeviceInfo info; AlsaDeviceInfo info;
info.cardId_ = cardNum; info.cardId_ = cardNum;
info.id_ = std::string("hw:") + snd_ctl_card_info_get_id(alsaInfo); info.id_ = std::string("hw:") + snd_ctl_card_info_get_id(alsaInfo);
const char* driver = snd_ctl_card_info_get_driver(alsaInfo); const char *driver = snd_ctl_card_info_get_driver(alsaInfo);
info.name_ = snd_ctl_card_info_get_name(alsaInfo); info.name_ = snd_ctl_card_info_get_name(alsaInfo);
info.longName_ = snd_ctl_card_info_get_longname(alsaInfo); info.longName_ = snd_ctl_card_info_get_longname(alsaInfo);
snd_pcm_t *hDevice = nullptr; snd_pcm_t *hDevice = nullptr;
// must support capture AND playback // must support capture AND playback
err = snd_pcm_open(&hDevice, cardId.c_str(), SND_PCM_STREAM_CAPTURE, 0); err = snd_pcm_open(&hDevice, cardId.c_str(), SND_PCM_STREAM_CAPTURE, 0);
if (err == 0) { if (err == 0)
{
snd_pcm_close(hDevice); snd_pcm_close(hDevice);
} }
if (err == 0) if (err == 0)
@@ -104,7 +118,7 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAvailableAlsaDevices()
if (err == 0) if (err == 0)
{ {
unsigned int minRate = 0, maxRate = 0; unsigned int minRate = 0, maxRate = 0;
snd_pcm_uframes_t minBufferSize,maxBufferSize; snd_pcm_uframes_t minBufferSize, maxBufferSize;
int dir; int dir;
err = snd_pcm_hw_params_get_rate_min(params, &minRate, &dir); err = snd_pcm_hw_params_get_rate_min(params, &minRate, &dir);
if (err == 0) if (err == 0)
@@ -113,11 +127,11 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAvailableAlsaDevices()
} }
if (err == 0) if (err == 0)
{ {
err = snd_pcm_hw_params_get_buffer_size_min(params,&minBufferSize); err = snd_pcm_hw_params_get_buffer_size_min(params, &minBufferSize);
} }
if (err == 0) if (err == 0)
{ {
err = snd_pcm_hw_params_get_buffer_size_max(params,&maxBufferSize); err = snd_pcm_hw_params_get_buffer_size_max(params, &maxBufferSize);
} }
if (err == 0) if (err == 0)
{ {
@@ -131,7 +145,8 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAvailableAlsaDevices()
} }
info.minBufferSize_ = (uint32_t)minBufferSize; info.minBufferSize_ = (uint32_t)minBufferSize;
info.maxBufferSize_ = (uint32_t)maxBufferSize; info.maxBufferSize_ = (uint32_t)maxBufferSize;
result.push_back(std::move(info)); cacheDevice(info.name_, info);
result.push_back(info);
} }
} }
} }
@@ -139,6 +154,13 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAvailableAlsaDevices()
snd_pcm_hw_params_free(params); snd_pcm_hw_params_free(params);
snd_pcm_close(hDevice); snd_pcm_close(hDevice);
} }
else
{
if (getCachedDevice(info.name_, &info))
{
result.push_back(info);
}
}
} }
snd_ctl_card_info_free(alsaInfo); snd_ctl_card_info_free(alsaInfo);
snd_ctl_close(hDevice); snd_ctl_close(hDevice);
@@ -148,47 +170,6 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAvailableAlsaDevices()
return result; 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_BEGIN(AlsaDeviceInfo)
JSON_MAP_REFERENCE(AlsaDeviceInfo, cardId) JSON_MAP_REFERENCE(AlsaDeviceInfo, cardId)
JSON_MAP_REFERENCE(AlsaDeviceInfo, id) JSON_MAP_REFERENCE(AlsaDeviceInfo, id)
+5 -5
View File
@@ -37,12 +37,12 @@ namespace pipedal {
class PiPedalAlsaDevices { class PiPedalAlsaDevices {
bool hasJackDevice = false; std::map<std::string,AlsaDeviceInfo> cachedDevices;
AlsaDeviceInfo currentJackDevice;
std::vector<AlsaDeviceInfo> GetAvailableAlsaDevices();
public:
void PreLoadJackDevice(const std::string&deviceName);
bool getCachedDevice(const std::string&name, AlsaDeviceInfo*pResult);
void cacheDevice(const std::string&name, const AlsaDeviceInfo&deviceInfo);
public:
std::vector<AlsaDeviceInfo> GetAlsaDevices(); std::vector<AlsaDeviceInfo> GetAlsaDevices();
}; };
} }
+2 -5
View File
@@ -47,6 +47,8 @@ std::string AtomToJson(int length, uint8_t *pData)
PiPedalModel::PiPedalModel() PiPedalModel::PiPedalModel()
{ {
this->pedalBoard = PedalBoard::MakeDefault(); this->pedalBoard = PedalBoard::MakeDefault();
this->jackServerSettings.ReadJackConfiguration();
} }
void PiPedalModel::Close() void PiPedalModel::Close()
@@ -87,7 +89,6 @@ void PiPedalModel::Load(const PiPedalConfiguration &configuration)
this->jackServerSettings.ReadJackConfiguration(); this->jackServerSettings.ReadJackConfiguration();
alsaDevices.PreLoadJackDevice(this->jackServerSettings.GetAlsaDevice());
storage.SetDataRoot(configuration.GetLocalStoragePath().c_str()); storage.SetDataRoot(configuration.GetLocalStoragePath().c_str());
storage.Initialize(); storage.Initialize();
@@ -862,10 +863,6 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
throw PiPedalException("Can't change server settings when running a debug server."); 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; this->jackServerSettings = jackServerSettings;
+31 -14
View File
@@ -28,7 +28,6 @@
using namespace pipedal; using namespace pipedal;
using namespace std; using namespace std;
// find on path, but ONLY /usr/bin and /usr/sbin // find on path, but ONLY /usr/bin and /usr/sbin
static std::filesystem::path findOnSystemPath(const std::string &command) static std::filesystem::path findOnSystemPath(const std::string &command)
@@ -60,23 +59,42 @@ static std::filesystem::path findOnSystemPath(const std::string &command)
throw PiPedalException(s.str()); throw PiPedalException(s.str());
} }
void pipedal::SilentSysExec(const char *szCommand)
{
std::stringstream s;
s << szCommand << " 2>&1";
FILE *output = popen(s.str().c_str(), "r");
char buffer[512];
if (output)
{
while (!feof(output))
{
fgets(buffer, sizeof(buffer), output);
}
pclose(output);
}
}
int pipedal::SysExec(const char*szCommand) { int pipedal::SysExec(const char *szCommand)
char*args = strdup(szCommand); {
char *args = strdup(szCommand);
int argc; int argc;
std::vector<char*> argv; std::vector<char *> argv;
char *p = args; char *p = args;
while (*p) { while (*p)
{
argv.push_back(p); argv.push_back(p);
while (*p && *p != ' ') { while (*p && *p != ' ')
{
++p; ++p;
} }
if (*p) { if (*p)
{
*p++ = '\0'; *p++ = '\0';
while (*p && *p == ' ') while (*p && *p == ' ')
{ {
++p; ++p;
} }
@@ -91,12 +109,11 @@ int pipedal::SysExec(const char*szCommand) {
} }
if (!std::filesystem::exists(execPath)) if (!std::filesystem::exists(execPath))
{ {
throw PiPedalException( SS("Path does not exist: " << execPath << ".")); throw PiPedalException(SS("Path does not exist: " << execPath << "."));
} }
argv[0] = (char*)(execPath.c_str()); argv[0] = (char *)(execPath.c_str());
char **rawArgv = &argv[0];
char**rawArgv = &argv[0];
int pbPid; int pbPid;
int returnValue = 0; int returnValue = 0;
@@ -107,7 +124,7 @@ int pipedal::SysExec(const char*szCommand) {
} }
else else
{ {
free((void*)args); free((void *)args);
waitpid(pbPid, &returnValue, 0); waitpid(pbPid, &returnValue, 0);
int exitStatus = WEXITSTATUS(returnValue); int exitStatus = WEXITSTATUS(returnValue);
return exitStatus; return exitStatus;
+3
View File
@@ -24,4 +24,7 @@ namespace pipedal {
// exec a command, returning the actual exit code (unlike execXX() or system() ) // exec a command, returning the actual exit code (unlike execXX() or system() )
int SysExec(const char*szCommand); int SysExec(const char*szCommand);
// execute a command, suppressing output.
void SilentSysExec(const char *szCommand);
} }
+102 -23
View File
@@ -37,6 +37,8 @@
#include <signal.h> #include <signal.h>
#include <semaphore.h> #include <semaphore.h>
#include <systemd/sd-daemon.h>
using namespace pipedal; using namespace pipedal;
using namespace boost::beast; using namespace boost::beast;
@@ -45,6 +47,16 @@ using namespace boost::beast;
sem_t signalSemaphore; sem_t signalSemaphore;
bool HasAlsaDevice(const std::vector<AlsaDeviceInfo> devices, const std::string &deviceId)
{
for (auto &device : devices)
{
if (device.id_ == deviceId)
return true;
}
return false;
}
class application_category : public boost::system::error_category class application_category : public boost::system::error_category
{ {
public: public:
@@ -52,13 +64,14 @@ public:
std::string message(int ev) const { return "error message"; } std::string message(int ev) const { return "error message"; }
}; };
static volatile bool g_SigBreak = false;
void sig_handler(int signo) void sig_handler(int signo)
{ {
g_SigBreak = true;
sem_post(&signalSemaphore); sem_post(&signalSemaphore);
} }
using namespace boost::system; using namespace boost::system;
class DownloadIntercept : public RequestHandler class DownloadIntercept : public RequestHandler
{ {
PiPedalModel *model; PiPedalModel *model;
@@ -81,7 +94,8 @@ public:
std::string strInstanceId = request_uri.query("id"); std::string strInstanceId = request_uri.query("id");
if (strInstanceId != "") if (strInstanceId != "")
return true; return true;
} else if (request_uri.segment(1) == "uploadPreset") }
else if (request_uri.segment(1) == "uploadPreset")
{ {
return true; return true;
} }
@@ -90,19 +104,19 @@ public:
std::string strInstanceId = request_uri.query("id"); std::string strInstanceId = request_uri.query("id");
if (strInstanceId != "") if (strInstanceId != "")
return true; return true;
} else if (request_uri.segment(1) == "uploadBank") }
else if (request_uri.segment(1) == "uploadBank")
{ {
return true; return true;
} }
return false; return false;
} }
std::string GetContentDispositionHeader(const std::string &name, const std::string &extension) std::string GetContentDispositionHeader(const std::string &name, const std::string &extension)
{ {
std::string fileName = name.substr(0,64) + extension; std::string fileName = name.substr(0, 64) + extension;
std::stringstream s; std::stringstream s;
s << "attachment; filename*=" << HtmlHelper::Rfc5987EncodeFileName(fileName) << "; filename=\"" << HtmlHelper::SafeFileName(fileName) << "\""; s << "attachment; filename*=" << HtmlHelper::Rfc5987EncodeFileName(fileName) << "; filename=\"" << HtmlHelper::SafeFileName(fileName) << "\"";
std::string result = s.str(); std::string result = s.str();
return result; return result;
@@ -131,11 +145,10 @@ public:
std::string strInstanceId = request_uri.query("id"); std::string strInstanceId = request_uri.query("id");
int64_t instanceId = std::stol(strInstanceId); int64_t instanceId = std::stol(strInstanceId);
BankFile bank; BankFile bank;
model->getBank(instanceId,&bank); model->getBank(instanceId, &bank);
std::stringstream s; std::stringstream s;
json_writer writer(s,true); // do what we can to reduce the file size. json_writer writer(s, true); // do what we can to reduce the file size.
writer.write(bank); writer.write(bank);
*pContent = s.str(); *pContent = s.str();
*pName = bank.name(); *pName = bank.name();
@@ -160,7 +173,7 @@ public:
res.set(http::field::content_length, content.length()); res.set(http::field::content_length, content.length());
res.set(http::field::content_disposition, GetContentDispositionHeader(name, PRESET_EXTENSION)); res.set(http::field::content_disposition, GetContentDispositionHeader(name, PRESET_EXTENSION));
return; return;
} }
if (request_uri.segment(1) == "downloadBank") if (request_uri.segment(1) == "downloadBank")
{ {
std::string name; std::string name;
@@ -172,7 +185,7 @@ public:
res.set(http::field::content_length, content.length()); res.set(http::field::content_length, content.length());
res.set(http::field::content_disposition, GetContentDispositionHeader(name, BANK_EXTENSION)); res.set(http::field::content_disposition, GetContentDispositionHeader(name, BANK_EXTENSION));
return; return;
} }
throw PiPedalException("Not found."); throw PiPedalException("Not found.");
} }
catch (const std::exception &e) catch (const std::exception &e)
@@ -207,7 +220,8 @@ public:
res.set(http::field::content_length, content.length()); res.set(http::field::content_length, content.length());
res.set(http::field::content_disposition, GetContentDispositionHeader(name, PRESET_EXTENSION)); res.set(http::field::content_disposition, GetContentDispositionHeader(name, PRESET_EXTENSION));
res.body() = content; res.body() = content;
} else if (request_uri.segment(1) == "downloadBank") }
else if (request_uri.segment(1) == "downloadBank")
{ {
std::string name; std::string name;
std::string content; std::string content;
@@ -218,7 +232,9 @@ public:
res.set(http::field::content_length, content.length()); res.set(http::field::content_length, content.length());
res.set(http::field::content_disposition, GetContentDispositionHeader(name, BANK_EXTENSION)); res.set(http::field::content_disposition, GetContentDispositionHeader(name, BANK_EXTENSION));
res.body() = content; res.body() = content;
} else { }
else
{
throw PiPedalException("Not found"); throw PiPedalException("Not found");
} }
} }
@@ -258,7 +274,7 @@ public:
BankFile bankFile; BankFile bankFile;
reader.read(&bankFile); reader.read(&bankFile);
uint64_t instanceId = model->uploadPreset(bankFile,uploadAfter); uint64_t instanceId = model->uploadPreset(bankFile, uploadAfter);
res.set(http::field::content_type, "application/json"); res.set(http::field::content_type, "application/json");
res.set(http::field::cache_control, "no-cache"); res.set(http::field::cache_control, "no-cache");
@@ -268,8 +284,8 @@ public:
res.set(http::field::content_length, result.length()); res.set(http::field::content_length, result.length());
res.body() = result; res.body() = result;
} }
else if (request_uri.segment(1) == "uploadBank" ) else if (request_uri.segment(1) == "uploadBank")
{ {
std::string presetBody = req.body(); std::string presetBody = req.body();
std::stringstream s(presetBody); std::stringstream s(presetBody);
@@ -285,7 +301,7 @@ public:
BankFile bankFile; BankFile bankFile;
reader.read(&bankFile); reader.read(&bankFile);
uint64_t instanceId = model->uploadBank(bankFile,uploadAfter); uint64_t instanceId = model->uploadBank(bankFile, uploadAfter);
res.set(http::field::content_type, "application/json"); res.set(http::field::content_type, "application/json");
res.set(http::field::cache_control, "no-cache"); res.set(http::field::cache_control, "no-cache");
@@ -296,7 +312,8 @@ public:
res.body() = result; res.body() = result;
} }
else { else
{
throw PiPedalException("Not found"); throw PiPedalException("Not found");
} }
} }
@@ -312,7 +329,6 @@ public:
} }
} }
} }
}; };
/* When hosting a react app, replace /var/config.json with /* When hosting a react app, replace /var/config.json with
@@ -326,7 +342,7 @@ private:
uint64_t maxUploadSize; uint64_t maxUploadSize;
public: public:
InterceptConfig(int portNumber,uint64_t maxUploadSize) InterceptConfig(int portNumber, uint64_t maxUploadSize)
: RequestHandler("/var/config.json"), : RequestHandler("/var/config.json"),
maxUploadSize(maxUploadSize) maxUploadSize(maxUploadSize)
{ {
@@ -414,11 +430,17 @@ public:
} }
}; };
static bool isJackServiceRunning()
{
// look for the jack shmem .
std::filesystem::path path = "/dev/shm/jack_default_0";
return std::filesystem::exists(path);
}
uint16_t g_ShutdownPort = 0; uint16_t g_ShutdownPort = 0;
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
sem_init(&signalSemaphore, 0, 0); sem_init(&signalSemaphore, 0, 0);
@@ -471,7 +493,7 @@ int main(int argc, char *argv[])
{ {
std::cout << "Usage: pipedal <doc_root> [<web_root>] [options...]\n\n" std::cout << "Usage: pipedal <doc_root> [<web_root>] [options...]\n\n"
<< "Options:\n" << "Options:\n"
<< " -systemd: Log to systemd journals.\n" << " -systemd: Log to systemd journals, wait for jack service.\n"
<< " -port: Port to listen on e.g. 0.0.0.0:80\n" << " -port: Port to listen on e.g. 0.0.0.0:80\n"
<< " -shutdownPort: Port for the shutdown service.\n" << " -shutdownPort: Port for the shutdown service.\n"
<< "Example:\n" << "Example:\n"
@@ -556,13 +578,65 @@ int main(int argc, char *argv[])
{ {
PiPedalModel model; PiPedalModel model;
// Pre-populate ALSA device info,wait for jackd service (daemon only)
if (systemd)
{
auto devices = model.GetAlsaDevices();
auto serverSettings = model.GetJackServerSettings();
if (serverSettings.IsValid())
{
// wait up to 15 seconds for the midi device to come online.
if (!HasAlsaDevice(devices, serverSettings.GetAlsaDevice()))
{
Lv2Log::info("Waiting for ALSA device " + serverSettings.GetAlsaDevice());
for (int i = 0; i < 15; ++i)
{
sleep(1);
devices = model.GetAlsaDevices();
if (HasAlsaDevice(devices, serverSettings.GetAlsaDevice()))
{
break;
}
if (g_SigBreak)
break;
}
}
}
Lv2Log::info("Waiting for jack service.");
// pre-cache device info.
model.GetAlsaDevices();
sd_notify(0, "READY=1");
if (!isJackServiceRunning())
{
Lv2Log::info("Waiting for Jack service.");
// wait up to 15 seconds for the jack service to come online.
for (int i = 0; i < 15; ++i)
{
// use the time to prepopulate ALSA device cache before jack
// opens the device and we can't read properties.
model.GetAlsaDevices();
sleep(1);
if (isJackServiceRunning())
{
break;
}
}
Lv2Log::info("Found Jack service.");
}
sleep(3); // jack needs a little time to get up to speed.
}
model.Load(configuration); model.Load(configuration);
auto pipedalSocketFactory = MakePiPedalSocketFactory(model); auto pipedalSocketFactory = MakePiPedalSocketFactory(model);
server->AddSocketFactory(pipedalSocketFactory); server->AddSocketFactory(pipedalSocketFactory);
std::shared_ptr<RequestHandler> interceptConfig{new InterceptConfig(port,configuration.GetMaxUploadSize())}; std::shared_ptr<RequestHandler> interceptConfig{new InterceptConfig(port, configuration.GetMaxUploadSize())};
server->AddRequestHandler(interceptConfig); server->AddRequestHandler(interceptConfig);
std::shared_ptr<DownloadIntercept> downloadIntercept = std::make_shared<DownloadIntercept>(&model); std::shared_ptr<DownloadIntercept> downloadIntercept = std::make_shared<DownloadIntercept>(&model);
@@ -572,6 +646,11 @@ int main(int argc, char *argv[])
sem_wait(&signalSemaphore); sem_wait(&signalSemaphore);
if (systemd)
{
sd_notify(0, "STOPPING=1");
}
Lv2Log::info("Shutting down gracefully."); Lv2Log::info("Shutting down gracefully.");
model.Close(); model.Close();
Lv2Log::info("Stopping web server."); Lv2Log::info("Stopping web server.");
+6 -4
View File
@@ -1,12 +1,14 @@
[Unit] [Unit]
Description=${DESCRIPTION} Description=${DESCRIPTION}
After=jack.service Before=jack.service
After=network.target After=network.target
BindsTo=jack.service After=sound.target
After=local-fs.target
[Service] [Service]
Type=notify
LimitMEMLOCK=infinity LimitMEMLOCK=infinity
LimitRTPRIO=95 LimitRTPRIO=95
ExecStart=${COMMAND} ExecStart=${COMMAND}
@@ -16,8 +18,8 @@ Restart=always
RestartSec=25 RestartSec=25
TimeoutStopSec=10 TimeoutStopSec=10
WorkingDirectory=/var/pipedal WorkingDirectory=/var/pipedal
Environment=JACK_PROMISCUOUS_SERVER= Environment=JACK_PROMISCUOUS_SERVER=jack
Environment=JACK_STJACK_NO_START_SERVER= Environment=JACK_NO_START_SERVER=1
[Install] [Install]