Skip to content

Commit

Permalink
Parabolic time knobs
Browse files Browse the repository at this point in the history
  • Loading branch information
tamlyn committed Mar 7, 2024
1 parent 38ac206 commit 3ec033e
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 29 deletions.
68 changes: 42 additions & 26 deletions src/components/Knob/Knobs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,62 @@ import { KnobBase } from './KnobBase.tsx'

const stepFn = (): number => 0.01
const stepLargerFn = (): number => 0.1
const valueRawRoundFn = (num: number) => Math.round(num * 100) / 100
const displaySeconds = (valueRaw: number): string =>
`${valueRawRoundFn(valueRaw)}s`

const exponentialStepFn = (valueRaw: number): number => {
if (valueRaw < 0.1) return 0.01
if (valueRaw < 1) return 0.1
if (valueRaw < 10) return 1
if (valueRaw < 100) return 10
return 100
}

const mapFrom01Parabolic = (x: number, min: number, max: number): number =>
min + (max - min) * x * x

const mapTo01Parabolic = (x: number, min: number, max: number): number =>
Math.sqrt((x - min) / (max - min))

const valueRawRoundFn = (num: number) => Math.round(num * 1000) / 1000
const displaySeconds = (valueRaw: number): string => {
if (valueRaw < 1) return `${Math.round(valueRaw * 1000)}ms`
return `${valueRaw.toFixed(1)}s`
}

const displayPercent = (valueRaw: number): string =>
`${valueRawRoundFn(valueRaw) * 100}%`
`${(valueRaw * 100).toPrecision(3)}%`

type KnobProps = {
label: string
value: number
onChange: (newValue: number) => void
min?: number
max?: number
}

export const TimeKnob = ({ label, value, onChange }: KnobProps) => {
export const TimeKnob = ({ min = 0, max = 1, ...props }: KnobProps) => {
return (
<KnobBase
label={label}
value={value}
onChange={onChange}
stepFn={stepFn}
stepFn={exponentialStepFn}
stepLargerFn={stepLargerFn}
valueMin={0}
valueMax={5}
valueMin={min}
valueMax={max}
valueRawRoundFn={valueRawRoundFn}
valueRawDisplayFn={displaySeconds}
mapTo01={mapTo01Parabolic}
mapFrom01={mapFrom01Parabolic}
{...props}
/>
)
}

export const PercentKnob = ({ label, value, onChange }: KnobProps) => {
return (
<KnobBase
label={label}
value={value}
onChange={onChange}
stepFn={stepFn}
stepLargerFn={stepLargerFn}
valueMin={0}
valueMax={1}
valueRawRoundFn={valueRawRoundFn}
valueRawDisplayFn={displayPercent}
/>
)
}
export const PercentKnob = ({ min = 0, max = 1, ...props }: KnobProps) => (
<KnobBase
stepFn={stepFn}
stepLargerFn={stepLargerFn}
valueMin={min}
valueMax={max}
valueRawRoundFn={valueRawRoundFn}
valueRawDisplayFn={displayPercent}
{...props}
/>
)
1 change: 1 addition & 0 deletions src/components/Knob/knob.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

.label {
font-size: 12px;
font-family: monospace;
}

.knob {
Expand Down
31 changes: 28 additions & 3 deletions src/modules/Srvb/Srvb.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useEffect, useState } from 'react'

import { PercentKnob, TimeKnob } from '../../components/Knob/Knobs.tsx'
import { ModuleSpec, RenderAudioGraph } from '../types.ts'
import styles from './reverb.module.css'
import srvbAudio from './srvb-audio.ts'

type State = {
Expand All @@ -20,7 +22,7 @@ export const Reverb: ModuleSpec['Component'] = ({
telephone,
inputNode,
}) => {
const [state] = useState({
const [state, setState] = useState({
size: 0.5,
decay: 0.5,
mod: 0.5,
Expand All @@ -33,9 +35,32 @@ export const Reverb: ModuleSpec['Component'] = ({
'audioGraph',
renderAudioGraph({ id: moduleId, state, input: inputNode }),
)
}, [telephone, state, inputNode])
}, [telephone, state, inputNode, moduleId])

return <div />
return (
<div className={styles.knobs}>
<PercentKnob
label="Size"
value={state.size}
onChange={(size) => setState({ ...state, size })}
/>
<TimeKnob
label="Decay"
value={state.decay}
onChange={(decay) => setState({ ...state, decay })}
/>
<PercentKnob
label="Mod"
value={state.mod}
onChange={(mod) => setState({ ...state, mod })}
/>
<PercentKnob
label="Dry/wet"
value={state.mix}
onChange={(mix) => setState({ ...state, mix })}
/>
</div>
)
}

export default {
Expand Down
4 changes: 4 additions & 0 deletions src/modules/Srvb/reverb.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.knobs {
display: flex;
gap: 10px;
}
1 change: 1 addition & 0 deletions src/modules/Synth/Synth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export const Synth: ModuleSpec['Component'] = ({
label="Release"
value={state.release}
onChange={(release) => setState({ ...state, release })}
max={6}
/>
</div>
)
Expand Down

0 comments on commit 3ec033e

Please sign in to comment.