1use std::error::Error;
2use std::sync::{Arc, Mutex};
3use std::collections::HashMap;
4use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
5use cpal::{StreamConfig, Stream};
6
7use crate::error::SynthError;
8use crate::instrument::{Instrument, InstrumentSource, SampleData, SequenceElement};
9use crate::track::{MelodyTrack, LoopPoint};
10use crate::arrangement::{Arrangement, TrackOverrides};
11use crate::effects::EffectsProcessor;
12
13#[derive(Debug, Clone, Copy, PartialEq)]
14pub enum PlaybackState {
15 Stopped,
16 Playing,
17 Paused,
18}
19
20pub struct DynamicParameters {
21 pub master_volume: f32,
22 pub master_pitch: f32,
23 pub track_volumes: HashMap<String, f32>,
24 pub track_enabled: HashMap<String, bool>,
25 pub crossfade_duration: f32,
26}
27
28impl Default for DynamicParameters {
29 fn default() -> Self {
30 DynamicParameters {
31 master_volume: 1.0,
32 master_pitch: 1.0,
33 track_volumes: HashMap::new(),
34 track_enabled: HashMap::new(),
35 crossfade_duration: 1.0,
36 }
37 }
38}
39
40struct PlaybackContext {
41 arrangement: Arrangement,
42 current_sample: usize,
43 state: PlaybackState,
44 loop_enabled: bool,
45 dynamic_params: DynamicParameters,
46 param_interpolators: HashMap<String, f32>,
47 crossfade_state: Option<CrossfadeState>,
48}
49
50struct CrossfadeState {
51 target_arrangement: Arrangement,
52 progress: f32,
53 duration_samples: usize,
54}
55
56pub struct SynthEngine {
57 mel_cache: HashMap<String, MelodyTrack>, sample_cache: HashMap<String, SampleData>,
59 stream_config: StreamConfig,
60 sample_rate: f32,
61 playback_context: Arc<Mutex<Option<PlaybackContext>>>,
62 stream: Option<Stream>,
63}
64
65impl SynthEngine {
66 pub fn new() -> Result<Self, SynthError> {
67 let host = cpal::default_host();
68 let device = host.default_output_device()
69 .ok_or_else(|| SynthError::AudioError("No output device found".to_string()));
70 let config = device?.default_output_config()
71 .map_err(|e| SynthError::AudioError(e.to_string()))?;
72 let stream_config = config.config();
73
74 Ok(SynthEngine {
75 mel_cache: HashMap::new(),
76 sample_cache: HashMap::new(),
77 stream_config: stream_config.clone(),
78 sample_rate: stream_config.sample_rate.0 as f32,
79 playback_context: Arc::new(Mutex::new(None)),
80 stream: None,
81 })
82 }
83
84 pub fn get_sample_cache(&self) -> &HashMap<String, SampleData> {
85 &self.sample_cache
86 }
87
88 pub fn load_sample(&mut self, name: &str, path: &str) -> Result<(), Box<dyn Error>> {
90 let data = std::fs::read(path)?;
91 let cursor = std::io::Cursor::new(data);
92 let mut reader = hound::WavReader::new(cursor)?;
93 let spec = reader.spec();
94
95 println!("Loading sample \'{}\': {} Hz, {} channels", name, spec.sample_rate, spec.channels);
96 println!("Output sample rate: {} Hz", self.sample_rate);
97
98 let samples: Result<Vec<f32>, _> = reader.samples::<i16>()
99 .map(|r| r.map(|s| s as f32 / 32768.0)) .collect();
101
102 let sample_data = SampleData {
103 samples: Arc::new(samples?),
104 sample_rate: spec.sample_rate,
105 };
106
107 self.sample_cache.insert(name.to_string(), sample_data);
108 Ok(())
109 }
110
111 pub fn load_melody(&mut self, name: &str, path: &str) -> Result<(), Box<dyn Error>> {
112 let content = std::fs::read_to_string(path)?;
113 let track = MelodyTrack::from_mel(&content, &self.sample_cache)?;
114 self.mel_cache.insert(name.to_string(), track);
115 Ok(())
116 }
117
118 pub fn load_arrangement(&self, path: &str) -> Result<Arrangement, SynthError> {
119 let content = std::fs::read_to_string(path)
120 .map_err(|e| SynthError::FileError(e.to_string()))?;
121 Arrangement::from_bmi(&content, &self.mel_cache)
122 }
123
124 pub fn play_arrangement(&mut self, arrangement: Arrangement) -> Result<(), SynthError> {
125 self.stop();
126
127 let mut context = PlaybackContext {
128 arrangement,
129 current_sample: 0,
130 state: PlaybackState::Playing,
131 loop_enabled: false,
132 dynamic_params: DynamicParameters::default(),
133 param_interpolators: HashMap::new(),
134 crossfade_state: None,
135 };
136
137 for (track, _, _) in &context.arrangement.tracks {
138 context.dynamic_params.track_enabled.insert(track.name.clone(), true);
139 context.dynamic_params.track_volumes.insert(track.name.clone(), 1.0);
140 }
141
142 *self.playback_context.lock().unwrap() = Some(context);
143 self.start_stream()?;
144
145 Ok(())
146 }
147
148 pub fn crossfade_to(&mut self, new_arrangement: Arrangement, duration: f32) -> Result<(), SynthError> {
149 {
150 let mut ctx_lock = self.playback_context.lock().unwrap();
151
152 if let Some(ctx) = ctx_lock.as_mut() {
153 ctx.crossfade_state = Some(CrossfadeState {
154 target_arrangement: new_arrangement,
155 progress: 0.0,
156 duration_samples: (duration * self.sample_rate) as usize,
157 });
158 return Ok(());
159 }
160 }
161
162 self.play_arrangement(new_arrangement)?;
163 Ok(())
164 }
165
166 pub fn set_loop_enabled(&self, enabled: bool) {
167 if let Some(ctx) = self.playback_context.lock().unwrap().as_mut() {
168 ctx.loop_enabled = enabled;
169 }
170 }
171
172 pub fn pause(&self) {
173 if let Some(ctx) = self.playback_context.lock().unwrap().as_mut() {
174 if ctx.state == PlaybackState::Playing {
175 ctx.state = PlaybackState::Paused;
176 }
177 }
178 }
179
180 pub fn resume(&self) {
181 if let Some(ctx) = self.playback_context.lock().unwrap().as_mut() {
182 if ctx.state == PlaybackState::Paused {
183 ctx.state = PlaybackState::Playing;
184 }
185 }
186 }
187
188 pub fn stop(&mut self) {
189 if let Some(stream) = self.stream.take() {
190 drop(stream);
191 }
192 *self.playback_context.lock().unwrap() = None;
193 }
194
195 pub fn set_master_volume(&self, volume: f32) {
196 if let Some(ctx) = self.playback_context.lock().unwrap().as_mut() {
197 ctx.dynamic_params.master_volume = volume.max(0.0).min(2.0);
198 }
199 }
200
201 pub fn set_master_pitch(&self, pitch: f32) {
202 if let Some(ctx) = self.playback_context.lock().unwrap().as_mut() {
203 ctx.dynamic_params.master_pitch = pitch.max(0.5).min(2.0);
204 }
205 }
206
207 pub fn set_track_enabled(&self, track_name: &str, enabled: bool) {
208 if let Some(ctx) = self.playback_context.lock().unwrap().as_mut() {
209 ctx.dynamic_params.track_enabled.insert(track_name.to_string(), enabled);
210 }
211 }
212
213 pub fn set_track_volume(&self, track_name: &str, volume: f32) {
214 if let Some(ctx) = self.playback_context.lock().unwrap().as_mut() {
215 ctx.dynamic_params.track_volumes.insert(track_name.to_string(), volume.max(0.0).min(2.0));
216 }
217 }
218
219 pub fn interpolate_track_volume(&self, track_name: &str, target: f32, duration: f32) {
220 if let Some(ctx) = self.playback_context.lock().unwrap().as_mut() {
221 let key = format!("vol_{}", track_name);
222 ctx.param_interpolators.insert(key, duration);
223 ctx.dynamic_params.track_volumes.insert(track_name.to_string(), target);
224 }
225 }
226
227 pub fn get_playback_position(&self) -> f32 {
228 if let Some(ctx) = self.playback_context.lock().unwrap().as_ref() {
229 ctx.current_sample as f32 / self.sample_rate
230 } else {
231 0.0
232 }
233 }
234
235 pub fn get_playback_state(&self) -> PlaybackState {
236 if let Some(ctx) = self.playback_context.lock().unwrap().as_ref() {
237 ctx.state
238 } else {
239 PlaybackState::Stopped
240 }
241 }
242
243 fn start_stream(&mut self) -> Result<(), SynthError> {
244 let host = cpal::default_host();
245 let device = host.default_output_device()
246 .ok_or_else(|| SynthError::AudioError("No output device".to_string()))?;
247
248 let config = self.stream_config.clone();
249 let sample_rate = self.sample_rate;
250 let ctx = Arc::clone(&self.playback_context);
251
252 let stream = device.build_output_stream(
253 &config,
254 move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
255 let mut context_lock = ctx.lock().unwrap();
256
257 if let Some(context) = context_lock.as_mut() {
258 if context.state != PlaybackState::Playing {
259 for sample in data.iter_mut() {
260 *sample = 0.0;
261 }
262 return;
263 }
264
265 for frame in data.chunks_mut(config.channels as usize) {
266 let mut output = 0.0;
267
268 output = Self::synthesize_single_sample(
270 &context.arrangement,
271 context.current_sample,
272 sample_rate,
273 &context.dynamic_params
274 );
275
276 if let Some(ref mut crossfade) = context.crossfade_state {
278 let t = (crossfade.progress as f32) / (crossfade.duration_samples as f32);
279
280 let target_sample = Self::synthesize_single_sample(
281 &crossfade.target_arrangement,
282 context.current_sample,
283 sample_rate,
284 &context.dynamic_params
285 );
286
287 output = output * (1.0 - t) + target_sample * t;
288 crossfade.progress += 1.0;
289
290 if crossfade.progress >= crossfade.duration_samples as f32 {
292 context.arrangement = crossfade.target_arrangement.clone();
293 context.crossfade_state = None;
294 }
295 }
296
297 context.current_sample += 1;
298
299 if context.loop_enabled {
301 if let Some(ref loop_point) = context.arrangement.loop_point {
302 let pos = context.current_sample as f32 / sample_rate;
303 if pos >= loop_point.end {
304 context.current_sample = (loop_point.start * sample_rate) as usize;
305 }
306 } else {
307 let total_samples = (context.arrangement.total_length * sample_rate) as usize;
308 if context.current_sample >= total_samples {
309 context.current_sample = 0;
310 }
311 }
312 } else {
313 let total_samples = (context.arrangement.total_length * sample_rate) as usize;
314 if context.current_sample >= total_samples {
315 context.state = PlaybackState::Stopped;
316 }
317 }
318
319 let current_time = context.current_sample as f32 / sample_rate;
321 let total_length = context.arrangement.total_length;
322 let mut fade_mult = 1.0;
323
324 if let Some(fade_in_dur) = context.arrangement.fade_in {
325 if current_time < fade_in_dur {
326 fade_mult *= current_time / fade_in_dur;
327 }
328 }
329
330 if let Some(fade_out_dur) = context.arrangement.fade_out {
331 let fade_out_start = total_length - fade_out_dur;
332 if current_time > fade_out_start {
333 fade_mult *= (total_length - current_time) / fade_out_dur;
334 }
335 }
336
337 let final_output = output * context.dynamic_params.master_volume * fade_mult;
338 for sample in frame.iter_mut() {
339 *sample = final_output;
340 }
341 }
342 } else {
343 for sample in data.iter_mut() {
344 *sample = 0.0;
345 }
346 }
347 },
348 |err| eprintln!("Stream error: {}", err),
349 None
350 ).map_err(|e| SynthError::AudioError(e.to_string()))?;
351
352 stream.play().map_err(|e| SynthError::AudioError(e.to_string()))?;
353 self.stream = Some(stream);
354
355 Ok(())
356 }
357
358 fn synthesize_single_sample(
359 arrangement: &Arrangement,
360 sample_idx: usize,
361 sample_rate: f32,
362 params: &DynamicParameters
363 ) -> f32 {
364 let mut output = 0.0;
365 let current_time = sample_idx as f32 / sample_rate;
366
367 for (track, start_time, overrides) in &arrangement.tracks {
368 let enabled = params.track_enabled.get(&track.name).copied().unwrap_or(true);
369 if !enabled {
370 continue;
371 }
372
373 let track_vol = params.track_volumes.get(&track.name).copied().unwrap_or(1.0);
374
375 if current_time < *start_time {
376 continue;
377 }
378
379 let track_time = current_time - start_time;
380
381 let mut cumulative_time = 0.0;
382 let beat_duration = 60.0 / track.tempo;
383
384 for element in &track.sequence {
386 match element {
387 SequenceElement::Note(note) => {
388 let note_duration = note.duration * beat_duration;
389 let next_time = cumulative_time + note_duration;
390
391 if track_time >= cumulative_time && track_time < next_time {
392 let time_in_note = track_time - cumulative_time;
393 let envelope = Self::calculate_envelope_static(time_in_note, note_duration, &track.instrument);
394
395 let mut pitch = note.pitch;
397 if let Some(slide_target) = note.slide_to {
398 let slide_progress = time_in_note / note_duration;
399 pitch = note.pitch * (1.0 - slide_progress) + slide_target * slide_progress;
400 }
401
402 let sample = match &track.instrument.source {
403 InstrumentSource::Synthesized(waveform) => {
404 let phase = (track_time * pitch * params.master_pitch) % 1.0;
405 waveform.generate_sample(phase)
406 }
407 InstrumentSource::Sample(sample_data) => {
408 Self::interpolate_sample(
409 sample_data,
410 time_in_note,
411 track.instrument.pitch * params.master_pitch
412 )
413 }
414 };
415
416 let volume = track.instrument.volume * overrides.volume.unwrap_or(1.0) * track_vol;
417 output += sample * envelope * note.velocity * volume;
418 break;
419 }
420
421 cumulative_time = next_time;
422 }
423 SequenceElement::Chord(chord) => { let chord_duration = chord.duration * beat_duration;
425 let next_time = cumulative_time + chord_duration;
426
427 if track_time >= cumulative_time && track_time < next_time {
428 let time_in_note = track_time - cumulative_time;
429 let envelope = Self::calculate_envelope_static(time_in_note, chord_duration, &track.instrument);
430
431 for pitch in &chord.pitches {
433 let sample = match &track.instrument.source {
434 InstrumentSource::Synthesized(waveform) => {
435 let phase = (track_time * pitch * params.master_pitch) % 1.0;
436 waveform.generate_sample(phase)
437 }
438 InstrumentSource::Sample(sample_data) => {
439 Self::interpolate_sample(
440 sample_data,
441 time_in_note,
442 track.instrument.pitch * params.master_pitch
443 )
444 }
445 };
446
447 let volume = track.instrument.volume * overrides.volume.unwrap_or(1.0) * track_vol;
448 output += sample * envelope * chord.velocity * volume / chord.pitches.len() as f32;
449 }
450 break;
451 }
452
453 cumulative_time = next_time;
454 }
455 SequenceElement::Rest(duration) => { let rest_duration = duration * beat_duration;
457 cumulative_time += rest_duration;
458 }
459 }
460 }
461 }
462
463 output
464 }
465
466 pub fn synthesize_arrangement(&self, arrangement: &Arrangement) -> Result<Vec<f32>, SynthError> {
467 self.synthesize_arrangement_private(arrangement, &DynamicParameters::default())
468 }
469
470 fn synthesize_arrangement_private(
471 &self,
472 arrangement: &Arrangement,
473 params: &DynamicParameters,
474 ) -> Result<Vec<f32>, SynthError> {
475 let total_samples = (arrangement.total_length * self.sample_rate) as usize;
476 let mut buffer = vec![0.0; total_samples];
477 let chunk_size = 1024;
478
479 for (track, start_time, overrides) in &arrangement.tracks {
480 let enabled = params.track_enabled.get(&track.name).copied().unwrap_or(true);
481 if !enabled {
482 continue;
483 }
484
485 let track_vol = params.track_volumes.get(&track.name).copied().unwrap_or(1.0);
486 let start_sample = (start_time * self.sample_rate) as usize;
487
488 let mut t = track.clone();
489
490 if let Some(v) = overrides.volume { t.instrument.volume = v; }
492 if let Some(p) = overrides.pitch { t.instrument.pitch = p * params.master_pitch; }
493 if let Some(tm) = overrides.tempo { t.tempo = tm; }
494 if let Some(r) = &overrides.reverb { t.instrument.effects.reverb = Some(r.clone()); }
495 if let Some(d) = &overrides.delay { t.instrument.effects.delay = Some(d.clone()); }
496 if let Some(x) = &overrides.distortion { t.instrument.effects.distortion = Some(x.clone()); }
497 if let Some(f) = &overrides.filter { t.instrument.effects.filter = Some(f.clone()); }
498
499 t.instrument.volume *= track_vol;
500
501 let track_total_samples = (t.length * self.sample_rate) as usize;
502 let mut fx = if t.instrument.effects.has_any() {
503 Some(EffectsProcessor::new(self.sample_rate))
504 } else {
505 None
506 };
507
508 let mut sample_offset = 0;
509 while sample_offset < track_total_samples {
510 let current_chunk_size = (chunk_size).min(track_total_samples - sample_offset);
511 let mut chunk_buf = vec![0.0; current_chunk_size];
512
513 self.synthesize_track_into(&mut chunk_buf, &t, sample_offset);
514
515 if let Some(fx_processor) = &mut fx {
516 for s in chunk_buf.iter_mut() {
517 *s = fx_processor.process(*s, &t.instrument.effects);
518 }
519 }
520
521 for (i, &s) in chunk_buf.iter().enumerate() {
523 if let Some(dst) = buffer.get_mut(start_sample + sample_offset + i) {
524 *dst += s * params.master_volume;
525 }
526 }
527
528 sample_offset += current_chunk_size;
529 }
530 }
531
532 if let Some(fade_in_dur) = arrangement.fade_in {
534 let fade_in_samples = (fade_in_dur * self.sample_rate) as usize;
535 for i in 0..fade_in_samples.min(buffer.len()) {
536 let fade_mult = i as f32 / fade_in_samples as f32;
537 buffer[i] *= fade_mult;
538 }
539 }
540
541 if let Some(fade_out_dur) = arrangement.fade_out {
543 let fade_out_samples = (fade_out_dur * self.sample_rate) as usize;
544 let fade_start = buffer.len().saturating_sub(fade_out_samples);
545 for i in fade_start..buffer.len() {
546 let fade_mult = (buffer.len() - i) as f32 / fade_out_samples as f32;
547 buffer[i] *= fade_mult;
548 }
549 }
550
551 if let Some(max) = buffer.iter().map(|v| v.abs()).max_by(|a,b| a.partial_cmp(b).unwrap()) {
553 if max > 1.0 { buffer.iter_mut().for_each(|s| *s /= max); }
554 }
555
556 Ok(buffer)
557 }
558
559
560 fn synthesize_track_into(&self, buffer: &mut [f32], track: &MelodyTrack, start_sample: usize) {
561 let mut current_sample = 0usize;
562 let beat_duration = 60.0 / track.tempo;
563
564 for element in &track.sequence {
566 match element {
567 SequenceElement::Note(note) => {
568 let note_duration_seconds = note.duration * beat_duration;
569
570 match &track.instrument.source {
571 InstrumentSource::Synthesized(_) => {
572 let note_samples = (note_duration_seconds * self.sample_rate) as usize;
573 let mut phase = 0.0f32;
574
575 for i in 0..note_samples {
576 let sample_idx = start_sample + current_sample + i;
577 if sample_idx >= buffer.len() {
578 break;
579 }
580
581 let time_in_note = i as f32 / self.sample_rate;
582 let envelope = self.calculate_envelope(time_in_note, note_duration_seconds, &track.instrument);
583
584 let mut pitch = note.pitch;
585 if let Some(slide_target) = note.slide_to {
586 let slide_progress = time_in_note / note_duration_seconds;
587 pitch = note.pitch * (1.0 - slide_progress) + slide_target * slide_progress;
588 }
589
590 if let InstrumentSource::Synthesized(waveform) = &track.instrument.source {
591 let output = waveform.generate_sample(phase);
592 phase += pitch / self.sample_rate;
593 if phase >= 1.0 {
594 phase -= 1.0;
595 }
596
597 buffer[sample_idx] += output * envelope * note.velocity * track.instrument.volume;
598 }
599 }
600
601 current_sample += note_samples;
602 }
603
604 InstrumentSource::Sample(sample_data) => { let pitch_adjusted_rate = track.instrument.pitch;
606 let sample_len = sample_data.samples.len();
607
608 let output_len = (sample_len as f32 / pitch_adjusted_rate) as usize;
609 let actual_duration = output_len as f32 / self.sample_rate;
610
611 for i in 0..output_len {
612 let sample_idx = start_sample + current_sample + i;
613 if sample_idx >= buffer.len() {
614 break;
615 }
616
617 let time_in_note = i as f32 / self.sample_rate;
618 let envelope = self.calculate_envelope(time_in_note, actual_duration, &track.instrument);
619
620 let sample_value = Self::interpolate_sample(
621 sample_data,
622 time_in_note,
623 pitch_adjusted_rate
624 );
625
626 buffer[sample_idx] += sample_value * envelope * note.velocity * track.instrument.volume;
627 }
628
629 current_sample += output_len; }
632 }
633 }
634 SequenceElement::Chord(chord) => { let chord_duration_seconds = chord.duration * beat_duration;
636 let chord_samples = (chord_duration_seconds * self.sample_rate) as usize;
637
638 for pitch in &chord.pitches {
640 let mut phase = 0.0f32;
641
642 for i in 0..chord_samples {
643 let sample_idx = start_sample + current_sample + i;
644 if sample_idx >= buffer.len() {
645 break;
646 }
647
648 let time_in_note = i as f32 / self.sample_rate;
649 let envelope = self.calculate_envelope(time_in_note, chord_duration_seconds, &track.instrument);
650
651 if let InstrumentSource::Synthesized(waveform) = &track.instrument.source {
652 let output = waveform.generate_sample(phase);
653 phase += pitch / self.sample_rate;
654 if phase >= 1.0 {
655 phase -= 1.0;
656 }
657
658 buffer[sample_idx] += output * envelope * chord.velocity * track.instrument.volume / chord.pitches.len() as f32;
659 }
660 }
661 }
662
663 current_sample += chord_samples;
664 }
665 SequenceElement::Rest(duration) => { let rest_duration_seconds = duration * beat_duration;
667 let rest_samples = (rest_duration_seconds * self.sample_rate) as usize;
668 current_sample += rest_samples;
669 }
670 }
671 }
672 }
673
674 #[inline]
675 fn interpolate_sample(sample_data: &SampleData, time_in_note: f32, pitch_rate: f32) -> f32 {
676 let src_pos = time_in_note * sample_data.sample_rate as f32 * pitch_rate;
677 let src_idx = src_pos as usize;
678
679 if src_idx >= sample_data.samples.len() {
680 return 0.0;
681 }
682
683 if src_idx < sample_data.samples.len() - 1 {
685 let frac = src_pos - src_idx as f32;
686 let s1 = sample_data.samples[src_idx];
687 let s2 = sample_data.samples[src_idx + 1];
688 s1 * (1.0 - frac) + s2 * frac
689 } else {
690 sample_data.samples[src_idx]
691 }
692 }
693
694 fn calculate_envelope(&self, time: f32, duration: f32, instr: &Instrument) -> f32 {
696 Self::calculate_envelope_static(time, duration, instr)
697 }
698
699 fn calculate_envelope_static(time: f32, duration: f32, instr: &Instrument) -> f32 {
700 let attack_end = instr.attack;
701 let decay_end = attack_end + instr.decay;
702 let release_start = duration - instr.release;
703
704 if time < attack_end {
706 time / attack_end
707 } else if time < decay_end {
708 let decay_progress = (time - attack_end) / instr.decay;
709 1.0 - decay_progress * (1.0 - instr.sustain)
710 } else if time < release_start {
711 instr.sustain
712 } else {
713 let release_progress = (time - release_start) / instr.release;
714 instr.sustain * (1.0 - release_progress)
715 }
716 }
717}