refactor service.conf

This commit is contained in:
Robin Davies
2022-07-12 21:10:49 -04:00
parent 2133403f7e
commit fa9888914d
38 changed files with 1181 additions and 1733 deletions
+1 -1
View File
@@ -42,7 +42,7 @@
<http://two-play.com/plugins/toob-cab-sim>
a lv2:Plugin ,
lv2:AmplifierPlugin ;
lv2:SimulatorPlugin ;
doap:name "TooB Cab Simulator" ,
"TooB Cab Simulator"@en-gb-gb
;
+5 -4
View File
@@ -52,7 +52,7 @@ inputStage:filterGroup
<http://two-play.com/plugins/toob-input_stage>
a lv2:Plugin ,
lv2:MixerPlugin ;
lv2:SimulatorPlugin ;
doap:name "TooB Input Stage" ,
"TooB Input Stage"@en-gb-gb
;
@@ -118,7 +118,8 @@ use the following plugins in the following order.
] , [
rdfs:label "-INF" ;
rdf:value -60.0
]
];
rdfs:comment "Input level trim"
], [
a lv2:InputPort ,
lv2:ControlPort ;
@@ -154,7 +155,7 @@ use the following plugins in the following order.
rdfs:label "OFF" ;
rdf:value 0.0
];
rdfs:comment "Boost high frequencies." ;
rdfs:comment "High frequency boost. Zero to disable." ;
], [
a lv2:InputPort ,
@@ -173,7 +174,7 @@ use the following plugins in the following order.
rdfs:label "OFF" ;
rdf:value 0.0
];
rdfs:comment "Bright frequency." ;
rdfs:comment "Bright cutoff frequency." ;
], [
a lv2:InputPort ,
+19 -11
View File
@@ -60,7 +60,7 @@ pstage:stage3
<http://two-play.com/plugins/toob-power-stage-2>
a lv2:Plugin ,
lv2:AmplifierPlugin ;
lv2:SimulatorPlugin ;
doap:name "TooB Power Stage";
@@ -79,8 +79,8 @@ pstage:stage3
rdfs:comment """
The TooB Power Stage provides flexible and nuanced emulations of guitar amp power stages.
The goal of TooB Power Stage is not to inflexibly emulate existing tube power stages, but instead,
to be better.
The goal of TooB Power Stage is emulate the best features of real tube amps without slavishly
reproducing the undesirable features as well.
TooB Power Stage provides 3 consecutive gain stages, each of which can be overdriven to varying
degrees. Power transformer sag is emulated using the SAG and SAGD controls, which emulate the
@@ -105,19 +105,22 @@ General experience seems to suggest that gradually increasing the gain in each s
the signal harder without introducing undesirable effects in the output. However, there definitely interesting
and subtle differences in tone that can be acheived by not adhering to that rule of thumb.
The SAG and SAGD controls are important controls. You need to undertand how they work. SAG controls how much
The SAG and SAGD controls the emulation of "forgiveness", and "compression" that are provided in a real tube
amp by sagging of power supply voltage. SAG controls how much
the overall volume of the output decreases as the transform sags. It provides the subtle "compression" that
tube amps provide, which is quite different from the compression provided by compressors. SAGD controls
the amount of additional distortion that occurs as the gain stages receive reduced voltage. It provides
the "forgiveness" of a good tube amp. Specifically, because SAGD operates in a feedback loop that runs through
the "forgiveness" of a good tube amp. Because SAGD operates in a feedback loop that runs through
all three gain stages. Increasing the distortion in the gain stages increases the power use of the gain stages;
increasing the power use of the gain stages increases the effect of SAG and SAGD which has an opposing effect
on power use. The net result is that using SAG and SAGD causes signals to break up more gradually
on power use. The net result is that SAGD causes signals to break up more gradually
(hence the perceived "forgiveness"). The effects are non-linear, so not easy to describe completely. You
are strongly urged to experiment with SAG and SAGD settings in order to get best tone results.
are strongly urged to experiment with SAG and SAGD settings in order to get best tone results. Use very
moderate settings to emulate tube amp output; higher settings of SAG and SAGD will cause audible compression
and pumping of the outputs which may or may not be desireable.
The TooB Power Stage design is based on an experimental design, that unexpectedly produced extraordinarily
good amp tones. To be perfectly honest, we have not completely explored the sonic possibilities of the
good amp tones. We have not completely explored the sonic possibilities of the
TooB Power Stage. What's known so far: it produces exceptionally pretty clean tones; it can be driven really
hard, and seems to preserve an unusual amount of detail in the signal even when being driven hard; and it
can definitely produce signals that have many of the most prized features of tube amplifiers: warmth, detail,
@@ -235,10 +238,11 @@ use the following plugins in the following order.
lv2:index 5 ;
lv2:symbol "bias1" ;
lv2:name "Bias1";
lv2:name "Bias";
lv2:default 0.0 ;
lv2:minimum -2.0 ;
lv2:maximum 2.0;
rdfs:comment "Waveshape bias (stage 1)"
],
@@ -345,10 +349,12 @@ use the following plugins in the following order.
lv2:index 11 ;
lv2:symbol "bias2" ;
lv2:name "Bias2";
lv2:name "Bias";
lv2:default 0.0 ;
lv2:minimum -2.0 ;
lv2:maximum 2.0;
rdfs:comment "Waveshape bias (stage 2)"
],
[
a lv2:InputPort ,
@@ -466,10 +472,12 @@ use the following plugins in the following order.
lv2:index 18 ;
lv2:symbol "bias3" ;
lv2:name "Bias3";
lv2:name "Bias";
lv2:default 0.0 ;
lv2:minimum -2.0 ;
lv2:maximum 2.0;
rdfs:comment "Waveshape bias (stage 3)"
],
+1 -1
View File
@@ -42,7 +42,7 @@
<http://two-play.com/plugins/toob-tone-stack>
a lv2:Plugin ,
lv2:AmplifierPlugin ;
lv2:SimulatorPlugin ;
doap:name "TooB Tone Stack" ,
"TooB Tone Stack"@en-gb-gb
;
Binary file not shown.
+5 -1
View File
@@ -41,7 +41,7 @@
lv2:minorVersion 0 ;
lv2:microVersion 13 ;
rdfs:comment """
Digital delay.
A straighforward no-frills digital delay.
""" ;
@@ -62,6 +62,7 @@ Digital delay.
lv2:maximum 1200;
units:unit units:ms;
lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic>;
rdfs:comment "Delay in ms";
],
[
@@ -76,6 +77,8 @@ Digital delay.
lv2:minimum 0.0 ;
lv2:maximum 100.0 ;
units:unit units:pc;
rdfs:comment "Attenuation of the first delay";
], [
a lv2:InputPort ,
lv2:ControlPort ;
@@ -89,6 +92,7 @@ Digital delay.
lv2:minimum 0.0 ;
lv2:maximum 100.0 ;
units:unit units:pc;
rdfs:comment "Attenuation of subequent delays";
],
[
a lv2:AudioPort ,
+65 -12
View File
@@ -40,7 +40,7 @@ toobml:filterGroup
<http://two-play.com/plugins/toob-ml>
a lv2:Plugin ,
lv2:AmplifierPlugin ;
lv2:SimulatorPlugin ;
doap:name "TooB ML Amplifier" ,
"TooB ML Amplifier"@en-gb-gb
;
@@ -84,7 +84,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
lv2:minimum -30.0 ;
lv2:maximum 30.0 ;
units:unit units:db ;
rdf:comment "Input volume trim (pre-model)";
rdfs:comment "Input volume trim (pre-model)";
],
[
a lv2:InputPort, lv2:ControlPort ;
@@ -228,6 +228,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
rdfs:label "XoticCompSP";
rdf:value 32.0
];
rdfs:comment "Select neural network model."
] ,
[
a lv2:InputPort ,
@@ -239,7 +240,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
lv2:default 3.0 ;
lv2:minimum 0.0 ;
lv2:maximum 10.0 ;
rdf:comment "Model gain (used by some but not all models)"
rdfs:comment "Model gain parameter (used by some but not all models)"
],
[
a lv2:InputPort ,
@@ -252,7 +253,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
lv2:minimum -30.0 ;
lv2:maximum 30.0 ;
units:unit units:db ;
rdf:comment "Output volume trim (pot-model)";
rdfs:comment "Output volume trim (pre-model)";
],
[
@@ -269,7 +270,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
lv2:minimum 0.0 ;
lv2:maximum 1.0 ;
rdf:comment "Bass eq (pre-model)";
rdfs:comment "Bass eq (pre-model)";
], [
a lv2:InputPort ,
lv2:ControlPort ;
@@ -285,9 +286,11 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
lv2:maximum 1.0 ;
rdf:comment "Mid eq (pre-model)";
rdfs:comment "Mid eq (pre-model)";
],
],
[
a lv2:InputPort ,
lv2:ControlPort ;
@@ -303,7 +306,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
lv2:maximum 1.0 ;
rdf:comment "Treble eq (pre-model)";
rdfs:comment "Treble eq (pre-model)";
],
[
a lv2:OutputPort ,
@@ -320,16 +323,42 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
],
[
a lv2:InputPort ,
lv2:ControlPort ;
lv2:index 8 ;
lv2:symbol "sag" ;
lv2:name "Sag Vol";
lv2:default 0.0 ;
lv2:minimum 0.0 ;
lv2:maximum 1.0;
rdfs:comment "Transformer sag (volume)." ;
],
[
a lv2:InputPort ,
lv2:ControlPort ;
lv2:index 9 ;
lv2:symbol "sagd" ;
lv2:name "Sag Dist";
lv2:default 0.0 ;
lv2:minimum 0.0 ;
lv2:maximum 1.0;
rdfs:comment "Transformer sag (distortion)." ;
],
[
a lv2:AudioPort ,
lv2:InputPort ;
lv2:index 8 ;
lv2:index 10 ;
lv2:symbol "in" ;
lv2:name "In"
], [
a lv2:AudioPort ,
lv2:OutputPort ;
lv2:index 9 ;
lv2:index 11 ;
lv2:symbol "out" ;
lv2:name "Out"
]
@@ -340,7 +369,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
atom:bufferType atom:Sequence ;
atom:supports patch:Message;
lv2:designation lv2:control ;
lv2:index 10 ;
lv2:index 12 ;
lv2:symbol "control" ;
lv2:name "Control" ;
rdfs:comment "Plugin to GUI communication" ;
@@ -351,11 +380,35 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
atom:bufferType atom:Sequence ;
atom:supports patch:Message;
lv2:designation lv2:control ;
lv2:index 11 ;
lv2:index 13 ;
lv2:symbol "notify" ;
lv2:name "Notify" ;
rdfs:comment "Plugin to GUI communication" ;
],
[
a atom:AtomPort ,
lv2:OutputPort ;
atom:bufferType atom:Sequence ;
atom:supports patch:Message;
lv2:designation lv2:control ;
lv2:index 13 ;
lv2:symbol "notify" ;
lv2:name "Notify" ;
rdfs:comment "Plugin to GUI communication" ;
],
[
a lv2:InputPort ,
lv2:ControlPort ;
lv2:index 14 ;
lv2:symbol "sagf" ;
lv2:name "SagF";
lv2:default 13.0 ;
lv2:minimum 5.0 ;
lv2:maximum 25.0;
rdfs:comment "Sag filter Fc." ;
]
.
+2 -2
View File
@@ -1,8 +1,8 @@
{
"socket_server_port": 8080,
"socket_server_port": 80,
"socket_server_address": "*",
"debug": true,
"max_upload_size": 1048576,
"fakeAndroid": true,
"fakeAndroid": false,
"ui_plugins": []
}
+6 -1
View File
@@ -44,6 +44,7 @@ export class Port implements Deserializable<Port> {
this.supports_midi = input.supports_midi;
this.supports_time_position = input.supports_time_position;
this.port_group = input.port_group;
this.comment = input.comment;
return this;
}
@@ -73,6 +74,7 @@ export class Port implements Deserializable<Port> {
supports_midi: boolean = false;
supports_time_position: boolean = false;
port_group: string = "";
comment: string = "";
}
export class PortGroup {
@@ -221,8 +223,11 @@ export class UiControl implements Deserializable<UiControl> {
this.scale_points = ScalePoint.deserialize_array(input.scale_points);
this.port_group = input.port_group;
this.units = input.units as Units;
this.comment = input.comment;
this.controlType = ControlType.Dial;
if (this.enumeration_property && this.scale_points.length === 2)
{
this.controlType = ControlType.Toggle;
@@ -239,7 +244,6 @@ export class UiControl implements Deserializable<UiControl> {
{
this.controlType = ControlType.Select;
}
return this;
}
@@ -271,6 +275,7 @@ export class UiControl implements Deserializable<UiControl> {
scale_points: ScalePoint[] = [];
port_group: string = "";
units: Units = Units.none;
comment: string = "";
// Return the value of the closest scale_point.
clampSelectValue(value: number): number{
-1
View File
@@ -503,7 +503,6 @@ export const MainPage =
GetControlView(pedalBoardItem)
)
}
</div>
<MidiBindingsDialog open={this.state.showMidiBindingsDialog}
onClose={()=> this.setState({showMidiBindingsDialog: false} ) }
+5 -1
View File
@@ -2020,14 +2020,18 @@ class PiPedalModelImpl implements PiPedalModel {
let oldSettings = this.wifiDirectConfigSettings.get();
wifiDirectConfigSettings = wifiDirectConfigSettings.clone();
if ((!oldSettings.enable) && (!wifiDirectConfigSettings.enable)) {
if ((!oldSettings.enable) && (!wifiDirectConfigSettings.enable)
&& (oldSettings.hotspotName == wifiDirectConfigSettings.hotspotName)
) {
// no effective change.
resolve();
return;
}
if (!wifiDirectConfigSettings.enable) {
let t = wifiDirectConfigSettings.hotspotName; // hotspot name can be changed when disabled because it's also the mDNS service name.
wifiDirectConfigSettings = oldSettings.clone();
wifiDirectConfigSettings.enable = false;
wifiDirectConfigSettings.hotspotName = t;
} else {
if (wifiDirectConfigSettings.countryCode === oldSettings.countryCode
&& wifiDirectConfigSettings.channel === oldSettings.channel
+6 -1
View File
@@ -455,7 +455,12 @@ const PluginControlView =
{
nodes
}
<div style={{ flex: "0 0 40px", width: 40, height: 1 }} />
<div style={{ flex: "0 0 40px", width: 40, height: 40 }} />
{
(!this.state.landscapeGrid) && (
<div style={{ flex: "0 1 100%", width: "0px", height: 40 }} />
)
}
</div>
<FullScreenIME uiControl={this.state.imeUiControl} value={this.state.imeValue}
+70 -23
View File
@@ -134,22 +134,63 @@ function makeParagraphs(description: string) {
);
}
function makeControls(controls: UiControl[]) {
return (
<Grid container direction="row" justifyContent="flex-start" alignItems="flex-start" spacing={1} style={{ paddingLeft: "24px" }}>
{
controls.map((control) => (
<Grid xs={6} sm={4} key={control.symbol} >
<Typography variant="body2">
let hasComments = false;
for (let i = 0; i < controls.length; ++i) {
if (controls[i].comment !== "") {
hasComments = true;
break;
}
}
if (hasComments) {
let trs: React.ReactElement[] = [];
for (let i = 0; i < controls.length; ++i) {
let control = controls[i];
trs.push((
<tr>
<td style={{ verticalAlign: "top" }}>
<Typography variant="body2" style={{ whiteSpace: "nowrap" }}>
{control.name}
</Typography>
</Grid>
))
}
</Grid>
);
</td>
<td style={{ paddingLeft: "16px", verticalAlign: "top" }}>
<Typography variant="body2">
{control.comment}
</Typography>
</td>
</tr >
));
}
return (
<table style={{ paddingLeft: "24px", verticalAlign: "top" }}>
{
trs
}
</table>
);
} else {
return (
<Grid container direction="row" justifyContent="flex-start" alignItems="flex-start" spacing={1} style={{ paddingLeft: "24px" }}>
{
controls.map((control) => (
<Grid xs={6} sm={4} key={control.symbol + "x"} >
<Typography variant="body2">
{control.name}
</Typography>
</Grid>
))
}
</Grid>
);
}
}
const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => {
let model = PiPedalModelFactory.getInstance();
@@ -205,19 +246,25 @@ const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => {
</div>
</MuiDialogTitle>
<PluginInfoDialogContent dividers style={{ width: "100%", maxHeight: "80%", overflowX: "hidden" }}>
<Typography gutterBottom>
Author:&nbsp;
{(plugin.author_homepage !== "")
? <a href={plugin.author_homepage} target="_blank" rel="noreferrer">{plugin.author_name}</a>
: (
plugin.author_name
)
}
</Typography>
<Typography gutterBottom>
{ioDescription(plugin)}
</Typography>
<div style={{ width: "100%",display: "flex", flexFlow: "row", justifyItems: "stretch", flexWrap: "nowrap" }} >
<div style={{ flex: "1 1 100%" }}>
<Typography gutterBottom >
Author:&nbsp;
{(plugin.author_homepage !== "")
? <a href={plugin.author_homepage} target="_blank" rel="noreferrer">{plugin.author_name}</a>
: (
plugin.author_name
)
}
</Typography>
</div>
<div style={{ flex: "0 0 auto" }}>
<Typography gutterBottom >
{ioDescription(plugin)}
</Typography>
</div>
</div>
<Typography variant="body1" gutterBottom style={{ paddingTop: "1em" }}>
Controls:
</Typography>
+7
View File
@@ -38,7 +38,14 @@ export interface ControlEntry {
};
const Utility = class {
static isLandscape(): boolean {
return window.innerWidth > window.innerHeight;
}
static isTouchDevice(): boolean {
if (!this.isLandscape()) return false;
if (window.innerHeight > 500) return false;
return (('ontouchstart' in window) &&
((navigator.maxTouchPoints??0) > 0) );
}
+18 -17
View File
@@ -339,7 +339,7 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
autoComplete="off"
id="name"
spellCheck="false"
label="SSID"
label="Service Name"
type="text"
size={this.state.controlSize}
fullWidth
@@ -348,11 +348,11 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
value={this.state.name}
onChange={(e) => this.setState({ name: e.target.value, nameError: false, nameErrorMessage: NBSP })}
inputRef={this.refName}
disabled={!this.state.enabled}
variant="filled"
/>
<TextField
required
inputRef={this.refPassword}
sx={{ maxWidth: this.state.controlWidth }}
@@ -377,7 +377,7 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
</div>
</div>
<div style={{ marginTop: 0, display: 'flex', alignItems: 'stretch', gap: 16 }}>
<TextField select label="Country" size={this.state.controlSize} value={this.state.countryCode} variant="filled"
<TextField required select label="Country" size={this.state.controlSize} value={this.state.countryCode} variant="filled"
sx={{ width: this.state.controlWidth }}
disabled={!this.state.enabled}
onChange={(event) => { this.handleCountryChanged(event); }}
@@ -413,17 +413,18 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
)}
{(!this.state.landscapeLayout) && (
<DialogContent sx={{minHeight: 96}} >
<FormControlLabel
control={(
<Switch
checked={this.state.enabled}
onChange={(e: any) => this.handleEnableChanged(e)}
color="secondary"
/>
)}
label="Wi-Fi Direct Hotspot"
/>
<div style={{ marginTop: 16 }}>
<div>
<FormControlLabel
control={(
<Switch
checked={this.state.enabled}
onChange={(e: any) => this.handleEnableChanged(e)}
color="secondary"
/>
)}
label="Wi-Fi Direct Hotspot"
/>
</div><div style={{ marginTop: 16 }}>
<TextField
required
@@ -431,7 +432,7 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
autoComplete="off"
id="name"
spellCheck="false"
label="SSID"
label="Service Name"
type="text"
size={this.state.controlSize}
fullWidth
@@ -440,10 +441,10 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
value={this.state.name}
onChange={(e) => this.setState({ name: e.target.value, nameError: false, nameErrorMessage: NBSP })}
inputRef={this.refName}
disabled={!this.state.enabled}
variant="filled"
/>
</div><div>
</div>
<div>
<TextField
inputRef={this.refPassword}
sx={{ maxWidth: this.state.controlWidth }}
+5 -5
View File
@@ -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
View File
@@ -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
-217
View File
@@ -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
View File
@@ -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
View File
@@ -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";
}
+175
View File
@@ -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();
}
+369
View File
@@ -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
View File
@@ -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;
+5
View File
@@ -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),
}};
+5
View File
@@ -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
View File
@@ -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)
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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");
+2 -2
View File
@@ -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 == "")
{
+63
View File
@@ -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
View File
@@ -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
+1
View File
@@ -7,6 +7,7 @@ After=local-fs.target
[Service]
Restart=on-failure
Type=notify
LimitMEMLOCK=infinity
LimitRTPRIO=95