Skip to content

Commit

Permalink
chore: documentation structure
Browse files Browse the repository at this point in the history
  • Loading branch information
danigb committed Sep 3, 2024
1 parent 12b88cd commit d407d14
Show file tree
Hide file tree
Showing 41 changed files with 1,441 additions and 40 deletions.
24 changes: 20 additions & 4 deletions site/.source/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions site/app/audio-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { registerSynthletOnce } from "synthlet";

export async function createSynthAudioContext(): Promise<AudioContext> {
const audioContext = new AudioContext();
await registerSynthletOnce(audioContext);
return audioContext;
}
67 changes: 67 additions & 0 deletions site/app/components/AdsrControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useEffect } from "react";
import { Slider } from "./Slider";

export function AdsrControls({
onAttackChanged,
onDecayChanged,
onSustainChanged,
onReleaseChanged,
adsr,
}: {
onAttackChanged: (value: number) => void;
onDecayChanged: (value: number) => void;
onSustainChanged: (value: number) => void;
onReleaseChanged: (value: number) => void;
adsr: [number, number, number, number];
}) {
useEffect(() => {
onAttackChanged(adsr[0]);
onDecayChanged(adsr[1]);
onSustainChanged(adsr[2]);
onReleaseChanged(adsr[3]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<>
<Slider
label="attack"
inputClassName="col-span-2"
min={0.1}
max={10}
step={0.1}
initial={adsr[0]}
onChange={onAttackChanged}
units="secs"
/>
<Slider
label="decay"
inputClassName="col-span-2"
min={0.1}
max={10}
step={0.1}
initial={adsr[1]}
onChange={onDecayChanged}
units="secs"
/>
<Slider
label="sustain"
inputClassName="col-span-2"
min={0.1}
max={1}
step={0.01}
initial={adsr[2]}
onChange={onSustainChanged}
/>
<Slider
label="release"
inputClassName="col-span-2"
min={0.1}
max={100}
step={0.2}
initial={adsr[3]}
onChange={onReleaseChanged}
units="secs"
/>
</>
);
}
52 changes: 52 additions & 0 deletions site/app/components/FrequencySelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useState } from "react";

export function FrequencySelector({
label,
initial,
onChange,
}: {
label: string;
initial: number;
onChange: (value: number) => void;
}) {
const NOTES = "C C# D D# E F F# G G# A A# B".split(" ");

const [freq, setFreq] = useState(initial);

const updateFreq = (freq: number) => {
setFreq(freq);
onChange(freq);
};

return (
<>
<p className={"text-right"}>{label}</p>
<input
className={"col-span-2"}
type="range"
min={20}
max={20000}
step={1}
value={freq}
onChange={(e) => {
updateFreq(e.target.valueAsNumber);
}}
/>
<p className={""}>{freq.toFixed(2)}</p>
<p></p>
<div className="col-span-3 flex gap-1">
{NOTES.map((note, i) => (
<button
key={note}
className="border border-white px-1 rounded opacity-70 hover:opacity-80"
onClick={() => {
updateFreq(440 * Math.pow(2, i / 12));
}}
>
{note}
</button>
))}
</div>
</>
);
}
45 changes: 45 additions & 0 deletions site/app/components/GateControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useState } from "react";

export function GateControls({
onGateChanged,
}: {
onGateChanged: (on: boolean) => void;
}) {
const [isOn, setOn] = useState(false);
return (
<div className="flex gap-2 mt-4 items-center">
<div
className={`w-4 h-4 rounded-full border ${
isOn ? "bg-blue-100" : "bg-transparent"
}`}
/>
<button
className="border border-white px-1 rounded opacity-70 hover:opacity-80"
onMouseDown={() => {
onGateChanged(true);
setOn(true);
}}
onMouseUp={() => {
onGateChanged(false);
setOn(false);
}}
onMouseLeave={() => {
onGateChanged(false);
setOn(false);
}}
>
Gate
</button>
<button
className="border border-white px-1 rounded opacity-70 hover:opacity-80"
onClick={() => {
const value = !isOn;
onGateChanged(value);
setOn(value);
}}
>
On/off
</button>
</div>
);
}
44 changes: 44 additions & 0 deletions site/app/components/Selector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useEffect, useState } from "react";

export function Selector({
name,
initialValue,
values,
onChange,
selectClassName,
initialize,
}: {
name: string;
values: readonly string[];
onChange: (value: string) => void;
initialValue: string;
selectClassName?: string;
initialize?: boolean;
}) {
const [value, setValue] = useState<string>(initialValue);

useEffect(() => {
if (initialize) {
onChange(initialValue);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [initialize, initialValue]);

return (
<>
<p className="text-right">{name}</p>
<select
className={selectClassName}
value={value}
onChange={(e) => {
setValue(e.target.value);
onChange(e.target.value);
}}
>
{values.map((type) => (
<option key={type}>{type}</option>
))}
</select>
</>
);
}
59 changes: 59 additions & 0 deletions site/app/components/Slider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useEffect, useState } from "react";

export function Slider({
label,
min,
max,
initial,
onChange,
step = 1,
transform = (x) => x,
inputClassName,
labelClassName = "text-right",
valueClassName,
units,
initialize,
}: {
label: string;
min: number;
max: number;
initial: number;
onChange: (value: number) => void;
transform?: (value: number) => number;
step?: number;
labelClassName?: string;
inputClassName?: string;
valueClassName?: string;
units?: string;
initialize?: boolean;
}) {
const [value, setValue] = useState(initial);

useEffect(() => {
if (initialize) onChange(initial);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [initialize]);

return (
<>
<p className={labelClassName}>{label}</p>
<input
className={inputClassName}
type="range"
min={min}
max={max}
step={step}
value={value}
onChange={(e) => {
const value = e.target.valueAsNumber;
setValue(value);
onChange(transform(value));
}}
/>
<p className={valueClassName}>
{transform(value).toFixed(2)}
{units}
</p>
</>
);
}
33 changes: 33 additions & 0 deletions site/app/components/StateVariableFilterControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Slider } from "./Slider";

export function StateVariableFilterControls({
onFrequencyChanged,
onResonanceChanged,
}: {
onFrequencyChanged: (value: number) => void;
onResonanceChanged: (value: number) => void;
}) {
return (
<>
<Slider
label="frequency"
inputClassName="col-span-2"
min={0}
max={20000}
step={1}
initial={16000}
onChange={onFrequencyChanged}
units="Hz"
/>
<Slider
label="resonance"
inputClassName="col-span-2"
min={0.01}
max={40}
step={0.01}
initial={0.5}
onChange={onResonanceChanged}
/>
</>
);
}
Loading

0 comments on commit d407d14

Please sign in to comment.