Audio stability, snapshots
This commit is contained in:
Vendored
+3
-123
@@ -11,33 +11,6 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"name": "Attach to Edge",
|
||||
"port": 9222,
|
||||
"request": "attach",
|
||||
"type": "msedge",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "(gdb) Attach",
|
||||
"type": "cppdbg",
|
||||
"request": "attach",
|
||||
"program": "enter program name, for example ${workspaceFolder}/a.out",
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"description": "Set Disassembly Flavor to Intel",
|
||||
"text": "-gdb-set disassembly-flavor intel",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -67,57 +40,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "(gdb) makeRelease",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
// Resolved by CMake Tools:
|
||||
"program": "${command:cmake.launchTargetPath}",
|
||||
"args": [ ],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/../..",
|
||||
"environment": [
|
||||
{
|
||||
"name": "PATH",
|
||||
"value": "$PATH:${command:cmake.launchTargetDirectory}"
|
||||
}
|
||||
],
|
||||
"externalConsole": true,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "(gdb) downloadCounts",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
// Resolved by CMake Tools:
|
||||
"program": "${command:cmake.launchTargetPath}",
|
||||
"args": [ "--downloads" ],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/../..",
|
||||
"environment": [
|
||||
{
|
||||
"name": "PATH",
|
||||
"value": "$PATH:${command:cmake.launchTargetDirectory}"
|
||||
}
|
||||
],
|
||||
"externalConsole": true,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"name": "(gdb) pipedal_nm_p2pd",
|
||||
"type": "cppdbg",
|
||||
@@ -174,13 +96,7 @@
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"description": "Add alsa source directories.",
|
||||
"text": "directory $cd:$cwd:/usr/src/alsa-lib-1.2.8",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
|
||||
@@ -196,7 +112,7 @@
|
||||
//"[json_variants]" // subtest of your choice, or none to run all of the tests.
|
||||
//"[inverting_mutex_test]"
|
||||
// "[utf8_to_utf32]"
|
||||
"[updater]"
|
||||
"[wifi_channels_test]"
|
||||
],
|
||||
|
||||
"stopAtEntry": false,
|
||||
@@ -256,38 +172,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "(gdb) profilePlugin Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
// Resolved by CMake Tools:
|
||||
"program": "${command:cmake.launchTargetPath}",
|
||||
|
||||
"args": [ "ToobNam_Profile", "--no-profile","-w" ],
|
||||
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [
|
||||
{
|
||||
"name": "PATH",
|
||||
"value": "$PATH:${command:cmake.launchTargetDirectory}"
|
||||
},
|
||||
{
|
||||
"name": "OTHER_VALUE",
|
||||
"value": "Something something"
|
||||
}
|
||||
],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
@@ -373,15 +257,11 @@
|
||||
// Resolved by CMake Tools:
|
||||
"program": "${command:cmake.launchTargetPath}",
|
||||
"args": [
|
||||
"--prefix",
|
||||
"/usr/sbin",
|
||||
"--nosudo", // run without sudo (which will fail because of perms, but allow us to debug deeper into install code.)
|
||||
"--install"
|
||||
|
||||
//"--get-current-port"
|
||||
//"--install"
|
||||
// "--uninstall"
|
||||
//"--enable-p2p" , "CA", "PiPedalTest","12345678","14"
|
||||
//"--list-p2p-channels"
|
||||
"--list-p2p-channels"
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
|
||||
Vendored
+2
-1
@@ -97,7 +97,8 @@
|
||||
"strstream": "cpp",
|
||||
"p2p_i.h": "c",
|
||||
"p2p.h": "c",
|
||||
"hash_set": "cpp"
|
||||
"hash_set": "cpp",
|
||||
"barrier": "cpp"
|
||||
},
|
||||
"cSpell.words": [
|
||||
"Guitarix",
|
||||
|
||||
@@ -14,7 +14,7 @@ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
|
||||
if (CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
message(STATUS "Debug build")
|
||||
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -DDEBUG" )
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -DDEBUG -D_GLIBCXX_DEBUG" )
|
||||
endif()
|
||||
|
||||
# Can't get the pkg_check to work.
|
||||
|
||||
@@ -680,7 +680,7 @@ void WifiConfigSettings::ParseArguments(
|
||||
|
||||
)
|
||||
{
|
||||
this->valid_ = false;
|
||||
this->valid_ = false ;
|
||||
if (argv.size() != 4)
|
||||
{
|
||||
throw invalid_argument("Invalid number of arguments.");
|
||||
|
||||
@@ -61,3 +61,10 @@ export class FakeAndroidHost implements AndroidHostInterface
|
||||
return this.theme;
|
||||
}
|
||||
}
|
||||
export function isAndroidHosted(): boolean {
|
||||
return ((window as any).AndroidHost as AndroidHostInterface) !== undefined;
|
||||
}
|
||||
|
||||
export function getAndroidHost(): AndroidHostInterface | undefined {
|
||||
return ((window as any).AndroidHost as AndroidHostInterface);
|
||||
}
|
||||
@@ -624,7 +624,7 @@ export class PiPedalModel //implements PiPedalModel
|
||||
let atomJson = body.atomJson as any;
|
||||
this.handleNotifyPathPatchPropertyChanged(instanceId, propertyUri, atomJson);
|
||||
if (header.replyTo) {
|
||||
this.webSocket?.reply(header.replyTo, "onNotifyPatchProperty", true);
|
||||
this.webSocket?.reply(header.replyTo, "onNotifyPathPatchPropertyChanged", true);
|
||||
}
|
||||
} else if (message === "onNotifyPatchProperty") {
|
||||
let clientHandle = body.clientHandle as number;
|
||||
|
||||
@@ -51,6 +51,7 @@ import SelectThemeDialog from './SelectThemeDialog';
|
||||
import Slide, { SlideProps } from '@mui/material/Slide';
|
||||
import { createStyles, Theme } from '@mui/material/styles';
|
||||
import { WithStyles, withStyles } from '@mui/styles';
|
||||
import { canScaleWindow, getWindowScaleText } from './WindowScale';
|
||||
|
||||
|
||||
|
||||
@@ -466,8 +467,15 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
|
||||
midiSummary(): string {
|
||||
let ports = this.state.jackSettings.inputMidiDevices;
|
||||
if (ports.length === 0) return "Disabled";
|
||||
if (ports.length === 1) return ports[0].description;
|
||||
return ports.length + " channels";
|
||||
|
||||
let result = "";
|
||||
for (let port of ports) {
|
||||
if (result.length !== 0) {
|
||||
result += ", ";
|
||||
}
|
||||
result += port.description;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
handleShowWifiConfigDialog() {
|
||||
this.setState({
|
||||
@@ -571,7 +579,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
|
||||
(
|
||||
<div>
|
||||
<Typography display="block" variant="body1" color="textSecondary" style={{ paddingLeft: 24, paddingBottom: 8 }}>
|
||||
Select and configure an audio device. You may optionally configure MIDI inputs, and configure up a Wi-Fi Auto-Hotspot as well.
|
||||
Select and configure an audio device. You may optionally configure MIDI inputs, and configure a Wi-Fi Auto-Hotspot as well.
|
||||
The Auto-Hotspot feature allows you to connect to Pipedal even if you don't have access to a Wi-Fi router.
|
||||
</Typography>
|
||||
<Typography display="block" variant="body1" color="textSecondary" style={{ paddingLeft: 24, paddingBottom: 8 }}>
|
||||
@@ -678,6 +686,79 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<Divider />
|
||||
{(!this.props.onboarding) &&
|
||||
(
|
||||
<div>
|
||||
<Typography className={classes.sectionHead} display="block" variant="caption" color="secondary">
|
||||
DISPLAY
|
||||
</Typography>
|
||||
<ButtonBase
|
||||
className={classes.setting}
|
||||
onClick={() => { this.handleThemeSelection(); }} >
|
||||
<SelectHoverBackground selected={false} showHover={true} />
|
||||
<div style={{ width: "100%" }}>
|
||||
<Typography className={classes.primaryItem} display="block" variant="body2" color="textPrimary" noWrap>
|
||||
Color theme</Typography>
|
||||
<Typography className={classes.secondaryItem} display="block" variant="caption" color="textSecondary" noWrap>
|
||||
{this.model.getTheme() === ColorTheme.Dark ? "Dark" :
|
||||
(this.model.getTheme() === ColorTheme.Light ? "Light" : "System")}
|
||||
</Typography>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
|
||||
{(canScaleWindow()) &&
|
||||
(
|
||||
<ButtonBase
|
||||
className={classes.setting}
|
||||
onClick={() => { this.handleThemeSelection(); }} >
|
||||
<SelectHoverBackground selected={false} showHover={true} />
|
||||
<div style={{ width: "100%" }}>
|
||||
<Typography className={classes.primaryItem} display="block" variant="body2" color="textPrimary" noWrap>
|
||||
Scale</Typography>
|
||||
<Typography className={classes.secondaryItem} display="block" variant="caption" color="textSecondary" noWrap>
|
||||
{getWindowScaleText()}
|
||||
</Typography>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
<ButtonBase
|
||||
className={classes.setting}
|
||||
onClick={() => {
|
||||
this.model.setShowStatusMonitor(!this.state.showStatusMonitor)
|
||||
}} >
|
||||
<SelectHoverBackground selected={false} showHover={true} />
|
||||
<div style={{ width: "100%" }}>
|
||||
<div style={{
|
||||
width: "100%", display: "flex", flexDirection: "row", flexWrap: "nowrap",
|
||||
alignItems: "center", maxWidth: 400
|
||||
}}>
|
||||
<div style={{ flex: "1 1 auto" }}>
|
||||
<Typography className={classes.primaryItem} display="block" variant="body2" color="textPrimary" noWrap>
|
||||
Show status monitor on main screen.</Typography>
|
||||
</div>
|
||||
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<Switch
|
||||
checked={this.state.showStatusMonitor}
|
||||
onChange={
|
||||
(e) => { this.model.setShowStatusMonitor(e.target.checked); }
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
<Divider />
|
||||
<div >
|
||||
<Typography className={classes.sectionHead} display="block" variant="caption" color="secondary">CONNECTION</Typography>
|
||||
@@ -720,65 +801,6 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
|
||||
<Divider />
|
||||
<Typography className={classes.sectionHead} display="block" variant="caption" color="secondary">SYSTEM</Typography>
|
||||
|
||||
<ButtonBase
|
||||
style={{ display: "none" }}
|
||||
className={classes.setting} disabled={!this.state.wifiConfigSettings.valid}
|
||||
onClick={() => this.handleShowGovernorSettingsDialogDialog()} >
|
||||
<SelectHoverBackground selected={false} showHover={true} />
|
||||
<div style={{ width: "100%" }}>
|
||||
<Typography className={classes.primaryItem} display="block" variant="body2" color="textPrimary" noWrap>
|
||||
CPU Governor</Typography>
|
||||
<Typography display="block" variant="caption" noWrap color="textSecondary">
|
||||
{this.state.governorSettings.governor}
|
||||
</Typography>
|
||||
|
||||
</div>
|
||||
</ButtonBase>
|
||||
|
||||
<ButtonBase
|
||||
className={classes.setting}
|
||||
onClick={() => { this.handleThemeSelection(); }} >
|
||||
<SelectHoverBackground selected={false} showHover={true} />
|
||||
<div style={{ width: "100%" }}>
|
||||
<Typography className={classes.primaryItem} display="block" variant="body2" color="textPrimary" noWrap>
|
||||
Color theme</Typography>
|
||||
<Typography className={classes.secondaryItem} display="block" variant="caption" color="textSecondary" noWrap>
|
||||
{this.model.getTheme() === ColorTheme.Dark ? "Dark" :
|
||||
(this.model.getTheme() === ColorTheme.Light ? "Light" : "System")}
|
||||
</Typography>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
|
||||
|
||||
<ButtonBase
|
||||
className={classes.setting}
|
||||
onClick={() => {
|
||||
this.model.setShowStatusMonitor(!this.state.showStatusMonitor)
|
||||
}} >
|
||||
<SelectHoverBackground selected={false} showHover={true} />
|
||||
<div style={{ width: "100%" }}>
|
||||
<div style={{
|
||||
width: "100%", display: "flex", flexDirection: "row", flexWrap: "nowrap",
|
||||
alignItems: "center", maxWidth: 400
|
||||
}}>
|
||||
<div style={{ flex: "1 1 auto" }}>
|
||||
<Typography className={classes.primaryItem} display="block" variant="body2" color="textPrimary" noWrap>
|
||||
Show status monitor on main screen.</Typography>
|
||||
</div>
|
||||
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<Switch
|
||||
checked={this.state.showStatusMonitor}
|
||||
onChange={
|
||||
(e) => { this.model.setShowStatusMonitor(e.target.checked); }
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
|
||||
{
|
||||
this.model.enableAutoUpdate && (
|
||||
<ButtonBase
|
||||
|
||||
@@ -133,7 +133,6 @@ export default class SnapshotPanel extends ResizeResponsiveComponent<SnapshotPan
|
||||
} else {
|
||||
return window.innerWidth < 800;
|
||||
}
|
||||
return window.innerWidth < 500;
|
||||
}
|
||||
getPortraitOrientation() {
|
||||
return window.innerWidth * 2 < window.innerHeight * 3;
|
||||
|
||||
@@ -50,7 +50,7 @@ const styles = (theme: Theme) => createStyles({
|
||||
flex: "1 1 auto",
|
||||
},
|
||||
pluginTable: {
|
||||
border: "collapse",
|
||||
borderCollapse: "collapse",
|
||||
width: "100%",
|
||||
|
||||
},
|
||||
@@ -66,6 +66,13 @@ const styles = (theme: Theme) => createStyles({
|
||||
verticalAlign: "top",
|
||||
paddingTop: 12
|
||||
},
|
||||
plainRow: {
|
||||
borderWidth: "1px 0px 0px 0px", borderStyle: "solid", borderColor: "transparent"
|
||||
},
|
||||
dividerRow: {
|
||||
borderWidth: "1px 0px 0px 0px", borderStyle: "solid", borderColor: theme.palette.divider
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -118,40 +125,53 @@ export const SystemMidiBindingDialog =
|
||||
|
||||
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;
|
||||
} else if (item.symbol === "startHotspot")
|
||||
{
|
||||
displayName = "Enable Hotspot";
|
||||
instanceId = 3;
|
||||
} else if (item.symbol === "stopHotspot")
|
||||
{
|
||||
displayName = "Disable Hotspot";
|
||||
instanceId = 4;
|
||||
} else if (item.symbol === "shutdown")
|
||||
{
|
||||
displayName = "Shutdown";
|
||||
instanceId = 5;
|
||||
} else if (item.symbol === "reboot")
|
||||
{
|
||||
displayName = "Reboot";
|
||||
instanceId = 6;
|
||||
let listenInstanceId = 0;
|
||||
for (var item of this.model.systemMidiBindings.get()) {
|
||||
let displayName = "";
|
||||
let found = true;
|
||||
if (item.symbol === "nextBank") {
|
||||
displayName = "Next Bank";
|
||||
}
|
||||
|
||||
if (instanceId !== -1)
|
||||
{
|
||||
result.push(new BindingEntry(displayName,instanceId,item));
|
||||
else if (item.symbol === "prevBank") {
|
||||
displayName = "Previous Bank";
|
||||
}
|
||||
else if (item.symbol === "nextProgram") {
|
||||
displayName = "Next Preset";
|
||||
}else if (item.symbol === "prevProgram") {
|
||||
displayName = "Previous Preset";
|
||||
}
|
||||
else if (item.symbol === "snapshot1") {
|
||||
displayName = "Snapshot 1";
|
||||
}
|
||||
else if (item.symbol === "snapshot2") {
|
||||
displayName = "Snapshot 2";
|
||||
}
|
||||
else if (item.symbol === "snapshot3") {
|
||||
displayName = "Snapshot 3";
|
||||
}
|
||||
else if (item.symbol === "snapshot4") {
|
||||
displayName = "Snapshot 4";
|
||||
}
|
||||
else if (item.symbol === "snapshot5") {
|
||||
displayName = "Snapshot 5";
|
||||
}
|
||||
else if (item.symbol === "snapshot6") {
|
||||
displayName = "Snapshot 6";
|
||||
} else if (item.symbol === "startHotspot") {
|
||||
displayName = "Enable Hotspot";
|
||||
} else if (item.symbol === "stopHotspot") {
|
||||
displayName = "Disable Hotspot";
|
||||
} else if (item.symbol === "shutdown") {
|
||||
displayName = "Shutdown";
|
||||
} else if (item.symbol === "reboot") {
|
||||
displayName = "Reboot";
|
||||
} else {
|
||||
found = false;
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
result.push(new BindingEntry(displayName, listenInstanceId, item));
|
||||
++listenInstanceId;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -174,8 +194,7 @@ export const SystemMidiBindingDialog =
|
||||
clearTimeout(this.listenTimeoutHandle);
|
||||
this.listenTimeoutHandle = undefined;
|
||||
}
|
||||
if (this.listenHandle)
|
||||
{
|
||||
if (this.listenHandle) {
|
||||
this.model.cancelListenForMidiEvent(this.listenHandle)
|
||||
this.listenHandle = undefined;
|
||||
}
|
||||
@@ -184,14 +203,11 @@ export const SystemMidiBindingDialog =
|
||||
|
||||
}
|
||||
|
||||
handleListenSucceeded(instanceId: number, symbol: string, isNote: boolean, noteOrControl: number)
|
||||
{
|
||||
handleListenSucceeded(instanceId: number, symbol: string, isNote: boolean, noteOrControl: number) {
|
||||
this.cancelListenForControl();
|
||||
|
||||
for (var binding of this.state.systemMidiBindings)
|
||||
{
|
||||
if (binding.instanceId === instanceId)
|
||||
{
|
||||
for (var binding of this.state.systemMidiBindings) {
|
||||
if (binding.instanceId === instanceId) {
|
||||
let newBinding = binding.midiBinding.clone();
|
||||
|
||||
if (isNote) {
|
||||
@@ -201,8 +217,8 @@ export const SystemMidiBindingDialog =
|
||||
newBinding.bindingType = MidiBinding.BINDING_TYPE_CONTROL;
|
||||
newBinding.control = noteOrControl;
|
||||
}
|
||||
|
||||
this.model.setSystemMidiBinding(instanceId,newBinding);
|
||||
|
||||
this.model.setSystemMidiBinding(instanceId, newBinding);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -218,9 +234,9 @@ export const SystemMidiBindingDialog =
|
||||
|
||||
this.listenHandle = this.model.listenForMidiEvent(listenForControl,
|
||||
(isNote: boolean, noteOrControl: number) => {
|
||||
this.handleListenSucceeded(instanceId,symbol,isNote, noteOrControl);
|
||||
this.handleListenSucceeded(instanceId, symbol, isNote, noteOrControl);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -229,7 +245,7 @@ export const SystemMidiBindingDialog =
|
||||
}
|
||||
|
||||
onMidiBindingsChanged() {
|
||||
this.setState({systemMidiBindings: this.createBindings()});
|
||||
this.setState({ systemMidiBindings: this.createBindings() });
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -256,10 +272,20 @@ export const SystemMidiBindingDialog =
|
||||
let result: React.ReactNode[] = [];
|
||||
|
||||
let items = this.state.systemMidiBindings;
|
||||
|
||||
|
||||
for (var item of items) {
|
||||
let symbol = item.midiBinding.symbol;
|
||||
let hasDivider = symbol === "snapshot1" || symbol === "stopHotspot" || symbol === "shotdown";
|
||||
if (hasDivider)
|
||||
{
|
||||
result.push(
|
||||
<tr>
|
||||
<td colSpan={2} className={classes.dividerRow}><div style={{height: 1}} /></td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
result.push(
|
||||
<tr key={item.instanceId}>
|
||||
<tr key={item.instanceId} >
|
||||
<td className={classes.nameTd}>
|
||||
<Typography noWrap style={{ verticalAlign: "center", height: 48 }}>
|
||||
{item.displayName}
|
||||
@@ -268,8 +294,7 @@ export const SystemMidiBindingDialog =
|
||||
<td className={classes.bindingTd}>
|
||||
<SystemMidiBindingView instanceId={item.instanceId} midiBinding={item.midiBinding}
|
||||
onListen={(instanceId: number, symbol: string, listenForControl: boolean) => {
|
||||
if (instanceId === -2)
|
||||
{
|
||||
if (instanceId === -2) {
|
||||
this.cancelListenForControl();
|
||||
} else {
|
||||
this.handleListenForControl(instanceId, symbol, listenForControl);
|
||||
@@ -300,52 +325,52 @@ export const SystemMidiBindingDialog =
|
||||
}
|
||||
|
||||
return (
|
||||
<DialogEx tag="systemMidiBindings" open={open} fullWidth onClose={this.handleClose} aria-labelledby="Rename-dialog-title"
|
||||
fullScreen={true}
|
||||
style={{userSelect: "none"}}
|
||||
<DialogEx tag="systemMidiBindings" 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 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>
|
||||
<Snackbar
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'left',
|
||||
}}
|
||||
open={this.state.listenSnackbarOpen}
|
||||
autoHideDuration={1500}
|
||||
onClose={() => this.setState({ listenSnackbarOpen: false })}
|
||||
message="Listening for MIDI input"
|
||||
/>
|
||||
<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 >
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
// converts android handling of the virtual keyboard to use pan instead of zoom.
|
||||
|
||||
import { isAndroidHosted } from './AndroidHost';
|
||||
import Rectangle from './Rectangle';
|
||||
|
||||
|
||||
@@ -45,7 +46,7 @@ export default class VirtualKeyboardHandler
|
||||
}
|
||||
}
|
||||
|
||||
if ('visualViewport' in window) {
|
||||
if ('visualViewport' in window && isAndroidHosted()) {
|
||||
window.visualViewport?.addEventListener('resize', this.handleVisualViewportResize.bind(this));
|
||||
} else {
|
||||
enabled = false;
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import {isAndroidHosted} from './AndroidHost';
|
||||
|
||||
|
||||
var validScales = [1.0,1.10,1.25,1.35,1.50,1.75,2.0,2.5];
|
||||
|
||||
export function canScaleWindow(): boolean {
|
||||
return getValidWindowScales().length > 1;
|
||||
}
|
||||
|
||||
export function getValidWindowScales(): number[]
|
||||
{
|
||||
if (isAndroidHosted())
|
||||
{
|
||||
return [1.0];
|
||||
}
|
||||
let result: number[] = [];
|
||||
let minDimension = Math.min(window.innerHeight,window.innerWidth);
|
||||
|
||||
for (let validScale of validScales)
|
||||
{
|
||||
if (minDimension/validScale > 400)
|
||||
{
|
||||
result.push(validScale);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function setWindowScale(scale: number): void {
|
||||
localStorage.setItem("pipedalWindowScale", scale.toString());
|
||||
}
|
||||
export function getWindowScale(): number {
|
||||
const strvalue = localStorage.getItem("pipedalWindowScale");
|
||||
return strvalue ? parseFloat(strvalue) : 1.0;
|
||||
}
|
||||
|
||||
export function getWindowScaleText(scale?: number)
|
||||
{
|
||||
let value: number;
|
||||
if (scale)
|
||||
{
|
||||
value = scale;
|
||||
} else {
|
||||
value = getWindowScale();
|
||||
}
|
||||
let iValue = Math.round(value*100);
|
||||
return iValue.toString() + "%";
|
||||
}
|
||||
+155
-155
@@ -189,14 +189,23 @@ namespace pipedal
|
||||
|
||||
AudioDriverHost *driverHost = nullptr;
|
||||
|
||||
|
||||
void validate_capture_handle() { // leftover debugging for a buffer overrun :-/
|
||||
// if (snd_pcm_type(captureHandle) != SND_PCM_TYPE_HW)
|
||||
// {
|
||||
// throw std::runtime_error("Capture handle has been overwritten");
|
||||
// }
|
||||
}
|
||||
public:
|
||||
AlsaDriverImpl(AudioDriverHost *driverHost)
|
||||
: driverHost(driverHost)
|
||||
{
|
||||
#ifdef ALSADRIVER_CONFIG_DBG
|
||||
snd_output_stdio_attach(&snd_output, stdout, 0);
|
||||
snd_pcm_status_malloc(&snd_status);
|
||||
#endif
|
||||
midiEventMemory.resize(MAX_MIDI_EVENT * MAX_MIDI_EVENT_SIZE);
|
||||
midiEvents.resize(MAX_MIDI_EVENT);
|
||||
for (size_t i = 0; i < midiEvents.size(); ++i)
|
||||
{
|
||||
midiEvents[i].buffer = midiEventMemory.data() + i * MAX_MIDI_EVENT_SIZE;
|
||||
}
|
||||
}
|
||||
virtual ~AlsaDriverImpl()
|
||||
{
|
||||
@@ -302,15 +311,14 @@ namespace pipedal
|
||||
snd_pcm_sw_params_free(playbackSwParams);
|
||||
playbackSwParams = nullptr;
|
||||
}
|
||||
for (auto *midiState : this->midiStates)
|
||||
for (auto &midiState : this->midiDevices)
|
||||
{
|
||||
if (midiState != nullptr)
|
||||
if (midiState)
|
||||
{
|
||||
midiState->Close();
|
||||
delete midiState;
|
||||
}
|
||||
}
|
||||
midiStates.resize(0);
|
||||
midiDevices.resize(0);
|
||||
}
|
||||
|
||||
std::string discover_alsa_using_apps()
|
||||
@@ -1234,17 +1242,25 @@ namespace pipedal
|
||||
|
||||
void FillOutputBuffer()
|
||||
{
|
||||
validate_capture_handle();
|
||||
|
||||
memset(rawPlaybackBuffer.data(), 0, playbackFrameSize * bufferSize);
|
||||
int retry = 0;
|
||||
while (true)
|
||||
{
|
||||
auto avail = snd_pcm_avail(this->playbackHandle);
|
||||
if (avail < 0)
|
||||
{
|
||||
if (++retry >= 5) // kinda sus code. let's make sure we don't spin forever.
|
||||
{
|
||||
throw std::runtime_error("Timed out trying to fill the audio output buffer.");
|
||||
}
|
||||
int err = snd_pcm_prepare(playbackHandle);
|
||||
if (err < 0)
|
||||
{
|
||||
throw PiPedalStateException(SS("Audio playback failed. " << snd_strerror(err)));
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
continue;
|
||||
}
|
||||
if (avail == 0)
|
||||
@@ -1258,9 +1274,12 @@ namespace pipedal
|
||||
throw PiPedalStateException(SS("Audio playback failed. " << snd_strerror(err)));
|
||||
}
|
||||
}
|
||||
validate_capture_handle();
|
||||
|
||||
}
|
||||
void recover_from_output_underrun(snd_pcm_t *capture_handle, snd_pcm_t *playback_handle, int err)
|
||||
{
|
||||
validate_capture_handle();
|
||||
if (err == -EPIPE)
|
||||
{
|
||||
err = snd_pcm_prepare(playback_handle);
|
||||
@@ -1269,13 +1288,17 @@ namespace pipedal
|
||||
throw PiPedalStateException(SS("Can't recover from ALSA output underrun. (" << snd_strerror(err) << ")"));
|
||||
}
|
||||
FillOutputBuffer();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
throw PiPedalStateException(SS("Can't recover from ALSA output error. (" << snd_strerror(err) << ")"));
|
||||
}
|
||||
|
||||
validate_capture_handle();
|
||||
}
|
||||
void recover_from_input_underrun(snd_pcm_t *capture_handle, snd_pcm_t *playback_handle, int err)
|
||||
{
|
||||
validate_capture_handle();
|
||||
|
||||
if (err == -EPIPE)
|
||||
{
|
||||
|
||||
@@ -1317,10 +1340,13 @@ namespace pipedal
|
||||
{
|
||||
throw std::runtime_error(SS("Cannot restart capture stream: " << snd_strerror(err)));
|
||||
}
|
||||
validate_capture_handle();
|
||||
}
|
||||
else if (err == ESTRPIPE)
|
||||
{
|
||||
audioRunning = false;
|
||||
validate_capture_handle();
|
||||
|
||||
while ((err = snd_pcm_resume(capture_handle)) == -EAGAIN)
|
||||
{
|
||||
sleep(1);
|
||||
@@ -1334,9 +1360,11 @@ namespace pipedal
|
||||
}
|
||||
}
|
||||
audioRunning = true;
|
||||
validate_capture_handle();
|
||||
|
||||
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(SS("Can't restart audio: " << snd_strerror(err)));
|
||||
}
|
||||
}
|
||||
@@ -1359,9 +1387,10 @@ namespace pipedal
|
||||
// transcode to jack format.
|
||||
// expand running status if neccessary.
|
||||
// deal with regular and sysex messages split across
|
||||
// buffer boundaries.
|
||||
// buffer boundaries (but discard them)
|
||||
snd_pcm_sframes_t framesRead;
|
||||
|
||||
|
||||
auto state = snd_pcm_state(handle);
|
||||
auto frame_bytes = this->captureFrameSize;
|
||||
do
|
||||
@@ -1384,6 +1413,18 @@ namespace pipedal
|
||||
return framesRead;
|
||||
}
|
||||
|
||||
void ReadMidiData(uint32_t audioFrame)
|
||||
{
|
||||
for (size_t i = 0; i < midiDevices.size(); ++i)
|
||||
{
|
||||
size_t nRead = midiDevices[i]->ReadMidiEvents(
|
||||
this->midiEvents,
|
||||
midiEventCount,
|
||||
audioFrame);
|
||||
midiEventCount += nRead;
|
||||
}
|
||||
}
|
||||
|
||||
long WriteBuffer(snd_pcm_t *handle, uint8_t *buf, size_t frames)
|
||||
{
|
||||
long framesRead;
|
||||
@@ -1448,34 +1489,42 @@ namespace pipedal
|
||||
cpuUse.SetStartTime(cpuUse.Now());
|
||||
while (true)
|
||||
{
|
||||
validate_capture_handle();
|
||||
cpuUse.UpdateCpuUse();
|
||||
|
||||
if (terminateAudio())
|
||||
{
|
||||
break;
|
||||
}
|
||||
this->midiEventCount = 0;
|
||||
|
||||
// snd_pcm_wait(captureHandle, 1);
|
||||
ssize_t framesToRead = bufferSize;
|
||||
ssize_t framesRead = 0;
|
||||
bool xrun = false;
|
||||
validate_capture_handle();
|
||||
|
||||
while (framesToRead != 0)
|
||||
{
|
||||
ReadMidiData((uint32_t)framesRead);
|
||||
|
||||
ssize_t thisTime = framesToRead;
|
||||
ssize_t nFrames;
|
||||
if ((nFrames = ReadBuffer(
|
||||
captureHandle,
|
||||
this->rawCaptureBuffer.data() + this->captureFrameSize * framesRead,
|
||||
bufferSize)) < 0)
|
||||
framesToRead)) < 0)
|
||||
{
|
||||
this->driverHost->OnUnderrun();
|
||||
recover_from_input_underrun(captureHandle, playbackHandle,nFrames);
|
||||
recover_from_input_underrun(captureHandle, playbackHandle, nFrames);
|
||||
xrun = true;
|
||||
break;
|
||||
}
|
||||
framesRead += nFrames;
|
||||
framesToRead -= nFrames;
|
||||
}
|
||||
validate_capture_handle();
|
||||
|
||||
if (xrun)
|
||||
{
|
||||
continue;
|
||||
@@ -1504,7 +1553,7 @@ namespace pipedal
|
||||
if (err < 0)
|
||||
{
|
||||
this->driverHost->OnUnderrun();
|
||||
recover_from_output_underrun(captureHandle, playbackHandle,err);
|
||||
recover_from_output_underrun(captureHandle, playbackHandle, err);
|
||||
}
|
||||
cpuUse.AddSample(ProfileCategory::Write);
|
||||
}
|
||||
@@ -1618,11 +1667,16 @@ namespace pipedal
|
||||
Lv2Log::debug("Audio thread joined.");
|
||||
}
|
||||
|
||||
static constexpr size_t MAX_MIDI_EVENT_SIZE = 3;
|
||||
static constexpr size_t MIDI_BUFFER_SIZE = 16 * 1024;
|
||||
static constexpr size_t MAX_MIDI_EVENT = 4 * 1024;
|
||||
|
||||
size_t midiEventCount = 0;
|
||||
std::vector<MidiEvent> midiEvents;
|
||||
std::vector<uint8_t> midiEventMemory;
|
||||
|
||||
public:
|
||||
class MidiState
|
||||
class AlsaMidiDeviceImpl
|
||||
{
|
||||
private:
|
||||
snd_rawmidi_t *hIn = nullptr;
|
||||
@@ -1637,15 +1691,12 @@ namespace pipedal
|
||||
size_t data0 = 0;
|
||||
size_t data1 = 0;
|
||||
|
||||
size_t eventCount = 0;
|
||||
MidiEvent events[MAX_MIDI_EVENT];
|
||||
size_t bufferCount = 0;
|
||||
uint8_t buffer[MIDI_BUFFER_SIZE];
|
||||
bool inputProcessingSysex = false;
|
||||
size_t inputSysexBufferCount = 0;
|
||||
std::vector<uint8_t> inputSysexBuffer;
|
||||
|
||||
uint8_t readBuffer[1024];
|
||||
|
||||
ssize_t sysexStartIndex = -1;
|
||||
|
||||
void checkError(int result, const char *message)
|
||||
{
|
||||
if (result < 0)
|
||||
@@ -1655,12 +1706,16 @@ namespace pipedal
|
||||
}
|
||||
|
||||
public:
|
||||
AlsaMidiDeviceImpl()
|
||||
{
|
||||
inputSysexBuffer.resize(1024);
|
||||
}
|
||||
void Open(const AlsaMidiDeviceInfo &device)
|
||||
{
|
||||
bufferCount = 0;
|
||||
eventCount = 0;
|
||||
sysexStartIndex = -1;
|
||||
runningStatus = 0;
|
||||
inputProcessingSysex = false;
|
||||
inputSysexBufferCount = 0;
|
||||
|
||||
dataIndex = 0;
|
||||
dataLength = 0;
|
||||
|
||||
@@ -1701,73 +1756,29 @@ namespace pipedal
|
||||
return sDataLength[cc >> 4];
|
||||
}
|
||||
|
||||
size_t GetMidiInputEventCount()
|
||||
{
|
||||
return eventCount;
|
||||
}
|
||||
|
||||
void NextEventBuffer()
|
||||
{
|
||||
// xxx preserve unflushed sysex data.
|
||||
if (sysexStartIndex != -1)
|
||||
{
|
||||
int end = bufferCount;
|
||||
bufferCount = 0;
|
||||
eventCount = 0;
|
||||
for (int i = sysexStartIndex; i < end; ++i)
|
||||
{
|
||||
buffer[bufferCount++] = buffer[i];
|
||||
}
|
||||
sysexStartIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bufferCount = 0;
|
||||
eventCount = 0;
|
||||
}
|
||||
}
|
||||
bool GetMidiInputEvent(MidiEvent *event, size_t nFrame)
|
||||
{
|
||||
if (nFrame >= eventCount)
|
||||
return false;
|
||||
*event = this->events[nFrame];
|
||||
return true;
|
||||
}
|
||||
|
||||
void MidiPut(uint8_t cc, uint8_t d0, uint8_t d1)
|
||||
{
|
||||
if (cc == 0)
|
||||
return;
|
||||
|
||||
// check for overrun.
|
||||
if (bufferCount + 1 + dataLength >= sizeof(buffer))
|
||||
{
|
||||
bufferCount = sizeof(buffer);
|
||||
return;
|
||||
}
|
||||
if (eventCount >= MAX_MIDI_EVENT)
|
||||
if (inputEventBufferIndex >= pInputEventBuffer->size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto *event = &(this->events[eventCount++]);
|
||||
auto &event = (*pInputEventBuffer)[inputEventBufferIndex];
|
||||
|
||||
event->time = 0;
|
||||
event->buffer = &buffer[bufferCount];
|
||||
event->size = dataLength + 1;
|
||||
|
||||
buffer[bufferCount++] = cc;
|
||||
if (dataLength >= 1)
|
||||
{
|
||||
buffer[bufferCount++] = d0;
|
||||
if (dataLength >= 2)
|
||||
{
|
||||
buffer[bufferCount++] = d1;
|
||||
}
|
||||
}
|
||||
event.time = inputSampleFrame;
|
||||
event.size = dataLength + 1;
|
||||
assert(dataLength + 1 <= MAX_MIDI_EVENT_SIZE);
|
||||
event.buffer[0] = cc;
|
||||
event.buffer[1] = d0;
|
||||
event.buffer[2] = d1;
|
||||
++inputEventBufferIndex;
|
||||
}
|
||||
|
||||
void FillBuffer()
|
||||
void FillInputBuffer()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
@@ -1778,23 +1789,42 @@ namespace pipedal
|
||||
{
|
||||
checkError(nRead, SS(this->deviceName << "MIDI event read failed. (" << snd_strerror(nRead)).c_str());
|
||||
}
|
||||
WriteBuffer(readBuffer, nRead); // expose write to test code.
|
||||
ProcessInputBuffer(readBuffer, nRead); // expose write to test code.
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t inputSampleFrame = -1;
|
||||
size_t inputEventBufferIndex;
|
||||
std::vector<MidiEvent> *pInputEventBuffer = nullptr;
|
||||
|
||||
size_t ReadMidiEvents(
|
||||
std::vector<MidiEvent> &outputBuffer,
|
||||
size_t startIndex,
|
||||
uint32_t sampleFrame)
|
||||
{
|
||||
inputSampleFrame = sampleFrame;
|
||||
inputEventBufferIndex = startIndex;
|
||||
pInputEventBuffer = &outputBuffer;
|
||||
FillInputBuffer();
|
||||
pInputEventBuffer = nullptr;
|
||||
return inputEventBufferIndex - startIndex;
|
||||
}
|
||||
|
||||
void FlushSysex()
|
||||
{
|
||||
if (sysexStartIndex != -1)
|
||||
if (inputProcessingSysex)
|
||||
{
|
||||
if (this->eventCount != MAX_MIDI_EVENT)
|
||||
{
|
||||
auto *event = &(events[eventCount++]);
|
||||
event->size = this->bufferCount - sysexStartIndex;
|
||||
event->buffer = &(this->buffer[this->sysexStartIndex]);
|
||||
event->time = 0;
|
||||
}
|
||||
sysexStartIndex = -1;
|
||||
// just discard it. :-/
|
||||
// if (this->eventCount != MAX_MIDI_EVENT)
|
||||
// {
|
||||
// auto *event = &(events[eventCount++]);
|
||||
// event->size = this->bufferCount - sysexStartIndex;
|
||||
// event->buffer = &(this->buffer[this->sysexStartIndex]);
|
||||
// event->time = 0;
|
||||
// }
|
||||
// sysexStartIndex = -1;
|
||||
}
|
||||
inputProcessingSysex = false;
|
||||
}
|
||||
|
||||
int GetSystemCommonLength(uint8_t cc)
|
||||
@@ -1802,7 +1832,7 @@ namespace pipedal
|
||||
static int sizes[] = {-1, 1, 2, 1, -1, -1, 0, 0};
|
||||
return sizes[(cc >> 4) & 0x07];
|
||||
}
|
||||
void WriteBuffer(uint8_t *readBuffer, size_t nRead)
|
||||
void ProcessInputBuffer(uint8_t *readBuffer, size_t nRead)
|
||||
{
|
||||
for (ssize_t i = 0; i < nRead; ++i)
|
||||
{
|
||||
@@ -1814,13 +1844,10 @@ namespace pipedal
|
||||
{
|
||||
if (v == 0xF0)
|
||||
{
|
||||
if (bufferCount == sizeof(buffer))
|
||||
{
|
||||
break;
|
||||
}
|
||||
sysexStartIndex = bufferCount;
|
||||
inputProcessingSysex = true;
|
||||
inputSysexBufferCount = 0;
|
||||
|
||||
buffer[bufferCount++] = 0xF0;
|
||||
inputSysexBuffer[inputSysexBufferCount++] = 0xF0;
|
||||
|
||||
runningStatus = 0; // discard subsequent data.
|
||||
dataLength = -2; // indefinitely.
|
||||
@@ -1863,11 +1890,11 @@ namespace pipedal
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sysexStartIndex != -1)
|
||||
if (inputProcessingSysex)
|
||||
{
|
||||
if (bufferCount != sizeof(buffer))
|
||||
if (inputSysexBufferCount != inputSysexBuffer.size())
|
||||
{
|
||||
buffer[bufferCount++] = v;
|
||||
inputSysexBuffer[inputSysexBufferCount++] = v;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1888,7 +1915,7 @@ namespace pipedal
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dataIndex == dataLength)
|
||||
if (dataIndex == dataLength && dataLength >= 0 && runningStatus != 0)
|
||||
{
|
||||
MidiPut(runningStatus, data0, data1);
|
||||
dataIndex = 0;
|
||||
@@ -1897,70 +1924,40 @@ namespace pipedal
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<MidiState *> midiStates;
|
||||
std::vector<std::unique_ptr<AlsaMidiDeviceImpl>> midiDevices;
|
||||
|
||||
void OpenMidi(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
|
||||
{
|
||||
const auto &devices = channelSelection.GetInputMidiDevices();
|
||||
|
||||
midiStates.reserve(devices.size());
|
||||
midiDevices.reserve(devices.size());
|
||||
|
||||
for (size_t i = 0; i < devices.size(); ++i)
|
||||
{
|
||||
const auto &device = devices[i];
|
||||
MidiState *midiState = nullptr;
|
||||
try
|
||||
{
|
||||
midiState = new MidiState();
|
||||
midiState->Open(device);
|
||||
midiStates.push_back(midiState);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
// logged already.
|
||||
delete midiState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual size_t MidiInputBufferCount() const
|
||||
{
|
||||
return this->midiStates.size();
|
||||
}
|
||||
virtual void *GetMidiInputBuffer(size_t channel, size_t nFrames)
|
||||
{
|
||||
return (void *)midiStates[channel];
|
||||
}
|
||||
|
||||
virtual size_t GetMidiInputEventCount(void *portBuffer)
|
||||
{
|
||||
MidiState *state = (MidiState *)portBuffer;
|
||||
return state->GetMidiInputEventCount();
|
||||
}
|
||||
virtual bool GetMidiInputEvent(MidiEvent *event, void *portBuf, size_t nFrame)
|
||||
{
|
||||
MidiState *state = (MidiState *)portBuf;
|
||||
return state->GetMidiInputEvent(event, nFrame);
|
||||
}
|
||||
|
||||
virtual void FillMidiBuffers()
|
||||
{
|
||||
for (size_t i = 0; i < this->midiStates.size(); ++i)
|
||||
{
|
||||
auto *state = midiStates[i];
|
||||
state->NextEventBuffer();
|
||||
state->FillBuffer();
|
||||
auto midiDevice = std::make_unique<AlsaMidiDeviceImpl>();
|
||||
midiDevice->Open(device);
|
||||
midiDevices.push_back(std::move(midiDevice));
|
||||
}
|
||||
}
|
||||
|
||||
virtual size_t InputBufferCount() const { return activeCaptureBuffers.size(); }
|
||||
virtual float *GetInputBuffer(size_t channel, size_t nFrames)
|
||||
virtual float *GetInputBuffer(size_t channel) override
|
||||
{
|
||||
return activeCaptureBuffers[channel];
|
||||
}
|
||||
|
||||
virtual size_t GetMidiInputEventCount() override
|
||||
{
|
||||
return midiEventCount;
|
||||
}
|
||||
virtual MidiEvent *GetMidiEvents() override
|
||||
{
|
||||
return this->midiEvents.data();
|
||||
}
|
||||
|
||||
virtual size_t OutputBufferCount() const { return activePlaybackBuffers.size(); }
|
||||
virtual float *GetOutputBuffer(size_t channel, size_t nFrames)
|
||||
virtual float *GetOutputBuffer(size_t channel) override
|
||||
{
|
||||
return activePlaybackBuffers[channel];
|
||||
}
|
||||
@@ -2158,7 +2155,8 @@ namespace pipedal
|
||||
throw PiPedalStateException("Assert failed.");
|
||||
}
|
||||
|
||||
static void ExpectEvent(AlsaDriverImpl::MidiState &m, int event, const std::vector<uint8_t> message)
|
||||
#ifdef JUNK
|
||||
static void ExpectEvent(AlsaDriverImpl::AlsaMidiDeviceImpl &m, int event, const std::vector<uint8_t> message)
|
||||
{
|
||||
MidiEvent e;
|
||||
m.GetMidiInputEvent(&e, event);
|
||||
@@ -2168,6 +2166,7 @@ namespace pipedal
|
||||
AlsaAssert(message[i] == e.buffer[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void AlsaDriverImpl::TestFormatEncodeDecode(snd_pcm_format_t captureFormat)
|
||||
{
|
||||
@@ -2243,8 +2242,8 @@ namespace pipedal
|
||||
}
|
||||
void MidiDecoderTest()
|
||||
{
|
||||
|
||||
AlsaDriverImpl::MidiState midiState;
|
||||
#ifdef JUNK
|
||||
AlsaDriverImpl::AlsaMidiDeviceImpl midiState;
|
||||
|
||||
MidiEvent event;
|
||||
|
||||
@@ -2252,7 +2251,7 @@ namespace pipedal
|
||||
{
|
||||
static uint8_t m0[] = {0x80, 0x1, 0x2, 0x3, 0x4, 0x5};
|
||||
midiState.NextEventBuffer();
|
||||
midiState.WriteBuffer(m0, sizeof(m0));
|
||||
midiState.ProcessInputBuffer(m0, sizeof(m0));
|
||||
AlsaAssert(midiState.GetMidiInputEventCount() == 2);
|
||||
AlsaAssert(midiState.GetMidiInputEvent(&event, 0));
|
||||
|
||||
@@ -2261,7 +2260,7 @@ namespace pipedal
|
||||
|
||||
static uint8_t m1[] = {0x06, 0xC0, 0x1, 0x2};
|
||||
midiState.NextEventBuffer();
|
||||
midiState.WriteBuffer(m1, sizeof(m1));
|
||||
midiState.ProcessInputBuffer(m1, sizeof(m1));
|
||||
AlsaAssert(midiState.GetMidiInputEventCount() == 3);
|
||||
ExpectEvent(midiState, 0, {0x80, 0x05, 0x06});
|
||||
ExpectEvent(midiState, 1, {0xC0, 0x1});
|
||||
@@ -2272,7 +2271,7 @@ namespace pipedal
|
||||
{
|
||||
static uint8_t m0[] = {0xF0, 0x76, 0xF7, 0xA};
|
||||
midiState.NextEventBuffer();
|
||||
midiState.WriteBuffer(m0, 4);
|
||||
midiState.ProcessInputBuffer(m0, 4);
|
||||
AlsaAssert(midiState.GetMidiInputEventCount() == 2);
|
||||
AlsaAssert(midiState.GetMidiInputEvent(&event, 0));
|
||||
AlsaAssert(event.size == 2);
|
||||
@@ -2284,11 +2283,11 @@ namespace pipedal
|
||||
{
|
||||
static uint8_t m0[] = {0xF0, 0x76, 0x3B};
|
||||
midiState.NextEventBuffer();
|
||||
midiState.WriteBuffer(m0, sizeof(m0));
|
||||
midiState.ProcessInputBuffer(m0, sizeof(m0));
|
||||
AlsaAssert(midiState.GetMidiInputEventCount() == 0);
|
||||
static uint8_t m1[] = {0x77, 0xF7};
|
||||
midiState.NextEventBuffer();
|
||||
midiState.WriteBuffer(m1, sizeof(m1));
|
||||
midiState.ProcessInputBuffer(m1, sizeof(m1));
|
||||
AlsaAssert(midiState.GetMidiInputEventCount() == 2);
|
||||
|
||||
AlsaAssert(midiState.GetMidiInputEvent(&event, 0));
|
||||
@@ -2298,5 +2297,6 @@ namespace pipedal
|
||||
AlsaAssert(event.buffer[2] == 0x3B);
|
||||
AlsaAssert(event.buffer[3] == 0x77);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
+7
-13
@@ -38,11 +38,9 @@ namespace pipedal {
|
||||
|
||||
struct MidiEvent
|
||||
{
|
||||
/* BINARY COMPATIBLE WITH jack_midi_event_t;! (in case we ever want to resurrect Jack support) */
|
||||
uint32_t time; /**< Sample index at which event is valid */
|
||||
size_t size; /**< Number of bytes of data in \a buffer */
|
||||
uint8_t *buffer; /**< Raw MIDI data */
|
||||
|
||||
uint32_t time; /**< Sample frame at which event is valid */
|
||||
uint32_t size; /**< Number of bytes of data in \a buffer */
|
||||
uint8_t *buffer; /**< Raw MIDI data */
|
||||
};
|
||||
|
||||
|
||||
@@ -65,19 +63,15 @@ namespace pipedal {
|
||||
|
||||
virtual uint32_t GetSampleRate() = 0;
|
||||
|
||||
virtual size_t MidiInputBufferCount() const = 0;
|
||||
virtual void*GetMidiInputBuffer(size_t channel, size_t nFrames) = 0;
|
||||
virtual size_t GetMidiInputEventCount(void*buffer) = 0;
|
||||
virtual bool GetMidiInputEvent(MidiEvent*event, void*portBuf, size_t nFrame) = 0;
|
||||
|
||||
virtual void FillMidiBuffers() = 0;
|
||||
virtual size_t GetMidiInputEventCount() = 0;
|
||||
virtual MidiEvent*GetMidiEvents() = 0;
|
||||
|
||||
virtual size_t InputBufferCount() const = 0;
|
||||
virtual float*GetInputBuffer(size_t channel, size_t nFrames) = 0;
|
||||
virtual float*GetInputBuffer(size_t channel) = 0;
|
||||
|
||||
|
||||
virtual size_t OutputBufferCount() const = 0;
|
||||
virtual float*GetOutputBuffer(size_t channel, size_t nFrames) = 0;
|
||||
virtual float*GetOutputBuffer(size_t channe) = 0;
|
||||
|
||||
virtual void Open(const JackServerSettings & jackServerSettings,const JackChannelSelection &channelSelection) = 0;
|
||||
|
||||
|
||||
+264
-155
@@ -291,6 +291,10 @@ public:
|
||||
|
||||
bool SystemMidiBinding::IsTriggered(const MidiEvent &event)
|
||||
{
|
||||
if (!IsMatch(event))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
switch (currentBinding.bindingType())
|
||||
{
|
||||
case BINDING_TYPE_NOTE:
|
||||
@@ -374,6 +378,9 @@ private:
|
||||
uint8_t deferredMidiMessages[DEFERRED_MIDI_BUFFER_SIZE];
|
||||
size_t deferredMidiMessageCount = 0;
|
||||
bool midiProgramChangePending = false;
|
||||
bool midiSnapshotRequestPending = false;
|
||||
int64_t snapshotRequestId = 0;
|
||||
|
||||
int selectedBank = -1;
|
||||
int64_t midiProgramChangeId = 0;
|
||||
|
||||
@@ -462,8 +469,18 @@ private:
|
||||
HostRingBufferReader hostReader;
|
||||
HostRingBufferWriter hostWriter;
|
||||
|
||||
SystemMidiBinding nextMidiBinding;
|
||||
SystemMidiBinding prevMidiBinding;
|
||||
SystemMidiBinding prevBankMidiBinding;
|
||||
SystemMidiBinding nextBankMidiBinding;
|
||||
SystemMidiBinding nextPresetMidiBinding;
|
||||
SystemMidiBinding prevPresetMidiBinding;
|
||||
|
||||
SystemMidiBinding snapshot1MidiBinding;
|
||||
SystemMidiBinding snapshot2MidiBinding;
|
||||
SystemMidiBinding snapshot3MidiBinding;
|
||||
SystemMidiBinding snapshot4MidiBinding;
|
||||
SystemMidiBinding snapshot5MidiBinding;
|
||||
SystemMidiBinding snapshot6MidiBinding;
|
||||
|
||||
SystemMidiBinding startHotspotMidiBinding;
|
||||
SystemMidiBinding stopHotspotMidiBinding;
|
||||
SystemMidiBinding rebootMidiBinding;
|
||||
@@ -478,8 +495,6 @@ private:
|
||||
std::vector<std::shared_ptr<Lv2Pedalboard>> activePedalboards; // pedalboards that have been sent to the audio queue.
|
||||
Lv2Pedalboard *realtimeActivePedalboard = nullptr;
|
||||
|
||||
std::vector<uint8_t *> midiLv2Buffers;
|
||||
|
||||
uint32_t sampleRate = 0;
|
||||
uint64_t currentSample = 0;
|
||||
|
||||
@@ -558,11 +573,6 @@ private:
|
||||
this->inputRingBuffer.reset();
|
||||
this->outputRingBuffer.reset();
|
||||
|
||||
for (size_t i = 0; i < midiLv2Buffers.size(); ++i)
|
||||
{
|
||||
delete[] midiLv2Buffers[i];
|
||||
}
|
||||
midiLv2Buffers.resize(0);
|
||||
audioDriver = nullptr;
|
||||
}
|
||||
|
||||
@@ -579,7 +589,7 @@ private:
|
||||
{
|
||||
for (size_t i = 0; i < audioDriver->OutputBufferCount(); ++i)
|
||||
{
|
||||
float *out = (float *)audioDriver->GetOutputBuffer(i, nframes);
|
||||
float *out = (float *)audioDriver->GetOutputBuffer(i);
|
||||
if (out)
|
||||
{
|
||||
ZeroBuffer(out, nframes);
|
||||
@@ -774,6 +784,16 @@ private:
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RingBufferCommand::AckMidiSnapshotRequest:
|
||||
{
|
||||
int64_t requestId;
|
||||
realtimeReader.readComplete(&requestId);
|
||||
if (requestId == this->snapshotRequestId)
|
||||
{
|
||||
this->midiSnapshotRequestPending = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RingBufferCommand::LoadSnapshot:
|
||||
{
|
||||
IndexedSnapshot *snapshot;
|
||||
@@ -851,6 +871,10 @@ private:
|
||||
{
|
||||
hostWriter.AckMidiProgramRequest(requestId);
|
||||
}
|
||||
virtual void AckSnapshotRequest(uint64_t snapshotRequestId)
|
||||
{
|
||||
hostWriter.AckMidiSnapshotRequest(snapshotRequestId);
|
||||
}
|
||||
|
||||
void OnMidiValueChanged(uint64_t instanceId, int controlIndex, float value)
|
||||
{
|
||||
@@ -867,7 +891,7 @@ private:
|
||||
|
||||
bool onMidiEvent(Lv2EventBufferWriter &eventBufferWriter, Lv2EventBufferWriter::LV2_EvBuf_Iterator &iterator, MidiEvent &event)
|
||||
{
|
||||
eventBufferWriter.writeMidiEvent(iterator, 0, event.size, event.buffer);
|
||||
//eventBufferWriter.writeMidiEvent(iterator, 0, event.size, event.buffer);
|
||||
|
||||
this->realtimeActivePedalboard->OnMidiMessage(event.size, event.buffer, this, fnMidiValueChanged);
|
||||
if (listenForMidiEvent)
|
||||
@@ -886,126 +910,168 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnSnapshotTriggered(int snapshotIndex)
|
||||
{
|
||||
// midiProgramChangePending = true;
|
||||
this->midiSnapshotRequestPending = true;
|
||||
this->realtimeWriter.OnRealtimeMidiSnapshotRequest(snapshotIndex, ++snapshotRequestId);
|
||||
}
|
||||
|
||||
void ProcessMidiEvent(Lv2EventBufferWriter &eventBufferWriter, Lv2EventBufferWriter::LV2_EvBuf_Iterator &iterator, MidiEvent &event)
|
||||
{
|
||||
uint8_t midiCommand = (uint8_t)(event.buffer[0] & 0xF0);
|
||||
if (midiCommand == 0xC0) // midi program change.
|
||||
{
|
||||
this->deferredMidiMessageCount = 0; // we can discard previous control changes.
|
||||
midiProgramChangePending = true;
|
||||
|
||||
this->realtimeWriter.OnMidiProgramChange(++(this->midiProgramChangeId), selectedBank, event.buffer[1]);
|
||||
}
|
||||
else if (isBankChange(event))
|
||||
{
|
||||
this->selectedBank = event.buffer[2];
|
||||
}
|
||||
else if (this->nextBankMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->deferredMidiMessageCount = 0; // we can discard previous control changes.
|
||||
midiProgramChangePending = true;
|
||||
|
||||
this->realtimeWriter.OnNextMidiBank(++(this->midiProgramChangeId), 1);
|
||||
}
|
||||
else if (this->prevBankMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->deferredMidiMessageCount = 0; // we can discard previous control changes.
|
||||
midiProgramChangePending = true;
|
||||
|
||||
this->realtimeWriter.OnNextMidiBank(++(this->midiProgramChangeId), -1);
|
||||
}
|
||||
else if (nextPresetMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->deferredMidiMessageCount = 0; // we can discard previous control changes.
|
||||
midiProgramChangePending = true;
|
||||
|
||||
midiProgramChangePending = true;
|
||||
this->realtimeWriter.OnNextMidiProgram(++(this->midiProgramChangeId), 1);
|
||||
}
|
||||
else if (prevPresetMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->deferredMidiMessageCount = 0; // we can discard previous control changes.
|
||||
midiProgramChangePending = true;
|
||||
this->realtimeWriter.OnNextMidiProgram(++(this->midiProgramChangeId), -1);
|
||||
}
|
||||
|
||||
else if (shutdownMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->realtimeWriter.OnRealtimeMidiEvent(RealtimeMidiEventType::Shutdown);
|
||||
}
|
||||
if (rebootMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->realtimeWriter.OnRealtimeMidiEvent(RealtimeMidiEventType::Reboot);
|
||||
}
|
||||
if (startHotspotMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->realtimeWriter.OnRealtimeMidiEvent(RealtimeMidiEventType::StartHotspot);
|
||||
}
|
||||
if (stopHotspotMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->realtimeWriter.OnRealtimeMidiEvent(RealtimeMidiEventType::StopHotspot);
|
||||
}
|
||||
else if (midiProgramChangePending)
|
||||
{
|
||||
// defer the message for processing after the program change has completed discarding messages that don't fit in our buffer.
|
||||
if (event.size > 0 && event.size < 128 && event.size + 2 + this->deferredMidiMessageCount < DEFERRED_MIDI_BUFFER_SIZE)
|
||||
{
|
||||
this->deferredMidiMessages[deferredMidiMessageCount++] = (uint8_t)event.size;
|
||||
for (size_t i = 0; i < event.size; ++i)
|
||||
{
|
||||
this->deferredMidiMessages[deferredMidiMessageCount++] = event.buffer[i];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (this->snapshot1MidiBinding.IsTriggered(event))
|
||||
{
|
||||
OnSnapshotTriggered(0);
|
||||
}
|
||||
else if (this->snapshot2MidiBinding.IsTriggered(event))
|
||||
{
|
||||
OnSnapshotTriggered(1);
|
||||
}
|
||||
else if (this->snapshot3MidiBinding.IsTriggered(event))
|
||||
{
|
||||
OnSnapshotTriggered(2);
|
||||
}
|
||||
else if (this->snapshot4MidiBinding.IsTriggered(event))
|
||||
{
|
||||
OnSnapshotTriggered(3);
|
||||
}
|
||||
else if (this->snapshot5MidiBinding.IsTriggered(event))
|
||||
{
|
||||
OnSnapshotTriggered(4);
|
||||
}
|
||||
else if (this->snapshot6MidiBinding.IsTriggered(event))
|
||||
{
|
||||
OnSnapshotTriggered(5);
|
||||
}
|
||||
else
|
||||
{
|
||||
onMidiEvent(eventBufferWriter, iterator, event);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessDeferredMidiMessages(
|
||||
Lv2EventBufferWriter eventBufferWriter,
|
||||
Lv2EventBufferWriter::LV2_EvBuf_Iterator &iterator)
|
||||
{
|
||||
MidiEvent event;
|
||||
|
||||
// write all deferred midi messages.
|
||||
if (!midiProgramChangePending && !midiSnapshotRequestPending)
|
||||
{
|
||||
for (size_t i = 0; i < deferredMidiMessageCount; /**/)
|
||||
{
|
||||
int8_t deviceIndex = deferredMidiMessages[i++];
|
||||
int8_t messageCount = deferredMidiMessages[i++];
|
||||
event.size = messageCount;
|
||||
event.buffer = deferredMidiMessages + i;
|
||||
event.time = 0;
|
||||
|
||||
ProcessMidiEvent(eventBufferWriter, iterator, event);
|
||||
|
||||
if (midiProgramChangePending)
|
||||
{
|
||||
break;
|
||||
}
|
||||
i += messageCount;
|
||||
|
||||
if (midiSnapshotRequestPending)
|
||||
{
|
||||
// consume what has been written so far, leaving what follows for future processing.
|
||||
size_t remaining = deferredMidiMessageCount - i;
|
||||
for (size_t j = 0; j < remaining; ++i)
|
||||
{
|
||||
deferredMidiMessages[j] = deferredMidiMessages[i + j];
|
||||
}
|
||||
deferredMidiMessageCount = remaining;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void ProcessMidiInput()
|
||||
{
|
||||
Lv2EventBufferWriter eventBufferWriter(this->eventBufferUrids);
|
||||
Lv2EventBufferWriter::LV2_EvBuf_Iterator iterator = eventBufferWriter.begin();
|
||||
|
||||
size_t midiInputBufferCount = audioDriver->MidiInputBufferCount();
|
||||
ProcessDeferredMidiMessages(eventBufferWriter, iterator);
|
||||
|
||||
audioDriver->FillMidiBuffers();
|
||||
|
||||
for (size_t midiDeviceIx = 0; midiDeviceIx < midiInputBufferCount; ++midiDeviceIx)
|
||||
{
|
||||
size_t n = audioDriver->GetMidiInputEventCount();
|
||||
MidiEvent *events = audioDriver->GetMidiEvents();
|
||||
|
||||
void *portBuffer = audioDriver->GetMidiInputBuffer(midiDeviceIx, 0);
|
||||
if (portBuffer)
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
uint8_t *lv2Buffer = this->midiLv2Buffers[midiDeviceIx];
|
||||
size_t n = audioDriver->GetMidiInputEventCount(portBuffer);
|
||||
|
||||
eventBufferWriter.Reset(lv2Buffer, MIDI_LV2_BUFFER_SIZE);
|
||||
Lv2EventBufferWriter::LV2_EvBuf_Iterator iterator = eventBufferWriter.begin();
|
||||
MidiEvent event;
|
||||
|
||||
// write all deferred midi messages.
|
||||
if (deferredMidiMessageCount != 0 && !midiProgramChangePending)
|
||||
{
|
||||
for (size_t i = 0; i < deferredMidiMessageCount; /**/)
|
||||
{
|
||||
int8_t deviceIndex = deferredMidiMessages[i++];
|
||||
int8_t messageCount = deferredMidiMessages[i++];
|
||||
if (deviceIndex == midiDeviceIx)
|
||||
{
|
||||
event.size = messageCount;
|
||||
event.buffer = deferredMidiMessages + i;
|
||||
event.time = 0;
|
||||
|
||||
onMidiEvent(eventBufferWriter, iterator, event);
|
||||
}
|
||||
|
||||
i += messageCount;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t frame = 0; frame < n; ++frame)
|
||||
{
|
||||
if (audioDriver->GetMidiInputEvent(&event, portBuffer, frame))
|
||||
{
|
||||
uint8_t midiCommand = (uint8_t)(event.buffer[0] & 0xF0);
|
||||
if (midiCommand == 0xC0) // midi program change.
|
||||
{
|
||||
this->deferredMidiMessageCount = 0; // we can discard previous control changes.
|
||||
midiProgramChangePending = true;
|
||||
|
||||
this->realtimeWriter.OnMidiProgramChange(++(this->midiProgramChangeId), selectedBank, event.buffer[1]);
|
||||
}
|
||||
else if (isBankChange(event))
|
||||
{
|
||||
this->selectedBank = event.buffer[2];
|
||||
}
|
||||
else if (this->nextMidiBinding.IsMatch(event))
|
||||
{
|
||||
if (nextMidiBinding.IsTriggered(event))
|
||||
{
|
||||
midiProgramChangePending = true;
|
||||
this->realtimeWriter.OnNextMidiProgram(++(this->midiProgramChangeId), 1);
|
||||
}
|
||||
}
|
||||
else if (this->prevMidiBinding.IsMatch(event))
|
||||
{
|
||||
if (prevMidiBinding.IsTriggered(event))
|
||||
{
|
||||
midiProgramChangePending = true;
|
||||
this->realtimeWriter.OnNextMidiProgram(++(this->midiProgramChangeId), -1);
|
||||
}
|
||||
}
|
||||
else if (this->shutdownMidiBinding.IsMatch(event))
|
||||
{
|
||||
if (shutdownMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->realtimeWriter.OnRealtimeMidiEvent(RealtimeMidiEventType::Shutdown);
|
||||
}
|
||||
}
|
||||
else if (this->rebootMidiBinding.IsMatch(event))
|
||||
{
|
||||
if (rebootMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->realtimeWriter.OnRealtimeMidiEvent(RealtimeMidiEventType::Reboot);
|
||||
}
|
||||
}
|
||||
else if (this->startHotspotMidiBinding.IsMatch(event))
|
||||
{
|
||||
if (startHotspotMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->realtimeWriter.OnRealtimeMidiEvent(RealtimeMidiEventType::StartHotspot);
|
||||
}
|
||||
}
|
||||
else if (this->stopHotspotMidiBinding.IsMatch(event))
|
||||
{
|
||||
if (stopHotspotMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->realtimeWriter.OnRealtimeMidiEvent(RealtimeMidiEventType::StopHotspot);
|
||||
}
|
||||
}
|
||||
else if (midiProgramChangePending)
|
||||
{
|
||||
// defer the message for processing after the program change has completed.
|
||||
if (event.size > 0 && event.size < 128 && event.size + 2 + this->deferredMidiMessageCount < DEFERRED_MIDI_BUFFER_SIZE)
|
||||
{
|
||||
this->deferredMidiMessages[deferredMidiMessageCount++] = midiDeviceIx;
|
||||
this->deferredMidiMessages[deferredMidiMessageCount++] = (uint8_t)event.size;
|
||||
for (size_t i = 0; i < event.size; ++i)
|
||||
{
|
||||
this->deferredMidiMessages[deferredMidiMessageCount++] = event.buffer[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
onMidiEvent(eventBufferWriter, iterator, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
ProcessMidiEvent(eventBufferWriter, iterator, events[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1062,7 +1128,7 @@ private:
|
||||
bool buffersValid = true;
|
||||
for (int i = 0; i < audioDriver->InputBufferCount(); ++i)
|
||||
{
|
||||
float *input = (float *)audioDriver->GetInputBuffer(i, nframes);
|
||||
float *input = (float *)audioDriver->GetInputBuffer(i);
|
||||
if (input == nullptr)
|
||||
{
|
||||
buffersValid = false;
|
||||
@@ -1074,7 +1140,7 @@ private:
|
||||
|
||||
for (int i = 0; i < audioDriver->OutputBufferCount(); ++i)
|
||||
{
|
||||
float *output = audioDriver->GetOutputBuffer(i, nframes);
|
||||
float *output = audioDriver->GetOutputBuffer(i);
|
||||
if (output == nullptr)
|
||||
{
|
||||
buffersValid = false;
|
||||
@@ -1393,7 +1459,7 @@ public:
|
||||
{
|
||||
LV2_URID propertyUrid = ((LV2_Atom_URID *)property)->body;
|
||||
this->pNotifyCallbacks->OnPatchSetReply(instanceId, propertyUrid, value);
|
||||
this->pNotifyCallbacks->OnNotifyMaybeLv2StateChanged(instanceId);
|
||||
//this->pNotifyCallbacks->OnNotifyMaybeLv2StateChanged(instanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1449,12 +1515,27 @@ public:
|
||||
hostReader.read(&request);
|
||||
pNotifyCallbacks->OnNotifyNextMidiProgram(request);
|
||||
}
|
||||
else if (command == RingBufferCommand::NextMidiBank)
|
||||
{
|
||||
RealtimeNextMidiProgramRequest request;
|
||||
hostReader.read(&request);
|
||||
pNotifyCallbacks->OnNotifyNextMidiBank(request);
|
||||
}
|
||||
|
||||
else if (command == RingBufferCommand::RealtimeMidiEvent)
|
||||
{
|
||||
RealtimeMidiEventRequest request;
|
||||
hostReader.read(&request);
|
||||
pNotifyCallbacks->OnNotifyMidiRealtimeEvent(request.eventType);
|
||||
}
|
||||
else if (command == RingBufferCommand::RealtimeMidiSnapshotRequest)
|
||||
{
|
||||
RealtimeMidiSnapshotRequest request;
|
||||
hostReader.read(&request);
|
||||
pNotifyCallbacks->OnNotifyMidiRealtimeSnapshotRequest(
|
||||
request.snapshotIndex,
|
||||
request.snapshotRequestId);
|
||||
}
|
||||
else if (command == RingBufferCommand::Lv2ErrorMessage)
|
||||
{
|
||||
size_t size;
|
||||
@@ -1579,12 +1660,6 @@ public:
|
||||
this->overrunGracePeriodSamples = (uint64_t)(((uint64_t)this->sampleRate) * OVERRUN_GRACE_PERIOD_S);
|
||||
this->vuSamplesPerUpdate = (size_t)(sampleRate * VU_UPDATE_RATE_S);
|
||||
|
||||
midiLv2Buffers.resize(audioDriver->MidiInputBufferCount());
|
||||
for (size_t i = 0; i < audioDriver->MidiInputBufferCount(); ++i)
|
||||
{
|
||||
midiLv2Buffers[i] = AllocateRealtimeBuffer(MIDI_LV2_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
active = true;
|
||||
audioStopped = false;
|
||||
audioDriver->Activate();
|
||||
@@ -1855,7 +1930,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
virtual bool UpdatePluginStates(Pedalboard &pedalboard) override;
|
||||
//virtual bool UpdatePluginStates(Pedalboard &pedalboard) override;
|
||||
virtual bool UpdatePluginState(PedalboardItem &pedalboardItem) override;
|
||||
|
||||
class RestartThread
|
||||
@@ -2040,13 +2115,21 @@ void AudioHostImpl::SetSystemMidiBindings(const std::vector<MidiBinding> &bindin
|
||||
for (auto i = bindings.begin(); i != bindings.end(); ++i)
|
||||
{
|
||||
|
||||
if (i->symbol() == "nextProgram")
|
||||
if (i->symbol() == "nextBank")
|
||||
{
|
||||
this->nextMidiBinding.SetBinding(*i);
|
||||
this->nextBankMidiBinding.SetBinding(*i);
|
||||
}
|
||||
else if (i->symbol() == "prevBank")
|
||||
{
|
||||
this->prevBankMidiBinding.SetBinding(*i);
|
||||
}
|
||||
else if (i->symbol() == "nextProgram")
|
||||
{
|
||||
this->nextPresetMidiBinding.SetBinding(*i);
|
||||
}
|
||||
else if (i->symbol() == "prevProgram")
|
||||
{
|
||||
this->prevMidiBinding.SetBinding(*i);
|
||||
this->prevPresetMidiBinding.SetBinding(*i);
|
||||
}
|
||||
else if (i->symbol() == "startHotspot")
|
||||
{
|
||||
@@ -2064,6 +2147,30 @@ void AudioHostImpl::SetSystemMidiBindings(const std::vector<MidiBinding> &bindin
|
||||
{
|
||||
this->shutdownMidiBinding.SetBinding(*i);
|
||||
}
|
||||
else if (i->symbol() == "snapshot1")
|
||||
{
|
||||
this->snapshot1MidiBinding.SetBinding(*i);
|
||||
}
|
||||
else if (i->symbol() == "snapshot2")
|
||||
{
|
||||
this->snapshot2MidiBinding.SetBinding(*i);
|
||||
}
|
||||
else if (i->symbol() == "snapshot3")
|
||||
{
|
||||
this->snapshot3MidiBinding.SetBinding(*i);
|
||||
}
|
||||
else if (i->symbol() == "snapshot4")
|
||||
{
|
||||
this->snapshot4MidiBinding.SetBinding(*i);
|
||||
}
|
||||
else if (i->symbol() == "snapshot5")
|
||||
{
|
||||
this->snapshot5MidiBinding.SetBinding(*i);
|
||||
}
|
||||
else if (i->symbol() == "snapshot6")
|
||||
{
|
||||
this->snapshot6MidiBinding.SetBinding(*i);
|
||||
}
|
||||
else
|
||||
{
|
||||
Lv2Log::error(SS("Invalid system midi binding: " << i->symbol()));
|
||||
@@ -2076,22 +2183,24 @@ AudioHost *AudioHost::CreateInstance(IHost *pHost)
|
||||
return new AudioHostImpl(pHost);
|
||||
}
|
||||
|
||||
bool AudioHostImpl::UpdatePluginStates(Pedalboard &pedalboard)
|
||||
{
|
||||
bool changed = false;
|
||||
auto pedalboardItems = pedalboard.GetAllPlugins();
|
||||
for (PedalboardItem *item : pedalboardItems)
|
||||
{
|
||||
if (!item->isSplit())
|
||||
{
|
||||
if (UpdatePluginState(*item))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Removed because any updates to state have to be sent to clients as well,
|
||||
// so this functionality was moved to PiPedalModel::SyncLv2States();
|
||||
// bool AudioHostImpl::UpdatePluginStates(Pedalboard &pedalboard)
|
||||
// {
|
||||
// bool changed = false;
|
||||
// auto pedalboardItems = pedalboard.GetAllPlugins();
|
||||
// for (PedalboardItem *item : pedalboardItems)
|
||||
// {
|
||||
// if (!item->isSplit())
|
||||
// {
|
||||
// if (UpdatePluginState(*item))
|
||||
// {
|
||||
// changed = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return changed;
|
||||
// }
|
||||
|
||||
void AudioHostImpl::OnNotifyPathPatchPropertyReceived(
|
||||
int64_t instanceId,
|
||||
@@ -2100,15 +2209,15 @@ void AudioHostImpl::OnNotifyPathPatchPropertyReceived(
|
||||
{
|
||||
if (this->currentPedalboard)
|
||||
{
|
||||
IEffect*effect = this->currentPedalboard->GetEffect(instanceId);
|
||||
IEffect *effect = this->currentPedalboard->GetEffect(instanceId);
|
||||
if (!effect)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (effect->IsLv2Effect())
|
||||
{
|
||||
Lv2Effect*lv2Effect = (Lv2Effect*)effect;
|
||||
lv2Effect->SetPathPatchProperty(pathPatchPropertyUri,jsonAtom);
|
||||
Lv2Effect *lv2Effect = (Lv2Effect *)effect;
|
||||
lv2Effect->SetPathPatchProperty(pathPatchPropertyUri, jsonAtom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -166,8 +166,10 @@ namespace pipedal
|
||||
|
||||
virtual void OnNotifyMidiProgramChange(RealtimeMidiProgramRequest &midiProgramRequest) = 0;
|
||||
virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest &request) = 0;
|
||||
virtual void OnNotifyNextMidiBank(const RealtimeNextMidiProgramRequest &request) = 0;
|
||||
virtual void OnNotifyLv2RealtimeError(int64_t instanceId, const std::string &error) = 0;
|
||||
virtual void OnNotifyMidiRealtimeEvent(RealtimeMidiEventType eventType) = 0;
|
||||
virtual void OnNotifyMidiRealtimeSnapshotRequest(int32_t snapshotIndex,int64_t snapshotRequestId) = 0;
|
||||
};
|
||||
|
||||
class JackHostStatus
|
||||
@@ -206,7 +208,7 @@ namespace pipedal
|
||||
virtual void SetListenForMidiEvent(bool listen) = 0;
|
||||
virtual void SetListenForAtomOutput(bool listen) = 0;
|
||||
|
||||
virtual bool UpdatePluginStates(Pedalboard &pedalboard) = 0;
|
||||
//virtual bool UpdatePluginStates(Pedalboard &pedalboard) = 0;
|
||||
virtual bool UpdatePluginState(PedalboardItem &pedalboardItem) = 0;
|
||||
|
||||
virtual std::string AtomToJson(const LV2_Atom *atom) = 0;
|
||||
@@ -235,6 +237,8 @@ namespace pipedal
|
||||
|
||||
virtual void sendRealtimeParameterRequest(RealtimePatchPropertyRequest *pParameterRequest) = 0;
|
||||
virtual void AckMidiProgramRequest(uint64_t requestId) = 0;
|
||||
virtual void AckSnapshotRequest(uint64_t snapshotRequestId) = 0;
|
||||
|
||||
|
||||
virtual JackHostStatus getJackStatus() = 0;
|
||||
|
||||
|
||||
+2
-2
@@ -113,9 +113,9 @@ add_compile_definitions(DEBIAN_ARCHITECTURE="${DEBIAN_ARCHITECTURE}")
|
||||
if (CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
message(STATUS "Debug build")
|
||||
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -DDEBUG" )
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -DDEBUG -D_GLIBCXX_DEBUG" )
|
||||
if (USE_SANITIZE)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -static-libasan " )
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -static-libasan " )
|
||||
endif()
|
||||
elseif(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo)
|
||||
message(STATUS "RelWithgDebInfo build")
|
||||
|
||||
+11
-115
@@ -150,6 +150,14 @@ namespace pipedal
|
||||
}
|
||||
}
|
||||
|
||||
virtual size_t GetMidiInputEventCount() override {
|
||||
return 0;
|
||||
}
|
||||
virtual MidiEvent*GetMidiEvents() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
JackChannelSelection channelSelection;
|
||||
bool open = false;
|
||||
virtual void Open(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
|
||||
@@ -166,7 +174,6 @@ namespace pipedal
|
||||
open = true;
|
||||
try
|
||||
{
|
||||
OpenMidi(jackServerSettings, channelSelection);
|
||||
OpenAudio(jackServerSettings, channelSelection);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
@@ -348,128 +355,17 @@ namespace pipedal
|
||||
static constexpr size_t MAX_MIDI_EVENT = 4 * 1024;
|
||||
|
||||
public:
|
||||
class MidiState
|
||||
{
|
||||
private:
|
||||
snd_rawmidi_t *hIn = nullptr;
|
||||
snd_rawmidi_params_t *hInParams = nullptr;
|
||||
std::string deviceName;
|
||||
|
||||
// running status state.
|
||||
uint8_t runningStatus = 0;
|
||||
int dataLength = 0;
|
||||
int dataIndex = 0;
|
||||
size_t statusBytesRemaining = 0;
|
||||
size_t data0;
|
||||
size_t data1;
|
||||
|
||||
size_t eventCount = 0;
|
||||
MidiEvent events[MAX_MIDI_EVENT];
|
||||
size_t bufferCount = 0;
|
||||
uint8_t buffer[MIDI_BUFFER_SIZE];
|
||||
|
||||
uint8_t readBuffer[1024];
|
||||
|
||||
ssize_t sysexStartIndex = -1;
|
||||
|
||||
void checkError(int result, const char *message)
|
||||
{
|
||||
if (result < 0)
|
||||
{
|
||||
throw PiPedalStateException(SS("Unexpected error: " << message << " (" << this->deviceName));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void Open(const AlsaMidiDeviceInfo &device)
|
||||
{
|
||||
bufferCount = 0;
|
||||
eventCount = 0;
|
||||
sysexStartIndex = -1;
|
||||
runningStatus = 0;
|
||||
dataIndex = 0;
|
||||
dataLength = 0;
|
||||
|
||||
this->deviceName = device.description_;
|
||||
|
||||
}
|
||||
void Close()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
size_t GetMidiInputEventCount()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool GetMidiInputEvent(MidiEvent *event, size_t nFrame)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void MidiPut(uint8_t cc, uint8_t d0, uint8_t d1)
|
||||
{
|
||||
}
|
||||
|
||||
void FillBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
void WriteBuffer(uint8_t *readBuffer, size_t nRead)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<MidiState *> midiStates;
|
||||
|
||||
void OpenMidi(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
|
||||
{
|
||||
const auto &devices = channelSelection.GetInputMidiDevices();
|
||||
|
||||
midiStates.resize(devices.size());
|
||||
|
||||
for (size_t i = 0; i < devices.size(); ++i)
|
||||
{
|
||||
const auto &device = devices[i];
|
||||
MidiState *state = new MidiState();
|
||||
midiStates[i] = state;
|
||||
state->Open(device);
|
||||
}
|
||||
}
|
||||
|
||||
virtual size_t MidiInputBufferCount() const
|
||||
{
|
||||
return this->midiStates.size();
|
||||
}
|
||||
virtual void *GetMidiInputBuffer(size_t channel, size_t nFrames)
|
||||
{
|
||||
return (void *)midiStates[channel];
|
||||
}
|
||||
|
||||
virtual size_t GetMidiInputEventCount(void *portBuffer)
|
||||
{
|
||||
MidiState *state = (MidiState *)portBuffer;
|
||||
return state->GetMidiInputEventCount();
|
||||
}
|
||||
virtual bool GetMidiInputEvent(MidiEvent *event, void *portBuf, size_t nFrame)
|
||||
{
|
||||
MidiState *state = (MidiState *)portBuf;
|
||||
return state->GetMidiInputEvent(event, nFrame);
|
||||
}
|
||||
|
||||
virtual void FillMidiBuffers()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
virtual size_t InputBufferCount() const { return activeCaptureBuffers.size(); }
|
||||
virtual float *GetInputBuffer(size_t channel, size_t nFrames)
|
||||
virtual float *GetInputBuffer(size_t channel)
|
||||
{
|
||||
return activeCaptureBuffers[channel];
|
||||
}
|
||||
|
||||
virtual size_t OutputBufferCount() const { return activePlaybackBuffers.size(); }
|
||||
virtual float *GetOutputBuffer(size_t channel, size_t nFrames)
|
||||
virtual float *GetOutputBuffer(size_t channel)
|
||||
{
|
||||
return activePlaybackBuffers[channel];
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ using namespace dbus::networkmanager;
|
||||
namespace pipedal::impl
|
||||
{
|
||||
|
||||
constexpr std::string PIPEDAL_HOTSPOT_NAME = "PiPedal Hotspot";
|
||||
#define PIPEDAL_HOTSPOT_NAME "PiPedal Hotspot"
|
||||
|
||||
class HotspotManagerImpl : public HotspotManager
|
||||
{
|
||||
|
||||
@@ -358,11 +358,11 @@ public:
|
||||
|
||||
for (size_t i = 0; i < inputs; ++i)
|
||||
{
|
||||
inputBuffers[i] = audioDriver->GetInputBuffer(i, nFrames);
|
||||
inputBuffers[i] = audioDriver->GetInputBuffer(i);
|
||||
}
|
||||
for (size_t i = 0; i < outputs; ++i)
|
||||
{
|
||||
outputBuffers[i] = audioDriver->GetOutputBuffer(i, nFrames);
|
||||
outputBuffers[i] = audioDriver->GetOutputBuffer(i);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < nFrames; ++i)
|
||||
|
||||
+91
-14
@@ -349,6 +349,7 @@ void PiPedalModel::PreviewControl(int64_t clientId, int64_t pedalItemId, const s
|
||||
}
|
||||
}
|
||||
|
||||
// we were explicitly told that the state was changed by the plugin
|
||||
void PiPedalModel::OnNotifyLv2StateChanged(uint64_t instanceId)
|
||||
{
|
||||
// a sent PATCH_Set, or an explicit state changed notification.
|
||||
@@ -356,6 +357,7 @@ void PiPedalModel::OnNotifyLv2StateChanged(uint64_t instanceId)
|
||||
this->SetPresetChanged(-1, true, false);
|
||||
}
|
||||
|
||||
// The plugin notified us that a path path property changed. The state *purrobably changed.
|
||||
void PiPedalModel::OnNotifyMaybeLv2StateChanged(uint64_t instanceId)
|
||||
{
|
||||
// one or more received PATCH_Sets, which MAY change the state.
|
||||
@@ -363,7 +365,9 @@ void PiPedalModel::OnNotifyMaybeLv2StateChanged(uint64_t instanceId)
|
||||
PedalboardItem *item = pedalboard.GetItem(instanceId);
|
||||
if (item != nullptr)
|
||||
{
|
||||
|
||||
if (!audioHost) {
|
||||
return;
|
||||
}
|
||||
bool changed = this->audioHost->UpdatePluginState(*item);
|
||||
if (changed)
|
||||
{
|
||||
@@ -371,11 +375,8 @@ void PiPedalModel::OnNotifyMaybeLv2StateChanged(uint64_t instanceId)
|
||||
item->stateUpdateCount(item->stateUpdateCount() + 1);
|
||||
|
||||
Lv2PluginState newState = item->lv2State();
|
||||
std::vector<IPiPedalModelSubscriber::ptr> t{subscribers.begin(), subscribers.end()};
|
||||
for (auto &subscriber : t)
|
||||
{
|
||||
subscriber->OnLv2StateChanged(instanceId, newState);
|
||||
}
|
||||
|
||||
FireLv2StateChanged(instanceId,newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -509,6 +510,8 @@ void PiPedalModel::SetPedalboard(int64_t clientId, Pedalboard &pedalboard)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PiPedalModel::SetSnapshot(int64_t selectedSnapshot)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
@@ -723,12 +726,43 @@ void PiPedalModel::UpdateVst3Settings(Pedalboard &pedalboard)
|
||||
#endif
|
||||
}
|
||||
|
||||
void PiPedalModel::FireLv2StateChanged(int64_t instanceId, const Lv2PluginState &lv2State) {
|
||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||
std::vector<IPiPedalModelSubscriber::ptr> t{subscribers.begin(), subscribers.end()};
|
||||
|
||||
for (auto &subscriber : t)
|
||||
{
|
||||
subscriber->OnLv2StateChanged(instanceId,lv2State);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// referesh the plugin state for all plugins.
|
||||
bool PiPedalModel::SyncLv2State()
|
||||
{
|
||||
bool changed = false;
|
||||
auto pedalboardItems = pedalboard.GetAllPlugins();
|
||||
for (PedalboardItem *item : pedalboardItems)
|
||||
{
|
||||
if (!item->isSplit())
|
||||
{
|
||||
if (audioHost->UpdatePluginState(*item))
|
||||
{
|
||||
FireLv2StateChanged(item->instanceId(),item->lv2State());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
|
||||
}
|
||||
void PiPedalModel::SaveCurrentPreset(int64_t clientId)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||
|
||||
UpdateVst3Settings(this->pedalboard);
|
||||
this->audioHost->UpdatePluginStates(this->pedalboard);
|
||||
SyncLv2State();
|
||||
|
||||
storage.SaveCurrentPreset(this->pedalboard);
|
||||
this->SetPresetChanged(clientId, false);
|
||||
}
|
||||
@@ -761,6 +795,7 @@ int64_t PiPedalModel::SaveCurrentPresetAs(int64_t clientId, const std::string &n
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||
|
||||
SyncLv2State();
|
||||
auto pedalboard = this->pedalboard;
|
||||
UpdateVst3Settings(pedalboard);
|
||||
pedalboard.name(name);
|
||||
@@ -880,7 +915,7 @@ void PiPedalModel::OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest
|
||||
try
|
||||
{
|
||||
|
||||
if (request.direction < 0)
|
||||
if (request.direction >= 0)
|
||||
{
|
||||
NextPreset();
|
||||
}
|
||||
@@ -900,6 +935,47 @@ void PiPedalModel::OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest
|
||||
}
|
||||
}
|
||||
|
||||
void PiPedalModel::OnNotifyMidiRealtimeSnapshotRequest(int32_t snapshotIndex,int64_t snapshotRequestId)
|
||||
{
|
||||
try {
|
||||
SetSnapshot((int64_t)snapshotIndex);
|
||||
} catch (const std::exception&e)
|
||||
{
|
||||
Lv2Log::error(SS("SetSnapshot failed. " << e.what()));
|
||||
}
|
||||
if (this->audioHost)
|
||||
{
|
||||
this->audioHost->AckSnapshotRequest(snapshotRequestId);
|
||||
}
|
||||
}
|
||||
|
||||
void PiPedalModel::OnNotifyNextMidiBank(const RealtimeNextMidiProgramRequest &request)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||
try
|
||||
{
|
||||
|
||||
if (request.direction >= 0)
|
||||
{
|
||||
NextBank();
|
||||
}
|
||||
else
|
||||
{
|
||||
PreviousBank();
|
||||
}
|
||||
}
|
||||
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};
|
||||
@@ -1953,14 +2029,14 @@ void PiPedalModel::OnPatchSetReply(uint64_t instanceId, LV2_URID patchSetPropert
|
||||
atom_object atomObject{atomValue};
|
||||
|
||||
PedalboardItem::PropertyMap &properties = item->PatchProperties();
|
||||
if (properties.contains(propertyUri))
|
||||
if (properties.contains(propertyUri) && properties[propertyUri] == atomObject)
|
||||
{
|
||||
if (properties[propertyUri] == atomObject)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// do noting.
|
||||
} else {
|
||||
properties[propertyUri] = std::move(atomObject);
|
||||
}
|
||||
properties[propertyUri] = std::move(atomObject);
|
||||
|
||||
OnNotifyMaybeLv2StateChanged(instanceId);
|
||||
}
|
||||
|
||||
bool hasAtomJson = false;
|
||||
@@ -2509,6 +2585,7 @@ void PiPedalModel::OnNetworkChanged(bool ethernetConnected, bool hotspotConnecte
|
||||
FireNetworkChanged();
|
||||
}
|
||||
|
||||
|
||||
void PiPedalModel::OnNotifyMidiRealtimeEvent(RealtimeMidiEventType eventType)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -50,6 +50,7 @@ namespace pipedal
|
||||
class Lv2PluginChangeMonitor;
|
||||
class Updater;
|
||||
class AvahiService;
|
||||
class Lv2PluginState;
|
||||
|
||||
class IPiPedalModelSubscriber
|
||||
{
|
||||
@@ -187,7 +188,7 @@ namespace pipedal
|
||||
void FireChannelSelectionChanged(int64_t clientId);
|
||||
void FireBanksChanged(int64_t clientId);
|
||||
void FireJackConfigurationChanged(const JackConfiguration &jackConfiguration);
|
||||
|
||||
void FireLv2StateChanged(int64_t instanceId, const Lv2PluginState &lv2State);
|
||||
void UpdateDefaults(Snapshot *snapshot, std::unordered_map<int64_t, PedalboardItem *> &itemMap);
|
||||
void UpdateDefaults(PedalboardItem *pedalboardItem, std::unordered_map<int64_t, PedalboardItem *> &itemMap);
|
||||
void UpdateDefaults(Pedalboard *pedalboard);
|
||||
@@ -211,7 +212,7 @@ namespace pipedal
|
||||
|
||||
IPiPedalModelSubscriber *GetNotificationSubscriber(int64_t clientId);
|
||||
std::atomic<bool> closed = false;
|
||||
|
||||
bool SyncLv2State();
|
||||
private: // IAudioHostCallbacks
|
||||
virtual void OnNotifyLv2StateChanged(uint64_t instanceId) override;
|
||||
virtual void OnNotifyMaybeLv2StateChanged(uint64_t instanceId) override;
|
||||
@@ -221,6 +222,7 @@ namespace pipedal
|
||||
virtual void OnNotifyMidiListen(bool isNote, uint8_t noteOrControl) override;
|
||||
virtual void OnPatchSetReply(uint64_t instanceId, LV2_URID patchSetProperty, const LV2_Atom *atomValue) override;
|
||||
virtual void OnNotifyMidiRealtimeEvent(RealtimeMidiEventType eventType) override;
|
||||
virtual void OnNotifyMidiRealtimeSnapshotRequest(int32_t snapshotIndex,int64_t snapshotRequestId) override;
|
||||
|
||||
void OnNotifyPathPatchPropertyReceived(
|
||||
int64_t instanceId,
|
||||
@@ -229,6 +231,7 @@ namespace pipedal
|
||||
|
||||
virtual void OnNotifyMidiProgramChange(RealtimeMidiProgramRequest &midiProgramRequest) override;
|
||||
virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest &request) override;
|
||||
virtual void OnNotifyNextMidiBank(const RealtimeNextMidiProgramRequest &request) override;
|
||||
virtual void OnNotifyLv2RealtimeError(int64_t instanceId, const std::string &error) override;
|
||||
|
||||
PostHandle networkChangingDelayHandle = 0;
|
||||
|
||||
@@ -23,7 +23,16 @@ namespace pipedal {
|
||||
Shutdown,
|
||||
Reboot,
|
||||
StartHotspot,
|
||||
StopHotspot
|
||||
StopHotspot,
|
||||
NextBank,
|
||||
PrevBank,
|
||||
Snapshot1,
|
||||
Snapshot2,
|
||||
Snapshot3,
|
||||
Snapshot4,
|
||||
Snapshot5,
|
||||
Snapshot6,
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
+95
-81
@@ -27,8 +27,6 @@
|
||||
#include "RealtimeMidiEventType.hpp"
|
||||
#include <chrono>
|
||||
|
||||
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
class IndexedSnapshot;
|
||||
@@ -65,8 +63,10 @@ namespace pipedal
|
||||
|
||||
MidiProgramChange, // program change requested via midi.
|
||||
AckMidiProgramChange,
|
||||
AckMidiSnapshotRequest,
|
||||
|
||||
NextMidiProgram,
|
||||
NextMidiBank,
|
||||
|
||||
Lv2StateChanged,
|
||||
MaybeLv2StateChanged,
|
||||
@@ -75,28 +75,36 @@ namespace pipedal
|
||||
Lv2ErrorMessage,
|
||||
|
||||
RealtimeMidiEvent,
|
||||
RealtimeMidiSnapshotRequest,
|
||||
|
||||
SendPathPropertyBuffer,
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct RealtimeMidiEventRequest {
|
||||
struct RealtimeMidiEventRequest
|
||||
{
|
||||
RealtimeMidiEventType eventType;
|
||||
};
|
||||
struct RealtimeNextMidiProgramRequest {
|
||||
|
||||
struct RealtimeMidiSnapshotRequest
|
||||
{
|
||||
int32_t snapshotIndex;
|
||||
int64_t snapshotRequestId;
|
||||
};
|
||||
|
||||
struct RealtimeNextMidiProgramRequest
|
||||
{
|
||||
int64_t requestId;
|
||||
int32_t direction;
|
||||
};
|
||||
|
||||
struct RealtimeMidiProgramRequest {
|
||||
struct RealtimeMidiProgramRequest
|
||||
{
|
||||
int64_t requestId;
|
||||
int8_t bank;
|
||||
uint8_t program;
|
||||
};
|
||||
|
||||
|
||||
class RealtimeMonitorPortSubscription
|
||||
{
|
||||
public:
|
||||
@@ -169,7 +177,6 @@ namespace pipedal
|
||||
float value;
|
||||
};
|
||||
|
||||
|
||||
class SetControlValueBody
|
||||
{
|
||||
public:
|
||||
@@ -201,36 +208,38 @@ namespace pipedal
|
||||
{
|
||||
|
||||
private:
|
||||
RingBuffer<MULTI_WRITE,SEMAPHORE_READ> *ringBuffer = nullptr;
|
||||
RingBuffer<MULTI_WRITE, SEMAPHORE_READ> *ringBuffer = nullptr;
|
||||
|
||||
public:
|
||||
RingBufferReader()
|
||||
: ringBuffer(nullptr)
|
||||
{
|
||||
}
|
||||
RingBufferReader(RingBuffer<MULTI_WRITE,SEMAPHORE_READ> *ringBuffer)
|
||||
RingBufferReader(RingBuffer<MULTI_WRITE, SEMAPHORE_READ> *ringBuffer)
|
||||
: ringBuffer(ringBuffer)
|
||||
{
|
||||
}
|
||||
void Reset() { ringBuffer->reset(); }
|
||||
// 0 -> ready. -1: timed out. -2: closing.
|
||||
template <class Rep, class Period>
|
||||
RingBufferStatus wait_for(const std::chrono::duration<Rep,Period>& timeout) {
|
||||
template <class Rep, class Period>
|
||||
RingBufferStatus wait_for(const std::chrono::duration<Rep, Period> &timeout)
|
||||
{
|
||||
return ringBuffer->readWait_for(timeout);
|
||||
}
|
||||
|
||||
template <typename Clock, typename Duration>
|
||||
RingBufferStatus wait_until(std::chrono::time_point<Clock,Duration>&time_point)
|
||||
RingBufferStatus wait_until(std::chrono::time_point<Clock, Duration> &time_point)
|
||||
{
|
||||
return ringBuffer->readWait_until(time_point);
|
||||
}
|
||||
template <typename Clock, typename Duration>
|
||||
RingBufferStatus wait_until(size_t size,std::chrono::time_point<Clock,Duration>&time_point)
|
||||
RingBufferStatus wait_until(size_t size, std::chrono::time_point<Clock, Duration> &time_point)
|
||||
{
|
||||
return ringBuffer->readWait_until(size,time_point);
|
||||
return ringBuffer->readWait_until(size, time_point);
|
||||
}
|
||||
|
||||
bool wait() {
|
||||
bool wait()
|
||||
{
|
||||
return ringBuffer->readWait();
|
||||
}
|
||||
size_t readSpace() const
|
||||
@@ -240,33 +249,29 @@ namespace pipedal
|
||||
template <typename T>
|
||||
bool read(T *output)
|
||||
{
|
||||
if (!ringBuffer->read(sizeof(T),(uint8_t*)output))
|
||||
if (!ringBuffer->read(sizeof(T), (uint8_t *)output))
|
||||
{
|
||||
throw PiPedalStateException("Ringbuffer read failed. Did you forget to check for space?");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read(size_t size, uint8_t*data)
|
||||
bool read(size_t size, uint8_t *data)
|
||||
{
|
||||
if (!ringBuffer->read(size,data))
|
||||
if (!ringBuffer->read(size, data))
|
||||
{
|
||||
throw PiPedalStateException("Ringbuffer read failed. Did you forget to check for space?");
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
template <typename T>
|
||||
void readComplete(T *output)
|
||||
{
|
||||
if (!ringBuffer->read(sizeof(T),(uint8_t*)output))
|
||||
if (!ringBuffer->read(sizeof(T), (uint8_t *)output))
|
||||
{
|
||||
throw PiPedalStateException("Ringbuffer read failed. Did you forget to check for space?");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -290,19 +295,19 @@ namespace pipedal
|
||||
T value;
|
||||
};
|
||||
|
||||
template<bool MULTI_WRITER, bool SEMAPHORE_READER>
|
||||
template <bool MULTI_WRITER, bool SEMAPHORE_READER>
|
||||
class RingBufferWriter
|
||||
{
|
||||
|
||||
private:
|
||||
RingBuffer<MULTI_WRITER,SEMAPHORE_READER>* ringBuffer;
|
||||
RingBuffer<MULTI_WRITER, SEMAPHORE_READER> *ringBuffer;
|
||||
|
||||
public:
|
||||
RingBufferWriter()
|
||||
: ringBuffer(nullptr)
|
||||
{
|
||||
}
|
||||
RingBufferWriter(RingBuffer<MULTI_WRITER,SEMAPHORE_READER>*ringBuffer)
|
||||
RingBufferWriter(RingBuffer<MULTI_WRITER, SEMAPHORE_READER> *ringBuffer)
|
||||
: ringBuffer(ringBuffer)
|
||||
{
|
||||
}
|
||||
@@ -315,88 +320,99 @@ namespace pipedal
|
||||
// the goal: to atomically write the command and associated data.
|
||||
CommandBuffer<T> buffer(command, value);
|
||||
|
||||
if (!ringBuffer->write(buffer.size(),(uint8_t *)&buffer))
|
||||
if (!ringBuffer->write(buffer.size(), (uint8_t *)&buffer))
|
||||
{
|
||||
Lv2Log::error("No space in audio service ringbuffer.");
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
void write(RingBufferCommand command, const T &value, size_t dataLength, uint8_t*variableData)
|
||||
void write(RingBufferCommand command, const T &value, size_t dataLength, uint8_t *variableData)
|
||||
{
|
||||
|
||||
// the goal: to atomically write the command and associated data.
|
||||
CommandBuffer<T> buffer(command, value);
|
||||
|
||||
if (!ringBuffer->write(buffer.size(),(uint8_t *)&buffer,dataLength,variableData))
|
||||
if (!ringBuffer->write(buffer.size(), (uint8_t *)&buffer, dataLength, variableData))
|
||||
{
|
||||
Lv2Log::error("No space in audio service ringbuffer.");
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
void Lv2StateChanged(uint64_t instanceId)
|
||||
{
|
||||
write(RingBufferCommand::Lv2StateChanged,instanceId);
|
||||
write(RingBufferCommand::Lv2StateChanged, instanceId);
|
||||
}
|
||||
void MaybeLv2StateChanged(uint64_t instanceId)
|
||||
{
|
||||
write(RingBufferCommand::MaybeLv2StateChanged,instanceId);
|
||||
write(RingBufferCommand::MaybeLv2StateChanged, instanceId);
|
||||
}
|
||||
void AtomOutput(uint64_t instanceId, size_t bytes, uint8_t*data)
|
||||
void AtomOutput(uint64_t instanceId, size_t bytes, uint8_t *data)
|
||||
{
|
||||
write(RingBufferCommand::AtomOutput,instanceId,bytes,data);
|
||||
write(RingBufferCommand::AtomOutput, instanceId, bytes, data);
|
||||
}
|
||||
void AtomOutput(uint64_t instanceId, const LV2_Atom*atom)
|
||||
void AtomOutput(uint64_t instanceId, const LV2_Atom *atom)
|
||||
{
|
||||
write(RingBufferCommand::AtomOutput,instanceId,atom->size+sizeof(LV2_Atom),(uint8_t*)atom);
|
||||
write(RingBufferCommand::AtomOutput, instanceId, atom->size + sizeof(LV2_Atom), (uint8_t *)atom);
|
||||
}
|
||||
void ParameterRequest(RealtimePatchPropertyRequest *pRequest)
|
||||
{
|
||||
write(RingBufferCommand::ParameterRequest,pRequest);
|
||||
write(RingBufferCommand::ParameterRequest, pRequest);
|
||||
}
|
||||
void ParameterRequestComplete(RealtimePatchPropertyRequest *pRequest)
|
||||
{
|
||||
write(RingBufferCommand::ParameterRequestComplete,pRequest);
|
||||
write(RingBufferCommand::ParameterRequestComplete, pRequest);
|
||||
}
|
||||
void MidiValueChanged(int64_t instanceId, int controlIndex,float value)
|
||||
void MidiValueChanged(int64_t instanceId, int controlIndex, float value)
|
||||
{
|
||||
MidiValueChangedBody body;
|
||||
body.instanceId = instanceId;
|
||||
body.controlIndex = controlIndex;
|
||||
body.value = value;
|
||||
write(RingBufferCommand::MidiValueChanged,body);
|
||||
write(RingBufferCommand::MidiValueChanged, body);
|
||||
}
|
||||
|
||||
void OnMidiListen(bool isNote, uint8_t noteOrControl) {
|
||||
void OnMidiListen(bool isNote, uint8_t noteOrControl)
|
||||
{
|
||||
uint16_t msg = noteOrControl;
|
||||
if (isNote) msg |= 0x100;
|
||||
if (isNote)
|
||||
msg |= 0x100;
|
||||
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)
|
||||
void OnMidiProgramChange(int64_t _requestId, int8_t _bank, uint8_t _program)
|
||||
{
|
||||
RealtimeMidiProgramRequest msg { requestId:_requestId, bank: _bank,program: _program};
|
||||
RealtimeMidiProgramRequest msg{requestId : _requestId, bank : _bank, program : _program};
|
||||
|
||||
|
||||
write(RingBufferCommand::MidiProgramChange,msg);
|
||||
write(RingBufferCommand::MidiProgramChange, msg);
|
||||
}
|
||||
void OnRealtimeMidiEvent(RealtimeMidiEventType eventType)
|
||||
{
|
||||
RealtimeMidiEventRequest msg { eventType};
|
||||
write(RingBufferCommand::RealtimeMidiEvent,msg);
|
||||
RealtimeMidiEventRequest msg{eventType};
|
||||
write(RingBufferCommand::RealtimeMidiEvent, msg);
|
||||
}
|
||||
void OnNextMidiProgram(int64_t requestId,int32_t direction)
|
||||
void OnRealtimeMidiSnapshotRequest(int32_t snapshotIndex, int64_t snapshotRequestId)
|
||||
{
|
||||
RealtimeNextMidiProgramRequest msg { requestId: requestId, direction:direction};
|
||||
write(RingBufferCommand::NextMidiProgram,msg);
|
||||
RealtimeMidiSnapshotRequest msg{snapshotIndex, snapshotRequestId};
|
||||
|
||||
write(RingBufferCommand::RealtimeMidiSnapshotRequest, msg);
|
||||
}
|
||||
|
||||
void OnNextMidiProgram(int64_t requestId, int32_t direction)
|
||||
{
|
||||
RealtimeNextMidiProgramRequest msg{requestId : requestId, direction : direction};
|
||||
write(RingBufferCommand::NextMidiProgram, msg);
|
||||
}
|
||||
void OnNextMidiBank(int64_t requestId, int32_t direction)
|
||||
{
|
||||
RealtimeNextMidiProgramRequest msg{requestId : requestId, direction : direction};
|
||||
write(RingBufferCommand::NextMidiBank, msg);
|
||||
}
|
||||
|
||||
void SetControlValue(int effectIndex, int controlIndex, float value)
|
||||
@@ -405,22 +421,21 @@ namespace pipedal
|
||||
body.effectIndex = effectIndex;
|
||||
body.controlIndex = controlIndex;
|
||||
body.value = value;
|
||||
write(RingBufferCommand::SetValue,body);
|
||||
write(RingBufferCommand::SetValue, body);
|
||||
}
|
||||
void SetInputVolume(float value)
|
||||
{
|
||||
SetVolumeBody body;
|
||||
body.value = value;
|
||||
write(RingBufferCommand::SetInputVolume,body);
|
||||
write(RingBufferCommand::SetInputVolume, body);
|
||||
}
|
||||
void SetOutputVolume(float value)
|
||||
{
|
||||
SetVolumeBody body;
|
||||
body.value = value;
|
||||
write(RingBufferCommand::SetOutputVolume,body);
|
||||
write(RingBufferCommand::SetOutputVolume, body);
|
||||
}
|
||||
|
||||
|
||||
void FreeVuSubscriptions(RealtimeVuBuffers *configuration)
|
||||
{
|
||||
write(RingBufferCommand::FreeVuSubscriptions, configuration);
|
||||
@@ -432,7 +447,7 @@ namespace pipedal
|
||||
}
|
||||
|
||||
void SendMonitorPortUpdate(
|
||||
PortMonitorCallback* callback,
|
||||
PortMonitorCallback *callback,
|
||||
int64_t subscriptionHandle,
|
||||
float value)
|
||||
{
|
||||
@@ -442,12 +457,12 @@ namespace pipedal
|
||||
|
||||
void SendVuUpdate(const std::vector<VuUpdate> *pUpdates)
|
||||
{
|
||||
write(RingBufferCommand::SendVuUpdate,pUpdates);
|
||||
write(RingBufferCommand::SendVuUpdate, pUpdates);
|
||||
}
|
||||
void AckVuUpdate()
|
||||
{
|
||||
bool value = true;
|
||||
write(RingBufferCommand::AckVuUpdate,value);
|
||||
write(RingBufferCommand::AckVuUpdate, value);
|
||||
}
|
||||
void AckMonitorPortUpdate(int64_t subscriptionHandle)
|
||||
{
|
||||
@@ -459,14 +474,17 @@ namespace pipedal
|
||||
{
|
||||
write(RingBufferCommand::SetVuSubscriptions, configuration);
|
||||
}
|
||||
void LoadSnapshot(IndexedSnapshot*snapshot)
|
||||
void LoadSnapshot(IndexedSnapshot *snapshot)
|
||||
{
|
||||
write(RingBufferCommand::LoadSnapshot,snapshot);
|
||||
write(RingBufferCommand::LoadSnapshot, snapshot);
|
||||
}
|
||||
|
||||
|
||||
void AckMidiProgramRequest(int64_t requestId)
|
||||
{
|
||||
write(RingBufferCommand::AckMidiProgramChange,requestId);
|
||||
write(RingBufferCommand::AckMidiProgramChange, requestId);
|
||||
}
|
||||
void AckMidiSnapshotRequest(uint64_t snapshotRequestId) {
|
||||
write(RingBufferCommand::AckMidiSnapshotRequest, snapshotRequestId);
|
||||
}
|
||||
|
||||
void SetMonitorPortSubscriptions(RealtimeMonitorPortSubscriptions *subscriptions)
|
||||
@@ -481,7 +499,7 @@ namespace pipedal
|
||||
SetBypassBody body;
|
||||
body.effectIndex = effectIndex;
|
||||
body.enabled = enabled;
|
||||
write(RingBufferCommand::SetBypass,body);
|
||||
write(RingBufferCommand::SetBypass, body);
|
||||
}
|
||||
|
||||
void ReplaceEffect(Lv2Pedalboard *pedalboard)
|
||||
@@ -500,19 +518,19 @@ namespace pipedal
|
||||
write(RingBufferCommand::EffectReplaced, pedalboard);
|
||||
}
|
||||
|
||||
void FreeSnapshot(IndexedSnapshot*snapshot)
|
||||
void FreeSnapshot(IndexedSnapshot *snapshot)
|
||||
{
|
||||
write(RingBufferCommand::FreeSnapshot,snapshot);
|
||||
write(RingBufferCommand::FreeSnapshot, snapshot);
|
||||
}
|
||||
|
||||
void WriteLv2ErrorMessage(int64_t instanceId, const char*message)
|
||||
void WriteLv2ErrorMessage(int64_t instanceId, const char *message)
|
||||
{
|
||||
size_t length = strlen(message);
|
||||
write(RingBufferCommand::Lv2ErrorMessage,instanceId,length,(uint8_t*)message);
|
||||
write(RingBufferCommand::Lv2ErrorMessage, instanceId, length, (uint8_t *)message);
|
||||
}
|
||||
void SendPathPropertyBuffer(PatchPropertyWriter::Buffer*buffer)
|
||||
void SendPathPropertyBuffer(PatchPropertyWriter::Buffer *buffer)
|
||||
{
|
||||
write(RingBufferCommand::SendPathPropertyBuffer,buffer);
|
||||
write(RingBufferCommand::SendPathPropertyBuffer, buffer);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -521,20 +539,16 @@ namespace pipedal
|
||||
typedef RingBufferWriter<true, false> HostRingBufferWriter;
|
||||
|
||||
// cures a forward-declaration problem.
|
||||
class RealtimeRingBufferWriter: public RingBufferWriter<false, true>
|
||||
class RealtimeRingBufferWriter : public RingBufferWriter<false, true>
|
||||
{
|
||||
public:
|
||||
RealtimeRingBufferWriter()
|
||||
{
|
||||
}
|
||||
RealtimeRingBufferWriter(RingBuffer<false,true>*ringBuffer)
|
||||
: RingBufferWriter<false, true> (ringBuffer)
|
||||
RealtimeRingBufferWriter(RingBuffer<false, true> *ringBuffer)
|
||||
: RingBufferWriter<false, true>(ringBuffer)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
} //namespace
|
||||
} // namespace
|
||||
+1
-1
@@ -295,7 +295,7 @@ namespace pipedal
|
||||
}
|
||||
}
|
||||
uint64_t instanceId;
|
||||
virtual bool IsLv2Effect() const override { return true; }
|
||||
virtual bool IsLv2Effect() const override { return false; }
|
||||
|
||||
virtual uint8_t *GetAtomInputBuffer() { return nullptr; }
|
||||
virtual uint8_t *GetAtomOutputBuffer() { return nullptr; }
|
||||
|
||||
@@ -1418,6 +1418,17 @@ void Storage::SetSystemMidiBindings(const std::vector<MidiBinding> &bindings)
|
||||
writer.write(bindings);
|
||||
}
|
||||
}
|
||||
static bool hasBinding(std::vector<MidiBinding> &bindings, const std::string&name)
|
||||
{
|
||||
for (auto&binding: bindings)
|
||||
{
|
||||
if (binding.symbol() == name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
std::vector<MidiBinding> Storage::GetSystemMidiBindings()
|
||||
{
|
||||
std::vector<MidiBinding> result;
|
||||
@@ -1441,6 +1452,20 @@ std::vector<MidiBinding> Storage::GetSystemMidiBindings()
|
||||
result.push_back(MidiBinding::SystemBinding("shutdown"));
|
||||
result.push_back(MidiBinding::SystemBinding("reboot"));
|
||||
}
|
||||
if (!hasBinding(result,"prevBank"))
|
||||
{
|
||||
result.insert(result.begin(),MidiBinding::SystemBinding("nextBank")); // reverse order.
|
||||
result.insert(result.begin(),MidiBinding::SystemBinding("prevBank"));
|
||||
}
|
||||
if (!hasBinding(result,"snapshot1"))
|
||||
{
|
||||
auto position = 4;
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
result.insert(result.begin()+position,MidiBinding::SystemBinding(SS("snapshot" << (i+1))));
|
||||
++position;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (const std::exception&e)
|
||||
@@ -1449,8 +1474,18 @@ std::vector<MidiBinding> Storage::GetSystemMidiBindings()
|
||||
}
|
||||
}
|
||||
result.clear();
|
||||
result.push_back(MidiBinding::SystemBinding("prevBank"));
|
||||
result.push_back(MidiBinding::SystemBinding("nextBank"));
|
||||
result.push_back(MidiBinding::SystemBinding("prevProgram"));
|
||||
result.push_back(MidiBinding::SystemBinding("nextProgram"));
|
||||
|
||||
result.push_back(MidiBinding::SystemBinding("snapshot1"));
|
||||
result.push_back(MidiBinding::SystemBinding("snapshot2"));
|
||||
result.push_back(MidiBinding::SystemBinding("snapshot3"));
|
||||
result.push_back(MidiBinding::SystemBinding("snapshot4"));
|
||||
result.push_back(MidiBinding::SystemBinding("snapshot5"));
|
||||
result.push_back(MidiBinding::SystemBinding("snapshot6"));
|
||||
|
||||
result.push_back(MidiBinding::SystemBinding("stopHotspot"));
|
||||
result.push_back(MidiBinding::SystemBinding("startHotspot"));
|
||||
result.push_back(MidiBinding::SystemBinding("shutdown"));
|
||||
|
||||
@@ -34,12 +34,12 @@ class Copyrights {
|
||||
static std::string trim(const std::string&value)
|
||||
{
|
||||
size_t start = 0;
|
||||
while (start < value.length() && value[start] == ' ' || value[start] == '\t')
|
||||
while (start < value.length() && (value[start] == ' ' || value[start] == '\t'))
|
||||
{
|
||||
++start;
|
||||
}
|
||||
size_t end = value.length();
|
||||
while (end > start && value[end-1] == ' ' || value[end-1] == '\t')
|
||||
while (end > start && (value[end-1] == ' ' || value[end-1] == '\t'))
|
||||
{
|
||||
--end;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
ToobFreeverb has a big memory leak!
|
||||
|
||||
Review state changing code. Do we need this anymmore? I think not. It is a bit sus.
|
||||
|
||||
|
||||
Review current handleNotifyPatchProperty. I tink server-side support needs to be
|
||||
removed, and rely on handleNotifyPathPatchPropertyChanged instead.
|
||||
@@ -10,26 +6,18 @@ removed, and rely on handleNotifyPathPatchPropertyChanged instead.
|
||||
|
||||
Midi bindings.
|
||||
|
||||
SEGV at void PiPedalModel::OnNotifyMaybeLv2StateChanged(uint64_t instanceId)
|
||||
bool changed = this->audioHost->UpdatePluginState(*item);
|
||||
PiPedalModel.cpp:375
|
||||
|
||||
because audioHost is gone.
|
||||
revamp shutdown procedure for the audio thread.
|
||||
write a marker on exit, and don't release
|
||||
audioHost UNTIL the service thread has read the close marker.
|
||||
|
||||
- continuing corrupt memory.
|
||||
|
||||
|
||||
- verify that scanning doesn't cause overruns.
|
||||
- verify address change behaviour in client
|
||||
-verify clean removal of dhcpcd and nm_p2p2d
|
||||
|
||||
X Review docs changes once we go live.
|
||||
|
||||
- Localisation: support non-UTF8 code pages.
|
||||
- unicode commandline arguments.
|
||||
- unicode commandline arguments. (probably the hotspot name)
|
||||
- review unicode filenames (this is probably ok)
|
||||
|
||||
- BUG: gcs when we have an animated output control
|
||||
|
||||
Reference in New Issue
Block a user