Generators API Reference¶
The audiomancer.generators module provides pattern generation and synth evolution functionality.
Overview¶
generators
¶
Pattern generation and evolution for audiomancer.
Provides interfaces for generating patterns with Magenta and evolving them through mutation and crossover.
Synth Evolution: - generate_synth: Generate new SynthDefs from templates or descriptions - mutate_synth: Mutate existing synths with genetic operations - breed_synths: Crossover two synths to create hybrids - Lineage tracking: Track evolutionary history and ratings
Example
Pattern generation:
from audiomancer.generators import PatternGenerator, EvolutionEngine generator = PatternGenerator() pattern = generator.generate_drums(style="techno", bars=4, bpm=125) engine = EvolutionEngine() mutated = engine.mutate_pattern(pattern.id, amount=0.3)
Synth evolution:
from audiomancer.generators import generate_synth, mutate_synth synth = generate_synth("acid bass with filter sweep", category="bass") variant = mutate_synth(synth, amount=0.5, seed=42) print(variant.mutation_log) ['Saw → Pulse', 'Added LFO modulation to cutoff']
__all__ = ['EvolutionEngine', 'PatternGenerator', 'PatternMetadata', 'SynthLineage', 'generate_synth', 'mutate_synth', 'breed_synths', 'GeneratedSynth', 'LineageTracker', 'SynthRecord', 'record_synth', 'rate_synth', 'get_synth_lineage', 'get_top_rated', 'get_generation_stats']
module-attribute
¶
EvolutionEngine
¶
Bases: Protocol
Interface for evolving patterns and synths through mutation and crossover.
Implements genetic algorithm-style evolution with lineage tracking.
crossover_patterns(pattern_id_1: str, pattern_id_2: str, seed: Optional[int] = None) -> PatternMetadata
¶
Crossover two patterns to create hybrid.
Algorithm: 1. Split each pattern at random bar boundary 2. Combine first half of pattern 1 with second half of pattern 2 3. Quantize and adjust timing to maintain coherence
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pattern_id_1
|
str
|
First parent pattern |
required |
pattern_id_2
|
str
|
Second parent pattern |
required |
seed
|
Optional[int]
|
Random seed for deterministic testing |
None
|
Returns:
| Type | Description |
|---|---|
PatternMetadata
|
New hybrid pattern with both parents in lineage |
Raises:
| Type | Description |
|---|---|
PatternNotFoundError
|
If either pattern not found |
IncompatiblePatternsError
|
If patterns have different time signatures |
Example
engine = EvolutionEngine() hybrid = engine.crossover_patterns( ... "ptrn_abc123", ... "ptrn_def456", ... seed=42, ... ) hybrid['parent_ids']["ptrn_abc123", "ptrn_def456"] hybrid['generation_method'] "crossover"
evolve_population(population: list[str], generations: int = 5, mutation_rate: float = 0.3, crossover_rate: float = 0.2, seed: Optional[int] = None) -> list[PatternMetadata]
¶
Evolve a population of patterns over multiple generations.
Genetic algorithm: 1. Rank population by fitness (user ratings) 2. Select top 50% as parents 3. Mutate parents (mutation_rate probability) 4. Crossover random parent pairs (crossover_rate probability) 5. Repeat for N generations
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
population
|
list[str]
|
List of pattern IDs to evolve |
required |
generations
|
int
|
Number of evolution cycles |
5
|
mutation_rate
|
float
|
Probability of mutation (0.0-1.0) |
0.3
|
crossover_rate
|
float
|
Probability of crossover (0.0-1.0) |
0.2
|
seed
|
Optional[int]
|
Random seed for deterministic testing |
None
|
Returns:
| Type | Description |
|---|---|
list[PatternMetadata]
|
Final population (patterns from all generations) |
Example
initial = ["ptrn_1", "ptrn_2", "ptrn_3", "ptrn_4"] final_pop = engine.evolve_population( ... population=initial, ... generations=5, ... mutation_rate=0.3, ... crossover_rate=0.2, ... seed=42, ... ) len(final_pop) > len(initial) True
get_lineage(item_id: str) -> list[str]
¶
Get full ancestry chain for pattern or synth.
Recursively traces parent_ids to root.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item_id
|
str
|
Pattern or synth ID |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
List of ancestor IDs from oldest to newest |
mutate_pattern(pattern_id: str, amount: float = 0.3, seed: Optional[int] = None) -> PatternMetadata
¶
Mutate pattern with deterministic randomness.
Mutation types (probability based on amount): 1. Shift timing: ±50ms per note (amount * 0.3 probability) 2. Swap notes: exchange two notes (amount * 0.2) 3. Change velocity: ±20% (amount * 0.3) 4. Add note: insert on empty step (amount * 0.1) 5. Remove note: delete random note (amount * 0.1)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pattern_id
|
str
|
Pattern to mutate |
required |
amount
|
float
|
Mutation strength (0.0-1.0, higher = more drastic) |
0.3
|
seed
|
Optional[int]
|
Random seed for deterministic testing |
None
|
Returns:
| Type | Description |
|---|---|
PatternMetadata
|
New mutated pattern with lineage tracking |
Raises:
| Type | Description |
|---|---|
PatternNotFoundError
|
If pattern_id does not exist |
Example
engine = EvolutionEngine() original = pattern_store.get("ptrn_abc123") mutated = engine.mutate_pattern( ... "ptrn_abc123", ... amount=0.5, ... seed=42, # Deterministic for testing ... ) mutated['parent_ids']["ptrn_abc123"] mutated['mutation_amount'] 0.5 mutated['generation_method'] "mutated"
mutate_synth(synth_id: str, amount: float = 0.3, seed: Optional[int] = None) -> SynthLineage
¶
Mutate SynthDef parameters.
Mutation types: 1. Adjust control defaults: ±20% of range 2. Swap oscillator types: Saw ↔ Pulse ↔ Sine 3. Add/remove effects: reverb, distortion, delay 4. Adjust envelope times: ±30%
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
synth_id
|
str
|
SynthDef to mutate |
required |
amount
|
float
|
Mutation strength (0.0-1.0) |
0.3
|
seed
|
Optional[int]
|
Random seed for deterministic testing |
None
|
Returns:
| Type | Description |
|---|---|
SynthLineage
|
New mutated SynthDef with lineage |
Raises:
| Type | Description |
|---|---|
SynthNotFoundError
|
If synth_id does not exist |
InvalidSynthError
|
If mutated synth fails validation with sclang |
Example
engine = EvolutionEngine() mutated = engine.mutate_synth( ... "synt_tb303", ... amount=0.4, ... seed=42, ... ) mutated['parent_ids']["synt_tb303"] mutated['mutation_log'] [ "Increased cutoff default: 1200 → 1440", "Changed oscillator: Saw → Pulse", ]
rank_by_rating(pattern_type: Optional[Literal['drums', 'melody', 'bass']] = None, limit: int = 10) -> list[PatternMetadata]
¶
Get highest-rated patterns.
Useful for selecting best candidates for further evolution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pattern_type
|
Optional[Literal['drums', 'melody', 'bass']]
|
Filter by type (None = all types) |
None
|
limit
|
int
|
Maximum results |
10
|
Returns:
| Type | Description |
|---|---|
list[PatternMetadata]
|
Patterns sorted by user_rating descending |
Example
top_drums = engine.rank_by_rating( ... pattern_type="drums", ... limit=5, ... ) top_drums[0]['user_rating'] 5
validate_mutated_synth(source_code: str) -> bool
¶
Validate SynthDef by compiling with sclang.
Ensures mutated SynthDef is syntactically valid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_code
|
str
|
SuperCollider code to validate |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if valid, False if compilation fails |
Example
code = "SynthDef(\test, { Out.ar(0, SinOsc.ar(440)); })" engine.validate_mutated_synth(code) True bad_code = "SynthDef(\test, { invalid syntax )" engine.validate_mutated_synth(bad_code) False
GeneratedSynth
dataclass
¶
A generated or mutated SynthDef.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
SynthDef name (unique identifier) |
source_code |
str
|
Full SuperCollider .scd code |
controls |
list[SynthControl]
|
List of control parameters |
parent_ids |
Optional[list[str]]
|
Parent synth IDs (empty if generated from scratch) |
generation_method |
str
|
How synth was created (generated/mutation/crossover) |
mutation_log |
Optional[list[str]]
|
Human-readable list of mutations applied |
category |
Optional[str]
|
Inferred category (bass, lead, pad, drum, fx) |
Example
synth = GeneratedSynth( ... name="tb303_evolved_1", ... source_code="SynthDef(...)", ... controls=[SynthControl("cutoff", 1500.0)], ... parent_ids=["tb303"], ... generation_method="mutation", ... mutation_log=["Saw → Pulse", "cutoff: 1200 → 1500"], ... )
LineageTracker
¶
Tracks synth evolution lineage and fitness scores.
Stores synth records in a JSON file for persistence across sessions.
Attributes:
| Name | Type | Description |
|---|---|---|
db_path |
Path to lineage database file |
Example
tracker = LineageTracker() tracker.record_synth("tb303_m1", ["tb303"], ["Saw → Pulse"]) lineage = tracker.get_lineage("tb303_m1") lineage["ancestors"]['tb303']
__init__(db_path: Optional[Path] = None)
¶
Initialize lineage tracker.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
db_path
|
Optional[Path]
|
Path to lineage database (defaults to ~/.audiomancer/lineage.json) |
None
|
get_generation_stats() -> Dict[str, Any]
¶
Get statistics about evolutionary generations.
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary with generation counts and depth metrics |
Example
tracker = LineageTracker() stats = tracker.get_generation_stats() stats { 'total_synths': 42, 'original_synths': 5, 'mutated_synths': 28, 'crossover_synths': 9, 'max_generation': 7, 'avg_generation': 3.2, 'avg_rating': 4.1, }
get_lineage(synth_id: str) -> Dict[str, Any]
¶
Get full ancestry and descendants for a synth.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
synth_id
|
str
|
Synth to trace |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary with ancestors, descendants, and generation info |
Example
tracker = LineageTracker() lineage = tracker.get_lineage("tb303_m2") lineage { 'synth_id': 'tb303_m2', 'ancestors': ['tb303', 'tb303_m1'], 'descendants': ['tb303_m3', 'tb303_m4'], 'generation': 2, 'family_tree': { 'tb303': { 'tb303_m1': { 'tb303_m2': {} } } } }
get_top_rated(limit: int = 10, min_rating: Optional[int] = None) -> List[Dict[str, Any]]
¶
Get highest-rated synths.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
int
|
Maximum number of results |
10
|
min_rating
|
Optional[int]
|
Minimum rating threshold (1-5) |
None
|
Returns:
| Type | Description |
|---|---|
List[Dict[str, Any]]
|
List of synth records sorted by rating (descending) |
Example
tracker = LineageTracker() top = tracker.get_top_rated(limit=5, min_rating=4) top[0]['user_rating'] 5
rate_synth(synth_id: str, score: int, notes: Optional[str] = None) -> None
¶
Record user rating for a synth.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
synth_id
|
str
|
Synth to rate |
required |
score
|
int
|
Rating 1-5 |
required |
notes
|
Optional[str]
|
Optional feedback text |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If score not in range 1-5 |
KeyError
|
If synth_id not found |
Example
tracker = LineageTracker() tracker.rate_synth("tb303_m1", 5, "Perfect acid sound!")
record_synth(synth_id: str, name: str, parent_ids: List[str], generation_method: str, mutation_log: List[str]) -> None
¶
Record a new synth in the lineage database.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
synth_id
|
str
|
Unique synth identifier |
required |
name
|
str
|
SynthDef name |
required |
parent_ids
|
List[str]
|
List of parent synth IDs |
required |
generation_method
|
str
|
Creation method (generated/mutation/crossover) |
required |
mutation_log
|
List[str]
|
List of mutations applied |
required |
Example
tracker = LineageTracker() tracker.record_synth( ... "synt_m1", ... "tb303_m1", ... ["tb303"], ... "mutation", ... ["Saw → Pulse", "cutoff: 1200 → 1440"] ... )
PatternGenerator
¶
Bases: Protocol
Interface for generating musical patterns using Magenta.
Supports drum and melody generation with style and scale constraints.
download_models() -> None
¶
Download all required Magenta models (~500MB total).
Downloads to ~/.local/share/audiomancer/models/
Raises:
| Type | Description |
|---|---|
IOError
|
If download fails |
Example
generator = PatternGenerator() generator.download_models()
Downloads drums_rnn.mag, melody_rnn.mag¶
generate_bass(key: str = 'C', scale: Literal['major', 'minor', 'pentatonic', 'chromatic'] = 'minor', bars: int = 4, bpm: float = 120.0, timeout: int = 30) -> PatternMetadata
¶
Generate bass line pattern.
Similar to melody but constrained to lower register.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Root note |
'C'
|
scale
|
Literal['major', 'minor', 'pentatonic', 'chromatic']
|
Scale type |
'minor'
|
bars
|
int
|
Number of bars |
4
|
bpm
|
float
|
Tempo in BPM |
120.0
|
timeout
|
int
|
Maximum generation time (seconds) |
30
|
Returns:
| Type | Description |
|---|---|
PatternMetadata
|
Generated bass pattern |
Raises:
| Type | Description |
|---|---|
InferenceTimeoutError
|
If generation exceeds timeout |
ModelLoadError
|
If Magenta not available |
Example
bass = generator.generate_bass( ... key="F#", ... scale="minor", ... bars=8, ... bpm=140, ... ) bass['type'] "bass"
generate_drums(style: Literal['basic', 'techno', 'house', 'dnb'] = 'basic', bars: int = 4, bpm: float = 120.0, timeout: int = 30) -> PatternMetadata
¶
Generate drum pattern using Magenta DrumsRNN.
Styles: - basic: 4-on-floor kick, snare on 2&4 - techno: 16th hat variations, offbeat kicks - house: shuffle groove, open hats - dnb: fast breaks, syncopation
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
style
|
Literal['basic', 'techno', 'house', 'dnb']
|
Generation style |
'basic'
|
bars
|
int
|
Number of bars to generate |
4
|
bpm
|
float
|
Tempo in BPM |
120.0
|
timeout
|
int
|
Maximum generation time (seconds) |
30
|
Returns:
| Type | Description |
|---|---|
PatternMetadata
|
Generated pattern with MIDI data |
Raises:
| Type | Description |
|---|---|
InferenceTimeoutError
|
If generation exceeds timeout |
ModelLoadError
|
If Magenta not available |
Example
generator = PatternGenerator() pattern = generator.generate_drums( ... style="techno", ... bars=4, ... bpm=125, ... timeout=30, ... ) pattern['type'] "drums" pattern['bpm'] 125.0 pattern['parent_ids'][]
generate_melody(key: str = 'C', scale: Literal['major', 'minor', 'pentatonic', 'chromatic'] = 'minor', bars: int = 4, bpm: float = 120.0, timeout: int = 30) -> PatternMetadata
¶
Generate melody using Magenta MelodyRNN.
Scales: - major: C D E F G A B (Ionian) - minor: C D Eb F G Ab Bb (Natural minor/Aeolian) - pentatonic: C D F G A (Minor pentatonic) - chromatic: All 12 notes
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Root note (e.g., "C", "F#", "Bb") |
'C'
|
scale
|
Literal['major', 'minor', 'pentatonic', 'chromatic']
|
Scale type |
'minor'
|
bars
|
int
|
Number of bars to generate |
4
|
bpm
|
float
|
Tempo in BPM |
120.0
|
timeout
|
int
|
Maximum generation time (seconds) |
30
|
Returns:
| Type | Description |
|---|---|
PatternMetadata
|
Generated melody pattern |
Raises:
| Type | Description |
|---|---|
InferenceTimeoutError
|
If generation exceeds timeout |
ModelLoadError
|
If Magenta not available |
Example
generator = PatternGenerator() melody = generator.generate_melody( ... key="C", ... scale="minor", ... bars=4, ... bpm=120, ... ) melody['key'] "C" melody['scale'] "minor"
load_model(model_name: Literal['drums_rnn', 'melody_rnn']) -> None
¶
Load and cache Magenta model.
Downloads model if not present. Caches in memory for reuse.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_name
|
Literal['drums_rnn', 'melody_rnn']
|
Model to load |
required |
Raises:
| Type | Description |
|---|---|
ModelLoadError
|
If download or loading fails |
ImportError
|
If Magenta/TensorFlow not installed |
Example
generator = PatternGenerator() generator.load_model("drums_rnn") generator.load_model("melody_rnn")
PatternMetadata
¶
Bases: TypedDict
Metadata for a generated pattern.
Stores MIDI data and conversion outputs for patterns.
Example
pattern = PatternMetadata( ... id="ptrn_abc12345", ... type="drums", ... midi_data='{"tracks": [...], "tempo": 120}', ... tidal_code="d1 $ sound "bd ~ sn ~"", ... sc_code="Pbind(\instrument, \default, ...)", ... style="techno", ... key="C", ... scale="minor", ... bpm=125.0, ... bars=4, ... parent_ids=["ptrn_xyz789"], ... generation_method="mutated", ... mutation_amount=0.3, ... user_rating=4, ... user_notes="Good groove, needs variation", ... created_at=datetime.now(), ... )
SynthLineage
¶
Bases: TypedDict
Lineage tracking for evolved SynthDefs.
Records mutation history and parent relationships.
Example
lineage = SynthLineage( ... id="synt_evolved1", ... name="tb303_evolved_1", ... source_code="SynthDef(\tb303_evolved_1, { ... })", ... parent_ids=["synt_tb303"], ... generation_method="mutated", ... mutation_log=[ ... "Increased cutoff default: 1200 → 1500", ... "Added distortion: 0 → 0.3", ... ], ... user_rating=5, ... user_notes="Perfect acid sound", ... created_at=datetime.now(), ... )
SynthRecord
dataclass
¶
Record of a synth in the lineage database.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
Unique synth identifier |
name |
str
|
SynthDef name |
parent_ids |
List[str]
|
List of parent synth IDs |
generation_method |
str
|
How synth was created |
mutation_log |
List[str]
|
List of applied mutations |
user_rating |
Optional[int]
|
User rating 1-5 (None if not rated) |
user_notes |
Optional[str]
|
User feedback text |
created_at |
Optional[datetime]
|
Timestamp of creation |
Example
record = SynthRecord( ... id="synt_abc123", ... name="tb303_evolved_1", ... parent_ids=["tb303"], ... generation_method="mutation", ... mutation_log=["Saw → Pulse"], ... user_rating=5, ... user_notes="Amazing acid sound", ... created_at=datetime.now(), ... )
breed_synths(synth_a: SynthDefInfo, synth_b: SynthDefInfo, seed: Optional[int] = None) -> GeneratedSynth
¶
Crossover two SynthDefs to create a hybrid child.
Crossover strategy: 1. Take oscillator section from parent A 2. Take filter section from parent B 3. Randomly mix effects from both parents 4. Combine unique controls from both parents 5. Average numeric parameter defaults
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
synth_a
|
SynthDefInfo
|
First parent SynthDef |
required |
synth_b
|
SynthDefInfo
|
Second parent SynthDef |
required |
seed
|
Optional[int]
|
Random seed for deterministic testing |
None
|
Returns:
| Type | Description |
|---|---|
GeneratedSynth
|
GeneratedSynth combining features of both parents |
Raises:
| Type | Description |
|---|---|
SynthDefError
|
If crossover produces invalid code |
Example
tb303 = parse_synthdef(Path("synths/tb303.scd")) juno = parse_synthdef(Path("synths/juno_pad.scd")) child = breed_synths(tb303, juno, seed=42) child.name 'tb303_x_juno_pad' child.parent_ids ['tb303', 'juno_pad'] "Saw" in child.source_code # From tb303 True
generate_synth(description: str, base_synth: Optional[SynthDefInfo] = None, category: Literal['bass', 'lead', 'pad', 'drum', 'fx'] = 'bass') -> GeneratedSynth
¶
Generate a new SynthDef from description.
If base_synth provided, uses it as starting point and applies transformations based on description keywords. Otherwise generates from category template.
Description keywords: - "acid": Adds resonant filter with envelope - "warm": Uses saw wave with low-pass filter - "bright": Uses pulse wave with high cutoff - "filtered": Adds MoogFF or RLPF - "distorted": Adds distortion/saturation - "sweep": Adds LFO modulation to filter
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
description
|
str
|
Natural language description of desired sound |
required |
base_synth
|
Optional[SynthDefInfo]
|
Optional existing synth to modify |
None
|
category
|
Literal['bass', 'lead', 'pad', 'drum', 'fx']
|
Sound category (bass, lead, pad, drum, fx) |
'bass'
|
Returns:
| Type | Description |
|---|---|
GeneratedSynth
|
GeneratedSynth with source code and metadata |
Raises:
| Type | Description |
|---|---|
SynthDefError
|
If template cannot be loaded or customized |
Example
synth = generate_synth("warm acid bass with filter sweep", category="bass") synth.name 'warm_acid_bass_001' "MoogFF" in synth.source_code True "LFO" in synth.source_code # For sweep True
get_generation_stats() -> Dict[str, Any]
¶
Get statistics about evolutionary generations.
See LineageTracker.get_generation_stats for details.
get_synth_lineage(synth_id: str) -> Dict[str, Any]
¶
Get full ancestry and descendants for a synth.
See LineageTracker.get_lineage for details.
get_top_rated(limit: int = 10, min_rating: Optional[int] = None) -> List[Dict[str, Any]]
¶
Get highest-rated synths.
See LineageTracker.get_top_rated for details.
mutate_synth(synth: SynthDefInfo, amount: float = 0.3, seed: Optional[int] = None) -> GeneratedSynth
¶
Create a variation of a SynthDef through mutation.
Mutation operations (probability based on amount parameter): 1. Swap oscillator UGen (e.g., Saw → Pulse) - amount * 0.4 2. Swap filter UGen (e.g., LPF → MoogFF) - amount * 0.3 3. Add modulation (LFO to parameter) - amount * 0.3 4. Add distortion effect - amount * 0.2 5. Modify parameter defaults - amount * 0.5 6. Modify envelope shape - amount * 0.2
All mutations preserve synth structure (SynthDef, Out, envelope).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
synth
|
SynthDefInfo
|
Source SynthDef to mutate |
required |
amount
|
float
|
Mutation strength (0.0-1.0, higher = more drastic changes) |
0.3
|
seed
|
Optional[int]
|
Random seed for deterministic testing |
None
|
Returns:
| Type | Description |
|---|---|
GeneratedSynth
|
GeneratedSynth with mutations applied and logged |
Raises:
| Type | Description |
|---|---|
SynthDefError
|
If mutation produces invalid SuperCollider code |
Example
from audiomancer.analyzers.synthdef import parse_synthdef tb303 = parse_synthdef(Path("synths/tb303.scd")) variant = mutate_synth(tb303, amount=0.5, seed=42) variant.name 'tb303_m427' variant.mutation_log ['Saw → Pulse', 'Added LFO modulation to cutoff', 'cutoff: 1200.0 → 1440.0'] variant.parent_ids ['tb303']
rate_synth(synth_id: str, score: int, notes: Optional[str] = None) -> None
¶
Record user rating for a synth.
See LineageTracker.rate_synth for details.
record_synth(synth_id: str, name: str, parent_ids: List[str], generation_method: str, mutation_log: List[str]) -> None
¶
Record a new synth in the lineage database.
See LineageTracker.record_synth for details.