1#[macro_use]
2extern crate lazy_static;
3#[macro_use]
4extern crate log;
5
6use std::collections::VecDeque;
7use std::fs::{create_dir_all, read_to_string, File};
8use std::io;
9use std::io::{Read, Write};
10use std::ops::Range;
11use std::path::{Path, PathBuf};
12use std::sync::Arc;
13
14use crossbeam_channel::Receiver;
15
16use loopers_common::api::QuantizationMode::Free;
17use loopers_common::api::{
18 get_sample_rate, set_sample_rate, Command, FrameTime, LooperCommand, LooperMode, LooperTarget,
19 Part, PartSet, QuantizationMode, SavedSession,
20};
21use loopers_common::config::{Config, MidiMapping, FILE_HEADER};
22use loopers_common::gui_channel::{
23 EngineState, EngineStateSnapshot, GuiCommand, GuiSender, LogMessage,
24};
25use loopers_common::midi::MidiEvent;
26use loopers_common::music::*;
27use loopers_common::Host;
28
29use crate::error::SaveLoadError;
30use crate::looper::Looper;
31use crate::metronome::Metronome;
32use crate::sample::Sample;
33use crate::session::{SaveSessionData, SessionSaver};
34use crate::trigger::{Trigger, TriggerCondition};
35
36mod error;
37pub mod looper;
38pub mod metronome;
39pub mod sample;
40pub mod session;
41mod trigger;
42
43pub struct Engine {
44 config: Config,
45
46 state: EngineState,
47
48 time: i64,
49
50 metric_structure: MetricStructure,
51
52 command_input: Receiver<Command>,
53
54 gui_sender: GuiSender,
55
56 loopers: Vec<Looper>,
57 active: u32,
58
59 current_part: Part,
60
61 sync_mode: QuantizationMode,
62
63 metronome: Option<Metronome>,
64
65 triggers: VecDeque<Trigger>,
66
67 id_counter: u32,
68
69 session_saver: SessionSaver,
70
71 tmp_left: Vec<f64>,
72 tmp_right: Vec<f64>,
73 output_left: Vec<f64>,
74 output_right: Vec<f64>,
75
76 looper_peaks: [[f32; 2]; 64],
77}
78
79#[allow(dead_code)]
80const THRESHOLD: f32 = 0.05;
81
82#[allow(dead_code)]
83fn max_abs(b: &[f32]) -> f32 {
84 b.iter()
85 .map(|v| v.abs())
86 .fold(f32::NEG_INFINITY, |a, b| a.max(b))
87}
88
89pub fn last_session_path() -> io::Result<PathBuf> {
90 let mut config_path = dirs::config_dir().unwrap_or(PathBuf::new());
91 config_path.push("loopers");
92 create_dir_all(&config_path)?;
93 config_path.push(".last-session");
94 Ok(config_path)
95}
96
97pub fn read_config() -> Result<Config, String> {
98 let mut mapping_path = dirs::config_dir().unwrap_or(PathBuf::new());
99 mapping_path.push("loopers/midi_mappings.tsv");
100
101 let mut config = Config::new();
102
103 match File::open(&mapping_path) {
104 Ok(file) => match MidiMapping::from_file(&mapping_path.to_string_lossy(), &file) {
105 Ok(mms) => config.midi_mappings.extend(mms),
106 Err(e) => {
107 return Err(format!("Failed to load midi mappings: {:?}", e));
108 }
109 },
110 Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
111 if let Ok(ref mut file) = File::create(&mapping_path) {
113 writeln!(file, "{}", FILE_HEADER).unwrap();
114 }
115 }
116 Err(_) => {}
117 }
118
119 Ok(config)
120}
121
122impl Engine {
123 pub fn new<'a, H: Host<'a>>(
124 host: &mut H,
125 mut gui_sender: GuiSender,
126 command_input: Receiver<Command>,
127 beat_normal: Vec<f32>,
128 beat_emphasis: Vec<f32>,
129 restore: bool,
130 sample_rate: usize,
131 ) -> Engine {
132 let metric_structure = MetricStructure::new(4, 4, Tempo::from_bpm(120.0)).unwrap();
133
134 let config = match read_config() {
135 Ok(config) => config,
136 Err(err) => {
137 let mut error = LogMessage::error();
138 if let Err(e) = write!(error, "{}", err) {
139 error!("Failed to report config error: {}", e);
140 } else {
141 gui_sender.send_log(error);
142 }
143
144 Config::new()
145 }
146 };
147
148 let mut engine = Engine {
149 config,
150
151 state: EngineState::Stopped,
152 time: 0,
153
154 metric_structure,
155
156 gui_sender: gui_sender.clone(),
157 command_input,
158
159 loopers: vec![Looper::new(0, PartSet::new(), gui_sender.clone()).start()],
160 active: 0,
161 current_part: Part::A,
162
163 sync_mode: QuantizationMode::Measure,
164
165 id_counter: 1,
166
167 metronome: Some(Metronome::new(
168 metric_structure,
169 Sample::from_mono(&beat_normal),
170 Sample::from_mono(&beat_emphasis),
171 )),
172
173 triggers: VecDeque::with_capacity(128),
174
175 session_saver: SessionSaver::new(gui_sender),
176
177 tmp_left: vec![0f64; 2048],
178 tmp_right: vec![0f64; 2048],
179
180 output_left: vec![0f64; 2048],
181 output_right: vec![0f64; 2048],
182
183 looper_peaks: [[0.0; 2]; 64],
184 };
185
186 set_sample_rate(sample_rate);
187
188 engine.reset();
189
190 for l in &engine.loopers {
191 engine.session_saver.add_looper(l);
192 if let Err(e) = host.add_looper(l.id) {
193 error!("Failed to add host port for looper {}: {}", l.id, e);
194 }
195 }
196
197 if restore {
198 let mut restore_fn = || {
199 let config_path = last_session_path()?;
200 let restore_path = read_to_string(config_path)?;
201 info!("Restoring from {}", restore_path);
202 engine.load_session(host, Path::new(&restore_path))
203 };
204
205 if let Err(err) = restore_fn() {
206 warn!("Failed to restore existing session {:?}", err);
207 }
208 }
209
210 engine
211 }
212
213 fn add_trigger(triggers: &mut VecDeque<Trigger>, t: Trigger) {
214 while triggers.len() >= triggers.capacity() {
215 triggers.pop_front();
216 }
217
218 triggers.push_back(t);
219 }
220
221 fn reset(&mut self) {
222 if let Some(m) = &mut self.metronome {
223 m.reset();
224 }
225 self.triggers.clear();
226 self.set_time(FrameTime(-(self.measure_len().0 as i64)));
227 for l in &mut self.loopers {
228 l.handle_command(LooperCommand::Play);
229 }
230 }
231
232 fn looper_by_index_mut(&mut self, idx: u8) -> Option<&mut Looper> {
233 self.loopers
234 .iter_mut()
235 .filter(|l| !l.deleted)
236 .skip(idx as usize)
237 .next()
238 }
239
240 fn commands_from_midi<'a, H: Host<'a>>(&mut self, host: &mut H, events: &[MidiEvent]) {
241 for e in events {
242 debug!("midi {:?}", e);
243 for i in 0..self.config.midi_mappings.len() {
244 let mm = &self.config.midi_mappings[i];
245 if let Some(c) = mm.command_for_event(e) {
246 self.handle_command(host, &c, false);
247 }
248 }
249 }
250 }
251
252 fn trigger_from_command(
254 ms: MetricStructure,
255 sync_mode: QuantizationMode,
256 time: FrameTime,
257 lc: LooperCommand,
258 target: LooperTarget,
259 looper: &Looper,
260 ) -> Option<Trigger> {
261 let trigger_condition = match sync_mode {
262 Free => if time.0 < 0 { Some(TriggerCondition::Beat) } else { None },
263 QuantizationMode::Beat => Some(TriggerCondition::Beat),
264 QuantizationMode::Measure => Some(TriggerCondition::Measure),
265 }?;
266
267 use LooperCommand::*;
268 match (looper.length() == 0, looper.mode(), lc) {
269 (_, _, SetLevel(_)) => None,
271 (_, _, SetPan(_)) => None,
272
273 (_, _, Record)
274 | (_, LooperMode::Recording, _)
275 | (true, _, RecordOverdubPlay)
276 | (_, LooperMode::Overdubbing, _) => Some(Trigger::new(
277 trigger_condition,
278 Command::Looper(lc, target),
279 ms,
280 time,
281 )),
282 (_, _, RecordOverdubPlay) => Some(Trigger::new(
283 TriggerCondition::Immediate,
284 Command::Looper(lc, target),
285 ms,
286 time,
287 )),
288 _ => None,
289 }
290 }
291
292 fn handle_loop_command(&mut self, lc: LooperCommand, target: LooperTarget, triggered: bool) {
293 debug!("Handling loop command: {:?} for {:?}", lc, target);
294
295 let ms = self.metric_structure;
296 let sync_mode = self.sync_mode;
297 let time = FrameTime(self.time);
298 let triggers = &mut self.triggers;
299 let gui_sender = &mut self.gui_sender;
300
301 fn handle_or_trigger(
302 triggered: bool,
303 ms: MetricStructure,
304 sync_mode: QuantizationMode,
305 time: FrameTime,
306 lc: LooperCommand,
307 target: LooperTarget,
308 looper: &mut Looper,
309 triggers: &mut VecDeque<Trigger>,
310 gui_sender: &mut GuiSender,
311 ) {
312 if triggered {
313 looper.handle_command(lc);
314 } else if let Some(trigger) =
315 Engine::trigger_from_command(ms, sync_mode, time, lc, target, looper)
316 {
317 Engine::add_trigger(triggers, trigger.clone());
318
319 gui_sender.send_update(GuiCommand::AddLoopTrigger(
320 looper.id,
321 trigger.triggered_at(),
322 lc,
323 ));
324 } else {
325 looper.handle_command(lc);
326 }
327 }
328
329 let mut selected = None;
330 match target {
331 LooperTarget::Id(id) => {
332 if let Some(l) = self.loopers.iter_mut().find(|l| l.id == id) {
333 handle_or_trigger(
334 triggered, ms, sync_mode, time, lc, target, l, triggers, gui_sender,
335 );
336 } else {
337 warn!(
338 "Could not find looper with id {} while handling command {:?}",
339 id, lc
340 );
341 }
342 }
343 LooperTarget::Index(idx) => {
344 if let Some(l) = self
345 .loopers
346 .iter_mut()
347 .filter(|l| !l.deleted)
348 .skip(idx as usize)
349 .next()
350 {
351 selected = Some(l.id);
352 handle_or_trigger(
353 triggered, ms, sync_mode, time, lc, target, l, triggers, gui_sender,
354 );
355 } else {
356 warn!("No looper at index {} while handling command {:?}", idx, lc);
357 }
358 }
359 LooperTarget::All => {
360 for l in &mut self.loopers {
361 handle_or_trigger(
362 triggered, ms, sync_mode, time, lc, target, l, triggers, gui_sender,
363 );
364 }
365 }
366 LooperTarget::Selected => {
367 let active = self.active;
368 if let Some(l) = self.loopers.iter_mut().find(|l| l.id == active) {
369 handle_or_trigger(
370 triggered, ms, sync_mode, time, lc, target, l, triggers, gui_sender,
371 );
372 } else {
373 error!(
374 "selected looper {} not found while handling command {:?}",
375 self.active, lc
376 );
377 }
378 }
379 };
380
381 if let Some(id) = selected {
382 self.active = id;
383 }
384 }
385
386 fn load_session<'a, H: Host<'a>>(
387 &mut self,
388 host: &mut H,
389 path: &Path,
390 ) -> Result<(), SaveLoadError> {
391 let mut file = File::open(&path)?;
392 let mut contents = String::new();
393 file.read_to_string(&mut contents)?;
394
395 let dir = path.parent().unwrap();
396
397 let mut session: SavedSession = serde_json::from_str(&contents).map_err(|err| {
398 warn!("Found invalid SavedSession during load: {:?}", err);
399 SaveLoadError::OtherError("Failed to restore session; file is invalid".to_string())
401 })?;
402
403 if session.sample_rate != get_sample_rate() {
404 let mut error = LogMessage::error();
405 if let Err(_) = write!(
406 &mut error,
407 "Session was saved with sample rate {}, but system rate \
408 is set to {}, playback will be affected",
409 session.sample_rate,
410 get_sample_rate()
411 ) {
412 error!("Different sample rate");
413 };
414 self.gui_sender.send_log(error);
415 }
416
417 debug!("Restoring session: {:?}", session);
418
419 self.metric_structure = session.metric_structure.to_ms()
420 .map_err(|e| SaveLoadError::OtherError(e))?;
421 self.sync_mode = session.sync_mode;
422
423 if let Some(metronome) = &mut self.metronome {
424 metronome.set_volume((session.metronome_volume as f32 / 100.0).min(1.0).max(0.0));
425 }
426
427 for l in &self.loopers {
428 self.session_saver.remove_looper(l.id);
429 self.gui_sender.send_update(GuiCommand::RemoveLooper(l.id));
430 }
431 self.loopers.clear();
432
433 session.loopers.sort_by_key(|l| l.id);
434
435 for l in session.loopers {
436 debug!("Restoring looper {}", l.id);
437 let looper = Looper::from_serialized(&l, dir, self.gui_sender.clone())?.start();
438 self.session_saver.add_looper(&looper);
439 if let Err(e) = host.add_looper(looper.id) {
440 error!("Failed to create host port for looper {}: {}", looper.id, e);
441 }
442 self.loopers.push(looper);
443 }
444
445 self.id_counter = self.loopers.iter().map(|l| l.id).max().unwrap_or(0) + 1;
446
447 self.reset();
448
449 Ok(())
450 }
451
452 fn handle_command<'a, H: Host<'a>>(
453 &mut self,
454 host: &mut H,
455 command: &Command,
456 triggered: bool,
457 ) {
458 fn trigger_or_run<F>(
459 engine: &mut Engine,
460 command: &Command,
461 triggered: bool,
462 queued: bool,
463 f: F,
464 ) where
465 F: FnOnce(&mut Engine),
466 {
467 if engine.state == EngineState::Stopped || triggered {
468 f(engine);
469 return;
470 }
471
472 let trigger_condition = match (queued, engine.sync_mode) {
473 (true, _) => TriggerCondition::Immediate,
474 (false, QuantizationMode::Free) => TriggerCondition::Immediate,
475 (false, QuantizationMode::Beat) => TriggerCondition::Beat,
476 (false, QuantizationMode::Measure) => TriggerCondition::Measure,
477 };
478
479 let trigger = Trigger::new(
480 trigger_condition,
481 command.clone(),
482 engine.metric_structure,
483 FrameTime(engine.time),
484 );
485
486 if trigger.triggered_at() != FrameTime(0)
487 && trigger.triggered_at() < FrameTime(engine.time)
488 {
489 f(engine);
490 return;
491 }
492
493 Engine::add_trigger(&mut engine.triggers, trigger.clone());
494 engine.gui_sender.send_update(GuiCommand::AddGlobalTrigger(
495 trigger.triggered_at(),
496 trigger.command,
497 ));
498 }
499
500 use Command::*;
501 match command {
502 Looper(lc, target) => {
503 self.handle_loop_command(*lc, *target, triggered);
504 }
505 Start => {
506 self.state = EngineState::Active;
507 }
508 Pause => {
509 self.state = EngineState::Paused;
510 }
511 Stop => {
512 self.state = EngineState::Stopped;
513 self.reset();
514 }
515 StartStop => {
516 self.state = match self.state {
517 EngineState::Stopped | EngineState::Paused => EngineState::Active,
518 EngineState::Active => {
519 self.reset();
520 EngineState::Stopped
521 }
522 };
523 }
524 PlayPause => {
525 self.state = match self.state {
526 EngineState::Stopped | EngineState::Paused => EngineState::Active,
527 EngineState::Active => EngineState::Paused,
528 }
529 }
530 Reset => {
531 self.reset();
532 }
533 SetTime(time) => self.set_time(*time),
534 AddLooper => {
535 let looper = crate::Looper::new(
537 self.id_counter,
538 PartSet::with(self.current_part),
539 self.gui_sender.clone(),
540 )
541 .start();
542 self.session_saver.add_looper(&looper);
543 self.loopers.push(looper);
544 self.active = self.id_counter;
545 if let Err(e) = host.add_looper(self.id_counter) {
547 error!(
548 "failed to create host port for looper {}: {}",
549 self.id_counter, e
550 );
551 }
552 self.id_counter += 1;
553 }
554 SelectLooperById(id) => {
555 if self.loopers.iter().any(|l| l.id == *id) {
556 self.active = *id;
557 } else {
558 warn!("tried to select non-existent looper id {}", id);
559 }
560 }
561 SelectLooperByIndex(idx) => {
562 if let Some(l) = self.looper_by_index_mut(*idx) {
563 self.active = l.id;
564 } else {
565 warn!("tried to select non-existent looper index {}", idx);
566 }
567 }
568 SelectNextLooper | SelectPreviousLooper => {
569 trigger_or_run(self, command, triggered, true, |engine| {
570 if let Some((i, _)) = engine
571 .loopers
572 .iter()
573 .filter(|l| !l.deleted)
574 .filter(|l| l.parts[engine.current_part])
575 .enumerate()
576 .find(|(_, l)| l.id == engine.active)
577 {
578 let count = engine
579 .loopers
580 .iter()
581 .filter(|l| l.parts[engine.current_part])
582 .filter(|l| !l.deleted)
583 .count();
584
585 let next = if *command == SelectNextLooper {
586 (i + 1) % count
587 } else {
588 (i as isize - 1).rem_euclid(count as isize) as usize
589 };
590
591 if let Some(l) = engine
592 .loopers
593 .iter()
594 .filter(|l| l.parts[engine.current_part])
595 .filter(|l| !l.deleted)
596 .skip(next)
597 .next()
598 {
599 engine.active = l.id;
600 }
601 } else {
602 if let Some(l) = engine
603 .loopers
604 .iter()
605 .filter(|l| !l.deleted)
606 .filter(|l| l.parts[engine.current_part])
607 .next()
608 {
609 engine.active = l.id;
610 }
611 }
612 });
613 }
614 PreviousPart => {
615 trigger_or_run(self, command, triggered, false, |engine| {
616 let original = engine.current_part;
617 loop {
618 engine.current_part = match engine.current_part {
619 Part::A => Part::D,
620 Part::B => Part::A,
621 Part::C => Part::B,
622 Part::D => Part::C,
623 };
624 if engine
625 .loopers
626 .iter()
627 .any(|l| !l.deleted && l.parts[engine.current_part])
628 || engine.current_part == original
629 {
630 break;
631 }
632 }
633 engine.select_first_in_part();
634 });
635 }
636 NextPart => {
637 trigger_or_run(self, command, triggered, false, |engine| {
638 let original = engine.current_part;
639 loop {
640 engine.current_part = match engine.current_part {
641 Part::A => Part::B,
642 Part::B => Part::C,
643 Part::C => Part::D,
644 Part::D => Part::A,
645 };
646 if engine
647 .loopers
648 .iter()
649 .any(|l| !l.deleted && l.parts[engine.current_part])
650 || engine.current_part == original
651 {
652 break;
653 }
654 }
655 engine.select_first_in_part();
656 });
657 }
658 GoToPart(part) => {
659 trigger_or_run(self, command, triggered, false, |engine| {
660 engine.current_part = *part;
661 engine.select_first_in_part();
662 });
663 }
664 SetQuantizationMode(sync_mode) => {
665 self.sync_mode = *sync_mode;
666 }
667 SaveSession(path) => {
668 if let Err(e) = self.session_saver.save_session(SaveSessionData {
669 metric_structure: self.metric_structure,
670 metronome_volume: self
671 .metronome
672 .as_ref()
673 .map(|m| (m.get_volume() * 100.0) as u8)
674 .unwrap_or(100),
675 sync_mode: self.sync_mode,
676 path: Arc::clone(path),
677 sample_rate: get_sample_rate(),
678 }) {
679 error!("Failed to save session {:?}", e);
680 }
681 }
682 LoadSession(path) => {
683 if let Err(e) = self.load_session(host, path) {
684 error!("Failed to load session {:?}", e);
685 }
686 }
687 SetMetronomeLevel(l) => {
688 if *l <= 100 {
689 if let Some(metronome) = &mut self.metronome {
690 metronome.set_volume(*l as f32 / 100.0);
691 }
692 } else {
693 error!("Invalid metronome volume; must be between 0 and 100");
694 }
695 }
696 SetTempoBPM(bpm) => {
697 self.metric_structure.tempo = Tempo::from_bpm(*bpm);
698 if let Some(met) = &mut self.metronome {
699 met.set_metric_structure(self.metric_structure);
700 }
701 self.reset();
702 }
703 SetTimeSignature(upper, lower) => {
704 if let Some(ts) = TimeSignature::new(*upper, *lower) {
705 self.metric_structure.time_signature = ts;
706 if let Some(met) = &mut self.metronome {
707 met.set_metric_structure(self.metric_structure);
708 }
709 self.reset();
710 }
711 }
712 }
713 }
714
715 fn select_first_in_part(&mut self) {
717 if let Some(l) = self
718 .loopers
719 .iter()
720 .filter(|l| l.id == self.active && l.parts[self.current_part])
721 .next()
722 .or(self
723 .loopers
724 .iter()
725 .filter(|l| !l.deleted && l.parts[self.current_part])
726 .next())
727 {
728 self.active = l.id;
729 }
730 }
731
732 fn measure_len(&self) -> FrameTime {
734 let bps = self.metric_structure.tempo.bpm() as f32 / 60.0;
735 let mspb = 1000.0 / bps;
736 let mspm = mspb * self.metric_structure.time_signature.upper as f32;
737
738 FrameTime::from_ms(mspm as f64)
739 }
740
741 fn set_time(&mut self, time: FrameTime) {
742 self.time = time.0;
743 for l in &mut self.loopers {
744 l.set_time(time);
745 }
746 }
747
748 fn perform_looper_io<'a, H: Host<'a>>(
749 &mut self,
750 host: &mut H,
751 in_bufs: &[&[f32]],
752 time: FrameTime,
753 idx_range: Range<usize>,
754 solo: bool,
755 ) {
756 if time.0 >= 0 {
757 let mut looper_index = 0;
758 for looper in self.loopers.iter_mut() {
759 if !looper.deleted {
760 self.tmp_left.iter_mut().for_each(|i| *i = 0.0);
761 self.tmp_right.iter_mut().for_each(|i| *i = 0.0);
762
763 let mut o = [
764 &mut self.tmp_left[idx_range.clone()],
765 &mut self.tmp_right[idx_range.clone()],
766 ];
767
768 looper.process_output(time, &mut o, self.current_part, solo);
769
770 if let Some([l, r]) = host.output_for_looper(looper.id) {
772 l.iter_mut()
773 .zip(&self.tmp_left[idx_range.clone()])
774 .for_each(|(a, b)| *a = *b as f32);
775 r.iter_mut()
776 .zip(&self.tmp_right[idx_range.clone()])
777 .for_each(|(a, b)| *a = *b as f32);
778 }
779
780 self.output_left[idx_range.clone()]
782 .iter_mut()
783 .zip(&self.tmp_left[idx_range.clone()])
784 .for_each(|(a, b)| *a += *b);
785 self.output_right[idx_range.clone()]
786 .iter_mut()
787 .zip(&self.tmp_right[idx_range.clone()])
788 .for_each(|(a, b)| *a += *b);
789
790 let mut peaks = [0f32; 2];
792 for (i, vs) in [&self.tmp_left, &self.tmp_right].iter().enumerate() {
793 for v in *vs {
794 let v_abs = v.abs() as f32;
795 if v_abs > peaks[i] {
796 peaks[i] = v_abs;
797 }
798 }
799 }
800
801 if let Some(p) = self.looper_peaks.get_mut(looper_index) {
802 *p = peaks;
803 }
804 looper_index += 1;
805
806 looper.process_input(
807 time.0 as u64,
808 &[
809 &in_bufs[0][idx_range.clone()],
810 &in_bufs[1][idx_range.clone()],
811 ],
812 self.current_part,
813 );
814 }
815 }
816 } else {
817 error!("perform_looper_io called with negative time {}", time.0);
818 }
819 }
820
821 fn process_loopers<'a, H: Host<'a>>(&mut self, host: &mut H, in_bufs: &[&[f32]], frames: u64, solo: bool) {
822 let mut time = self.time;
823 let mut idx = 0usize;
824
825 if time < 0 {
826 time = (self.time + frames as i64).min(0);
827 if time < 0 {
828 return;
829 }
830 idx = (time - self.time) as usize;
831 }
832
833 let mut time = time as u64;
834
835 let next_time = (self.time + frames as i64) as u64;
836 while time < next_time {
837 if let Some(_) = self
838 .triggers
839 .iter()
840 .peekable()
841 .peek()
842 .filter(|t| t.triggered_at().0 < next_time as i64)
843 {
844 let trigger = self.triggers.pop_front().unwrap();
846
847 let trigger_at = trigger.triggered_at();
848 if trigger_at != FrameTime(0) && trigger_at.0 < time as i64 {
851 error!(
855 "missed trigger for time {} (cur time = {})",
856 trigger_at.0, time
857 );
858 continue;
859 }
860
861 let trigger_at = trigger_at.0 as u64;
863
864 if trigger_at > time {
866 let idx_range = idx..(trigger_at as i64 - self.time) as usize;
869 assert_eq!(
870 idx_range.end - idx_range.start,
871 (trigger_at - time) as usize
872 );
873
874 self.perform_looper_io(
875 host,
876 &in_bufs,
877 FrameTime(time as i64),
878 idx_range.clone(),
879 solo,
880 );
881 time = trigger_at;
882 idx = idx_range.end;
883 }
884
885 self.handle_command(host, &trigger.command, true);
886 } else {
887 self.perform_looper_io(
889 host,
890 &in_bufs,
891 FrameTime(time as i64),
892 idx..frames as usize,
893 solo,
894 );
895 time = next_time;
896 }
897 }
898 }
899
900 fn compute_peaks(in_bufs: &[&[f32]]) -> [u8; 2] {
901 let mut peaks = [0u8; 2];
902 for c in 0..2 {
903 let mut peak = 0f32;
904 for v in in_bufs[c] {
905 let v_abs = v.abs();
906 if v_abs > peak {
907 peak = v_abs;
908 }
909 }
910
911 peaks[c] = Self::iec_scale(peak);
912 }
913
914 peaks
915 }
916
917 fn iec_scale(amp: f32) -> u8 {
918 let db = 20.0 * amp.log10();
919
920 let d = if db < -70.0 {
921 0.0
922 } else if db < -60.0 {
923 db + 70.0 * 0.25
924 } else if db < -50.0 {
925 db + 60.0 * 0.5 + 5.0
926 } else if db < -40.0 {
927 db + 50.0 * 0.75 + 7.5
928 } else if db < -30.0 {
929 db + 40.0 * 1.5 + 15.0
930 } else if db < -20.0 {
931 db + 30.0 * 2.0 + 30.0
932 } else if db < 0.0 {
933 db + 20.0 * 2.5 + 50.0
934 } else {
935 100.0
936 };
937
938 d as u8
939 }
940
941 pub fn process<'a, H: Host<'a>>(
947 &mut self,
948 host: &mut H,
949 in_bufs: [&[f32]; 2],
950 out_l: &mut [f32],
951 out_r: &mut [f32],
952 mut met_bufs: [&mut [f32]; 2],
953 frames: u64,
954 midi_events: &[MidiEvent],
955 ) {
956 self.commands_from_midi(host, midi_events);
958
959 loop {
961 match self.command_input.try_recv() {
962 Ok(c) => {
963 self.handle_command(host, &c, false);
964 }
965 Err(_) => break,
966 }
967 }
968
969 for l in self.loopers.iter().filter(|l| l.deleted) {
971 self.session_saver.remove_looper(l.id);
972 }
973 self.loopers.retain(|l| !l.deleted);
974
975 while self.output_left.len() < frames as usize {
978 self.output_left.push(0.0);
979 }
980 while self.output_right.len() < frames as usize {
981 self.output_right.push(0.0);
982 }
983 while self.tmp_left.len() < frames as usize {
984 self.output_left.push(0.0);
985 }
986 while self.tmp_right.len() < frames as usize {
987 self.output_right.push(0.0);
988 }
989
990 for (i, (l, r)) in in_bufs[0].iter().zip(in_bufs[1]).enumerate() {
993 self.output_left[i] = *l as f64;
994 self.output_right[i] = *r as f64;
995 }
996
997 if (self.state != EngineState::Active && self.state != EngineState::Paused) && (!self.triggers.is_empty() ||
998 self.loopers.iter().any(|l| l.local_mode() == LooperMode::Recording ||
999 l.local_mode() == LooperMode::Overdubbing)) {
1000 self.state = EngineState::Active;
1001 }
1002
1003 let solo = self.loopers.iter()
1004 .any(|l| l.parts[self.current_part] && !l.deleted && l.mode() == LooperMode::Soloed);
1005
1006 if self.state == EngineState::Active {
1007 self.process_loopers(host, &in_bufs, frames, solo);
1009
1010 if let Some(metronome) = &mut self.metronome {
1012 metronome.advance(&mut met_bufs);
1013 }
1014
1015 self.time += frames as i64;
1016 }
1017
1018 for i in 0..frames as usize {
1019 out_l[i] = self.output_left[i] as f32;
1020 }
1021 for i in 0..frames as usize {
1022 out_r[i] = self.output_right[i] as f32;
1023 }
1024
1025 let mut peaks = [[0u8; 2]; 64];
1026 for (i, ps) in self.looper_peaks.iter().enumerate() {
1027 peaks[i][0] = Self::iec_scale(ps[0]);
1028 peaks[i][1] = Self::iec_scale(ps[1]);
1029 }
1030
1031 self.gui_sender
1033 .send_update(GuiCommand::StateSnapshot(EngineStateSnapshot {
1034 engine_state: self.state,
1035 time: FrameTime(self.time),
1036 metric_structure: self.metric_structure,
1037 active_looper: self.active,
1038 looper_count: self.loopers.len(),
1039 part: self.current_part,
1040 solo,
1041 sync_mode: self.sync_mode,
1042 input_levels: Self::compute_peaks(&in_bufs),
1043 looper_levels: peaks,
1044 metronome_volume: self
1045 .metronome
1046 .as_ref()
1047 .map(|m| m.get_volume())
1048 .unwrap_or(0.0),
1049 }));
1050 }
1051}