nimphea/nimphea_mapped_value

Source   Edit  

Mapped Value Module

This module provides value mapping and scaling utilities for control parameters. Maps normalized 0-1 input to typed ranges (int, float) with quantization and clamping. Useful for converting knob/CV inputs to musically meaningful parameter values.

Key Features:

  • Map 0-1 input to int or float ranges
  • Step quantization for discrete parameters
  • Bipolar and unipolar range mapping
  • Clamping to min/max bounds
  • Zero heap allocation (pure Nim implementation)
  • Audio-rate safe

Usage Example:

import nimphea_mapped_value

# Map knob (0-1) to discrete octave selection (0-4)
let knobValue = 0.6
let octave = mapValueInt(knobValue, 0, 4)
echo "Octave: ", octave  # 2

# Map CV input to bipolar range
let cvInput = 0.75  # 0-1 from ADC
let bipolar = mapValueBipolar(cvInput, -5.0, 5.0)
echo "CV: ", bipolar, "V"  # 2.5V

# Quantize float to steps
let continuous = 0.333
let stepped = mapValueFloatQuantized(continuous, 0.0, 1.0, 4)
echo "Quantized: ", stepped  # 0.25 (4 steps: 0.0, 0.25, 0.5, 0.75, 1.0)

Common Use Cases:

  • Discrete parameter selection (waveform, mode, octave)
  • Quantized control values (semitones, scale degrees)
  • Bipolar CV inputs (-5V to +5V → -1.0 to +1.0)
  • Range clamping and normalization

Procs

proc inverseLerp(a: float32; b: float32; value: float32): float32 {.inline,
    ...raises: [], tags: [], forbids: [].}

Inverse linear interpolation - find t for a given value.

Parameters:

  • a: Start value
  • b: End value
  • value: Target value

Returns: Interpolation factor t where lerp(a, b, t) = value

Example:

let t = inverseLerp(100.0, 200.0, 150.0)
# Result: 0.5
Source   Edit  
proc lerp(a: float32; b: float32; t: float32): float32 {.inline, ...raises: [],
    tags: [], forbids: [].}

Linear interpolation between two values.

Parameters:

  • a: Start value (at t = 0.0)
  • b: End value (at t = 1.0)
  • t: Interpolation factor (0.0 to 1.0)

Returns: Interpolated value

Note: t is not clamped - extrapolation allowed

Example:

let mid = lerp(100.0, 200.0, 0.5)
# Result: 150.0
Source   Edit  
proc mapValueBipolar(input: float32; min: float32; max: float32): float32 {.
    inline, ...raises: [], tags: [], forbids: [].}

Map bipolar 0-1 input to range (center at 0.5).

Parameters:

  • input: Normalized input (0.0 to 1.0, where 0.5 = center)
  • min: Output value at input = 0.0 (typically negative)
  • max: Output value at input = 1.0 (typically positive)

Returns: Linearly mapped bipolar value

Note: Useful for CV inputs where 0.5 represents 0V

Example:

# Map CV input to ±5V range
let cv = mapValueBipolar(0.75, -5.0, 5.0)
# Result: 2.5V (halfway between center and max)

# Map pan control (0.5 = center)
let pan = mapValueBipolar(0.5, -1.0, 1.0)
# Result: 0.0 (center)
Source   Edit  
proc mapValueFloat(input: float32; min: float32; max: float32): float32 {.
    inline, ...raises: [], tags: [], forbids: [].}

Map normalized 0-1 input to float range.

Parameters:

  • input: Normalized input (0.0 to 1.0)
  • min: Minimum output value
  • max: Maximum output value

Returns: Linearly mapped float value, clamped to range

Example:

let frequency = mapValueFloat(0.5, 100.0, 1000.0)
# Result: 550.0
Source   Edit  
proc mapValueFloatQuantized(input: float32; min: float32; max: float32;
                            numSteps: int): float32 {.inline, ...raises: [],
    tags: [], forbids: [].}

Map 0-1 input to quantized float steps.

Parameters:

  • input: Normalized input (0.0 to 1.0)
  • min: Minimum output value
  • max: Maximum output value
  • numSteps: Number of discrete steps (must be > 1)

Returns: Float value quantized to nearest step

Example:

# Quantize to semitones (12 steps per octave)
let semitone = mapValueFloatQuantized(0.45, 0.0, 12.0, 12)
# Result: 5.0 (closest semitone)

# Quantize mix to 5% increments (20 steps)
let mix = mapValueFloatQuantized(0.37, 0.0, 1.0, 20)
# Result: 0.35
Source   Edit  
proc mapValueInt(input: float32; min: int; max: int): int {.inline, ...raises: [],
    tags: [], forbids: [].}

Map normalized 0-1 input to integer range.

Parameters:

  • input: Normalized input (0.0 to 1.0)
  • min: Minimum output value (inclusive)
  • max: Maximum output value (inclusive)

Returns: Linearly mapped integer value

Note: Useful for discrete parameter selection (modes, octaves, etc.)

Example:

let waveform = mapValueInt(0.3, 0, 3)  # 4 waveforms: 0, 1, 2, 3
# Result: 1 (30% of range)
Source   Edit  
proc mapValueUnipolar(input: float32; max: float32): float32 {.inline,
    ...raises: [], tags: [], forbids: [].}

Map unipolar 0-1 input to 0-max range.

Parameters:

  • input: Normalized input (0.0 to 1.0)
  • max: Maximum output value

Returns: Linearly scaled value from 0 to max

Example:

let amplitude = mapValueUnipolar(0.8, 1.0)
# Result: 0.8
Source   Edit  
proc normalizeValue(value: float32; min: float32; max: float32): float32 {.
    inline, ...raises: [], tags: [], forbids: [].}

Normalize a value from range to 0-1.

Parameters:

  • value: Input value in range min, max
  • min: Minimum of input range
  • max: Maximum of input range

Returns: Normalized value in range 0.0, 1.0

Note: Inverse operation of mapValueFloat

Example:

let normalized = normalizeValue(440.0, 20.0, 20000.0)
# Result: ~0.021 (440 Hz in 20-20000 Hz range)
Source   Edit  
proc normalizeValueInt(value: int; min: int; max: int): float32 {.inline,
    ...raises: [], tags: [], forbids: [].}

Normalize an integer value from range to 0-1.

Parameters:

  • value: Input value in range min, max
  • min: Minimum of input range
  • max: Maximum of input range

Returns: Normalized value in range 0.0, 1.0

Example:

let normalized = normalizeValueInt(2, 0, 4)
# Result: 0.5 (middle of 0-4 range)
Source   Edit  
proc quantizeFloat(value: float32; stepSize: float32): float32 {.inline,
    ...raises: [], tags: [], forbids: [].}

Quantize a float value to nearest step.

Parameters:

  • value: Input value
  • stepSize: Size of quantization steps

Returns: Value rounded to nearest multiple of stepSize

Example:

let quantized = quantizeFloat(3.7, 0.5)
# Result: 3.5 (nearest 0.5 step)

# Quantize frequency to 10 Hz steps
let freq = quantizeFloat(447.3, 10.0)
# Result: 450.0
Source   Edit