conformal_component/parameters/utils/
constant_buffer_states.rs

1use std::collections::HashMap;
2
3use crate::{
4    events::NoteID,
5    synth::{
6        NumericGlobalExpression, NumericPerNoteExpression, SwitchGlobalExpression,
7        SynthParamBufferStates, SynthParamStates,
8    },
9};
10
11use super::super::{
12    BufferState, BufferStates, EnumBufferState, InternalValue, NumericBufferState,
13    PiecewiseLinearCurvePoint, States, SwitchBufferState, TimedValue,
14};
15use super::states_map::{StatesMap, SynthStatesMap};
16
17/// Simple implementation of [`BufferStates`] trait where every parameter is
18/// constant throughout the whole buffer.
19///
20/// This is in general useful for testing or other scenarios where you need
21/// to create a [`BufferStates`] object outside of a Conformal wrapper.
22#[derive(Clone, Debug, Default)]
23pub struct ConstantBufferStates<S> {
24    s: S,
25}
26
27impl<S: States> BufferStates for ConstantBufferStates<S> {
28    fn get_by_hash(
29        &self,
30        id_hash: super::super::IdHash,
31    ) -> std::option::Option<
32        BufferState<
33            impl Iterator<Item = PiecewiseLinearCurvePoint> + Clone,
34            impl Iterator<Item = TimedValue<u32>> + Clone,
35            impl Iterator<Item = TimedValue<bool>> + Clone,
36        >,
37    > {
38        match self.s.get_by_hash(id_hash) {
39            Some(InternalValue::Numeric(n)) => {
40                Some(BufferState::Numeric(NumericBufferState::<
41                    std::iter::Empty<PiecewiseLinearCurvePoint>,
42                >::Constant(n)))
43            }
44            Some(InternalValue::Enum(e)) => Some(BufferState::Enum(EnumBufferState::<
45                std::iter::Empty<TimedValue<u32>>,
46            >::Constant(e))),
47            Some(InternalValue::Switch(s)) => Some(BufferState::Switch(SwitchBufferState::<
48                std::iter::Empty<TimedValue<bool>>,
49            >::Constant(s))),
50            None => None,
51        }
52    }
53}
54
55impl<S: SynthParamStates> SynthParamBufferStates for ConstantBufferStates<S> {
56    fn get_numeric_global_expression(
57        &self,
58        expression: NumericGlobalExpression,
59    ) -> NumericBufferState<impl Iterator<Item = PiecewiseLinearCurvePoint> + Clone> {
60        NumericBufferState::<std::iter::Empty<PiecewiseLinearCurvePoint>>::Constant(
61            self.s.get_numeric_global_expression(expression),
62        )
63    }
64
65    fn get_switch_global_expression(
66        &self,
67        expression: SwitchGlobalExpression,
68    ) -> SwitchBufferState<impl Iterator<Item = TimedValue<bool>> + Clone> {
69        SwitchBufferState::<std::iter::Empty<TimedValue<bool>>>::Constant(
70            self.s.get_switch_global_expression(expression),
71        )
72    }
73
74    fn get_numeric_expression_for_note(
75        &self,
76        expression: NumericPerNoteExpression,
77        note_id: NoteID,
78    ) -> NumericBufferState<impl Iterator<Item = PiecewiseLinearCurvePoint> + Clone> {
79        NumericBufferState::<std::iter::Empty<PiecewiseLinearCurvePoint>>::Constant(
80            self.s.get_numeric_expression_for_note(expression, note_id),
81        )
82    }
83}
84
85impl<S: States> ConstantBufferStates<S> {
86    /// Create a new [`ConstantBufferStates`] object from a [`States`] object.
87    pub fn new(s: S) -> Self {
88        Self { s }
89    }
90}
91
92impl ConstantBufferStates<StatesMap> {
93    /// Create a new [`ConstantBufferStates`] object from a list of `Info`s and `override`s.
94    ///
95    /// This creates a `ConstantBufferStates` with all parameters set to default values
96    /// for the whole buffer.
97    ///
98    /// Note that if you want to pass this into a synth, you should use
99    /// [`Self::new_override_synth_defaults`] instead.
100    ///
101    /// `overrides` work exactly as in [`override_defaults`].
102    ///
103    /// # Examples
104    ///
105    /// ```
106    /// # use conformal_component::parameters::{StaticInfoRef, InternalValue, TypeSpecificInfoRef, ConstantBufferStates, BufferStates, NumericBufferState};
107    /// let infos = vec![
108    ///   StaticInfoRef {
109    ///     title: "Numeric",
110    ///     short_title: "Numeric",
111    ///     unique_id: "numeric",
112    ///     flags: Default::default(),
113    ///     type_specific: TypeSpecificInfoRef::Numeric {
114    ///       default: 0.0,
115    ///       valid_range: 0.0..=1.0,
116    ///       units: None,
117    ///     },
118    ///   },
119    /// ];
120    /// let overrides = vec![("numeric", InternalValue::Numeric(0.5))].into_iter().collect();
121    /// let buffer_states = ConstantBufferStates::new_override_defaults(infos, &overrides);
122    /// match buffer_states.get_numeric("numeric") {
123    ///   Some(NumericBufferState::Constant(0.5)) => (),
124    ///   _ => panic!("Expected constant value of 0.5"),
125    /// };
126    /// ```
127    pub fn new_override_defaults<'a, S: AsRef<str> + 'a>(
128        infos: impl IntoIterator<Item = super::super::InfoRef<'a, S>> + 'a,
129        overrides: &HashMap<&'_ str, InternalValue>,
130    ) -> Self {
131        Self::new(StatesMap::new_override_defaults(infos, overrides))
132    }
133
134    /// Create a new [`ConstantBufferStates`] object from a list of `Info`s.
135    ///
136    /// Each parameter in `Info`s will be set to its default value for the whole buffer.
137    ///
138    /// Note that if you want to pass this into a synth, you should use
139    /// [`Self::new_synth_defaults`] instead.
140    ///
141    /// # Examples
142    ///
143    /// ```
144    /// # use conformal_component::parameters::{StaticInfoRef, InternalValue, TypeSpecificInfoRef, ConstantBufferStates, BufferStates, NumericBufferState};
145    /// let infos = vec![
146    ///   StaticInfoRef {
147    ///     title: "Numeric",
148    ///     short_title: "Numeric",
149    ///     unique_id: "numeric",
150    ///     flags: Default::default(),
151    ///     type_specific: TypeSpecificInfoRef::Numeric {
152    ///       default: 0.0,
153    ///       valid_range: 0.0..=1.0,
154    ///       units: None,
155    ///     },
156    ///   },
157    /// ];
158    ///
159    /// let buffer_states = ConstantBufferStates::new_defaults(infos);
160    /// match buffer_states.get_numeric("numeric") {
161    ///   Some(NumericBufferState::Constant(0.0)) => (),
162    ///   _ => panic!("Expected constant value of 0.0"),
163    /// };
164    /// ```
165    pub fn new_defaults<'a, S: AsRef<str> + 'a>(
166        infos: impl IntoIterator<Item = super::super::InfoRef<'a, S>> + 'a,
167    ) -> Self {
168        Self::new_override_defaults(infos, &Default::default())
169    }
170}
171
172impl ConstantBufferStates<SynthStatesMap> {
173    /// Create a new [`ConstantBufferStates`] object to pass to a synth from a list of `Info`s and `override`s.
174    ///
175    /// This is similar to [`Self::new_override_defaults`], but it also includes expression controllers.
176    ///
177    ///
178    /// # Examples
179    ///
180    /// ```
181    /// # use conformal_component::parameters::{StaticInfoRef, InternalValue, TypeSpecificInfoRef, ConstantBufferStates, BufferStates, NumericBufferState, SynthStatesMap};
182    /// # use conformal_component::synth::{SynthParamBufferStates, NumericGlobalExpression};
183    ///
184    /// let infos = vec![
185    ///   StaticInfoRef {
186    ///     title: "Numeric",
187    ///     short_title: "Numeric",
188    ///     unique_id: "numeric",
189    ///     flags: Default::default(),
190    ///     type_specific: TypeSpecificInfoRef::Numeric {
191    ///       default: 0.0,
192    ///       valid_range: 0.0..=1.0,
193    ///       units: None,
194    ///     },
195    ///   },
196    /// ];
197    /// let overrides = vec![
198    ///   // You can override declared parameters
199    ///   ("numeric", InternalValue::Numeric(0.5)),
200    /// ].into_iter().collect();
201    ///
202    /// // and you can override control parameters
203    /// let expression_overrides = vec![
204    ///   (NumericGlobalExpression::ModWheel, 0.2),
205    /// ].into_iter().collect();
206    ///
207    /// let buffer_states = ConstantBufferStates::new_override_synth_defaults(infos, &overrides, &expression_overrides, &Default::default());
208    ///
209    /// // Overridden parameters get the values you passed in
210    /// match buffer_states.get_numeric("numeric") {
211    ///   Some(NumericBufferState::Constant(0.5)) => (),
212    ///   _ => panic!("Expected constant value of 0.5"),
213    /// };
214    /// match buffer_states.get_numeric_global_expression(NumericGlobalExpression::ModWheel) {
215    ///   NumericBufferState::Constant(0.2) => (),
216    ///   _ => panic!("Expected constant value of 0.2"),
217    /// };
218    ///
219    /// // Other parameters get their default values
220    /// match buffer_states.get_numeric_global_expression(NumericGlobalExpression::PitchBend) {
221    ///   NumericBufferState::Constant(0.0) => (),
222    ///   _ => panic!("Expected constant value of 0.0"),
223    /// };
224    /// ```
225    pub fn new_override_synth_defaults<'a, 'b: 'a>(
226        infos: impl IntoIterator<Item = super::super::InfoRef<'a, &'b str>> + 'a,
227        overrides: &HashMap<&'_ str, InternalValue>,
228        numeric_expression_overrides: &HashMap<NumericGlobalExpression, f32>,
229        switch_expression_overrides: &HashMap<SwitchGlobalExpression, bool>,
230    ) -> Self {
231        Self::new(SynthStatesMap::new_override_defaults(
232            infos,
233            overrides,
234            numeric_expression_overrides,
235            switch_expression_overrides,
236        ))
237    }
238
239    /// Create a new [`ConstantBufferStates`] object to pass to a synth from a list of `Info`s.
240    ///
241    /// Each parameter in `Info`s will be set to its default value for the whole buffer.
242    ///
243    /// This is similar to [`Self::new_defaults`], but it also includes expression controllers.
244    ///
245    /// # Examples
246    ///
247    /// ```
248    /// # use conformal_component::parameters::{StaticInfoRef, InternalValue, TypeSpecificInfoRef, ConstantBufferStates, BufferStates, NumericBufferState, SynthStatesMap};
249    /// # use conformal_component::synth::{SynthParamBufferStates, NumericGlobalExpression};
250    /// let infos = vec![
251    ///   StaticInfoRef {
252    ///     title: "Numeric",
253    ///     short_title: "Numeric",
254    ///     unique_id: "numeric",
255    ///     flags: Default::default(),
256    ///     type_specific: TypeSpecificInfoRef::Numeric {
257    ///       default: 0.0,
258    ///       valid_range: 0.0..=1.0,
259    ///       units: None,
260    ///     },
261    ///   },
262    /// ];
263    ///
264    /// let buffer_states = ConstantBufferStates::new_synth_defaults(infos);
265    /// match buffer_states.get_numeric("numeric") {
266    ///   Some(NumericBufferState::Constant(0.0)) => (),
267    ///   _ => panic!("Expected constant value of 0.0"),
268    /// };
269    /// match buffer_states.get_numeric_global_expression(NumericGlobalExpression::ModWheel) {
270    ///   NumericBufferState::Constant(0.0) => (),
271    ///   _ => panic!("Expected constant value of 0.0"),
272    /// };
273    /// ```
274    pub fn new_synth_defaults<'a, 'b: 'a>(
275        infos: impl IntoIterator<Item = super::super::InfoRef<'a, &'b str>> + 'a,
276    ) -> Self {
277        Self::new_override_synth_defaults(
278            infos,
279            &Default::default(),
280            &Default::default(),
281            &Default::default(),
282        )
283    }
284}