Skip to content

Converters API Reference

The audiomancer.converters module provides format conversion functionality.

Overview

converters

MIDI conversion for audiomancer.

Provides interfaces for converting between MIDI, TidalCycles, and SuperCollider formats.

DRUM_MIDI_MAP: dict[int, str] = {35: 'bd', 36: 'bd', 38: 'sn', 40: 'sn', 42: 'hc', 44: 'hc', 46: 'ho', 41: 'lt', 43: 'lt', 45: 'mt', 47: 'mt', 48: 'ht', 50: 'ht', 49: 'cc', 51: 'cr', 52: 'cc', 53: 'cr', 55: 'cc', 57: 'cc', 59: 'cr', 37: 'cp', 39: 'cp', 54: 'perc', 56: 'perc', 58: 'perc', 60: 'perc', 61: 'perc', 62: 'perc', 63: 'perc', 64: 'perc', 65: 'perc', 66: 'perc', 67: 'perc', 68: 'perc', 69: 'perc', 70: 'perc', 71: 'perc', 72: 'perc', 73: 'perc', 74: 'perc', 75: 'perc', 76: 'perc', 77: 'perc', 78: 'perc', 79: 'perc', 80: 'perc', 81: 'perc'} module-attribute

__all__ = ['DRUM_MIDI_MAP', 'MidiConverter', 'MidiData', 'MidiNote', 'MidiTrack', 'SCPattern', 'TidalPattern', 'midi_to_supercollider', 'supercollider_to_midi', 'midi_to_freq', 'freq_to_midi', 'quantize_time'] module-attribute

MidiConverter

Bases: Protocol

Interface for converting between MIDI and pattern formats.

Supports bidirectional conversion between MIDI, TidalCycles, and SuperCollider patterns.

change_tempo(midi: MidiData, new_bpm: float) -> MidiData

Change MIDI tempo without affecting note timing.

Adjusts note durations to maintain same musical rhythm at new tempo.

Parameters:

Name Type Description Default
midi MidiData

MIDI data

required
new_bpm float

New tempo in BPM

required

Returns:

Type Description
MidiData

MIDI data with new tempo (new copy)

Example

midi = MidiData(tempo=120, ...) faster = converter.change_tempo(midi, new_bpm=140) faster['tempo'] 140.0

load_midi_file(file_path: str) -> MidiData

Load MIDI file from disk.

Parameters:

Name Type Description Default
file_path str

Path to .mid file

required

Returns:

Type Description
MidiData

Parsed MIDI data

Raises:

Type Description
FileNotFoundError

If file does not exist

MidiParseError

If file is not valid MIDI

Example

midi = converter.load_midi_file("/patterns/beat.mid") midi['tempo'] 120.0 len(midi['tracks']) 2

merge_tracks(tracks: list[MidiTrack], tempo: float = 120.0) -> MidiData

Merge multiple MIDI tracks into single MIDI file.

Parameters:

Name Type Description Default
tracks list[MidiTrack]

List of MIDI tracks to merge

required
tempo float

Tempo for merged file

120.0

Returns:

Type Description
MidiData

MIDI data with all tracks

Example

drums = MidiTrack(...) bass = MidiTrack(...) midi = converter.merge_tracks([drums, bass], tempo=125) len(midi['tracks']) 2

midi_to_sc(midi: MidiData, synth: str = 'default') -> SCPattern

Convert MIDI to SuperCollider Pbind.

Algorithm: 1. Extract note pitches, durations, velocities from MIDI 2. Generate Pbind with Pseq patterns 3. Map velocity to amplitude (0-127 → 0.0-1.0)

Parameters:

Name Type Description Default
midi MidiData

MIDI data to convert

required
synth str

SuperCollider synth name

'default'

Returns:

Type Description
SCPattern

SCPattern with Pbind code

Example

midi = MidiData(...) pattern = converter.midi_to_sc(midi, synth="tb303") print(pattern['pbind_code']) Pbind( \instrument, \tb303, \midinote, Pseq([36, 38, 36, 38], inf), \dur, Pseq([0.5, 0.5, 0.5, 0.5], inf), \amp, Pseq([0.8, 0.6, 0.8, 0.6], inf), )

midi_to_tidal(midi: MidiData, sample_map: Optional[dict[int, str]] = None, channel: str = 'd1') -> TidalPattern

Convert MIDI to TidalCycles mininotation.

Algorithm: 1. Quantize notes to 16th note grid 2. Map MIDI pitch to sample name (using DRUM_MIDI_MAP or custom) 3. Convert to mininotation: - Note on 16th → sample name - Rest on 16th → ~ - Multiple notes → [sample1, sample2] 4. Handle timing subdivisions (8ths, 16ths, triplets)

Parameters:

Name Type Description Default
midi MidiData

MIDI data to convert

required
sample_map Optional[dict[int, str]]

Custom pitch→sample mapping (uses DRUM_MIDI_MAP if None)

None
channel str

Tidal channel to target (d1-d9)

'd1'

Returns:

Type Description
TidalPattern

TidalPattern with mininotation string

Example

midi = MidiData( ... tracks=[MidiTrack(notes=[ ... MidiNote(pitch=36, start=0.0, end=0.25, velocity=100), ... MidiNote(pitch=38, start=0.5, end=0.75, velocity=80), ... ])], ... tempo=120.0, ... time_signature=(4, 4), ... ) pattern = converter.midi_to_tidal(midi, channel="d1") pattern['pattern'] "bd ~ sn ~" pattern['sample_mapping'] {36: "bd", 38: "sn"}

quantize_midi(midi: MidiData, grid: Literal['8th', '16th', '32nd'] = '16th') -> MidiData

Quantize MIDI notes to grid.

Snaps note start times to nearest grid position.

Parameters:

Name Type Description Default
midi MidiData

MIDI data to quantize

required
grid Literal['8th', '16th', '32nd']

Quantization grid resolution

'16th'

Returns:

Type Description
MidiData

Quantized MIDI data (new copy, original unchanged)

Example

midi = MidiData(...) quantized = converter.quantize_midi(midi, grid="16th")

All note starts now align to 16th notes

save_midi_file(midi: MidiData, file_path: str) -> None

Save MIDI data to disk.

Parameters:

Name Type Description Default
midi MidiData

MIDI data to save

required
file_path str

Destination path for .mid file

required

Raises:

Type Description
IOError

If file cannot be written

Example

midi = MidiData(...) converter.save_midi_file(midi, "/patterns/beat.mid")

sc_to_midi(pattern: SCPattern, bpm: float = 120.0) -> MidiData

Convert SuperCollider Pbind to MIDI.

Parses Pbind code and extracts note data.

Parameters:

Name Type Description Default
pattern SCPattern

SCPattern to convert

required
bpm float

Tempo for MIDI output

120.0

Returns:

Type Description
MidiData

MIDI data with notes

Example

pattern = SCPattern( ... synth="tb303", ... pbind_code="Pbind(...)", ... ) midi = converter.sc_to_midi(pattern, bpm=125) midi['tempo'] 125.0

split_by_channel(midi: MidiData) -> dict[int, MidiTrack]

Split MIDI file into separate tracks by channel.

Parameters:

Name Type Description Default
midi MidiData

MIDI data to split

required

Returns:

Type Description
dict[int, MidiTrack]

Dictionary mapping channel number to track

Example

midi = MidiData(...) by_channel = converter.split_by_channel(midi) drums = by_channel[9] # Channel 10 (0-indexed = 9) bass = by_channel[0]

tidal_to_midi(pattern: TidalPattern, bpm: float = 120.0, bars: int = 1) -> MidiData

Convert TidalCycles pattern to MIDI.

Inverse of midi_to_tidal(). Parses mininotation and generates MIDI notes.

Parameters:

Name Type Description Default
pattern TidalPattern

TidalPattern to convert

required
bpm float

Tempo for MIDI output

120.0
bars int

Number of bars to generate

1

Returns:

Type Description
MidiData

MIDI data with notes

Example

pattern = TidalPattern( ... channel="d1", ... pattern="bd ~ sn ~", ... sample_mapping={36: "bd", 38: "sn"}, ... ) midi = converter.tidal_to_midi(pattern, bpm=120, bars=1) len(midi['tracks'][0]['notes']) 2

transpose_midi(midi: MidiData, semitones: int) -> MidiData

Transpose MIDI notes by semitones.

Parameters:

Name Type Description Default
midi MidiData

MIDI data to transpose

required
semitones int

Number of semitones (positive = up, negative = down)

required

Returns:

Type Description
MidiData

Transposed MIDI data (new copy)

Example

midi = MidiData(...) up_octave = converter.transpose_midi(midi, semitones=12) down_fifth = converter.transpose_midi(midi, semitones=-7)

MidiData

Bases: TypedDict

Complete MIDI file data.

Example

midi = MidiData( ... tracks=[ ... MidiTrack(notes=[...], channel=0, name="Drums"), ... MidiTrack(notes=[...], channel=1, name="Bass"), ... ], ... tempo=120.0, ... time_signature=(4, 4), ... ticks_per_beat=480, ... )

MidiNote

Bases: TypedDict

A single MIDI note event.

Example

note = MidiNote( ... pitch=36, # MIDI note number (0-127) ... start=0.0, # Start time in seconds ... end=0.25, # End time in seconds ... velocity=100, # Velocity (0-127) ... )

MidiTrack

Bases: TypedDict

A MIDI track containing notes.

Example

track = MidiTrack( ... notes=[ ... MidiNote(pitch=36, start=0.0, end=0.25, velocity=100), ... MidiNote(pitch=38, start=0.5, end=0.75, velocity=80), ... ], ... channel=0, ... name="Drums", ... )

SCPattern

Bases: TypedDict

SuperCollider Pbind pattern.

Example

pattern = SCPattern( ... synth="tb303", ... pbind_code=''' ... Pbind( ... \instrument, \tb303, ... \midinote, Pseq([36, 38], inf), ... \dur, Pseq([0.5, 0.5], inf), ... \amp, Pseq([0.8, 0.6], inf), ... ) ... ''', ... )

TidalPattern

Bases: TypedDict

TidalCycles pattern in mininotation format.

Example

pattern = TidalPattern( ... channel="d1", ... pattern="bd ~ sn ~", ... sample_mapping={36: "bd", 38: "sn"}, ... )

freq_to_midi(freq: float) -> int

Convert frequency to nearest MIDI note.

Parameters:

Name Type Description Default
freq float

Frequency in Hz

required

Returns:

Type Description
int

MIDI note number (0-127)

Example

freq_to_midi(440.0) 69 freq_to_midi(261.63) 60

midi_to_freq(note: int) -> float

Convert MIDI note number to frequency in Hz.

Parameters:

Name Type Description Default
note int

MIDI note number (0-127)

required

Returns:

Type Description
float

Frequency in Hz

Example

midi_to_freq(69) # A4 440.0 midi_to_freq(60) # C4 261.63

midi_to_supercollider(midi_data: bytes, synth_name: str = 'default', bpm: float = 120.0, output_format: Literal['pbind', 'routine', 'pattern'] = 'pbind') -> str

Convert MIDI to SuperCollider code.

Formats: - pbind: Pbind pattern (most common) - routine: Routine with explicit timing - pattern: Pdef pattern definition

Parameters:

Name Type Description Default
midi_data bytes

Raw MIDI bytes

required
synth_name str

SuperCollider synth name

'default'
bpm float

Tempo (used for timing calculations)

120.0
output_format Literal['pbind', 'routine', 'pattern']

Output format type

'pbind'

Returns:

Type Description
str

SuperCollider code string

Example

sc_code = midi_to_supercollider(midi_bytes, synth_name="tb303") print(sc_code) Pbind( \instrument, \tb303, \dur, Pseq([0.25, 0.25, 0.5, 0.25], 1), \freq, Pseq([261.63, 293.66, 329.63, 349.23], 1), \amp, Pseq([0.8, 0.6, 0.9, 0.7], 1) ).play;

quantize_time(time: float, grid: float = 0.25) -> float

Quantize time to grid (in beats).

Parameters:

Name Type Description Default
time float

Time in beats

required
grid float

Grid size in beats (0.25 = 16th note)

0.25

Returns:

Type Description
float

Quantized time

Example

quantize_time(0.23, grid=0.25) 0.25 quantize_time(1.1, grid=0.5) 1.0

supercollider_to_midi(sc_code: str, bpm: float = 120.0) -> bytes

Convert SuperCollider Pbind to MIDI.

Parses Pseq arrays to extract note data.

Parameters:

Name Type Description Default
sc_code str

SuperCollider Pbind code

required
bpm float

Tempo for MIDI output

120.0

Returns:

Type Description
bytes

MIDI data as bytes

Example

sc_code = 'Pbind(\freq, Pseq([440, 880], 1)).play;' midi = supercollider_to_midi(sc_code, bpm=120)