From fa9888914d0c3c89ac362e596230aa926f06341a Mon Sep 17 00:00:00 2001 From: Robin Davies Date: Tue, 12 Jul 2022 21:10:49 -0400 Subject: [PATCH] refactor service.conf --- lv2/aarch64/ToobAmp.lv2/CabSim.ttl | 2 +- lv2/aarch64/ToobAmp.lv2/InputStage.ttl | 9 +- lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl | 30 +- lv2/aarch64/ToobAmp.lv2/ToneStack.ttl | 2 +- lv2/aarch64/ToobAmp.lv2/ToobAmp.so.0.1.7 | Bin 737408 -> 737408 bytes lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl | 6 +- lv2/aarch64/ToobAmp.lv2/ToobML.ttl | 77 +- react/public/var/config.json | 4 +- react/src/Lv2Plugin.tsx | 7 +- react/src/MainPage.tsx | 1 - react/src/PiPedalModel.tsx | 6 +- react/src/PluginControlView.tsx | 7 +- react/src/PluginInfoDialog.tsx | 93 +- react/src/Utility.tsx | 7 + react/src/WifiDirectConfigDialog.tsx | 35 +- src/AdminMain.cpp | 10 +- src/AlsaDriver.cpp | 37 +- src/BeastServer~.cpp.bak | 1248 ----------------- src/BeastServer~.hpp.bak | 217 --- src/CMakeLists.txt | 12 +- src/ConfigMain.cpp | 85 +- src/ConfigSerializer.cpp | 175 +++ src/ConfigSerializer.h | 369 +++++ src/JackHost.cpp | 9 +- src/Lv2Host.cpp | 5 + src/Lv2Host.hpp | 5 + src/PiPedalModel.cpp | 265 ++-- src/PiPedalModel.hpp | 2 + src/PiPedalSocket.cpp | 24 +- ...iceIdFile.cpp => ServiceConfiguration.cpp} | 55 +- ...iceIdFile.hpp => ServiceConfiguration.hpp} | 12 +- src/SetWifiConfig.cpp | 19 +- src/WifiDirectConfigSettings.cpp | 4 +- src/autoptr_vector.h | 63 + src/main.cpp | 4 +- src/templates/pipedal_p2pd.service.template | 5 + src/templates/pipedald.service.template | 1 + submodules/pipedal_p2pd | 2 +- 38 files changed, 1181 insertions(+), 1733 deletions(-) delete mode 100644 src/BeastServer~.cpp.bak delete mode 100644 src/BeastServer~.hpp.bak create mode 100644 src/ConfigSerializer.cpp create mode 100644 src/ConfigSerializer.h rename src/{DeviceIdFile.cpp => ServiceConfiguration.cpp} (62%) rename src/{DeviceIdFile.hpp => ServiceConfiguration.hpp} (80%) create mode 100644 src/autoptr_vector.h diff --git a/lv2/aarch64/ToobAmp.lv2/CabSim.ttl b/lv2/aarch64/ToobAmp.lv2/CabSim.ttl index d689338..66490c3 100644 --- a/lv2/aarch64/ToobAmp.lv2/CabSim.ttl +++ b/lv2/aarch64/ToobAmp.lv2/CabSim.ttl @@ -42,7 +42,7 @@ a lv2:Plugin , - lv2:AmplifierPlugin ; + lv2:SimulatorPlugin ; doap:name "TooB Cab Simulator" , "TooB Cab Simulator"@en-gb ; diff --git a/lv2/aarch64/ToobAmp.lv2/InputStage.ttl b/lv2/aarch64/ToobAmp.lv2/InputStage.ttl index 736e19c..41b1fb7 100644 --- a/lv2/aarch64/ToobAmp.lv2/InputStage.ttl +++ b/lv2/aarch64/ToobAmp.lv2/InputStage.ttl @@ -52,7 +52,7 @@ inputStage:filterGroup a lv2:Plugin , - lv2:MixerPlugin ; + lv2:SimulatorPlugin ; doap:name "TooB Input Stage" , "TooB Input Stage"@en-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 , diff --git a/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl b/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl index 0563734..d929a9b 100644 --- a/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl +++ b/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl @@ -60,7 +60,7 @@ pstage:stage3 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)" + ], diff --git a/lv2/aarch64/ToobAmp.lv2/ToneStack.ttl b/lv2/aarch64/ToobAmp.lv2/ToneStack.ttl index a3db68b..042818a 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToneStack.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToneStack.ttl @@ -42,7 +42,7 @@ a lv2:Plugin , - lv2:AmplifierPlugin ; + lv2:SimulatorPlugin ; doap:name "TooB Tone Stack" , "TooB Tone Stack"@en-gb ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.0.1.7 b/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.0.1.7 index dc4c8bdeded14c5dcf7aff645e20e00c47b888cb..207df41e063436d9f086dde7f834152cebc1c3ae 100644 GIT binary patch delta 152419 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 O@@stVn9h}>D*S&ObQPEY delta 152201 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 X{*(tvX|YyTJ7l?fS-dR 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 diff --git a/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl b/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl index b38a479..b636728 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl @@ -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 ; + 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 , diff --git a/lv2/aarch64/ToobAmp.lv2/ToobML.ttl b/lv2/aarch64/ToobAmp.lv2/ToobML.ttl index c715b04..74fa372 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobML.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobML.ttl @@ -40,7 +40,7 @@ toobml:filterGroup a lv2:Plugin , - lv2:AmplifierPlugin ; + lv2:SimulatorPlugin ; doap:name "TooB ML Amplifier" , "TooB ML Amplifier"@en-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." ; ] + . diff --git a/react/public/var/config.json b/react/public/var/config.json index 20919e1..80d344a 100644 --- a/react/public/var/config.json +++ b/react/public/var/config.json @@ -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": [] } \ No newline at end of file diff --git a/react/src/Lv2Plugin.tsx b/react/src/Lv2Plugin.tsx index 667c5fe..b2420fb 100644 --- a/react/src/Lv2Plugin.tsx +++ b/react/src/Lv2Plugin.tsx @@ -44,6 +44,7 @@ export class Port implements Deserializable { 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 { 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 { 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 { { this.controlType = ControlType.Select; } - return this; } @@ -271,6 +275,7 @@ export class UiControl implements Deserializable { scale_points: ScalePoint[] = []; port_group: string = ""; units: Units = Units.none; + comment: string = ""; // Return the value of the closest scale_point. clampSelectValue(value: number): number{ diff --git a/react/src/MainPage.tsx b/react/src/MainPage.tsx index 336ebb8..ae67584 100644 --- a/react/src/MainPage.tsx +++ b/react/src/MainPage.tsx @@ -503,7 +503,6 @@ export const MainPage = GetControlView(pedalBoardItem) ) } - this.setState({showMidiBindingsDialog: false} ) } diff --git a/react/src/PiPedalModel.tsx b/react/src/PiPedalModel.tsx index f6e7508..1ac315c 100644 --- a/react/src/PiPedalModel.tsx +++ b/react/src/PiPedalModel.tsx @@ -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 diff --git a/react/src/PluginControlView.tsx b/react/src/PluginControlView.tsx index bd81fac..6e6301e 100644 --- a/react/src/PluginControlView.tsx +++ b/react/src/PluginControlView.tsx @@ -455,7 +455,12 @@ const PluginControlView = { nodes } -

+
+ { + (!this.state.landscapeGrid) && ( +
+ ) + }
- { - controls.map((control) => ( - - + 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(( + + + {control.name} - - )) - } - - ); + + + + {control.comment} + + + + )); + + } + return ( + + { + trs + } +
+ ); + + } else { + return ( + + { + controls.map((control) => ( + + + {control.name} + + + )) + } + + ); + } } + + const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => { let model = PiPedalModelFactory.getInstance(); @@ -205,19 +246,25 @@ const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => {
- - Author:  - {(plugin.author_homepage !== "") - ?
{plugin.author_name} - : ( - plugin.author_name - ) - } - - - {ioDescription(plugin)} - +
+
+ + Author:  + {(plugin.author_homepage !== "") + ? {plugin.author_name} + : ( + plugin.author_name + ) + } + +
+
+ + {ioDescription(plugin)} + +
+
Controls: diff --git a/react/src/Utility.tsx b/react/src/Utility.tsx index adf0144..3e22bb6 100644 --- a/react/src/Utility.tsx +++ b/react/src/Utility.tsx @@ -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) ); } diff --git a/react/src/WifiDirectConfigDialog.tsx b/react/src/WifiDirectConfigDialog.tsx index da0cf19..dfdac9e 100644 --- a/react/src/WifiDirectConfigDialog.tsx +++ b/react/src/WifiDirectConfigDialog.tsx @@ -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" />
- { this.handleCountryChanged(event); }} @@ -413,17 +413,18 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })( )} {(!this.state.landscapeLayout) && ( - this.handleEnableChanged(e)} - color="secondary" - /> - )} - label="Wi-Fi Direct Hotspot" - /> -
+
+ this.handleEnableChanged(e)} + color="secondary" + /> + )} + label="Wi-Fi Direct Hotspot" + /> +
this.setState({ name: e.target.value, nameError: false, nameErrorMessage: NBSP })} inputRef={this.refName} - disabled={!this.state.enabled} variant="filled" /> -
+
+
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) { diff --git a/src/BeastServer~.cpp.bak b/src/BeastServer~.cpp.bak deleted file mode 100644 index 97d2d18..0000000 --- a/src/BeastServer~.cpp.bak +++ /dev/null @@ -1,1248 +0,0 @@ -// -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/boostorg/beast -// - -//------------------------------------------------------------------------------ -// -// Example: Advanced server -// -//------------------------------------------------------------------------------ - -#include "pch.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "HtmlHelper.hpp" -#include "Lv2Log.hpp" - -#include "BeastServer.hpp" - -#include "Uri.hpp" - -using namespace pipedal; - -using tcp = boost::asio::ip::tcp; // from -namespace http = boost::beast::http; // from -namespace websocket = boost::beast::websocket; // from - -const size_t MAX_READ_SIZE = 1 * 1024 * 204; -using namespace boost; - -std::string -pipedal::last_modified(const std::filesystem::path &path) -{ - auto cPath = path.c_str(); - - struct stat fStat; - if (stat(cPath, &fStat) == 0) - { - return HtmlHelper::timeToHttpDate(fStat.st_mtim.tv_sec); - } - else - { - return HtmlHelper::timeToHttpDate(0); - } -} - -std::map extensionsToMimeType = { - {".htm", "text/html"}, - {".html", "text/html"}, - {".php", "text/html"}, - {".css", "text/css"}, - {".txt", "text/plain"}, - {".js", "application/javascript"}, - {".json", "application/json"}, - {".xml", "application/xml"}, - {".png", "image/png"}, - {".jpe", "image/jpeg"}, - {".jpeg", "image/jpeg"}, - {".jpg", "image/jpeg"}, - {".gif", "image/gif"}, - {".bmp", "image/bmp"}, - {".ico", "image/x-icon"}, - {".tiff", "image/tiff"}, - {".tif", "image/tiff"}, - {".svg", "image/svg+xml"}, - {".svgz", "image/svg+xml"}, - {".woff", "font/woff2" }, - {".woff2", "font/woff2"} -}; -// Return a reasonable mime type based on the extension of a file. -std::string -mime_type(const std::filesystem::path &path) -{ - auto const ext = path.extension(); - try - { - return extensionsToMimeType.at(ext); - } - catch (const std::exception &) - { - return "application/octet-stream"; - } -} - -// Append an HTTP rel-path to a local filesystem path. -// The returned path is normalized for the platform. -std::filesystem::path -path_cat( - boost::beast::string_view base, - const std::vector &segments) -{ - - std::filesystem::path result = base.to_string(); - - for (const std::string &segment : segments) - { - if (segment.find("..") != -1) - { - throw std::invalid_argument("Invalid segment."); - } - if (segment.find('/') != -1) - throw std::invalid_argument("Invalid segment."); - if (segment.find('\\') != -1) - throw std::invalid_argument("Invalid segment."); - if (segment.find('/') != -1) - throw std::invalid_argument("Invalid segment."); - if (segment.find(':') != -1) - throw std::invalid_argument("Invalid segment."); - - result /= segment; - } - return result; -} - -std::string GetFromAddress(const tcp::socket &socket) -{ - std::stringstream s; - s << socket.remote_endpoint().address().to_string() << ':' << socket.remote_endpoint().port(); - return s.str(); -} -// This function produces an HTTP response for the given -// request. The type of the response object depends on the -// contents of the request, so the interface requires the -// caller to pass a generic lambda for receiving the response. -template < - class Body, class Allocator, - class Send> -void handle_request( - const tcp::socket &socket_, - BeastServer *server, - http::request> &&req, - Send &&send) -{ - - - // Returns a bad request response - auto const bad_request = - [&req, &socket_](boost::beast::string_view why) - { - Lv2Log::error("http - (%s) Bad request: %s,", GetFromAddress(socket_).c_str(), why.to_string().c_str()); - http::response res{http::status::bad_request, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/plain"); - res.keep_alive(req.keep_alive()); - res.body() = why.to_string(); - res.prepare_payload(); - return res; - }; - - // Returns a not found response - auto const not_found = - [&req, &socket_](boost::beast::string_view target) - { - Lv2Log::error("http - (%s) Not found: %s,", GetFromAddress(socket_).c_str(), target.to_string().c_str()); - http::response res{http::status::not_found, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/plain"); - res.keep_alive(req.keep_alive()); - res.body() = "The resource '" + target.to_string() + "' was not found."; - res.prepare_payload(); - return res; - }; - - // Returns a server error response - auto const server_error = - [&req, &socket_](boost::beast::string_view what) - { - Lv2Log::error("http - (%s) Server error: %s,", GetFromAddress(socket_).c_str(), what.to_string().c_str()); - http::response res{http::status::not_found, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/plain"); - res.keep_alive(req.keep_alive()); - res.body() = what.to_string(); - res.prepare_payload(); - return res; - }; - - if (server->HasAccessPointGateway()) - { - auto &gateway = server->GetAccessPointGateway(); - auto remoteAddress = socket_.remote_endpoint().address(); - if (remoteAddress.is_v4()) - { - auto v4Address = remoteAddress.to_v4(); - unsigned int gwAddr = (gateway.netmask().to_uint() & gateway.address().to_uint()); - unsigned int ipAddr = (gateway.netmask().to_uint() & v4Address.to_uint()); - - if ((gateway.netmask().to_uint() & gateway.address().to_uint()) == (gateway.netmask().to_uint() & v4Address.to_uint())) - { - std::string host = req[http::field::host].to_string(); - std::string gatewayHost = server->GetAccessPointServerAddress(); - if (host != gatewayHost) - { - - uri requestUri; - requestUri.set(req.target().begin(), req.target().end()); - if (requestUri.segment_count() != 0) { - return send(not_found(req.target())); - } - uri_builder builder(requestUri); - builder.set_authority(gatewayHost); - - std::string redirectUri = builder.str(); - Lv2Log::info("Access point redirect from" + host + " to " + redirectUri); - - - // redirect to the gateway host. - http::response res{http::status::temporary_redirect, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::location, redirectUri); - res.prepare_payload(); - return send(std::move(res)); - - } - } - } - } - - if (req.method() == http::verb::options) - { - http::response res{http::status::no_content, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::access_control_allow_origin, req[http::field::origin]); - res.set(http::field::access_control_allow_methods, "POST, GET, OPTIONS, HEADERS"); - res.set(http::field::access_control_allow_headers, "Content-Type"); - res.keep_alive(req.keep_alive()); - return send(std::move(res)); - } - - uri request_uri; - try - { - request_uri.set(req.target().begin(), req.target().end()); - std::string host = req[http::field::host].to_string(); - int pos = host.find_last_of(':'); - if (pos != std::string::npos) - { - host = host.substr(0, pos); - } - uri_builder builder(request_uri); - builder.set_authority(host); - builder.set_port(socket_.local_endpoint().port()); - // stub: set htts scheme once we implement it. - builder.set_scheme("http"); - - std::string strUri = builder.str(); - request_uri = uri(strUri.c_str()); - } - catch (const std::exception &_) - { - return send((bad_request("Malformed target."))); - } - - for (auto requestHandler : server->RequestHandlers()) - { - - if (requestHandler->wants(req.method(), request_uri)) - { - - if (req.method() == http::verb::head) - { - Lv2Log::info("http - (%s) head %s", GetFromAddress(socket_), request_uri.str().c_str()); - - boost::beast::error_code ec; - http::response res{http::status::ok, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::date, HtmlHelper::timeToHttpDate(time(nullptr))); - res.set(http::field::access_control_allow_origin, req[http::field::origin]); - requestHandler->head_response(request_uri, req, res, ec); - res.keep_alive(req.keep_alive()); - // Handle the case where the file doesn't exist - - if (ec == boost::system::errc::no_such_file_or_directory) - return send(not_found(req.target())); - - if (ec) - { - return send(server_error(ec.message())); - } - req.prepare_payload(); - return send(std::move(res)); - } - else if (req.method() == http::verb::get) - { - if (server->LogHttpRequests()) - { - Lv2Log::info("http - (%s) get %s", GetFromAddress(socket_).c_str(), request_uri.str().c_str()); - } - - boost::beast::error_code ec; - http::response res{http::status::ok, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::date, HtmlHelper::timeToHttpDate(time(nullptr))); - res.set(http::field::access_control_allow_origin, req[http::field::origin]); - requestHandler->get_response(request_uri, req, res, ec); - res.keep_alive(req.keep_alive()); - if (ec == boost::system::errc::no_such_file_or_directory) - return send(not_found(req.target())); - - if (ec) - { - return send(server_error(ec.message())); - } - res.prepare_payload(); - return send(std::move(res)); - } - else if (req.method() == http::verb::post) - { - if (server->LogHttpRequests()) - { - Lv2Log::info("http - (%s) post %s", GetFromAddress(socket_).c_str(), request_uri.str().c_str()); - } - - boost::beast::error_code ec; - http::response res{http::status::ok, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::date, HtmlHelper::timeToHttpDate(time(nullptr))); - res.set(http::field::access_control_allow_origin, req[http::field::origin]); - requestHandler->post_response(request_uri, req, res, ec); - res.keep_alive(false); // bug in Beast Server -- posted body not reset. - if (ec == boost::system::errc::no_such_file_or_directory) - return send(not_found(req.target())); - - if (ec) - { - return send(server_error(ec.message())); - } - res.prepare_payload(); - return send(std::move(res)); - } - return send(bad_request("Unknown HTTP-method")); - } - } - // Make sure we can handle the method - if (req.method() != http::verb::get && - req.method() != http::verb::head) - return send(bad_request("Unknown HTTP-method")); - - // Request path must be absolute and not contain "..". - if (req.target().empty() || - req.target()[0] != '/' || - req.target().find("..") != boost::beast::string_view::npos) - return send(not_found(req.target())); // security: report not found to avoid disclosing info. - - // Build the path to the requested file - auto path = path_cat(server->GetDocRoot().c_str(), request_uri.segments()); - if (req.target().back() == '/' || req.target().size() == 0) - path /= "index.html"; - - // Attempt to open the file - boost::beast::error_code ec; - http::file_body::value_type body; - body.open(path.c_str(), boost::beast::file_mode::scan, ec); - - // Handle the case where the file doesn't exist - if (ec == boost::system::errc::no_such_file_or_directory) - return send(not_found(req.target())); - - // Handle an unknown error - if (ec) - return send(server_error(ec.message())); - - // Respond to HEAD request - if (req.method() == http::verb::head) - { - if (server->LogHttpRequests()) - { - Lv2Log::info("http - (%s) head %s", GetFromAddress(socket_).c_str(), request_uri.str().c_str()); - } - - http::response res{http::status::ok, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::date, HtmlHelper::timeToHttpDate(time(nullptr))); - res.set(http::field::content_type, mime_type(path)); - res.set(http::field::last_modified, last_modified(path)); - res.content_length(body.size()); - res.keep_alive(req.keep_alive()); - return send(std::move(res)); - } - else if (req.method() == http::verb::get) - { - if (server->LogHttpRequests()) - { - Lv2Log::info("http - (%s) get %s", GetFromAddress(socket_).c_str(), request_uri.str().c_str()); - } - - // Respond to GET request - http::response res{ - std::piecewise_construct, - std::make_tuple(std::move(body)), - std::make_tuple(http::status::ok, req.version())}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::date, HtmlHelper::timeToHttpDate(time(nullptr))); - res.set(http::field::content_type, mime_type(path)); - res.set(http::field::last_modified, last_modified(path)); - res.content_length(body.size()); - res.keep_alive(req.keep_alive()); - return send(std::move(res)); - } - else - { - return send(bad_request("Unknown HTTP-method")); - } -} - -//------------------------------------------------------------------------------ - -// Echoes back all received WebSocket messages -class websocket_session : public std::enable_shared_from_this, private SocketHandler::IWriteCallback -{ - websocket::stream ws_; - boost::asio::strand< - boost::asio::io_context::executor_type> - strand_; - boost::asio::steady_timer timer_; - boost::beast::multi_buffer read_buffer_; - boost::beast::multi_buffer write_buffer_; - - BeastServer *beastServer_; - std::shared_ptr socket_handler_; - - std::queue writeQueue; - - bool writePending = false; - ; - std::mutex writeQueueMutex; - std::string fromAddress; - -public: - ~websocket_session() - { - Lv2Log::info("http - (%s) Websocket closed.", fromAddress.c_str()); - } - virtual void close() - { - this->ws_.close(websocket::close_reason()); - } - virtual std::string getFromAddress() const { - return this->fromAddress; - } - virtual void writeCallback(const std::string &text) - { - { - std::lock_guard guard(writeQueueMutex); - if (writePending) - { - writeQueue.push(text); - return; - } - writePending = true; - } - do_write(text); - } - void do_write(const std::string &text) - { - size_t n = buffer_copy(write_buffer_.prepare(text.size()), boost::asio::buffer(text)); - write_buffer_.commit(n); - ws_.text(true); - - ws_.async_write( - write_buffer_.data(), - boost::asio::bind_executor( - strand_, - std::bind( - &websocket_session::on_write, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2))); - } - // Take ownership of the socket - explicit websocket_session( - const std::string &fromAddress, - BeastServer *beastServer, - std::shared_ptr &&socketHandler, - tcp::socket socket) - : beastServer_(beastServer), socket_handler_(socketHandler), - fromAddress(fromAddress), - ws_(std::move(socket)), - strand_(ws_.get_executor()), - read_buffer_(MAX_READ_SIZE), - timer_(ws_.get_executor().context(), - (std::chrono::steady_clock::time_point::max)()) - { - } - - // Start the asynchronous operation - template - void - run(http::request> req) - { - // Run the timer. The timer is operated - // continuously, this simplifies the code. - on_timer({}); - - // Set the timer - timer_.expires_after(std::chrono::seconds(15)); - // Accept the websocket handshake - - ws_.async_accept( - req, - boost::asio::bind_executor( - strand_, - std::bind( - &websocket_session::on_accept, - shared_from_this(), - std::placeholders::_1))); - } - - // Called when the timer expires. - void - on_timer(boost::system::error_code ec) - { - if (ec && ec != boost::asio::error::operation_aborted) - this->beastServer_->onError(ec, "timer"); - return; - - // Verify that the timer really expired since the deadline may have moved. - if (timer_.expiry() <= std::chrono::steady_clock::now()) - { - // Closing the socket cancels all outstanding operations. They - // will complete with boost::asio::error::operation_aborted - ws_.next_layer().shutdown(tcp::socket::shutdown_both, ec); - ws_.next_layer().close(ec); - return; - } - - // Wait on the timer - timer_.async_wait( - boost::asio::bind_executor( - strand_, - std::bind( - &websocket_session::on_timer, - shared_from_this(), - std::placeholders::_1))); - } - - void - on_accept(boost::system::error_code ec) - { - // Happens when the timer closes the socket - if (ec == boost::asio::error::operation_aborted) - return; - - if (ec) - { - beastServer_->onError(ec, "accept"); - return; - } - if (socket_handler_) - { - auto t = socket_handler_.get(); - t->setWriteCallback(this); - socket_handler_->onAttach(); - } - - // Read a message - do_read(); - } - - void - do_read() - { - // Set the timer - timer_.expires_after(std::chrono::seconds(15)); - - // Read a message into our buffer - ws_.async_read( - read_buffer_, - boost::asio::bind_executor( - strand_, - std::bind( - &websocket_session::on_read, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2))); - } - - void - on_read( - boost::system::error_code ec, - std::size_t bytes_transferred) - { - - // Happens when the timer closes the socket - if (ec == boost::asio::error::operation_aborted) - return; - - // This indicates that the websocket_session was closed - if (ec == websocket::error::closed) - return; - - if (ec) - { - beastServer_->onError(ec, "read"); - return; - } - - // Echo the message - if (ws_.got_text()) - { - std::string data(beast::buffers_to_string(read_buffer_.data())); - this->socket_handler_->onReceive(data); - read_buffer_.consume(bytes_transferred); - } - else - { - read_buffer_.consume(bytes_transferred); // ignore binary messages - } - - do_read(); - // ws_.text(ws_.got_text()); - // ws_.async_write( - // buffer_.data(), - // boost::asio::bind_executor( - // strand_, - // std::bind( - // &websocket_session::on_write, - // shared_from_this(), - // std::placeholders::_1, - // std::placeholders::_2))); - } - - void fail(boost::system::error_code ec, const char *what) - { - beastServer_->onError(ec, what); - } - void - on_write( - boost::system::error_code ec, - std::size_t bytes_transferred) - { - boost::ignore_unused(bytes_transferred); - - // Happens when the timer closes the socket - if (ec == boost::asio::error::operation_aborted) - return; - - if (ec) - { - fail(ec, "write"); - } - - // Clear the buffer - std::string nextWrite; - write_buffer_.consume(write_buffer_.size()); - - { - std::lock_guard guard(this->writeQueueMutex); - if (writeQueue.size() != 0) - { - nextWrite = writeQueue.front(); - writeQueue.pop(); - } - else - { - writePending = false; - return; - } - } - do_write(nextWrite); - } -}; - -// Handles an HTTP server connection -class http_session : public std::enable_shared_from_this -{ - // This queue is used for HTTP pipelining. - class queue - { - enum - { - // Maximum number of responses we will queue - limit = 8 - }; - - // The type-erased, saved work item - struct work - { - virtual ~work() = default; - virtual void operator()() = 0; - }; - - http_session &self_; - std::vector> items_; - - public: - explicit queue(http_session &self) - : self_(self) - { - static_assert(limit > 0, "queue limit must be positive"); - items_.reserve(limit); - } - - // Returns `true` if we have reached the queue limit - bool - is_full() const - { - return items_.size() >= limit; - } - - // Called when a message finishes sending - // Returns `true` if the caller should initiate a read - bool - on_write() - { - BOOST_ASSERT(!items_.empty()); - auto const was_full = is_full(); - items_.erase(items_.begin()); - if (!items_.empty()) - (*items_.front())(); - return was_full; - } - - // Called by the HTTP handler to send a response. - template - void - operator()(http::message &&msg) - { - // This holds a work item - struct work_impl : work - { - http_session &self_; - http::message msg_; - - work_impl( - http_session &self, - http::message &&msg) - : self_(self), msg_(std::move(msg)) - { - } - - void - operator()() - { - http::async_write( - self_.socket_, - msg_, - boost::asio::bind_executor( - self_.strand_, - std::bind( - &http_session::on_write, - self_.shared_from_this(), - std::placeholders::_1, - msg_.need_eof()))); - } - }; - - // Allocate and store the work - items_.emplace_back(new work_impl(self_, std::move(msg))); - - // If there was no previous work, start this one - if (items_.size() == 1) - (*items_.front())(); - } - }; - - tcp::socket socket_; - boost::asio::strand< - boost::asio::io_context::executor_type> - strand_; - boost::asio::steady_timer timer_; - boost::beast::flat_buffer buffer_; - http::request req_; - queue queue_; - BeastServer *beastServer_; - -public: - // Take ownership of the socket - explicit http_session( - BeastServer *beastServer, - tcp::socket socket) - : beastServer_(beastServer), socket_(std::move(socket)), strand_(socket_.get_executor()), timer_(socket_.get_executor().context(), - (std::chrono::steady_clock::time_point::max)()), - queue_(*this) - { - } - - // Start the asynchronous operation - void - run() - { - // Run the timer. The timer is operated - // continuously, this simplifies the code. - on_timer({}); - - do_read(); - } - void - do_read() - { - // make the request empty before reading - // otherwise the operation behavour is undefined. - req_ = {}; - - // Set the time - timer_.expires_after(std::chrono::seconds(15)); - - // Read a request - http::async_read(socket_, buffer_, req_, - boost::asio::bind_executor( - strand_, - std::bind( - &http_session::on_read, - shared_from_this(), - std::placeholders::_1))); - } - - // Called when the timer expires. - void - on_timer(boost::system::error_code ec) - { - if (ec && ec != boost::asio::error::operation_aborted) - return fail(ec, "timer"); - - // Verify that the timer really expired since the deadline may have moved. - if (timer_.expiry() <= std::chrono::steady_clock::now()) - { - // Closing the socket cancels all outstanding operations. They - // will complete with boost::asio::error::operation_aborted - if (!beastServer_->Stopping()) - { - socket_.shutdown(tcp::socket::shutdown_both, ec); - } - socket_.close(ec); - return; - } - - // Wait on the timer - timer_.async_wait( - boost::asio::bind_executor( - strand_, - std::bind( - &http_session::on_timer, - shared_from_this(), - std::placeholders::_1))); - } - - void fail(boost::system::error_code ec, const char *what) - { - beastServer_->onError(ec, what); - } - - void - on_read(boost::system::error_code ec) - { - // Happens when the timer closes the socket - if (ec == boost::asio::error::operation_aborted) - return; - - // This means they closed the connection - if (ec == http::error::end_of_stream) - return do_close(); - - if (ec) - { - fail(ec, "read"); - return; - } - // See if it is a WebSocket Upgrade - if (websocket::is_upgrade(req_)) - { - uri request_uri; - try - { - request_uri.set(req_.target().begin(), req_.target().end()); - } - catch (const std::exception &_) - { - fail(ec, "socket uri"); - return; - } - - std::shared_ptr socketFactory = beastServer_->GetSocketFactory(request_uri); - if (!socketFactory) - { - fail(http::error::bad_target, "Not found"); - return; - } - - Lv2Log::info("http - (%s) Websocket opened: %s", GetFromAddress(socket_).c_str(), request_uri.str().c_str()); - // Create a WebSocket websocket_session by transferring the socket - std::string fromName = GetFromAddress(socket_); - std::make_shared( - fromName, - this->beastServer_, - socketFactory ? socketFactory->CreateHandler(request_uri) : nullptr, - std::move(socket_)) - ->run(std::move(req_)); - return; - } - // Send the response - handle_request( - this->socket_, - beastServer_, - std::move(req_), - queue_); - - // If we aren't at the queue limit, try to pipeline another request - if (!queue_.is_full()) - do_read(); - } - - void - on_write(boost::system::error_code ec, bool close) - { - // Happens when the timer closes the socket - if (ec == boost::asio::error::operation_aborted) - return; - - if (ec) - return fail(ec, "write"); - - if (close) - { - // This means we should close the connection, usually because - // the response indicated the "Connection: close" semantic. - return do_close(); - } - - // Inform the queue that a write completed - if (queue_.on_write()) - { - // Read another request - do_read(); - } - } - - void - do_close() - { - // Send a TCP shutdown - boost::system::error_code ec; - if (!beastServer_->Stopping()) - { - socket_.shutdown(tcp::socket::shutdown_send, ec); - } - socket_.close(); - - - } -}; - -//------------------------------------------------------------------------------ - -// Accepts incoming connections and launches the sessions -class listener : public std::enable_shared_from_this -{ - tcp::acceptor acceptor_; - tcp::socket socket_; - BeastServer *beastServer_; - -public: - ~listener() - { - beastServer_->onListenerClosed(); - } - listener( - BeastServer *server, - boost::asio::io_context &ioc, - tcp::endpoint endpoint) - : beastServer_(server), acceptor_(ioc), socket_(ioc) - { - boost::system::error_code ec; - - // Open the acceptor - acceptor_.open(endpoint.protocol(), ec); - if (ec) - { - fail(ec, "open"); - return; - } - - boost::asio::socket_base::reuse_address option(true); - acceptor_.set_option(option); - // Lv2Log::warning("http - Accepting connections with reuse_adddress to allow faster debug turns. Should NOT be enabled in production use. "); - - // Leftover client sockets in TIME_WAIT state prevent binding of the server address. - // Retry for up to 140 seconds in order to allow the TIME_WAIT sockets to expire. - // If we still can't establish a connection, we can be sure that somebody else or an existing instance is servicing the address, so - // quit after that. - int retries = 0; - int retryTime = 1; - int totalRetryTime = 0; - int MAX_RETRY_TIME = 240; // socket-timeout-ish. - - while (true) - { - // Bind to the server address - acceptor_.bind(endpoint, ec); - if (!ec) - { - break; - } - else - { - ++retries; - if (totalRetryTime > MAX_RETRY_TIME) - { - fail(ec, "bind"); - return; - } - Lv2Log::info("http - bind: %s. (port %d) Retrying...", ec.message().c_str(), endpoint.port()); - sleep(retryTime); - retryTime *= 2; - if (retryTime > 10) - retryTime = 10; - totalRetryTime += retryTime; - } - } - - Lv2Log::info("http - Server listening on %s:%d", endpoint.address().to_string().c_str(), (int)endpoint.port()); - - // Start listening for connections - acceptor_.listen( - boost::asio::socket_base::max_listen_connections, ec); - if (ec) - { - fail(ec, "listen"); - return; - } - } - - void - stop() - { - acceptor_.cancel(); - } - - // Start accepting incoming connections - void - run() - { - if (!acceptor_.is_open()) - return; - do_accept(); - } - - void - do_accept() - { - acceptor_.async_accept( - socket_, - std::bind( - &listener::on_accept, - shared_from_this(), - std::placeholders::_1)); - } - - void fail(boost::system::error_code ec, const char *what) - { - beastServer_->onError(ec, what); - } - void - on_accept(boost::system::error_code ec) - { - if (ec) - { - fail(ec, "accept"); - return; - } - else - { - // Create the http_session and run it - std::make_shared( - beastServer_, - std::move(socket_)) - ->run(); - } - - // Accept another connection - do_accept(); - } -}; - -//------------------------------------------------------------------------------ - -BeastServer::BeastServer(const boost::asio::ip::address &address, int port, const char *rootPath, int threads) - : address(address), - port((unsigned short)port), - doc_root(rootPath), - threads(threads), - pBgThread(nullptr) -{ -} - -void BeastServer::Stop() -{ - if (stopping) return; - stopping = true; - listener *p = nullptr; - { - std::unique_lock lk(io_mutex); - p = (listener*)this->pListener; - this->pListener = nullptr; - } - if (p != nullptr) - { - p->stop(); - } - boost::asio::io_context *pIoContext = nullptr; - { - std::unique_lock lk(io_mutex); - pIoContext = this->pIoContext; - this->pIoContext = nullptr; - } - if (pIoContext != nullptr) - { - pIoContext->stop(); - }; -} - -void BeastServer::onListenerClosed() -{ - std::unique_lock lk(io_mutex); - this->pListener = nullptr; -} - -static void ThreadProc(BeastServer *server) -{ - server->Run(); -} - -void BeastServer::RunInBackground() -{ - if (this->pBgThread != nullptr) - { - throw std::runtime_error("Bad state."); - } - this->pBgThread = new std::thread(ThreadProc, this); -} - -void BeastServer::Join() -{ - if (this->pBgThread != nullptr) - { - Stop(); - this->pBgThread->join(); - this->pBgThread = nullptr; - } -} - -void BeastServer::Run() -{ - // The io_context is required for all I/O - boost::asio::io_context ioc{threads}; - - { - // save a reference for use by stop() - std::unique_lock lk(io_mutex); - pIoContext = &ioc; - } - - // Create and launch a listening port - std::shared_ptr listenerInstance = std::make_shared( - this, - ioc, - tcp::endpoint{address, port}); - listenerInstance->run(); - - // save a reference for use by stop() - { - std::unique_lock lk(io_mutex); - this->pListener = (void *)&(*listenerInstance); - } - - // Run the I/O service on the requested number of threads - std::vector v; - v.reserve(threads - 1); - for (auto i = threads - 1; i > 0; --i) - v.emplace_back( - [&ioc] - { - ioc.run(); - }); - ioc.run(); - - ioc.stop(); - for (auto &thread : v) - { - thread.join(); - } - { - std::unique_lock lk(io_mutex); - this->pListener = nullptr; - this->pIoContext = nullptr; - } -} - -BeastServer::~BeastServer() -{ - // Shutdown if running in background. - Stop(); - Join(); -} - -void BeastServer::onWarning(boost::system::error_code ec, char const *what) -{ - std::stringstream s; - s << "http - " << what << ": " << ec.message() << " (" << ec.value() << ')'; - Lv2Log::warning(s.str().c_str()); -} -void BeastServer::onInfo(char const *message) -{ - Lv2Log::info("%s", message); -} - -void BeastServer::onError(boost::system::error_code ec, char const *what) -{ - std::stringstream s; - s << "http - " << what << ": " << ec.message() << " (" << ec.value() << ')'; - Lv2Log::warning(s.str().c_str()); -} - -std::shared_ptr BeastServer::GetSocketFactory(const uri &request_uri) -{ - for (auto factory : this->socket_factories) - { - if (factory->wants(request_uri)) - { - return factory; - } - } - return nullptr; -} diff --git a/src/BeastServer~.hpp.bak b/src/BeastServer~.hpp.bak deleted file mode 100644 index 74c12f2..0000000 --- a/src/BeastServer~.hpp.bak +++ /dev/null @@ -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 -#include -#include - - -#include -#include -#include "Uri.hpp" -#include -#include - - -// forward declaration. - -class websocket_session; - -namespace pipedal { -using HttpRequest =boost::beast::http::request; - - -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 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 &req, - boost::beast::http::response &res, - boost::beast::error_code &ec) = 0; - - virtual void get_response( - const uri&request_uri, - const boost::beast::http::request &req, - boost::beast::http::response &res, - boost::beast::error_code &ec) = 0; - - virtual void post_response( - const uri&request_uri, - const boost::beast::http::request &req, - boost::beast::http::response &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 > request_handlers; - std::vector > 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) - { - request_handlers.push_back(requestHandler); - } - - void AddSocketFactory(const std::shared_ptr &socketHandler) - { - socket_factories.push_back(socketHandler); - } - - std::vector > &RequestHandlers() { return request_handlers; } - - const std::string&GetDocRoot() { return doc_root; } - - virtual ~BeastServer(); - - std::shared_ptr 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; \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7a083b5..75725f0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/ConfigMain.cpp b/src/ConfigMain.cpp index e819b47..854dcb1 100644 --- a/src/ConfigMain.cpp +++ b/src/ConfigMain.cpp @@ -33,7 +33,7 @@ #include "JackServerSettings.hpp" #include "P2pConfigFiles.hpp" #include "PrettyPrinter.hpp" -#include "DeviceIdFile.hpp" +#include "ServiceConfiguration.hpp" #include #include #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"; } diff --git a/src/ConfigSerializer.cpp b/src/ConfigSerializer.cpp new file mode 100644 index 0000000..d5bcfdf --- /dev/null +++ b/src/ConfigSerializer.cpp @@ -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 config_serializer::detail::split(const std::string &value, char delimiter) +{ + size_t start = 0; + std::vector 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(); +} + diff --git a/src/ConfigSerializer.h b/src/ConfigSerializer.h new file mode 100644 index 0000000..0cd5dfa --- /dev/null +++ b/src/ConfigSerializer.h @@ -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 +#include +#include +#include "autoptr_vector.h" +#include +#include + +namespace config_serializer +{ + namespace detail + { + std::string trim(const std::string &v); + + std::vector 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 + 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 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 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::FromString(const std::string &value) + { + return DecodeString(value); + } + template <> + inline std::string ConfigConverter::ToString(const std::string &config) + { + return EncodeString(config); + } + // bool specializations. + template <> + inline bool ConfigConverter::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(value); + result = (t != 0); + } + catch (const std::exception &) + { + throw std::invalid_argument("Expecting true or false."); + } + } + return result; + } + template <> + inline std::string ConfigConverter::ToString(const bool &value) + { + return value ? "true" : "false"; + } + + template + class ConfigSerializer : public ConfigSerializerBase + { + public: + using Obj = OBJ; + using pointer_type = T Obj::*; + using base = ConfigSerializerBase; + using converter = ConfigConverter; + + 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::FromString(value); + } + virtual const std::string GetValue(Obj &configurable) const + { + return ConfigConverter::ToString(configurable.*pMember); + } + + private: + pointer_type pMember; + }; + + template + class ConfigSerializable + { + private: + using Obj = OBJ; + + using serializer_t = ConfigSerializerBase; + using serializers_t = p2p::autoptr_vector; + + 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 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 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()); + } + } + }; +} diff --git a/src/JackHost.cpp b/src/JackHost.cpp index b338a33..2e7254a 100644 --- a/src/JackHost.cpp +++ b/src/JackHost.cpp @@ -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; diff --git a/src/Lv2Host.cpp b/src/Lv2Host.cpp index d53f3da..4ac7683 100644 --- a/src/Lv2Host.cpp +++ b/src/Lv2Host.cpp @@ -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::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::jmap{{ @@ -1229,6 +1233,7 @@ json_map::storage_type Lv2PluginUiControlPort::jmap{{ MAP_REF(Lv2PluginUiControlPort, port_group), json_map::enum_reference("units", &Lv2PluginUiControlPort::units_, get_units_enum_converter()), + MAP_REF(Lv2PluginUiControlPort, comment), }}; diff --git a/src/Lv2Host.hpp b/src/Lv2Host.hpp index d93e1a6..5d8197c 100644 --- a/src/Lv2Host.hpp +++ b/src/Lv2Host.hpp @@ -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 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 jmap; diff --git a/src/PiPedalModel.cpp b/src/PiPedalModel.cpp index 83ec572..60a14fc 100644 --- a/src/PiPedalModel.cpp +++ b/src/PiPedalModel.cpp @@ -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 #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 -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{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 guard{mutex}; { @@ -473,7 +475,6 @@ void PiPedalModel::FirePluginPresetsChanged(const std::string & pluginUri) } } - void PiPedalModel::SaveCurrentPreset(int64_t clientId) { std::lock_guard 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 values; - for (const auto & controlValue: item->controlValues()) + std::map 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 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 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 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 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 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{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 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 guard(mutex); @@ -1095,7 +1157,7 @@ void PiPedalModel::GetLv2Parameter( BankIndex PiPedalModel::GetBankIndex() const { - std::lock_guard guard(const_cast(mutex)); + std::lock_guard guard(const_cast(mutex)); return storage.GetBanks(); } @@ -1137,16 +1199,17 @@ JackServerSettings PiPedalModel::GetJackServerSettings() void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSettings) { - std::lock_guard guard(mutex); + std::unique_lock 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 guard(mutex); return storage.GetPluginPresets(pluginUri); - } -PluginUiPresets PiPedalModel::GetPluginUiPresets(const std::string& pluginUri) +PluginUiPresets PiPedalModel::GetPluginUiPresets(const std::string &pluginUri) { std::lock_guard 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 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 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 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 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 PiPedalModel::GetAlsaDevices() return this->alsaDevices.GetAlsaDevices(); } -const std::filesystem::path& PiPedalModel::GetWebRoot() const +const std::filesystem::path &PiPedalModel::GetWebRoot() const { return webRoot; } -std::map PiPedalModel::GetFavorites() const +std::map PiPedalModel::GetFavorites() const { - std::lock_guard guard(const_cast(mutex)); - + std::lock_guard guard(const_cast(mutex)); + return storage.GetFavorites(); } -void PiPedalModel::SetFavorites(const std::map &favorites) +void PiPedalModel::SetFavorites(const std::map &favorites) { std::lock_guard 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 t; + std::vector t; t.reserve(this->subscribers.size()); for (size_t i = 0; i < subscribers.size(); ++i) diff --git a/src/PiPedalModel.hpp b/src/PiPedalModel.hpp index 5a93006..dd2a7cc 100644 --- a/src/PiPedalModel.hpp +++ b/src/PiPedalModel.hpp @@ -142,6 +142,8 @@ private: void UpdateRealtimeMonitorPortSubscriptions(); void OnVuUpdate(const std::vector& updates); + void RestartAudio(); + std::vector outstandingParameterRequests; IPiPedalModelSubscriber*GetNotificationSubscriber(int64_t clientId); diff --git a/src/PiPedalSocket.cpp b/src/PiPedalSocket.cpp index 91b0095..bc4bf46 100644 --- a/src/PiPedalSocket.cpp +++ b/src/PiPedalSocket.cpp @@ -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"); diff --git a/src/DeviceIdFile.cpp b/src/ServiceConfiguration.cpp similarity index 62% rename from src/DeviceIdFile.cpp rename to src/ServiceConfiguration.cpp index 027f0cc..ae8abb7 100644 --- a/src/DeviceIdFile.cpp +++ b/src/ServiceConfiguration.cpp @@ -22,7 +22,7 @@ * SOFTWARE. */ -#include "DeviceIdFile.hpp" +#include "ServiceConfiguration.hpp" #include #include #include @@ -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(#MEMBER_NAME, &ServiceConfiguration::MEMBER_NAME, "") + + +static p2p::autoptr_vector> +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); } } \ No newline at end of file diff --git a/src/DeviceIdFile.hpp b/src/ServiceConfiguration.hpp similarity index 80% rename from src/DeviceIdFile.hpp rename to src/ServiceConfiguration.hpp index 073b961..eb70fbb 100644 --- a/src/DeviceIdFile.hpp +++ b/src/ServiceConfiguration.hpp @@ -25,10 +25,17 @@ #pragma once #include +#include "ConfigSerializer.h" namespace pipedal { - class DeviceIdFile { + using namespace config_serializer; + + class ServiceConfiguration: protected ConfigSerializable { public: + using base = ConfigSerializable; + + 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; }; } \ No newline at end of file diff --git a/src/SetWifiConfig.cpp b/src/SetWifiConfig.cpp index 0a7902f..409fd03 100644 --- a/src/SetWifiConfig.cpp +++ b/src/SetWifiConfig.cpp @@ -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"); diff --git a/src/WifiDirectConfigSettings.cpp b/src/WifiDirectConfigSettings.cpp index 97273b8..b24d590 100644 --- a/src/WifiDirectConfigSettings.cpp +++ b/src/WifiDirectConfigSettings.cpp @@ -25,7 +25,7 @@ #include #include #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 == "") { diff --git a/src/autoptr_vector.h b/src/autoptr_vector.h new file mode 100644 index 0000000..fff6224 --- /dev/null +++ b/src/autoptr_vector.h @@ -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 + + + +namespace p2p { + + template class autoptr_vector + { + std::vector v; + private: + // no copy no move. + autoptr_vector(const autoptr_vector &) = delete; + autoptr_vector(autoptr_vector && ) = delete; + public: + using iterator = std::vector::const_iterator; + + autoptr_vector() { } + autoptr_vector(std::initializer_list 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(); } + }; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 8550920..8805600 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,7 +23,7 @@ #include "BeastServer.hpp" #include #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 diff --git a/src/templates/pipedal_p2pd.service.template b/src/templates/pipedal_p2pd.service.template index 9de872c..67e31e1 100644 --- a/src/templates/pipedal_p2pd.service.template +++ b/src/templates/pipedal_p2pd.service.template @@ -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 diff --git a/src/templates/pipedald.service.template b/src/templates/pipedald.service.template index 6d4af18..3fe571a 100644 --- a/src/templates/pipedald.service.template +++ b/src/templates/pipedald.service.template @@ -7,6 +7,7 @@ After=local-fs.target [Service] +Restart=on-failure Type=notify LimitMEMLOCK=infinity LimitRTPRIO=95 diff --git a/submodules/pipedal_p2pd b/submodules/pipedal_p2pd index 85b68b8..81111f1 160000 --- a/submodules/pipedal_p2pd +++ b/submodules/pipedal_p2pd @@ -1 +1 @@ -Subproject commit 85b68b8400608284062cbbba77cbcc377120ccbf +Subproject commit 81111f111fba3249f9a3853877bcd098e3c98250