fix: WS protocol — send positional args from React, add OutputRoutingPanel, add deleteScene handler
This commit is contained in:
@@ -1409,6 +1409,15 @@ public:
|
|||||||
}
|
}
|
||||||
REGISTER_MESSAGE_HANDLER(mixerGetScenes)
|
REGISTER_MESSAGE_HANDLER(mixerGetScenes)
|
||||||
|
|
||||||
|
void handle_mixerDeleteScene(int replyTo, json_reader *pReader)
|
||||||
|
{
|
||||||
|
int64_t sceneId;
|
||||||
|
pReader->read(&sceneId);
|
||||||
|
bool ok = this->mixerApi.deleteScene(std::to_string(sceneId));
|
||||||
|
this->Reply(replyTo, "mixerDeleteScene", ok);
|
||||||
|
}
|
||||||
|
REGISTER_MESSAGE_HANDLER(mixerDeleteScene)
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
/* Mixer Output Routing Messages */
|
/* Mixer Output Routing Messages */
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Generated
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "pipedal",
|
"name": "op-pedal",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "pipedal",
|
"name": "op-pedal",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ const MixerPage: React.FC<MixerPageProps> = ({ onClose }) => {
|
|||||||
const handleLearnTarget = useCallback(
|
const handleLearnTarget = useCallback(
|
||||||
(targetType: string, targetId: number) => {
|
(targetType: string, targetId: number) => {
|
||||||
if (learnModeActive) {
|
if (learnModeActive) {
|
||||||
ws.send("mixerSetMidiLearnTarget", { targetType, targetId }).catch(() => {});
|
ws.send("mixerSetMidiLearnTarget", [targetType, targetId]).catch(() => {});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[ws, learnModeActive],
|
[ws, learnModeActive],
|
||||||
@@ -123,42 +123,42 @@ const MixerPage: React.FC<MixerPageProps> = ({ onClose }) => {
|
|||||||
|
|
||||||
const handleChannelVolume = useCallback(
|
const handleChannelVolume = useCallback(
|
||||||
(channelIndex: number, volume: number) => {
|
(channelIndex: number, volume: number) => {
|
||||||
ws.send("mixerSetChannelVolume", { channelIndex, volume }).catch(() => {});
|
ws.send("mixerSetChannelVolume", [channelIndex, volume]).catch(() => {});
|
||||||
},
|
},
|
||||||
[ws]
|
[ws]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChannelPan = useCallback(
|
const handleChannelPan = useCallback(
|
||||||
(channelIndex: number, pan: number) => {
|
(channelIndex: number, pan: number) => {
|
||||||
ws.send("mixerSetChannelPan", { channelIndex, pan }).catch(() => {});
|
ws.send("mixerSetChannelPan", [channelIndex, pan]).catch(() => {});
|
||||||
},
|
},
|
||||||
[ws]
|
[ws]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChannelMute = useCallback(
|
const handleChannelMute = useCallback(
|
||||||
(channelIndex: number, mute: boolean) => {
|
(channelIndex: number, mute: boolean) => {
|
||||||
ws.send("mixerSetChannelMute", { channelIndex, mute }).catch(() => {});
|
ws.send("mixerSetChannelMute", [channelIndex, mute]).catch(() => {});
|
||||||
},
|
},
|
||||||
[ws]
|
[ws]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChannelSolo = useCallback(
|
const handleChannelSolo = useCallback(
|
||||||
(channelIndex: number, solo: boolean) => {
|
(channelIndex: number, solo: boolean) => {
|
||||||
ws.send("mixerSetChannelSolo", { channelIndex, solo }).catch(() => {});
|
ws.send("mixerSetChannelSolo", [channelIndex, solo]).catch(() => {});
|
||||||
},
|
},
|
||||||
[ws]
|
[ws]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleBusVolume = useCallback(
|
const handleBusVolume = useCallback(
|
||||||
(busId: number, volume: number) => {
|
(busId: number, volume: number) => {
|
||||||
ws.send("mixerSetBusVolume", { busId, volume }).catch(() => {});
|
ws.send("mixerSetBusVolume", [busId, volume]).catch(() => {});
|
||||||
},
|
},
|
||||||
[ws]
|
[ws]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleBusMute = useCallback(
|
const handleBusMute = useCallback(
|
||||||
(busId: number, mute: boolean) => {
|
(busId: number, mute: boolean) => {
|
||||||
ws.send("mixerSetBusMute", { busId, mute }).catch(() => {});
|
ws.send("mixerSetBusMute", [busId, mute]).catch(() => {});
|
||||||
},
|
},
|
||||||
[ws]
|
[ws]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// OutputRoutingPanel - configure which hardware output channels each bus routes to
|
||||||
|
import React from "react";
|
||||||
|
import type { MixerBusState, MixerOutputRouteState } from "./useMixerWS";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
buses: MixerBusState[];
|
||||||
|
routes: MixerOutputRouteState[];
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function OutputRoutingPanel({ buses, routes, onClose }: Props) {
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
position: "fixed", top: 0, left: 0, right: 0, bottom: 0,
|
||||||
|
background: "rgba(0,0,0,0.7)", display: "flex",
|
||||||
|
alignItems: "center", justifyContent: "center", zIndex: 1000
|
||||||
|
}}>
|
||||||
|
<div style={{
|
||||||
|
background: "#1a1a2e", borderRadius: 12, padding: 24,
|
||||||
|
minWidth: 480, maxHeight: "80vh", overflow: "auto",
|
||||||
|
color: "#e0e0e0", fontFamily: "system-ui, sans-serif"
|
||||||
|
}}>
|
||||||
|
<h2 style={{ margin: "0 0 16px", color: "#a78bfa" }}>Output Routing</h2>
|
||||||
|
{routes.length === 0 && (
|
||||||
|
<p style={{ color: "#888", fontStyle: "italic" }}>No output routes configured.</p>
|
||||||
|
)}
|
||||||
|
{routes.map((route, idx) => (
|
||||||
|
<div key={idx} style={{
|
||||||
|
display: "flex", gap: 12, alignItems: "center",
|
||||||
|
padding: "8px 0", borderBottom: "1px solid #333"
|
||||||
|
}}>
|
||||||
|
<span style={{ minWidth: 80, fontWeight: 600 }}>
|
||||||
|
{buses.find(b => b.id === route.sourceBusId)?.name ?? ("Bus " + String(route.sourceBusId))}
|
||||||
|
</span>
|
||||||
|
<span style={{ color: "#888" }}>CH {route.sourceStartChannel}</span>
|
||||||
|
<span style={{ color: "#555" }}>{"->"}</span>
|
||||||
|
<span style={{ color: "#888" }}>Out {route.targetStartChannel}</span>
|
||||||
|
<span style={{ color: "#555", marginLeft: 8 }}>({route.channels} ch)</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div style={{ marginTop: 20, display: "flex", gap: 8, justifyContent: "flex-end" }}>
|
||||||
|
<button onClick={onClose} style={{
|
||||||
|
padding: "8px 20px", border: "1px solid #555",
|
||||||
|
borderRadius: 6, background: "transparent", color: "#ccc",
|
||||||
|
cursor: "pointer"
|
||||||
|
}}>Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -39,7 +39,15 @@ export interface MixerRouteState {
|
|||||||
sourceType: string; // "channel" | "bus"
|
sourceType: string; // "channel" | "bus"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MixerOutputRouteState {
|
||||||
|
sourceBusId: number;
|
||||||
|
sourceStartChannel: number;
|
||||||
|
targetStartChannel: number;
|
||||||
|
channels: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MixerState {
|
export interface MixerState {
|
||||||
|
outputRoutes?: MixerOutputRouteState[];
|
||||||
channels: MixerChannelState[];
|
channels: MixerChannelState[];
|
||||||
buses: MixerBusState[];
|
buses: MixerBusState[];
|
||||||
routes: MixerRouteState[];
|
routes: MixerRouteState[];
|
||||||
@@ -237,7 +245,7 @@ export function useMixerWS(wsUrl: string = DEFAULT_WS_URL): MixerWsHandle {
|
|||||||
|
|
||||||
// Build message in PiPedalSocket format: [header, body?]
|
// Build message in PiPedalSocket format: [header, body?]
|
||||||
const header: Record<string, unknown> = { message: command, replyTo };
|
const header: Record<string, unknown> = { message: command, replyTo };
|
||||||
const msg = params !== undefined ? [header, params] : [header];
|
const msg = params !== undefined ? [header, ...(Array.isArray(params) ? params : [params])] : [header];
|
||||||
|
|
||||||
// Register the reply handler before sending (race-safe).
|
// Register the reply handler before sending (race-safe).
|
||||||
replyMapRef.current.set(replyTo, (value: unknown) => {
|
replyMapRef.current.set(replyTo, (value: unknown) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user