-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhandles.ts
113 lines (95 loc) · 3.34 KB
/
handles.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { useCallback } from "react";
import { Connection, Edge } from "react-flow-renderer";
import { AnyAudioNode, isComplexAudioNode, useNodeContext } from "context/NodeContext";
import { nodeCleanup } from "components/Nodes";
function getChannelIndex(handle: string): number {
return +(handle.match(/-(\d+)$/)?.[1] ?? 0);
}
interface ResolvedConnection {
inputIndex?: number;
outputIndex?: number;
source: AudioNode;
target: AudioNode | AudioParam;
}
function resolveConnection(
connection: Edge | Connection,
getNode: (id: string) => AnyAudioNode
): ResolvedConnection | never {
if (!connection.source || !connection.target || !connection.sourceHandle || !connection.targetHandle) {
throw new Error("Invalid connection");
}
const connectToTargetNode = connection.targetHandle.startsWith("input");
const connectToSourceNode = connection.sourceHandle.startsWith("output");
let source = getNode(connection.source);
let target = getNode(connection.target);
if (isComplexAudioNode(source) && connectToSourceNode) {
if (!source.output) {
throw new Error("Invalid connection");
}
source = source.output;
}
if (isComplexAudioNode(target) && connectToTargetNode) {
if (!target.input) {
throw new Error("Invalid connection");
}
target = target.input;
}
if (!source || !target) {
throw new Error("Invalid connection");
}
return {
inputIndex: connectToTargetNode ? getChannelIndex(connection.targetHandle) : undefined,
outputIndex: getChannelIndex(connection.sourceHandle),
source: connectToSourceNode
? source
: // @ts-ignore
source[connection.sourceHandle],
target: connectToTargetNode
? target
: // @ts-ignore
target[connection.targetHandle],
};
}
export function connectNodes(connection: Edge | Connection, getNode: (id: string) => AnyAudioNode) {
try {
const { inputIndex, outputIndex, source, target } = resolveConnection(connection, getNode);
if (inputIndex != null) {
// @ts-ignore
source.connect(target, outputIndex, inputIndex);
} else {
// @ts-ignore
source.connect(target, outputIndex);
}
} catch (e) {
console.error(e);
}
}
export function disconnectNodes(connection: Edge | Connection, getNode: (id: string) => AnyAudioNode) {
try {
const { inputIndex, outputIndex, source, target } = resolveConnection(connection, getNode);
if (inputIndex != null) {
// @ts-ignore
source.disconnect(target, outputIndex, inputIndex);
} else {
// @ts-ignore
source.disconnect(target, outputIndex);
}
} catch (e) {
console.error(e);
}
}
// FIXME This should be handled on changes to ReactFlowRenderer state instead.
export function useOnConnect() {
const { getNode } = useNodeContext();
return useCallback((connection: Edge | Connection) => connectNodes(connection, getNode), [getNode]);
}
// FIXME This should be handled on changes to ReactFlowRenderer state instead.
export function useOnEdgeRemove() {
const { getNode } = useNodeContext();
return useCallback((edge: Edge) => disconnectNodes(edge, getNode), [getNode]);
}
// FIXME This should be handled on changes to ReactFlowRenderer state instead.
export function useOnNodeRemove() {
const { getNode } = useNodeContext();
return useCallback((nodeId: string) => nodeCleanup(getNode(nodeId)), [getNode]);
}