diff --git a/.vscode/settings.json b/.vscode/settings.json index a0747af..d58ac81 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -115,6 +115,7 @@ "Chowdhury", "Chowdhury's", "discursus", + "distro", "distros", "dpkg", "flac", @@ -162,7 +163,7 @@ "cSpell.ignoreWords": [ "nammodel" ], - "cSpell.enabled": true, + "cSpell.enabled": false, // Disable all automatic completion suggestions - only show when Ctrl+Space is pressed "editor.quickSuggestions": { diff --git a/todo.txt b/todo.txt index 95ebe0b..e4a646c 100644 --- a/todo.txt +++ b/todo.txt @@ -1,19 +1,12 @@ Crash-proofing: do not reload pedalboard if the previous run crashed before normal shutdown. -Persist and reconnect IP addresses in client. -Zeroconv crash assert. /var/pipedal/audio_uploads/ReverbImpulseFiles/ - -Investigate if ((keyCode == KeyEvent.KEYCODE_BACK)) - MOD ir uses some scheme to limit the minimum buffer size, which is not implemented in Pipedal. - +wa Vu Meters, move rendering to background images, in order to reduce layout overhead. check reload after change of LV2 plugins. -Remove FullScreenIME in PluginControlView.tsx - Exactly one underrun per seek in Toob Player @@ -21,6 +14,7 @@ check filetime conversion in gcc 12.2! (missing c++ 20 time conversion functions AudioFiles.cpp fileTimeToInt64 +json "skip" code doesn't work with complex objects (MidiChannelBinding specifically). - pipewire aux in? @@ -40,5 +34,3 @@ pcm.pipedal_aux_in { } - -[{\"message\":\"updateCurrentPedalboard\"},{\"clientId\":6,\"pedalboard\":{\"name\":\"Test\",\"input_volume_db\":0,\"output_volume_db\":0,\"items\":[{\"instanceId\":51,\"title\":\"\",\"isEnabled\":true,\"uri\":\"uri://two-play/pipedal/pedalboard#Empty\",\"pluginName\":\"\",\"controlValues\":[],\"midiBindings\":[],\"midiChannelBinding\":null,\"vstState\":\"\",\"stateUpdateCount\":0,\"lv2State\":[false,{}],\"lilvPresetUri\":\"\",\"pathProperties\":{}},{\"instanceId\":50,\"title\":\"\",\"isEnabled\":true,\"uri\":\"http://calf.sourceforge.net/plugins/Gate\",\"pluginName\":\"Calf Gate\",\"controlValues\":[{\"key\":\"bypass\",\"value\":0},{\"key\":\"level_in\",\"value\":1},{\"key\":\"meter_in\",\"value\":0},{\"key\":\"meter_out\",\"value\":0},{\"key\":\"clip_in\",\"value\":0},{\"key\":\"clip_out\",\"value\":0},{\"key\":\"range\",\"value\":0.0612500012},{\"key\":\"threshold\",\"value\":0.125},{\"key\":\"ratio\",\"value\":2},{\"key\":\"attack\",\"value\":20},{\"key\":\"release\",\"value\":250},{\"key\":\"makeup\",\"value\":1},{\"key\":\"knee\",\"value\":2.82842994},{\"key\":\"detection\",\"value\":0},{\"key\":\"stereo_link\",\"value\":0},{\"key\":\"gating\",\"value\":0.03125}],\"midiBindings\":[],\"midiChannelBinding\":{\"deviceSelection\":0,\"midiDevices\":[],\"channel\":-1,\"acceptProgramChanges\":true,\"acceptCommonMessages\":true},\"vstState\":\"\",\"stateUpdateCount\":0,\"lv2State\":[false,{}],\"lilvPresetUri\":\"\",\"pathProperties\":{}},{\"instanceId\":46,\"title\":\"\",\"isEnabled\":true,\"uri\":\"uri://two-play/pipedal/pedalboard#Empty\",\"pluginName\":\"\",\"controlValues\":[],\"midiBindings\":[],\"midiChannelBinding\":null,\"vstState\":\"\",\"stateUpdateCount\":0,\"lv2State\":[false,{}],\"lilvPresetUri\":\"\",\"pathProperties\":{}},{\"instanceId\":48,\"title\":\"\",\"isEnabled\":true,\"uri\":\"uri://two-play/pipedal/pedalboard#Empty\",\"pluginName\":\"\",\"controlValues\":[],\"midiBindings\":[],\"midiChannelBinding\":null,\"vstState\":\"\",\"stateUpdateCount\":0,\"lv2State\":[false,{}],\"lilvPresetUri\":\"\",\"pathProperties\":{}},{\"instanceId\":47,\"title\":\"\",\"isEnabled\":true,\"uri\":\"uri://two-play/pipedal/pedalboard#Empty\",\"pluginName\":\"\",\"controlValues\":[],\"midiBindings\":[],\"midiChannelBinding\":null,\"vstState\":\"\",\"stateUpdateCount\":1,\"lv2State\":[false,{}],\"lilvPresetUri\":\"\",\"pathProperties\":{\"http://two-play.com/plugins/toob-ml#modelFile\":\"{\\\"otype_\\\": \\\"Path\\\",\\\"value\\\": \\\"/usr/lib/lv2/ToobAmp.lv2/models/tones/MesaBoogieMk2b_Crunch.json\\\"}\"}},{\"instanceId\":31,\"title\":\"\",\"isEnabled\":true,\"uri\":\"http://two-play.com/plugins/toob-freeverb\",\"pluginName\":\"TooB Freeverb\",\"controlValues\":[{\"key\":\"dryWet\",\"value\":0.653543293},{\"key\":\"roomSize\",\"value\":0.625},{\"key\":\"damping\",\"value\":0.574999988}],\"midiBindings\":[{\"symbol\":\"dryWet\",\"bindingType\":2,\"note\":72,\"control\":23,\"minControlValue\":0,\"maxControlValue\":127,\"minValue\":0,\"maxValue\":1,\"rotaryScale\":1,\"linearControlType\":0,\"switchControlType\":0}],\"midiChannelBinding\":null,\"vstState\":\"\",\"stateUpdateCount\":0,\"lv2State\":[false,{}],\"lilvPresetUri\":\"\",\"pathProperties\":{}}],\"nextInstanceId\":51,\"snapshots\":[],\"selectedSnapshot\":-1}}] \ No newline at end of file diff --git a/vite/src/pipedal/MainPage.tsx b/vite/src/pipedal/MainPage.tsx index a1ea987..e7ba524 100644 --- a/vite/src/pipedal/MainPage.tsx +++ b/vite/src/pipedal/MainPage.tsx @@ -255,8 +255,11 @@ export const MainPage = // selectedItem = value.getFirstSelectableItem(); // } let selectedItem = value.selectedPlugin; - if (selectedItem === -1 || !value.hasItem(selectedItem)) { - selectedItem = value.getFirstSelectableItem(); + if (selectedItem !== -2 && selectedItem !== -3) // start and end nodes. + { + if (selectedItem === -1 || !value.hasItem(selectedItem)) { + selectedItem = value.getFirstSelectableItem(); + } } this.setState({ pedalboard: value, diff --git a/vite/src/pipedal/ModGuiHost.tsx b/vite/src/pipedal/ModGuiHost.tsx index 11bbcff..cd58a2a 100644 --- a/vite/src/pipedal/ModGuiHost.tsx +++ b/vite/src/pipedal/ModGuiHost.tsx @@ -399,7 +399,6 @@ class CustomSelectPathControl implements ModGuiControl { } let valueElement = this.getValueElement(); if (valueElement) { - /// xxx: update the display value. if (this.pathValue === null || this.pathValue === "") { valueElement.textContent = "No file selected"; return; diff --git a/vite/src/pipedal/VuMeter.tsx b/vite/src/pipedal/VuMeter.tsx index c77fbe0..ebcd333 100644 --- a/vite/src/pipedal/VuMeter.tsx +++ b/vite/src/pipedal/VuMeter.tsx @@ -95,7 +95,9 @@ class TelltaleState { } const LEFT_X = BORDER_THICKNESS; -const RIGHT_X = BORDER_THICKNESS + + CHANNEL_WIDTH + BORDER_THICKNESS; +const RIGHT_X = BORDER_THICKNESS + CHANNEL_WIDTH + BORDER_THICKNESS; +const TOP_Y = BORDER_THICKNESS; +const VU_BAR_HEIGHT = DISPLAY_HEIGHT-2*BORDER_THICKNESS; const styles = (theme: Theme) => ({ frame: css({ @@ -119,15 +121,16 @@ const styles = (theme: Theme) => ({ overflow: "hidden", }), monoTextFrame: css({ - display: "flex",flexFlow: "column nowrap", alignItems: "center", textAlign: "center" + display: "flex",flexFlow: "column nowrap", alignItems: "center", textAlign: "center", overflow: "clip", }), stereoTextFrame: css({ - display: "flex",flexFlow: "column nowrap", alignItems: "center", textAlign: "center" + display: "flex",flexFlow: "column nowrap", alignItems: "center", textAlign: "center",overflow: "clip", }), vuTextFrame: css({ color: theme.palette.text.secondary, - display: "table-cell", + display: "div", position: "relative", + overflow: "clip", marginTop: 4, textAlign: "center", width: 20, height: 16, @@ -135,73 +138,60 @@ const styles = (theme: Theme) => ({ justifyContent: "center", alignItems: "center" }), - redFrameL: css({ + frameL: css({ position: "absolute", - top: "100%", + top: TOP_Y , left: LEFT_X, width: CHANNEL_WIDTH, - height: DISPLAY_HEIGHT - 2, - background: "#F00" + height: VU_BAR_HEIGHT, + overflow: "clip" }), - yellowFrameL: css({ - top: "100%", - left: LEFT_X, + frameR: css({ + position: "absolute", + overflow: "clip", + top: TOP_Y, + left: RIGHT_X, width: CHANNEL_WIDTH, - position: "absolute", - height: DISPLAY_HEIGHT - 2, - background: "#CC0" + height: VU_BAR_HEIGHT, }), - greenFrameL: css({ - top: "100%", - left: LEFT_X, + yellowBar: css({ + top: 0, + left: 0, width: CHANNEL_WIDTH, + height: VU_BAR_HEIGHT, position: "absolute", - height: DISPLAY_HEIGHT - 2, - background: "#0C2" + background: "#CC0", + transform: "translateY(" + VU_BAR_HEIGHT + "px)" }), - indicatorL: css({ + greenBar: css({ + top: 0, + left: 0, + width: CHANNEL_WIDTH, + height: VU_BAR_HEIGHT, position: "absolute", - left: LEFT_X, + background: "#0C2", + transform: "translateY(" + VU_BAR_HEIGHT + "px)" + }), + redBar: css({ + top: 0, + left: 0, + width: CHANNEL_WIDTH, + height: VU_BAR_HEIGHT, + position: "absolute", + background: "#F00", + transform: "translateY(" + VU_BAR_HEIGHT + "px)" + }), + indicatorBar: css({ + position: "absolute", + left: 0, width: CHANNEL_WIDTH, height: TELLTALE_HEIGHT + "px", - top: "100%", - background: "#F00" + top: 0, + background: "#F00", + transform: "translateY(" + VU_BAR_HEIGHT + "px)" + }), - redFrameR: css({ - position: "absolute", - top: "100%", - left: RIGHT_X, - width: CHANNEL_WIDTH, - height: DISPLAY_HEIGHT - 2, - background: "#F00" - }), - yellowFrameR: css({ - top: "100%", - left: RIGHT_X, - width: CHANNEL_WIDTH, - position: "absolute", - height: DISPLAY_HEIGHT - 2, - background: "#CC0" - }), - greenFrameR: css({ - top: "100%", - left: RIGHT_X, - width: CHANNEL_WIDTH, - position: "absolute", - height: DISPLAY_HEIGHT - 2, - background: "#0C2" - }), - indicatorR: css({ - position: "absolute", - left: RIGHT_X, - width: CHANNEL_WIDTH, - height: TELLTALE_HEIGHT + "px", - top: "100%", - background: "#F00" - - }) - }); @@ -261,10 +251,12 @@ export const VuMeter = telltaleStateR: TelltaleState = new TelltaleState(); vuInfo?: VuUpdateInfo; - requesttedStereoState: boolean = false; + requestedStereoState: boolean = false; + private currentDisplayValue: string | null = null; updateText(telltaleDb: number) { + if (this.textRef.current) { let displayValue: string; if (telltaleDb <= MIN_DB) @@ -288,16 +280,39 @@ export const VuMeter = } } } - this.textRef.current.innerText = displayValue; + + if (this.currentDisplayValue !== displayValue) + { + this.currentDisplayValue = displayValue; + this.textRef.current.innerText = displayValue; + } } } + private currentVuInfo: VuUpdateInfo | null = null; + + private animationFrameHandle: number | null = null; onVuChanged(vuInfo: VuUpdateInfo): void { + this.currentVuInfo = vuInfo; + if (this.animationFrameHandle == null) + { + this.animationFrameHandle = window.requestAnimationFrame(() => { + this.animationFrameHandle = null; + this.animateVuUpdate(); + }); + } + } + private animateVuUpdate() : void + { let value: number; let valueR: number; - - this.vuInfo = vuInfo; - + let vuInfo = this.currentVuInfo; + if (!vuInfo) { + return; // no vu info. + } + if (!this.divRef.current) { // unmounted? + return; + } let isStereo: boolean; @@ -312,34 +327,37 @@ export const VuMeter = } if (this.state.isStereo !== isStereo) { - if (isStereo !== this.requesttedStereoState) // guard against overrunning the layout engine. + if (isStereo !== this.requestedStereoState) // guard against overrunning the layout engine. { - this.requesttedStereoState = isStereo; + this.requestedStereoState = isStereo; this.setState({ isStereo: isStereo }); - return; // we can't procede without stereo layout. + return; // we can't proceed without stereo layout. } } let childNodes = this.divRef.current!.childNodes; + let leftFrameChildNodes = childNodes[0].childNodes; + let vuData: VuChannelData = { value: value, - redDiv: childNodes[0] as HTMLDivElement, - yellowDiv: childNodes[1] as HTMLDivElement, - greenDiv: childNodes[2] as HTMLDivElement, - telltaleDiv: childNodes[3] as HTMLDivElement + redDiv: leftFrameChildNodes[0] as HTMLDivElement, + yellowDiv: leftFrameChildNodes[1] as HTMLDivElement, + greenDiv: leftFrameChildNodes[2] as HTMLDivElement, + telltaleDiv: leftFrameChildNodes[3] as HTMLDivElement }; this.updateChannel(vuData,this.telltaleStateL); if (this.state.isStereo) { + let rightFrameChildren = childNodes[1].childNodes; vuData = { value: valueR, - redDiv: childNodes[4] as HTMLDivElement, - yellowDiv: childNodes[5] as HTMLDivElement, - greenDiv: childNodes[6] as HTMLDivElement, - telltaleDiv: childNodes[7] as HTMLDivElement + redDiv: rightFrameChildren[0] as HTMLDivElement, + yellowDiv: rightFrameChildren[1] as HTMLDivElement, + greenDiv: rightFrameChildren[2] as HTMLDivElement, + telltaleDiv: rightFrameChildren[3] as HTMLDivElement }; this.updateChannel(vuData, this.telltaleStateR); } @@ -367,17 +385,17 @@ export const VuMeter = let INVISIBLE_Y = INTERIOR_DISPLAY_HEIGHT + "px"; if (y >= this.yYellow) { // green only. - vuData.greenDiv.style.top = y + "px"; - vuData.yellowDiv.style.top = INVISIBLE_Y; - vuData.redDiv.style.top = INVISIBLE_Y; + vuData.greenDiv.style.transform = `translateY(${y}px)`; + vuData.yellowDiv.style.transform = `translateY(${INVISIBLE_Y})`; + vuData.redDiv.style.transform = `translateY(${INVISIBLE_Y})`; } else { - vuData.greenDiv.style.top = this.yYellow + "px"; + vuData.greenDiv.style.transform = `translateY(${this.yYellow}px)`; if (y >= this.yZero) { - vuData.yellowDiv.style.top = y + "px"; - vuData.redDiv.style.top = INVISIBLE_Y; + vuData.yellowDiv.style.transform = `translateY(${y}px)`; + vuData.redDiv.style.transform = `translateY(${INVISIBLE_Y})`; } else { - vuData.yellowDiv.style.top = this.yZero + "px"; - vuData.redDiv.style.top = y + "px"; + vuData.yellowDiv.style.transform = `translateY(${this.yZero}px)`; + vuData.redDiv.style.transform = `translateY(${y}px)`; } } let dbTelltale = telltaleState.getTelltaleHoldValue(db); @@ -399,7 +417,7 @@ export const VuMeter = yTelltale = INTERIOR_DISPLAY_HEIGHT; } } - vuData.telltaleDiv.style.top = yTelltale + "px"; + vuData.telltaleDiv.style.transform = `translateY(${yTelltale}px)`; } @@ -467,23 +485,28 @@ export const VuMeter = if (!this.state.isStereo) { return (
-
-
-
-
+
+
+
+
+
+
); } else { return (
-
-
-
-
- -
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
);