diff --git a/vite/src/pipedal/Pedalboard.tsx b/vite/src/pipedal/Pedalboard.tsx index 56b9d94..1012687 100644 --- a/vite/src/pipedal/Pedalboard.tsx +++ b/vite/src/pipedal/Pedalboard.tsx @@ -394,6 +394,7 @@ export class Pedalboard implements Deserializable { pathProperties: {[Name: string]: string} = {}; selectedPlugin: number = -1; + // yields all items in the pedalboard, including split items. Splits are yielded before their children. *itemsGenerator(): Generator { let it = itemGenerator_(this.items); while (true) @@ -403,6 +404,16 @@ export class Pedalboard implements Deserializable { yield v.value; } } + // same as itemsGenerator, but yields split items after their chains. + *itemsGeneratorSplitAfter(): Generator { + let it = itemGeneratorSplitAfter_(this.items); + while (true) + { + let v = it.next(); + if (v.done) break; + yield v.value; + } + } makeSnapshot(): Snapshot { let result = new Snapshot(); @@ -793,4 +804,28 @@ function* itemGenerator_(items: PedalboardItem[]): Generator { + for (let i = 0; i < items.length; ++i) { + let item = items[i]; + if (item.uri === SPLIT_PEDALBOARD_ITEM_URI) { + + let splitItem = item as PedalboardSplitItem; + + let it = itemGenerator_(splitItem.topChain); + while (true) { + let v = it.next(); + if (v.done) break; + yield v.value; + } + it = itemGenerator_(splitItem.bottomChain); + while (true) { + let v = it.next(); + if (v.done) break; + yield v.value; + } + } + yield item; + } +} + diff --git a/vite/src/pipedal/PiPedalModel.tsx b/vite/src/pipedal/PiPedalModel.tsx index b2c478f..7f4fd54 100644 --- a/vite/src/pipedal/PiPedalModel.tsx +++ b/vite/src/pipedal/PiPedalModel.tsx @@ -659,6 +659,7 @@ export class PiPedalModel //implements PiPedalModel } private setModelPedalboard(pedalboard: Pedalboard) { + this.removeInvalidSidechains(pedalboard); this.pedalboard.set(pedalboard); this.selectedSnapshot.set(pedalboard.selectedSnapshot); this.updateEnabledItems(pedalboard); @@ -1763,13 +1764,17 @@ export class PiPedalModel //implements PiPedalModel } let it = newPedalboard.itemsGenerator(); + let oldInstanceId = -1; + let newInstanceId = -1; while (true) { let v = it.next(); if (v.done) break; let item = v.value; if (item.instanceId === itemId) { + oldInstanceId = item.instanceId; item.deserialize(new PedalboardItem()); // skeezy way to re-initialize. item.instanceId = ++newPedalboard.nextInstanceId; + newInstanceId = item.instanceId; item.uri = selectedUri; item.pluginName = plugin.name; item.controlValues = this.getDefaultValues(item.uri); @@ -1784,6 +1789,9 @@ export class PiPedalModel //implements PiPedalModel item.pathProperties[fileProperty.patchProperty] = "null"; } newPedalboard.selectedPlugin = item.instanceId; + if (oldInstanceId !== -1) { + this.updateSidechainReferences(newPedalboard, oldInstanceId, newInstanceId); + } this.setModelPedalboard(newPedalboard); this.updateServerPedalboard() return item.instanceId; @@ -1938,6 +1946,9 @@ export class PiPedalModel //implements PiPedalModel newPedalboard.replaceItem(fromInstanceId, emptyItem); newPedalboard.replaceItem(toInstanceId, fromItem); newPedalboard.selectedPlugin = fromItem.instanceId; + if (!toItem.isEmpty()) { + this.updateSidechainReferences(newPedalboard, toItem.instanceId, fromItem.instanceId); + } this.setModelPedalboard(newPedalboard); this.updateServerPedalboard(); @@ -3545,29 +3556,67 @@ export class PiPedalModel //implements PiPedalModel } requestBankPresets(bankInstanceId: number): Promise { - return nullCast(this.webSocket).request("requestBankPresets", {bankInstanceId: bankInstanceId}); - } + return nullCast(this.webSocket).request("requestBankPresets", { bankInstanceId: bankInstanceId }); + } importPresetsFromBank(bankInstanceId: number, presets: number[]): Promise { - return nullCast(this.webSocket).request("importPresetsFromBank", {bankInstanceId: bankInstanceId, presets: presets}); + return nullCast(this.webSocket).request("importPresetsFromBank", { bankInstanceId: bankInstanceId, presets: presets }); } copyPresetsToBank(bankInstanceId: number, presets: number[]): Promise { - return nullCast(this.webSocket).request("copyPresetsToBank", {bankInstanceId: bankInstanceId, presets: presets}); + return nullCast(this.webSocket).request("copyPresetsToBank", { bankInstanceId: bankInstanceId, presets: presets }); } setPedalboardSideChainInput(instanceId: number, sideChainInputId: number): void { let pedalboard = this.pedalboard.get().clone(); let pedalboardItem = pedalboard.getItem(instanceId); - if (!pedalboardItem) - { + if (!pedalboardItem) { return; } pedalboardItem.sideChainInputId = sideChainInputId; - pedalboardItem.instanceId = ++pedalboard.nextInstanceId; + let oldId = pedalboardItem.instanceId; + let newId = ++pedalboard.nextInstanceId; + + pedalboardItem.instanceId = newId; // force reload of pedalboard. pedalboard.selectedPlugin = pedalboardItem.instanceId; + + this.updateSidechainReferences(pedalboard, oldId, newId); this.setModelPedalboard(pedalboard); this.updateServerPedalboard(); } + + removeInvalidSidechains(pedalboard: Pedalboard) { + let it = pedalboard.itemsGeneratorSplitAfter(); + let previousItemIds: Set = new Set(); + previousItemIds.add(-2); // start is visible. + while (true) { + let r = it.next(); + if (r.done) { + break; + } + let item = r.value; + if (item.sideChainInputId !== -1) { + if (!previousItemIds.has(item.sideChainInputId)) { + item.sideChainInputId = -1; + } + } + if (!item.isEmpty()) { // empty items are not valid sidechain targets. + previousItemIds.add(item.instanceId); + } + } + } + updateSidechainReferences(pedalboard: Pedalboard, oldItemId: number, newItemId: number) { + let it = pedalboard.itemsGenerator(); + + while (true) { + let r = it.next(); + if (r.done) break; + let item = r.value; + if (item.sideChainInputId === oldItemId) { + item.sideChainInputId = newItemId; + } + } + this.removeInvalidSidechains(pedalboard); + } }; let instance: PiPedalModel | undefined = undefined; diff --git a/vite/src/pipedal/SnapshotEditor.tsx b/vite/src/pipedal/SnapshotEditor.tsx index 5708b44..5bb0110 100644 --- a/vite/src/pipedal/SnapshotEditor.tsx +++ b/vite/src/pipedal/SnapshotEditor.tsx @@ -24,7 +24,7 @@ import CloseIcon from '@mui/icons-material/Close'; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; import CssBaseline from '@mui/material/CssBaseline'; -import {createStyles} from './WithStyles'; +import { createStyles } from './WithStyles'; import { withStyles } from "tss-react/mui"; import IconButtonEx from './IconButtonEx'; @@ -148,10 +148,12 @@ const SnapshotEditor = withStyles( this.model_.zoomedUiControl.addOnChangedHandler( () => { - this.setState({ - zoomedControlOpen: this.model_.zoomedUiControl.get() !== undefined, - zoomedControlInfo: this.model_.zoomedUiControl.get() - }); + if (this.mounted93) { + this.setState({ + zoomedControlOpen: this.model_.zoomedUiControl.get() !== undefined, + zoomedControlInfo: this.model_.zoomedUiControl.get() + }); + } } ); @@ -182,9 +184,11 @@ const SnapshotEditor = withStyles( this.setState({ isFullScreen: !this.state.isFullScreen }); } + private mounted93: boolean = false; componentDidMount() { super.componentDidMount(); + this.mounted93 = true; this.model_.showStatusMonitor.addOnChangedHandler(this.showStatusMonitorHandler); } @@ -206,6 +210,7 @@ const SnapshotEditor = withStyles( } componentWillUnmount() { + this.mounted93 = false; super.componentWillUnmount(); this.model_.banks.removeOnChangedHandler(this.showStatusMonitorHandler); }