refrain_adapters/
schedule.rs1use refrain_core::{Op, Pattern};
9
10#[derive(Debug, Clone, PartialEq)]
12pub struct Hap {
13 pub start: f64,
15 pub end: f64,
17 pub pitch: Option<String>,
19 pub value: String,
21}
22
23impl Hap {
24 pub fn duration(&self) -> f64 {
25 self.end - self.start
26 }
27}
28
29fn dur_to_cycles(d: &str) -> f64 {
31 match d {
32 "w" | "whole" => 1.0,
33 "h" | "half" => 0.5,
34 "q" | "quarter" => 0.25,
35 "e" | "eighth" => 0.125,
36 "s" | "sixteenth" => 0.0625,
37 s => s.parse::<f64>().unwrap_or(0.25),
39 }
40}
41
42pub fn schedule(pattern: &Pattern, t0: f64) -> (Vec<Hap>, f64) {
45 match pattern {
46 Pattern::Op(op) => schedule_op(op, t0),
47 Pattern::Seq(items) => {
48 let mut t = t0;
49 let mut out = Vec::with_capacity(items.len());
50 for p in items {
51 let (sub, dur) = schedule(p, t);
52 out.extend(sub);
53 t += dur;
54 }
55 (out, t - t0)
56 }
57 }
58}
59
60fn schedule_op(op: &Op, t0: f64) -> (Vec<Hap>, f64) {
61 match op {
62 Op::Note { pitch, dur } => {
63 let d = dur_to_cycles(dur);
64 (
65 vec![Hap {
66 start: t0,
67 end: t0 + d,
68 pitch: Some(pitch.clone()),
69 value: format!("note:{}:{}", pitch, dur),
70 }],
71 d,
72 )
73 }
74 Op::Loop { count, body } => {
75 let mut t = t0;
76 let mut out = Vec::new();
77 for _ in 0..*count {
78 let (sub, dur) = schedule(body, t);
79 out.extend(sub);
80 t += dur;
81 }
82 (out, t - t0)
83 }
84 Op::Diff { x, t } => (
85 vec![Hap {
86 start: t0,
87 end: t0,
88 pitch: None,
89 value: format!("diff:{}:{}", x, t),
90 }],
91 0.0,
92 ),
93 Op::Quotient { rels } => (
94 vec![Hap {
95 start: t0,
96 end: t0,
97 pitch: None,
98 value: format!("quotient:{}", rels.join(",")),
99 }],
100 0.0,
101 ),
102 Op::Sym(s) => (
103 vec![Hap {
104 start: t0,
105 end: t0,
106 pitch: None,
107 value: format!("sym:{}", s),
108 }],
109 0.0,
110 ),
111 Op::Call { head, args } => {
112 let mut out = Vec::new();
113 let mut t = t0;
114 for a in args {
115 let (sub, dur) = schedule(a, t);
116 out.extend(sub);
117 t += dur;
118 }
119 out.push(Hap {
120 start: t0,
121 end: t,
122 pitch: None,
123 value: format!("call:{}", head),
124 });
125 (out, t - t0)
126 }
127 }
128}