Soundorv0.13.0

useParameter

A parameter's value, re-rendering the component whenever it changes.

function useParameter<T>(parameter: ParameterLike<T>): T;

useParameter(parameters.gain) returns the parameter's current value and re-renders the component when it changes, whoever changes it: the host's automation, a preset, another view, the audio side, or this UI.

import { Pressable, Text, View, useParameter } from '@soundor/react';
import type { ReactNode } from 'react';
import { parameters } from 'soundor:parameters';

function Button({
  label,
  selected = false,
  onPress,
}: {
  label: string;
  selected?: boolean;
  onPress: () => void;
}) {
  return (
    <Pressable
      onPress={onPress}
      accessibilityState={{ selected }}
      style={({ pressed }) => ({
        minWidth: 32,
        alignItems: 'center',
        paddingVertical: 4,
        paddingHorizontal: 10,
        borderRadius: 6,
        backgroundColor: selected || pressed ? '#3a7bfd' : '#2a2c33',
      })}
    >
      <Text style={{ color: 'white' }}>{label}</Text>
    </Pressable>
  );
}

function Row({ label, children }: { label: string; children: ReactNode }) {
  return (
    <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
      <Text style={{ width: 64, color: '#8b8f98' }}>{label}</Text>
      {children}
    </View>
  );
}

// One parameter of each type. Values are plain: a number in range (an int
// is rounded), a boolean, or one of an enum's values.
export default function Parameters() {
  const gain = useParameter(parameters.gain);
  const voices = useParameter(parameters.voices);
  const bypass = useParameter(parameters.bypass);
  const mode = useParameter(parameters.mode);

  return (
    <View style={{ gap: 10 }}>
      <Row label="Gain">
        <Button label="−" onPress={() => parameters.gain.set(gain - 0.1)} />
        <Text style={{ width: 40, color: '#e6e7ea' }}>{gain.toFixed(2)}</Text>
        <Button label="+" onPress={() => parameters.gain.set(gain + 0.1)} />
      </Row>
      <Row label="Voices">
        <Button label="−" onPress={() => parameters.voices.set(voices - 1)} />
        <Text style={{ width: 40, color: '#e6e7ea' }}>{voices}</Text>
        <Button label="+" onPress={() => parameters.voices.set(voices + 1)} />
      </Row>
      <Row label="Bypass">
        <Button
          label={bypass ? 'On' : 'Off'}
          selected={bypass}
          onPress={() => parameters.bypass.set(!bypass)}
        />
      </Row>
      <Row label="Mode">
        {parameters.mode.info.values.map((value) => (
          <Button
            key={value}
            label={value}
            selected={value === mode}
            onPress={() => parameters.mode.set(value)}
          />
        ))}
      </Row>
    </View>
  );
}
parameters.tsxSource
  • The parameter stays the source of truth. Show its value and change it with set(); do not copy it into React state, or the host's automation and presets will not show.
  • Changes arrive once a frame. A burst of changes (automation from the audio thread) re-renders once, with the latest value.
  • Typed from the config: a float or int parameter gives a number, a bool a boolean, an enum one of its values.
  • During a drag, bracket the set()s with beginGesture() and endGesture(), so the host records one edit:
const gain = useParameter(parameters.gain);

<View
  onPointerDown={() => parameters.gain.beginGesture()}
  onPointerMove={(event) => parameters.gain.set(valueAt(event.locationY))}
  onPointerUp={() => parameters.gain.endGesture()}
/>;

It takes anything with get() and subscribe(), as soundor:parameters' parameters have. The Parameters guide shows how to declare them and connect them to audio.