Skip to content

Commit

Permalink
prog; setup ipc communication and knob ui
Browse files Browse the repository at this point in the history
  • Loading branch information
AZProductions committed Jan 8, 2024
1 parent 232e1cb commit 65efc92
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 12 deletions.
40 changes: 33 additions & 7 deletions crates/toybox_c1200/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ enum Action {
Init,
SetGain { value: f32 },
SetPreset { preset: Presets },
SetReverbDryWet {value: f32},
}

#[derive(Enum, Debug, PartialEq, Eq, Clone, Copy)]
Expand Down Expand Up @@ -95,6 +96,7 @@ struct ToyboxCParams {

#[id = "reverb-dry-wet"]
pub reverb_dry_wet_ratio: FloatParam,
reverb_dry_wet_changed: Arc<AtomicBool>,

#[id = "reverb-room-size"]
pub reverb_room_size: FloatParam,
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -429,6 +440,7 @@ impl Plugin for ToyboxC {

fn editor(&mut self, _async_executor: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
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"),
Expand All @@ -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);
}
Expand All @@ -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(&params.output_gain);
Expand All @@ -484,6 +496,11 @@ impl Plugin for ToyboxC {
setter.set_parameter(&params.preset, preset);
setter.end_set_parameter(&params.preset);
}
Action::SetReverbDryWet { value } => {
setter.begin_set_parameter(&params.reverb_dry_wet_ratio);
setter.set_parameter_normalized(&params.reverb_dry_wet_ratio, value);
setter.end_set_parameter(&params.reverb_dry_wet_ratio);
}
Action::Init => {
let _ = ctx.send_json(json!({
"type": "preset_change",
Expand All @@ -494,22 +511,31 @@ 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),
_ => {}
}
}

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",
"param": "preset_change",
"value": params.preset.value().to_string(),
"text": params.preset.to_string()
}));
nih_log!("[webview] preset changed")
}
});

Expand Down
40 changes: 37 additions & 3 deletions packages/toybox_c1200_ui/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import {
Card,
useTheme,
useScale,
ToggleList,
} from "@himalaya-ui/core";
import {
Grid as GridIcon,
HelpCircle,
Settings,
} from "@himalaya-ui/core/icons";
import { KnobDecorative } from "@/lib/Knob";
import { KnobBase } from "@/lib/percentage/KnobBase";

declare global {
interface Window {
Expand Down Expand Up @@ -70,6 +72,7 @@ export default function Home() {
window.ipc.postMessage(JSON.stringify(msg));
};

const [reverbDryWetValue, setreverbDryWetValue] = useState<number>(50);
const [presetValue, setpresetValue] = useState<string>("");

useEffect(() => {
Expand All @@ -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" });
Expand Down Expand Up @@ -164,7 +172,14 @@ export default function Home() {
style={{ padding: "10px", gap: "10px" }}
>
<Grid xs={4}>
<Module name="Filter">hello<KnobDecorative valueDefault={74} value01={1} /></Module>
<Module name="Filter" footer={<ToggleList value="test">
<ToggleList.Item value="test">List</ToggleList.Item>
<ToggleList.Item value="test2">Grid</ToggleList.Item>
</ToggleList>}>
<div style={{ height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }}>
<div>content</div>
</div>
</Module>
</Grid>
<Grid xs={4}>
<Module name="Vibrato"></Module>
Expand All @@ -173,7 +188,12 @@ export default function Home() {
<Module name="Chorus"></Module>
</Grid>
<Grid xs={7}>
<Module name="Reverb"></Module>
<Module name="Reverb"><KnobBase onValueRawChange={(value) => {
sendToPlugin({
type: "SetReverbDryWet",
value: (value / 100),
})
}} label='Dry/Wet' value01={reverbDryWetValue / 100} /></Module>
</Grid>
</Grid.Container>
</main>
Expand All @@ -183,10 +203,11 @@ export default function Home() {

interface ModuleProps {
children?: ReactNode;
footer?: ReactNode;
name?: string;
}

const Module: React.FC<ModuleProps> = ({ children, name }) => {
const Module: React.FC<ModuleProps> = ({ children, footer, name }) => {
const theme = useTheme();
const { SCALES } = useScale();
return (
Expand Down Expand Up @@ -225,6 +246,19 @@ const Module: React.FC<ModuleProps> = ({ children, name }) => {
</header>
)}
<Card.Content>{children}</Card.Content>
{footer && (
<div
style={{
position: 'absolute',
display: 'flex',
justifyContent: 'center',
bottom: 0,
marginTop: 'auto', // Add this line
}}
>
{footer}
</div>
)}
</Card>
);
};
6 changes: 6 additions & 0 deletions packages/toybox_c1200_ui/lib/Knob.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)}%`;
4 changes: 2 additions & 2 deletions packages/toybox_c1200_ui/lib/KnobBaseThumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function KnobBaseThumb({ value01 }: KnobBaseThumbProps) {
const angleMin = -145;
const angleMax = 145;
const angle = mapFrom01Linear(value01, angleMin, angleMax);

return (
<div
style={{
Expand All @@ -19,7 +19,7 @@ export function KnobBaseThumb({ value01 }: KnobBaseThumbProps) {
height: "100%",
position: "absolute",
filter: "drop-shadow(0px 0px 5px " + UItheme.palette.background + ")",
background: "linear-gradient(to bottom, "+UItheme.palette.accents_2+", "+UItheme.palette.accents_1+")",
background: "linear-gradient(to bottom, " + UItheme.palette.accents_2 + ", " + UItheme.palette.accents_1 + ")",
}}
>
<div
Expand Down
85 changes: 85 additions & 0 deletions packages/toybox_c1200_ui/lib/percentage/KnobBase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
'use client';
import { useId, useState } from 'react';
import {
KnobHeadless,
KnobHeadlessLabel,
KnobHeadlessOutput,
} from 'react-knob-headless';
import { mapFrom01Linear, mapTo01Linear } from '@dsp-ts/math';
import { KnobBaseThumb } from '../KnobBaseThumb';
import useTheme from '@himalaya-ui/core/esm/use-theme';

type KnobHeadlessProps = React.ComponentProps<typeof KnobHeadless>;
type KnobBaseThumbProps = React.ComponentProps<typeof KnobBaseThumb>;
type KnobBaseProps = Pick<
KnobHeadlessProps,
| 'orientation'
| 'mapTo01'
| 'mapFrom01'
> &
Pick<KnobBaseThumbProps, 'value01'> & {
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<number>(valueDefault);
const value01 = mapTo01(valueRaw, valueMin, valueMax);
const dragSensitivity = 0.006;
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}
>
<KnobHeadlessLabel id={labelId} style={{ fontSize: 12 }}>{label}</KnobHeadlessLabel>
<KnobHeadless
style={{
outline: "2px solid transparent",
outlineOffset: 2,
width: "4rem",
height: "4rem",
position: "relative",
background:
"linear-gradient(white, white) padding-box, linear-gradient(to bottom," +
UITheme.palette.accents_5 +
"B5," +
UITheme.palette.background +
") border-box",
borderRadius: "56em",
border: "3px solid transparent",
}}
id={knobId}
aria-labelledby={labelId}
valueMin={valueMin}
valueMax={valueMax}
valueRaw={valueRaw}
valueRawRoundFn={valueRawRoundFn}
valueRawDisplayFn={valueRawDisplayFn}
dragSensitivity={dragSensitivity}
orientation={orientation}
mapTo01={mapTo01}
mapFrom01={mapFrom01}
onValueRawChange={(value) => { onValueRawChange(value); setValueRaw(value) }} // Pass the prop
>
<KnobBaseThumb value01={value01} />
</KnobHeadless>
<KnobHeadlessOutput htmlFor={knobId} style={{ fontSize: 12 }}>
{valueRawDisplayFn(valueRaw)}
</KnobHeadlessOutput>
</div>
);
}

const valueMin = 0;
const valueMax = 100;
const valueDefault = 50;
const valueRawRoundFn = Math.round;
const valueRawDisplayFn = (valueRaw: number): string =>
`${valueRawRoundFn(valueRaw)}%`;

0 comments on commit 65efc92

Please sign in to comment.