Skip to main content

boomie/
track.rs

1use std::collections::HashMap;
2use crate::error::SynthError;
3use crate::instrument::{Instrument, InstrumentSource, SampleData, Note, Chord, SequenceElement};
4use crate::waveform::WaveformType;
5use crate::effects::{ReverbParams, DelayParams, DistortionParams, FilterParams, FilterType};
6use crate::utils::parse_note;
7
8#[derive(Debug, Clone)]
9pub struct LoopPoint {
10    pub start: f32,
11    pub end: f32,
12}
13
14#[derive(Debug, Clone)]
15pub struct MelodyTrack {
16    pub name: String,
17    pub instrument: Instrument,
18    pub sequence: Vec<SequenceElement>,
19    pub tempo: f32,
20    pub length: f32,
21    pub loop_point: Option<LoopPoint>,
22    pub time_signature: (u32, u32), 
23    pub swing: f32, // Swing feel: 0.0 = straight, 0.5 = triplet, 1.0 = max
24}
25
26impl MelodyTrack {
27    pub fn from_mel(content: &str, sample_cache: &HashMap<String, SampleData>) -> Result<Self, SynthError> {
28        let mut track = MelodyTrack {
29            name: "melody".to_string(),
30            instrument: Instrument::default(),
31            sequence: Vec::new(),
32            tempo: 120.0,
33            length: 0.0,
34            loop_point: None,
35            time_signature: (4, 4),
36            swing: 0.0,
37        };
38
39        macro_rules! parse_field {
40            ($line:expr, $prefix:expr, $field:expr) => {
41                if let Some(v) = $line.strip_prefix($prefix) {
42                    $field = v.trim().parse()
43                        .map_err(|_| SynthError::ParseError(format!("Invalid {}", $prefix)))?;
44                    continue;
45                }
46            };
47        }
48
49        for line in content.lines() {
50            let line = line.trim();
51            if line.is_empty() || line.starts_with("//") { continue; } // Comments (//) & empty lines 
52
53            if let Some(v) = line.strip_prefix("name:") {
54                track.name = v.trim().to_string();
55            } else if let Some(v) = line.strip_prefix("loop:") {
56                let parts: Vec<&str> = v.split(',').map(|s| s.trim()).collect();
57                if parts.len() >= 2 {
58                    track.loop_point = Some(LoopPoint {
59                        start: parts[0].parse().unwrap_or(0.0),
60                        end: parts[1].parse().unwrap_or(track.length),
61                    });
62                }
63
64            } else if let Some(v) = line.strip_prefix("time_sig:") { 
65                let parts: Vec<&str> = v.split('/').map(|s| s.trim()).collect();
66                if parts.len() >= 2 {
67                    track.time_signature = (
68                        parts[0].parse().unwrap_or(4),
69                        parts[1].parse().unwrap_or(4),
70                    );
71                }
72
73            } else if let Some(v) = line.strip_prefix("sample:") {
74                track.instrument.source = InstrumentSource::Sample(
75                    sample_cache.get(v.trim())
76                        .ok_or_else(|| SynthError::InvalidInstrument(format!("Sample not found: {}", v.trim())))?
77                        .clone()
78                );
79                
80            } else if let Some(v) = line.strip_prefix("waveform:") {
81                track.instrument.source = InstrumentSource::Synthesized(match v.trim().to_lowercase().as_str() {
82                    "sine" => WaveformType::Sine,
83                    "square" => WaveformType::Square,
84                    "triangle" => WaveformType::Triangle,
85                    "sawtooth" => WaveformType::Sawtooth,
86                    "noise" => WaveformType::Noise,
87                    _ => return Err(SynthError::ParseError("Unknown Waveform".to_string())),
88                });
89
90            } else if let Some(v) = line.strip_prefix("note:") {
91                let parts: Vec<&str> = v.split(',').map(|s| s.trim()).collect();
92                if parts.len() >= 3 {
93                    let pitch = parse_note(parts[0])?;
94                    let duration: f32 = parts[1].parse()
95                        .map_err(|_| SynthError::ParseError("Invalid Duration".to_string()))?;
96                    let velocity: f32 = parts[2].split("//").next().unwrap_or("0").trim().parse()
97                        .map_err(|_| SynthError::ParseError("Invalid Velocity".to_string()))?;
98                    
99                    let mut note = Note { pitch, duration, velocity, pan: None, slide_to: None };
100                    
101                    // Prse optional per-note parameters
102                    for param in parts.iter().skip(3) {
103                        if let Some((key, val)) = param.split_once('=') {
104                            match key.trim() {
105                                "pan" => note.pan = val.trim().parse().ok(),
106                                "slide" => note.slide_to = Some(parse_note(val.trim())?),
107                                _ => {}
108                            }
109                        }
110                    }
111                    
112                    track.sequence.push(SequenceElement::Note(note));
113                    track.length += duration;
114                }
115
116            } else if let Some(v) = line.strip_prefix("chord:") { // Parse chords
117                let parts: Vec<&str> = v.split(',').map(|s| s.trim()).collect();
118                if parts.len() >= 3 {
119                    let notes_str = parts[0];
120                    let duration: f32 = parts[1].parse()
121                        .map_err(|_| SynthError::ParseError("Invalid Duration".to_string()))?;
122                    let velocity: f32 = parts[2].split("//").next().unwrap_or("0").trim().parse()
123                        .map_err(|_| SynthError::ParseError("Invalid Velocity".to_string()))?;
124                    
125                    let pitches: Result<Vec<f32>, _> = notes_str
126                        .split('+')
127                        .map(|n| parse_note(n.trim()))
128                        .collect();
129                    
130                    track.sequence.push(SequenceElement::Chord(Chord {
131                        pitches: pitches?,
132                        duration,
133                        velocity,
134                    }));
135                    track.length += duration;
136                }
137
138            } else if let Some(v) = line.strip_prefix("rest:") { 
139                let duration: f32 = v.trim().parse()
140                    .map_err(|_| SynthError::ParseError("Invalid rest duration".to_string()))?;
141                track.sequence.push(SequenceElement::Rest(duration));
142                track.length += duration;
143
144            } else if let Some(v) = line.strip_prefix("filter:") { 
145                let parts: Vec<&str> = v.split(',').map(|s| s.trim()).collect();
146                if parts.len() >= 3 {
147                    let filter_type = match parts[0].to_lowercase().as_str() {
148                        "lowpass" | "lp" => FilterType::LowPass,
149                        "highpass" | "hp" => FilterType::HighPass,
150                        "bandpass" | "bp" => FilterType::BandPass,
151                        _ => FilterType::LowPass,
152                    };
153                    track.instrument.effects.filter = Some(FilterParams {
154                        filter_type,
155                        cutoff: parts[1].parse().unwrap_or(1000.0),
156                        resonance: parts[2].parse().unwrap_or(0.7),
157                    });
158                }
159
160            } else if let Some(v) = line.strip_prefix("reverb:") {
161                let parts: Vec<&str> = v.split(',').map(|s| s.trim()).collect();
162                if parts.len() >= 4 {
163                    track.instrument.effects.reverb = Some(ReverbParams {
164                        room_size: parts[0].parse().unwrap_or(0.5),
165                        damping: parts[1].parse().unwrap_or(0.5),
166                        wet: parts[2].parse().unwrap_or(0.3),
167                        width: parts[3].parse().unwrap_or(1.0),
168                    });
169                }
170
171            } else if let Some(v) = line.strip_prefix("delay:") {
172                let parts: Vec<&str> = v.split(',').map(|s| s.trim()).collect();
173                if parts.len() >= 3 {
174                    track.instrument.effects.delay = Some(DelayParams {
175                        time: parts[0].parse().unwrap_or(0.25),
176                        feedback: parts[1].parse().unwrap_or(0.4),
177                        wet: parts[2].parse().unwrap_or(0.3),
178                    });
179                }
180
181            } else if let Some(v) = line.strip_prefix("distortion:") {
182                let parts: Vec<&str> = v.split(',').map(|s| s.trim()).collect();
183                if parts.len() >= 3 {
184                    track.instrument.effects.distortion = Some(DistortionParams {
185                        drive: parts[0].parse().unwrap_or(2.0),
186                        tone: parts[1].parse().unwrap_or(0.7),
187                        wet: parts[2].parse().unwrap_or(0.5),
188                    });
189                }
190
191            } else {
192                parse_field!(line, "tempo:", track.tempo);
193                parse_field!(line, "volume:", track.instrument.volume);
194                parse_field!(line, "attack:", track.instrument.attack);
195                parse_field!(line, "decay:", track.instrument.decay);
196                parse_field!(line, "sustain:", track.instrument.sustain);
197                parse_field!(line, "release:", track.instrument.release);
198                parse_field!(line, "pitch:", track.instrument.pitch);
199                parse_field!(line, "pan:", track.instrument.pan);
200                parse_field!(line, "detune:", track.instrument.detune);
201                parse_field!(line, "swing:", track.swing);
202            }
203        }
204
205        Ok(track)
206    }
207}