From 65efc924c3e7de98c29ec607af074007e5625f36 Mon Sep 17 00:00:00 2001 From: AZ Software <66299945+AZProductions@users.noreply.github.com> Date: Mon, 8 Jan 2024 21:23:29 +0100 Subject: [PATCH] prog; setup ipc communication and knob ui --- crates/toybox_c1200/src/lib.rs | 40 +++++++-- packages/toybox_c1200_ui/app/page.tsx | 40 ++++++++- packages/toybox_c1200_ui/lib/Knob.tsx | 6 ++ .../toybox_c1200_ui/lib/KnobBaseThumb.tsx | 4 +- .../lib/percentage/KnobBase.tsx | 85 +++++++++++++++++++ 5 files changed, 163 insertions(+), 12 deletions(-) create mode 100644 packages/toybox_c1200_ui/lib/percentage/KnobBase.tsx diff --git a/crates/toybox_c1200/src/lib.rs b/crates/toybox_c1200/src/lib.rs index a5af18e7..fc6bf9d6 100644 --- a/crates/toybox_c1200/src/lib.rs +++ b/crates/toybox_c1200/src/lib.rs @@ -51,6 +51,7 @@ enum Action { Init, SetGain { value: f32 }, SetPreset { preset: Presets }, + SetReverbDryWet {value: f32}, } #[derive(Enum, Debug, PartialEq, Eq, Clone, Copy)] @@ -95,6 +96,7 @@ struct ToyboxCParams { #[id = "reverb-dry-wet"] pub reverb_dry_wet_ratio: FloatParam, + reverb_dry_wet_changed: Arc, #[id = "reverb-room-size"] pub reverb_room_size: FloatParam, @@ -178,6 +180,14 @@ impl Default for ToyboxCParams { let preset_callback = Arc::new(move |_: Presets| { preset_changed_mem.store(true, Ordering::Relaxed); }); + + + let reverb_dry_wet_changed = Arc::new(AtomicBool::new(false)); + let v = reverb_dry_wet_changed.clone(); + let reverb_dry_wet_value_param_callback = Arc::new(move |_: f32| { + v.store(true, Ordering::Relaxed); + }); + Self { reverb_gain: FloatParam::new( "Reverb Gain", @@ -211,7 +221,8 @@ impl Default for ToyboxCParams { FloatRange::Linear { min: 0.0, max: 1.0 }, ) .with_smoother(SmoothingStyle::Linear(50.0)) - .with_value_to_string(formatters::v2s_f32_rounded(2)), + .with_value_to_string(formatters::v2s_f32_rounded(2)).with_callback(reverb_dry_wet_value_param_callback), + reverb_dry_wet_changed, reverb_room_size: FloatParam::new( "Reverb Room size", 0.5, @@ -429,6 +440,7 @@ impl Plugin for ToyboxC { fn editor(&mut self, _async_executor: AsyncExecutor) -> Option> { let params = self.params.clone(); + let reverb_dry_wet_changed = self.params.reverb_dry_wet_changed.clone(); let preset_value_changed = self.params.preset_changed.clone(); let editor = WebViewEditor::new( HTMLSource::URL("https://zmann.localhost/index.html"), @@ -445,21 +457,21 @@ impl Plugin for ToyboxC { } else if path.ends_with(".png") { "image/png" } else { - "text/html" // falback, replace with mime_guess + "application/octet-stream" // falback, replace with mime_guess }; match WEB_ASSETS.get_file(path.trim_start_matches("/")) { Some(content) => { return Response::builder() .header(CONTENT_TYPE, mimetype) - .header("Access-Control-Allow-Origin", "https://toybox.localhost") + .header("Access-Control-Allow-Origin", "https://zmann.localhost") .body(content.contents().into()) .map_err(Into::into); } None => { return Response::builder() .header(CONTENT_TYPE, mimetype) - .header("Access-Control-Allow-Origin", "https://toybox.localhost") + .header("Access-Control-Allow-Origin", "https://zmann.localhost") .body((b"not found" as &[u8]).into()) .map_err(Into::into); } @@ -472,7 +484,7 @@ impl Plugin for ToyboxC { while let Some(event) = ctx.next_event() { match event { WebviewEvent::JSON(value) => { - if let Ok(action) = serde_json::from_value(value) { + if let Ok(action) = serde_json::from_value(value.clone()) { match action { Action::SetGain { value } => { setter.begin_set_parameter(¶ms.output_gain); @@ -484,6 +496,11 @@ impl Plugin for ToyboxC { setter.set_parameter(¶ms.preset, preset); setter.end_set_parameter(¶ms.preset); } + Action::SetReverbDryWet { value } => { + setter.begin_set_parameter(¶ms.reverb_dry_wet_ratio); + setter.set_parameter_normalized(¶ms.reverb_dry_wet_ratio, value); + setter.end_set_parameter(¶ms.reverb_dry_wet_ratio); + } Action::Init => { let _ = ctx.send_json(json!({ "type": "preset_change", @@ -494,7 +511,7 @@ impl Plugin for ToyboxC { } } } else { - panic!("Invalid action received from web UI.") + panic!("Invalid action received from web UI. {:?}", value) } } WebviewEvent::FileDropped(path) => println!("File dropped: {:?}", path), @@ -502,6 +519,16 @@ impl Plugin for ToyboxC { } } + if reverb_dry_wet_changed.swap(false, Ordering::Relaxed) + { + let _ = ctx.send_json(json!({ + "type": "reverb_dry_wet_change", + "param": "reverb_dry_wet_change", + "value": params.reverb_dry_wet_ratio.value().to_string(), + "text": params.reverb_dry_wet_ratio.to_string() + })); + } + if preset_value_changed.swap(false, Ordering::Relaxed) { let _ = ctx.send_json(json!({ "type": "preset_change", @@ -509,7 +536,6 @@ impl Plugin for ToyboxC { "value": params.preset.value().to_string(), "text": params.preset.to_string() })); - nih_log!("[webview] preset changed") } }); diff --git a/packages/toybox_c1200_ui/app/page.tsx b/packages/toybox_c1200_ui/app/page.tsx index 1afbe452..0f386ef3 100644 --- a/packages/toybox_c1200_ui/app/page.tsx +++ b/packages/toybox_c1200_ui/app/page.tsx @@ -9,6 +9,7 @@ import { Card, useTheme, useScale, + ToggleList, } from "@himalaya-ui/core"; import { Grid as GridIcon, @@ -16,6 +17,7 @@ import { Settings, } from "@himalaya-ui/core/icons"; import { KnobDecorative } from "@/lib/Knob"; +import { KnobBase } from "@/lib/percentage/KnobBase"; declare global { interface Window { @@ -70,6 +72,7 @@ export default function Home() { window.ipc.postMessage(JSON.stringify(msg)); }; + const [reverbDryWetValue, setreverbDryWetValue] = useState(50); const [presetValue, setpresetValue] = useState(""); useEffect(() => { @@ -84,6 +87,11 @@ export default function Home() { setpresetValue(msg.value); break; } + case "reverb_dry_wet_change": { + console.log(msg.value * 100); + setreverbDryWetValue(msg.value * 100); + break; + } } }; sendToPlugin({ type: "Init" }); @@ -164,7 +172,14 @@ export default function Home() { style={{ padding: "10px", gap: "10px" }} > - hello + + List + Grid + }> +
+
content
+
+
@@ -173,7 +188,12 @@ export default function Home() { - + { + sendToPlugin({ + type: "SetReverbDryWet", + value: (value / 100), + }) + }} label='Dry/Wet' value01={reverbDryWetValue / 100} /> @@ -183,10 +203,11 @@ export default function Home() { interface ModuleProps { children?: ReactNode; + footer?: ReactNode; name?: string; } -const Module: React.FC = ({ children, name }) => { +const Module: React.FC = ({ children, footer, name }) => { const theme = useTheme(); const { SCALES } = useScale(); return ( @@ -225,6 +246,19 @@ const Module: React.FC = ({ children, name }) => { )} {children} + {footer && ( +
+ {footer} +
+ )} ); }; diff --git a/packages/toybox_c1200_ui/lib/Knob.tsx b/packages/toybox_c1200_ui/lib/Knob.tsx index 8c9f82bf..25635bfc 100644 --- a/packages/toybox_c1200_ui/lib/Knob.tsx +++ b/packages/toybox_c1200_ui/lib/Knob.tsx @@ -51,3 +51,9 @@ const dragSensitivity = 0.006; const valueRawRoundFn = Math.round; const valueRawDisplayFn = (valueRaw: number): string => `${valueRawRoundFn(valueRaw)} units`; + const valueMin = 0; + const valueMax = 100; + const valueDefault = 50; + const valueRawRoundFn = Math.round; + const valueRawDisplayFn = (valueRaw: number): string => + `${valueRawRoundFn(valueRaw)}%`; \ No newline at end of file diff --git a/packages/toybox_c1200_ui/lib/KnobBaseThumb.tsx b/packages/toybox_c1200_ui/lib/KnobBaseThumb.tsx index 69afa20a..f516e74c 100644 --- a/packages/toybox_c1200_ui/lib/KnobBaseThumb.tsx +++ b/packages/toybox_c1200_ui/lib/KnobBaseThumb.tsx @@ -10,7 +10,7 @@ export function KnobBaseThumb({ value01 }: KnobBaseThumbProps) { const angleMin = -145; const angleMax = 145; const angle = mapFrom01Linear(value01, angleMin, angleMax); - + return (
; +type KnobBaseThumbProps = React.ComponentProps; +type KnobBaseProps = Pick< + KnobHeadlessProps, + | 'orientation' + | 'mapTo01' + | 'mapFrom01' +> & + Pick & { + readonly label: string; + readonly onValueRawChange: (value: number) => void; // Add this prop + }; + +export function KnobBase({ + label, + orientation, + mapTo01 = mapTo01Linear, + mapFrom01 = mapFrom01Linear, + onValueRawChange, // Add this prop +}: KnobBaseProps) { + const knobId = useId(); + const UITheme = useTheme(); + const labelId = useId(); + const [valueRaw, setValueRaw] = useState(valueDefault); + const value01 = mapTo01(valueRaw, valueMin, valueMax); + const dragSensitivity = 0.006; + return ( +
+ {label} + { onValueRawChange(value); setValueRaw(value) }} // Pass the prop + > + + + + {valueRawDisplayFn(valueRaw)} + +
+ ); +} + +const valueMin = 0; +const valueMax = 100; +const valueDefault = 50; +const valueRawRoundFn = Math.round; +const valueRawDisplayFn = (valueRaw: number): string => + `${valueRawRoundFn(valueRaw)}%`; \ No newline at end of file