rytm_rs/object/
settings.rs

1// All casts in this file are intended or safe within the context of this library.
2//
3// One can change `allow` to `warn` to review them if necessary.
4#![allow(
5    clippy::cast_lossless,
6    clippy::cast_possible_truncation,
7    clippy::cast_sign_loss
8)]
9// TODO: Re-check if bpm related casts are accurate.
10
11pub mod types;
12pub(crate) mod unknown;
13
14use self::types::{
15    FxParameterMenuItem, ParameterMenuItem, PatternMode, SampleRecorderRecordingLength,
16    SampleRecorderSource, SequencerMode,
17};
18use crate::{
19    error::{ParameterError, RytmError, SysexConversionError},
20    impl_sysex_compatible,
21    sysex::{SysexCompatible, SysexMeta, SysexType, SETTINGS_SYSEX_SIZE},
22    util::{assemble_u32_from_u8_array_be, break_u32_into_u8_array_be},
23    AnySysexType,
24};
25use derivative::Derivative;
26use rytm_rs_macro::parameter_range;
27use rytm_sys::{ar_settings_raw_to_syx, ar_settings_t, ar_sysex_meta_t};
28use serde::{Deserialize, Serialize};
29use unknown::SettingsUnknown;
30
31impl_sysex_compatible!(
32    Settings,
33    ar_settings_t,
34    ar_settings_raw_to_syx,
35    SysexType::Settings,
36    SETTINGS_SYSEX_SIZE
37);
38
39/// Represents settings in the analog rytm.
40///
41/// It does not map identically to the relevant structure in the firmware.
42///
43/// Settings may not be a familiar structure for all, to understand what kind of settings are available, please check the methods of this struct.
44#[derive(Derivative, Clone, Copy, Serialize, Deserialize)]
45#[derivative(Debug)]
46pub struct Settings {
47    #[derivative(Debug = "ignore")]
48    sysex_meta: SysexMeta,
49    /// Version of the settings structure.
50    version: u32,
51
52    bpm_project: f32,
53
54    selected_track: u8,
55    selected_parameter_menu_item: ParameterMenuItem,
56    selected_fx_menu_item: FxParameterMenuItem,
57    selected_page: u8,
58    selected_mode: SequencerMode,
59    selected_pattern_mode: PatternMode,
60
61    mute_flags: u16,
62
63    fixed_velocity_enable: bool,
64    fixed_velocity_amount: u8,
65
66    sample_recorder_src: SampleRecorderSource,
67    sample_recorder_thr: u8,
68    sample_recorder_monitor_enable: bool,
69    sample_recorder_rlen: SampleRecorderRecordingLength,
70
71    // #[derivative(Debug = "ignore")]
72    __unknown: SettingsUnknown,
73}
74
75impl From<&Settings> for ar_settings_t {
76    fn from(settings: &Settings) -> Self {
77        let bpm = (settings.bpm_project * 120.0) as u16;
78        let bpm_msb = (bpm >> 8) as u8;
79        let bpm_lsb = bpm as u8;
80
81        let track_mute_msb = (settings.mute_flags >> 8) as u8;
82        let track_mute_lsb = settings.mute_flags as u8;
83
84        let mut raw_settings = Self {
85            version: break_u32_into_u8_array_be(settings.version),
86            bpm_msb,
87            bpm_lsb,
88            selected_track: settings.selected_track,
89            _selected_track_duplicate: settings.selected_track,
90            selected_trig_or_parameter_menu: settings.selected_parameter_menu_item.into(),
91            selected_fx_menu: settings.selected_fx_menu_item.into(),
92            selected_page: settings.selected_page,
93
94            track_mute_msb,
95            track_mute_lsb,
96
97            selected_mode: settings.selected_mode.into(),
98            selected_pattern_transition_mode: settings.selected_pattern_mode.into(),
99
100            fixed_velocity_enable: settings.fixed_velocity_enable.into(),
101            fixed_velocity_amount: settings.fixed_velocity_amount,
102
103            sample_recorder_src: settings.sample_recorder_src.into(),
104            sample_recorder_thr: settings.sample_recorder_thr,
105            sample_recorder_monitor: settings.sample_recorder_monitor_enable.into(),
106
107            sample_recorder_rlen: settings.sample_recorder_rlen.into(),
108
109            ..Default::default()
110        };
111
112        settings.__unknown.apply_to_raw_settings(&mut raw_settings);
113
114        raw_settings
115    }
116}
117
118impl Default for Settings {
119    fn default() -> Self {
120        Self::try_default_with_device_id(0).unwrap()
121    }
122}
123
124impl Settings {
125    /// Makes a new settings complying to project defaults.
126    ///
127    /// Range `0..=127`
128    #[parameter_range(range = "device_id:0..=127")]
129    pub fn try_default_with_device_id(device_id: u8) -> Result<Self, RytmError> {
130        Ok(Self {
131            sysex_meta: SysexMeta::default_for_settings(Some(device_id)),
132            version: 3,
133            bpm_project: 120.0,
134            selected_track: 0,
135
136            selected_parameter_menu_item: ParameterMenuItem::default(),
137            selected_fx_menu_item: FxParameterMenuItem::default(),
138            selected_page: 0,
139
140            mute_flags: 0,
141
142            selected_mode: SequencerMode::default(),
143            selected_pattern_mode: PatternMode::default(),
144
145            fixed_velocity_enable: false,
146            fixed_velocity_amount: 100,
147            sample_recorder_src: SampleRecorderSource::default(),
148            sample_recorder_thr: 0,
149            sample_recorder_monitor_enable: false,
150
151            sample_recorder_rlen: SampleRecorderRecordingLength::default(),
152
153            __unknown: SettingsUnknown::default(),
154        })
155    }
156
157    pub(crate) fn as_raw_parts(&self) -> (SysexMeta, ar_settings_t) {
158        (self.sysex_meta, self.into())
159    }
160
161    pub(crate) fn try_from_raw(
162        sysex_meta: SysexMeta,
163        raw_settings: &ar_settings_t,
164    ) -> Result<Self, RytmError> {
165        let bpm_project = (raw_settings.bpm_msb as u16) << 8 | raw_settings.bpm_lsb as u16;
166        let bpm_project = bpm_project as f32 / 120.0;
167
168        let mute_flags =
169            (raw_settings.track_mute_msb as u16) << 8 | raw_settings.track_mute_lsb as u16;
170
171        Ok(Self {
172            sysex_meta,
173            version: assemble_u32_from_u8_array_be(&raw_settings.version),
174            bpm_project,
175            selected_track: raw_settings.selected_track,
176
177            selected_parameter_menu_item: raw_settings
178                .selected_trig_or_parameter_menu
179                .try_into()?,
180            selected_fx_menu_item: raw_settings.selected_fx_menu.try_into()?,
181            selected_page: raw_settings.selected_page,
182
183            mute_flags,
184
185            selected_mode: raw_settings.selected_mode.try_into()?,
186            selected_pattern_mode: raw_settings.selected_pattern_transition_mode.try_into()?,
187
188            fixed_velocity_enable: raw_settings.fixed_velocity_enable != 0,
189            fixed_velocity_amount: raw_settings.fixed_velocity_amount,
190
191            sample_recorder_src: raw_settings.sample_recorder_src.try_into()?,
192            sample_recorder_thr: raw_settings.sample_recorder_thr,
193            sample_recorder_monitor_enable: raw_settings.sample_recorder_monitor != 0,
194            sample_recorder_rlen: raw_settings.sample_recorder_rlen.try_into()?,
195
196            __unknown: raw_settings.into(),
197        })
198    }
199
200    /// Sets the BPM for the entire project.
201    ///
202    /// Range `30.0..=300.0`
203    ///
204    /// This is only effective when project level bpm is enabled.
205    #[parameter_range(range = "bpm:30.0..=300.0")]
206    pub fn set_bpm(&mut self, bpm: f32) -> Result<(), RytmError> {
207        self.bpm_project = bpm;
208        Ok(())
209    }
210
211    /// Sets the selected track.
212    ///
213    /// Range `0..=11`
214    #[parameter_range(range = "track_index:0..=11")]
215    pub fn set_selected_track(&mut self, track_index: usize) -> Result<(), RytmError> {
216        self.selected_track = track_index as u8;
217        Ok(())
218    }
219
220    /// Sets the selected parameter menu item.
221    ///
222    /// The six sequential square buttons on the right side of the Analog Rytm MKII.
223    pub fn set_selected_parameter_menu_item(&mut self, parameter_menu_item: ParameterMenuItem) {
224        self.selected_parameter_menu_item = parameter_menu_item;
225    }
226
227    /// Sets the selected fx menu item.
228    ///
229    /// The six sequential square buttons on the right side of the Analog Rytm MKII.
230    ///
231    /// The fx menu is only available when the FX button is pressed.
232    pub fn set_selected_fx_menu_item(&mut self, fx_menu_item: FxParameterMenuItem) {
233        self.selected_fx_menu_item = fx_menu_item;
234    }
235
236    /// Sets the selected page.
237    ///
238    /// The `[PAGE]` button on the Analog Rytm MKII.
239    ///
240    /// Range `0..=3`
241    #[parameter_range(range = "page_index:0..=3")]
242    pub fn set_selected_page(&mut self, page_index: usize) -> Result<(), RytmError> {
243        self.selected_page = page_index as u8;
244        Ok(())
245    }
246
247    /// Sets the selected sequencer mode.
248    pub fn set_selected_mode(&mut self, sequencer_mode: SequencerMode) {
249        self.selected_mode = sequencer_mode;
250    }
251
252    /// Sets the selected pattern mode.  
253    pub fn set_selected_pattern_mode(&mut self, pattern_mode: PatternMode) {
254        self.selected_pattern_mode = pattern_mode;
255    }
256
257    /// Sets the mute flags for sounds.
258    ///
259    /// Range `0..=0b1111_1111_1111`
260    #[parameter_range(range = "mute_flags:0..=4095")]
261    pub fn set_mute_flags(&mut self, mute_flags: u16) -> Result<(), RytmError> {
262        self.mute_flags = mute_flags;
263        Ok(())
264    }
265
266    /// Mutes a sound by sound index.
267    ///
268    /// Range `0..=11`
269    #[parameter_range(range = "sound_index:0..=11")]
270    pub fn mute_sound(&mut self, sound_index: usize) -> Result<(), RytmError> {
271        self.mute_flags |= 1 << sound_index;
272        Ok(())
273    }
274
275    /// Unmute a sound by sound index.
276    ///
277    /// Range `0..=11`
278    #[parameter_range(range = "sound_index:0..=11")]
279    pub fn unmute_sound(&mut self, sound_index: usize) -> Result<(), RytmError> {
280        self.mute_flags &= !(1 << sound_index);
281        Ok(())
282    }
283
284    /// Toggles the mute state of a sound by sound index.
285    ///
286    /// Range `0..=11`
287    #[parameter_range(range = "sound_index:0..=11")]
288    pub fn toggle_mute_sound(&mut self, sound_index: usize) -> Result<(), RytmError> {
289        self.mute_flags ^= 1 << sound_index;
290        Ok(())
291    }
292
293    /// Mutes a range of sounds.
294    ///
295    /// Maximum range `0..=11`
296    ///
297    /// # Errors
298    ///
299    /// Returns an error if the range is out of bounds.
300    pub fn mute_range_of_sounds(&mut self, range: std::ops::Range<usize>) -> Result<(), RytmError> {
301        if range.end > 11 {
302            return Err(RytmError::Parameter(ParameterError::Range {
303                value: format!("{range:?}",),
304                parameter_name: "range".to_string(),
305            }));
306        }
307
308        for sound_index in range {
309            self.mute_sound(sound_index)?;
310        }
311
312        Ok(())
313    }
314
315    /// Unmute a range of sounds.
316    ///
317    /// Maximum range `0..=11`
318    ///
319    /// # Errors
320    ///
321    /// Returns an error if the range is out of bounds.
322    pub fn unmute_range_of_sounds(
323        &mut self,
324        range: std::ops::Range<usize>,
325    ) -> Result<(), RytmError> {
326        for sound_index in range {
327            self.unmute_sound(sound_index)?;
328        }
329        Ok(())
330    }
331
332    /// Toggles the mute state of a range of sounds.
333    ///
334    /// Maximum range `0..=11`
335    ///
336    /// # Errors
337    ///
338    /// Returns an error if the range is out of bounds.
339    pub fn toggle_mute_range_of_sounds(
340        &mut self,
341        range: std::ops::Range<usize>,
342    ) -> Result<(), RytmError> {
343        for sound_index in range {
344            self.toggle_mute_sound(sound_index)?;
345        }
346        Ok(())
347    }
348
349    /// Sets the fixed velocity enable state.
350    pub fn set_fixed_velocity_enable(&mut self, fixed_velocity_enable: bool) {
351        self.fixed_velocity_enable = fixed_velocity_enable;
352    }
353
354    /// Sets the fixed velocity amount.
355    ///
356    /// Range `0..=127`
357    #[parameter_range(range = "fixed_velocity_amount:0..=127")]
358    pub fn set_fixed_velocity_amount(
359        &mut self,
360        fixed_velocity_amount: usize,
361    ) -> Result<(), RytmError> {
362        self.fixed_velocity_amount = fixed_velocity_amount as u8;
363        Ok(())
364    }
365
366    /// Sets the sample recorder source.
367    pub fn set_sample_recorder_source(&mut self, sample_recorder_source: SampleRecorderSource) {
368        self.sample_recorder_src = sample_recorder_source;
369    }
370
371    /// Sets the sample recorder threshold.
372    ///
373    /// Range `0..=127`
374    #[parameter_range(range = "sample_recorder_threshold:0..=127")]
375    pub fn set_sample_recorder_threshold(
376        &mut self,
377        sample_recorder_threshold: usize,
378    ) -> Result<(), RytmError> {
379        self.sample_recorder_thr = sample_recorder_threshold as u8;
380        Ok(())
381    }
382
383    /// Sets the sample recorder monitor state.
384    pub fn set_sample_recorder_monitor_enable(&mut self, set_sample_recorder_monitor_enable: bool) {
385        self.sample_recorder_monitor_enable = set_sample_recorder_monitor_enable;
386    }
387
388    /// Sets the sample recorder recording length.
389    pub fn set_sample_recorder_recording_length(
390        &mut self,
391        sample_recorder_recording_length: SampleRecorderRecordingLength,
392    ) {
393        self.sample_recorder_rlen = sample_recorder_recording_length;
394    }
395
396    /// Returns the BPM for the entire project.
397    ///
398    /// Range `30.0..=300.0`
399    ///
400    /// This is only effective when project level bpm is enabled.
401    pub const fn bpm(&self) -> f32 {
402        self.bpm_project
403    }
404
405    /// Returns the selected track.
406    ///
407    /// Range `0..=11`
408    pub const fn selected_track(&self) -> usize {
409        self.selected_track as usize
410    }
411
412    /// Returns the selected parameter menu item.
413    pub const fn selected_parameter_menu_item(&self) -> ParameterMenuItem {
414        self.selected_parameter_menu_item
415    }
416
417    /// Returns the selected fx menu item.
418    pub const fn selected_fx_menu_item(&self) -> FxParameterMenuItem {
419        self.selected_fx_menu_item
420    }
421
422    /// Returns the selected page.
423    ///
424    /// Range `0..=3`
425    pub const fn selected_page(&self) -> usize {
426        self.selected_page as usize
427    }
428
429    /// Returns the selected sequencer mode.
430    pub const fn selected_mode(&self) -> SequencerMode {
431        self.selected_mode
432    }
433
434    /// Returns the selected pattern mode.
435    pub const fn selected_pattern_mode(&self) -> PatternMode {
436        self.selected_pattern_mode
437    }
438
439    /// Returns the raw mute flags for sounds.
440    pub const fn raw_mute_flags(&self) -> u16 {
441        self.mute_flags
442    }
443
444    /// Returns the collection of muted sound indexes.
445    pub fn muted_sound_indexes(&self) -> Vec<usize> {
446        let mut muted_track_numbers = Vec::new();
447        for sound_index in 0..=11 {
448            if self.mute_flags & (1 << sound_index) != 0 {
449                muted_track_numbers.push(sound_index);
450            }
451        }
452        muted_track_numbers
453    }
454
455    /// Returns the collection of unmuted sound indexes.
456    pub fn unmuted_sound_indexes(&self) -> Vec<usize> {
457        let mut unmuted_sound_indexes = Vec::new();
458        for sound_index in 0..=11 {
459            if self.mute_flags & (1 << sound_index) == 0 {
460                unmuted_sound_indexes.push(sound_index);
461            }
462        }
463        unmuted_sound_indexes
464    }
465
466    /// Returns if a sound is muted by sound index.
467    #[parameter_range(range = "sound_index:0..=11")]
468    pub fn is_sound_muted(&self, sound_index: usize) -> Result<bool, RytmError> {
469        Ok(self.mute_flags & (1 << sound_index) != 0)
470    }
471
472    /// Returns the fixed velocity enable state.
473    pub const fn fixed_velocity_enabled(&self) -> bool {
474        self.fixed_velocity_enable
475    }
476
477    /// Returns the fixed velocity amount.
478    ///
479    /// Range `0..=127`
480    pub const fn fixed_velocity_amount(&self) -> usize {
481        self.fixed_velocity_amount as usize
482    }
483
484    /// Returns the sample recorder source.
485    pub const fn sample_recorder_source(&self) -> SampleRecorderSource {
486        self.sample_recorder_src
487    }
488
489    /// Returns the sample recorder threshold.
490    ///
491    /// Range `0..=127`
492    pub const fn sample_recorder_threshold(&self) -> usize {
493        self.sample_recorder_thr as usize
494    }
495
496    /// Returns the sample recorder monitor state.
497    pub const fn sample_recorder_monitor_enabled(&self) -> bool {
498        self.sample_recorder_monitor_enable
499    }
500
501    /// Returns the sample recorder recording length.
502    pub const fn sample_recorder_recording_length(&self) -> SampleRecorderRecordingLength {
503        self.sample_recorder_rlen
504    }
505
506    /// Returns the version of the settings structure.
507    pub const fn structure_version(&self) -> u32 {
508        self.version
509    }
510
511    pub(crate) fn set_device_id(&mut self, device_id: u8) {
512        self.sysex_meta.set_device_id(device_id);
513    }
514}