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> <http://two-play.com/plugins/toob-cab-sim>
a lv2:Plugin , a lv2:Plugin ,
lv2:AmplifierPlugin ; lv2:SimulatorPlugin ;
doap:name "TooB Cab Simulator" , doap:name "TooB Cab Simulator" ,
"TooB Cab Simulator"@en-gb-gb "TooB Cab Simulator"@en-gb-gb
; ;
+5 -4
View File
@@ -52,7 +52,7 @@ inputStage:filterGroup
<http://two-play.com/plugins/toob-input_stage> <http://two-play.com/plugins/toob-input_stage>
a lv2:Plugin , a lv2:Plugin ,
lv2:MixerPlugin ; lv2:SimulatorPlugin ;
doap:name "TooB Input Stage" , doap:name "TooB Input Stage" ,
"TooB Input Stage"@en-gb-gb "TooB Input Stage"@en-gb-gb
; ;
@@ -118,7 +118,8 @@ use the following plugins in the following order.
] , [ ] , [
rdfs:label "-INF" ; rdfs:label "-INF" ;
rdf:value -60.0 rdf:value -60.0
] ];
rdfs:comment "Input level trim"
], [ ], [
a lv2:InputPort , a lv2:InputPort ,
lv2:ControlPort ; lv2:ControlPort ;
@@ -154,7 +155,7 @@ use the following plugins in the following order.
rdfs:label "OFF" ; rdfs:label "OFF" ;
rdf:value 0.0 rdf:value 0.0
]; ];
rdfs:comment "Boost high frequencies." ; rdfs:comment "High frequency boost. Zero to disable." ;
], [ ], [
a lv2:InputPort , a lv2:InputPort ,
@@ -173,7 +174,7 @@ use the following plugins in the following order.
rdfs:label "OFF" ; rdfs:label "OFF" ;
rdf:value 0.0 rdf:value 0.0
]; ];
rdfs:comment "Bright frequency." ; rdfs:comment "Bright cutoff frequency." ;
], [ ], [
a lv2:InputPort , a lv2:InputPort ,
+19 -11
View File
@@ -60,7 +60,7 @@ pstage:stage3
<http://two-play.com/plugins/toob-power-stage-2> <http://two-play.com/plugins/toob-power-stage-2>
a lv2:Plugin , a lv2:Plugin ,
lv2:AmplifierPlugin ; lv2:SimulatorPlugin ;
doap:name "TooB Power Stage"; doap:name "TooB Power Stage";
@@ -79,8 +79,8 @@ pstage:stage3
rdfs:comment """ rdfs:comment """
The TooB Power Stage provides flexible and nuanced emulations of guitar amp power stages. 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, The goal of TooB Power Stage is emulate the best features of real tube amps without slavishly
to be better. reproducing the undesirable features as well.
TooB Power Stage provides 3 consecutive gain stages, each of which can be overdriven to varying 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 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 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. 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 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 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 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; 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 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 (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 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 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 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, 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:index 5 ;
lv2:symbol "bias1" ; lv2:symbol "bias1" ;
lv2:name "Bias1"; lv2:name "Bias";
lv2:default 0.0 ; lv2:default 0.0 ;
lv2:minimum -2.0 ; lv2:minimum -2.0 ;
lv2:maximum 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:index 11 ;
lv2:symbol "bias2" ; lv2:symbol "bias2" ;
lv2:name "Bias2"; lv2:name "Bias";
lv2:default 0.0 ; lv2:default 0.0 ;
lv2:minimum -2.0 ; lv2:minimum -2.0 ;
lv2:maximum 2.0; lv2:maximum 2.0;
rdfs:comment "Waveshape bias (stage 2)"
], ],
[ [
a lv2:InputPort , a lv2:InputPort ,
@@ -466,10 +472,12 @@ use the following plugins in the following order.
lv2:index 18 ; lv2:index 18 ;
lv2:symbol "bias3" ; lv2:symbol "bias3" ;
lv2:name "Bias3"; lv2:name "Bias";
lv2:default 0.0 ; lv2:default 0.0 ;
lv2:minimum -2.0 ; lv2:minimum -2.0 ;
lv2:maximum 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> <http://two-play.com/plugins/toob-tone-stack>
a lv2:Plugin , a lv2:Plugin ,
lv2:AmplifierPlugin ; lv2:SimulatorPlugin ;
doap:name "TooB Tone Stack" , doap:name "TooB Tone Stack" ,
"TooB Tone Stack"@en-gb-gb "TooB Tone Stack"@en-gb-gb
; ;
Binary file not shown.
+5 -1
View File
@@ -41,7 +41,7 @@
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 13 ; lv2:microVersion 13 ;
rdfs:comment """ rdfs:comment """
Digital delay. A straighforward no-frills digital delay.
""" ; """ ;
@@ -62,6 +62,7 @@ Digital delay.
lv2:maximum 1200; lv2:maximum 1200;
units:unit units:ms; units:unit units:ms;
lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic>; 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:minimum 0.0 ;
lv2:maximum 100.0 ; lv2:maximum 100.0 ;
units:unit units:pc; units:unit units:pc;
rdfs:comment "Attenuation of the first delay";
], [ ], [
a lv2:InputPort , a lv2:InputPort ,
lv2:ControlPort ; lv2:ControlPort ;
@@ -89,6 +92,7 @@ Digital delay.
lv2:minimum 0.0 ; lv2:minimum 0.0 ;
lv2:maximum 100.0 ; lv2:maximum 100.0 ;
units:unit units:pc; units:unit units:pc;
rdfs:comment "Attenuation of subequent delays";
], ],
[ [
a lv2:AudioPort , a lv2:AudioPort ,
+65 -12
View File
@@ -40,7 +40,7 @@ toobml:filterGroup
<http://two-play.com/plugins/toob-ml> <http://two-play.com/plugins/toob-ml>
a lv2:Plugin , a lv2:Plugin ,
lv2:AmplifierPlugin ; lv2:SimulatorPlugin ;
doap:name "TooB ML Amplifier" , doap:name "TooB ML Amplifier" ,
"TooB ML Amplifier"@en-gb-gb "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:minimum -30.0 ;
lv2:maximum 30.0 ; lv2:maximum 30.0 ;
units:unit units:db ; units:unit units:db ;
rdf:comment "Input volume trim (pre-model)"; rdfs:comment "Input volume trim (pre-model)";
], ],
[ [
a lv2:InputPort, lv2:ControlPort ; 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"; rdfs:label "XoticCompSP";
rdf:value 32.0 rdf:value 32.0
]; ];
rdfs:comment "Select neural network model."
] , ] ,
[ [
a lv2:InputPort , 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:default 3.0 ;
lv2:minimum 0.0 ; lv2:minimum 0.0 ;
lv2:maximum 10.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 , 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:minimum -30.0 ;
lv2:maximum 30.0 ; lv2:maximum 30.0 ;
units:unit units:db ; 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:minimum 0.0 ;
lv2:maximum 1.0 ; lv2:maximum 1.0 ;
rdf:comment "Bass eq (pre-model)"; rdfs:comment "Bass eq (pre-model)";
], [ ], [
a lv2:InputPort , a lv2:InputPort ,
lv2:ControlPort ; lv2:ControlPort ;
@@ -285,9 +286,11 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
lv2:maximum 1.0 ; lv2:maximum 1.0 ;
rdf:comment "Mid eq (pre-model)"; rdfs:comment "Mid eq (pre-model)";
], ],
[ [
a lv2:InputPort , a lv2:InputPort ,
lv2:ControlPort ; lv2:ControlPort ;
@@ -303,7 +306,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
lv2:maximum 1.0 ; lv2:maximum 1.0 ;
rdf:comment "Treble eq (pre-model)"; rdfs:comment "Treble eq (pre-model)";
], ],
[ [
a lv2:OutputPort , 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 , a lv2:AudioPort ,
lv2:InputPort ; lv2:InputPort ;
lv2:index 8 ; lv2:index 10 ;
lv2:symbol "in" ; lv2:symbol "in" ;
lv2:name "In" lv2:name "In"
], [ ], [
a lv2:AudioPort , a lv2:AudioPort ,
lv2:OutputPort ; lv2:OutputPort ;
lv2:index 9 ; lv2:index 11 ;
lv2:symbol "out" ; lv2:symbol "out" ;
lv2:name "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:bufferType atom:Sequence ;
atom:supports patch:Message; atom:supports patch:Message;
lv2:designation lv2:control ; lv2:designation lv2:control ;
lv2:index 10 ; lv2:index 12 ;
lv2:symbol "control" ; lv2:symbol "control" ;
lv2:name "Control" ; lv2:name "Control" ;
rdfs:comment "Plugin to GUI communication" ; 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:bufferType atom:Sequence ;
atom:supports patch:Message; atom:supports patch:Message;
lv2:designation lv2:control ; lv2:designation lv2:control ;
lv2:index 11 ; lv2:index 13 ;
lv2:symbol "notify" ; lv2:symbol "notify" ;
lv2:name "Notify" ; lv2:name "Notify" ;
rdfs:comment "Plugin to GUI communication" ; 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": "*", "socket_server_address": "*",
"debug": true, "debug": true,
"max_upload_size": 1048576, "max_upload_size": 1048576,
"fakeAndroid": true, "fakeAndroid": false,
"ui_plugins": [] "ui_plugins": []
} }
+6 -1
View File
@@ -44,6 +44,7 @@ export class Port implements Deserializable<Port> {
this.supports_midi = input.supports_midi; this.supports_midi = input.supports_midi;
this.supports_time_position = input.supports_time_position; this.supports_time_position = input.supports_time_position;
this.port_group = input.port_group; this.port_group = input.port_group;
this.comment = input.comment;
return this; return this;
} }
@@ -73,6 +74,7 @@ export class Port implements Deserializable<Port> {
supports_midi: boolean = false; supports_midi: boolean = false;
supports_time_position: boolean = false; supports_time_position: boolean = false;
port_group: string = ""; port_group: string = "";
comment: string = "";
} }
export class PortGroup { export class PortGroup {
@@ -221,8 +223,11 @@ export class UiControl implements Deserializable<UiControl> {
this.scale_points = ScalePoint.deserialize_array(input.scale_points); this.scale_points = ScalePoint.deserialize_array(input.scale_points);
this.port_group = input.port_group; this.port_group = input.port_group;
this.units = input.units as Units; this.units = input.units as Units;
this.comment = input.comment;
this.controlType = ControlType.Dial; this.controlType = ControlType.Dial;
if (this.enumeration_property && this.scale_points.length === 2) if (this.enumeration_property && this.scale_points.length === 2)
{ {
this.controlType = ControlType.Toggle; this.controlType = ControlType.Toggle;
@@ -239,7 +244,6 @@ export class UiControl implements Deserializable<UiControl> {
{ {
this.controlType = ControlType.Select; this.controlType = ControlType.Select;
} }
return this; return this;
} }
@@ -271,6 +275,7 @@ export class UiControl implements Deserializable<UiControl> {
scale_points: ScalePoint[] = []; scale_points: ScalePoint[] = [];
port_group: string = ""; port_group: string = "";
units: Units = Units.none; units: Units = Units.none;
comment: string = "";
// Return the value of the closest scale_point. // Return the value of the closest scale_point.
clampSelectValue(value: number): number{ clampSelectValue(value: number): number{
-1
View File
@@ -503,7 +503,6 @@ export const MainPage =
GetControlView(pedalBoardItem) GetControlView(pedalBoardItem)
) )
} }
</div> </div>
<MidiBindingsDialog open={this.state.showMidiBindingsDialog} <MidiBindingsDialog open={this.state.showMidiBindingsDialog}
onClose={()=> this.setState({showMidiBindingsDialog: false} ) } onClose={()=> this.setState({showMidiBindingsDialog: false} ) }
+5 -1
View File
@@ -2020,14 +2020,18 @@ class PiPedalModelImpl implements PiPedalModel {
let oldSettings = this.wifiDirectConfigSettings.get(); let oldSettings = this.wifiDirectConfigSettings.get();
wifiDirectConfigSettings = wifiDirectConfigSettings.clone(); wifiDirectConfigSettings = wifiDirectConfigSettings.clone();
if ((!oldSettings.enable) && (!wifiDirectConfigSettings.enable)) { if ((!oldSettings.enable) && (!wifiDirectConfigSettings.enable)
&& (oldSettings.hotspotName == wifiDirectConfigSettings.hotspotName)
) {
// no effective change. // no effective change.
resolve(); resolve();
return; return;
} }
if (!wifiDirectConfigSettings.enable) { 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 = oldSettings.clone();
wifiDirectConfigSettings.enable = false; wifiDirectConfigSettings.enable = false;
wifiDirectConfigSettings.hotspotName = t;
} else { } else {
if (wifiDirectConfigSettings.countryCode === oldSettings.countryCode if (wifiDirectConfigSettings.countryCode === oldSettings.countryCode
&& wifiDirectConfigSettings.channel === oldSettings.channel && wifiDirectConfigSettings.channel === oldSettings.channel
+6 -1
View File
@@ -455,7 +455,12 @@ const PluginControlView =
{ {
nodes 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> </div>
<FullScreenIME uiControl={this.state.imeUiControl} value={this.state.imeValue} <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[]) { function makeControls(controls: UiControl[]) {
return ( let hasComments = false;
<Grid container direction="row" justifyContent="flex-start" alignItems="flex-start" spacing={1} style={{ paddingLeft: "24px" }}>
{ for (let i = 0; i < controls.length; ++i) {
controls.map((control) => ( if (controls[i].comment !== "") {
<Grid xs={6} sm={4} key={control.symbol} > hasComments = true;
<Typography variant="body2"> 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} {control.name}
</Typography> </Typography>
</Grid> </td>
)) <td style={{ paddingLeft: "16px", verticalAlign: "top" }}>
} <Typography variant="body2">
</Grid> {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) => { const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => {
let model = PiPedalModelFactory.getInstance(); let model = PiPedalModelFactory.getInstance();
@@ -205,19 +246,25 @@ const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => {
</div> </div>
</MuiDialogTitle> </MuiDialogTitle>
<PluginInfoDialogContent dividers style={{ width: "100%", maxHeight: "80%", overflowX: "hidden" }}> <PluginInfoDialogContent dividers style={{ width: "100%", maxHeight: "80%", overflowX: "hidden" }}>
<Typography gutterBottom> <div style={{ width: "100%",display: "flex", flexFlow: "row", justifyItems: "stretch", flexWrap: "nowrap" }} >
Author:&nbsp; <div style={{ flex: "1 1 100%" }}>
{(plugin.author_homepage !== "") <Typography gutterBottom >
? <a href={plugin.author_homepage} target="_blank" rel="noreferrer">{plugin.author_name}</a> Author:&nbsp;
: ( {(plugin.author_homepage !== "")
plugin.author_name ? <a href={plugin.author_homepage} target="_blank" rel="noreferrer">{plugin.author_name}</a>
) : (
} plugin.author_name
</Typography> )
<Typography gutterBottom> }
{ioDescription(plugin)} </Typography>
</Typography> </div>
<div style={{ flex: "0 0 auto" }}>
<Typography gutterBottom >
{ioDescription(plugin)}
</Typography>
</div>
</div>
<Typography variant="body1" gutterBottom style={{ paddingTop: "1em" }}> <Typography variant="body1" gutterBottom style={{ paddingTop: "1em" }}>
Controls: Controls:
</Typography> </Typography>
+7
View File
@@ -38,7 +38,14 @@ export interface ControlEntry {
}; };
const Utility = class { const Utility = class {
static isLandscape(): boolean {
return window.innerWidth > window.innerHeight;
}
static isTouchDevice(): boolean { static isTouchDevice(): boolean {
if (!this.isLandscape()) return false;
if (window.innerHeight > 500) return false;
return (('ontouchstart' in window) && return (('ontouchstart' in window) &&
((navigator.maxTouchPoints??0) > 0) ); ((navigator.maxTouchPoints??0) > 0) );
} }
+18 -17
View File
@@ -339,7 +339,7 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
autoComplete="off" autoComplete="off"
id="name" id="name"
spellCheck="false" spellCheck="false"
label="SSID" label="Service Name"
type="text" type="text"
size={this.state.controlSize} size={this.state.controlSize}
fullWidth fullWidth
@@ -348,11 +348,11 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
value={this.state.name} value={this.state.name}
onChange={(e) => this.setState({ name: e.target.value, nameError: false, nameErrorMessage: NBSP })} onChange={(e) => this.setState({ name: e.target.value, nameError: false, nameErrorMessage: NBSP })}
inputRef={this.refName} inputRef={this.refName}
disabled={!this.state.enabled}
variant="filled" variant="filled"
/> />
<TextField <TextField
required
inputRef={this.refPassword} inputRef={this.refPassword}
sx={{ maxWidth: this.state.controlWidth }} sx={{ maxWidth: this.state.controlWidth }}
@@ -377,7 +377,7 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
</div> </div>
</div> </div>
<div style={{ marginTop: 0, display: 'flex', alignItems: 'stretch', gap: 16 }}> <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 }} sx={{ width: this.state.controlWidth }}
disabled={!this.state.enabled} disabled={!this.state.enabled}
onChange={(event) => { this.handleCountryChanged(event); }} onChange={(event) => { this.handleCountryChanged(event); }}
@@ -413,17 +413,18 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
)} )}
{(!this.state.landscapeLayout) && ( {(!this.state.landscapeLayout) && (
<DialogContent sx={{minHeight: 96}} > <DialogContent sx={{minHeight: 96}} >
<FormControlLabel <div>
control={( <FormControlLabel
<Switch control={(
checked={this.state.enabled} <Switch
onChange={(e: any) => this.handleEnableChanged(e)} checked={this.state.enabled}
color="secondary" onChange={(e: any) => this.handleEnableChanged(e)}
/> color="secondary"
)} />
label="Wi-Fi Direct Hotspot" )}
/> label="Wi-Fi Direct Hotspot"
<div style={{ marginTop: 16 }}> />
</div><div style={{ marginTop: 16 }}>
<TextField <TextField
required required
@@ -431,7 +432,7 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
autoComplete="off" autoComplete="off"
id="name" id="name"
spellCheck="false" spellCheck="false"
label="SSID" label="Service Name"
type="text" type="text"
size={this.state.controlSize} size={this.state.controlSize}
fullWidth fullWidth
@@ -440,10 +441,10 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
value={this.state.name} value={this.state.name}
onChange={(e) => this.setState({ name: e.target.value, nameError: false, nameErrorMessage: NBSP })} onChange={(e) => this.setState({ name: e.target.value, nameError: false, nameErrorMessage: NBSP })}
inputRef={this.refName} inputRef={this.refName}
disabled={!this.state.enabled}
variant="filled" variant="filled"
/> />
</div><div> </div>
<div>
<TextField <TextField
inputRef={this.refPassword} inputRef={this.refPassword}
sx={{ maxWidth: this.state.controlWidth }} 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 unmask jack");
silentSysExec("/usr/bin/systemctl enable 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); std::thread delayedRestartThread(delayedRestartProc);
delayedRestartThread.detach(); 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; return true;
} }
+30 -7
View File
@@ -268,6 +268,15 @@ namespace pipedal
snd_pcm_sw_params_free(playbackSwParams); snd_pcm_sw_params_free(playbackSwParams);
playbackSwParams = nullptr; playbackSwParams = nullptr;
} }
for (auto *midiState: this->midiStates)
{
if (midiState != nullptr)
{
midiState->Close();
delete midiState;
}
}
midiStates.resize(0);
} }
std::string discover_alsa_using_apps() std::string discover_alsa_using_apps()
@@ -573,9 +582,14 @@ namespace pipedal
this->channelSelection = channelSelection; this->channelSelection = channelSelection;
open = true; open = true;
try {
OpenMidi(jackServerSettings, channelSelection); OpenMidi(jackServerSettings, channelSelection);
OpenAudio(jackServerSettings, channelSelection); OpenAudio(jackServerSettings, channelSelection);
} catch (const std::exception&e)
{
Close();
throw;
}
} }
void OpenAudio(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection) void OpenAudio(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
@@ -1512,7 +1526,7 @@ namespace pipedal
try try
{ {
int err; 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); err = snd_pcm_open(&playbackHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (err == -EBUSY) if (err == -EBUSY)
@@ -1524,13 +1538,22 @@ namespace pipedal
} }
if (err < 0) if (err < 0)
{ {
throw PiPedalStateException(SS(alsaDeviceName << " not found. " throw PiPedalStateException(SS(alsaDeviceName << " playback device not found. "
<< "(" << snd_strerror(err) << ")")); << "(" << 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) 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) 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 CpuUse.hpp CpuUse.cpp
P2pConfigFiles.hpp P2pConfigFiles.hpp
AvahiService.cpp AvahiService.hpp AvahiService.cpp AvahiService.hpp
DeviceIdFile.cpp DeviceIdFile.hpp ServiceConfiguration.cpp ServiceConfiguration.hpp
WriteTemplateFile.cpp WriteTemplateFile.hpp WriteTemplateFile.cpp WriteTemplateFile.hpp
ConfigUtil.hpp ConfigUtil.cpp
ConfigSerializer.h ConfigSerializer.cpp
StdErrorCapture.hpp StdErrorCapture.cpp StdErrorCapture.hpp StdErrorCapture.cpp
Ipv6Helpers.cpp Ipv6Helpers.hpp Ipv6Helpers.cpp Ipv6Helpers.hpp
PluginPreset.cpp PluginPreset.hpp PluginPreset.cpp PluginPreset.hpp
@@ -236,12 +237,13 @@ add_test(NAME DevTest COMMAND pipedaltest "[Dev]")
add_executable(pipedalconfig add_executable(pipedalconfig
PrettyPrinter.hpp PrettyPrinter.hpp
DeviceIdFile.cpp DeviceIdFile.hpp ServiceConfiguration.cpp ServiceConfiguration.hpp
json.cpp json.hpp json.cpp json.hpp
HtmlHelper.cpp HtmlHelper.hpp HtmlHelper.cpp HtmlHelper.hpp
CommandLineParser.hpp CommandLineParser.hpp
WriteTemplateFile.hpp WriteTemplateFile.cpp WriteTemplateFile.hpp WriteTemplateFile.cpp
PiPedalException.hpp PiPedalException.hpp
ConfigSerializer.h ConfigSerializer.cpp
ConfigMain.cpp ConfigMain.cpp
WifiConfigSettings.cpp WifiConfigSettings.hpp WifiConfigSettings.cpp WifiConfigSettings.hpp
WifiDirectConfigSettings.cpp WifiDirectConfigSettings.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 add_executable(pipedaladmind AdminMain.cpp CommandLineParser.hpp
UnixSocket.cpp UnixSocket.hpp UnixSocket.cpp UnixSocket.hpp
DeviceIdFile.cpp DeviceIdFile.hpp ServiceConfiguration.cpp ServiceConfiguration.hpp
ConfigSerializer.h ConfigSerializer.cpp
autoptr_vector.h
JackServerSettings.hpp JackServerSettings.cpp JackServerSettings.hpp JackServerSettings.cpp
json.hpp json.cpp HtmlHelper.hpp HtmlHelper.cpp Lv2Log.hpp Lv2Log.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 "JackServerSettings.hpp"
#include "P2pConfigFiles.hpp" #include "P2pConfigFiles.hpp"
#include "PrettyPrinter.hpp" #include "PrettyPrinter.hpp"
#include "DeviceIdFile.hpp" #include "ServiceConfiguration.hpp"
#include <uuid/uuid.h> #include <uuid/uuid.h>
#include <random> #include <random>
#include "AudioConfig.hpp" #include "AudioConfig.hpp"
@@ -366,6 +366,7 @@ void InstallLimits()
} }
#if JACK_HOST
void MaybeStartJackService() void MaybeStartJackService()
{ {
std::filesystem::path drcFile = "/etc/jackdrc"; std::filesystem::path drcFile = "/etc/jackdrc";
@@ -379,7 +380,9 @@ void MaybeStartJackService()
silentSysExec(SYSTEMCTL_BIN " mask jack"); silentSysExec(SYSTEMCTL_BIN " mask jack");
} }
} }
void InstallJackService() #endif
void InstallAudioService()
{ {
InstallLimits(); InstallLimits();
InstallPamEnv(); 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() void Uninstall()
{ {
try try
@@ -444,9 +469,13 @@ void Uninstall()
StopService(); StopService();
DisableService(); DisableService();
UninstallP2pdService();
#if UNINSTALL_JACK_SERVICE
silentSysExec(SYSTEMCTL_BIN " stop jack"); silentSysExec(SYSTEMCTL_BIN " stop jack");
silentSysExec(SYSTEMCTL_BIN " disable jack"); silentSysExec(SYSTEMCTL_BIN " disable jack");
OnWifiUninstall(); #endif
try try
{ {
@@ -545,33 +574,33 @@ static std::string RandomChars(int nchars)
return s.str(); 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) void Install(const std::filesystem::path &programPrefix, const std::string endpointAddress)
{ {
if (sysExec(GROUPADD_BIN " -f " AUDIO_SERVICE_GROUP_NAME) != EXIT_SUCCESS) 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."); throw PiPedalException("Failed to create pipedald service group.");
} }
PrepareDeviceidFile(); InstallAudioService();
InstallJackService();
auto endpos = endpointAddress.find_last_of(':'); auto endpos = endpointAddress.find_last_of(':');
if (endpos == string::npos) if (endpos == string::npos)
{ {
@@ -610,6 +638,9 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
s << "Invalid port number: " << strPort; s << "Invalid port number: " << strPort;
throw PiPedalException(s.str()); throw PiPedalException(s.str());
} }
PrepareServiceConfigurationFile(port);
bool authBindRequired = port < 512; bool authBindRequired = port < 512;
@@ -744,6 +775,8 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
RestartService(false); RestartService(false);
EnableService(); EnableService();
RestartP2pdService();
cout << "Complete" cout << "Complete"
<< "\n"; << "\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; isOpen = false;
StopReaderThread(); StopReaderThread();
if (realtimeMonitorPortSubscriptions != nullptr)
{
delete realtimeMonitorPortSubscriptions;
realtimeMonitorPortSubscriptions = nullptr;
}
if (active) if (active)
{ {
audioDriver->Deactivate(); audioDriver->Deactivate();
@@ -933,8 +939,9 @@ public:
Lv2Log::info("Audio started."); Lv2Log::info("Audio started.");
} }
catch (PiPedalException &e) catch (const std::exception &e)
{ {
Lv2Log::error(SS("Failed to start audio. " << e.what()));
Close(); Close();
active = false; active = false;
throw; 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); NodeAutoFree unitsValueUri = lilv_port_get(plugin, pPort, host->lilvUris.unitsUri);
this->units_ = UriToUnits(nodeAsString(unitsValueUri)); 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); NodeAutoFree bufferType = lilv_port_get(plugin, pPort, host->lilvUris.bufferType_uri);
this->buffer_type_ = ""; this->buffer_type_ = "";
@@ -1167,6 +1170,7 @@ json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{{
MAP_REF(Lv2PortInfo, buffer_type), MAP_REF(Lv2PortInfo, buffer_type),
MAP_REF(Lv2PortInfo, port_group), MAP_REF(Lv2PortInfo, port_group),
json_map::enum_reference("units", &Lv2PortInfo::units_, get_units_enum_converter()), json_map::enum_reference("units", &Lv2PortInfo::units_, get_units_enum_converter()),
MAP_REF(Lv2PortInfo, comment)
}}; }};
json_map::storage_type<Lv2PortGroup> Lv2PortGroup::jmap{{ json_map::storage_type<Lv2PortGroup> Lv2PortGroup::jmap{{
@@ -1229,6 +1233,7 @@ json_map::storage_type<Lv2PluginUiControlPort> Lv2PluginUiControlPort::jmap{{
MAP_REF(Lv2PluginUiControlPort, port_group), MAP_REF(Lv2PluginUiControlPort, port_group),
json_map::enum_reference("units", &Lv2PluginUiControlPort::units_, get_units_enum_converter()), 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_; std::string designation_;
Units units_ = Units::none; Units units_ = Units::none;
std::string comment_;
public: public:
bool IsSwitch() const { bool IsSwitch() const {
return min_value_ == 0 && max_value_ == 1 return min_value_ == 0 && max_value_ == 1
@@ -261,6 +262,7 @@ public:
LV2_PROPERTY_GETSET_SCALAR(toggled_property); LV2_PROPERTY_GETSET_SCALAR(toggled_property);
LV2_PROPERTY_GETSET_SCALAR(not_on_gui); LV2_PROPERTY_GETSET_SCALAR(not_on_gui);
LV2_PROPERTY_GETSET(port_group); LV2_PROPERTY_GETSET(port_group);
LV2_PROPERTY_GETSET(comment);
LV2_PROPERTY_GETSET_SCALAR(units); LV2_PROPERTY_GETSET_SCALAR(units);
LV2_PROPERTY_GETSET(buffer_type); LV2_PROPERTY_GETSET(buffer_type);
@@ -417,6 +419,7 @@ public:
, toggled_property_(pPort->toggled_property()) , toggled_property_(pPort->toggled_property())
, not_on_gui_(pPort->not_on_gui()) , not_on_gui_(pPort->not_on_gui())
, scale_points_(pPort->scale_points()) , scale_points_(pPort->scale_points())
, comment_(pPort->comment())
, units_(pPort->units()) , units_(pPort->units())
{ {
// Use symbols to index port groups, instead of uris. // Use symbols to index port groups, instead of uris.
@@ -450,6 +453,7 @@ private:
std::vector<Lv2ScalePoint> scale_points_; std::vector<Lv2ScalePoint> scale_points_;
std::string port_group_; std::string port_group_;
Units units_ = Units::none; Units units_ = Units::none;
std::string comment_;
public: public:
LV2_PROPERTY_GETSET(symbol); LV2_PROPERTY_GETSET(symbol);
LV2_PROPERTY_GETSET_SCALAR(index); LV2_PROPERTY_GETSET_SCALAR(index);
@@ -466,6 +470,7 @@ public:
LV2_PROPERTY_GETSET_SCALAR(not_on_gui); LV2_PROPERTY_GETSET_SCALAR(not_on_gui);
LV2_PROPERTY_GETSET(scale_points); LV2_PROPERTY_GETSET(scale_points);
LV2_PROPERTY_GETSET(units); LV2_PROPERTY_GETSET(units);
LV2_PROPERTY_GETSET(comment);
public: public:
static json_map::storage_type<Lv2PluginUiControlPort> jmap; 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. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "pch.h" #include "pch.h"
#include "DeviceIdFile.hpp" #include "ServiceConfiguration.hpp"
#include "AudioConfig.hpp" #include "AudioConfig.hpp"
#include "ConfigUtil.hpp"
#include <sched.h> #include <sched.h>
#include "PiPedalModel.hpp" #include "PiPedalModel.hpp"
#include "JackHost.hpp" #include "JackHost.hpp"
@@ -55,8 +56,6 @@ PiPedalModel::PiPedalModel()
#else #else
this->jackServerSettings = this->storage.GetJackServerSettings(); this->jackServerSettings = this->storage.GetJackServerSettings();
#endif #endif
} }
void PiPedalModel::Close() void PiPedalModel::Close()
@@ -84,37 +83,40 @@ void PiPedalModel::Close()
PiPedalModel::~PiPedalModel() PiPedalModel::~PiPedalModel()
{ {
try { try
adminClient.UnmonitorGovernor(); {
} catch (...) // noexcept here! adminClient.UnmonitorGovernor();
}
catch (...) // noexcept here!
{ {
} }
try { try
{
CurrentPreset currentPreset; CurrentPreset currentPreset;
currentPreset.modified_ = this->hasPresetChanged; currentPreset.modified_ = this->hasPresetChanged;
currentPreset.preset_ = this->pedalBoard; currentPreset.preset_ = this->pedalBoard;
storage.SaveCurrentPreset(currentPreset); storage.SaveCurrentPreset(currentPreset);
} catch (...)
{
} }
catch (...)
try { {
}
try
{
if (jackHost) if (jackHost)
{ {
jackHost->Close(); jackHost->Close();
} }
} catch (...) }
catch (...)
{ {
} }
} }
#include <fstream> #include <fstream>
void PiPedalModel::Init(const PiPedalConfiguration&configuration) void PiPedalModel::Init(const PiPedalConfiguration &configuration)
{ {
this->configuration = configuration; this->configuration = configuration;
storage.SetConfigRoot(configuration.GetDocRoot()); storage.SetConfigRoot(configuration.GetDocRoot());
@@ -122,7 +124,6 @@ void PiPedalModel::Init(const PiPedalConfiguration&configuration)
storage.Initialize(); storage.Initialize();
this->jackServerSettings.ReadJackConfiguration(); this->jackServerSettings.ReadJackConfiguration();
} }
void PiPedalModel::LoadLv2PluginInfo() void PiPedalModel::LoadLv2PluginInfo()
@@ -147,14 +148,14 @@ void PiPedalModel::LoadLv2PluginInfo()
// Copy all presets out of Lilv data to json files // Copy all presets out of Lilv data to json files
// so that we can close lilv while we're actually // so that we can close lilv while we're actually
// running. // running.
for (const auto&plugin: lv2Host.GetPlugins()) for (const auto &plugin : lv2Host.GetPlugins())
{ {
if (plugin->has_factory_presets()) if (plugin->has_factory_presets())
{ {
if (!storage.HasPluginPresets(plugin->uri())) if (!storage.HasPluginPresets(plugin->uri()))
{ {
PluginPresets pluginPresets = lv2Host.GetFactoryPluginPresets(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->webRoot = configuration.GetWebRoot();
this->webPort = (uint16_t)configuration.GetSocketServerPort(); this->webPort = (uint16_t)configuration.GetSocketServerPort();
adminClient.MonitorGovernor(storage.GetGovernorSettings()); adminClient.MonitorGovernor(storage.GetGovernorSettings());
// lv2Host.Load(configuration.GetLv2Path().c_str()); // lv2Host.Load(configuration.GetLv2Path().c_str());
@@ -200,8 +200,8 @@ void PiPedalModel::Load()
struct sched_param scheduler_params; struct sched_param scheduler_params;
scheduler_params.sched_priority = 10; scheduler_params.sched_priority = 10;
memset(&scheduler_params,0,sizeof(sched_param)); memset(&scheduler_params, 0, sizeof(sched_param));
sched_setscheduler(0,SCHED_RR,&scheduler_params); sched_setscheduler(0, SCHED_RR, &scheduler_params);
#if JACK_HOST #if JACK_HOST
this->jackConfiguration = this->jackConfiguration.JackInitialize(); this->jackConfiguration = this->jackConfiguration.JackInitialize();
#else #else
@@ -216,7 +216,7 @@ void PiPedalModel::Load()
this->lv2Host.OnConfigurationChanged(jackConfiguration, selection); this->lv2Host.OnConfigurationChanged(jackConfiguration, selection);
try try
{ {
jackHost->Open(this->jackServerSettings,selection); jackHost->Open(this->jackServerSettings, selection);
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)}; std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
this->lv2PedalBoard = lv2PedalBoard; this->lv2PedalBoard = lv2PedalBoard;
@@ -226,7 +226,9 @@ void PiPedalModel::Load()
{ {
Lv2Log::error("Failed to load initial plugin. (%s)", e.what()); Lv2Log::error("Failed to load initial plugin. (%s)", e.what());
} }
} else { }
else
{
Lv2Log::error("Audio device not configured."); Lv2Log::error("Audio device not configured.");
} }
} }
@@ -453,7 +455,7 @@ void PiPedalModel::FirePresetsChanged(int64_t clientId)
delete[] t; delete[] t;
} }
} }
void PiPedalModel::FirePluginPresetsChanged(const std::string & pluginUri) void PiPedalModel::FirePluginPresetsChanged(const std::string &pluginUri)
{ {
std::lock_guard<std::recursive_mutex> guard{mutex}; 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) void PiPedalModel::SaveCurrentPreset(int64_t clientId)
{ {
std::lock_guard<std::recursive_mutex> guard{mutex}; std::lock_guard<std::recursive_mutex> guard{mutex};
@@ -482,31 +483,31 @@ void PiPedalModel::SaveCurrentPreset(int64_t clientId)
this->SetPresetChanged(clientId, false); 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); FirePluginPresetsChanged(pluginUri);
return result; return result;
} }
void PiPedalModel::UpdatePluginPresets(const PluginUiPresets&pluginPresets) void PiPedalModel::UpdatePluginPresets(const PluginUiPresets &pluginPresets)
{ {
storage.UpdatePluginPresets(pluginPresets); storage.UpdatePluginPresets(pluginPresets);
FirePluginPresetsChanged(pluginPresets.pluginUri_); 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); auto *item = this->pedalBoard.GetItem(instanceId);
if (!item) if (!item)
{ {
throw PiPedalException("Plugin not found."); throw PiPedalException("Plugin not found.");
} }
std::map<std::string,float> values; std::map<std::string, float> values;
for (const auto & controlValue: item->controlValues()) for (const auto &controlValue : item->controlValues())
{ {
values[controlValue.key()] = controlValue.value(); 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()); FirePluginPresetsChanged(item->uri());
return presetId; return presetId;
} }
@@ -522,16 +523,15 @@ int64_t PiPedalModel::SaveCurrentPresetAs(int64_t clientId, const std::string &n
return result; return result;
} }
void PiPedalModel::UploadPluginPresets(const PluginPresets&pluginPresets) void PiPedalModel::UploadPluginPresets(const PluginPresets &pluginPresets)
{ {
if (pluginPresets.pluginUri_.length() == 0) if (pluginPresets.pluginUri_.length() == 0)
{ {
throw PiPedalException("Invalid plugin presets."); throw PiPedalException("Invalid plugin presets.");
} }
std::lock_guard<std::recursive_mutex> guard{mutex}; std::lock_guard<std::recursive_mutex> guard{mutex};
storage.SavePluginPresets(pluginPresets.pluginUri_,pluginPresets); storage.SavePluginPresets(pluginPresets.pluginUri_, pluginPresets);
FirePluginPresetsChanged(pluginPresets.pluginUri_); FirePluginPresetsChanged(pluginPresets.pluginUri_);
} }
int64_t PiPedalModel::UploadPreset(const BankFile &bankFile, int64_t uploadAfter) 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. -1, // can't use cached version.
newSelection); newSelection);
} }
return newSelection; return newSelection;
} }
bool PiPedalModel::RenamePreset(int64_t clientId, int64_t instanceId, const std::string &name) bool PiPedalModel::RenamePreset(int64_t clientId, int64_t instanceId, const std::string &name)
{ {
@@ -649,13 +649,13 @@ GovernorSettings PiPedalModel::GetGovernorSettings()
return result; return result;
} }
} }
void PiPedalModel::SetGovernorSettings(const std::string& governor) void PiPedalModel::SetGovernorSettings(const std::string &governor)
{ {
std::lock_guard<std::recursive_mutex> guard(mutex); std::lock_guard<std::recursive_mutex> guard(mutex);
adminClient.SetGovernorSettings(governor); adminClient.SetGovernorSettings(governor);
this->storage.SetGovernorSettings(governor); this->storage.SetGovernorSettings(governor);
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()]; IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
{ {
for (size_t i = 0; i < subscribers.size(); ++i) for (size_t i = 0; i < subscribers.size(); ++i)
@@ -669,9 +669,8 @@ void PiPedalModel::SetGovernorSettings(const std::string& governor)
} }
delete[] t; delete[] t;
} }
} }
void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings) void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings)
{ {
std::lock_guard<std::recursive_mutex> guard(mutex); 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() void PiPedalModel::UpdateDnsSd()
{ {
avahiService.Unannounce(); avahiService.Unannounce();
DeviceIdFile deviceIdFile; ServiceConfiguration deviceIdFile;
deviceIdFile.Load(); deviceIdFile.Load();
std::string p2pdName = GetP2pdName();
if (p2pdName != "") {
deviceIdFile.deviceName = p2pdName;
}
if (deviceIdFile.deviceName != "" && deviceIdFile.uuid != "") if (deviceIdFile.deviceName != "" && deviceIdFile.uuid != "")
{ {
avahiService.Announce(webPort,deviceIdFile.deviceName,deviceIdFile.uuid,"pipedal"); avahiService.Announce(webPort, deviceIdFile.deviceName, deviceIdFile.uuid, "pipedal");
} else {
}
else
{
// device_uuid file is written at install time. This warning is harmless if you're debugging. // 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. // Without it, we can't pulblish the website via dnsDS.
// Run "pipedalconfig --install" to create the file. // 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) void PiPedalModel::SetWifiDirectConfigSettings(const WifiDirectConfigSettings &wifiDirectConfigSettings)
{ {
@@ -741,11 +764,7 @@ void PiPedalModel::SetWifiDirectConfigSettings(const WifiDirectConfigSettings &w
// update NSD-SD announement. // update NSD-SD announement.
UpdateDnsSd(); UpdateDnsSd();
} }
} }
WifiConfigSettings PiPedalModel::GetWifiConfigSettings() WifiConfigSettings PiPedalModel::GetWifiConfigSettings()
@@ -786,7 +805,6 @@ bool PiPedalModel::GetShowStatusMonitor()
{ {
std::lock_guard<std::recursive_mutex> guard(mutex); // copy atomically. std::lock_guard<std::recursive_mutex> guard(mutex); // copy atomically.
return storage.GetShowStatusMonitor(); return storage.GetShowStatusMonitor();
} }
JackConfiguration PiPedalModel::GetJackConfiguration() JackConfiguration PiPedalModel::GetJackConfiguration()
@@ -795,37 +813,82 @@ JackConfiguration PiPedalModel::GetJackConfiguration()
return this->jackConfiguration; 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()) if (this->jackHost->IsOpen())
{ {
this->jackHost->Close(); this->jackHost->Close();
#ifdef JUNK }
// restarting is a bit dodgy. It was impossible with Jack, but // restarting is a bit dodgy. It was impossible with Jack, but
// now very plausible with the ALSA audio stack. // now very plausible with the ALSA audio stack.
// Still bugs wrt/ restarting the circular buffers for the audio thread. // 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. // the process.
//... //...
// do a complete reload. // do a complete reload.
this->jackHost->SetPedalBoard(nullptr); this->jackHost->SetPedalBoard(nullptr);
this->jackHost->Open(this->jackServerSettings,channelSelection);
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)}; std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
this->lv2PedalBoard = lv2PedalBoard; this->lv2PedalBoard = lv2PedalBoard;
jackHost->SetPedalBoard(lv2PedalBoard); jackHost->SetPedalBoard(lv2PedalBoard);
this->UpdateRealtimeVuSubscriptions(); 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) void PiPedalModel::FireChannelSelectionChanged(int64_t clientId)
@@ -1024,7 +1087,6 @@ void PiPedalModel::UnmonitorPort(int64_t subscriptionHandle)
} }
} }
void PiPedalModel::OnNotifyMonitorPort(const MonitorPortUpdate &update) void PiPedalModel::OnNotifyMonitorPort(const MonitorPortUpdate &update)
{ {
std::lock_guard<std::recursive_mutex> guard(mutex); std::lock_guard<std::recursive_mutex> guard(mutex);
@@ -1095,7 +1157,7 @@ void PiPedalModel::GetLv2Parameter(
BankIndex PiPedalModel::GetBankIndex() const 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(); return storage.GetBanks();
} }
@@ -1137,16 +1199,17 @@ JackServerSettings PiPedalModel::GetJackServerSettings()
void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSettings) 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()) if (!adminClient.CanUseShutdownClient())
{ {
throw PiPedalException("Can't change server settings when running a debug server."); throw PiPedalException("Can't change server settings when running a debug server.");
} }
#endif
this->jackServerSettings = jackServerSettings; this->jackServerSettings = jackServerSettings;
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us) // 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()]; IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
for (size_t i = 0; i < subscribers.size(); ++i) for (size_t i = 0; i < subscribers.size(); ++i)
@@ -1160,19 +1223,25 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
} }
delete[] t; delete[] t;
#if ALSA_HOST
storage.SetJackServerSettings(jackServerSettings);
FireJackConfigurationChanged(this->jackConfiguration);
guard.unlock();
RestartAudio();
#endif
#if JACK_HOST
if (adminClient.CanUseShutdownClient()) if (adminClient.CanUseShutdownClient())
{ {
#if ALSA_HOST
storage.SetJackServerSettings(jackServerSettings);
#endif
// save the current (edited) preset now in case the service shutdown isn't clean. // save the current (edited) preset now in case the service shutdown isn't clean.
CurrentPreset currentPreset; CurrentPreset currentPreset;
currentPreset.modified_ = this->hasPresetChanged; currentPreset.modified_ = this->hasPresetChanged;
currentPreset.preset_ = this->pedalBoard; currentPreset.preset_ = this->pedalBoard;
storage.SaveCurrentPreset(currentPreset); storage.SaveCurrentPreset(currentPreset);
this->jackConfiguration.SetIsRestarting(true); this->jackConfiguration.SetIsRestarting(true);
FireJackConfigurationChanged(this->jackConfiguration); FireJackConfigurationChanged(this->jackConfiguration);
this->jackHost->UpdateServerConfiguration( this->jackHost->UpdateServerConfiguration(
@@ -1195,8 +1264,8 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
} }
else else
{ {
// we now do a complete restart of the services, // we now do a complete restart of the services,
// so just sit tight and wait for the restart. // so just sit tight and wait for the restart.
#ifdef JUNK #ifdef JUNK
this->jackConfiguration.SetErrorStatus(""); this->jackConfiguration.SetErrorStatus("");
FireJackConfigurationChanged(this->jackConfiguration); FireJackConfigurationChanged(this->jackConfiguration);
@@ -1211,7 +1280,9 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
#endif #endif
} }
}); });
}
}
#endif
} }
void PiPedalModel::UpdateDefaults(PedalBoardItem *pedalBoardItem) 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); std::lock_guard<std::recursive_mutex> guard(mutex);
return storage.GetPluginPresets(pluginUri); 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); std::lock_guard<std::recursive_mutex> guard(mutex);
@@ -1332,7 +1402,7 @@ void PiPedalModel::DeleteMidiListeners(int64_t clientId)
jackHost->SetListenForMidiEvent(midiEventListeners.size() != 0); 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); 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); auto subscriber = this->GetNotificationSubscriber(listener.clientId);
if (subscriber) if (subscriber)
{ {
subscriber->OnNotifyAtomOutput(listener.clientHandle,instanceId,atomType,atomJson); subscriber->OnNotifyAtomOutput(listener.clientHandle, instanceId, atomType, atomJson);
} else { }
else
{
atomOutputListeners.erase(atomOutputListeners.begin() + i); atomOutputListeners.erase(atomOutputListeners.begin() + i);
--i; --i;
} }
} }
} }
jackHost->SetListenForAtomOutput(atomOutputListeners.size() != 0); jackHost->SetListenForAtomOutput(atomOutputListeners.size() != 0);
} }
void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl) void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl)
{ {
std::lock_guard<std::recursive_mutex> guard(mutex); std::lock_guard<std::recursive_mutex> guard(mutex);
@@ -1370,7 +1439,9 @@ void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl)
if (subscriber) if (subscriber)
{ {
subscriber->OnNotifyMidiListener(listener.clientHandle, isNote, noteOrControl); subscriber->OnNotifyMidiListener(listener.clientHandle, isNote, noteOrControl);
} else { }
else
{
midiEventListeners.erase(midiEventListeners.begin() + i); midiEventListeners.erase(midiEventListeners.begin() + i);
--i; --i;
} }
@@ -1400,7 +1471,7 @@ void PiPedalModel::CancelListenForMidiEvent(int64_t clientId, int64_t clientHand
std::lock_guard<std::recursive_mutex> guard(mutex); std::lock_guard<std::recursive_mutex> guard(mutex);
for (size_t i = 0; i < midiEventListeners.size(); ++i) 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) if (listener.clientId == clientId && listener.clientHandle == clientHandle)
{ {
midiEventListeners.erase(midiEventListeners.begin() + i); 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); std::lock_guard<std::recursive_mutex> guard(mutex);
for (size_t i = 0; i < atomOutputListeners.size(); ++i) 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) if (listener.clientId == clientId && listener.clientHandle == clientHandle)
{ {
atomOutputListeners.erase(atomOutputListeners.begin() + i); atomOutputListeners.erase(atomOutputListeners.begin() + i);
@@ -1434,24 +1505,24 @@ std::vector<AlsaDeviceInfo> PiPedalModel::GetAlsaDevices()
return this->alsaDevices.GetAlsaDevices(); return this->alsaDevices.GetAlsaDevices();
} }
const std::filesystem::path& PiPedalModel::GetWebRoot() const const std::filesystem::path &PiPedalModel::GetWebRoot() const
{ {
return webRoot; 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(); 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); std::lock_guard<std::recursive_mutex> guard(mutex);
storage.SetFavorites(favorites); storage.SetFavorites(favorites);
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us) // 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()); t.reserve(this->subscribers.size());
for (size_t i = 0; i < subscribers.size(); ++i) for (size_t i = 0; i < subscribers.size(); ++i)
+2
View File
@@ -142,6 +142,8 @@ private:
void UpdateRealtimeMonitorPortSubscriptions(); void UpdateRealtimeMonitorPortSubscriptions();
void OnVuUpdate(const std::vector<VuUpdate>& updates); void OnVuUpdate(const std::vector<VuUpdate>& updates);
void RestartAudio();
std::vector<RealtimeParameterRequest*> outstandingParameterRequests; std::vector<RealtimeParameterRequest*> outstandingParameterRequests;
IPiPedalModelSubscriber*GetNotificationSubscriber(int64_t clientId); IPiPedalModelSubscriber*GetNotificationSubscriber(int64_t clientId);
+12 -12
View File
@@ -836,10 +836,10 @@ public:
std::string governor; std::string governor;
pReader->read(&governor); pReader->read(&governor);
std::string fromAddress = this->getFromAddress(); std::string fromAddress = this->getFromAddress();
if (!IsOnLocalSubnet(fromAddress)) // if (!IsOnLocalSubnet(fromAddress))
{ // {
throw PiPedalException("Permission denied. Not on local subnet."); // throw PiPedalException("Permission denied. Not on local subnet.");
} // }
this->model.SetGovernorSettings(governor); this->model.SetGovernorSettings(governor);
this->Reply(replyTo, "setGovernorSettings"); this->Reply(replyTo, "setGovernorSettings");
} }
@@ -852,10 +852,10 @@ public:
throw PiPedalException("Can't change server settings when running interactively."); throw PiPedalException("Can't change server settings when running interactively.");
} }
std::string fromAddress = this->getFromAddress(); std::string fromAddress = this->getFromAddress();
if (!IsOnLocalSubnet(fromAddress)) // if (!IsOnLocalSubnet(fromAddress))
{ // {
throw PiPedalException("Permission denied. Not on local subnet."); // throw PiPedalException("Permission denied. Not on local subnet.");
} // }
this->model.SetWifiConfigSettings(wifiConfigSettings); this->model.SetWifiConfigSettings(wifiConfigSettings);
this->Reply(replyTo, "setWifiConfigSettings"); this->Reply(replyTo, "setWifiConfigSettings");
@@ -873,10 +873,10 @@ public:
throw PiPedalException("Can't change server settings when running interactively."); throw PiPedalException("Can't change server settings when running interactively.");
} }
std::string fromAddress = this->getFromAddress(); std::string fromAddress = this->getFromAddress();
if (!IsOnLocalSubnet(fromAddress)) // if (!IsOnLocalSubnet(fromAddress))
{ // {
throw PiPedalException("Permission denied. Not on local subnet."); // throw PiPedalException("Permission denied. Not on local subnet.");
} // }
this->model.SetWifiDirectConfigSettings(wifiDirectConfigSettings); this->model.SetWifiDirectConfigSettings(wifiDirectConfigSettings);
this->Reply(replyTo, "setWifiDirectConfigSettings"); this->Reply(replyTo, "setWifiDirectConfigSettings");
@@ -22,7 +22,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
#include "DeviceIdFile.hpp" #include "ServiceConfiguration.hpp"
#include <fstream> #include <fstream>
#include <stdexcept> #include <stdexcept>
#include <grp.h> #include <grp.h>
@@ -33,44 +33,45 @@
using namespace pipedal; using namespace pipedal;
using namespace std; 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_; struct group *group_;
group_ = getgrnam("pipedal_d"); group_ = getgrnam("pipedal_d");
if (group_ == nullptr) if (group_ == nullptr)
{ {
throw logic_error("Group not found: pipedal_d"); throw logic_error("Group not found: pipedal_d");
} }
std::ignore = chown(path.c_str(), -1, group_->gr_gid); std::ignore = chown(DEVICEID_FILE_NAME,-1,group_->gr_gid);
std::ignore = chmod(path.c_str(), S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); std::ignore = chmod(DEVICEID_FILE_NAME, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
f << uuid << endl;
f << deviceName << endl;
} }
} }
@@ -25,10 +25,17 @@
#pragma once #pragma once
#include <string> #include <string>
#include "ConfigSerializer.h"
namespace pipedal { namespace pipedal {
class DeviceIdFile { using namespace config_serializer;
class ServiceConfiguration: protected ConfigSerializable<ServiceConfiguration> {
public: public:
using base = ConfigSerializable<ServiceConfiguration>;
ServiceConfiguration();
static const char DEVICEID_FILE_NAME[]; static const char DEVICEID_FILE_NAME[];
void Load(); void Load();
@@ -36,6 +43,7 @@ namespace pipedal {
std::string uuid; 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 "ss.hpp"
#include "P2pConfigFiles.hpp" #include "P2pConfigFiles.hpp"
#include "DeviceIdFile.hpp" #include "ServiceConfiguration.hpp"
#include "SetWifiConfig.hpp" #include "SetWifiConfig.hpp"
#include "PiPedalException.hpp" #include "PiPedalException.hpp"
#include "SystemConfigFile.hpp" #include "SystemConfigFile.hpp"
@@ -410,6 +410,16 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
{ {
cout << "Disabling P2P" << endl; cout << "Disabling P2P" << endl;
UninstallP2p(); 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 else
{ {
@@ -417,7 +427,7 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
settings.Save(); settings.Save();
// ******************* device_uuid // ******************* device_uuid
DeviceIdFile deviceIdFile; ServiceConfiguration deviceIdFile;
deviceIdFile.Load(); deviceIdFile.Load();
deviceIdFile.deviceName = settings.hotspotName_; deviceIdFile.deviceName = settings.hotspotName_;
deviceIdFile.Save(); deviceIdFile.Save();
@@ -502,12 +512,9 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
sysExec(SYSTEMCTL_BIN " daemon-reload"); sysExec(SYSTEMCTL_BIN " daemon-reload");
sysExec(SYSTEMCTL_BIN " stop pipedal_p2pd"); 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 " restart dhcpcd");
sysExec(SYSTEMCTL_BIN " unmask wpa_supplicant");
sysExec(SYSTEMCTL_BIN " start 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 " start dnsmasq");
sysExec(SYSTEMCTL_BIN " enable dnsmasq"); sysExec(SYSTEMCTL_BIN " enable dnsmasq");
sysExec(SYSTEMCTL_BIN " start pipedal_p2pd"); sysExec(SYSTEMCTL_BIN " start pipedal_p2pd");
+2 -2
View File
@@ -25,7 +25,7 @@
#include <stdexcept> #include <stdexcept>
#include <random> #include <random>
#include "ConfigUtil.hpp" #include "ConfigUtil.hpp"
#include "DeviceIdFile.hpp" #include "ServiceConfiguration.hpp"
#include "WriteTemplateFile.hpp" #include "WriteTemplateFile.hpp"
#include "ss.hpp" #include "ss.hpp"
@@ -169,7 +169,7 @@ void WifiDirectConfigSettings::Load()
this->channel_ = "6"; this->channel_ = "6";
} }
try { try {
DeviceIdFile deviceIdFile; ServiceConfiguration deviceIdFile;
deviceIdFile.Load(); deviceIdFile.Load();
if (deviceIdFile.deviceName == "") 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 "BeastServer.hpp"
#include <iostream> #include <iostream>
#include "Lv2Log.hpp" #include "Lv2Log.hpp"
#include "DeviceIdFile.hpp" #include "ServiceConfiguration.hpp"
#include "AvahiService.hpp" #include "AvahiService.hpp"
#include "PiPedalSocket.hpp" #include "PiPedalSocket.hpp"
@@ -428,7 +428,7 @@ public:
#define LINK_LOCAL_WEB_SOCKET 1 #define LINK_LOCAL_WEB_SOCKET 1
#if LINK_LOCAL_WEB_SOCKET #if LINK_LOCAL_WEB_SOCKET
std::string webSocketAddress = GetLinkLocalAddress(fromAddress); std::string webSocketAddress = GetLinkLocalAddress(fromAddress);
Lv2Log::info(SS("Web Socket Adddress: " << webSocketAddress << ":" << portNumber)); Lv2Log::info(SS("Web Socket Address: " << webSocketAddress << ":" << portNumber));
#else #else
std::string webSocketAddress = "*"; std::string webSocketAddress = "*";
#endif #endif
@@ -1,14 +1,19 @@
[Unit] [Unit]
Description=PiPedal P2P Session Manager Description=PiPedal P2P Session Manager
After=network.target After=network.target
After=nss-lookup.target
After=wpa_supplicant.service After=wpa_supplicant.service
After=dhcpcd.service After=dhcpcd.service
StartLimitIntervalSec=300
StartLimitBurst=8
[Service] [Service]
ExecStart=${COMMAND} --systemd --log-level debug ExecStart=${COMMAND} --systemd --log-level debug
Type=notify Type=notify
WorkingDirectory=/var/pipedal WorkingDirectory=/var/pipedal
TimeoutStopSec=5 TimeoutStopSec=5
Restart=on-failure
RestartSec=10s
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
+1
View File
@@ -7,6 +7,7 @@ After=local-fs.target
[Service] [Service]
Restart=on-failure
Type=notify Type=notify
LimitMEMLOCK=infinity LimitMEMLOCK=infinity
LimitRTPRIO=95 LimitRTPRIO=95