- MIDI Program/Bank select.
- MIDI next/previous program.
This commit is contained in:
Robin Davies
2023-01-13 20:59:43 -05:00
parent ee3e264df5
commit ff8ed6b733
31 changed files with 1647 additions and 580 deletions
+2 -2
View File
@@ -1,10 +1,10 @@
cmake_minimum_required(VERSION 3.16.0) cmake_minimum_required(VERSION 3.16.0)
project(pipedal project(pipedal
VERSION 1.0.17 VERSION 1.0.18
DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi" DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi"
HOMEPAGE_URL "https://rerdavies.github.io/pipedal" HOMEPAGE_URL "https://rerdavies.github.io/pipedal"
) )
set (DISPLAY_VERSION "v1.0.17") set (DISPLAY_VERSION "v1.0.18")
set (CMAKE_INSTALL_PREFIX "/usr/") set (CMAKE_INSTALL_PREFIX "/usr/")
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright 2021-2022 Robin Davies Copyright 2021-2023 Robin Davies
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"), a copy of this software and associated documentation files (the "Software"),
+1 -1
View File
@@ -1,7 +1,7 @@
<img src='docs/GithubBanner.png' width="100%" /><br/> <img src='docs/GithubBanner.png' width="100%" /><br/>
Download: <a href='https://rerdavies.github.io/pipedal/download.html'>v1.0.17</a> Download: <a href='https://rerdavies.github.io/pipedal/download.html'>v1.0.18</a>
Website: [https://rerdavies.github.io/pipedal](https://rerdavies.github.io/pipedal). Website: [https://rerdavies.github.io/pipedal](https://rerdavies.github.io/pipedal).
+1 -1
View File
@@ -9,6 +9,6 @@ Package: pipedal
Pre-Depends: hostapd;authbind Pre-Depends: hostapd;authbind
Priority: optional Priority: optional
Section: sound Section: sound
Version: 1.0.17 Version: 1.0.18
Installed-Size: 15147 Installed-Size: 15147
+2 -2
View File
@@ -13,14 +13,14 @@ page_icon: img/Install4.jpg
Download the most recent Debian (.deb) package for your platform: Download the most recent Debian (.deb) package for your platform:
- [Ubuntu/Raspberry Pi OS (64-bit) v1.0.17](https://github.com/rerdavies/pipedal/releases/download/v1.0.17/pipedal_1.0.17_arm64.deb) - [Ubuntu/Raspberry Pi OS (64-bit) v1.0.18](https://github.com/rerdavies/pipedal/releases/download/v1.0.18/pipedal_1.0.18_arm64.deb)
Install the package by running Install the package by running
``` ```
sudo apt update sudo apt update
cd ~/Downloads cd ~/Downloads
sudo apt-get install ./pipedal_1.0.17_arm64.deb sudo apt-get install ./pipedal_1.0.18_arm64.deb
``` ```
On Raspberry Pi OS, if you have a graphical desktop installed, you can also install the package by double-clicking on the downloaded package in the File Manager. On Raspberry Pi OS, if you have a graphical desktop installed, you can also install the package by double-clicking on the downloaded package in the File Manager.
+2 -2
View File
@@ -4,14 +4,14 @@
Download the most recent Debian (.deb) package for your platform: Download the most recent Debian (.deb) package for your platform:
- [Ubuntu or Raspberry Pi OS (64-bit)](https://github.com/rerdavies/pipedal/releases/download/v1.0.17/pipedal_1.0.17_arm64.deb) v1.0.17 - [Ubuntu or Raspberry Pi OS (64-bit)](https://github.com/rerdavies/pipedal/releases/download/v1.0.18/pipedal_1.0.18_arm64.deb) v1.0.18
Install the package by running Install the package by running
``` ```
sudo apt update sudo apt update
cd ~/Downloads cd ~/Downloads
sudo apt-get install ./pipedal_1.0.17_arm64.deb sudo apt-get install ./pipedal_1.0.18_arm64.deb
``` ```
Follow the instructions in [_Configuring PiPedal After Installation_](https://rerdavies.github.io/pipedal/Configuring.html) to complete the installation. Follow the instructions in [_Configuring PiPedal After Installation_](https://rerdavies.github.io/pipedal/Configuring.html) to complete the installation.
+1 -1
View File
@@ -1,7 +1,7 @@
<img src="GithubBanner.png" width="100%"/> <img src="GithubBanner.png" width="100%"/>
<a href="Installing.html"><i>v1.0.17</i></a> <a href="Installing.html"><i>v1.0.18</i></a>
&nbsp; &nbsp;
+5
View File
@@ -31,6 +31,11 @@ export default class MidiBinding {
this.switchControlType = input.switchControlType; this.switchControlType = input.switchControlType;
return this; return this;
} }
static systemBinding(symbol: string): MidiBinding {
let result = new MidiBinding();
result.symbol = symbol;
return result;
}
static deserialize_array(input: any): MidiBinding[] { static deserialize_array(input: any): MidiBinding[] {
let result: MidiBinding[] = []; let result: MidiBinding[] = [];
for (let i = 0; i < input.length; ++i) for (let i = 0; i < input.length; ++i)
+1 -1
View File
@@ -257,7 +257,7 @@ const MidiBindingView =
onChange={(e, extra) => this.handleLatchControlTypeChange(e, extra)} onChange={(e, extra) => this.handleLatchControlTypeChange(e, extra)}
value={midiBinding.switchControlType} value={midiBinding.switchControlType}
> >
<MenuItem value={MidiBinding.LATCH_CONTROL_TYPE}>Latch</MenuItem> <MenuItem value={MidiBinding.LATCH_CONTROL_TYPE}>Toggle</MenuItem>
<MenuItem value={MidiBinding.MOMENTARY_CONTROL_TYPE}>Momentary</MenuItem> <MenuItem value={MidiBinding.MOMENTARY_CONTROL_TYPE}>Momentary</MenuItem>
</Select> </Select>
</div> </div>
+63 -33
View File
@@ -339,6 +339,7 @@ export interface PiPedalModel {
favorites: ObservableProperty<FavoritesList>; favorites: ObservableProperty<FavoritesList>;
presets: ObservableProperty<PresetIndex>; presets: ObservableProperty<PresetIndex>;
systemMidiBindings: ObservableProperty<MidiBinding[]>;
zoomedUiControl: ObservableProperty<ZoomedControlInfo | undefined>; zoomedUiControl: ObservableProperty<ZoomedControlInfo | undefined>;
@@ -409,6 +410,8 @@ export interface PiPedalModel {
updatePluginPresets(uri: string, presets: PluginUiPresets): Promise<void>; updatePluginPresets(uri: string, presets: PluginUiPresets): Promise<void>;
duplicatePluginPreset(uri: string, instanceId: number): Promise<number>; duplicatePluginPreset(uri: string, instanceId: number): Promise<number>;
setSystemMidiBinding(instanceId: number, midiBinding: MidiBinding): void;
shutdown(): Promise<void>; shutdown(): Promise<void>;
restart(): Promise<void>; restart(): Promise<void>;
@@ -441,8 +444,8 @@ export interface PiPedalModel {
zoomUiControl(sourceElement: HTMLElement, instanceId: number, uiControl: UiControl): void; zoomUiControl(sourceElement: HTMLElement, instanceId: number, uiControl: UiControl): void;
onPreviousZoomedControl() : void; onPreviousZoomedControl(): void;
onNextZoomedControl() : void; onNextZoomedControl(): void;
clearZoomedControl(): void; clearZoomedControl(): void;
setFavorite(pluginUrl: string, isFavorite: boolean): void; setFavorite(pluginUrl: string, isFavorite: boolean): void;
@@ -495,7 +498,13 @@ class PiPedalModelImpl implements PiPedalModel {
( (
new PresetIndex() new PresetIndex()
); );
systemMidiBindings: ObservableProperty<MidiBinding[]> = new ObservableProperty<MidiBinding[]>
(
[
MidiBinding.systemBinding("prevProgram"),
MidiBinding.systemBinding("nextProgram")
]
);
zoomedUiControl: ObservableProperty<ZoomedControlInfo | undefined> = new ObservableProperty<ZoomedControlInfo | undefined>(undefined); zoomedUiControl: ObservableProperty<ZoomedControlInfo | undefined> = new ObservableProperty<ZoomedControlInfo | undefined>(undefined);
svgImgUrl(svgImage: string): string { svgImgUrl(svgImage: string): string {
@@ -682,7 +691,10 @@ class PiPedalModelImpl implements PiPedalModel {
if (header.replyTo) { if (header.replyTo) {
this.webSocket?.reply(header.replyTo, "onVuUpdate", true); this.webSocket?.reply(header.replyTo, "onVuUpdate", true);
} }
} else if (message === "onSystemMidiBindingsChanged")
{
let bindings = MidiBinding.deserialize_array(body);
this.systemMidiBindings.set(bindings);
} else { } else {
throw new PiPedalStateError("Unrecognized message received from server: " + message); throw new PiPedalStateError("Unrecognized message received from server: " + message);
} }
@@ -815,6 +827,12 @@ class PiPedalModelImpl implements PiPedalModel {
.then((data) => { .then((data) => {
this.favorites.set(data); this.favorites.set(data);
return this.getWebSocket().request<MidiBinding[]>("getSystemMidiBindings");
})
.then((data)=> {
let bindings = MidiBinding.deserialize_array(data);
this.systemMidiBindings.set(bindings);
this.setState(State.Ready); this.setState(State.Ready);
}) })
.catch((what) => { .catch((what) => {
@@ -944,7 +962,7 @@ class PiPedalModelImpl implements PiPedalModel {
}) })
.then(data => { .then(data => {
this.showStatusMonitor.set(data); this.showStatusMonitor.set(data);
return this.getWebSocket().request<any>("getJackServerSettings"); return this.getWebSocket().request<any>("getJackServerSettings");
}) })
.then(data => { .then(data => {
@@ -977,6 +995,12 @@ class PiPedalModelImpl implements PiPedalModel {
.then((data) => { .then((data) => {
this.favorites.set(data); this.favorites.set(data);
return this.getWebSocket().request<MidiBinding[]>("getSystemMidiBindings");
})
.then((data)=> {
let bindings = MidiBinding.deserialize_array(data);
this.systemMidiBindings.set(bindings);
if (this.webSocket) { if (this.webSocket) {
// MUST not allow reconnect until at least one complete load has finished. // MUST not allow reconnect until at least one complete load has finished.
this.webSocket.canReconnect = true; this.webSocket.canReconnect = true;
@@ -1215,7 +1239,7 @@ class PiPedalModelImpl implements PiPedalModel {
} }
} }
_setVst3PedalBoardControlValue(instanceId: number, key: string, value: number, state: string, notifyServer: boolean ): void { _setVst3PedalBoardControlValue(instanceId: number, key: string, value: number, state: string, notifyServer: boolean): void {
let pedalBoard = this.pedalBoard.get(); let pedalBoard = this.pedalBoard.get();
if (pedalBoard === undefined) throw new PiPedalStateError("Pedalboard not ready."); if (pedalBoard === undefined) throw new PiPedalStateError("Pedalboard not ready.");
let newPedalBoard = pedalBoard.clone(); let newPedalBoard = pedalBoard.clone();
@@ -1251,7 +1275,7 @@ class PiPedalModelImpl implements PiPedalModel {
let newPedalBoard = pedalBoard.clone(); let newPedalBoard = pedalBoard.clone();
let item = newPedalBoard.getItem(instanceId); let item = newPedalBoard.getItem(instanceId);
let changed = value !== item.isEnabled; let changed = value !== item.isEnabled;
if (changed) { if (changed) {
item.isEnabled = value; item.isEnabled = value;
@@ -1837,8 +1861,7 @@ class PiPedalModelImpl implements PiPedalModel {
}); });
} }
updateVst3State(pedalBoard: PedalBoard) updateVst3State(pedalBoard: PedalBoard) {
{
// let it = pedalBoard.itemsGenerator(); // let it = pedalBoard.itemsGenerator();
// while (true) { // while (true) {
// let v = it.next(); // let v = it.next();
@@ -1862,6 +1885,23 @@ class PiPedalModelImpl implements PiPedalModel {
} }
} }
setSystemMidiBinding(instanceId: number, midiBinding: MidiBinding): void {
let currentBindings = this.systemMidiBindings.get();
let result: MidiBinding[] = [];
for (var binding of currentBindings)
{
if (binding.symbol === midiBinding.symbol)
{
result.push(midiBinding);
} else {
result.push(binding);
}
}
this.systemMidiBindings.set(result);
this.webSocket?.send("setSystemMidiBindings",result);
}
midiListeners: MidiEventListener[] = []; midiListeners: MidiEventListener[] = [];
atomOutputListeners: AtomOutputListener[] = []; atomOutputListeners: AtomOutputListener[] = [];
@@ -1918,8 +1958,7 @@ class PiPedalModelImpl implements PiPedalModel {
break; break;
} }
} }
if (!found) if (!found) {
{
console.log('cancelListenForMidiEvent: event not found.'); console.log('cancelListenForMidiEvent: event not found.');
} }
this.webSocket?.send("cancelListenForMidiEvent", listenHandle._handle); this.webSocket?.send("cancelListenForMidiEvent", listenHandle._handle);
@@ -2101,7 +2140,7 @@ class PiPedalModelImpl implements PiPedalModel {
wifiDirectConfigSettings = wifiDirectConfigSettings.clone(); wifiDirectConfigSettings = wifiDirectConfigSettings.clone();
if ((!oldSettings.enable) && (!wifiDirectConfigSettings.enable) if ((!oldSettings.enable) && (!wifiDirectConfigSettings.enable)
&& (oldSettings.hotspotName === wifiDirectConfigSettings.hotspotName) && (oldSettings.hotspotName === wifiDirectConfigSettings.hotspotName)
) { ) {
// no effective change. // no effective change.
resolve(); resolve();
@@ -2183,19 +2222,14 @@ class PiPedalModelImpl implements PiPedalModel {
} }
zoomUiControl(sourceElement: HTMLElement, instanceId: number, uiControl: UiControl): void { zoomUiControl(sourceElement: HTMLElement, instanceId: number, uiControl: UiControl): void {
let name = uiControl.name; let name = uiControl.name;
if (uiControl.port_group !== "") if (uiControl.port_group !== "") {
{
let pedalboard = this.pedalBoard.get(); let pedalboard = this.pedalBoard.get();
if (pedalboard) if (pedalboard) {
{
let plugin = pedalboard.getItem(instanceId); let plugin = pedalboard.getItem(instanceId);
let uiPlugin = this.getUiPlugin(plugin.uri); let uiPlugin = this.getUiPlugin(plugin.uri);
if (uiPlugin) if (uiPlugin) {
{ for (let i = 0; i < uiPlugin.port_groups.length; ++i) {
for (let i = 0; i < uiPlugin.port_groups.length; ++i) if (uiPlugin.port_groups[i].symbol === uiControl.port_group) {
{
if (uiPlugin.port_groups[i].symbol === uiControl.port_group)
{
name = uiPlugin.port_groups[i].name + " / " + name; name = uiPlugin.port_groups[i].name + " / " + name;
break; break;
} }
@@ -2205,8 +2239,7 @@ class PiPedalModelImpl implements PiPedalModel {
} }
this.zoomedUiControl.set({ source: sourceElement, name: name, instanceId: instanceId, uiControl: uiControl }); this.zoomedUiControl.set({ source: sourceElement, name: name, instanceId: instanceId, uiControl: uiControl });
} }
onPreviousZoomedControl() : void onPreviousZoomedControl(): void {
{
let currentUiControl = this.zoomedUiControl.get(); let currentUiControl = this.zoomedUiControl.get();
if (!currentUiControl) return; if (!currentUiControl) return;
@@ -2222,8 +2255,7 @@ class PiPedalModelImpl implements PiPedalModel {
let i = 0; let i = 0;
let ix = -1; let ix = -1;
for (i = 0; i < uiPlugin.controls.length; ++i) for (i = 0; i < uiPlugin.controls.length; ++i) {
{
if (uiPlugin.controls[i].symbol === currentSymbol) { if (uiPlugin.controls[i].symbol === currentSymbol) {
ix = i; ix = i;
break; break;
@@ -2234,10 +2266,9 @@ class PiPedalModelImpl implements PiPedalModel {
++ix; ++ix;
if (ix >= uiPlugin.controls.length) return; if (ix >= uiPlugin.controls.length) return;
this.zoomUiControl(currentUiControl.source,currentUiControl.instanceId,uiPlugin.controls[ix]); this.zoomUiControl(currentUiControl.source, currentUiControl.instanceId, uiPlugin.controls[ix]);
} }
onNextZoomedControl() : void onNextZoomedControl(): void {
{
let currentUiControl = this.zoomedUiControl.get(); let currentUiControl = this.zoomedUiControl.get();
if (!currentUiControl) return; if (!currentUiControl) return;
@@ -2253,8 +2284,7 @@ class PiPedalModelImpl implements PiPedalModel {
let i = 0; let i = 0;
let ix = -1; let ix = -1;
for (i = 0; i < uiPlugin.controls.length; ++i) for (i = 0; i < uiPlugin.controls.length; ++i) {
{
if (uiPlugin.controls[i].symbol === currentSymbol) { if (uiPlugin.controls[i].symbol === currentSymbol) {
ix = i; ix = i;
break; break;
@@ -2264,8 +2294,8 @@ class PiPedalModelImpl implements PiPedalModel {
--ix; --ix;
if (ix < 0) return; if (ix < 0) return;
this.zoomUiControl(currentUiControl.source,currentUiControl.instanceId,uiPlugin.controls[ix]); this.zoomUiControl(currentUiControl.source, currentUiControl.instanceId, uiPlugin.controls[ix]);
} }
clearZoomedControl(): void { clearZoomedControl(): void {
+21
View File
@@ -44,6 +44,7 @@ import WifiDirectConfigDialog from './WifiDirectConfigDialog';
import DialogEx from './DialogEx' import DialogEx from './DialogEx'
import GovernorSettings from './GovernorSettings'; import GovernorSettings from './GovernorSettings';
import { AlsaMidiDeviceInfo } from './AlsaMidiDeviceInfo'; import { AlsaMidiDeviceInfo } from './AlsaMidiDeviceInfo';
import SystemMidiBindingsDialog from './SystemMidiBindingsDialog';
import Slide, { SlideProps } from '@mui/material/Slide'; import Slide, { SlideProps } from '@mui/material/Slide';
import { createStyles, Theme } from '@mui/material/styles'; import { createStyles, Theme } from '@mui/material/styles';
@@ -84,6 +85,7 @@ interface SettingsDialogState {
isAndroidHosted: boolean; isAndroidHosted: boolean;
showRestartOkDialog: boolean; showRestartOkDialog: boolean;
showShutdownOkDialog: boolean; showShutdownOkDialog: boolean;
showSystemMidiBindingsDialog: boolean;
}; };
@@ -182,6 +184,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
restarting: false, restarting: false,
showShutdownOkDialog: false, showShutdownOkDialog: false,
showRestartOkDialog: false, showRestartOkDialog: false,
showSystemMidiBindingsDialog: false,
isAndroidHosted: this.model.isAndroidHosted() isAndroidHosted: this.model.isAndroidHosted()
@@ -376,6 +379,12 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
showMidiSelectDialog: true showMidiSelectDialog: true
}) })
} }
handleMidiMessageSettings() {
this.setState({
showSystemMidiBindingsDialog: true
});
}
handleJackServerSettings() { handleJackServerSettings() {
this.setState({ this.setState({
showJackServerSettingsDialog: true, showJackServerSettingsDialog: true,
@@ -634,6 +643,14 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
<Typography className={classes.secondaryItem} display="block" variant="caption" color="textSecondary" noWrap>{this.midiSummary()}</Typography> <Typography className={classes.secondaryItem} display="block" variant="caption" color="textSecondary" noWrap>{this.midiSummary()}</Typography>
</div> </div>
</ButtonBase> </ButtonBase>
<ButtonBase className={classes.setting} disabled={!isConfigValid} onClick={() => this.handleMidiMessageSettings()} >
<SelectHoverBackground selected={false} showHover={true} />
<div style={{ width: "100%" }}>
<Typography className={classes.primaryItem} display="block" variant="body2" noWrap>System MIDI Bindings</Typography>
</div>
</ButtonBase>
</div> </div>
</div> </div>
<Divider /> <Divider />
@@ -840,6 +857,10 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
onOk={() => { this.setState({ showShutdownOkDialog: false }); this.handleShutdownOk(); }} onOk={() => { this.setState({ showShutdownOkDialog: false }); this.handleShutdownOk(); }}
onClose={() => { this.setState({ showShutdownOkDialog: false }); }} onClose={() => { this.setState({ showShutdownOkDialog: false }); }}
/> />
<SystemMidiBindingsDialog
open={this.state.showSystemMidiBindingsDialog}
onClose={()=> { this.setState({showSystemMidiBindingsDialog: false});}}
/>
</DialogEx > </DialogEx >
); );
+218
View File
@@ -0,0 +1,218 @@
/*
* MIT License
*
* Copyright (c) 2022-2023 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.
*/
import { Component } from 'react';
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
import { Theme } from '@mui/material/styles';
import { WithStyles } from '@mui/styles';
import withStyles from '@mui/styles/withStyles';
import createStyles from '@mui/styles/createStyles';
import MenuItem from '@mui/material/MenuItem';
import Select from '@mui/material/Select';
import MidiBinding from './MidiBinding';
import Utility from './Utility';
import MicNoneOutlinedIcon from '@mui/icons-material/MicNoneOutlined';
import MicOutlinedIcon from '@mui/icons-material/MicOutlined';
import IconButton from '@mui/material/IconButton';
const styles = (theme: Theme) => createStyles({
controlDiv: { flex: "0 0 auto", marginRight: 12, verticalAlign: "center", height: 48, paddingTop: 8, paddingBottom: 8 },
controlDiv2: {
flex: "0 0 auto", marginRight: 12, verticalAlign: "center",
height: 48, paddingTop: 0, paddingBottom: 0, whiteSpace: "nowrap"
}
});
interface SystemMidiBindingViewProps extends WithStyles<typeof styles> {
instanceId: number;
listen: boolean;
midiBinding: MidiBinding;
onChange: (instanceId: number, newBinding: MidiBinding) => void;
onListen: (instanceId: number, key: string, listenForControl: boolean) => void;
}
interface SystemMidiBindingViewState {
}
const SystemMidiBindingView =
withStyles(styles, { withTheme: true })(
class extends Component<SystemMidiBindingViewProps, SystemMidiBindingViewState> {
model: PiPedalModel;
constructor(props: SystemMidiBindingViewProps) {
super(props);
this.model = PiPedalModelFactory.getInstance();
this.state = {
};
}
handleTypeChange(e: any, extra: any) {
let newValue = parseInt(e.target.value);
let newBinding = this.props.midiBinding.clone();
newBinding.bindingType = newValue;
this.props.onChange(this.props.instanceId, newBinding);
}
handleNoteChange(e: any, extra: any) {
let newValue = parseInt(e.target.value);
let newBinding = this.props.midiBinding.clone();
newBinding.note = newValue;
this.props.onChange(this.props.instanceId, newBinding);
}
handleControlChange(e: any, extra: any) {
let newValue = parseInt(e.target.value);
let newBinding = this.props.midiBinding.clone();
newBinding.control = newValue;
this.props.onChange(this.props.instanceId, newBinding);
}
handleLatchControlTypeChange(e: any, extra: any) {
let newValue = parseInt(e.target.value);
let newBinding = this.props.midiBinding.clone();
newBinding.switchControlType = newValue;
this.props.onChange(this.props.instanceId, newBinding);
}
handleLinearControlTypeChange(e: any, extra: any) {
let newValue = parseInt(e.target.value);
let newBinding = this.props.midiBinding.clone();
newBinding.linearControlType = newValue;
this.props.onChange(this.props.instanceId, newBinding);
}
handleMinChange(value: number): void {
let newBinding = this.props.midiBinding.clone();
newBinding.minValue = value;
this.props.onChange(this.props.instanceId, newBinding);
}
handleMaxChange(value: number): void {
let newBinding = this.props.midiBinding.clone();
newBinding.maxValue = value;
this.props.onChange(this.props.instanceId, newBinding);
}
handleScaleChange(value: number): void {
let newBinding = this.props.midiBinding.clone();
newBinding.rotaryScale = value;
this.props.onChange(this.props.instanceId, newBinding);
}
generateMidiSelects(): React.ReactNode[] {
let result: React.ReactNode[] = [];
for (let i = 0; i < 127; ++i) {
result.push(
<MenuItem value={i}>{Utility.midiNoteName(i)}</MenuItem>
)
}
return result;
}
generateControlSelects(): React.ReactNode[] {
return Utility.validMidiControllers.map((control) => (
<MenuItem key={control.value} value={control.value}>{control.displayName}</MenuItem>
)
);
}
render() {
let classes = this.props.classes;
let midiBinding = this.props.midiBinding;
return (
<div style={{ display: "flex", flexDirection: "row", flexWrap: "wrap", justifyContent: "left", alignItems: "center", minHeight: 48 }}>
<div className={classes.controlDiv} >
<Select variant="standard"
style={{ width: 80, }}
onChange={(e, extra) => this.handleTypeChange(e, extra)}
value={midiBinding.bindingType}
>
<MenuItem value={0}>None</MenuItem>
<MenuItem value={1}>Note</MenuItem>
<MenuItem value={2}>Control</MenuItem>
</Select>
</div>
{
(midiBinding.bindingType === MidiBinding.BINDING_TYPE_NOTE) &&
(
<div className={classes.controlDiv} >
<Select variant="standard"
style={{ width: 80, verticalAlign: 'center' }}
onChange={(e, extra) => this.handleNoteChange(e, extra)}
value={midiBinding.note}
>
{
this.generateMidiSelects()
}
</Select>
</div>
)
}
{
(midiBinding.bindingType === MidiBinding.BINDING_TYPE_CONTROL) &&
(
<div className={classes.controlDiv} >
<Select variant="standard"
style={{ width: 80 }}
onChange={(e, extra) => this.handleControlChange(e, extra)}
value={midiBinding.control}
renderValue={(value) => { return "CC-" + value }}
>
{
this.generateControlSelects()
}
</Select>
</div>
)
}
<IconButton
onClick={() => {
if (this.props.listen) {
this.props.onListen(-2, "", false)
} else {
this.props.onListen(this.props.instanceId, this.props.midiBinding.symbol, false)
}
}}
size="large">
{this.props.listen ? (
<MicOutlinedIcon />
) : (
<MicNoneOutlinedIcon />
)}
</IconButton>
</div>
);
}
});
export default SystemMidiBindingView;
+338
View File
@@ -0,0 +1,338 @@
/*
* MIT License
*
* Copyright (c) 2022-2023 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.
*/
import React, { SyntheticEvent } from 'react';
import DialogEx from './DialogEx';
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
import { PiPedalModel, PiPedalModelFactory, ListenHandle } from './PiPedalModel';
import Typography from '@mui/material/Typography';
import { Theme } from '@mui/material/styles';
import { WithStyles } from '@mui/styles';
import createStyles from '@mui/styles/createStyles';
import withStyles from '@mui/styles/withStyles';
import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import IconButton from '@mui/material/IconButton';
import MidiBinding from './MidiBinding';
import SystemMidiBindingView from './SystemMidiBindingView';
import Snackbar from '@mui/material/Snackbar';
const styles = (theme: Theme) => createStyles({
dialogAppBar: {
position: 'relative',
top: 0, left: 0
},
dialogTitle: {
marginLeft: theme.spacing(2),
flex: "1 1 auto",
},
pluginTable: {
border: "collapse",
width: "100%",
},
pluginHead: {
borderTop: "1pt #DDD solid", paddingLeft: 22, paddingRight: 22
},
bindingTd: {
verticalAlign: "top",
paddingLeft: 12, paddingBottom: 8
},
nameTd: {
paddingLeft: 16,
verticalAlign: "top",
paddingTop: 12
},
});
export interface SystemMidiBindingDialogProps extends WithStyles<typeof styles> {
open: boolean,
onClose: () => void
}
class BindingEntry {
constructor(
displayName: string,
instanceId: number,
midiBinding: MidiBinding,
) {
this.displayName = displayName;
this.instanceId = instanceId;
this.midiBinding = midiBinding;
}
displayName: string;
instanceId: number;
midiBinding: MidiBinding;
}
export interface SystemMidiBindingDialogState {
listenInstanceId: number;
listenSymbol: string;
listenSnackbarOpen: boolean;
systemMidiBindings: BindingEntry[];
}
export const SystemMidiBindingDialog =
withStyles(styles, { withTheme: true })(
class extends ResizeResponsiveComponent<SystemMidiBindingDialogProps, SystemMidiBindingDialogState> {
model: PiPedalModel;
constructor(props: SystemMidiBindingDialogProps) {
super(props);
this.model = PiPedalModelFactory.getInstance();
this.state = {
listenInstanceId: -2,
listenSymbol: "",
listenSnackbarOpen: false,
systemMidiBindings: this.createBindings()
};
this.handleClose = this.handleClose.bind(this);
this.onMidiBindingsChanged = this.onMidiBindingsChanged.bind(this);
}
createBindings(): BindingEntry[] {
let result: BindingEntry[] = [];
for (var item of this.model.systemMidiBindings.get())
{
let displayName = "";
let instanceId = -1;
if (item.symbol === "prevProgram")
{
displayName = "Previous Preset";
instanceId = 1;
} else if (item.symbol === "nextProgram")
{
displayName = "Next Preset";
instanceId = 2;
}
if (instanceId !== -1)
{
result.push(new BindingEntry(displayName,instanceId,item));
}
}
return result;
}
mounted: boolean = false;
hasHooks: boolean = false;
handleClose() {
this.cancelListenForControl();
this.props.onClose();
}
listenTimeoutHandle?: NodeJS.Timeout;
listenHandle?: ListenHandle;
cancelListenForControl() {
if (this.listenTimeoutHandle) {
clearTimeout(this.listenTimeoutHandle);
this.listenTimeoutHandle = undefined;
}
if (this.listenHandle)
{
this.model.cancelListenForMidiEvent(this.listenHandle)
this.listenHandle = undefined;
}
this.setState({ listenInstanceId: -2, listenSymbol: "" });
}
handleListenSucceeded(instanceId: number, symbol: string, isNote: boolean, noteOrControl: number)
{
this.cancelListenForControl();
for (var binding of this.state.systemMidiBindings)
{
if (binding.instanceId === instanceId)
{
let newBinding = binding.midiBinding.clone();
if (isNote) {
newBinding.bindingType = MidiBinding.BINDING_TYPE_NOTE;
newBinding.note = noteOrControl;
} else {
newBinding.bindingType = MidiBinding.BINDING_TYPE_CONTROL;
newBinding.control = noteOrControl;
}
this.model.setSystemMidiBinding(instanceId,newBinding);
return;
}
}
}
handleListenForControl(instanceId: number, symbol: string, listenForControl: boolean): void {
this.cancelListenForControl();
this.setState({ listenInstanceId: instanceId, listenSymbol: symbol, listenSnackbarOpen: true });
this.listenTimeoutHandle = setTimeout(() => {
this.cancelListenForControl();
}, 8000);
this.listenHandle = this.model.listenForMidiEvent(listenForControl,
(isNote: boolean, noteOrControl: number) => {
this.handleListenSucceeded(instanceId,symbol,isNote, noteOrControl);
});
}
onWindowSizeChanged(width: number, height: number): void {
}
onMidiBindingsChanged() {
this.setState({systemMidiBindings: this.createBindings()});
}
componentDidMount() {
super.componentDidMount();
this.mounted = true;
this.model.systemMidiBindings.addOnChangedHandler(this.onMidiBindingsChanged);
this.onMidiBindingsChanged();
}
componentWillUnmount() {
super.componentWillUnmount();
this.mounted = false;
this.model.systemMidiBindings.removeOnChangedHandler(this.onMidiBindingsChanged);
}
componentDidUpdate() {
}
handleItemChanged(instanceId: number, newBinding: MidiBinding) {
this.model.setSystemMidiBinding(instanceId, newBinding);
}
generateTable(): React.ReactNode[] {
let classes = this.props.classes;
let result: React.ReactNode[] = [];
let items = this.state.systemMidiBindings;
for (var item of items) {
result.push(
<tr key={item.instanceId}>
<td className={classes.nameTd}>
<Typography noWrap style={{ verticalAlign: "center", height: 48 }}>
{item.displayName}
</Typography>
</td>
<td className={classes.bindingTd}>
<SystemMidiBindingView instanceId={item.instanceId} midiBinding={item.midiBinding}
onListen={(instanceId: number, symbol: string, listenForControl: boolean) => {
if (instanceId === -2)
{
this.cancelListenForControl();
} else {
this.handleListenForControl(instanceId, symbol, listenForControl);
}
}}
listen={item.instanceId === this.state.listenInstanceId && this.state.listenSymbol === item.midiBinding.symbol}
onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)}
/>
</td>
</tr>
);
}
return result;
}
supressDefault(e: SyntheticEvent) {
//e.preventDefault();
//e.stopPropagation();
}
render() {
let props = this.props;
let { open, classes } = props;
if (!open) {
return (<div />);
}
return (
<DialogEx tag="SystemMidiBindingsDialog" open={open} fullWidth onClose={this.handleClose} aria-labelledby="Rename-dialog-title"
fullScreen={true}
style={{userSelect: "none"}}
>
<div style={{ display: "flex", flexDirection: "column", flexWrap: "nowrap", width: "100%", height: "100%", overflow: "hidden" }}>
<div style={{ flex: "0 0 auto" }}>
<AppBar className={classes.dialogAppBar} >
<Toolbar>
<IconButton
edge="start"
color="inherit"
onClick={this.handleClose}
aria-label="back"
size="large">
<ArrowBackIcon />
</IconButton>
<Typography variant="h6" className={classes.dialogTitle}>
System MIDI Bindings
</Typography>
</Toolbar>
</AppBar>
</div>
<div style={{ overflow: "auto", flex: "1 1 auto", width: "100%" }}>
<table className={classes.pluginTable} >
<colgroup>
<col style={{ width: "auto" }} />
<col style={{ width: "100%" }} />
</colgroup>
<tbody>
{this.generateTable()}
</tbody>
</table>
</div>
</div>
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={this.state.listenSnackbarOpen}
autoHideDuration={1500}
onClose={() => this.setState({ listenSnackbarOpen: false })}
message="Listening for MIDI input"
/>
</DialogEx >
);
}
});
export default SystemMidiBindingDialog;
File diff suppressed because it is too large Load Diff
+15 -8
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2022 Robin Davies // Copyright (c) 2022-2023 Robin Davies
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy of // 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 // this software and associated documentation files (the "Software"), to deal in
@@ -24,7 +24,7 @@
#include "Lv2PedalBoard.hpp" #include "Lv2PedalBoard.hpp"
#include "VuUpdate.hpp" #include "VuUpdate.hpp"
#include "json.hpp" #include "json.hpp"
#include "JackHost.hpp" #include "AudioHost.hpp"
#include "JackServerSettings.hpp" #include "JackServerSettings.hpp"
#include <functional> #include <functional>
#include "PiPedalAlsa.hpp" #include "PiPedalAlsa.hpp"
@@ -32,6 +32,8 @@
namespace pipedal { namespace pipedal {
struct RealtimeMidiProgramRequest;
struct RealtimeNextMidiProgramRequest;
using PortMonitorCallback = std::function<void (int64_t handle, float value)>; using PortMonitorCallback = std::function<void (int64_t handle, float value)>;
@@ -91,7 +93,7 @@ public:
}; };
class IJackHostCallbacks { class IAudioHostCallbacks {
public: public:
virtual void OnNotifyVusSubscription(const std::vector<VuUpdate> & updates) = 0; virtual void OnNotifyVusSubscription(const std::vector<VuUpdate> & updates) = 0;
virtual void OnNotifyMonitorPort(const MonitorPortUpdate &update) = 0; virtual void OnNotifyMonitorPort(const MonitorPortUpdate &update) = 0;
@@ -99,6 +101,8 @@ public:
virtual void OnNotifyMidiListen(bool isNote, uint8_t noteOrControl) = 0; virtual void OnNotifyMidiListen(bool isNote, uint8_t noteOrControl) = 0;
virtual void OnNotifyAtomOutput(uint64_t instanceId, const std::string&atomType,const std::string&atomJson) = 0; virtual void OnNotifyAtomOutput(uint64_t instanceId, const std::string&atomType,const std::string&atomJson) = 0;
virtual void OnNotifyMidiProgramChange(RealtimeMidiProgramRequest&midiProgramRequest) = 0;
virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest&request) = 0;
}; };
@@ -124,17 +128,17 @@ public:
class IHost; class IHost;
class JackHost { class AudioHost {
protected: protected:
JackHost() { } AudioHost() { }
public: public:
static JackHost*CreateInstance(IHost *pHost); static AudioHost*CreateInstance(IHost *pHost);
virtual ~JackHost() { }; virtual ~AudioHost() { };
virtual void UpdateServerConfiguration(const JackServerSettings & jackServerSettings, virtual void UpdateServerConfiguration(const JackServerSettings & jackServerSettings,
std::function<void(bool success, const std::string&errorMessage)> onComplete) = 0; std::function<void(bool success, const std::string&errorMessage)> onComplete) = 0;
virtual void SetNotificationCallbacks(IJackHostCallbacks *pNotifyCallbacks) = 0; virtual void SetNotificationCallbacks(IAudioHostCallbacks *pNotifyCallbacks) = 0;
virtual void SetListenForMidiEvent(bool listen) = 0; virtual void SetListenForMidiEvent(bool listen) = 0;
virtual void SetListenForAtomOutput(bool listen) = 0; virtual void SetListenForAtomOutput(bool listen) = 0;
@@ -158,7 +162,10 @@ public:
virtual void SetVuSubscriptions(const std::vector<int64_t> &instanceIds) = 0; virtual void SetVuSubscriptions(const std::vector<int64_t> &instanceIds) = 0;
virtual void SetMonitorPortSubscriptions(const std::vector<MonitorPortSubscription> &subscriptions) = 0; virtual void SetMonitorPortSubscriptions(const std::vector<MonitorPortSubscription> &subscriptions) = 0;
virtual void SetSystemMidiBindings(const std::vector<MidiBinding>&bindings) = 0;
virtual void getRealtimeParameter(RealtimeParameterRequest*pParameterRequest) = 0; virtual void getRealtimeParameter(RealtimeParameterRequest*pParameterRequest) = 0;
virtual void AckMidiProgramRequest(uint64_t requestId) = 0;
virtual JackHostStatus getJackStatus() = 0; virtual JackHostStatus getJackStatus() = 0;
+3 -3
View File
@@ -149,7 +149,7 @@ set (PIPEDAL_SOURCES
CpuGovernor.cpp CpuGovernor.hpp CpuGovernor.cpp CpuGovernor.hpp
GovernorSettings.cpp GovernorSettings.hpp GovernorSettings.cpp GovernorSettings.hpp
SysExec.cpp SysExec.hpp SysExec.cpp SysExec.hpp
BeastServer.cpp BeastServer.hpp HtmlHelper.cpp HtmlHelper.hpp pch.h Uri.cpp Uri.hpp WebServer.cpp WebServer.hpp HtmlHelper.cpp HtmlHelper.hpp pch.h Uri.cpp Uri.hpp
WifiConfigSettings.hpp WifiConfigSettings.cpp WifiConfigSettings.hpp WifiConfigSettings.cpp
WifiDirectConfigSettings.hpp WifiDirectConfigSettings.cpp WifiDirectConfigSettings.hpp WifiDirectConfigSettings.cpp
ConfigUtil.hpp ConfigUtil.cpp ConfigUtil.hpp ConfigUtil.cpp
@@ -164,7 +164,7 @@ set (PIPEDAL_SOURCES
Presets.hpp Presets.cpp Presets.hpp Presets.cpp
Storage.hpp Storage.cpp Storage.hpp Storage.cpp
Banks.hpp Banks.cpp Banks.hpp Banks.cpp
JackHost.hpp JackHost.cpp AudioHost.hpp AudioHost.cpp
JackConfiguration.hpp JackConfiguration.cpp JackConfiguration.hpp JackConfiguration.cpp
defer.hpp defer.hpp
Lv2Effect.cpp Lv2Effect.hpp Lv2Effect.cpp Lv2Effect.hpp
@@ -264,7 +264,7 @@ add_executable(pipedaltest testMain.cpp
SystemConfigFile.hpp SystemConfigFile.cpp SystemConfigFile.hpp SystemConfigFile.cpp
SystemConfigFileTest.cpp SystemConfigFileTest.cpp
BeastServerTest.cpp WebServerTest.cpp
MemDebug.cpp MemDebug.cpp
MemDebug.hpp MemDebug.hpp
) )
+1 -1
View File
@@ -37,7 +37,7 @@
#include "lv2/parameters.lv2/parameters.h" #include "lv2/parameters.lv2/parameters.h"
#include "lv2/units.lv2/units.h" #include "lv2/units.lv2/units.h"
#include "lv2/atom.lv2/util.h" #include "lv2/atom.lv2/util.h"
#include "JackHost.hpp" #include "AudioHost.hpp"
#include <exception> #include <exception>
#include "RingBufferReader.hpp" #include "RingBufferReader.hpp"
+1 -1
View File
@@ -25,7 +25,7 @@
#include "SplitEffect.hpp" #include "SplitEffect.hpp"
#include "RingBufferReader.hpp" #include "RingBufferReader.hpp"
#include "VuUpdate.hpp" #include "VuUpdate.hpp"
#include "JackHost.hpp" #include "AudioHost.hpp"
#include "Lv2EventBufferWriter.hpp" #include "Lv2EventBufferWriter.hpp"
using namespace pipedal; using namespace pipedal;
+9 -3
View File
@@ -58,15 +58,21 @@ public:
private: private:
std::string symbol_; std::string symbol_;
int channel_ = -1; int channel_ = -1;
int bindingType_; int bindingType_ = BINDING_TYPE_NONE;
int note_ = 12*4+24; int note_ = 12*4+24;
int control_ = 1; int control_ = 1;
float minValue_ = 0; float minValue_ = 0;
float maxValue_ = 1; float maxValue_ = 1;
float rotaryScale_ = 1; float rotaryScale_ = 1;
int linearControlType_ = 0; int linearControlType_ = LINEAR_CONTROL_TYPE;
int switchControlType_ = 0; int switchControlType_ = LATCH_CONTROL_TYPE;
public: public:
static MidiBinding SystemBinding(const std::string&symbol)
{
MidiBinding result;
result.symbol_ = symbol;
return result;
}
GETTER_SETTER(channel); GETTER_SETTER(channel);
GETTER_SETTER_REF(symbol); GETTER_SETTER_REF(symbol);
GETTER_SETTER(bindingType); GETTER_SETTER(bindingType);
+151 -34
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2022 Robin Davies // Copyright (c) 2022-2023 Robin Davies
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy of // 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 // this software and associated documentation files (the "Software"), to deal in
@@ -23,13 +23,14 @@
#include "ConfigUtil.hpp" #include "ConfigUtil.hpp"
#include <sched.h> #include <sched.h>
#include "PiPedalModel.hpp" #include "PiPedalModel.hpp"
#include "JackHost.hpp" #include "AudioHost.hpp"
#include "Lv2Log.hpp" #include "Lv2Log.hpp"
#include <set> #include <set>
#include "PiPedalConfiguration.hpp" #include "PiPedalConfiguration.hpp"
#include "AdminClient.hpp" #include "AdminClient.hpp"
#include "SplitEffect.hpp" #include "SplitEffect.hpp"
#include "CpuGovernor.hpp" #include "CpuGovernor.hpp"
#include "RingBufferReader.hpp"
#ifndef NO_MLOCK #ifndef NO_MLOCK
#include <sys/mman.h> #include <sys/mman.h>
@@ -84,9 +85,9 @@ void PiPedalModel::Close()
} }
delete[] t; delete[] t;
if (jackHost) if (audioHost)
{ {
jackHost->Close(); audioHost->Close();
} }
} }
@@ -113,9 +114,9 @@ PiPedalModel::~PiPedalModel()
try try
{ {
if (jackHost) if (audioHost)
{ {
jackHost->Close(); audioHost->Close();
} }
} }
catch (...) catch (...)
@@ -133,6 +134,8 @@ void PiPedalModel::Init(const PiPedalConfiguration &configuration)
storage.SetDataRoot(configuration.GetLocalStoragePath()); storage.SetDataRoot(configuration.GetLocalStoragePath());
storage.Initialize(); storage.Initialize();
this->systemMidiBindings = storage.GetSystemMidiBindings();
this->jackServerSettings.ReadJackConfiguration(); this->jackServerSettings.ReadJackConfiguration();
} }
@@ -191,10 +194,14 @@ void PiPedalModel::Load()
} }
UpdateDefaults(&this->pedalBoard); UpdateDefaults(&this->pedalBoard);
std::unique_ptr<JackHost> p{JackHost::CreateInstance(lv2Host.asIHost())}; std::unique_ptr<AudioHost> p{AudioHost::CreateInstance(lv2Host.asIHost())};
this->jackHost = std::move(p); this->audioHost = std::move(p);
this->jackHost->SetNotificationCallbacks(this); this->audioHost->SetNotificationCallbacks(this);
this->systemMidiBindings = storage.GetSystemMidiBindings();
this->audioHost->SetSystemMidiBindings(this->systemMidiBindings);
if (configuration.GetMLock()) if (configuration.GetMLock())
{ {
@@ -226,11 +233,11 @@ void PiPedalModel::Load()
this->lv2Host.OnConfigurationChanged(jackConfiguration, selection); this->lv2Host.OnConfigurationChanged(jackConfiguration, selection);
try try
{ {
jackHost->Open(this->jackServerSettings, selection); audioHost->Open(this->jackServerSettings, selection);
try { try {
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)}; std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
this->lv2PedalBoard = lv2PedalBoard; this->lv2PedalBoard = lv2PedalBoard;
jackHost->SetPedalBoard(lv2PedalBoard); audioHost->SetPedalBoard(lv2PedalBoard);
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
@@ -305,7 +312,7 @@ void PiPedalModel::PreviewControl(int64_t clientId, int64_t pedalItemId, const s
effect->SetControl(index,value);; effect->SetControl(index,value);;
} }
} else { } else {
jackHost->SetControlValue(pedalItemId, symbol, value); audioHost->SetControlValue(pedalItemId, symbol, value);
} }
} }
@@ -387,11 +394,11 @@ void PiPedalModel::FirePedalBoardChanged(int64_t clientId)
delete[] t; delete[] t;
// notify the audio thread. // notify the audio thread.
if (jackHost->IsOpen()) if (audioHost->IsOpen())
{ {
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)}; std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
this->lv2PedalBoard = lv2PedalBoard; this->lv2PedalBoard = lv2PedalBoard;
jackHost->SetPedalBoard(lv2PedalBoard); audioHost->SetPedalBoard(lv2PedalBoard);
UpdateRealtimeVuSubscriptions(); UpdateRealtimeVuSubscriptions();
UpdateRealtimeMonitorPortSubscriptions(); UpdateRealtimeMonitorPortSubscriptions();
} }
@@ -446,7 +453,7 @@ void PiPedalModel::SetPedalBoardItemEnable(int64_t clientId, int64_t pedalItemId
this->SetPresetChanged(clientId, true); this->SetPresetChanged(clientId, true);
// Notify audo thread. // Notify audo thread.
this->jackHost->SetBypass(pedalItemId, enabled); this->audioHost->SetBypass(pedalItemId, enabled);
} }
} }
@@ -621,6 +628,87 @@ int64_t PiPedalModel::UploadBank(BankFile &bankFile, int64_t uploadAfter)
return newPreset; return newPreset;
} }
void PiPedalModel::OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest&request)
{
std::lock_guard<std::recursive_mutex> guard{mutex};
try {
PresetIndex index;
storage.GetPresetIndex(&index);
auto currentPresetId = storage.GetCurrentPresetId();
size_t currentPresetIndex = 0;
for (size_t i = 0; i < index.presets().size(); ++i)
{
if (index.presets()[i].instanceId() == currentPresetId)
{
currentPresetIndex = i;
break;
}
}
if (index.presets().size() == 0)
{
throw PiPedalException("No presets loaded.");
}
if (request.direction < 0)
{
if (currentPresetIndex == 0)
{
currentPresetIndex = index.presets().size()-1;
} else {
--currentPresetIndex;
}
} else {
++currentPresetIndex;
if (currentPresetIndex >= index.presets().size())
{
currentPresetIndex = 0;
}
}
LoadPreset(-1,index.presets()[currentPresetIndex].instanceId());
} catch (std::exception&e)
{
Lv2Log::error(e.what());
}
if (this->audioHost)
{
this->audioHost->AckMidiProgramRequest(request.requestId);
}
}
void PiPedalModel::OnNotifyMidiProgramChange(RealtimeMidiProgramRequest&midiProgramRequest)
{
std::lock_guard<std::recursive_mutex> guard{mutex};
try {
if (midiProgramRequest.bank >= 0)
{
int64_t bankId = storage.GetBankByMidiBankNumber(midiProgramRequest.bank);
if (bankId == -1) throw PiPedalException("Bank not found.");
if (bankId != this->storage.GetBanks().selectedBank())
{
storage.LoadBank(bankId);
FireBanksChanged(-1);
FirePresetsChanged(-1);
}
}
int64_t presetId = storage.GetPresetByProgramNumber(midiProgramRequest.program);
if (presetId == -1) throw PiPedalException("No valid preset.");
LoadPreset(-1,presetId);
} catch (std::exception&e)
{
Lv2Log::error(e.what());
}
if (this->audioHost)
{
this->audioHost->AckMidiProgramRequest(midiProgramRequest.requestId);
}
}
void PiPedalModel::LoadPreset(int64_t clientId, int64_t instanceId) void PiPedalModel::LoadPreset(int64_t clientId, int64_t instanceId)
{ {
std::lock_guard<std::recursive_mutex> guard{mutex}; std::lock_guard<std::recursive_mutex> guard{mutex};
@@ -883,10 +971,10 @@ JackConfiguration PiPedalModel::GetJackConfiguration()
void PiPedalModel::RestartAudio() void PiPedalModel::RestartAudio()
{ {
if (this->jackHost->IsOpen()) if (this->audioHost->IsOpen())
{ {
this->jackHost->Close(); this->audioHost->Close();
} }
// restarting is a bit dodgy. It was impossible with Jack, but // restarting is a bit dodgy. It was impossible with Jack, but
// now very plausible with the ALSA audio stack. // now very plausible with the ALSA audio stack.
@@ -899,7 +987,7 @@ void PiPedalModel::RestartAudio()
// do a complete reload. // do a complete reload.
this->jackHost->SetPedalBoard(nullptr); this->audioHost->SetPedalBoard(nullptr);
this->jackConfiguration.AlsaInitialize(this->jackServerSettings); this->jackConfiguration.AlsaInitialize(this->jackServerSettings);
@@ -928,13 +1016,13 @@ void PiPedalModel::RestartAudio()
Lv2Log::error("Audio configuration not valid."); Lv2Log::error("Audio configuration not valid.");
return; return;
} }
this->jackHost->Open(this->jackServerSettings, channelSelection); this->audioHost->Open(this->jackServerSettings, channelSelection);
this->lv2Host.OnConfigurationChanged(jackConfiguration, channelSelection); this->lv2Host.OnConfigurationChanged(jackConfiguration, channelSelection);
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)}; std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
this->lv2PedalBoard = lv2PedalBoard; this->lv2PedalBoard = lv2PedalBoard;
jackHost->SetPedalBoard(lv2PedalBoard); audioHost->SetPedalBoard(lv2PedalBoard);
this->UpdateRealtimeVuSubscriptions(); this->UpdateRealtimeVuSubscriptions();
UpdateRealtimeMonitorPortSubscriptions(); UpdateRealtimeMonitorPortSubscriptions();
} }
@@ -1121,12 +1209,12 @@ void PiPedalModel::UpdateRealtimeVuSubscriptions()
} }
} }
std::vector<int64_t> instanceids(addedInstances.begin(), addedInstances.end()); std::vector<int64_t> instanceids(addedInstances.begin(), addedInstances.end());
jackHost->SetVuSubscriptions(instanceids); audioHost->SetVuSubscriptions(instanceids);
} }
void PiPedalModel::UpdateRealtimeMonitorPortSubscriptions() void PiPedalModel::UpdateRealtimeMonitorPortSubscriptions()
{ {
jackHost->SetMonitorPortSubscriptions(this->activeMonitorPortSubscriptions); audioHost->SetMonitorPortSubscriptions(this->activeMonitorPortSubscriptions);
} }
int64_t PiPedalModel::MonitorPort(int64_t instanceId, const std::string &key, float updateInterval, PortMonitorCallback onUpdate) int64_t PiPedalModel::MonitorPort(int64_t instanceId, const std::string &key, float updateInterval, PortMonitorCallback onUpdate)
@@ -1220,7 +1308,7 @@ void PiPedalModel::GetLv2Parameter(
std::lock_guard<std::recursive_mutex> guard(mutex); std::lock_guard<std::recursive_mutex> guard(mutex);
outstandingParameterRequests.push_back(request); outstandingParameterRequests.push_back(request);
this->jackHost->getRealtimeParameter(request); this->audioHost->getRealtimeParameter(request);
} }
BankIndex PiPedalModel::GetBankIndex() const BankIndex PiPedalModel::GetBankIndex() const
@@ -1254,6 +1342,7 @@ void PiPedalModel::OpenBank(int64_t clientId, int64_t bankId)
FirePresetsChanged(clientId); FirePresetsChanged(clientId);
this->pedalBoard = storage.GetCurrentPreset(); this->pedalBoard = storage.GetCurrentPreset();
UpdateDefaults(&this->pedalBoard); UpdateDefaults(&this->pedalBoard);
this->hasPresetChanged = false; this->hasPresetChanged = false;
this->FirePedalBoardChanged(clientId); this->FirePedalBoardChanged(clientId);
@@ -1311,7 +1400,7 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
this->jackConfiguration.SetIsRestarting(true); this->jackConfiguration.SetIsRestarting(true);
FireJackConfigurationChanged(this->jackConfiguration); FireJackConfigurationChanged(this->jackConfiguration);
this->jackHost->UpdateServerConfiguration( this->audioHost->UpdateServerConfiguration(
jackServerSettings, jackServerSettings,
[this](bool success, const std::string &errorMessage) [this](bool success, const std::string &errorMessage)
{ {
@@ -1341,7 +1430,7 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)}; std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
this->lv2PedalBoard = lv2PedalBoard; this->lv2PedalBoard = lv2PedalBoard;
jackHost->SetPedalBoard(lv2PedalBoard); audioHost->SetPedalBoard(lv2PedalBoard);
UpdateRealtimeVuSubscriptions(); UpdateRealtimeVuSubscriptions();
UpdateRealtimeMonitorPortSubscriptions(); UpdateRealtimeMonitorPortSubscriptions();
#endif #endif
@@ -1418,7 +1507,7 @@ void PiPedalModel::LoadPluginPreset(int64_t pluginInstanceId, uint64_t presetIns
if (pedalBoardItem != nullptr) if (pedalBoardItem != nullptr)
{ {
std::vector<ControlValue> controlValues = storage.GetPluginPresetValues(pedalBoardItem->uri(), presetInstanceId); std::vector<ControlValue> controlValues = storage.GetPluginPresetValues(pedalBoardItem->uri(), presetInstanceId);
jackHost->SetPluginPreset(pluginInstanceId, controlValues); audioHost->SetPluginPreset(pluginInstanceId, controlValues);
for (size_t i = 0; i < controlValues.size(); ++i) for (size_t i = 0; i < controlValues.size(); ++i)
{ {
@@ -1451,7 +1540,7 @@ void PiPedalModel::DeleteAtomOutputListeners(int64_t clientId)
--i; --i;
} }
} }
jackHost->SetListenForAtomOutput(atomOutputListeners.size() != 0); audioHost->SetListenForAtomOutput(atomOutputListeners.size() != 0);
} }
void PiPedalModel::DeleteMidiListeners(int64_t clientId) void PiPedalModel::DeleteMidiListeners(int64_t clientId)
@@ -1465,7 +1554,7 @@ void PiPedalModel::DeleteMidiListeners(int64_t clientId)
--i; --i;
} }
} }
jackHost->SetListenForMidiEvent(midiEventListeners.size() != 0); audioHost->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)
@@ -1489,7 +1578,7 @@ void PiPedalModel::OnNotifyAtomOutput(uint64_t instanceId, const std::string &at
} }
} }
} }
jackHost->SetListenForAtomOutput(atomOutputListeners.size() != 0); audioHost->SetListenForAtomOutput(atomOutputListeners.size() != 0);
} }
void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl) void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl)
@@ -1513,7 +1602,7 @@ void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl)
} }
} }
} }
jackHost->SetListenForMidiEvent(midiEventListeners.size() != 0); audioHost->SetListenForMidiEvent(midiEventListeners.size() != 0);
} }
void PiPedalModel::ListenForMidiEvent(int64_t clientId, int64_t clientHandle, bool listenForControlsOnly) void PiPedalModel::ListenForMidiEvent(int64_t clientId, int64_t clientHandle, bool listenForControlsOnly)
@@ -1521,7 +1610,7 @@ void PiPedalModel::ListenForMidiEvent(int64_t clientId, int64_t clientHandle, bo
std::lock_guard<std::recursive_mutex> guard(mutex); std::lock_guard<std::recursive_mutex> guard(mutex);
MidiListener listener{clientId, clientHandle, listenForControlsOnly}; MidiListener listener{clientId, clientHandle, listenForControlsOnly};
midiEventListeners.push_back(listener); midiEventListeners.push_back(listener);
jackHost->SetListenForMidiEvent(true); audioHost->SetListenForMidiEvent(true);
} }
void PiPedalModel::ListenForAtomOutputs(int64_t clientId, int64_t clientHandle, uint64_t instanceId) void PiPedalModel::ListenForAtomOutputs(int64_t clientId, int64_t clientHandle, uint64_t instanceId)
@@ -1529,7 +1618,7 @@ void PiPedalModel::ListenForAtomOutputs(int64_t clientId, int64_t clientHandle,
std::lock_guard<std::recursive_mutex> guard(mutex); std::lock_guard<std::recursive_mutex> guard(mutex);
AtomOutputListener listener{clientId, clientHandle, instanceId}; AtomOutputListener listener{clientId, clientHandle, instanceId};
atomOutputListeners.push_back(listener); atomOutputListeners.push_back(listener);
jackHost->SetListenForAtomOutput(true); audioHost->SetListenForAtomOutput(true);
} }
void PiPedalModel::CancelListenForMidiEvent(int64_t clientId, int64_t clientHandle) void PiPedalModel::CancelListenForMidiEvent(int64_t clientId, int64_t clientHandle)
@@ -1546,7 +1635,7 @@ void PiPedalModel::CancelListenForMidiEvent(int64_t clientId, int64_t clientHand
} }
if (midiEventListeners.size() == 0) if (midiEventListeners.size() == 0)
{ {
jackHost->SetListenForMidiEvent(false); audioHost->SetListenForMidiEvent(false);
} }
} }
void PiPedalModel::CancelListenForAtomOutputs(int64_t clientId, int64_t clientHandle) void PiPedalModel::CancelListenForAtomOutputs(int64_t clientId, int64_t clientHandle)
@@ -1563,7 +1652,7 @@ void PiPedalModel::CancelListenForAtomOutputs(int64_t clientId, int64_t clientHa
} }
if (midiEventListeners.size() == 0) if (midiEventListeners.size() == 0)
{ {
jackHost->SetListenForMidiEvent(false); audioHost->SetListenForMidiEvent(false);
} }
} }
std::vector<AlsaDeviceInfo> PiPedalModel::GetAlsaDevices() std::vector<AlsaDeviceInfo> PiPedalModel::GetAlsaDevices()
@@ -1600,3 +1689,31 @@ void PiPedalModel::SetFavorites(const std::map<std::string, bool> &favorites)
t[i]->OnFavoritesChanged(favorites); t[i]->OnFavoritesChanged(favorites);
} }
} }
std::vector<MidiBinding> PiPedalModel::GetSystemMidiBidings() {
std::lock_guard<std::recursive_mutex> guard(mutex);
return this->systemMidiBindings;
}
void PiPedalModel::SetSystemMidiBindings(std::vector<MidiBinding> &bindings)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
this->systemMidiBindings = bindings;
storage.SetSystemMidiBindings(bindings);
if (this->audioHost)
{
this->audioHost->SetSystemMidiBindings(bindings);
}
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
for (size_t i = 0; i < subscribers.size(); ++i)
{
t[i] = this->subscribers[i];
}
size_t n = this->subscribers.size();
for (size_t i = 0; i < n; ++i)
{
t[i]->OnSystemMidiBindingsChanged(bindings);
}
delete[] t;
}
+239 -235
View File
@@ -25,7 +25,7 @@
#include "Storage.hpp" #include "Storage.hpp"
#include "Banks.hpp" #include "Banks.hpp"
#include "JackConfiguration.hpp" #include "JackConfiguration.hpp"
#include "JackHost.hpp" #include "AudioHost.hpp"
#include "VuUpdate.hpp" #include "VuUpdate.hpp"
#include <functional> #include <functional>
#include <filesystem> #include <filesystem>
@@ -38,245 +38,249 @@
#include "AvahiService.hpp" #include "AvahiService.hpp"
#include <thread> #include <thread>
namespace pipedal
{
struct RealtimeMidiProgramRequest;
struct RealtimeNextMidiProgramRequest;
class IPiPedalModelSubscriber
{
namespace pipedal {
class IPiPedalModelSubscriber {
public:
virtual int64_t GetClientId() = 0;
virtual void OnItemEnabledChanged(int64_t clientId, int64_t pedalItemId, bool enabled) = 0;
virtual void OnControlChanged(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value) = 0;
virtual void OnVst3ControlChanged(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value, const std::string&state) = 0;
virtual void OnPedalBoardChanged(int64_t clientId, const PedalBoard &pedalBoard) = 0;
virtual void OnPresetsChanged(int64_t clientId, const PresetIndex&presets) = 0;
virtual void OnPluginPresetsChanged(const std::string&pluginUri) = 0;
virtual void OnChannelSelectionChanged(int64_t clientId, const JackChannelSelection&channelSelection) = 0;
virtual void OnVuMeterUpdate(const std::vector<VuUpdate>& updates) = 0;
virtual void OnBankIndexChanged(const BankIndex& bankIndex) = 0;
virtual void OnJackServerSettingsChanged(const JackServerSettings& jackServerSettings) = 0;
virtual void OnJackConfigurationChanged(const JackConfiguration& jackServerConfiguration) = 0;
virtual void OnLoadPluginPreset(int64_t instanceId,const std::vector<ControlValue>&controlValues) = 0;
virtual void OnMidiValueChanged(int64_t instanceId, const std::string&symbol, float value) = 0;
virtual void OnNotifyMidiListener(int64_t clientHandle, bool isNote, uint8_t noteOrControl) = 0;
virtual void OnNotifyAtomOutput(int64_t clientModel,uint64_t instanceId,const std::string&atomType, const std::string&atomJson) = 0;
virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings&wifiConfigSettings) = 0;
virtual void OnWifiDirectConfigSettingsChanged(const WifiDirectConfigSettings&wifiDirectConfigSettings) = 0;
virtual void OnGovernorSettingsChanged(const std::string &governor) = 0;
virtual void OnFavoritesChanged(const std::map<std::string,bool> &favorites) = 0;
virtual void OnShowStatusMonitorChanged(bool show) = 0;
virtual void Close() = 0;
};
class PiPedalModel: private IJackHostCallbacks {
private:
std::unique_ptr<std::jthread> pingThread;
AvahiService avahiService;
uint16_t webPort;
PiPedalAlsaDevices alsaDevices;
std::recursive_mutex mutex;
AdminClient adminClient;
class MidiListener {
public: public:
int64_t clientId; virtual int64_t GetClientId() = 0;
int64_t clientHandle; virtual void OnItemEnabledChanged(int64_t clientId, int64_t pedalItemId, bool enabled) = 0;
bool listenForControlsOnly; virtual void OnControlChanged(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value) = 0;
virtual void OnVst3ControlChanged(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value, const std::string &state) = 0;
virtual void OnPedalBoardChanged(int64_t clientId, const PedalBoard &pedalBoard) = 0;
virtual void OnPresetsChanged(int64_t clientId, const PresetIndex &presets) = 0;
virtual void OnPluginPresetsChanged(const std::string &pluginUri) = 0;
virtual void OnChannelSelectionChanged(int64_t clientId, const JackChannelSelection &channelSelection) = 0;
virtual void OnVuMeterUpdate(const std::vector<VuUpdate> &updates) = 0;
virtual void OnBankIndexChanged(const BankIndex &bankIndex) = 0;
virtual void OnJackServerSettingsChanged(const JackServerSettings &jackServerSettings) = 0;
virtual void OnJackConfigurationChanged(const JackConfiguration &jackServerConfiguration) = 0;
virtual void OnLoadPluginPreset(int64_t instanceId, const std::vector<ControlValue> &controlValues) = 0;
virtual void OnMidiValueChanged(int64_t instanceId, const std::string &symbol, float value) = 0;
virtual void OnNotifyMidiListener(int64_t clientHandle, bool isNote, uint8_t noteOrControl) = 0;
virtual void OnNotifyAtomOutput(int64_t clientModel, uint64_t instanceId, const std::string &atomType, const std::string &atomJson) = 0;
virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings &wifiConfigSettings) = 0;
virtual void OnWifiDirectConfigSettingsChanged(const WifiDirectConfigSettings &wifiDirectConfigSettings) = 0;
virtual void OnGovernorSettingsChanged(const std::string &governor) = 0;
virtual void OnFavoritesChanged(const std::map<std::string, bool> &favorites) = 0;
virtual void OnShowStatusMonitorChanged(bool show) = 0;
virtual void OnSystemMidiBindingsChanged(const std::vector<MidiBinding>&bindings) = 0;
virtual void Close() = 0;
}; };
class AtomOutputListener {
class PiPedalModel : private IAudioHostCallbacks
{
private:
std::unique_ptr<std::jthread> pingThread;
std::vector<MidiBinding> systemMidiBindings;
AvahiService avahiService;
uint16_t webPort;
PiPedalAlsaDevices alsaDevices;
std::recursive_mutex mutex;
AdminClient adminClient;
class MidiListener
{
public:
int64_t clientId;
int64_t clientHandle;
bool listenForControlsOnly;
};
class AtomOutputListener
{
public:
int64_t clientId;
int64_t clientHandle;
uint64_t instanceId;
};
void DeleteMidiListeners(int64_t clientId);
void DeleteAtomOutputListeners(int64_t clientId);
std::vector<MidiListener> midiEventListeners;
std::vector<AtomOutputListener> atomOutputListeners;
JackServerSettings jackServerSettings;
PiPedalHost lv2Host;
PedalBoard pedalBoard;
Storage storage;
bool hasPresetChanged = false;
std::unique_ptr<AudioHost> audioHost;
JackConfiguration jackConfiguration;
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard;
std::filesystem::path webRoot;
std::vector<IPiPedalModelSubscriber *> subscribers;
void SetPresetChanged(int64_t clientId, bool value);
void FirePresetsChanged(int64_t clientId);
void FirePluginPresetsChanged(const std::string &pluginUri);
void FirePedalBoardChanged(int64_t clientId);
void FireChannelSelectionChanged(int64_t clientId);
void FireBanksChanged(int64_t clientId);
void FireJackConfigurationChanged(const JackConfiguration &jackConfiguration);
void UpdateDefaults(PedalBoardItem *pedalBoardItem);
void UpdateDefaults(PedalBoard *pedalBoard);
class VuSubscription
{
public:
int64_t subscriptionHandle;
int64_t instanceid;
};
int64_t nextSubscriptionId = 1;
std::vector<VuSubscription> activeVuSubscriptions;
std::vector<MonitorPortSubscription> activeMonitorPortSubscriptions;
void UpdateRealtimeVuSubscriptions();
void UpdateRealtimeMonitorPortSubscriptions();
void OnVuUpdate(const std::vector<VuUpdate> &updates);
void RestartAudio();
std::vector<RealtimeParameterRequest *> outstandingParameterRequests;
IPiPedalModelSubscriber *GetNotificationSubscriber(int64_t clientId);
private: // IAudioHostCallbacks
virtual void OnNotifyVusSubscription(const std::vector<VuUpdate> &updates);
virtual void OnNotifyMonitorPort(const MonitorPortUpdate &update);
virtual void OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, float value);
virtual void OnNotifyMidiListen(bool isNote, uint8_t noteOrControl);
virtual void OnNotifyAtomOutput(uint64_t instanceId, const std::string &atomType, const std::string &atomJson);
virtual void OnNotifyMidiProgramChange(RealtimeMidiProgramRequest &midiProgramRequest);
virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest&request);
void UpdateVst3Settings(PedalBoard &pedalBoard);
PiPedalConfiguration configuration;
public: public:
int64_t clientId; PiPedalModel();
int64_t clientHandle; virtual ~PiPedalModel();
uint64_t instanceId;
uint16_t GetWebPort() const { return webPort; }
void Close();
void UpdateDnsSd();
AdminClient &GetAdminClient() { return adminClient; }
void Init(const PiPedalConfiguration &configuration);
void LoadLv2PluginInfo();
void Load();
const PiPedalHost &GetLv2Host() const { return lv2Host; }
PedalBoard GetCurrentPedalBoardCopy()
{
std::lock_guard<std::recursive_mutex> guard(mutex);
return pedalBoard; // can return a referece because we'd lose mutex protection
}
PluginUiPresets GetPluginUiPresets(const std::string &pluginUri);
PluginPresets GetPluginPresets(const std::string &pluginUri);
void LoadPluginPreset(int64_t pluginInstanceId, uint64_t presetInstanceId);
void AddNotificationSubscription(IPiPedalModelSubscriber *pSubscriber);
void RemoveNotificationSubsription(IPiPedalModelSubscriber *pSubscriber);
void SetPedalBoardItemEnable(int64_t clientId, int64_t instanceId, bool enabled);
void SetControl(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value);
void PreviewControl(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value);
void SetPedalBoard(int64_t clientId, PedalBoard &pedalBoard);
void UpdateCurrentPedalBoard(int64_t clientId, PedalBoard &pedalBoard);
void GetPresets(PresetIndex *pResult);
PedalBoard GetPreset(int64_t instanceId);
void GetBank(int64_t instanceId, BankFile *pBank);
int64_t UploadBank(BankFile &bankFile, int64_t uploadAfter = -1);
int64_t UploadPreset(const BankFile &bankFile, int64_t uploadAfter = -1);
void UploadPluginPresets(const PluginPresets &pluginPresets);
void SaveCurrentPreset(int64_t clientId);
int64_t SaveCurrentPresetAs(int64_t clientId, const std::string &name, int64_t saveAfterInstanceId = -1);
int64_t SavePluginPresetAs(int64_t instanceId, const std::string &name);
void LoadPreset(int64_t clientId, int64_t instanceId);
bool UpdatePresets(int64_t clientId, const PresetIndex &presets);
void UpdatePluginPresets(const PluginUiPresets &pluginPresets);
int64_t DeletePreset(int64_t clientId, int64_t instanceId);
bool RenamePreset(int64_t clientId, int64_t instanceId, const std::string &name);
int64_t CopyPreset(int64_t clientId, int64_t fromIndex, int64_t toIndex);
uint64_t CopyPluginPreset(const std::string &pluginUri, uint64_t presetId);
void MoveBank(int64_t clientId, int from, int to);
int64_t DeleteBank(int64_t clientId, int64_t instanceId);
int64_t DuplicatePreset(int64_t clientId, int64_t instanceId) { return CopyPreset(clientId, instanceId, -1); }
JackConfiguration GetJackConfiguration();
void SetJackChannelSelection(int64_t clientId, const JackChannelSelection &channelSelection);
JackChannelSelection GetJackChannelSelection();
void SetShowStatusMonitor(bool show);
bool GetShowStatusMonitor();
void SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings);
WifiConfigSettings GetWifiConfigSettings();
void SetWifiDirectConfigSettings(const WifiDirectConfigSettings &wifiConfigSettings);
WifiDirectConfigSettings GetWifiDirectConfigSettings();
void SetGovernorSettings(const std::string &governor);
GovernorSettings GetGovernorSettings();
int64_t AddVuSubscription(int64_t instanceId);
void RemoveVuSubscription(int64_t subscriptionHandle);
void SetSystemMidiBindings(std::vector<MidiBinding> &bindings);
std::vector<MidiBinding> GetSystemMidiBidings();
int64_t MonitorPort(int64_t instanceId, const std::string &key, float updateInterval, PortMonitorCallback onUpdate);
void UnmonitorPort(int64_t subscriptionHandle);
void GetLv2Parameter(
int64_t clientId,
int64_t instanceId,
const std::string uri,
std::function<void(const std::string &jsonResjult)> onSuccess,
std::function<void(const std::string &error)> onError);
BankIndex GetBankIndex() const;
void RenameBank(int64_t clientId, int64_t bankId, const std::string &newName);
int64_t SaveBankAs(int64_t clientId, int64_t bankId, const std::string &newName);
void OpenBank(int64_t clientId, int64_t bankId);
JackHostStatus GetJackStatus()
{
return this->audioHost->getJackStatus();
}
JackServerSettings GetJackServerSettings();
void SetJackServerSettings(const JackServerSettings &jackServerSettings);
void ListenForMidiEvent(int64_t clientId, int64_t clientHandle, bool listenForControlsOnly);
void CancelListenForMidiEvent(int64_t clientId, int64_t clientHandle);
void ListenForAtomOutputs(int64_t clientId, int64_t clientHandle, uint64_t instanceId);
void CancelListenForAtomOutputs(int64_t clientId, int64_t clientHandle);
std::vector<AlsaDeviceInfo> GetAlsaDevices();
const std::filesystem::path &GetWebRoot() const;
std::map<std::string, bool> GetFavorites() const;
void SetFavorites(const std::map<std::string, bool> &favorites);
}; };
void DeleteMidiListeners(int64_t clientId);
void DeleteAtomOutputListeners(int64_t clientId);
std::vector<MidiListener> midiEventListeners;
std::vector<AtomOutputListener> atomOutputListeners;
JackServerSettings jackServerSettings;
PiPedalHost lv2Host;
PedalBoard pedalBoard;
Storage storage;
bool hasPresetChanged = false;
std::unique_ptr<JackHost> jackHost;
JackConfiguration jackConfiguration;
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard;
std::filesystem::path webRoot;
std::vector<IPiPedalModelSubscriber*> subscribers;
void SetPresetChanged(int64_t clientId,bool value);
void FirePresetsChanged(int64_t clientId);
void FirePluginPresetsChanged(const std::string &pluginUri);
void FirePedalBoardChanged(int64_t clientId);
void FireChannelSelectionChanged(int64_t clientId);
void FireBanksChanged(int64_t clientId);
void FireJackConfigurationChanged(const JackConfiguration&jackConfiguration);
void UpdateDefaults(PedalBoardItem*pedalBoardItem);
void UpdateDefaults(PedalBoard*pedalBoard);
class VuSubscription {
public:
int64_t subscriptionHandle;
int64_t instanceid;
};
int64_t nextSubscriptionId = 1;
std::vector<VuSubscription> activeVuSubscriptions;
std::vector<MonitorPortSubscription> activeMonitorPortSubscriptions;
void UpdateRealtimeVuSubscriptions();
void UpdateRealtimeMonitorPortSubscriptions();
void OnVuUpdate(const std::vector<VuUpdate>& updates);
void RestartAudio();
std::vector<RealtimeParameterRequest*> outstandingParameterRequests;
IPiPedalModelSubscriber*GetNotificationSubscriber(int64_t clientId);
private: // IJackHostCallbacks
virtual void OnNotifyVusSubscription(const std::vector<VuUpdate> & updates);
virtual void OnNotifyMonitorPort(const MonitorPortUpdate &update);
virtual void OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, float value);
virtual void OnNotifyMidiListen(bool isNote, uint8_t noteOrControl);
virtual void OnNotifyAtomOutput(uint64_t instanceId, const std::string&atomType,const std::string&atomJson);
void UpdateVst3Settings(PedalBoard&pedalBoard);
PiPedalConfiguration configuration;
public:
PiPedalModel();
virtual ~PiPedalModel();
uint16_t GetWebPort() const { return webPort; }
void Close();
void UpdateDnsSd();
AdminClient&GetAdminClient() { return adminClient; }
void Init(const PiPedalConfiguration&configuration);
void LoadLv2PluginInfo();
void Load();
const PiPedalHost& GetLv2Host() const { return lv2Host; }
PedalBoard GetCurrentPedalBoardCopy()
{
std::lock_guard<std::recursive_mutex> guard(mutex);
return pedalBoard; // can return a referece because we'd lose mutex protection
}
PluginUiPresets GetPluginUiPresets(const std::string& pluginUri);
PluginPresets GetPluginPresets(const std::string&pluginUri);
void LoadPluginPreset(int64_t pluginInstanceId,uint64_t presetInstanceId);
void AddNotificationSubscription(IPiPedalModelSubscriber* pSubscriber);
void RemoveNotificationSubsription(IPiPedalModelSubscriber* pSubscriber);
void SetPedalBoardItemEnable(int64_t clientId, int64_t instanceId, bool enabled);
void SetControl(int64_t clientId,int64_t pedalItemId, const std::string&symbol,float value);
void PreviewControl(int64_t clientId,int64_t pedalItemId, const std::string&symbol,float value);
void SetPedalBoard(int64_t clientId,PedalBoard &pedalBoard);
void UpdateCurrentPedalBoard(int64_t clientId, PedalBoard &pedalBoard);
void GetPresets(PresetIndex*pResult);
PedalBoard GetPreset(int64_t instanceId);
void GetBank(int64_t instanceId, BankFile*pBank);
int64_t UploadBank(BankFile&bankFile, int64_t uploadAfter = -1);
int64_t UploadPreset(const BankFile&bankFile, int64_t uploadAfter = -1);
void UploadPluginPresets(const PluginPresets&pluginPresets);
void SaveCurrentPreset(int64_t clientId);
int64_t SaveCurrentPresetAs(int64_t clientId, const std::string& name,int64_t saveAfterInstanceId = -1);
int64_t SavePluginPresetAs(int64_t instanceId, const std::string&name);
void LoadPreset(int64_t clientId, int64_t instanceId);
bool UpdatePresets(int64_t clientId,const PresetIndex&presets);
void UpdatePluginPresets(const PluginUiPresets&pluginPresets);
int64_t DeletePreset(int64_t clientId,int64_t instanceId);
bool RenamePreset(int64_t clientId,int64_t instanceId,const std::string&name);
int64_t CopyPreset(int64_t clientId, int64_t fromIndex, int64_t toIndex);
uint64_t CopyPluginPreset(const std::string&pluginUri,uint64_t presetId);
void MoveBank(int64_t clientId,int from, int to);
int64_t DeleteBank(int64_t clientId, int64_t instanceId);
int64_t DuplicatePreset(int64_t clientId, int64_t instanceId) { return CopyPreset(clientId,instanceId,-1); }
JackConfiguration GetJackConfiguration();
void SetJackChannelSelection(int64_t clientId,const JackChannelSelection &channelSelection);
JackChannelSelection GetJackChannelSelection();
void SetShowStatusMonitor(bool show);
bool GetShowStatusMonitor();
void SetWifiConfigSettings(const WifiConfigSettings&wifiConfigSettings);
WifiConfigSettings GetWifiConfigSettings();
void SetWifiDirectConfigSettings(const WifiDirectConfigSettings&wifiConfigSettings);
WifiDirectConfigSettings GetWifiDirectConfigSettings();
void SetGovernorSettings(const std::string& governor);
GovernorSettings GetGovernorSettings();
int64_t AddVuSubscription(int64_t instanceId);
void RemoveVuSubscription(int64_t subscriptionHandle);
int64_t MonitorPort(int64_t instanceId,const std::string &key,float updateInterval, PortMonitorCallback onUpdate);
void UnmonitorPort(int64_t subscriptionHandle);
void GetLv2Parameter(
int64_t clientId,
int64_t instanceId,
const std::string uri,
std::function<void (const std::string&jsonResjult)> onSuccess,
std::function<void (const std::string& error)> onError
);
BankIndex GetBankIndex() const;
void RenameBank(int64_t clientId,int64_t bankId, const std::string& newName);
int64_t SaveBankAs(int64_t clientId,int64_t bankId, const std::string& newName);
void OpenBank(int64_t clientId,int64_t bankId);
JackHostStatus GetJackStatus() {
return this->jackHost->getJackStatus();
}
JackServerSettings GetJackServerSettings();
void SetJackServerSettings(const JackServerSettings& jackServerSettings);
void ListenForMidiEvent(int64_t clientId, int64_t clientHandle, bool listenForControlsOnly);
void CancelListenForMidiEvent(int64_t clientId, int64_t clientHandle);
void ListenForAtomOutputs(int64_t clientId, int64_t clientHandle, uint64_t instanceId);
void CancelListenForAtomOutputs(int64_t clientId, int64_t clientHandle);
std::vector<AlsaDeviceInfo> GetAlsaDevices();
const std::filesystem::path& GetWebRoot() const;
std::map<std::string,bool> GetFavorites() const;
void SetFavorites(const std::map<std::string,bool> &favorites);
};
} // namespace pipedal. } // namespace pipedal.
+16 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2022 Robin Davies // Copyright (c) 2022-2023 Robin Davies
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy of // 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 // this software and associated documentation files (the "Software"), to deal in
@@ -1235,6 +1235,17 @@ public:
pReader->read(&favorites); pReader->read(&favorites);
this->model.SetFavorites(favorites); this->model.SetFavorites(favorites);
} }
else if (message == "setSystemMidiBindings")
{
std::vector<MidiBinding> bindings;
pReader->read(&bindings);
this->model.SetSystemMidiBindings(bindings);
}
else if (message == "getSystemMidiBindings")
{
std::vector<MidiBinding> bindings = this->model.GetSystemMidiBidings();
this->Reply(replyTo,"getSystemMidiBindings",bindings);
}
else else
{ {
Lv2Log::error("Unknown message received: %s", message.c_str()); Lv2Log::error("Unknown message received: %s", message.c_str());
@@ -1308,6 +1319,10 @@ protected:
} }
public: public:
virtual void OnSystemMidiBindingsChanged(const std::vector<MidiBinding>&bindings) {
Send("onSystemMidiBindingsChanged",bindings);
}
virtual void OnFavoritesChanged(const std::map<std::string, bool> &favorites) virtual void OnFavoritesChanged(const std::map<std::string, bool> &favorites)
{ {
Send("onFavoritesChanged", favorites); Send("onFavoritesChanged", favorites);
+1 -1
View File
@@ -19,7 +19,7 @@
#pragma once #pragma once
#include "BeastServer.hpp" #include "WebServer.hpp"
#include "PiPedalModel.hpp" #include "PiPedalModel.hpp"
+42 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2022 Robin Davies // Copyright (c) 2022-2023 Robin Davies
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy of // 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 // this software and associated documentation files (the "Software"), to deal in
@@ -22,7 +22,7 @@
#include "PiPedalException.hpp" #include "PiPedalException.hpp"
#include "Lv2Log.hpp" #include "Lv2Log.hpp"
#include "VuUpdate.hpp" #include "VuUpdate.hpp"
#include "JackHost.hpp" #include "AudioHost.hpp"
namespace pipedal namespace pipedal
{ {
@@ -51,8 +51,25 @@ namespace pipedal
AtomOutput = 17, AtomOutput = 17,
MidiProgramChange = 18, // program change requested via midi.
AckMidiProgramChange = 19,
NextMidiProgram = 20,
}; };
struct RealtimeNextMidiProgramRequest {
int64_t requestId;
int32_t direction;
};
struct RealtimeMidiProgramRequest {
int64_t requestId;
int8_t bank;
uint8_t program;
};
class RealtimeMonitorPortSubscription class RealtimeMonitorPortSubscription
{ {
public: public:
@@ -304,6 +321,25 @@ namespace pipedal
write(RingBufferCommand::OnMidiListen, msg); write(RingBufferCommand::OnMidiListen, msg);
} }
/**
* @brief Notify host of a midi program change request.
*
* @param bank MIDI bank number, or -1 for current bank.
* @param program MIDI program number.
*/
void OnMidiProgramChange(int64_t _requestId,int8_t _bank,uint8_t _program)
{
RealtimeMidiProgramRequest msg { requestId:_requestId, bank: _bank,program: _program};
write(RingBufferCommand::MidiProgramChange,msg);
}
void OnNextMidiProgram(int64_t requestId,int32_t direction)
{
RealtimeNextMidiProgramRequest msg { requestId: requestId, direction:direction};
write(RingBufferCommand::NextMidiProgram,msg);
}
void SetControlValue(int effectIndex, int controlIndex, float value) void SetControlValue(int effectIndex, int controlIndex, float value)
{ {
SetControlValueBody body; SetControlValueBody body;
@@ -351,6 +387,10 @@ namespace pipedal
{ {
write(RingBufferCommand::SetVuSubscriptions, configuration); write(RingBufferCommand::SetVuSubscriptions, configuration);
} }
void AckMidiProgramRequest(int64_t requestId)
{
write(RingBufferCommand::AckMidiProgramChange,requestId);
}
void SetMonitorPortSubscriptions(RealtimeMonitorPortSubscriptions *subscriptions) void SetMonitorPortSubscriptions(RealtimeMonitorPortSubscriptions *subscriptions)
{ {
+52 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2022 Robin Davies // Copyright (c) 2022-2023 Robin Davies
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy of // 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 // this software and associated documentation files (the "Software"), to deal in
@@ -511,6 +511,7 @@ void Storage::SetPresetIndex(const PresetIndex &presets)
this->currentBank = std::move(bankFile); // deleting any stray presets while we're at it. this->currentBank = std::move(bankFile); // deleting any stray presets while we're at it.
this->SaveCurrentBank(); this->SaveCurrentBank();
} }
void Storage::GetPresetIndex(PresetIndex *pResult) void Storage::GetPresetIndex(PresetIndex *pResult)
{ {
*pResult = PresetIndex(); *pResult = PresetIndex();
@@ -523,6 +524,15 @@ void Storage::GetPresetIndex(PresetIndex *pResult)
pResult->presets().push_back(entry); pResult->presets().push_back(entry);
} }
} }
int64_t Storage::GetPresetByProgramNumber(uint8_t program)const
{
if (program >= currentBank.presets().size())
{
if (currentBank.presets().size() == 0) return -1;
program = (uint8_t)currentBank.presets().size()-1;
}
return currentBank.presets()[program]->instanceId();
}
PedalBoard Storage::GetPreset(int64_t instanceId) const PedalBoard Storage::GetPreset(int64_t instanceId) const
{ {
@@ -736,6 +746,17 @@ void Storage::MoveBank(int from, int to)
this->bankIndex.move(from, to); this->bankIndex.move(from, to);
this->SaveBankIndex(); this->SaveBankIndex();
} }
int64_t Storage::GetBankByMidiBankNumber(uint8_t bankNumber) {
auto &entries = this->bankIndex.entries();
if (bankNumber >= entries.size())
{
if (entries.size() == 0) return -1;
bankNumber = (uint8_t)(entries.size()-1);
}
return entries[bankNumber].instanceId();
}
int64_t Storage::DeleteBank(int64_t bankId) int64_t Storage::DeleteBank(int64_t bankId)
{ {
auto &entries = this->bankIndex.entries(); auto &entries = this->bankIndex.entries();
@@ -1333,6 +1354,36 @@ void Storage::SetJackServerSettings(const pipedal::JackServerSettings &jackConfi
#endif #endif
} }
void Storage::SetSystemMidiBindings(const std::vector<MidiBinding>&bindings)
{
std::filesystem::path fileName = this->dataRoot / "SystemMidiBindings.json";
std::ofstream f;
f.open(fileName);
if (f.is_open())
{
json_writer writer(f);
writer.write(bindings);
}
}
std::vector<MidiBinding> Storage::GetSystemMidiBindings()
{
std::vector<MidiBinding> result;
std::filesystem::path fileName = this->dataRoot / "SystemMidiBindings.json";
std::ifstream f;
f.open(fileName);
if (f.is_open())
{
json_reader reader(f);
reader.read(&result);
} else {
result.push_back(MidiBinding::SystemBinding("prevProgram"));
result.push_back(MidiBinding::SystemBinding("nextProgram"));
}
return result;
}
JSON_MAP_BEGIN(UserSettings) JSON_MAP_BEGIN(UserSettings)
JSON_MAP_REFERENCE(UserSettings, governor) JSON_MAP_REFERENCE(UserSettings, governor)
JSON_MAP_REFERENCE(UserSettings, showStatusMonitor) JSON_MAP_REFERENCE(UserSettings, showStatusMonitor)
+5 -1
View File
@@ -109,6 +109,7 @@ public:
void LoadUserSettings(); void LoadUserSettings();
void SaveUserSettings(); void SaveUserSettings();
void LoadBank(int64_t instanceId); void LoadBank(int64_t instanceId);
int64_t GetBankByMidiBankNumber(uint8_t bankNumber);
const PedalBoard& GetCurrentPreset(); const PedalBoard& GetCurrentPreset();
void SaveCurrentPreset(const PedalBoard&pedalBoard); void SaveCurrentPreset(const PedalBoard&pedalBoard);
int64_t SaveCurrentPresetAs(const PedalBoard&pedalBoard, const std::string&namne,int64_t saveAfterInstanceId = -1); int64_t SaveCurrentPresetAs(const PedalBoard&pedalBoard, const std::string&namne,int64_t saveAfterInstanceId = -1);
@@ -116,11 +117,12 @@ public:
void GetPresetIndex(PresetIndex*pResult); void GetPresetIndex(PresetIndex*pResult);
void SetPresetIndex(const PresetIndex &presetIndex); void SetPresetIndex(const PresetIndex &presetIndex);
PedalBoard GetPreset(int64_t instanceId) const; PedalBoard GetPreset(int64_t instanceId) const;
int64_t GetPresetByProgramNumber(uint8_t program) const;
void GetBankFile(int64_t instanceId,BankFile*pResult) const; void GetBankFile(int64_t instanceId,BankFile*pResult) const;
int64_t UploadPreset(const BankFile&bankFile, int64_t uploadAfter); int64_t UploadPreset(const BankFile&bankFile, int64_t uploadAfter);
int64_t UploadBank(BankFile&bankFile, int64_t uploadAfter); int64_t UploadBank(BankFile&bankFile, int64_t uploadAfter);
bool LoadPreset(int64_t presetId); bool LoadPreset(int64_t presetId);
int64_t DeletePreset(int64_t presetId); int64_t DeletePreset(int64_t presetId);
bool RenamePreset(int64_t presetId, const std::string&name); bool RenamePreset(int64_t presetId, const std::string&name);
@@ -170,6 +172,8 @@ public:
void SetShowStatusMonitor(bool show); void SetShowStatusMonitor(bool show);
bool GetShowStatusMonitor() const; bool GetShowStatusMonitor() const;
void SetSystemMidiBindings(const std::vector<MidiBinding>&bindings);
std::vector<MidiBinding> GetSystemMidiBindings();
}; };
+12 -12
View File
@@ -18,7 +18,7 @@
#include <strings.h> #include <strings.h>
#include "Ipv6Helpers.hpp" #include "Ipv6Helpers.hpp"
#include "BeastServer.hpp" #include "WebServer.hpp"
#include "Uri.hpp" #include "Uri.hpp"
@@ -133,7 +133,7 @@ std::string GetFromAddress(const tcp::socket &socket)
namespace pipedal namespace pipedal
{ {
class BeastServerImpl : public BeastServer class WebServerImpl : public WebServer
{ {
private: private:
int signalOnDone = -1; int signalOnDone = -1;
@@ -200,7 +200,7 @@ namespace pipedal
class WebSocketSession : public std::enable_shared_from_this<WebSocketSession>, public SocketHandler::IWriteCallback class WebSocketSession : public std::enable_shared_from_this<WebSocketSession>, public SocketHandler::IWriteCallback
{ {
BeastServerImpl *pServer; WebServerImpl *pServer;
server::connection_ptr webSocket; server::connection_ptr webSocket;
std::string fromAddress; std::string fromAddress;
std::shared_ptr<SocketHandler> socketHandler; std::shared_ptr<SocketHandler> socketHandler;
@@ -224,7 +224,7 @@ namespace pipedal
Lv2Log::info("WebSocketSession closed."); Lv2Log::info("WebSocketSession closed.");
} }
using ptr = std::shared_ptr<WebSocketSession>; using ptr = std::shared_ptr<WebSocketSession>;
WebSocketSession(BeastServerImpl *pServer, server::connection_ptr &webSocket) WebSocketSession(WebServerImpl *pServer, server::connection_ptr &webSocket)
: pServer(pServer), : pServer(pServer),
webSocket(webSocket) webSocket(webSocket)
{ {
@@ -599,9 +599,9 @@ namespace pipedal
using websocketpp::lib::placeholders::_1; using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2; using websocketpp::lib::placeholders::_2;
m_endpoint.set_open_handler(bind(&BeastServerImpl::on_open, this, _1)); m_endpoint.set_open_handler(bind(&WebServerImpl::on_open, this, _1));
m_endpoint.set_close_handler(bind(&BeastServerImpl::on_close, this, _1)); m_endpoint.set_close_handler(bind(&WebServerImpl::on_close, this, _1));
m_endpoint.set_http_handler(bind(&BeastServerImpl::on_http, this, _1)); m_endpoint.set_http_handler(bind(&WebServerImpl::on_http, this, _1));
std::string hostName = getHostName(); std::string hostName = getHostName();
if (hostName.length() != 0) if (hostName.length() != 0)
@@ -660,7 +660,7 @@ namespace pipedal
} }
} }
static void ThreadProc(BeastServerImpl *server) static void ThreadProc(WebServerImpl *server)
{ {
server->Run(); server->Run();
} }
@@ -717,7 +717,7 @@ namespace pipedal
this->pBgThread = new std::thread(ThreadProc, this); this->pBgThread = new std::thread(ThreadProc, this);
} }
BeastServerImpl(const std::string &address, int port, const char *rootPath, int threads) WebServerImpl(const std::string &address, int port, const char *rootPath, int threads)
: address(address), : address(address),
rootPath(rootPath), rootPath(rootPath),
port(port), port(port),
@@ -727,7 +727,7 @@ namespace pipedal
}; };
} // namespace pipedal } // namespace pipedal
std::shared_ptr<ISocketFactory> BeastServerImpl::GetSocketFactory(const uri &requestUri) std::shared_ptr<ISocketFactory> WebServerImpl::GetSocketFactory(const uri &requestUri)
{ {
for (auto factory : this->socket_factories) for (auto factory : this->socket_factories)
@@ -740,7 +740,7 @@ std::shared_ptr<ISocketFactory> BeastServerImpl::GetSocketFactory(const uri &req
return nullptr; return nullptr;
} }
std::shared_ptr<BeastServer> pipedal::createBeastServer(const boost::asio::ip::address &address, int port, const char *rootPath, int threads) std::shared_ptr<WebServer> pipedal::WebServer::create(const boost::asio::ip::address &address, int port, const char *rootPath, int threads)
{ {
return std::shared_ptr<BeastServer>(new BeastServerImpl(address.to_string(), port, rootPath, threads)); return std::shared_ptr<WebServer>(new WebServerImpl(address.to_string(), port, rootPath, threads));
} }
+12 -5
View File
@@ -68,10 +68,10 @@ public:
//xxx move this to HtmlHelpers. //xxx move this to HtmlHelpers.
std::string last_modified(const std::filesystem::path& path); std::string last_modified(const std::filesystem::path& path);
class BeastServerImpl; class WebServerImpl;
class SocketHandler { class SocketHandler {
friend class BeastServerImpl; friend class WebServerImpl;
public: public:
class IWriteCallback { class IWriteCallback {
@@ -213,9 +213,9 @@ public:
}; };
class BeastServer { class WebServer {
public: public:
virtual ~BeastServer() { } virtual ~WebServer() { }
virtual void SetLogHttpRequests(bool enableLogging) = 0; virtual void SetLogHttpRequests(bool enableLogging) = 0;
@@ -227,10 +227,17 @@ public:
// signalOnDone: fire the specified POSIX signal when the service thread terminates. -1 for no signal. // signalOnDone: fire the specified POSIX signal when the service thread terminates. -1 for no signal.
virtual void RunInBackground(int signalOnDone = -1) = 0; virtual void RunInBackground(int signalOnDone = -1) = 0;
static std::shared_ptr<WebServer> create(
const boost::asio::ip::address &address,
int port,
const char *rootPath,
int threads);
}; };
std::shared_ptr<BeastServer> createBeastServer(const boost::asio::ip::address &address, int port, const char *rootPath, int threads);
@@ -19,13 +19,13 @@
#include "pch.h" #include "pch.h"
#include "catch.hpp" #include "catch.hpp"
#include "BeastServer.hpp" #include "WebServer.hpp"
#include "MemDebug.hpp" #include "MemDebug.hpp"
#include <iostream> #include <iostream>
using namespace pipedal; using namespace pipedal;
TEST_CASE("BeastServer shutdown", "[beastServerShutdown][Build][Dev]") TEST_CASE("WebServer shutdown", "[webServerShutdown][Build][Dev]")
{ {
MemStats initialMemory = GetMemStats(); MemStats initialMemory = GetMemStats();
{ {
@@ -34,7 +34,7 @@ TEST_CASE("BeastServer shutdown", "[beastServerShutdown][Build][Dev]")
std::string doc_root = "."; std::string doc_root = ".";
auto const threads = 3; auto const threads = 3;
auto server = createBeastServer( auto server = WebServer::create(
address, port, doc_root.c_str(), threads); address, port, doc_root.c_str(), threads);
server->RunInBackground(); server->RunInBackground();
sleep(5); sleep(5);
+3 -3
View File
@@ -20,7 +20,7 @@
#include "pch.h" #include "pch.h"
#include "AudioConfig.hpp" #include "AudioConfig.hpp"
#include "BeastServer.hpp" #include "WebServer.hpp"
#include <iostream> #include <iostream>
#include "Lv2Log.hpp" #include "Lv2Log.hpp"
#include "ServiceConfiguration.hpp" #include "ServiceConfiguration.hpp"
@@ -601,7 +601,7 @@ int main(int argc, char *argv[])
} }
uint16_t port; uint16_t port;
std::shared_ptr<BeastServer> server; std::shared_ptr<WebServer> server;
try try
{ {
auto const address = boost::asio::ip::make_address(configuration.GetSocketServerAddress()); auto const address = boost::asio::ip::make_address(configuration.GetSocketServerAddress());
@@ -609,7 +609,7 @@ int main(int argc, char *argv[])
auto const threads = std::max<int>(1, configuration.GetThreads()); auto const threads = std::max<int>(1, configuration.GetThreads());
server = createBeastServer( server = WebServer::create(
address, port, web_root.c_str(), threads); address, port, web_root.c_str(), threads);
Lv2Log::info("Document root: %s Threads: %d", doc_root.c_str(), (int)threads); Lv2Log::info("Document root: %s Threads: %d", doc_root.c_str(), (int)threads);
+2 -2
View File
@@ -42,8 +42,8 @@ add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY)
add_executable(piddletest main.cpp pch.h UriTest.cpp JsonTest.cpp add_executable(piddletest main.cpp pch.h UriTest.cpp JsonTest.cpp
../src/Uri.cpp ../src/Uri.hpp ../src/HtmlHelper.cpp ../src/HtmlHelper.hpp BeastServerTest.cpp ../src/Uri.cpp ../src/Uri.hpp ../src/HtmlHelper.cpp ../src/HtmlHelper.hpp WebServerTest.cpp
../src/BeastServer.cpp ../src/BeastServer.hpp ../src/json.hpp ../src/json.cpp ../src/WebServer.cpp ../src/WebServer.hpp ../src/json.hpp ../src/json.cpp
../src/Lv2Host.cpp ../src/Lv2Host.hpp ../src/Lv2Host.cpp ../src/Lv2Host.hpp
../src/Lv2Log.hpp ../src/Lv2Log.cpp ../src/Lv2Log.hpp ../src/Lv2Log.cpp
../src/PluginType.hpp ../src/PluginType.cpp ../src/PluginType.hpp ../src/PluginType.cpp