Parameter Format

Preset.to_array() packs all synth state into a single (145,) float32 vector.

The first 123 values are continuous floats in [0, 1]. The last 22 values are integers (stored as float32 in the array, recovered by rounding on from_array()). The integer maxes are documented in the table below, making it straightforward to specify independent sampling distributions for each section.

You can create a Preset from a Patch (patch.to_preset() or Preset.from_patch(patch)), from a flat array (Preset.from_array(arr)), or by constructing one directly with keyword arguments.

All 145 values are JAX PyTree data leaves — changing any value, including algorithm, never triggers JIT recompilation.

Layout

Continuous floats — indices 0–122

Global (15 values)

Index

Field

Native range

0

feedback

0–7

1

transpose

0–48

2

pitch_mod_sensitivity

0–7

3

lfo_speed

0–99

4

lfo_delay

0–99

5

lfo_pitch_mod_depth

0–99

6

lfo_amp_mod_depth

0–99

7–10

pitch_env_rates (4)

0–99

11–14

pitch_env_levels (4)

0–99

Per-operator (108 values, 6 ops × 18 floats each)

The 6-operator fields are packed in op-major order: all values for op 0 come first, then op 1, and so on through op 5. For example, op_env_rates occupies indices 15–38 as:

[op0_r0, op0_r1, op0_r2, op0_r3,  op1_r0, ..., op1_r3,  ...,  op5_r0, ..., op5_r3]
 ─────── op 0 ──────────           ─────── op 1 ───           ─────── op 5 ───

The same pattern applies to every per-operator field in the tables below.

Index range

Field

Native range

15–38

op_env_rates (6 × 4)

0–99

39–62

op_env_levels (6 × 4)

0–99

63–68

op_output_level (6)

0–99

69–74

op_frequency_coarse (6)

0–31

75–80

op_frequency_fine (6)

0–99

81–86

op_detune (6)

0–14

87–92

op_velocity_sensitivity (6)

0–7

93–98

op_amp_mod_sensitivity (6)

0–3

99–104

op_rate_scaling (6)

0–7

105–110

op_breakpoint (6)

0–99

111–116

op_left_depth (6)

0–99

117–122

op_right_depth (6)

0–99

Integers — indices 123–144

Global (4 values)

Index

Field

Max

123

osc_key_sync

1

124

lfo_sync

1

125

algorithm

31

126

lfo_wave

5

Per-operator (18 values, 6 ops × 3 ints each)

Index range

Field

Max

127–132

op_frequency_mode (6)

1

133–138

op_left_curve (6)

3

139–144

op_right_curve (6)

3

Approach A — flat black-box

from dexed import Patch, Preset
import numpy as np

# Patch → Preset
preset = Patch.load_bank("bank.syx")[0].to_preset()

# Round-trip
arr     = preset.to_array()           # (145,) float32
preset2 = Preset.from_array(arr)

# Sample: 123 floats from Beta, 22 ints from Categoricals
floats   = beta_distribution.sample((123,))
integers = np.array([
    np.random.randint(0, mx + 1)
    for mx in Preset.GLOBAL_INT_MAXES             # [1, 1, 31, 5]
] + [
    np.random.randint(0, mx + 1)
    for mx in Preset.OP_INT_MAXES * 6             # [1, 3, 3] × 6 ops
])
preset = Preset.from_array(np.concatenate([floats, integers]).astype(np.float32))

# Bulk storage: 100k presets ≈ 55 MB
bank = np.stack([p.to_array() for p in presets])  # (N, 145) float32
np.save("bank.npy", bank)
presets = [Preset.from_array(row) for row in np.load("bank.npy")]

Approach B — per-operator black-box

A network predicts global params and one bundle per operator. The spec is self-contained: no DX7 knowledge needed, just array shapes and integer maxes.

# Spec (class constants — no DX7 knowledge required)
Preset.GLOBAL_CONTINUOUS_SIZE  # 15
Preset.GLOBAL_INT_MAXES        # [1, 1, 31, 5]  (osc_key_sync, lfo_sync, algorithm, lfo_wave)
Preset.OP_CONTINUOUS_SIZE      # 18
Preset.OP_INT_MAXES            # [1, 3, 3]       (frequency_mode, left_curve, right_curve)

# Decompose a Preset into bundles
gc = preset.global_continuous()   # (15,)   float32 in [0, 1]
gi = preset.global_ints()         # (4,)    int32
oc = preset.op_continuous()       # (6, 18) float32 in [0, 1]  — row i = operator i
oi = preset.op_ints()             # (6, 3)  int32

# Reconstruct from bundles
preset = Preset.from_operator_bundles(gc, gi, oc, oi)

A network in this style:

# Network predicts all four arrays; sample independently per array
global_cont = beta_dist.sample((Preset.GLOBAL_CONTINUOUS_SIZE,))
global_int  = np.array([np.random.randint(0, mx + 1)
                         for mx in Preset.GLOBAL_INT_MAXES], dtype=np.int32)
op_cont     = beta_dist.sample((6, Preset.OP_CONTINUOUS_SIZE))
op_int      = np.array([[np.random.randint(0, mx + 1)
                          for mx in Preset.OP_INT_MAXES]
                         for _ in range(6)], dtype=np.int32)

preset = Preset.from_operator_bundles(global_cont, global_int, op_cont, op_int)
synth.load_preset(preset)

The per-operator column ordering within op_continuous() is: env_rates[0..3], env_levels[0..3], output_level, frequency_coarse, frequency_fine, detune, velocity_sensitivity, amp_mod_sensitivity, rate_scaling, breakpoint, left_depth, right_depth.

JAX PyTree

All Presets share one treedef (no meta fields):

import jax
from dexed import Preset

_, treedef = jax.tree.flatten(Preset())  # universal

# JIT-traceable flat → Preset
leaves = Preset.array_to_leaves(flat_array)  # 28 arrays/scalars
preset = jax.tree.unflatten(treedef, leaves)