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)