refactor service.conf
This commit is contained in:
@@ -42,7 +42,7 @@
|
||||
|
||||
<http://two-play.com/plugins/toob-cab-sim>
|
||||
a lv2:Plugin ,
|
||||
lv2:AmplifierPlugin ;
|
||||
lv2:SimulatorPlugin ;
|
||||
doap:name "TooB Cab Simulator" ,
|
||||
"TooB Cab Simulator"@en-gb-gb
|
||||
;
|
||||
|
||||
@@ -52,7 +52,7 @@ inputStage:filterGroup
|
||||
|
||||
<http://two-play.com/plugins/toob-input_stage>
|
||||
a lv2:Plugin ,
|
||||
lv2:MixerPlugin ;
|
||||
lv2:SimulatorPlugin ;
|
||||
doap:name "TooB Input Stage" ,
|
||||
"TooB Input Stage"@en-gb-gb
|
||||
;
|
||||
@@ -118,7 +118,8 @@ use the following plugins in the following order.
|
||||
] , [
|
||||
rdfs:label "-INF" ;
|
||||
rdf:value -60.0
|
||||
]
|
||||
];
|
||||
rdfs:comment "Input level trim"
|
||||
], [
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
@@ -154,7 +155,7 @@ use the following plugins in the following order.
|
||||
rdfs:label "OFF" ;
|
||||
rdf:value 0.0
|
||||
];
|
||||
rdfs:comment "Boost high frequencies." ;
|
||||
rdfs:comment "High frequency boost. Zero to disable." ;
|
||||
|
||||
], [
|
||||
a lv2:InputPort ,
|
||||
@@ -173,7 +174,7 @@ use the following plugins in the following order.
|
||||
rdfs:label "OFF" ;
|
||||
rdf:value 0.0
|
||||
];
|
||||
rdfs:comment "Bright frequency." ;
|
||||
rdfs:comment "Bright cutoff frequency." ;
|
||||
|
||||
], [
|
||||
a lv2:InputPort ,
|
||||
|
||||
@@ -60,7 +60,7 @@ pstage:stage3
|
||||
|
||||
<http://two-play.com/plugins/toob-power-stage-2>
|
||||
a lv2:Plugin ,
|
||||
lv2:AmplifierPlugin ;
|
||||
lv2:SimulatorPlugin ;
|
||||
|
||||
doap:name "TooB Power Stage";
|
||||
|
||||
@@ -79,8 +79,8 @@ pstage:stage3
|
||||
|
||||
rdfs:comment """
|
||||
The TooB Power Stage provides flexible and nuanced emulations of guitar amp power stages.
|
||||
The goal of TooB Power Stage is not to inflexibly emulate existing tube power stages, but instead,
|
||||
to be better.
|
||||
The goal of TooB Power Stage is emulate the best features of real tube amps without slavishly
|
||||
reproducing the undesirable features as well.
|
||||
|
||||
TooB Power Stage provides 3 consecutive gain stages, each of which can be overdriven to varying
|
||||
degrees. Power transformer sag is emulated using the SAG and SAGD controls, which emulate the
|
||||
@@ -105,19 +105,22 @@ General experience seems to suggest that gradually increasing the gain in each s
|
||||
the signal harder without introducing undesirable effects in the output. However, there definitely interesting
|
||||
and subtle differences in tone that can be acheived by not adhering to that rule of thumb.
|
||||
|
||||
The SAG and SAGD controls are important controls. You need to undertand how they work. SAG controls how much
|
||||
The SAG and SAGD controls the emulation of "forgiveness", and "compression" that are provided in a real tube
|
||||
amp by sagging of power supply voltage. SAG controls how much
|
||||
the overall volume of the output decreases as the transform sags. It provides the subtle "compression" that
|
||||
tube amps provide, which is quite different from the compression provided by compressors. SAGD controls
|
||||
the amount of additional distortion that occurs as the gain stages receive reduced voltage. It provides
|
||||
the "forgiveness" of a good tube amp. Specifically, because SAGD operates in a feedback loop that runs through
|
||||
the "forgiveness" of a good tube amp. Because SAGD operates in a feedback loop that runs through
|
||||
all three gain stages. Increasing the distortion in the gain stages increases the power use of the gain stages;
|
||||
increasing the power use of the gain stages increases the effect of SAG and SAGD which has an opposing effect
|
||||
on power use. The net result is that using SAG and SAGD causes signals to break up more gradually
|
||||
on power use. The net result is that SAGD causes signals to break up more gradually
|
||||
(hence the perceived "forgiveness"). The effects are non-linear, so not easy to describe completely. You
|
||||
are strongly urged to experiment with SAG and SAGD settings in order to get best tone results.
|
||||
are strongly urged to experiment with SAG and SAGD settings in order to get best tone results. Use very
|
||||
moderate settings to emulate tube amp output; higher settings of SAG and SAGD will cause audible compression
|
||||
and pumping of the outputs which may or may not be desireable.
|
||||
|
||||
The TooB Power Stage design is based on an experimental design, that unexpectedly produced extraordinarily
|
||||
good amp tones. To be perfectly honest, we have not completely explored the sonic possibilities of the
|
||||
good amp tones. We have not completely explored the sonic possibilities of the
|
||||
TooB Power Stage. What's known so far: it produces exceptionally pretty clean tones; it can be driven really
|
||||
hard, and seems to preserve an unusual amount of detail in the signal even when being driven hard; and it
|
||||
can definitely produce signals that have many of the most prized features of tube amplifiers: warmth, detail,
|
||||
@@ -235,10 +238,11 @@ use the following plugins in the following order.
|
||||
|
||||
lv2:index 5 ;
|
||||
lv2:symbol "bias1" ;
|
||||
lv2:name "Bias1";
|
||||
lv2:name "Bias";
|
||||
lv2:default 0.0 ;
|
||||
lv2:minimum -2.0 ;
|
||||
lv2:maximum 2.0;
|
||||
rdfs:comment "Waveshape bias (stage 1)"
|
||||
],
|
||||
|
||||
|
||||
@@ -345,10 +349,12 @@ use the following plugins in the following order.
|
||||
|
||||
lv2:index 11 ;
|
||||
lv2:symbol "bias2" ;
|
||||
lv2:name "Bias2";
|
||||
lv2:name "Bias";
|
||||
lv2:default 0.0 ;
|
||||
lv2:minimum -2.0 ;
|
||||
lv2:maximum 2.0;
|
||||
rdfs:comment "Waveshape bias (stage 2)"
|
||||
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
@@ -466,10 +472,12 @@ use the following plugins in the following order.
|
||||
|
||||
lv2:index 18 ;
|
||||
lv2:symbol "bias3" ;
|
||||
lv2:name "Bias3";
|
||||
lv2:name "Bias";
|
||||
lv2:default 0.0 ;
|
||||
lv2:minimum -2.0 ;
|
||||
lv2:maximum 2.0;
|
||||
rdfs:comment "Waveshape bias (stage 3)"
|
||||
|
||||
],
|
||||
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
<http://two-play.com/plugins/toob-tone-stack>
|
||||
a lv2:Plugin ,
|
||||
lv2:AmplifierPlugin ;
|
||||
lv2:SimulatorPlugin ;
|
||||
doap:name "TooB Tone Stack" ,
|
||||
"TooB Tone Stack"@en-gb-gb
|
||||
;
|
||||
|
||||
Binary file not shown.
@@ -41,7 +41,7 @@
|
||||
lv2:minorVersion 0 ;
|
||||
lv2:microVersion 13 ;
|
||||
rdfs:comment """
|
||||
Digital delay.
|
||||
A straighforward no-frills digital delay.
|
||||
|
||||
""" ;
|
||||
|
||||
@@ -62,6 +62,7 @@ Digital delay.
|
||||
lv2:maximum 1200;
|
||||
units:unit units:ms;
|
||||
lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic>;
|
||||
rdfs:comment "Delay in ms";
|
||||
|
||||
],
|
||||
[
|
||||
@@ -76,6 +77,8 @@ Digital delay.
|
||||
lv2:minimum 0.0 ;
|
||||
lv2:maximum 100.0 ;
|
||||
units:unit units:pc;
|
||||
rdfs:comment "Attenuation of the first delay";
|
||||
|
||||
], [
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
@@ -89,6 +92,7 @@ Digital delay.
|
||||
lv2:minimum 0.0 ;
|
||||
lv2:maximum 100.0 ;
|
||||
units:unit units:pc;
|
||||
rdfs:comment "Attenuation of subequent delays";
|
||||
],
|
||||
[
|
||||
a lv2:AudioPort ,
|
||||
|
||||
@@ -40,7 +40,7 @@ toobml:filterGroup
|
||||
|
||||
<http://two-play.com/plugins/toob-ml>
|
||||
a lv2:Plugin ,
|
||||
lv2:AmplifierPlugin ;
|
||||
lv2:SimulatorPlugin ;
|
||||
doap:name "TooB ML Amplifier" ,
|
||||
"TooB ML Amplifier"@en-gb-gb
|
||||
;
|
||||
@@ -84,7 +84,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
|
||||
lv2:minimum -30.0 ;
|
||||
lv2:maximum 30.0 ;
|
||||
units:unit units:db ;
|
||||
rdf:comment "Input volume trim (pre-model)";
|
||||
rdfs:comment "Input volume trim (pre-model)";
|
||||
],
|
||||
[
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
@@ -228,6 +228,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
|
||||
rdfs:label "XoticCompSP";
|
||||
rdf:value 32.0
|
||||
];
|
||||
rdfs:comment "Select neural network model."
|
||||
] ,
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
@@ -239,7 +240,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
|
||||
lv2:default 3.0 ;
|
||||
lv2:minimum 0.0 ;
|
||||
lv2:maximum 10.0 ;
|
||||
rdf:comment "Model gain (used by some but not all models)"
|
||||
rdfs:comment "Model gain parameter (used by some but not all models)"
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
@@ -252,7 +253,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
|
||||
lv2:minimum -30.0 ;
|
||||
lv2:maximum 30.0 ;
|
||||
units:unit units:db ;
|
||||
rdf:comment "Output volume trim (pot-model)";
|
||||
rdfs:comment "Output volume trim (pre-model)";
|
||||
],
|
||||
|
||||
[
|
||||
@@ -269,7 +270,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
|
||||
lv2:minimum 0.0 ;
|
||||
lv2:maximum 1.0 ;
|
||||
|
||||
rdf:comment "Bass eq (pre-model)";
|
||||
rdfs:comment "Bass eq (pre-model)";
|
||||
], [
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
@@ -285,9 +286,11 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
|
||||
lv2:maximum 1.0 ;
|
||||
|
||||
|
||||
rdf:comment "Mid eq (pre-model)";
|
||||
rdfs:comment "Mid eq (pre-model)";
|
||||
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
@@ -303,7 +306,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
|
||||
lv2:maximum 1.0 ;
|
||||
|
||||
|
||||
rdf:comment "Treble eq (pre-model)";
|
||||
rdfs:comment "Treble eq (pre-model)";
|
||||
],
|
||||
[
|
||||
a lv2:OutputPort ,
|
||||
@@ -320,16 +323,42 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
|
||||
|
||||
],
|
||||
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 8 ;
|
||||
lv2:symbol "sag" ;
|
||||
lv2:name "Sag Vol";
|
||||
lv2:default 0.0 ;
|
||||
lv2:minimum 0.0 ;
|
||||
lv2:maximum 1.0;
|
||||
rdfs:comment "Transformer sag (volume)." ;
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 9 ;
|
||||
lv2:symbol "sagd" ;
|
||||
lv2:name "Sag Dist";
|
||||
lv2:default 0.0 ;
|
||||
lv2:minimum 0.0 ;
|
||||
lv2:maximum 1.0;
|
||||
rdfs:comment "Transformer sag (distortion)." ;
|
||||
],
|
||||
|
||||
|
||||
[
|
||||
a lv2:AudioPort ,
|
||||
lv2:InputPort ;
|
||||
lv2:index 8 ;
|
||||
lv2:index 10 ;
|
||||
lv2:symbol "in" ;
|
||||
lv2:name "In"
|
||||
], [
|
||||
a lv2:AudioPort ,
|
||||
lv2:OutputPort ;
|
||||
lv2:index 9 ;
|
||||
lv2:index 11 ;
|
||||
lv2:symbol "out" ;
|
||||
lv2:name "Out"
|
||||
]
|
||||
@@ -340,7 +369,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
|
||||
atom:bufferType atom:Sequence ;
|
||||
atom:supports patch:Message;
|
||||
lv2:designation lv2:control ;
|
||||
lv2:index 10 ;
|
||||
lv2:index 12 ;
|
||||
lv2:symbol "control" ;
|
||||
lv2:name "Control" ;
|
||||
rdfs:comment "Plugin to GUI communication" ;
|
||||
@@ -351,11 +380,35 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co
|
||||
atom:bufferType atom:Sequence ;
|
||||
atom:supports patch:Message;
|
||||
lv2:designation lv2:control ;
|
||||
lv2:index 11 ;
|
||||
lv2:index 13 ;
|
||||
lv2:symbol "notify" ;
|
||||
lv2:name "Notify" ;
|
||||
rdfs:comment "Plugin to GUI communication" ;
|
||||
],
|
||||
[
|
||||
a atom:AtomPort ,
|
||||
lv2:OutputPort ;
|
||||
atom:bufferType atom:Sequence ;
|
||||
atom:supports patch:Message;
|
||||
lv2:designation lv2:control ;
|
||||
lv2:index 13 ;
|
||||
lv2:symbol "notify" ;
|
||||
lv2:name "Notify" ;
|
||||
rdfs:comment "Plugin to GUI communication" ;
|
||||
],
|
||||
[
|
||||
a lv2:InputPort ,
|
||||
lv2:ControlPort ;
|
||||
|
||||
lv2:index 14 ;
|
||||
lv2:symbol "sagf" ;
|
||||
lv2:name "SagF";
|
||||
lv2:default 13.0 ;
|
||||
lv2:minimum 5.0 ;
|
||||
lv2:maximum 25.0;
|
||||
rdfs:comment "Sag filter Fc." ;
|
||||
]
|
||||
|
||||
.
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"socket_server_port": 8080,
|
||||
"socket_server_port": 80,
|
||||
"socket_server_address": "*",
|
||||
"debug": true,
|
||||
"max_upload_size": 1048576,
|
||||
"fakeAndroid": true,
|
||||
"fakeAndroid": false,
|
||||
"ui_plugins": []
|
||||
}
|
||||
@@ -44,6 +44,7 @@ export class Port implements Deserializable<Port> {
|
||||
this.supports_midi = input.supports_midi;
|
||||
this.supports_time_position = input.supports_time_position;
|
||||
this.port_group = input.port_group;
|
||||
this.comment = input.comment;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -73,6 +74,7 @@ export class Port implements Deserializable<Port> {
|
||||
supports_midi: boolean = false;
|
||||
supports_time_position: boolean = false;
|
||||
port_group: string = "";
|
||||
comment: string = "";
|
||||
}
|
||||
|
||||
export class PortGroup {
|
||||
@@ -221,8 +223,11 @@ export class UiControl implements Deserializable<UiControl> {
|
||||
this.scale_points = ScalePoint.deserialize_array(input.scale_points);
|
||||
this.port_group = input.port_group;
|
||||
this.units = input.units as Units;
|
||||
this.comment = input.comment;
|
||||
|
||||
this.controlType = ControlType.Dial;
|
||||
|
||||
|
||||
if (this.enumeration_property && this.scale_points.length === 2)
|
||||
{
|
||||
this.controlType = ControlType.Toggle;
|
||||
@@ -239,7 +244,6 @@ export class UiControl implements Deserializable<UiControl> {
|
||||
{
|
||||
this.controlType = ControlType.Select;
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
@@ -271,6 +275,7 @@ export class UiControl implements Deserializable<UiControl> {
|
||||
scale_points: ScalePoint[] = [];
|
||||
port_group: string = "";
|
||||
units: Units = Units.none;
|
||||
comment: string = "";
|
||||
|
||||
// Return the value of the closest scale_point.
|
||||
clampSelectValue(value: number): number{
|
||||
|
||||
@@ -503,7 +503,6 @@ export const MainPage =
|
||||
GetControlView(pedalBoardItem)
|
||||
)
|
||||
}
|
||||
|
||||
</div>
|
||||
<MidiBindingsDialog open={this.state.showMidiBindingsDialog}
|
||||
onClose={()=> this.setState({showMidiBindingsDialog: false} ) }
|
||||
|
||||
@@ -2020,14 +2020,18 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
let oldSettings = this.wifiDirectConfigSettings.get();
|
||||
wifiDirectConfigSettings = wifiDirectConfigSettings.clone();
|
||||
|
||||
if ((!oldSettings.enable) && (!wifiDirectConfigSettings.enable)) {
|
||||
if ((!oldSettings.enable) && (!wifiDirectConfigSettings.enable)
|
||||
&& (oldSettings.hotspotName == wifiDirectConfigSettings.hotspotName)
|
||||
) {
|
||||
// no effective change.
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
if (!wifiDirectConfigSettings.enable) {
|
||||
let t = wifiDirectConfigSettings.hotspotName; // hotspot name can be changed when disabled because it's also the mDNS service name.
|
||||
wifiDirectConfigSettings = oldSettings.clone();
|
||||
wifiDirectConfigSettings.enable = false;
|
||||
wifiDirectConfigSettings.hotspotName = t;
|
||||
} else {
|
||||
if (wifiDirectConfigSettings.countryCode === oldSettings.countryCode
|
||||
&& wifiDirectConfigSettings.channel === oldSettings.channel
|
||||
|
||||
@@ -455,7 +455,12 @@ const PluginControlView =
|
||||
{
|
||||
nodes
|
||||
}
|
||||
<div style={{ flex: "0 0 40px", width: 40, height: 1 }} />
|
||||
<div style={{ flex: "0 0 40px", width: 40, height: 40 }} />
|
||||
{
|
||||
(!this.state.landscapeGrid) && (
|
||||
<div style={{ flex: "0 1 100%", width: "0px", height: 40 }} />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<FullScreenIME uiControl={this.state.imeUiControl} value={this.state.imeValue}
|
||||
|
||||
|
||||
@@ -134,22 +134,63 @@ function makeParagraphs(description: string) {
|
||||
);
|
||||
}
|
||||
function makeControls(controls: UiControl[]) {
|
||||
return (
|
||||
<Grid container direction="row" justifyContent="flex-start" alignItems="flex-start" spacing={1} style={{ paddingLeft: "24px" }}>
|
||||
{
|
||||
controls.map((control) => (
|
||||
<Grid xs={6} sm={4} key={control.symbol} >
|
||||
<Typography variant="body2">
|
||||
let hasComments = false;
|
||||
|
||||
for (let i = 0; i < controls.length; ++i) {
|
||||
if (controls[i].comment !== "") {
|
||||
hasComments = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasComments) {
|
||||
let trs: React.ReactElement[] = [];
|
||||
for (let i = 0; i < controls.length; ++i) {
|
||||
let control = controls[i];
|
||||
|
||||
trs.push((
|
||||
<tr>
|
||||
<td style={{ verticalAlign: "top" }}>
|
||||
<Typography variant="body2" style={{ whiteSpace: "nowrap" }}>
|
||||
{control.name}
|
||||
</Typography>
|
||||
</Grid>
|
||||
))
|
||||
}
|
||||
</Grid>
|
||||
);
|
||||
</td>
|
||||
<td style={{ paddingLeft: "16px", verticalAlign: "top" }}>
|
||||
<Typography variant="body2">
|
||||
{control.comment}
|
||||
</Typography>
|
||||
</td>
|
||||
</tr >
|
||||
));
|
||||
|
||||
}
|
||||
return (
|
||||
<table style={{ paddingLeft: "24px", verticalAlign: "top" }}>
|
||||
{
|
||||
trs
|
||||
}
|
||||
</table>
|
||||
);
|
||||
|
||||
} else {
|
||||
return (
|
||||
<Grid container direction="row" justifyContent="flex-start" alignItems="flex-start" spacing={1} style={{ paddingLeft: "24px" }}>
|
||||
{
|
||||
controls.map((control) => (
|
||||
<Grid xs={6} sm={4} key={control.symbol + "x"} >
|
||||
<Typography variant="body2">
|
||||
{control.name}
|
||||
</Typography>
|
||||
</Grid>
|
||||
))
|
||||
}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => {
|
||||
|
||||
let model = PiPedalModelFactory.getInstance();
|
||||
@@ -205,19 +246,25 @@ const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => {
|
||||
</div>
|
||||
</MuiDialogTitle>
|
||||
<PluginInfoDialogContent dividers style={{ width: "100%", maxHeight: "80%", overflowX: "hidden" }}>
|
||||
<Typography gutterBottom>
|
||||
Author:
|
||||
{(plugin.author_homepage !== "")
|
||||
? <a href={plugin.author_homepage} target="_blank" rel="noreferrer">{plugin.author_name}</a>
|
||||
: (
|
||||
plugin.author_name
|
||||
)
|
||||
}
|
||||
</Typography>
|
||||
<Typography gutterBottom>
|
||||
{ioDescription(plugin)}
|
||||
</Typography>
|
||||
<div style={{ width: "100%",display: "flex", flexFlow: "row", justifyItems: "stretch", flexWrap: "nowrap" }} >
|
||||
<div style={{ flex: "1 1 100%" }}>
|
||||
<Typography gutterBottom >
|
||||
Author:
|
||||
{(plugin.author_homepage !== "")
|
||||
? <a href={plugin.author_homepage} target="_blank" rel="noreferrer">{plugin.author_name}</a>
|
||||
: (
|
||||
plugin.author_name
|
||||
)
|
||||
}
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<Typography gutterBottom >
|
||||
{ioDescription(plugin)}
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<Typography variant="body1" gutterBottom style={{ paddingTop: "1em" }}>
|
||||
Controls:
|
||||
</Typography>
|
||||
|
||||
@@ -38,7 +38,14 @@ export interface ControlEntry {
|
||||
};
|
||||
|
||||
const Utility = class {
|
||||
static isLandscape(): boolean {
|
||||
return window.innerWidth > window.innerHeight;
|
||||
}
|
||||
static isTouchDevice(): boolean {
|
||||
if (!this.isLandscape()) return false;
|
||||
|
||||
if (window.innerHeight > 500) return false;
|
||||
|
||||
return (('ontouchstart' in window) &&
|
||||
((navigator.maxTouchPoints??0) > 0) );
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
|
||||
autoComplete="off"
|
||||
id="name"
|
||||
spellCheck="false"
|
||||
label="SSID"
|
||||
label="Service Name"
|
||||
type="text"
|
||||
size={this.state.controlSize}
|
||||
fullWidth
|
||||
@@ -348,11 +348,11 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
|
||||
value={this.state.name}
|
||||
onChange={(e) => this.setState({ name: e.target.value, nameError: false, nameErrorMessage: NBSP })}
|
||||
inputRef={this.refName}
|
||||
disabled={!this.state.enabled}
|
||||
variant="filled"
|
||||
/>
|
||||
|
||||
<TextField
|
||||
required
|
||||
inputRef={this.refPassword}
|
||||
sx={{ maxWidth: this.state.controlWidth }}
|
||||
|
||||
@@ -377,7 +377,7 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ marginTop: 0, display: 'flex', alignItems: 'stretch', gap: 16 }}>
|
||||
<TextField select label="Country" size={this.state.controlSize} value={this.state.countryCode} variant="filled"
|
||||
<TextField required select label="Country" size={this.state.controlSize} value={this.state.countryCode} variant="filled"
|
||||
sx={{ width: this.state.controlWidth }}
|
||||
disabled={!this.state.enabled}
|
||||
onChange={(event) => { this.handleCountryChanged(event); }}
|
||||
@@ -413,17 +413,18 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
|
||||
)}
|
||||
{(!this.state.landscapeLayout) && (
|
||||
<DialogContent sx={{minHeight: 96}} >
|
||||
<FormControlLabel
|
||||
control={(
|
||||
<Switch
|
||||
checked={this.state.enabled}
|
||||
onChange={(e: any) => this.handleEnableChanged(e)}
|
||||
color="secondary"
|
||||
/>
|
||||
)}
|
||||
label="Wi-Fi Direct Hotspot"
|
||||
/>
|
||||
<div style={{ marginTop: 16 }}>
|
||||
<div>
|
||||
<FormControlLabel
|
||||
control={(
|
||||
<Switch
|
||||
checked={this.state.enabled}
|
||||
onChange={(e: any) => this.handleEnableChanged(e)}
|
||||
color="secondary"
|
||||
/>
|
||||
)}
|
||||
label="Wi-Fi Direct Hotspot"
|
||||
/>
|
||||
</div><div style={{ marginTop: 16 }}>
|
||||
<TextField
|
||||
required
|
||||
|
||||
@@ -431,7 +432,7 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
|
||||
autoComplete="off"
|
||||
id="name"
|
||||
spellCheck="false"
|
||||
label="SSID"
|
||||
label="Service Name"
|
||||
type="text"
|
||||
size={this.state.controlSize}
|
||||
fullWidth
|
||||
@@ -440,10 +441,10 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
|
||||
value={this.state.name}
|
||||
onChange={(e) => this.setState({ name: e.target.value, nameError: false, nameErrorMessage: NBSP })}
|
||||
inputRef={this.refName}
|
||||
disabled={!this.state.enabled}
|
||||
variant="filled"
|
||||
/>
|
||||
</div><div>
|
||||
</div>
|
||||
<div>
|
||||
<TextField
|
||||
inputRef={this.refPassword}
|
||||
sx={{ maxWidth: this.state.controlWidth }}
|
||||
|
||||
+5
-5
@@ -210,13 +210,13 @@ bool setJackConfiguration(JackServerSettings serverSettings)
|
||||
|
||||
silentSysExec("/usr/bin/systemctl unmask jack");
|
||||
silentSysExec("/usr/bin/systemctl enable jack");
|
||||
#else
|
||||
// otherwise,
|
||||
// the config was written before invoking admin main. but we still need to restart the service.
|
||||
#endif
|
||||
|
||||
std::thread delayedRestartThread(delayedRestartProc);
|
||||
delayedRestartThread.detach();
|
||||
#else
|
||||
// otherwise,
|
||||
// the config was written before invoking admin main and we don't need to to restart the service.
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+30
-7
@@ -268,6 +268,15 @@ namespace pipedal
|
||||
snd_pcm_sw_params_free(playbackSwParams);
|
||||
playbackSwParams = nullptr;
|
||||
}
|
||||
for (auto *midiState: this->midiStates)
|
||||
{
|
||||
if (midiState != nullptr)
|
||||
{
|
||||
midiState->Close();
|
||||
delete midiState;
|
||||
}
|
||||
}
|
||||
midiStates.resize(0);
|
||||
}
|
||||
|
||||
std::string discover_alsa_using_apps()
|
||||
@@ -573,9 +582,14 @@ namespace pipedal
|
||||
this->channelSelection = channelSelection;
|
||||
|
||||
open = true;
|
||||
|
||||
OpenMidi(jackServerSettings, channelSelection);
|
||||
OpenAudio(jackServerSettings, channelSelection);
|
||||
try {
|
||||
OpenMidi(jackServerSettings, channelSelection);
|
||||
OpenAudio(jackServerSettings, channelSelection);
|
||||
} catch (const std::exception&e)
|
||||
{
|
||||
Close();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenAudio(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
|
||||
@@ -1512,7 +1526,7 @@ namespace pipedal
|
||||
try
|
||||
{
|
||||
int err;
|
||||
for (int retry = 0; retry < 5; ++retry)
|
||||
for (int retry = 0; retry < 15; ++retry)
|
||||
{
|
||||
err = snd_pcm_open(&playbackHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
||||
if (err == -EBUSY)
|
||||
@@ -1524,13 +1538,22 @@ namespace pipedal
|
||||
}
|
||||
if (err < 0)
|
||||
{
|
||||
throw PiPedalStateException(SS(alsaDeviceName << " not found. "
|
||||
throw PiPedalStateException(SS(alsaDeviceName << " playback device not found. "
|
||||
<< "(" << snd_strerror(err) << ")"));
|
||||
}
|
||||
|
||||
err = snd_pcm_open(&captureHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
|
||||
for (int retry = 0; retry < 15; ++retry)
|
||||
{
|
||||
err = snd_pcm_open(&captureHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
|
||||
if (err == -EBUSY)
|
||||
{
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (err < 0)
|
||||
throw PiPedalStateException(SS(alsaDeviceName << " not found."));
|
||||
throw PiPedalStateException(SS(alsaDeviceName << " capture device not found."));
|
||||
|
||||
if (snd_pcm_hw_params_malloc(&playbackHwParams) < 0)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,217 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
// avoid breaking changes from 1.64 to.71
|
||||
// Note that raspbian only prvides 1.64; ubuntu studio only provides 1.71
|
||||
#define BOOST_BEAST_ALLOW_DEPRECATED
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/beast/http.hpp>
|
||||
#include <boost/asio/ip/network_v4.hpp>
|
||||
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include "Uri.hpp"
|
||||
#include <string_view>
|
||||
#include <filesystem>
|
||||
|
||||
|
||||
// forward declaration.
|
||||
|
||||
class websocket_session;
|
||||
|
||||
namespace pipedal {
|
||||
using HttpRequest =boost::beast::http::request<boost::beast::http::string_body>;
|
||||
|
||||
|
||||
std::string last_modified(const std::filesystem::path& path);
|
||||
|
||||
class SocketHandler {
|
||||
public:
|
||||
|
||||
class IWriteCallback {
|
||||
public:
|
||||
virtual void close() = 0;
|
||||
|
||||
virtual void writeCallback(const std::string& text) = 0;
|
||||
virtual std::string getFromAddress() const = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
friend class ::websocket_session;
|
||||
|
||||
|
||||
IWriteCallback *writeCallback_;
|
||||
|
||||
void setWriteCallback(IWriteCallback *writeCallback) {
|
||||
writeCallback_ = writeCallback;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void onReceive(const std::string_view&text) = 0;
|
||||
public:
|
||||
std::string getFromAddress() const { return writeCallback_->getFromAddress(); }
|
||||
void receive(const std::string_view&text) {
|
||||
onReceive(text);
|
||||
}
|
||||
void send(const std::string &text) {
|
||||
if (writeCallback_ != nullptr)
|
||||
{
|
||||
writeCallback_->writeCallback(text);
|
||||
}
|
||||
}
|
||||
virtual void Close()
|
||||
{
|
||||
writeCallback_->close();
|
||||
}
|
||||
|
||||
virtual ~SocketHandler() = default;
|
||||
|
||||
virtual void onAttach() { }
|
||||
|
||||
};
|
||||
|
||||
class ISocketFactory {
|
||||
public:
|
||||
virtual bool wants(const uri &request) = 0;
|
||||
virtual std::shared_ptr<SocketHandler> CreateHandler(const uri& request) = 0;
|
||||
};
|
||||
|
||||
class RequestHandler {
|
||||
private:
|
||||
uri target_url_;
|
||||
public:
|
||||
RequestHandler(const char*target_url)
|
||||
: target_url_(target_url)
|
||||
{
|
||||
|
||||
}
|
||||
~RequestHandler() = default;
|
||||
|
||||
virtual bool wantsRedirect(const uri& requestUri)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uri GetRedirect(const uri& requestUri)
|
||||
{
|
||||
return requestUri;
|
||||
}
|
||||
|
||||
virtual bool wants(boost::beast::http::verb verb,const uri &request_uri) const {
|
||||
if (request_uri.segment_count() < target_url_.segment_count())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < target_url_.segment_count(); ++i)
|
||||
{
|
||||
if (request_uri.segment(i) != target_url_.segment(i))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void head_response(
|
||||
const uri&request_uri,
|
||||
const boost::beast::http::request<boost::beast::http::string_body> &req,
|
||||
boost::beast::http::response<boost::beast::http::empty_body> &res,
|
||||
boost::beast::error_code &ec) = 0;
|
||||
|
||||
virtual void get_response(
|
||||
const uri&request_uri,
|
||||
const boost::beast::http::request<boost::beast::http::string_body> &req,
|
||||
boost::beast::http::response<boost::beast::http::string_body> &res,
|
||||
boost::beast::error_code &ec) = 0;
|
||||
|
||||
virtual void post_response(
|
||||
const uri&request_uri,
|
||||
const boost::beast::http::request<boost::beast::http::string_body> &req,
|
||||
boost::beast::http::response<boost::beast::http::string_body> &res,
|
||||
boost::beast::error_code &ec)
|
||||
{
|
||||
get_response(request_uri,req,res,ec);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
class BeastServer {
|
||||
private:
|
||||
boost::asio::ip::address address;
|
||||
unsigned short port;
|
||||
std::string doc_root;
|
||||
int threads;
|
||||
std::mutex log_mutex;
|
||||
std::vector<std::shared_ptr<RequestHandler> > request_handlers;
|
||||
std::vector<std::shared_ptr<ISocketFactory> > socket_factories;
|
||||
std::mutex io_mutex;
|
||||
boost::asio::io_context *pIoContext = nullptr;
|
||||
void*pListener = nullptr;
|
||||
std::thread*pBgThread = nullptr;
|
||||
|
||||
bool logHttpRequests = false;
|
||||
|
||||
bool hasAccessPointGateway = false;
|
||||
boost::asio::ip::network_v4 accessPointGateway;
|
||||
std::string accessPointServerAddress;
|
||||
bool stopping = false;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
BeastServer(
|
||||
const boost::asio::ip::address& address,
|
||||
int port,
|
||||
const char*rootPath,
|
||||
int threads = 10);
|
||||
|
||||
void Run();
|
||||
void RunInBackground();
|
||||
bool Stopping() const { return this->stopping; }
|
||||
void Stop();
|
||||
void Join();
|
||||
|
||||
void SetLogHttpRequests(bool logRequests) { this->logHttpRequests = logRequests; }
|
||||
bool LogHttpRequests() const { return this->logHttpRequests; }
|
||||
boost::asio::io_context& IoContext() {
|
||||
return *pIoContext;
|
||||
}
|
||||
|
||||
void AddRequestHandler(std::shared_ptr<RequestHandler> requestHandler)
|
||||
{
|
||||
request_handlers.push_back(requestHandler);
|
||||
}
|
||||
|
||||
void AddSocketFactory(const std::shared_ptr<ISocketFactory> &socketHandler)
|
||||
{
|
||||
socket_factories.push_back(socketHandler);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<RequestHandler> > &RequestHandlers() { return request_handlers; }
|
||||
|
||||
const std::string&GetDocRoot() { return doc_root; }
|
||||
|
||||
virtual ~BeastServer();
|
||||
|
||||
std::shared_ptr<ISocketFactory> GetSocketFactory(const uri &request_uri);
|
||||
|
||||
void SetAccessPointGateway(const boost::asio::ip::network_v4 &gateway, const std::string&accessPointServerAddress)
|
||||
{
|
||||
this->accessPointGateway = gateway;
|
||||
this->hasAccessPointGateway = true;
|
||||
this->accessPointServerAddress = accessPointServerAddress;
|
||||
}
|
||||
bool HasAccessPointGateway() const { return hasAccessPointGateway; }
|
||||
boost::asio::ip::network_v4 &GetAccessPointGateway() { return this->accessPointGateway; }
|
||||
const std::string & GetAccessPointServerAddress() const { return this->accessPointServerAddress; }
|
||||
|
||||
public:
|
||||
virtual void onError(boost::system::error_code ec, char const* what);
|
||||
virtual void onWarning(boost::system::error_code ec, char const* what);
|
||||
virtual void onInfo(char const* what);
|
||||
|
||||
virtual void onListenerClosed();
|
||||
};
|
||||
|
||||
} // namespace pipedal;
|
||||
+8
-4
@@ -89,9 +89,10 @@ set (PIPEDAL_SOURCES
|
||||
CpuUse.hpp CpuUse.cpp
|
||||
P2pConfigFiles.hpp
|
||||
AvahiService.cpp AvahiService.hpp
|
||||
DeviceIdFile.cpp DeviceIdFile.hpp
|
||||
ServiceConfiguration.cpp ServiceConfiguration.hpp
|
||||
WriteTemplateFile.cpp WriteTemplateFile.hpp
|
||||
|
||||
ConfigUtil.hpp ConfigUtil.cpp
|
||||
ConfigSerializer.h ConfigSerializer.cpp
|
||||
StdErrorCapture.hpp StdErrorCapture.cpp
|
||||
Ipv6Helpers.cpp Ipv6Helpers.hpp
|
||||
PluginPreset.cpp PluginPreset.hpp
|
||||
@@ -236,12 +237,13 @@ add_test(NAME DevTest COMMAND pipedaltest "[Dev]")
|
||||
|
||||
add_executable(pipedalconfig
|
||||
PrettyPrinter.hpp
|
||||
DeviceIdFile.cpp DeviceIdFile.hpp
|
||||
ServiceConfiguration.cpp ServiceConfiguration.hpp
|
||||
json.cpp json.hpp
|
||||
HtmlHelper.cpp HtmlHelper.hpp
|
||||
CommandLineParser.hpp
|
||||
WriteTemplateFile.hpp WriteTemplateFile.cpp
|
||||
PiPedalException.hpp
|
||||
ConfigSerializer.h ConfigSerializer.cpp
|
||||
ConfigMain.cpp
|
||||
WifiConfigSettings.cpp WifiConfigSettings.hpp
|
||||
WifiDirectConfigSettings.cpp WifiDirectConfigSettings.hpp
|
||||
@@ -283,7 +285,9 @@ target_link_libraries(pipedal_latency_test PRIVATE pthread asound
|
||||
|
||||
add_executable(pipedaladmind AdminMain.cpp CommandLineParser.hpp
|
||||
UnixSocket.cpp UnixSocket.hpp
|
||||
DeviceIdFile.cpp DeviceIdFile.hpp
|
||||
ServiceConfiguration.cpp ServiceConfiguration.hpp
|
||||
ConfigSerializer.h ConfigSerializer.cpp
|
||||
autoptr_vector.h
|
||||
|
||||
JackServerSettings.hpp JackServerSettings.cpp
|
||||
json.hpp json.cpp HtmlHelper.hpp HtmlHelper.cpp Lv2Log.hpp Lv2Log.cpp
|
||||
|
||||
+59
-26
@@ -33,7 +33,7 @@
|
||||
#include "JackServerSettings.hpp"
|
||||
#include "P2pConfigFiles.hpp"
|
||||
#include "PrettyPrinter.hpp"
|
||||
#include "DeviceIdFile.hpp"
|
||||
#include "ServiceConfiguration.hpp"
|
||||
#include <uuid/uuid.h>
|
||||
#include <random>
|
||||
#include "AudioConfig.hpp"
|
||||
@@ -366,6 +366,7 @@ void InstallLimits()
|
||||
}
|
||||
|
||||
|
||||
#if JACK_HOST
|
||||
void MaybeStartJackService()
|
||||
{
|
||||
std::filesystem::path drcFile = "/etc/jackdrc";
|
||||
@@ -379,7 +380,9 @@ void MaybeStartJackService()
|
||||
silentSysExec(SYSTEMCTL_BIN " mask jack");
|
||||
}
|
||||
}
|
||||
void InstallJackService()
|
||||
#endif
|
||||
|
||||
void InstallAudioService()
|
||||
{
|
||||
InstallLimits();
|
||||
InstallPamEnv();
|
||||
@@ -435,6 +438,28 @@ int SudoExec(char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsP2pServiceEnabled()
|
||||
{
|
||||
WifiDirectConfigSettings settings;
|
||||
settings.Load();
|
||||
return settings.enable_;
|
||||
}
|
||||
|
||||
static void RestartP2pdService()
|
||||
{
|
||||
if (IsP2pServiceEnabled())
|
||||
{
|
||||
silentSysExec(SYSTEMCTL_BIN " restart " PIPEDAL_P2PD_SERVICE);
|
||||
}
|
||||
}
|
||||
|
||||
static void UninstallP2pdService()
|
||||
{
|
||||
silentSysExec("systemctl stop " PIPEDAL_P2PD_SERVICE);
|
||||
silentSysExec("systemctl disable " PIPEDAL_P2PD_SERVICE);
|
||||
}
|
||||
|
||||
|
||||
void Uninstall()
|
||||
{
|
||||
try
|
||||
@@ -444,9 +469,13 @@ void Uninstall()
|
||||
|
||||
StopService();
|
||||
DisableService();
|
||||
UninstallP2pdService();
|
||||
|
||||
#if UNINSTALL_JACK_SERVICE
|
||||
silentSysExec(SYSTEMCTL_BIN " stop jack");
|
||||
silentSysExec(SYSTEMCTL_BIN " disable jack");
|
||||
OnWifiUninstall();
|
||||
#endif
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
@@ -545,33 +574,33 @@ static std::string RandomChars(int nchars)
|
||||
return s.str();
|
||||
}
|
||||
|
||||
static void PrepareDeviceidFile()
|
||||
static void PrepareServiceConfigurationFile(uint16_t portNumber)
|
||||
{
|
||||
if (!std::filesystem::exists(DeviceIdFile::DEVICEID_FILE_NAME))
|
||||
ServiceConfiguration serviceConfiguration;
|
||||
try {
|
||||
serviceConfiguration.Load();
|
||||
} catch (const std::exception &)
|
||||
{
|
||||
DeviceIdFile deviceIdFile;
|
||||
|
||||
deviceIdFile.deviceName = "PiPedal";
|
||||
deviceIdFile.uuid = MakeUuid();
|
||||
deviceIdFile.Save();
|
||||
} else {
|
||||
DeviceIdFile deviceIdFile;
|
||||
if (deviceIdFile.deviceName == "" || deviceIdFile.uuid == "")
|
||||
{
|
||||
if (deviceIdFile.deviceName == "")
|
||||
{
|
||||
deviceIdFile.deviceName = "PiPedal";
|
||||
}
|
||||
if (deviceIdFile.uuid == "")
|
||||
{
|
||||
deviceIdFile.uuid = MakeUuid();
|
||||
}
|
||||
deviceIdFile.Save();
|
||||
}
|
||||
|
||||
}
|
||||
if (serviceConfiguration.deviceName == "" || serviceConfiguration.uuid == ""
|
||||
|| portNumber != serviceConfiguration.server_port
|
||||
)
|
||||
{
|
||||
if (serviceConfiguration.deviceName == "")
|
||||
{
|
||||
serviceConfiguration.deviceName = "PiPedal";
|
||||
}
|
||||
if (serviceConfiguration.uuid == "")
|
||||
{
|
||||
serviceConfiguration.uuid = MakeUuid();
|
||||
}
|
||||
serviceConfiguration.server_port = portNumber;
|
||||
serviceConfiguration.Save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Install(const std::filesystem::path &programPrefix, const std::string endpointAddress)
|
||||
{
|
||||
if (sysExec(GROUPADD_BIN " -f " AUDIO_SERVICE_GROUP_NAME) != EXIT_SUCCESS)
|
||||
@@ -583,8 +612,7 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
|
||||
throw PiPedalException("Failed to create pipedald service group.");
|
||||
}
|
||||
|
||||
PrepareDeviceidFile();
|
||||
InstallJackService();
|
||||
InstallAudioService();
|
||||
auto endpos = endpointAddress.find_last_of(':');
|
||||
if (endpos == string::npos)
|
||||
{
|
||||
@@ -610,6 +638,9 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
|
||||
s << "Invalid port number: " << strPort;
|
||||
throw PiPedalException(s.str());
|
||||
}
|
||||
PrepareServiceConfigurationFile(port);
|
||||
|
||||
|
||||
|
||||
bool authBindRequired = port < 512;
|
||||
|
||||
@@ -744,6 +775,8 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
|
||||
RestartService(false);
|
||||
EnableService();
|
||||
|
||||
RestartP2pdService();
|
||||
|
||||
cout << "Complete"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "ConfigSerializer.h"
|
||||
|
||||
|
||||
using namespace config_serializer;
|
||||
using namespace config_serializer::detail;
|
||||
using namespace std;
|
||||
|
||||
std::string config_serializer::detail::trim(const std::string &v)
|
||||
{
|
||||
size_t start = v.find_first_not_of(' ');
|
||||
size_t end = v.find_last_not_of(' ');
|
||||
if (start >= end+1)
|
||||
return "";
|
||||
return v.substr(start, end+1 - start);
|
||||
}
|
||||
|
||||
std::vector<std::string> config_serializer::detail::split(const std::string &value, char delimiter)
|
||||
{
|
||||
size_t start = 0;
|
||||
std::vector<std::string> result;
|
||||
while (start < value.length())
|
||||
{
|
||||
size_t pos = value.find_first_of(delimiter, start);
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
result.push_back(value.substr(start));
|
||||
break;
|
||||
}
|
||||
result.push_back(value.substr(start, pos - start));
|
||||
start = pos + 1;
|
||||
if (start == value.length())
|
||||
{
|
||||
// ends with delimieter? Then there's an empty-length value at the end.
|
||||
result.push_back("");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string config_serializer::detail::EncodeString(const std::string &s)
|
||||
{
|
||||
bool requiresEncoding = false;
|
||||
|
||||
for (char c : s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\0':
|
||||
break;
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\\':
|
||||
case ' ':
|
||||
case '\"':
|
||||
requiresEncoding = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!requiresEncoding)
|
||||
return s;
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
ss << '"';
|
||||
for (char c : s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\0':
|
||||
// discard.
|
||||
break;
|
||||
case '\r':
|
||||
ss << "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
ss << "\\t";
|
||||
break;
|
||||
case '\\':
|
||||
ss << "\\\\";
|
||||
break;
|
||||
case '\"':
|
||||
ss << "\\\"";
|
||||
break;
|
||||
default:
|
||||
ss << c;
|
||||
}
|
||||
}
|
||||
ss << '"';
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string config_serializer::detail::DecodeString(const std::string &value)
|
||||
{
|
||||
std::stringstream s;
|
||||
const char *p = value.c_str();
|
||||
char quoteChar;
|
||||
if (*p == '\'' || *p == '\"')
|
||||
{
|
||||
quoteChar = *p;
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
if (*p != quoteChar)
|
||||
return value;
|
||||
if (value.at(value.length()-1) != quoteChar)
|
||||
{
|
||||
throw std::invalid_argument("Invalid quoted string.");
|
||||
}
|
||||
++p;
|
||||
while (*p != 0 && *p != quoteChar)
|
||||
{
|
||||
char c = *p++;
|
||||
if (c == '\\')
|
||||
{
|
||||
c = *p++;
|
||||
switch (c)
|
||||
{
|
||||
case 'r':
|
||||
s << '\r';
|
||||
break;
|
||||
case 't':
|
||||
s << '\t';
|
||||
break;
|
||||
case 'n':
|
||||
s << '\n';
|
||||
break;
|
||||
case '\0':
|
||||
throw std::invalid_argument("Invalid quoted string.");
|
||||
break;
|
||||
default:
|
||||
s << c;
|
||||
}
|
||||
if (c != 0)
|
||||
{
|
||||
s << c;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s << c;
|
||||
}
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include "autoptr_vector.h"
|
||||
#include <unordered_map>
|
||||
#include <fstream>
|
||||
|
||||
namespace config_serializer
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
std::string trim(const std::string &v);
|
||||
|
||||
std::vector<std::string> split(const std::string &v, char seperator);
|
||||
|
||||
/**
|
||||
* @brief Convert string to config file format.
|
||||
*
|
||||
* Adds quotes and escapes, but only if neccessary.
|
||||
*
|
||||
* Source text is assumed to be UTF-8. Escapes for the
|
||||
* following values only: \r \n \t \" \\.
|
||||
*
|
||||
*
|
||||
* @param s
|
||||
* @return std::string Encoded string.
|
||||
*/
|
||||
std::string EncodeString(const std::string &s);
|
||||
|
||||
/**
|
||||
* @brief Config file format to string.
|
||||
*
|
||||
* Decodes quotes and escapes, but only if neccessary.
|
||||
*
|
||||
* Source text is assumed to be UTF-8. Escapes for the
|
||||
* following values only: \r \n \t \" \\.
|
||||
*
|
||||
* @param s
|
||||
* @return std::string
|
||||
*/
|
||||
std::string DecodeString(const std::string &s);
|
||||
|
||||
/**
|
||||
* @brief Convert a string to an integer.
|
||||
*
|
||||
* @tparam T A signed or unsigned integral type.
|
||||
* @param value The string to parse.
|
||||
* @return T The parsed result.
|
||||
* @throws std::invalid_argument if the supplied string is not a valid integer.
|
||||
*/
|
||||
template <class T>
|
||||
T ToInt(const std::string &value)
|
||||
{
|
||||
std::stringstream s(value);
|
||||
T result;
|
||||
s >> result;
|
||||
if (s.fail())
|
||||
{
|
||||
throw std::invalid_argument("Invalid integer value.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using namespace detail;
|
||||
|
||||
template <class OBJ>
|
||||
class ConfigSerializerBase
|
||||
{
|
||||
protected:
|
||||
ConfigSerializerBase(const std::string &name, const std::string &comment)
|
||||
: name(name),
|
||||
comment(comment)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
using Obj = OBJ;
|
||||
virtual ~ConfigSerializerBase() {}
|
||||
|
||||
const std::string &GetName() const { return name; }
|
||||
const std::string &GetComment() const { return comment; }
|
||||
virtual void SetValue(Obj &configurable, const std::string &value) const = 0;
|
||||
virtual const std::string GetValue(Obj &configurable) const = 0;
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
std::string comment;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class ConfigConverter
|
||||
{
|
||||
public:
|
||||
static T FromString(const std::string &value)
|
||||
{
|
||||
T result;
|
||||
std::stringstream s(value);
|
||||
s >> result;
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string ToString(const T &value)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << value;
|
||||
return s.str();
|
||||
}
|
||||
};
|
||||
|
||||
// string specializations.
|
||||
template <>
|
||||
inline std::string ConfigConverter<std::string>::FromString(const std::string &value)
|
||||
{
|
||||
return DecodeString(value);
|
||||
}
|
||||
template <>
|
||||
inline std::string ConfigConverter<std::string>::ToString(const std::string &config)
|
||||
{
|
||||
return EncodeString(config);
|
||||
}
|
||||
// bool specializations.
|
||||
template <>
|
||||
inline bool ConfigConverter<bool>::FromString(const std::string &value)
|
||||
{
|
||||
bool result = false;
|
||||
if (value == "")
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
else if (value == "true")
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
else if (value == "false")
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
int t = ToInt<int>(value);
|
||||
result = (t != 0);
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
throw std::invalid_argument("Expecting true or false.");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
template <>
|
||||
inline std::string ConfigConverter<bool>::ToString(const bool &value)
|
||||
{
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
template <class OBJ, class T>
|
||||
class ConfigSerializer : public ConfigSerializerBase<OBJ>
|
||||
{
|
||||
public:
|
||||
using Obj = OBJ;
|
||||
using pointer_type = T Obj::*;
|
||||
using base = ConfigSerializerBase<Obj>;
|
||||
using converter = ConfigConverter<T>;
|
||||
|
||||
ConfigSerializer(const std::string &name,
|
||||
pointer_type pMember,
|
||||
const std::string &comment)
|
||||
: base(name, comment),
|
||||
pMember(pMember)
|
||||
{
|
||||
}
|
||||
virtual void SetValue(Obj &configurable, const std::string &value) const
|
||||
{
|
||||
configurable.*pMember = ConfigConverter<T>::FromString(value);
|
||||
}
|
||||
virtual const std::string GetValue(Obj &configurable) const
|
||||
{
|
||||
return ConfigConverter<T>::ToString(configurable.*pMember);
|
||||
}
|
||||
|
||||
private:
|
||||
pointer_type pMember;
|
||||
};
|
||||
|
||||
template <class OBJ>
|
||||
class ConfigSerializable
|
||||
{
|
||||
private:
|
||||
using Obj = OBJ;
|
||||
|
||||
using serializer_t = ConfigSerializerBase<OBJ>;
|
||||
using serializers_t = p2p::autoptr_vector<serializer_t>;
|
||||
|
||||
const serializers_t &serializers;
|
||||
|
||||
protected:
|
||||
ConfigSerializable(const serializers_t &serializers)
|
||||
: serializers(serializers)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void Save(std::ostream &f)
|
||||
{
|
||||
bool firstLine = true;
|
||||
for (const serializer_t *serializer : serializers)
|
||||
{
|
||||
if (serializer->GetComment().length() != 0)
|
||||
{
|
||||
if (!firstLine)
|
||||
{
|
||||
f << std::endl;
|
||||
}
|
||||
firstLine = false;
|
||||
std::vector<std::string> comments = split(serializer->GetComment(), '\n');
|
||||
|
||||
if (comments.size() != 0)
|
||||
for (const std::string &comment : comments)
|
||||
{
|
||||
f << "# " << comment << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
f << serializer->GetName() << '=' << serializer->GetValue((Obj &)*this) << std::endl;
|
||||
}
|
||||
}
|
||||
void Save(const std::string &path)
|
||||
{
|
||||
std::ofstream f;
|
||||
f.open(path);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw std::invalid_argument("Can't write to file " + path);
|
||||
}
|
||||
try
|
||||
{
|
||||
Save(f);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
throw std::invalid_argument("Error writing to '" + path + "'. " + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Load(std::istream &f)
|
||||
{
|
||||
std::unordered_map<std::string, serializer_t *> index;
|
||||
|
||||
for (serializer_t *serializer : serializers)
|
||||
{
|
||||
index[serializer->GetName()] = serializer;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
int nLine = 0;
|
||||
while (true)
|
||||
{
|
||||
if (f.eof())
|
||||
break;
|
||||
getline(f, line);
|
||||
++nLine;
|
||||
|
||||
// remove comments.
|
||||
auto npos = line.find_first_of('#');
|
||||
if (npos != std::string::npos)
|
||||
{
|
||||
line = line.substr(0, npos);
|
||||
}
|
||||
size_t start = 0;
|
||||
while (start < line.size() && line[start] == ' ')
|
||||
{
|
||||
++start;
|
||||
}
|
||||
if (start == line.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto pos = line.find_first_of('=', start);
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
std::stringstream msg;
|
||||
|
||||
msg << "(" << nLine << "," << (start + 1) << "): Syntax error. Expecting '='.";
|
||||
throw std::logic_error(msg.str());
|
||||
}
|
||||
std::string label = trim(line.substr(start, pos - start));
|
||||
std::string value = trim(line.substr(pos + 1));
|
||||
|
||||
if (index.contains(label))
|
||||
{
|
||||
serializer_t *serializer = index[label];
|
||||
try
|
||||
{
|
||||
serializer->SetValue((Obj &)*this, value);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "(" << nLine << ',' << (pos + 1) << "): " << e.what();
|
||||
throw std::logic_error(msg.str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "(" << nLine << ',' << (start) << "): "
|
||||
<< "Invalid property: " + label;
|
||||
throw std::logic_error(
|
||||
msg.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Load(const std::string &path, bool throwIfNotFound = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::ifstream f;
|
||||
f.open(path);
|
||||
if (!f.is_open())
|
||||
{
|
||||
if (throwIfNotFound)
|
||||
{
|
||||
throw std::logic_error("Can't open file.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
Load(f);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "Error: " << path << " " << e.what();
|
||||
throw std::logic_error(msg.str());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+8
-1
@@ -161,6 +161,12 @@ private:
|
||||
|
||||
isOpen = false;
|
||||
StopReaderThread();
|
||||
|
||||
if (realtimeMonitorPortSubscriptions != nullptr)
|
||||
{
|
||||
delete realtimeMonitorPortSubscriptions;
|
||||
realtimeMonitorPortSubscriptions = nullptr;
|
||||
}
|
||||
if (active)
|
||||
{
|
||||
audioDriver->Deactivate();
|
||||
@@ -933,8 +939,9 @@ public:
|
||||
Lv2Log::info("Audio started.");
|
||||
|
||||
}
|
||||
catch (PiPedalException &e)
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error(SS("Failed to start audio. " << e.what()));
|
||||
Close();
|
||||
active = false;
|
||||
throw;
|
||||
|
||||
@@ -785,6 +785,9 @@ Lv2PortInfo::Lv2PortInfo(Lv2Host *host, const LilvPlugin *plugin, const LilvPort
|
||||
NodeAutoFree unitsValueUri = lilv_port_get(plugin, pPort, host->lilvUris.unitsUri);
|
||||
this->units_ = UriToUnits(nodeAsString(unitsValueUri));
|
||||
|
||||
NodeAutoFree commentNode = lilv_port_get(plugin,pPort,host->lilvUris.rdfsComment);
|
||||
this->comment_ = nodeAsString(commentNode);
|
||||
|
||||
NodeAutoFree bufferType = lilv_port_get(plugin, pPort, host->lilvUris.bufferType_uri);
|
||||
|
||||
this->buffer_type_ = "";
|
||||
@@ -1167,6 +1170,7 @@ json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{{
|
||||
MAP_REF(Lv2PortInfo, buffer_type),
|
||||
MAP_REF(Lv2PortInfo, port_group),
|
||||
json_map::enum_reference("units", &Lv2PortInfo::units_, get_units_enum_converter()),
|
||||
MAP_REF(Lv2PortInfo, comment)
|
||||
}};
|
||||
|
||||
json_map::storage_type<Lv2PortGroup> Lv2PortGroup::jmap{{
|
||||
@@ -1229,6 +1233,7 @@ json_map::storage_type<Lv2PluginUiControlPort> Lv2PluginUiControlPort::jmap{{
|
||||
MAP_REF(Lv2PluginUiControlPort, port_group),
|
||||
|
||||
json_map::enum_reference("units", &Lv2PluginUiControlPort::units_, get_units_enum_converter()),
|
||||
MAP_REF(Lv2PluginUiControlPort, comment),
|
||||
|
||||
}};
|
||||
|
||||
|
||||
@@ -205,6 +205,7 @@ private:
|
||||
|
||||
std::string designation_;
|
||||
Units units_ = Units::none;
|
||||
std::string comment_;
|
||||
public:
|
||||
bool IsSwitch() const {
|
||||
return min_value_ == 0 && max_value_ == 1
|
||||
@@ -261,6 +262,7 @@ public:
|
||||
LV2_PROPERTY_GETSET_SCALAR(toggled_property);
|
||||
LV2_PROPERTY_GETSET_SCALAR(not_on_gui);
|
||||
LV2_PROPERTY_GETSET(port_group);
|
||||
LV2_PROPERTY_GETSET(comment);
|
||||
LV2_PROPERTY_GETSET_SCALAR(units);
|
||||
|
||||
LV2_PROPERTY_GETSET(buffer_type);
|
||||
@@ -417,6 +419,7 @@ public:
|
||||
, toggled_property_(pPort->toggled_property())
|
||||
, not_on_gui_(pPort->not_on_gui())
|
||||
, scale_points_(pPort->scale_points())
|
||||
, comment_(pPort->comment())
|
||||
, units_(pPort->units())
|
||||
{
|
||||
// Use symbols to index port groups, instead of uris.
|
||||
@@ -450,6 +453,7 @@ private:
|
||||
std::vector<Lv2ScalePoint> scale_points_;
|
||||
std::string port_group_;
|
||||
Units units_ = Units::none;
|
||||
std::string comment_;
|
||||
public:
|
||||
LV2_PROPERTY_GETSET(symbol);
|
||||
LV2_PROPERTY_GETSET_SCALAR(index);
|
||||
@@ -466,6 +470,7 @@ public:
|
||||
LV2_PROPERTY_GETSET_SCALAR(not_on_gui);
|
||||
LV2_PROPERTY_GETSET(scale_points);
|
||||
LV2_PROPERTY_GETSET(units);
|
||||
LV2_PROPERTY_GETSET(comment);
|
||||
|
||||
public:
|
||||
static json_map::storage_type<Lv2PluginUiControlPort> jmap;
|
||||
|
||||
+168
-97
@@ -18,8 +18,9 @@
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "pch.h"
|
||||
#include "DeviceIdFile.hpp"
|
||||
#include "ServiceConfiguration.hpp"
|
||||
#include "AudioConfig.hpp"
|
||||
#include "ConfigUtil.hpp"
|
||||
#include <sched.h>
|
||||
#include "PiPedalModel.hpp"
|
||||
#include "JackHost.hpp"
|
||||
@@ -55,8 +56,6 @@ PiPedalModel::PiPedalModel()
|
||||
#else
|
||||
this->jackServerSettings = this->storage.GetJackServerSettings();
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PiPedalModel::Close()
|
||||
@@ -84,37 +83,40 @@ void PiPedalModel::Close()
|
||||
|
||||
PiPedalModel::~PiPedalModel()
|
||||
{
|
||||
try {
|
||||
adminClient.UnmonitorGovernor();
|
||||
} catch (...) // noexcept here!
|
||||
try
|
||||
{
|
||||
adminClient.UnmonitorGovernor();
|
||||
}
|
||||
catch (...) // noexcept here!
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
CurrentPreset currentPreset;
|
||||
currentPreset.modified_ = this->hasPresetChanged;
|
||||
currentPreset.preset_ = this->pedalBoard;
|
||||
storage.SaveCurrentPreset(currentPreset);
|
||||
} catch (...)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (jackHost)
|
||||
{
|
||||
jackHost->Close();
|
||||
}
|
||||
} catch (...)
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#include <fstream>
|
||||
|
||||
void PiPedalModel::Init(const PiPedalConfiguration&configuration)
|
||||
void PiPedalModel::Init(const PiPedalConfiguration &configuration)
|
||||
{
|
||||
this->configuration = configuration;
|
||||
storage.SetConfigRoot(configuration.GetDocRoot());
|
||||
@@ -122,7 +124,6 @@ void PiPedalModel::Init(const PiPedalConfiguration&configuration)
|
||||
storage.Initialize();
|
||||
|
||||
this->jackServerSettings.ReadJackConfiguration();
|
||||
|
||||
}
|
||||
|
||||
void PiPedalModel::LoadLv2PluginInfo()
|
||||
@@ -147,14 +148,14 @@ void PiPedalModel::LoadLv2PluginInfo()
|
||||
// Copy all presets out of Lilv data to json files
|
||||
// so that we can close lilv while we're actually
|
||||
// running.
|
||||
for (const auto&plugin: lv2Host.GetPlugins())
|
||||
for (const auto &plugin : lv2Host.GetPlugins())
|
||||
{
|
||||
if (plugin->has_factory_presets())
|
||||
{
|
||||
if (!storage.HasPluginPresets(plugin->uri()))
|
||||
{
|
||||
PluginPresets pluginPresets = lv2Host.GetFactoryPluginPresets(plugin->uri());
|
||||
storage.SavePluginPresets(plugin->uri(),pluginPresets);
|
||||
storage.SavePluginPresets(plugin->uri(), pluginPresets);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,7 +166,6 @@ void PiPedalModel::Load()
|
||||
this->webRoot = configuration.GetWebRoot();
|
||||
this->webPort = (uint16_t)configuration.GetSocketServerPort();
|
||||
|
||||
|
||||
adminClient.MonitorGovernor(storage.GetGovernorSettings());
|
||||
|
||||
// lv2Host.Load(configuration.GetLv2Path().c_str());
|
||||
@@ -200,8 +200,8 @@ void PiPedalModel::Load()
|
||||
|
||||
struct sched_param scheduler_params;
|
||||
scheduler_params.sched_priority = 10;
|
||||
memset(&scheduler_params,0,sizeof(sched_param));
|
||||
sched_setscheduler(0,SCHED_RR,&scheduler_params);
|
||||
memset(&scheduler_params, 0, sizeof(sched_param));
|
||||
sched_setscheduler(0, SCHED_RR, &scheduler_params);
|
||||
#if JACK_HOST
|
||||
this->jackConfiguration = this->jackConfiguration.JackInitialize();
|
||||
#else
|
||||
@@ -216,7 +216,7 @@ void PiPedalModel::Load()
|
||||
this->lv2Host.OnConfigurationChanged(jackConfiguration, selection);
|
||||
try
|
||||
{
|
||||
jackHost->Open(this->jackServerSettings,selection);
|
||||
jackHost->Open(this->jackServerSettings, selection);
|
||||
|
||||
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
|
||||
this->lv2PedalBoard = lv2PedalBoard;
|
||||
@@ -226,7 +226,9 @@ void PiPedalModel::Load()
|
||||
{
|
||||
Lv2Log::error("Failed to load initial plugin. (%s)", e.what());
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
Lv2Log::error("Audio device not configured.");
|
||||
}
|
||||
}
|
||||
@@ -453,7 +455,7 @@ void PiPedalModel::FirePresetsChanged(int64_t clientId)
|
||||
delete[] t;
|
||||
}
|
||||
}
|
||||
void PiPedalModel::FirePluginPresetsChanged(const std::string & pluginUri)
|
||||
void PiPedalModel::FirePluginPresetsChanged(const std::string &pluginUri)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||
{
|
||||
@@ -473,7 +475,6 @@ void PiPedalModel::FirePluginPresetsChanged(const std::string & pluginUri)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PiPedalModel::SaveCurrentPreset(int64_t clientId)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||
@@ -482,31 +483,31 @@ void PiPedalModel::SaveCurrentPreset(int64_t clientId)
|
||||
this->SetPresetChanged(clientId, false);
|
||||
}
|
||||
|
||||
uint64_t PiPedalModel::CopyPluginPreset(const std::string&pluginUri,uint64_t presetId)
|
||||
uint64_t PiPedalModel::CopyPluginPreset(const std::string &pluginUri, uint64_t presetId)
|
||||
{
|
||||
uint64_t result = storage.CopyPluginPreset(pluginUri,presetId);
|
||||
uint64_t result = storage.CopyPluginPreset(pluginUri, presetId);
|
||||
FirePluginPresetsChanged(pluginUri);
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
void PiPedalModel::UpdatePluginPresets(const PluginUiPresets&pluginPresets)
|
||||
void PiPedalModel::UpdatePluginPresets(const PluginUiPresets &pluginPresets)
|
||||
{
|
||||
storage.UpdatePluginPresets(pluginPresets);
|
||||
FirePluginPresetsChanged(pluginPresets.pluginUri_);
|
||||
}
|
||||
int64_t PiPedalModel::SavePluginPresetAs(int64_t instanceId, const std::string&name)
|
||||
int64_t PiPedalModel::SavePluginPresetAs(int64_t instanceId, const std::string &name)
|
||||
{
|
||||
auto *item = this->pedalBoard.GetItem(instanceId);
|
||||
if (!item)
|
||||
{
|
||||
throw PiPedalException("Plugin not found.");
|
||||
}
|
||||
std::map<std::string,float> values;
|
||||
for (const auto & controlValue: item->controlValues())
|
||||
std::map<std::string, float> values;
|
||||
for (const auto &controlValue : item->controlValues())
|
||||
{
|
||||
values[controlValue.key()] = controlValue.value();
|
||||
}
|
||||
uint64_t presetId = storage.SavePluginPreset(item->uri(),name,values);
|
||||
uint64_t presetId = storage.SavePluginPreset(item->uri(), name, values);
|
||||
FirePluginPresetsChanged(item->uri());
|
||||
return presetId;
|
||||
}
|
||||
@@ -522,16 +523,15 @@ int64_t PiPedalModel::SaveCurrentPresetAs(int64_t clientId, const std::string &n
|
||||
return result;
|
||||
}
|
||||
|
||||
void PiPedalModel::UploadPluginPresets(const PluginPresets&pluginPresets)
|
||||
void PiPedalModel::UploadPluginPresets(const PluginPresets &pluginPresets)
|
||||
{
|
||||
if (pluginPresets.pluginUri_.length() == 0)
|
||||
{
|
||||
throw PiPedalException("Invalid plugin presets.");
|
||||
}
|
||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||
storage.SavePluginPresets(pluginPresets.pluginUri_,pluginPresets);
|
||||
storage.SavePluginPresets(pluginPresets.pluginUri_, pluginPresets);
|
||||
FirePluginPresetsChanged(pluginPresets.pluginUri_);
|
||||
|
||||
}
|
||||
int64_t PiPedalModel::UploadPreset(const BankFile &bankFile, int64_t uploadAfter)
|
||||
{
|
||||
@@ -623,7 +623,7 @@ int64_t PiPedalModel::DeletePreset(int64_t clientId, int64_t instanceId)
|
||||
-1, // can't use cached version.
|
||||
newSelection);
|
||||
}
|
||||
return newSelection;
|
||||
return newSelection;
|
||||
}
|
||||
bool PiPedalModel::RenamePreset(int64_t clientId, int64_t instanceId, const std::string &name)
|
||||
{
|
||||
@@ -649,13 +649,13 @@ GovernorSettings PiPedalModel::GetGovernorSettings()
|
||||
return result;
|
||||
}
|
||||
}
|
||||
void PiPedalModel::SetGovernorSettings(const std::string& governor)
|
||||
void PiPedalModel::SetGovernorSettings(const std::string &governor)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
adminClient.SetGovernorSettings(governor);
|
||||
|
||||
this->storage.SetGovernorSettings(governor);
|
||||
|
||||
|
||||
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
|
||||
{
|
||||
for (size_t i = 0; i < subscribers.size(); ++i)
|
||||
@@ -669,9 +669,8 @@ void PiPedalModel::SetGovernorSettings(const std::string& governor)
|
||||
}
|
||||
delete[] t;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
@@ -698,22 +697,46 @@ void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSet
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static std::string GetP2pdName()
|
||||
{
|
||||
std::string name = "/etc/pipedal/config/pipedal_p2pd.conf";
|
||||
|
||||
std::string result;
|
||||
|
||||
if (ConfigUtil::GetConfigLine(name,"p2p_device_name",&result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void PiPedalModel::UpdateDnsSd()
|
||||
{
|
||||
avahiService.Unannounce();
|
||||
|
||||
DeviceIdFile deviceIdFile;
|
||||
ServiceConfiguration deviceIdFile;
|
||||
deviceIdFile.Load();
|
||||
|
||||
|
||||
std::string p2pdName = GetP2pdName();
|
||||
if (p2pdName != "") {
|
||||
deviceIdFile.deviceName = p2pdName;
|
||||
}
|
||||
|
||||
if (deviceIdFile.deviceName != "" && deviceIdFile.uuid != "")
|
||||
{
|
||||
avahiService.Announce(webPort,deviceIdFile.deviceName,deviceIdFile.uuid,"pipedal");
|
||||
} else {
|
||||
avahiService.Announce(webPort, deviceIdFile.deviceName, deviceIdFile.uuid, "pipedal");
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// device_uuid file is written at install time. This warning is harmless if you're debugging.
|
||||
// Without it, we can't pulblish the website via dnsDS.
|
||||
// Run "pipedalconfig --install" to create the file.
|
||||
Lv2Log::warning("Cant read device_uuid file. dnsSD announcement skipped.");
|
||||
Lv2Log::warning("Cant read device_uuid file from service.conf file. dnsSD announcement skipped.");
|
||||
}
|
||||
|
||||
}
|
||||
void PiPedalModel::SetWifiDirectConfigSettings(const WifiDirectConfigSettings &wifiDirectConfigSettings)
|
||||
{
|
||||
@@ -741,11 +764,7 @@ void PiPedalModel::SetWifiDirectConfigSettings(const WifiDirectConfigSettings &w
|
||||
|
||||
// update NSD-SD announement.
|
||||
UpdateDnsSd();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WifiConfigSettings PiPedalModel::GetWifiConfigSettings()
|
||||
@@ -786,7 +805,6 @@ bool PiPedalModel::GetShowStatusMonitor()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex); // copy atomically.
|
||||
return storage.GetShowStatusMonitor();
|
||||
|
||||
}
|
||||
|
||||
JackConfiguration PiPedalModel::GetJackConfiguration()
|
||||
@@ -795,37 +813,82 @@ JackConfiguration PiPedalModel::GetJackConfiguration()
|
||||
return this->jackConfiguration;
|
||||
}
|
||||
|
||||
void PiPedalModel::SetJackChannelSelection(int64_t clientId, const JackChannelSelection &channelSelection)
|
||||
void PiPedalModel::RestartAudio()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex); // copy atomically.
|
||||
this->storage.SetJackChannelSelection(channelSelection);
|
||||
this->FireChannelSelectionChanged(clientId);
|
||||
|
||||
this->lv2Host.OnConfigurationChanged(jackConfiguration, channelSelection);
|
||||
if (this->jackHost->IsOpen())
|
||||
{
|
||||
|
||||
this->jackHost->Close();
|
||||
#ifdef JUNK
|
||||
// restarting is a bit dodgy. It was impossible with Jack, but
|
||||
// now very plausible with the ALSA audio stack.
|
||||
|
||||
}
|
||||
// restarting is a bit dodgy. It was impossible with Jack, but
|
||||
// now very plausible with the ALSA audio stack.
|
||||
|
||||
// Still bugs wrt/ restarting the circular buffers for the audio thread.
|
||||
|
||||
// for the meantime, just rely on the fact that the admin service will restart
|
||||
// for the meantime, just rely on the fact that the admin service will restart
|
||||
// the process.
|
||||
//...
|
||||
|
||||
// do a complete reload.
|
||||
|
||||
this->jackHost->SetPedalBoard(nullptr);
|
||||
this->jackHost->Open(this->jackServerSettings,channelSelection);
|
||||
this->jackHost->SetPedalBoard(nullptr);
|
||||
|
||||
this->jackConfiguration.AlsaInitialize(this->jackServerSettings);
|
||||
|
||||
if (this->jackConfiguration.isValid())
|
||||
{
|
||||
JackChannelSelection selection = storage.GetJackChannelSelection(jackConfiguration);
|
||||
selection = selection.RemoveInvalidChannels(jackConfiguration);
|
||||
|
||||
} else {
|
||||
jackConfiguration.SetErrorStatus("Error");
|
||||
}
|
||||
|
||||
FireJackConfigurationChanged(jackConfiguration);
|
||||
|
||||
|
||||
if (!jackServerSettings.IsValid() || !jackConfiguration.isValid())
|
||||
{
|
||||
Lv2Log::error("Audio configuration not valid.");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
JackChannelSelection channelSelection = GetJackChannelSelection();
|
||||
if (!channelSelection.isValid())
|
||||
{
|
||||
Lv2Log::error("Audio configuration not valid.");
|
||||
return;
|
||||
}
|
||||
this->jackHost->Open(this->jackServerSettings, channelSelection);
|
||||
|
||||
this->lv2Host.OnConfigurationChanged(jackConfiguration, channelSelection);
|
||||
|
||||
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
|
||||
this->lv2PedalBoard = lv2PedalBoard;
|
||||
jackHost->SetPedalBoard(lv2PedalBoard);
|
||||
this->UpdateRealtimeVuSubscriptions();
|
||||
#endif
|
||||
UpdateRealtimeMonitorPortSubscriptions();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error(SS("Failed to start audio. " << e.what()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void PiPedalModel::SetJackChannelSelection(int64_t clientId, const JackChannelSelection &channelSelection)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex); // copy atomically.
|
||||
this->storage.SetJackChannelSelection(channelSelection);
|
||||
|
||||
this->lv2Host.OnConfigurationChanged(jackConfiguration, channelSelection);
|
||||
}
|
||||
|
||||
RestartAudio(); // no lock to avoid mutex deadlock when reader thread is sending notifications..
|
||||
|
||||
this->FireChannelSelectionChanged(clientId);
|
||||
}
|
||||
|
||||
void PiPedalModel::FireChannelSelectionChanged(int64_t clientId)
|
||||
@@ -1024,7 +1087,6 @@ void PiPedalModel::UnmonitorPort(int64_t subscriptionHandle)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PiPedalModel::OnNotifyMonitorPort(const MonitorPortUpdate &update)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
@@ -1095,7 +1157,7 @@ void PiPedalModel::GetLv2Parameter(
|
||||
|
||||
BankIndex PiPedalModel::GetBankIndex() const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(const_cast<std::recursive_mutex&>(mutex));
|
||||
std::lock_guard<std::recursive_mutex> guard(const_cast<std::recursive_mutex &>(mutex));
|
||||
return storage.GetBanks();
|
||||
}
|
||||
|
||||
@@ -1137,16 +1199,17 @@ JackServerSettings PiPedalModel::GetJackServerSettings()
|
||||
|
||||
void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSettings)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
std::unique_lock<std::recursive_mutex> guard(mutex);
|
||||
|
||||
#if JACK_HOST
|
||||
if (!adminClient.CanUseShutdownClient())
|
||||
{
|
||||
throw PiPedalException("Can't change server settings when running a debug server.");
|
||||
}
|
||||
#endif
|
||||
|
||||
this->jackServerSettings = jackServerSettings;
|
||||
|
||||
|
||||
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us)
|
||||
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
|
||||
for (size_t i = 0; i < subscribers.size(); ++i)
|
||||
@@ -1160,19 +1223,25 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
|
||||
}
|
||||
delete[] t;
|
||||
|
||||
|
||||
#if ALSA_HOST
|
||||
storage.SetJackServerSettings(jackServerSettings);
|
||||
|
||||
FireJackConfigurationChanged(this->jackConfiguration);
|
||||
|
||||
guard.unlock();
|
||||
RestartAudio();
|
||||
|
||||
#endif
|
||||
#if JACK_HOST
|
||||
if (adminClient.CanUseShutdownClient())
|
||||
{
|
||||
|
||||
#if ALSA_HOST
|
||||
storage.SetJackServerSettings(jackServerSettings);
|
||||
#endif
|
||||
|
||||
// save the current (edited) preset now in case the service shutdown isn't clean.
|
||||
CurrentPreset currentPreset;
|
||||
currentPreset.modified_ = this->hasPresetChanged;
|
||||
currentPreset.preset_ = this->pedalBoard;
|
||||
storage.SaveCurrentPreset(currentPreset);
|
||||
|
||||
|
||||
this->jackConfiguration.SetIsRestarting(true);
|
||||
FireJackConfigurationChanged(this->jackConfiguration);
|
||||
this->jackHost->UpdateServerConfiguration(
|
||||
@@ -1195,8 +1264,8 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
|
||||
}
|
||||
else
|
||||
{
|
||||
// we now do a complete restart of the services,
|
||||
// so just sit tight and wait for the restart.
|
||||
// we now do a complete restart of the services,
|
||||
// so just sit tight and wait for the restart.
|
||||
#ifdef JUNK
|
||||
this->jackConfiguration.SetErrorStatus("");
|
||||
FireJackConfigurationChanged(this->jackConfiguration);
|
||||
@@ -1211,7 +1280,9 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
|
||||
#endif
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PiPedalModel::UpdateDefaults(PedalBoardItem *pedalBoardItem)
|
||||
@@ -1259,15 +1330,14 @@ void PiPedalModel::UpdateDefaults(PedalBoard *pedalBoard)
|
||||
}
|
||||
}
|
||||
|
||||
PluginPresets PiPedalModel::GetPluginPresets(const std::string& pluginUri)
|
||||
PluginPresets PiPedalModel::GetPluginPresets(const std::string &pluginUri)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
return storage.GetPluginPresets(pluginUri);
|
||||
|
||||
}
|
||||
|
||||
PluginUiPresets PiPedalModel::GetPluginUiPresets(const std::string& pluginUri)
|
||||
PluginUiPresets PiPedalModel::GetPluginUiPresets(const std::string &pluginUri)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
@@ -1332,7 +1402,7 @@ void PiPedalModel::DeleteMidiListeners(int64_t clientId)
|
||||
jackHost->SetListenForMidiEvent(midiEventListeners.size() != 0);
|
||||
}
|
||||
|
||||
void PiPedalModel::OnNotifyAtomOutput(uint64_t instanceId, const std::string&atomType,const std::string&atomJson)
|
||||
void PiPedalModel::OnNotifyAtomOutput(uint64_t instanceId, const std::string &atomType, const std::string &atomJson)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
@@ -1344,19 +1414,18 @@ void PiPedalModel::OnNotifyAtomOutput(uint64_t instanceId, const std::string&ato
|
||||
auto subscriber = this->GetNotificationSubscriber(listener.clientId);
|
||||
if (subscriber)
|
||||
{
|
||||
subscriber->OnNotifyAtomOutput(listener.clientHandle,instanceId,atomType,atomJson);
|
||||
} else {
|
||||
subscriber->OnNotifyAtomOutput(listener.clientHandle, instanceId, atomType, atomJson);
|
||||
}
|
||||
else
|
||||
{
|
||||
atomOutputListeners.erase(atomOutputListeners.begin() + i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
jackHost->SetListenForAtomOutput(atomOutputListeners.size() != 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
@@ -1370,7 +1439,9 @@ void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl)
|
||||
if (subscriber)
|
||||
{
|
||||
subscriber->OnNotifyMidiListener(listener.clientHandle, isNote, noteOrControl);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
midiEventListeners.erase(midiEventListeners.begin() + i);
|
||||
--i;
|
||||
}
|
||||
@@ -1400,7 +1471,7 @@ void PiPedalModel::CancelListenForMidiEvent(int64_t clientId, int64_t clientHand
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
for (size_t i = 0; i < midiEventListeners.size(); ++i)
|
||||
{
|
||||
const auto& listener = midiEventListeners[i];
|
||||
const auto &listener = midiEventListeners[i];
|
||||
if (listener.clientId == clientId && listener.clientHandle == clientHandle)
|
||||
{
|
||||
midiEventListeners.erase(midiEventListeners.begin() + i);
|
||||
@@ -1417,7 +1488,7 @@ void PiPedalModel::CancelListenForAtomOutputs(int64_t clientId, int64_t clientHa
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
for (size_t i = 0; i < atomOutputListeners.size(); ++i)
|
||||
{
|
||||
const auto&listener = atomOutputListeners[i];
|
||||
const auto &listener = atomOutputListeners[i];
|
||||
if (listener.clientId == clientId && listener.clientHandle == clientHandle)
|
||||
{
|
||||
atomOutputListeners.erase(atomOutputListeners.begin() + i);
|
||||
@@ -1434,24 +1505,24 @@ std::vector<AlsaDeviceInfo> PiPedalModel::GetAlsaDevices()
|
||||
return this->alsaDevices.GetAlsaDevices();
|
||||
}
|
||||
|
||||
const std::filesystem::path& PiPedalModel::GetWebRoot() const
|
||||
const std::filesystem::path &PiPedalModel::GetWebRoot() const
|
||||
{
|
||||
return webRoot;
|
||||
}
|
||||
|
||||
std::map<std::string,bool> PiPedalModel::GetFavorites() const
|
||||
std::map<std::string, bool> PiPedalModel::GetFavorites() const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(const_cast<std::recursive_mutex&>(mutex));
|
||||
|
||||
std::lock_guard<std::recursive_mutex> guard(const_cast<std::recursive_mutex &>(mutex));
|
||||
|
||||
return storage.GetFavorites();
|
||||
}
|
||||
void PiPedalModel::SetFavorites(const std::map<std::string,bool> &favorites)
|
||||
void PiPedalModel::SetFavorites(const std::map<std::string, bool> &favorites)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
storage.SetFavorites(favorites);
|
||||
|
||||
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us)
|
||||
std::vector<IPiPedalModelSubscriber*> t;
|
||||
std::vector<IPiPedalModelSubscriber *> t;
|
||||
t.reserve(this->subscribers.size());
|
||||
|
||||
for (size_t i = 0; i < subscribers.size(); ++i)
|
||||
|
||||
@@ -142,6 +142,8 @@ private:
|
||||
void UpdateRealtimeMonitorPortSubscriptions();
|
||||
void OnVuUpdate(const std::vector<VuUpdate>& updates);
|
||||
|
||||
void RestartAudio();
|
||||
|
||||
std::vector<RealtimeParameterRequest*> outstandingParameterRequests;
|
||||
|
||||
IPiPedalModelSubscriber*GetNotificationSubscriber(int64_t clientId);
|
||||
|
||||
+12
-12
@@ -836,10 +836,10 @@ public:
|
||||
std::string governor;
|
||||
pReader->read(&governor);
|
||||
std::string fromAddress = this->getFromAddress();
|
||||
if (!IsOnLocalSubnet(fromAddress))
|
||||
{
|
||||
throw PiPedalException("Permission denied. Not on local subnet.");
|
||||
}
|
||||
// if (!IsOnLocalSubnet(fromAddress))
|
||||
// {
|
||||
// throw PiPedalException("Permission denied. Not on local subnet.");
|
||||
// }
|
||||
this->model.SetGovernorSettings(governor);
|
||||
this->Reply(replyTo, "setGovernorSettings");
|
||||
}
|
||||
@@ -852,10 +852,10 @@ public:
|
||||
throw PiPedalException("Can't change server settings when running interactively.");
|
||||
}
|
||||
std::string fromAddress = this->getFromAddress();
|
||||
if (!IsOnLocalSubnet(fromAddress))
|
||||
{
|
||||
throw PiPedalException("Permission denied. Not on local subnet.");
|
||||
}
|
||||
// if (!IsOnLocalSubnet(fromAddress))
|
||||
// {
|
||||
// throw PiPedalException("Permission denied. Not on local subnet.");
|
||||
// }
|
||||
|
||||
this->model.SetWifiConfigSettings(wifiConfigSettings);
|
||||
this->Reply(replyTo, "setWifiConfigSettings");
|
||||
@@ -873,10 +873,10 @@ public:
|
||||
throw PiPedalException("Can't change server settings when running interactively.");
|
||||
}
|
||||
std::string fromAddress = this->getFromAddress();
|
||||
if (!IsOnLocalSubnet(fromAddress))
|
||||
{
|
||||
throw PiPedalException("Permission denied. Not on local subnet.");
|
||||
}
|
||||
// if (!IsOnLocalSubnet(fromAddress))
|
||||
// {
|
||||
// throw PiPedalException("Permission denied. Not on local subnet.");
|
||||
// }
|
||||
|
||||
this->model.SetWifiDirectConfigSettings(wifiDirectConfigSettings);
|
||||
this->Reply(replyTo, "setWifiDirectConfigSettings");
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "DeviceIdFile.hpp"
|
||||
#include "ServiceConfiguration.hpp"
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <grp.h>
|
||||
@@ -33,44 +33,45 @@
|
||||
using namespace pipedal;
|
||||
|
||||
using namespace std;
|
||||
using namespace config_serializer;
|
||||
|
||||
|
||||
const char DeviceIdFile::DEVICEID_FILE_NAME[] = "/etc/pipedal/config/device_uuid";
|
||||
const char ServiceConfiguration::DEVICEID_FILE_NAME[] = "/etc/pipedal/config/service.conf";
|
||||
|
||||
void DeviceIdFile::Load()
|
||||
#define SERIALIZER_ENTRY(MEMBER_NAME) \
|
||||
new ConfigSerializer<ServiceConfiguration,decltype(ServiceConfiguration::MEMBER_NAME)>(#MEMBER_NAME, &ServiceConfiguration::MEMBER_NAME, "")
|
||||
|
||||
|
||||
static p2p::autoptr_vector<ConfigSerializerBase<ServiceConfiguration>>
|
||||
deviceIdSerializers
|
||||
{
|
||||
SERIALIZER_ENTRY(uuid),
|
||||
SERIALIZER_ENTRY(deviceName),
|
||||
SERIALIZER_ENTRY(server_port)
|
||||
};
|
||||
|
||||
|
||||
ServiceConfiguration::ServiceConfiguration()
|
||||
: base(deviceIdSerializers)
|
||||
{
|
||||
{
|
||||
ifstream f;
|
||||
|
||||
f.open(DEVICEID_FILE_NAME);
|
||||
if (f.is_open())
|
||||
{
|
||||
std::getline(f, uuid);
|
||||
std::getline(f, deviceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
void DeviceIdFile::Save()
|
||||
{
|
||||
{
|
||||
std::string path = DEVICEID_FILE_NAME;
|
||||
ofstream f;
|
||||
f.open(path, ios_base::trunc);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw invalid_argument("Can't write to file " + path);
|
||||
}
|
||||
|
||||
void ServiceConfiguration::Load()
|
||||
{
|
||||
base::Load(DEVICEID_FILE_NAME,false);
|
||||
}
|
||||
void ServiceConfiguration::Save()
|
||||
{
|
||||
base::Save(DEVICEID_FILE_NAME);
|
||||
{
|
||||
struct group *group_;
|
||||
group_ = getgrnam("pipedal_d");
|
||||
if (group_ == nullptr)
|
||||
{
|
||||
throw logic_error("Group not found: pipedal_d");
|
||||
}
|
||||
std::ignore = chown(path.c_str(), -1, group_->gr_gid);
|
||||
std::ignore = chmod(path.c_str(), S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
|
||||
|
||||
f << uuid << endl;
|
||||
f << deviceName << endl;
|
||||
std::ignore = chown(DEVICEID_FILE_NAME,-1,group_->gr_gid);
|
||||
std::ignore = chmod(DEVICEID_FILE_NAME, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
|
||||
}
|
||||
}
|
||||
@@ -25,10 +25,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "ConfigSerializer.h"
|
||||
|
||||
namespace pipedal {
|
||||
class DeviceIdFile {
|
||||
using namespace config_serializer;
|
||||
|
||||
class ServiceConfiguration: protected ConfigSerializable<ServiceConfiguration> {
|
||||
public:
|
||||
using base = ConfigSerializable<ServiceConfiguration>;
|
||||
|
||||
ServiceConfiguration();
|
||||
|
||||
static const char DEVICEID_FILE_NAME[];
|
||||
|
||||
void Load();
|
||||
@@ -36,6 +43,7 @@ namespace pipedal {
|
||||
|
||||
|
||||
std::string uuid;
|
||||
std::string deviceName;
|
||||
std::string deviceName = "PiPedal";
|
||||
uint32_t server_port = 80;
|
||||
};
|
||||
}
|
||||
+13
-6
@@ -21,7 +21,7 @@
|
||||
#include "ss.hpp"
|
||||
|
||||
#include "P2pConfigFiles.hpp"
|
||||
#include "DeviceIdFile.hpp"
|
||||
#include "ServiceConfiguration.hpp"
|
||||
#include "SetWifiConfig.hpp"
|
||||
#include "PiPedalException.hpp"
|
||||
#include "SystemConfigFile.hpp"
|
||||
@@ -410,6 +410,16 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
|
||||
{
|
||||
cout << "Disabling P2P" << endl;
|
||||
UninstallP2p();
|
||||
|
||||
ServiceConfiguration deviceIdFile;
|
||||
deviceIdFile.Load();
|
||||
deviceIdFile.deviceName = settings.hotspotName_;
|
||||
deviceIdFile.Save();
|
||||
|
||||
|
||||
// Announce new mDNS service with potentially new deviceName.
|
||||
sysExec(SYSTEMCTL_BIN " restart pipedald");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -417,7 +427,7 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
|
||||
settings.Save();
|
||||
// ******************* device_uuid
|
||||
|
||||
DeviceIdFile deviceIdFile;
|
||||
ServiceConfiguration deviceIdFile;
|
||||
deviceIdFile.Load();
|
||||
deviceIdFile.deviceName = settings.hotspotName_;
|
||||
deviceIdFile.Save();
|
||||
@@ -502,12 +512,9 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
|
||||
sysExec(SYSTEMCTL_BIN " daemon-reload");
|
||||
|
||||
sysExec(SYSTEMCTL_BIN " stop pipedal_p2pd");
|
||||
sysExec(SYSTEMCTL_BIN " stop dnsmasq");
|
||||
sysExec(SYSTEMCTL_BIN " stop wpa_supplicant");
|
||||
sysExec(SYSTEMCTL_BIN " restart dhcpcd");
|
||||
sysExec(SYSTEMCTL_BIN " unmask wpa_supplicant");
|
||||
sysExec(SYSTEMCTL_BIN " start wpa_supplicant");
|
||||
sysExec(SYSTEMCTL_BIN " enable wpa_supplicant");
|
||||
sysExec(SYSTEMCTL_BIN " enable wpa_supplicant"); // ?? Not sure that's still right. :-/
|
||||
sysExec(SYSTEMCTL_BIN " start dnsmasq");
|
||||
sysExec(SYSTEMCTL_BIN " enable dnsmasq");
|
||||
sysExec(SYSTEMCTL_BIN " start pipedal_p2pd");
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <stdexcept>
|
||||
#include <random>
|
||||
#include "ConfigUtil.hpp"
|
||||
#include "DeviceIdFile.hpp"
|
||||
#include "ServiceConfiguration.hpp"
|
||||
#include "WriteTemplateFile.hpp"
|
||||
#include "ss.hpp"
|
||||
|
||||
@@ -169,7 +169,7 @@ void WifiDirectConfigSettings::Load()
|
||||
this->channel_ = "6";
|
||||
}
|
||||
try {
|
||||
DeviceIdFile deviceIdFile;
|
||||
ServiceConfiguration deviceIdFile;
|
||||
deviceIdFile.Load();
|
||||
if (deviceIdFile.deviceName == "")
|
||||
{
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
namespace p2p {
|
||||
|
||||
template <typename T> class autoptr_vector
|
||||
{
|
||||
std::vector<T*> v;
|
||||
private:
|
||||
// no copy no move.
|
||||
autoptr_vector(const autoptr_vector<T> &) = delete;
|
||||
autoptr_vector(autoptr_vector<T> && ) = delete;
|
||||
public:
|
||||
using iterator = std::vector<T*>::const_iterator;
|
||||
|
||||
autoptr_vector() { }
|
||||
autoptr_vector(std::initializer_list<T*> l)
|
||||
:v(l)
|
||||
{
|
||||
|
||||
}
|
||||
~autoptr_vector() {
|
||||
for (T *i: v)
|
||||
{
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
void push_back(T*value) { v.push_back(value);}
|
||||
|
||||
iterator begin() const { return v.begin(); }
|
||||
iterator end() const { return v.end(); }
|
||||
|
||||
T*operator[](size_t index) const { return v[index]; }
|
||||
|
||||
size_t size() const { return v.size(); }
|
||||
};
|
||||
}
|
||||
+2
-2
@@ -23,7 +23,7 @@
|
||||
#include "BeastServer.hpp"
|
||||
#include <iostream>
|
||||
#include "Lv2Log.hpp"
|
||||
#include "DeviceIdFile.hpp"
|
||||
#include "ServiceConfiguration.hpp"
|
||||
#include "AvahiService.hpp"
|
||||
|
||||
#include "PiPedalSocket.hpp"
|
||||
@@ -428,7 +428,7 @@ public:
|
||||
#define LINK_LOCAL_WEB_SOCKET 1
|
||||
#if LINK_LOCAL_WEB_SOCKET
|
||||
std::string webSocketAddress = GetLinkLocalAddress(fromAddress);
|
||||
Lv2Log::info(SS("Web Socket Adddress: " << webSocketAddress << ":" << portNumber));
|
||||
Lv2Log::info(SS("Web Socket Address: " << webSocketAddress << ":" << portNumber));
|
||||
#else
|
||||
std::string webSocketAddress = "*";
|
||||
#endif
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
[Unit]
|
||||
Description=PiPedal P2P Session Manager
|
||||
After=network.target
|
||||
After=nss-lookup.target
|
||||
After=wpa_supplicant.service
|
||||
After=dhcpcd.service
|
||||
StartLimitIntervalSec=300
|
||||
StartLimitBurst=8
|
||||
|
||||
[Service]
|
||||
ExecStart=${COMMAND} --systemd --log-level debug
|
||||
Type=notify
|
||||
WorkingDirectory=/var/pipedal
|
||||
TimeoutStopSec=5
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -7,6 +7,7 @@ After=local-fs.target
|
||||
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
Type=notify
|
||||
LimitMEMLOCK=infinity
|
||||
LimitRTPRIO=95
|
||||
|
||||
+1
-1
Submodule submodules/pipedal_p2pd updated: 85b68b8400...81111f111f
Reference in New Issue
Block a user