diff --git a/lv2/x86_64/toobamp_1.1.55_amd64.deb b/lv2/x86_64/toobamp_1.1.55_amd64.deb index d587a59..10b03ea 100644 Binary files a/lv2/x86_64/toobamp_1.1.55_amd64.deb and b/lv2/x86_64/toobamp_1.1.55_amd64.deb differ diff --git a/react/src/ControlTooltip.tsx b/react/src/ControlTooltip.tsx new file mode 100644 index 0000000..344c096 --- /dev/null +++ b/react/src/ControlTooltip.tsx @@ -0,0 +1,41 @@ + +import React, { ReactElement } from 'react'; +import Tooltip from "@mui/material/Tooltip" +import { UiControl } from './Lv2Plugin'; +import Typography from "@mui/material/Typography"; +import Divider from '@mui/material/Divider'; + + +interface ControlTooltipProps { + children: ReactElement, + uiControl: UiControl +} + + +export default function ControlTooltip(props: ControlTooltipProps) { + let { children, uiControl } = props; + if (uiControl.comment && (uiControl.comment !== uiControl.name)) { + return ( + + {uiControl.name} + + {uiControl.comment} + + + )} + placement="top-start" arrow enterDelay={1500} enterNextDelay={1500} + > + {children} + + ); + } else { + return ( + + {children} + + ); + } +} diff --git a/react/src/FilePropertyControl.tsx b/react/src/FilePropertyControl.tsx index a3d987b..f050d7c 100644 --- a/react/src/FilePropertyControl.tsx +++ b/react/src/FilePropertyControl.tsx @@ -34,6 +34,7 @@ import ButtonBase from '@mui/material/ButtonBase' import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; import {PedalboardItem} from './Pedalboard'; import {isDarkMode} from './DarkMode'; +import Tooltip from "@mui/material/Tooltip" export const StandardItemSize = { width: 80, height: 140 } @@ -208,10 +209,14 @@ const FilePropertyControl =
{/* TITLE SECTION */}
- {fileProperty.label} + + {fileProperty.label} +
{/* CONTROL SECTION */} diff --git a/react/src/MainPage.tsx b/react/src/MainPage.tsx index 00db0f4..1fc1607 100644 --- a/react/src/MainPage.tsx +++ b/react/src/MainPage.tsx @@ -288,7 +288,10 @@ export const MainPage = this.setSelection(selectedId); let item = this.getPedalboardItem(selectedId); if (item != null) { - if (item.isSplit()) { + if (item.isStart() || item.isEnd()) + { + // do nothing. + } else if (item.isSplit()) { let split = item as PedalboardSplitItem; if (split.getSplitType() === SplitType.Ab) { let cv = split.getToggleAbControlValue(); @@ -363,7 +366,7 @@ export const MainPage = titleBar(pedalboardItem: PedalboardItem | null): React.ReactNode { let title = ""; let author = ""; - let pluginUri = ""; + let infoPluginUri = ""; let presetsUri = ""; let missing = false; if (pedalboardItem) { @@ -375,7 +378,7 @@ export const MainPage = title = pedalboardItem.pluginName ?? "#error"; author = ""; presetsUri = ""; - pluginUri = ""; + infoPluginUri = ""; } else { let uiPlugin = this.model.getUiPlugin(pedalboardItem.uri); @@ -386,9 +389,9 @@ export const MainPage = title = uiPlugin.name; author = uiPlugin.author_name; presetsUri = uiPlugin.uri; - if (uiPlugin.description.length > 20) { - pluginUri = uiPlugin.uri; - } + // if (uiPlugin.description.length > 20) { + // } + infoPluginUri = uiPlugin.uri; } } } @@ -422,7 +425,7 @@ export const MainPage = )}
- +
2) result = 2; return result; @@ -125,12 +136,19 @@ const pedalboardStyles = (theme: Theme) => createStyles({ alignItems: "center", justifyContent: "center", }, + midiConnectorDecoration: { + position: "absolute", + left: -20, + top: -6, + fill: theme.palette.text.secondary + }, iconFrame: { display: "flex", alignItems: "center", justifyContent: "center", - + position: "relative", + background: theme.palette.background.default, marginLeft: (CELL_WIDTH - FRAME_SIZE) / 2, marginRight: (CELL_WIDTH - FRAME_SIZE) / 2, @@ -138,7 +156,7 @@ const pedalboardStyles = (theme: Theme) => createStyles({ marginBottom: (CELL_HEIGHT - FRAME_SIZE) / 2, width: FRAME_SIZE, height: FRAME_SIZE, - border: isDarkMode()? "1pt #AAA solid": "1pt #666 solid", + border: isDarkMode() ? "1pt #AAA solid" : "1pt #666 solid", borderRadius: 6 }, borderlessIconFrame: { @@ -146,7 +164,7 @@ const pedalboardStyles = (theme: Theme) => createStyles({ display: "flex", alignItems: "center", justifyContent: "center", - + background: "transparent", marginLeft: (CELL_WIDTH - FRAME_SIZE) / 2, marginRight: (CELL_WIDTH - FRAME_SIZE) / 2, @@ -227,6 +245,8 @@ class PedalLayout { numberOfInputs: number = 2; numberOfOutputs: number = 2; + originalInputs: number = 2; + originalOutputs: number = 2; pedalItem?: PedalboardItem; @@ -286,11 +306,17 @@ class PedalLayout { this.iconUrl = TERMINAL_ICON_URL; this.numberOfInputs = 0; this.numberOfOutputs = PiPedalModelFactory.getInstance().jackSettings.get().inputAudioPorts.length; + if (this.numberOfOutputs === 0) { + this.numberOfOutputs = 1; + } } else if (pedalItem.uri === END_PEDALBOARD_ITEM_URI) { this.pluginType = PluginType.UtilityPlugin; this.iconUrl = TERMINAL_ICON_URL; this.numberOfInputs = PiPedalModelFactory.getInstance().jackSettings.get().outputAudioPorts.length; + if (this.numberOfInputs === 0) { + this.numberOfInputs = 1; + } this.numberOfOutputs = 0; } else { @@ -299,17 +325,19 @@ class PedalLayout { this.pluginType = uiPlugin.plugin_type; this.iconUrl = SelectIconUri(uiPlugin.plugin_type); this.name = uiPlugin.label; - this.numberOfInputs = Math.max(uiPlugin.audio_inputs,2); - this.numberOfOutputs = Math.max(uiPlugin.audio_outputs,2); + this.numberOfInputs = Math.min(uiPlugin.audio_inputs, 2); + this.numberOfOutputs = Math.min(uiPlugin.audio_outputs, 2); } else { // default to empty plugin. this.pluginType = PluginType.ErrorPlugin; - this.name = pedalItem.pluginName??"#error"; + this.name = pedalItem.pluginName ?? "#error"; this.iconUrl = ERROR_ICON_URL; this.numberOfInputs = 2; this.numberOfOutputs = 2; } } + this.originalInputs = this.numberOfInputs; + this.originalOutputs = this.numberOfOutputs; } isEmpty(): boolean { return (!this.pedalItem) || this.pedalItem.isEmpty(); @@ -362,8 +390,7 @@ class LayoutParams { const PedalboardView = withStyles(pedalboardStyles, { withTheme: true })( - class extends Component - { + class extends Component { model: PiPedalModel; frameRef: React.RefObject; @@ -386,15 +413,13 @@ const PedalboardView = this.handleTouchStart = this.handleTouchStart.bind(this); } - handleTouchStart(e: any) - { + handleTouchStart(e: any) { // just has to exist to allow Draggable to receive // touchyMove. :-/ } onDragEnd(instanceId: number, clientX: number, clientY: number) { - if (!this.props.enableStructureEditing) - { + if (!this.props.enableStructureEditing) { return; } if (!this.currentLayout) return; @@ -462,9 +487,8 @@ const PedalboardView = return; } } - let lastBottom = item.bottomChildren[item.bottomChildren.length-1]; - if (clientX >= lastBottom.bounds.right && clientX < item.bounds.right-CELL_WIDTH/2) - { + let lastBottom = item.bottomChildren[item.bottomChildren.length - 1]; + if (clientX >= lastBottom.bounds.right && clientX < item.bounds.right - CELL_WIDTH / 2) { if (lastBottom.pedalItem) { this.model.movePedalboardItemAfter(instanceId, lastBottom.pedalItem.instanceId); this.setSelection(instanceId); @@ -485,8 +509,7 @@ const PedalboardView = this.setSelection(instanceId); return; } else { - if (item.pedalItem) - { + if (item.pedalItem) { let margin = (CELL_WIDTH - FRAME_SIZE) / 2; if (clientX < item.bounds.x + margin) { this.model.movePedalboardItemBefore(instanceId, item.pedalItem.instanceId); @@ -513,12 +536,12 @@ const PedalboardView = } componentDidMount() { - this.scrollRef.current!.addEventListener("touchstart",this.handleTouchStart, {passive: false}); + this.scrollRef.current!.addEventListener("touchstart", this.handleTouchStart, { passive: false }); this.model.pedalboard.addOnChangedHandler(this.onPedalboardChanged); } componentWillUnmount() { - this.scrollRef.current!.removeEventListener("touchstart",this.handleTouchStart); + this.scrollRef.current!.removeEventListener("touchstart", this.handleTouchStart); this.model.pedalboard.removeOnChangedHandler(this.onPedalboardChanged); } @@ -578,11 +601,11 @@ const PedalboardView = topBounds.height = CELL_HEIGHT; } - - let dyTop = (lp.cy + CELL_HEIGHT / 2)- (topBounds.y + topBounds.height) ; - + let dyTop = (lp.cy + CELL_HEIGHT / 2) - (topBounds.y + topBounds.height); + + this.offsetLayout_(layoutItem.topChildren, dyTop); topBounds.offset(0, dyTop); @@ -597,8 +620,8 @@ const PedalboardView = bottomBounds.x = lp.cx; bottomBounds.width = 0; bottomBounds.y = lp.cy; bottomBounds.height = CELL_HEIGHT; } - - let dyBottom = (lp.cy+CELL_HEIGHT/2)-bottomBounds.y ; + + let dyBottom = (lp.cy + CELL_HEIGHT / 2) - bottomBounds.y; this.offsetLayout_(layoutItem.bottomChildren, dyBottom) bottomBounds.offset(0, dyBottom); bounds.accumulate(bottomBounds); @@ -633,11 +656,10 @@ const PedalboardView = } doLayout(layoutItems: PedalLayout[]): LayoutSize { const TWO_ROW_HEIGHT = 142 - 14; - - if (layoutItems.length === 0) - { + + if (layoutItems.length === 0) { // if the current pedalboard is empty, reserve display space anyway. - return {width: 1, height: TWO_ROW_HEIGHT}; + return { width: 1, height: TWO_ROW_HEIGHT }; } let lp = new LayoutParams(); @@ -646,9 +668,9 @@ const PedalboardView = // shift everything down so there are no negative y coordinates. if (bounds.height < TWO_ROW_HEIGHT) { - - let extra = Math.floor((TWO_ROW_HEIGHT - Math.ceil(bounds.height))/2); - this.offsetLayout_(layoutItems, Math.floor(-bounds.y + extra/2 )); + + let extra = Math.floor((TWO_ROW_HEIGHT - Math.ceil(bounds.height)) / 2); + this.offsetLayout_(layoutItems, Math.floor(-bounds.y + extra / 2)); bounds.height += extra; } else { @@ -682,15 +704,13 @@ const PedalboardView = } onItemLongClick(event: SyntheticEvent, instanceId?: number): void { - if (!instanceId) - { + if (!instanceId) { return; } event.preventDefault(); event.stopPropagation(); - if (!this.props.enableStructureEditing) - { + if (!this.props.enableStructureEditing) { this.setSelection(instanceId); return; } @@ -702,6 +722,24 @@ const PedalboardView = } + strokeConnector(output: ReactNode[],channels: number,enabled: Boolean,svgPath: string) + { + let color = enabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; + + if (channels === 2) { + output.push(( + + )); + output.push(( + + )); + } else if (channels === 1) { + output.push(( + + )); + } + } + renderConnector(output: ReactNode[], item: PedalLayout, enabled: boolean): void { // let classes = this.props.classes; let x_ = item.bounds.x + CELL_WIDTH / 2; @@ -709,19 +747,35 @@ const PedalboardView = let numberOfOutputs = item.numberOfOutputs; let color = enabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; let stereoCenterColor = this.bgColor; + + if (item.originalInputs === 0) { + // break the input paths. + let rx = item.bounds.x + CELL_WIDTH / 2 - FRAME_SIZE / 2 - 4; + let ry = y_ - 4; + + output.push(( + + )); + } let svgPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_ + CELL_WIDTH, y_).toString(); + if (numberOfOutputs === 2) { output.push(( - + )); output.push(( - + )); } else if (numberOfOutputs === 1) { output.push(( - + )); + } else { + output.push(( + + )); + } } renderSplitConnectors(output: ReactNode[], item: PedalLayout, enabled: boolean, shortSplitOutput: boolean,): void { @@ -743,18 +797,18 @@ const PedalboardView = let bottomStartPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_, yBottom).lineTo(x_ + CELL_WIDTH, yBottom).toString(); if (item.numberOfInputs === 2 && item.topChildren[0].numberOfInputs === 2) { - output.push(()); - output.push(()); + output.push(()); + output.push(()); } else if (item.numberOfInputs !== 0 && item.topChildren[0].numberOfInputs !== 0) { - output.push(()); + output.push(()); } if (item.numberOfInputs === 2 && item.bottomChildren[0].numberOfInputs === 2) { - output.push(()); - output.push(()); + output.push(()); + output.push(()); } else if (item.numberOfInputs !== 0 && item.bottomChildren[0].numberOfInputs !== 0) { - output.push(()); + output.push(()); } let lastTop = item.topChildren[item.topChildren.length - 1]; @@ -798,7 +852,7 @@ const PedalboardView = secondPath = new SvgPathBuilder().moveTo(xTop, yTop).lineTo(xTee, yTop).lineTo(xTee, y_).toString(); hasThirdPath = true; - thirdPath= new SvgPathBuilder().moveTo(xTee0,y_).lineTo(xEnd,y_).toString(); + thirdPath = new SvgPathBuilder().moveTo(xTee0, y_).lineTo(xEnd, y_).toString(); firstPathEnabled = bottomEnabled; secondPathEnabled = topEnabled; } else if (bottomPathFirst || (topEnabled && lastTop.numberOfOutputs === 2)) { @@ -838,26 +892,26 @@ const PedalboardView = // display stereo strokes with cutoff line. if (firstPathStereo) { output.push(( - + )); output.push(( - + )); } else if (!firstPathAbsent) { output.push(( - + )); } if (secondPathStereo) { output.push(( - + )); output.push(( - + )); } else if (!secondPathAbsent) { output.push(( - + )); } @@ -865,57 +919,53 @@ const PedalboardView = // stereo strokes merge. if (firstPathStereo) { output.push(( - + )); } else if (!firstPathAbsent) { output.push(( - + )); } if (secondPathStereo) { output.push(( - + )); } else if (!secondPathAbsent) { output.push(( - + )); } // draw stereo inner lines. if (firstPathStereo) { output.push(( - + )); } if (secondPathStereo) { output.push(( - + )); } } - if (thirdPath != null) - { + if (thirdPath != null) { // stereo output of L/R splitter output.push(( - + )); output.push(( - + )); } } - getScrollContainer() - { - let el: HTMLElement | undefined | null= this.scrollRef.current; + getScrollContainer() { + let el: HTMLElement | undefined | null = this.scrollRef.current; // actually not here anymore. :-/ It has a reactive definition in MainPage.tsx now. - while (el) - { - if (el.id === "pedalboardScroll") - { + while (el) { + if (el.id === "pedalboardScroll") { return el as HTMLDivElement; } el = el.parentElement; @@ -924,16 +974,24 @@ const PedalboardView = } pedalButton( - instanceId: number, - iconType: PluginType, - draggable: boolean, + instanceId: number, + iconType: PluginType, + draggable: boolean, enabled: boolean, hasBorder: boolean = true, - pluginNotFound: boolean) - : ReactNode { + pluginNotFound: boolean, + hasMidiConnector: boolean + ) + : ReactNode { let classes = this.props.classes; return ( -
{ e.preventDefault(); }}> +
{ e.preventDefault(); }}> + {hasMidiConnector && ( +
+ +
+ + )} { this.onItemClick(e, instanceId); }} @@ -941,10 +999,10 @@ const PedalboardView = onContextMenu={(e: SyntheticEvent) => { this.onItemLongClick(e, instanceId); }} > - this.getScrollContainer()} + this.getScrollContainer()} onDragEnd={(x, y) => { this.onDragEnd(instanceId, x, y) }} > - + @@ -975,16 +1033,16 @@ const PedalboardView = let outputs: ReactNode[] = []; this.renderConnectors(outputs, layoutChain, true, false); return ( -
- - - { - outputs - } - +
+ + + { + outputs + } + - +
); @@ -1008,7 +1066,7 @@ const PedalboardView = result.push(
- {this.pedalButton(START_CONTROL, item.pluginType,false,true,false,false)} + {this.pedalButton(START_CONTROL, item.pluginType, false, true, false, false, false)}
); break; @@ -1016,7 +1074,7 @@ const PedalboardView = result.push(
- {this.pedalButton(END_CONTROL, item.pluginType, false,true,false,false)} + {this.pedalButton(END_CONTROL, item.pluginType, false, true, false, false, false)}
); break; @@ -1025,7 +1083,7 @@ const PedalboardView = result.push(
- {this.pedalButton(item.pedalItem?.instanceId ?? -1, this.getSplitterIcon(item), false,true,true,false)} + {this.pedalButton(item.pedalItem?.instanceId ?? -1, this.getSplitterIcon(item), false, true, true, false, false)}
); @@ -1040,10 +1098,18 @@ const PedalboardView = >{item.name}
) - let uiPlugin = this.model.getUiPlugin(item.pedalItem?.uri??""); + let uiPlugin = this.model.getUiPlugin(item.pedalItem?.uri ?? ""); let pluginMissing = uiPlugin == null; + result.push(
- {this.pedalButton(item.pedalItem?.instanceId ?? -1, item.pluginType, !item.isEmpty(), item.pedalItem?.isEnabled ?? false,true,pluginMissing)} + {this.pedalButton( + item.pedalItem?.instanceId ?? -1, + item.pluginType, + !item.isEmpty(), + item.pedalItem?.isEnabled ?? false, + true, + pluginMissing, + uiPlugin ? ((uiPlugin.has_midi_input !== 0) || (uiPlugin.has_midi_output !== 0)) : false)}
); @@ -1090,51 +1156,43 @@ const PedalboardView = this.markStereoBackward(layoutChain, numberOfOutputs); } - markStereoBackward(layoutChain: PedalLayout[], numberOfOutputs: number) : number - { - for (let i = layoutChain.length-1; i >= 0; --i) - { + markStereoBackward(layoutChain: PedalLayout[], numberOfOutputs: number): number { + for (let i = layoutChain.length - 1; i >= 0; --i) { let item = layoutChain[i]; - if (item.isSplitter()) - { + if (item.isSplitter()) { item.numberOfOutputs = CalculateConnection(item.numberOfOutputs, numberOfOutputs) - this.markStereoBackward(item.topChildren,numberOfOutputs); - this.markStereoBackward(item.bottomChildren,numberOfOutputs); + this.markStereoBackward(item.topChildren, numberOfOutputs); + this.markStereoBackward(item.bottomChildren, numberOfOutputs); let topInputs = item.topChildren[0].numberOfInputs; let bottomInputs = item.bottomChildren[0].numberOfInputs; let splitItem = item.pedalItem as PedalboardSplitItem; - if (splitItem.getSplitType() !== SplitType.Lr) - { - item.numberOfInputs = CalculateConnection(item.numberOfInputs, Math.max(topInputs,bottomInputs)); + if (splitItem.getSplitType() !== SplitType.Lr) { + item.numberOfInputs = CalculateConnection(item.numberOfInputs, Math.max(topInputs, bottomInputs)); } - } else if (item.isEnd()) - { + } else if (item.isEnd()) { - } else if (item.isStart()) - { - item.numberOfOutputs = CalculateConnection(item.numberOfOutputs,numberOfOutputs); + } else if (item.isStart()) { + item.numberOfOutputs = CalculateConnection(item.numberOfOutputs, numberOfOutputs); return item.numberOfOutputs; } else if (item.isEmpty()) { - if (numberOfOutputs === 0) - { + if (numberOfOutputs === 0) { item.numberOfOutputs = 0; - item.numberOfInputs = CalculateConnection(item.numberOfInputs,2); + item.numberOfInputs = CalculateConnection(item.numberOfInputs, 2); } else { - item.numberOfOutputs = CalculateConnection(item.numberOfOutputs,numberOfOutputs); - item.numberOfInputs = CalculateConnection(item.numberOfInputs,numberOfOutputs); + item.numberOfOutputs = CalculateConnection(item.numberOfOutputs, numberOfOutputs); + item.numberOfInputs = CalculateConnection(item.numberOfInputs, numberOfOutputs); } } else { - item.numberOfOutputs = CalculateConnection(item.numberOfOutputs,numberOfOutputs); + item.numberOfOutputs = CalculateConnection(item.numberOfOutputs, numberOfOutputs); } numberOfOutputs = item.numberOfInputs; } return numberOfOutputs; } - markStereoForward(layoutChain: PedalLayout[],numberOfInputs: number): number - { + markStereoForward(layoutChain: PedalLayout[], numberOfInputs: number): number { if (layoutChain.length === 0) { return numberOfInputs; } @@ -1145,9 +1203,8 @@ const PedalboardView = item.numberOfInputs = numberOfInputs; let chainInputs = numberOfInputs; - if (splitter.getSplitType() === SplitType.Lr) - { - chainInputs = CalculateConnection(numberOfInputs,1); + if (splitter.getSplitType() === SplitType.Lr) { + chainInputs = CalculateConnection(numberOfInputs, 1); } let topOutputs = this.markStereoForward(item.topChildren, chainInputs); let bottomOutputs = this.markStereoForward(item.bottomChildren, chainInputs); @@ -1160,28 +1217,32 @@ const PedalboardView = item.numberOfOutputs = bottomOutputs; } } else { - item.numberOfOutputs = (topOutputs >= 1 || bottomOutputs >= 1) ? 2: 1; - } - } else if (item.isStart()) - { - item.numberOfOutputs = Math.min(PiPedalModelFactory.getInstance().jackSettings.get().inputAudioPorts.length,2); + item.numberOfOutputs = (topOutputs >= 1 || bottomOutputs >= 1) ? 2 : 1; + } + } else if (item.isStart()) { + item.numberOfOutputs = Math.min(PiPedalModelFactory.getInstance().jackSettings.get().inputAudioPorts.length, 2); } else if (item.isEnd()) { - item.numberOfInputs = + item.numberOfInputs = CalculateConnection( - Math.min(PiPedalModelFactory.getInstance().jackSettings.get().outputAudioPorts.length,2), + Math.min(PiPedalModelFactory.getInstance().jackSettings.get().outputAudioPorts.length, 2), numberOfInputs); return item.numberOfInputs; } else if (item.isEmpty()) { item.numberOfInputs = numberOfInputs; - if (numberOfInputs === 0) - { + if (numberOfInputs === 0) { item.numberOfOutputs = 2; } else { item.numberOfOutputs = item.numberOfInputs; } - } else { - item.numberOfInputs = CalculateConnection(numberOfInputs,this.getNumberOfInputs(item)); - item.numberOfOutputs = this.getNumberOfOutputs(item); + } else { + if (item.numberOfInputs === 0) // zero-input plugins merge their output with the input. + { + item.numberOfInputs = numberOfInputs; + item.numberOfOutputs = Math.max(item.numberOfOutputs, numberOfInputs); + } else { + item.numberOfInputs = CalculateConnection(numberOfInputs, this.getNumberOfInputs(item)); + item.numberOfOutputs = this.getNumberOfOutputs(item); + } } numberOfInputs = item.numberOfOutputs; } @@ -1196,21 +1257,20 @@ const PedalboardView = let layoutChain = makeChain(this.model, this.state.pedalboard?.items); let start = PedalLayout.Start(); let end = PedalLayout.End(); - if (layoutChain.length !== 0) - { + if (layoutChain.length !== 0) { layoutChain.splice(0, 0, start); layoutChain.splice(layoutChain.length, 0, end); - this.markStereoOutputs(layoutChain, 2,2); + this.markStereoOutputs(layoutChain, 2, 2); } let layoutSize = this.doLayout(layoutChain); - + this.currentLayout = layoutChain; // save for mouse processing &c. return (
+ >
{/* TITLE SECTION */}
- {isTrigger ? "\u00A0" : control.name} + + {isTrigger ? "\u00A0" : control.name} +
{/* CONTROL SECTION */} diff --git a/react/src/PluginControlView.tsx b/react/src/PluginControlView.tsx index 9027588..dcf77d2 100644 --- a/react/src/PluginControlView.tsx +++ b/react/src/PluginControlView.tsx @@ -40,6 +40,7 @@ import JsonAtom from './JsonAtom'; import PluginOutputControl from './PluginOutputControl'; import Units from './Units'; import ToobFrequencyResponseView from './ToobFrequencyResponseView'; +import Tooltip from '@mui/material/Tooltip'; export const StandardItemSize = { width: 80, height: 110 }; @@ -553,7 +554,11 @@ const PluginControlView = result.push((
- {controlGroup.name} + + {controlGroup.name} +
{ diff --git a/react/src/PluginInfoDialog.tsx b/react/src/PluginInfoDialog.tsx index 6d9e887..fb5d79d 100644 --- a/react/src/PluginInfoDialog.tsx +++ b/react/src/PluginInfoDialog.tsx @@ -37,11 +37,10 @@ import { UiPlugin, UiControl } from './Lv2Plugin'; import PluginIcon from './PluginIcon'; import { Remark } from 'react-remark'; - /* eslint-disable */ -let myTheme: Theme| undefined = undefined; +/* eslint-disable */ +let myTheme: Theme | undefined = undefined; -const styles = (theme: Theme) => -{ +const styles = (theme: Theme) => { myTheme = theme; return createStyles({ root: { @@ -151,25 +150,26 @@ function makeControls(controls: UiControl[]) { break; } } + hasComments = true; if (hasComments) { let trs: React.ReactElement[] = []; for (let i = 0; i < controls.length; ++i) { let control = controls[i]; if (!(control.not_on_gui) && control.is_input) - trs.push(( - - - - {control.name} - - - - - {control.comment} - - - - )); + trs.push(( + + + + {control.name} + + + + + {control.comment} + + + + )); } return ( @@ -235,7 +235,7 @@ const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => { {open && ( - @@ -258,18 +258,18 @@ const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => {
-
+
Author:  {(plugin.author_homepage !== "") ? ( - {plugin.author_name} + {plugin.author_name} ) : ( {plugin.author_name} ) - + }
@@ -286,40 +286,44 @@ const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => { { makeControls(plugin.controls) } - - Description: - - + )} - diff --git a/react/src/PluginOutputControl.tsx b/react/src/PluginOutputControl.tsx index 2ed160a..0d1e231 100644 --- a/react/src/PluginOutputControl.tsx +++ b/react/src/PluginOutputControl.tsx @@ -24,10 +24,11 @@ import createStyles from '@mui/styles/createStyles'; import withStyles from '@mui/styles/withStyles'; import { UiControl } from './Lv2Plugin'; import Typography from '@mui/material/Typography'; -import { PiPedalModel, PiPedalModelFactory, MonitorPortHandle,State } from './PiPedalModel'; -import {isDarkMode} from './DarkMode'; +import { PiPedalModel, PiPedalModelFactory, MonitorPortHandle, State } from './PiPedalModel'; +import { isDarkMode } from './DarkMode'; import GxTunerControl from './GxTunerControl'; import Units from './Units'; +import ControlTooltip from './ControlTooltip'; @@ -74,8 +75,7 @@ type PluginOutputControlState = { const PluginOutputControl = withStyles(styles, { withTheme: true })( - class extends Component - { + class extends Component { private model: PiPedalModel; private vuRef: React.RefObject; @@ -98,10 +98,8 @@ const PluginOutputControl = this.onConnectionStateChanged = this.onConnectionStateChanged.bind(this); } - private onConnectionStateChanged(state: State) - { - if (state === State.Ready) - { + private onConnectionStateChanged(state: State) { + if (state === State.Ready) { this.unsubscribe(); this.subscribe(); } @@ -127,40 +125,36 @@ const PluginOutputControl = } componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any): void { - if (prevProps.instanceId !== this.props.instanceId) - { + if (prevProps.instanceId !== this.props.instanceId) { this.unsubscribe(); this.subscribe(); } } - private VU_HEIGHT = 60-4; - private DB_VU_HEIGHT = 60-4; + private VU_HEIGHT = 60 - 4; + private DB_VU_HEIGHT = 60 - 4; private animationHandle: number | undefined = undefined; - + private dbVuTelltale = -96.0; private dbVuValue = -96.0; private dbVuHoldTime = 0.0; private requestDbVuAnimation() { - if (!this.animationHandle) - { + if (!this.animationHandle) { this.animationHandle = requestAnimationFrame( - ()=> { + () => { let value = this.dbVuValue; let range = this.vuMap(value); - let top = range-this.VU_HEIGHT; + let top = range - this.VU_HEIGHT; if (top > 0) top = 0; - - if (value > this.dbVuTelltale) - { + + if (value > this.dbVuTelltale) { this.dbVuTelltale = value; - this.dbVuHoldTime = Date.now()+2000; + this.dbVuHoldTime = Date.now() + 2000; } - if (this.dbVuRef.current) - { - this.dbVuRef.current.style.marginTop = top+"px"; + if (this.dbVuRef.current) { + this.dbVuRef.current.style.marginTop = top + "px"; } this.animationHandle = undefined; this.updateDbVuTelltale(); @@ -171,19 +165,16 @@ const PluginOutputControl = updateDbVuTelltale() { let telltaleDone = true; - if (this.dbVuHoldTime !== 0) - { + if (this.dbVuHoldTime !== 0) { telltaleDone = false; let t = Date.now(); - if (t >= this.dbVuHoldTime) - { - let dt = t-this.dbVuHoldTime; - let telltaleValue = this.dbVuTelltale-30*dt/1000; - if (telltaleValue < -200) - { + if (t >= this.dbVuHoldTime) { + let dt = t - this.dbVuHoldTime; + let telltaleValue = this.dbVuTelltale - 30 * dt / 1000; + if (telltaleValue < -200) { telltaleValue = -200; telltaleDone = true; - } + } this.dbVuTelltale = telltaleValue; this.dbVuHoldTime = t; @@ -191,49 +182,41 @@ const PluginOutputControl = let y = this.dbVuMap(this.dbVuTelltale); if (y < 0) y = 0; - if (this.dbVuTelltaleRef.current) - { + if (this.dbVuTelltaleRef.current) { let telltaleStyle = this.dbVuTelltaleRef.current.style; telltaleStyle.marginTop = y + "px"; let telltaleColor = "#0C0"; - if (this.dbVuTelltale >= 0) - { + if (this.dbVuTelltale >= 0) { telltaleColor = "#F00"; - } else if (this.dbVuTelltale >= -10) - { + } else if (this.dbVuTelltale >= -10) { telltaleColor = "#FF0"; } telltaleStyle.background = telltaleColor; } } - if (!telltaleDone) - { + if (!telltaleDone) { this.requestDbVuAnimation(); } } private updateValue(value: number) { - if (this.lampRef.current) - { + if (this.lampRef.current) { let control = this.props.uiControl; - let range = (value-control.min_value)/(control.max_value-control.min_value); - this.lampRef.current.style.opacity = range +""; - } else if (this.dbVuRef.current) - { + let range = (value - control.min_value) / (control.max_value - control.min_value); + this.lampRef.current.style.opacity = range + ""; + } else if (this.dbVuRef.current) { this.dbVuValue = value; this.requestDbVuAnimation(); } else if (this.vuRef.current) { let control = this.props.uiControl; - let range = (value-control.min_value)/(control.max_value-control.min_value); - let top = this.VU_HEIGHT-range*this.VU_HEIGHT; + let range = (value - control.min_value) / (control.max_value - control.min_value); + let top = this.VU_HEIGHT - range * this.VU_HEIGHT; - if (!this.animationHandle) - { + if (!this.animationHandle) { this.animationHandle = requestAnimationFrame( - ()=> { - if (this.vuRef.current) - { - this.vuRef.current.style.marginTop = top+"px"; + () => { + if (this.vuRef.current) { + this.vuRef.current.style.marginTop = top + "px"; } this.animationHandle = undefined; } @@ -284,13 +267,13 @@ const PluginOutputControl = dbVuMap(value: number): number { let control = this.props.uiControl; - let y = (control.max_value-value)*this.DB_VU_HEIGHT/(control.max_value-control.min_value); + let y = (control.max_value - value) * this.DB_VU_HEIGHT / (control.max_value - control.min_value); return y; } vuMap(value: number): number { let control = this.props.uiControl; - let y = (control.max_value-value)*this.VU_HEIGHT/(control.max_value-control.min_value); + let y = (control.max_value - value) * this.VU_HEIGHT / (control.max_value - control.min_value); return y; } @@ -317,20 +300,18 @@ const PluginOutputControl = item_width = 80; } } - if (control.isTuner()) - { + if (control.isTuner()) { item_width = undefined; return (
- +
); - } else if (control.isDbVu()) - { + } else if (control.isDbVu()) { item_width = undefined; let redLevel = this.dbVuMap(0); let yellowLevel = this.dbVuMap(-10); @@ -338,22 +319,24 @@ const PluginOutputControl =
{/* TITLE SECTION */}
- {control.name === "" ? "\u00A0" : control.name} + + {control.name === "" ? "\u00A0" : control.name} +
{/* CONTROL SECTION */} -
-
+
+
-
-
-
+
+
+
-
-
+
+
@@ -369,15 +352,17 @@ const PluginOutputControl =
{/* TITLE SECTION */}
- {control.name === "" ? "\u00A0" : control.name} + + {control.name === "" ? "\u00A0" : control.name} +
{/* CONTROL SECTION */} -
-
+
+
@@ -394,26 +379,31 @@ const PluginOutputControl = ); } else if (control.isLamp()) { item_width = undefined; - let attachedLamp = control.name === "" || control.name === "\u00A0" ; + let attachedLamp = control.name === "" || control.name === "\u00A0"; return ( -
+
{/* TITLE SECTION */}
- {control.name === "" ? "\u00A0": (control.name)} + + {control.name === "" ? "\u00A0" : (control.name)} +
{/* CONTROL SECTION */}
-
-
+
+
@@ -429,11 +419,14 @@ const PluginOutputControl =
{/* TITLE SECTION */}
- {control.name} + + {control.name} +
+ {/* CONTROL SECTION */}
@@ -449,15 +442,17 @@ const PluginOutputControl =
); - }else { + } else { return (
{/* TITLE SECTION */}
- {control.name} + + {control.name} +
{/* CONTROL SECTION */} diff --git a/src/AlsaDriver.cpp b/src/AlsaDriver.cpp index 47d1c3a..9b14d64 100644 --- a/src/AlsaDriver.cpp +++ b/src/AlsaDriver.cpp @@ -1257,7 +1257,7 @@ namespace pipedal { message = SS("Device " << alsa_device_name << " in use. The following applications are using your soundcard: " << apps - << ". Stop them as neccesary before trying to restart pipedald."); + << ". Stop them as neccesary before trying to pipedald."); } else { @@ -1671,11 +1671,16 @@ namespace pipedal pBuffer[j] = 0; } } - while (!terminateAudio()) + try { + while (!terminateAudio()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + // zero out input buffers. + this->driverHost->OnProcess(this->bufferSize); + } + } catch (const std::exception &e) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - // zero out input buffers. - this->driverHost->OnProcess(this->bufferSize); + } } this->driverHost->OnAudioTerminated(); diff --git a/src/AudioHost.cpp b/src/AudioHost.cpp index 36c62aa..89af2c6 100644 --- a/src/AudioHost.cpp +++ b/src/AudioHost.cpp @@ -1217,7 +1217,7 @@ class AudioHostImpl : public AudioHost, private AudioDriverHost, private IPatchW } catch (const std::exception &e) { - Lv2Log::error("Fatal error while processing jack audio. (%s)", e.what()); + Lv2Log::error("Fatal error while processing realtime audio. (%s)", e.what()); throw; } } diff --git a/src/FilePropertyDirectoryTree.cpp b/src/FilePropertyDirectoryTree.cpp index c73bbed..0098873 100644 --- a/src/FilePropertyDirectoryTree.cpp +++ b/src/FilePropertyDirectoryTree.cpp @@ -18,6 +18,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "FilePropertyDirectoryTree.hpp" +#include using namespace pipedal; namespace fs = std::filesystem; diff --git a/src/IEffect.hpp b/src/IEffect.hpp index 520c19b..d502d01 100644 --- a/src/IEffect.hpp +++ b/src/IEffect.hpp @@ -58,6 +58,7 @@ namespace pipedal { virtual bool GetRequestStateChangedNotification() const = 0; virtual void SetRequestStateChangedNotification(bool value) = 0; + virtual void PrepareNoInputEffect(int numberOfInputs, size_t maxBufferSize) = 0; virtual void SetAudioInputBuffer(int index, float *buffer) = 0; virtual void SetAudioOutputBuffer(int index, float*buffer) = 0; diff --git a/src/Lv2Effect.cpp b/src/Lv2Effect.cpp index 3f4863d..75a86c4 100644 --- a/src/Lv2Effect.cpp +++ b/src/Lv2Effect.cpp @@ -47,7 +47,6 @@ namespace fs = std::filesystem; const float BYPASS_TIME_S = 0.1f; - static fs::path makeAbsolutePath(const std::filesystem::path &path, const std::filesystem::path &parentPath) { if (path.is_absolute()) @@ -57,7 +56,6 @@ static fs::path makeAbsolutePath(const std::filesystem::path &path, const std::f return parentPath / path; } - Lv2Effect::Lv2Effect( IHost *pHost_, const std::shared_ptr &info_, @@ -83,9 +81,9 @@ Lv2Effect::Lv2Effect( this->pathPropertyWriters.push_back(PatchPropertyWriter(instanceId, filePropertyUrid)); } } - for (auto &pathProperty: pedalboardItem.pathProperties_) + for (auto &pathProperty : pedalboardItem.pathProperties_) { - SetPathPatchProperty(pathProperty.first,pathProperty.second); + SetPathPatchProperty(pathProperty.first, pathProperty.second); } // initialize the atom forge used on the realtime thread. @@ -112,7 +110,6 @@ Lv2Effect::Lv2Effect( bundleUriString, storagePath); - mapPathFeature.Prepare(&(pHost_->GetMapFeature())); mapPathFeature.SetPluginStoragePath(pHost_->GetPluginStoragePath()); if (info->piPedalUI()) @@ -122,10 +119,8 @@ Lv2Effect::Lv2Effect( { if (!fileProperty->resourceDirectory().empty()) { - mapPathFeature.AddResourceFileMapping({ - makeAbsolutePath(fileProperty->resourceDirectory(),bundleUriString), - makeAbsolutePath(fileProperty->directory(),pHost_->GetPluginStoragePath()) - }); + mapPathFeature.AddResourceFileMapping({makeAbsolutePath(fileProperty->resourceDirectory(), bundleUriString), + makeAbsolutePath(fileProperty->directory(), pHost_->GetPluginStoragePath())}); } } } @@ -145,7 +140,6 @@ Lv2Effect::Lv2Effect( this->features.push_back(mapPathFeature.GetMakePathFeature()); this->features.push_back(mapPathFeature.GetFreePathFeature()); - this->features.push_back(this->fileBrowserFilesFeature.GetFeature()); this->work_schedule_feature = nullptr; @@ -370,15 +364,52 @@ void Lv2Effect::PreparePortIndices() outputAtomBuffers.resize(outputAtomPortIndices.size()); } -void Lv2Effect::SetAudioInputBuffer(int index, float *buffer) +void Lv2Effect::PrepareNoInputEffect(int numberOfInputs, size_t maxBufferSize) { - if (index >= inputAudioPortIndices.size()) + if (outputAudioPortIndices.size() == 0) + { + // pass the input through unmodified. + inputAudioBuffers.resize(std::max((size_t)numberOfInputs, inputAudioPortIndices.size())); + outputAudioBuffers.resize(numberOfOutputs); + } + else if (inputAudioPortIndices.size() == 0) { - throw PiPedalArgumentException("Buffer index out of range."); + inputAudioBuffers.resize(numberOfInputs); + outputAudioBuffers.resize(std::max((size_t)numberOfInputs, outputAudioPortIndices.size())); + + // allocate a working buffer which we will mix with passed-through data. + outputMixBuffers.resize(outputAudioPortIndices.size()); + for (size_t i = 0; i < outputMixBuffers.size(); ++i) + { + outputMixBuffers[i].resize(maxBufferSize); + } + // connect the plugin to the mix buffer instead of output buffer. + for (size_t i = 0; i < outputAudioPortIndices.size(); ++i) + { + int pluginIndex = this->outputAudioPortIndices[i]; + lilv_instance_connect_port(this->pInstance, pluginIndex, outputMixBuffers[i].data()); + } } +} + +void Lv2Effect::SetAudioInputBuffer(int index, float *buffer) +{ this->inputAudioBuffers[index] = buffer; - int pluginIndex = this->inputAudioPortIndices[index]; - lilv_instance_connect_port(this->pInstance, pluginIndex, buffer); + + if (inputAudioPortIndices.size() == inputAudioBuffers.size()) + { + int pluginIndex = this->inputAudioPortIndices[index]; + lilv_instance_connect_port(this->pInstance, pluginIndex, buffer); + } + else + { + // cases: 1->0, 1->1, 2->0, 2->1 + if (index < inputAudioPortIndices.size()) + { + int pluginIndex = this->inputAudioPortIndices[index]; + lilv_instance_connect_port(this->pInstance, pluginIndex, buffer); + } + } } void Lv2Effect::SetAudioInputBuffer(float *left) @@ -388,7 +419,7 @@ void Lv2Effect::SetAudioInputBuffer(float *left) SetAudioInputBuffer(0, left); SetAudioInputBuffer(1, left); } - else + else if (GetNumberOfInputAudioPorts() != 0) /// yyx MIXING! { SetAudioInputBuffer(0, left); } @@ -400,7 +431,7 @@ void Lv2Effect::SetAudioInputBuffers(float *left, float *right) { SetAudioInputBuffer(0, left); } - else + else if (GetNumberOfInputAudioPorts() > 1) { SetAudioInputBuffer(0, left); SetAudioInputBuffer(1, right); @@ -410,8 +441,15 @@ void Lv2Effect::SetAudioInputBuffers(float *left, float *right) void Lv2Effect::SetAudioOutputBuffer(int index, float *buffer) { this->outputAudioBuffers[index] = buffer; - int pluginIndex = this->outputAudioPortIndices[index]; - lilv_instance_connect_port(pInstance, pluginIndex, buffer); + + if (this->inputAudioPortIndices.size() != 0) // i.e. we're not mixing a zero-input control + { + if ((size_t)index < this->inputAudioPortIndices.size()) + { + int pluginIndex = this->outputAudioPortIndices[index]; + lilv_instance_connect_port(pInstance, pluginIndex, buffer); + } + } } int Lv2Effect::GetControlIndex(const std::string &key) const @@ -452,7 +490,7 @@ void Lv2Effect::Activate() void Lv2Effect::AssignUnconnectedPorts() { - for (int i = 0; i < this->GetNumberOfInputAudioPorts(); ++i) + for (size_t i = 0; i < this->inputAudioPortIndices.size(); ++i) { if (GetAudioInputBuffer(i) == nullptr) { @@ -462,14 +500,16 @@ void Lv2Effect::AssignUnconnectedPorts() lilv_instance_connect_port(pInstance, pluginIndex, buffer); } } - for (int i = 0; i < this->GetNumberOfOutputAudioPorts(); ++i) + + if (this->inputAudioPortIndices.size() != 0) // i.e. not using a mix buffer. { - if (GetAudioOutputBuffer(i) == nullptr) + for (size_t i = 0; i < this->outputAudioPortIndices.size(); ++i) { - int pluginIndex = this->outputAudioPortIndices[i]; - - float *buffer = bufferPool.AllocateBuffer(pHost->GetMaxAudioBufferSize()); - lilv_instance_connect_port(pInstance, pluginIndex, buffer); + if (GetAudioOutputBuffer(i) == nullptr) + { + float *buffer = bufferPool.AllocateBuffer(pHost->GetMaxAudioBufferSize()); + int pluginIndex = this->outputAudioPortIndices[i]; + } } } for (int i = 0; i < this->GetNumberOfInputAtomPorts(); ++i) @@ -529,6 +569,61 @@ void Lv2Effect::Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBuff // relay worker response worker->EmitResponses(); } + // for zero-input plugins, mix the plugin output with the input signal. + if (this->inputAudioPortIndices.size() == 0) + { + + // mix a zero input controls into the output buffer using a triangular mix curve. + float pluginLevel = std::max(1.0f, this->zeroInputMix * 2); + float inputLevel = std::max(1.0f, (1 - this->zeroInputMix) * 2); + + // case + // 1 plugin output into 1 output. + // 2 plugin outputs into 2 outputs. + if (this->outputAudioBuffers.size() == this->outputMixBuffers.size()) + { + for (size_t i = 0; i < this->outputMixBuffers.size(); ++i) + { + float *__restrict input; + if (i >= this->inputAudioBuffers.size()) { + if (this->inputAudioBuffers.size() == 0) + { + break; + } + input = this->inputAudioBuffers[0]; + } else { + input = this->inputAudioBuffers[i]; + } + float *__restrict pluginOutput = this->outputMixBuffers[i].data(); + float *__restrict finalOutput = this->outputAudioBuffers[i]; + + for (uint32_t i = 0; i < samples; ++i) + { + finalOutput[i] = input[i] * inputLevel + pluginOutput[i] * pluginLevel; + } + } + } + else if (this->outputAudioPortIndices.size() == 1 && this->outputAudioBuffers.size() == 2) + { + // 1 plugin output into 2 outputs. + float *__restrict pluginOutput = this->outputMixBuffers[0].data(); + for (size_t i = 0; i < this->outputMixBuffers.size(); ++i) + { + float *__restrict input = this->inputAudioBuffers[i]; + float *__restrict finalOutput = this->outputAudioBuffers[i]; + + for (uint32_t i = 0; i < samples; ++i) + { + finalOutput[i] = input[i] * inputLevel + pluginOutput[i] * pluginLevel; + } + } + } + else + { + // e.g. 2 plugin outputs into 1 output (should never happen) + std::runtime_error("Internal error 0xEA48"); + } + } // do soft bypass. if (this->bypassSamplesRemaining == 0) diff --git a/src/Lv2Effect.hpp b/src/Lv2Effect.hpp index a6d5417..3a7abed 100644 --- a/src/Lv2Effect.hpp +++ b/src/Lv2Effect.hpp @@ -193,9 +193,12 @@ namespace pipedal bool requestStateChangedNotification = false; + float zeroInputMix = 0.5f; + int actualAudioInputs = 0; + int actualAudioOutputs = 0; + std::vector> outputMixBuffers; void BypassTo(float value); - public: // non RT-thread use only. @@ -242,6 +245,9 @@ namespace pipedal bool HasErrorMessage() const { return this->hasErrorMessage; } const char*TakeErrorMessage() { this->hasErrorMessage = false; return this->errorMessage; } + virtual void PrepareNoInputEffect(int numberOfInputs,size_t maxBufferSize) override; + + virtual void ResetAtomBuffers(); virtual uint64_t GetInstanceId() const { return instanceId; } virtual int GetNumberOfInputAudioPorts() const { return inputAudioPortIndices.size(); } @@ -251,6 +257,8 @@ namespace pipedal virtual int GetNumberOfMidiInputPorts() const { return inputMidiPortIndices.size(); } virtual int GetNumberOfMidiOutputPorts() const { return outputMidiPortIndices.size(); } + + virtual void SetAudioInputBuffer(int index, float *buffer); virtual float *GetAudioInputBuffer(int index) const { return this->inputAudioBuffers[index]; } diff --git a/src/Lv2Pedalboard.cpp b/src/Lv2Pedalboard.cpp index 44d8697..32f685b 100644 --- a/src/Lv2Pedalboard.cpp +++ b/src/Lv2Pedalboard.cpp @@ -121,6 +121,7 @@ std::vector Lv2Pedalboard::PrepareItems( if (pLv2Effect) { + if (pLv2Effect->HasErrorMessage()) { std::string error = pLv2Effect->TakeErrorMessage(); @@ -131,6 +132,7 @@ std::vector Lv2Pedalboard::PrepareItems( pEffect = pLv2Effect; uint64_t instanceId = pEffect->GetInstanceId(); + pLv2Effect->PrepareNoInputEffect(inputBuffers.size(),pHost->GetMaxAudioBufferSize()); if (inputBuffers.size() == 1) { @@ -207,7 +209,7 @@ std::vector Lv2Pedalboard::PrepareItems( effectOutput.push_back(CreateNewAudioBuffer()); } #endif - for (size_t i = 0; i < effectOutput.size(); ++i) + for (size_t i = 0; i < effectOutput.size(); ++i) { pEffect->SetAudioOutputBuffer(i, effectOutput[i]); } diff --git a/src/PiPedalModel.cpp b/src/PiPedalModel.cpp index dc1950b..ad0e6e5 100644 --- a/src/PiPedalModel.cpp +++ b/src/PiPedalModel.cpp @@ -2782,6 +2782,7 @@ void PiPedalModel::OnAlsaDriverTerminatedAbnormally() { Lv2Log::info("Restarting audio."); this->RestartAudio(); }); + ++audioRestartRetries; } else if (audioRestartRetries < 3) { this->audioRetryPostHandle = this->PostDelayed( @@ -2794,13 +2795,21 @@ void PiPedalModel::OnAlsaDriverTerminatedAbnormally() { RestartAudio(); }); + ++audioRestartRetries; + } else if (audioRestartRetries == 3) // one attempt to start the dummy driver. + { + { + this->audioRetryPostHandle = this->Post( + // No lock to avoid deadlocks! + [this]() { + Lv2Log::info(SS("Switching to dummy driver.")); + RestartAudio(true); // switch to the dummy driver. + }); + } + ++audioRestartRetries; } else { - this->audioRetryPostHandle = this->Post( - // No lock to avoid deadlocks! - [this]() { - Lv2Log::info(SS("Switching to dummy driver.")); - RestartAudio(true); // switch to the dummy driver. - }); + Lv2Log::error(SS("Unable to reastart audio.")); + } }); } diff --git a/src/PluginHost.cpp b/src/PluginHost.cpp index 1d0ae47..fa11927 100644 --- a/src/PluginHost.cpp +++ b/src/PluginHost.cpp @@ -482,7 +482,19 @@ void PluginHost::Load(const char *lv2Path) Lv2PluginUiInfo info(this, plugin.get()); if (plugin->is_valid()) { -#if SUPPORT_MIDI +#if 1 + if (info.audio_inputs() > 2 || info.audio_outputs() > 2) { + Lv2Log::debug( + "Plugin %s (%s) skipped. %d inputs, %d outputs.", plugin->name().c_str(), plugin->uri().c_str(), + (int)info.audio_inputs(),(int)info.audio_outputs()); + + } + else if (info.audio_inputs() == 0 && info.audio_outputs() == 0 ) + { + Lv2Log::debug("Plugin %s (%s) skipped. No audio i/o.", plugin->name().c_str(), plugin->uri().c_str()); + + } +#elif SUPPORT_MIDI if (info.audio_inputs() == 0 && !info.has_midi_input()) { Lv2Log::debug("Plugin %s (%s) skipped. No inputs.", plugin->name().c_str(), plugin->uri().c_str()); @@ -503,6 +515,12 @@ void PluginHost::Load(const char *lv2Path) #endif else { + if (info.audio_inputs() == 0) { + Lv2Log::debug("************* ZERO INPUTS: %s (%s) skipped. No audio outputs.", plugin->name().c_str(), plugin->uri().c_str()); + } + if (info.audio_outputs() == 0) { + Lv2Log::debug("************* ZERO OUTPUTS: %s (%s) skipped. No audio outputs.", plugin->name().c_str(), plugin->uri().c_str()); + } ui_plugins_.push_back(std::move(info)); } } diff --git a/src/PresetBundle.hpp b/src/PresetBundle.hpp index 84c37ce..b766ff9 100644 --- a/src/PresetBundle.hpp +++ b/src/PresetBundle.hpp @@ -20,6 +20,7 @@ #pragma once #include +#include namespace pipedal { class PiPedalModel; diff --git a/src/SplitEffect.hpp b/src/SplitEffect.hpp index b85fdc2..b306570 100644 --- a/src/SplitEffect.hpp +++ b/src/SplitEffect.hpp @@ -370,6 +370,9 @@ namespace pipedal virtual void RequestAllPathPatchProperties() {} + virtual void PrepareNoInputEffect(int numberOfInputs,size_t maxBufferSize) override { + // do nothing. + } virtual int GetNumberOfOutputAudioPorts() const { return numberOfOutputPorts; diff --git a/src/Updater.hpp b/src/Updater.hpp index 201422b..24e760d 100644 --- a/src/Updater.hpp +++ b/src/Updater.hpp @@ -27,6 +27,7 @@ #include "json.hpp" #include #include "UpdaterStatus.hpp" +#include namespace pipedal diff --git a/src/pch.h b/src/pch.h index 7dca65f..9b33fd3 100644 --- a/src/pch.h +++ b/src/pch.h @@ -24,7 +24,7 @@ template class TypeDisplay; #ifndef SUPPORT_MIDI // currently, only whether midi plugins can be loaded. No routing or handling implemented (yet). -#define SUPPORT_MIDI 0 +#define SUPPORT_MIDI 1 #endif @@ -32,33 +32,9 @@ template class TypeDisplay; #include #include -#include #include -#include #include #include -#include -#include #include -#ifdef JUNK - -#endif -/* -#include - -#include "lv2/atom/atom.h" -#include "lv2/atom/util.h" -#include "lv2/log/log.h" -#include "lv2/log/logger.h" -#include "lv2/midi/midi.h" -#include "lv2/urid/urid.h" -#include "lv2/log/logger.h" -#include "lv2/uri-map/uri-map.h" -#include "lv2/atom/forge.h" -#include "lv2/worker/worker.h" -#include "lv2/patch/patch.h" -#include "lv2/parameters/parameters.h" -#include "lv2/units/units.h" -*/ #include "ss.hpp"