cycles/
ctrl.rs

1//! Control patterns and related items.
2
3use crate::{atom, Pattern, Rational};
4
5/// A pattern value type that allows for representing a set of labelled controls.
6pub type Controls = std::collections::BTreeMap<String, Value>;
7
8// These map directly to the OSC strings expected by superdirt.
9pub const SOUND: &str = "s";
10pub const NOTE: &str = "n";
11
12/// The set of possible control value types.
13#[derive(Clone, Debug, PartialEq)]
14pub enum Value {
15    String(String),
16    F64(f64),
17    Rational(Rational),
18}
19
20impl From<Rational> for Value {
21    fn from(r: Rational) -> Self {
22        Self::Rational(r)
23    }
24}
25
26impl From<f64> for Value {
27    fn from(f: f64) -> Self {
28        Self::F64(f)
29    }
30}
31
32impl From<String> for Value {
33    fn from(s: String) -> Self {
34        Self::String(s)
35    }
36}
37
38impl std::fmt::Display for Value {
39    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
40        match self {
41            Value::String(val) => val.fmt(f),
42            Value::Rational(val) => val.fmt(f),
43            Value::F64(val) => val.fmt(f),
44        }
45    }
46}
47
48/// Given a pattern of sound names, produce a control pattern of `"sound"` events.
49pub fn sound<P>(pattern: P) -> impl Pattern<Value = Controls>
50where
51    P: 'static + Pattern,
52    P::Value: Clone + Into<String>,
53{
54    let f = |s: P::Value| std::iter::once((SOUND.to_string(), Value::String(s.into()))).collect();
55    pattern.app(atom(f))
56}
57
58/// Given a pattern of note values, produce a control pattern of `"note"` events.
59pub fn note<P>(pattern: P) -> impl Pattern<Value = Controls>
60where
61    P: 'static + Pattern<Value = f64>,
62{
63    let f = |n| std::iter::once((NOTE.to_string(), Value::F64(n))).collect();
64    pattern.app(atom(f))
65}