API Reference

Synthesizer

class dexed.DexedSynth(sample_rate=44100.0)

Bases: object

DX7 synthesizer.

Example:

synth = DexedSynth(sample_rate=44100)

# From a Patch (DX7 sysex interface)
patch = Patch.load_bank("rom1a.syx")[0]
synth.load_patch(patch)
audio = synth.render(midi_note=60, velocity=100)

# From a Preset (ML interface)
from dexed import Preset
preset = Preset(algorithm=15, feedback=0.5)
synth.load_preset(preset)
audio = synth.render(midi_note=60)

A synth may be shared between threads: loading and rendering are serialized per instance, and the GIL is released while audio is computed. For parallel rendering, give each thread its own synth so the work actually overlaps.

Parameters:

sample_rate (float)

load_patch(patch)

Load a Patch into the synthesizer.

Return type:

None

Parameters:

patch (Patch)

load_preset(preset)

Load a Preset into the synthesizer.

Return type:

None

render(midi_note=60, velocity=100, note_duration=3.0, render_duration=4.0)

Render audio with current parameters.

Parameters:
  • midi_note (int) – MIDI note number 0-127 (default 60 = C4)

  • velocity (int) – Note velocity 0-127 (default 100)

  • note_duration (float) – How long the note is held in seconds (default 3.0)

  • render_duration (float) – Total audio duration in seconds (default 4.0)

Return type:

ndarray

Returns:

numpy array of shape [T] with audio samples (float32)

render_all_ops(midi_note=60, velocity=100, note_duration=3.0, render_duration=4.0)

Render individual operator outputs.

Returns:

channels 0-5 are operators 0-5, channel 6 is the final output.

Return type:

ndarray

Parameters:
property sample_rate: float

Get the sample rate in Hz.

property algorithm: int

Get the currently loaded DX7 algorithm (0-31).

property normalize_feedback: bool

When True, use consistent feedback scaling across all algorithms.

When False (default), algorithms 3, 5, and 31 have reduced feedback, matching original Dexed behavior.

Preset

class dexed.Preset(feedback=0.0, transpose=0.5, pitch_mod_sensitivity=0.0, lfo_speed=0.35353535353535354, lfo_delay=0.0, lfo_pitch_mod_depth=0.0, lfo_amp_mod_depth=0.0, osc_key_sync=1, lfo_sync=0, pitch_env_rates=<factory>, pitch_env_levels=<factory>, op_env_rates=<factory>, op_env_levels=<factory>, op_output_level=<factory>, op_frequency_coarse=<factory>, op_frequency_fine=<factory>, op_detune=<factory>, op_velocity_sensitivity=<factory>, op_amp_mod_sensitivity=<factory>, op_rate_scaling=<factory>, op_breakpoint=<factory>, op_left_depth=<factory>, op_right_depth=<factory>, algorithm=0, lfo_wave=4, op_frequency_mode=<factory>, op_left_curve=<factory>, op_right_curve=<factory>)

Bases: object

PyTree-compatible DX7 preset with all synth state.

All fields are JAX PyTree data leaves – changing any value never triggers JIT recompilation. This makes Preset ideal for use as the argument to jax.pure_callback.

algorithm is 0-indexed (0–31), matching the DX7 sysex byte.

Because there are no meta fields, all Presets share the same treedef:

_, treedef = jax.tree.flatten(Preset())  # works for any Preset

Flat vector to Preset inside JIT:

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

@jax.jit
def make_preset(flat_params):  # flat_params is (145,)
    leaves = Preset.array_to_leaves(flat_params)
    return jax.tree.unflatten(treedef, leaves)

Bulk serialization:

arr = np.stack([p.to_array() for p in presets])  # (N, 145) float32
np.save('presets.npy', arr)
presets = [Preset.from_array(row) for row in np.load('presets.npy')]
Parameters:
feedback: float = 0.0
transpose: float = 0.5
pitch_mod_sensitivity: float = 0.0
lfo_speed: float = 0.35353535353535354
lfo_delay: float = 0.0
lfo_pitch_mod_depth: float = 0.0
lfo_amp_mod_depth: float = 0.0
osc_key_sync: int = 1
lfo_sync: int = 0
pitch_env_rates: ndarray
pitch_env_levels: ndarray
op_env_rates: ndarray
op_env_levels: ndarray
op_output_level: ndarray
op_frequency_coarse: ndarray
op_frequency_fine: ndarray
op_detune: ndarray
op_velocity_sensitivity: ndarray
op_amp_mod_sensitivity: ndarray
op_rate_scaling: ndarray
op_breakpoint: ndarray
op_left_depth: ndarray
op_right_depth: ndarray
algorithm: int = 0
lfo_wave: int = 4
op_frequency_mode: ndarray
op_left_curve: ndarray
op_right_curve: ndarray
SCALES: ClassVar[Dict[str, int]] = {'feedback': 7, 'lfo_amp_mod_depth': 99, 'lfo_delay': 99, 'lfo_pitch_mod_depth': 99, 'lfo_speed': 99, 'op_amp_mod_sensitivity': 3, 'op_breakpoint': 99, 'op_detune': 14, 'op_env_levels': 99, 'op_env_rates': 99, 'op_frequency_coarse': 31, 'op_frequency_fine': 99, 'op_left_depth': 99, 'op_output_level': 99, 'op_rate_scaling': 7, 'op_right_depth': 99, 'op_velocity_sensitivity': 7, 'pitch_env_levels': 99, 'pitch_env_rates': 99, 'pitch_mod_sensitivity': 7, 'transpose': 48}
DATA_FIELDS: ClassVar[List[str]] = ['feedback', 'transpose', 'pitch_mod_sensitivity', 'lfo_speed', 'lfo_delay', 'lfo_pitch_mod_depth', 'lfo_amp_mod_depth', 'pitch_env_rates', 'pitch_env_levels', 'op_env_rates', 'op_env_levels', 'op_output_level', 'op_frequency_coarse', 'op_frequency_fine', 'op_detune', 'op_velocity_sensitivity', 'op_amp_mod_sensitivity', 'op_rate_scaling', 'op_breakpoint', 'op_left_depth', 'op_right_depth', 'osc_key_sync', 'lfo_sync', 'algorithm', 'lfo_wave', 'op_frequency_mode', 'op_left_curve', 'op_right_curve']
META_FIELDS: ClassVar[List[str]] = []
GLOBAL_CONTINUOUS_SIZE: ClassVar[int] = 15
GLOBAL_INT_MAXES: ClassVar[List[int]] = [1, 1, 31, 5]
OP_CONTINUOUS_SIZE: ClassVar[int] = 18
OP_INT_MAXES: ClassVar[List[int]] = [1, 3, 3]
to_array()

Pack all parameters into a (145,) float32 array.

Order matches DATA_FIELDS. Integer fields are cast to float32.

Return type:

ndarray

classmethod from_array(arr)

Unpack a (145,) float32 array into a Preset.

Integer fields are recovered by rounding.

Parameters:

arr (ndarray) – (145,) float32 array as produced by to_array().

Return type:

Preset

global_continuous()

Global continuous params as a (15,) float32 array.

Order: feedback, transpose, pitch_mod_sensitivity, lfo_speed, lfo_delay, lfo_pitch_mod_depth, lfo_amp_mod_depth, pitch_env_rates[0..3], pitch_env_levels[0..3].

Return type:

ndarray

global_ints()

Global integer params as a (4,) int32 array.

Order matches GLOBAL_INT_MAXES: osc_key_sync, lfo_sync, algorithm, lfo_wave.

Return type:

ndarray

op_continuous()

Per-operator continuous params as a (6, 18) float32 array.

Row i holds operator i’s 18 continuous params. Order matches OP_CONTINUOUS_SIZE: env_rates[4], env_levels[4], output_level, frequency_coarse, frequency_fine, detune, velocity_sensitivity, amp_mod_sensitivity, rate_scaling, breakpoint, left_depth, right_depth.

Return type:

ndarray

op_ints()

Per-operator integer params as a (6, 3) int32 array.

Row i holds operator i’s 3 ints. Order matches OP_INT_MAXES: frequency_mode, left_curve, right_curve.

Return type:

ndarray

classmethod from_operator_bundles(global_cont, global_int, op_cont, op_int)

Construct a Preset from operator-bundle arrays.

Parameters:
  • global_cont (ndarray) – (15,) float32 — global continuous params.

  • global_int (ndarray) – (4,) int — [osc_key_sync, lfo_sync, algorithm, lfo_wave].

  • op_cont (ndarray) – (6, 18) float32 — per-operator continuous params.

  • op_int (ndarray) – (6, 3) int — per-operator ints [freq_mode, left_curve, right_curve].

Return type:

Preset

This is the inverse of calling global_continuous(), global_ints(), op_continuous(), op_ints().

static array_to_leaves(arr)

Split a (145,) flat array into a list of PyTree leaves.

Works with both NumPy and JAX arrays. JIT-traceable.

All Presets share the same treedef (no meta fields), so one treedef works universally:

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

@jax.jit
def make_preset(flat_params):
    return jax.tree.unflatten(treedef, Preset.array_to_leaves(flat_params))
Parameters:

arr – flat array with len(arr) == 145.

Returns:

List of 28 arrays/scalars matching the PyTree leaf order.

classmethod from_patch(patch)

Create a Preset from a Patch.

Delegates to to_preset().

Return type:

Preset

to_patch(name='PRESET')

Convert to a Patch.

Denormalizes continuous parameters, rounds to integers, and clamps to native DX7 ranges.

Parameters:

name (str)

Patch

class dexed.Patch(name='INIT VOICE')

Bases: object

Complete DX7 patch with all parameters.

Provides named access to all DX7 parameters and conversion methods for various formats (sysex, packed, normalized arrays).

Operators are 0-indexed (op[0] through op[5]). Algorithm is 0-indexed (0-31).

Parameters:

name (str)

property op: _OperatorAccessor

patch.op[0] through patch.op[5].

Type:

Access operators by 0-based index

classmethod from_sysex(data)

Create Patch from 156-byte unpacked DX7 voice data.

This is the “VCED” format used in single-voice dumps.

Return type:

Patch

Parameters:

data (bytes)

to_sysex()

Export to 156-byte unpacked DX7 voice data.

Every field is clamped to its DX7 range, so an out-of-range value saturates rather than wrapping: algorithm = 32 writes 31, not 0.

Return type:

bytes

classmethod from_packed(data)

Create Patch from 128-byte packed format (used in bulk dumps).

Return type:

Patch

Parameters:

data (bytes)

to_packed()

Export to 128-byte packed format.

Return type:

bytes

classmethod load_bank(filename)

Load a DX7 bank file (32 voices).

Accepts a standard 4104-byte bulk dump (F0 43 00 09 20 00 … payload … checksum F7) or a raw 4096-byte payload with no framing. A bulk dump may be preceded by other sysex messages; they are skipped.

Warns if the dump’s checksum does not match, and loads it anyway – cartridge rips often carry a stale checksum.

Return type:

List[Patch]

Parameters:

filename (str)

classmethod save_to_bank(filename, patches)

Save 32 patches to a bank file.

Mirrors load_bank(), so it is called on the class:

Patch.save_to_bank("my_bank.syx", patches)
Parameters:
to_raw()

Export to 155-element array of raw DX7 parameter values.

Values are in their native DX7 ranges (0-99, 0-31, etc.).

Return type:

ndarray

classmethod from_raw(params)

Create Patch from 155-element raw parameter array.

Return type:

Patch

Parameters:

params (ndarray)

to_preset()

Convert to a Preset (flat, normalized ML representation).

Custom Operator Graphs

class dexed.OperatorGraph(num_ops=6)

Bases: object

Custom FM synthesis graph with arbitrary operator routing.

Provides DX7-accurate synthesis with the flexibility to create custom topologies beyond the 32 standard algorithms.

Parameters:

num_ops (int)

connect(source, target, amount=1.0)

Connect source operator to modulate target operator.

Parameters:
  • source (int) – Source operator index (0-indexed)

  • target (int) – Target operator index (0-indexed)

  • amount (float) – Modulation amount (default 1.0)

Return type:

OperatorGraph

Returns:

self for method chaining

Raises:

ValueError – if source == target. An operator modulating itself is feedback, which carries a one-sample delay and a 0-7 level; use set_feedback() for it.

disconnect(source, target)

Remove modulation connection between operators.

Return type:

OperatorGraph

Parameters:
set_carriers(carriers)

Set which operators output to audio.

Parameters:

carriers (List[int]) – List of operator indices (0-indexed)

Return type:

OperatorGraph

Returns:

self for method chaining

set_feedback(source, target, level=7)

Set feedback from one operator’s output to another’s input.

Parameters:
  • source (int) – Source operator index (0-indexed) whose output is fed back

  • target (int) – Target operator index (0-indexed) that receives feedback. Same as source for self-feedback.

  • level (int) – Feedback level 0-7 (0 disables, 7 is maximum). Must be an integer – the render loop uses it as a bit shift.

Return type:

OperatorGraph

Returns:

self for method chaining

Raises:
  • ValueError – if an operator index or the level is out of range.

  • TypeError – if level is not an integer.

property mod_matrix: ndarray

Get the modulation matrix (read-only copy).

property carriers: List[int]

Get carrier operator indices (0-indexed).

property modulators: List[int]

Get modulator operator indices (0-indexed).

get_connections()

Get all connections as (source, target, amount) tuples.

Return type:

List[tuple]

Returns:

List of (source_op, target_op, amount) tuples, all 0-indexed.

get_sources(target)

Get all operators that modulate a given target.

Parameters:

target (int) – Target operator index (0-indexed)

Return type:

List[tuple]

Returns:

List of (source_op, amount) tuples.

get_targets(source)

Get all operators that a given source modulates.

Parameters:

source (int) – Source operator index (0-indexed)

Return type:

List[tuple]

Returns:

List of (target_op, amount) tuples.

is_connected(source, target)

Check if source modulates target.

Return type:

bool

Parameters:
connection_amount(source, target)

Get modulation amount (0.0 if not connected).

Return type:

float

Parameters:
get_feedback(source, target)

Get feedback level for a source->target edge (0 if none).

Return type:

int

Parameters:
disconnect_all()

Remove all connections (reset to isolated operators).

Return type:

OperatorGraph

summary()

Get a human-readable summary of the graph configuration.

Return type:

str

Returns:

Multi-line summary string.

to_mermaid()

Generate Mermaid diagram syntax for visualization.

Return type:

str

Returns:

Mermaid flowchart syntax string.

to_ascii()

Generate ASCII art representation of the graph.

Return type:

str

Returns:

Multi-line string showing operators, connections, and carriers.

render(sample_rate=44100.0, midi_note=60, velocity=100, note_duration=1.0, render_duration=1.5)

Render audio from the operator graph with DX7-accurate synthesis.

Parameters:
  • sample_rate (float) – Audio sample rate in Hz

  • midi_note (int) – MIDI note number (0-127)

  • velocity (int) – Note velocity (0-127)

  • note_duration (float) – How long the note is held in seconds

  • render_duration (float) – Total audio duration in seconds

Return type:

ndarray

Returns:

Audio samples as float32 numpy array

render_all_ops(sample_rate=44100.0, midi_note=60, velocity=100, note_duration=1.0, render_duration=1.5)

Render individual operator outputs with DX7-accurate synthesis.

Returns:

  • Channels 0 to num_ops-1: Individual operator outputs

  • Last channel: Final output (sum of carriers)

Return type:

ndarray

Parameters:
classmethod from_matrix(mod_matrix, carriers, feedback=None)

Create an operator graph from a modulation matrix.

Parameters:
  • mod_matrix (ndarray) – NxN array where [i,j] = amount op j modulates op i. The diagonal must be zero – self-modulation is feedback, which the render loop applies with a one-sample delay and a 0-7 level.

  • carriers (List[int]) – List of carrier operator indices (0-indexed)

  • feedback (Optional[Dict[Tuple[int, int], int]]) – Optional dict of {(source, target): level} for feedback (level 0-7)

Return type:

OperatorGraph

Returns:

OperatorGraph instance

Raises:

ValueError – if mod_matrix is not square, or its diagonal is nonzero.

classmethod from_algorithm(algorithm)

Create an operator graph matching a DX7 algorithm.

Parameters:

algorithm (int) – DX7 algorithm number (0-31)

Return type:

OperatorGraph

Returns:

OperatorGraph configured to match the algorithm

class dexed.GraphOperator(output_level=99, frequency_coarse=1, frequency_fine=0, frequency_mode=0, detune=7, velocity_sensitivity=0, rate_scaling=0, amp_mod_sensitivity=0, breakpoint=39, left_depth=0, right_depth=0, left_curve=0, right_curve=0, envelope=<factory>)

Bases: object

Operator in a custom FM synthesis graph.

Parameters match DX7 operators for familiarity and accuracy.

Parameters:
  • output_level (int)

  • frequency_coarse (int)

  • frequency_fine (int)

  • frequency_mode (int)

  • detune (int)

  • velocity_sensitivity (int)

  • rate_scaling (int)

  • amp_mod_sensitivity (int)

  • breakpoint (int)

  • left_depth (int)

  • right_depth (int)

  • left_curve (int)

  • right_curve (int)

  • envelope (GraphEnvelope)

output_level: int = 99
frequency_coarse: int = 1
frequency_fine: int = 0
frequency_mode: int = 0
detune: int = 7
velocity_sensitivity: int = 0
rate_scaling: int = 0
amp_mod_sensitivity: int = 0
breakpoint: int = 39
left_depth: int = 0
right_depth: int = 0
left_curve: int = 0
right_curve: int = 0
envelope: GraphEnvelope
property frequency_ratio: float

Compute frequency ratio from coarse/fine settings.

class dexed.GraphEnvelope(rates=<factory>, levels=<factory>)

Bases: object

4-stage envelope for graph operators.

Parameters:
rates: List[int]
levels: List[int]

Algorithm Utilities

dexed.get_carriers(algorithm)

Get list of carrier operator indices (0-5) for an algorithm.

Returns a fresh list; mutating it does not affect the algorithm table.

Return type:

List[int]

Parameters:

algorithm (int)

dexed.get_modulators(algorithm)

Get list of modulator operator indices (0-5) for an algorithm.

Returns a fresh list; mutating it does not affect the algorithm table.

Return type:

List[int]

Parameters:

algorithm (int)

dexed.get_mod_matrix(algorithm)

Get 6x6 modulation matrix for an algorithm.

Return type:

ndarray

Parameters:

algorithm (int)

dexed.get_feedback_edge(algorithm)

Get the feedback edge (source, target) for an algorithm.

Return type:

Tuple[int, int]

Parameters:

algorithm (int)

class dexed.algorithms.Algorithm(number, carriers, modulators, mod_matrix, feedback_edge)

Bases: object

Information about a single DX7 algorithm.

Instances are shared module-wide and must not be mutated: the fields are read-only, mod_matrix is a non-writeable array, and the accessor functions hand out copies of the lists.

Parameters:
number

Algorithm number (0-31)

carriers

Operator indices (0-5) that output to audio

modulators

Operator indices (0-5) that modulate other operators

mod_matrix

read-only 6x6 matrix where mod_matrix[i,j]=1 means op j modulates op i

feedback_edge

(source, target) operator indices for the feedback connection. For most algorithms source == target (self-feedback). Algorithms 3 and 5 (DX7 algos 4 and 6) have cross-operator feedback.

dexed.algorithms = <dexed.algorithms._AlgorithmDict object>

Dictionary-like access to algorithms by number (0-31).

The 32 DX7 algorithms

The 32 DX7 algorithms

Operator indices in the API above are 0-based, so operator i is the box numbered i + 1, and algorithm index 0 is DX7 algorithm 1.