rytm_rs/query.rs
1//! Query structures for the rytm.
2//!
3//! The structures in this module are used to send sysex queries to the rytm.
4//!
5//! The all implement the `ObjectQuery` trait and there is a blanket implementation for all types which implement `ObjectQuery` to be `SysexCompatible`.
6//!
7//! # Example
8//!
9//! ```
10//! use rytm::prelude::*;
11//!
12//! let query = GlobalQuery::new_targeting_work_buffer();
13//! let sysex = query.as_sysex().unwrap();
14//!
15//! ```
16
17// All casts in this file are intended or safe within the context of this library.
18//
19// One can change `allow` to `warn` to review them if necessary.
20#![allow(
21 clippy::cast_lossless,
22 clippy::cast_possible_truncation,
23 clippy::cast_sign_loss
24)]
25
26/// Holds the global query structure.
27mod global;
28/// Holds the kit query structure.
29mod kit;
30/// Holds the pattern query structure.
31mod pattern;
32/// Holds the raw query structure.
33mod raw;
34/// Holds the settings query structure.
35mod settings;
36/// Holds the song query structure.
37mod song;
38/// Holds the sound query structure.
39mod sound;
40
41pub use global::GlobalQuery;
42pub use kit::KitQuery;
43pub use pattern::PatternQuery;
44pub use raw::RawQuery;
45pub use settings::SettingsQuery;
46pub use song::SongQuery;
47pub use sound::SoundQuery;
48
49/// The size of the rytm sysex query in bytes.
50///
51/// `0xF0 0x00 0x20 0x3c 0x07 0x00 <id> 0x01 0x01 <nr> 0x00 0x00 0x00 0x05 0xF7`
52const RYTM_SYSEX_QUERY_SIZE: usize = rytm_sys::AR_SYSEX_REQUEST_MSG_SZ as usize;
53
54use super::error::{RytmError, SysexConversionError};
55use crate::{
56 sysex::{AnySysexType, SysexMeta},
57 SysexCompatible,
58};
59
60/// A trait which is implemented by all structures which can be converted to rytm sysex query messages.
61pub trait ObjectQuery: SysexCompatible + Send + Sync {
62 /// Returns the sysex type of the object.
63 fn sysex_type(&self) -> AnySysexType;
64
65 /// Returns the device id of the object.
66 fn device_id(&self) -> u8;
67
68 /// Returns the object number (index) of the object.
69 fn obj_nr(&self) -> u16;
70
71 /// Returns the sysex meta data for the object creating it.
72 fn as_sysex_meta(&self) -> SysexMeta {
73 SysexMeta {
74 container_version: 0x0101,
75 dev_id: self.device_id(),
76 obj_type: ObjectQuery::sysex_type(self).into(),
77 obj_nr: self.obj_nr(),
78 // Calculated in libanalogrytm, they're dummy values here in this state.
79 chksum: 0,
80 data_size: 0,
81 }
82 }
83
84 /// Returns the information if this query is targeting the work buffer.
85 fn is_targeting_work_buffer(&self) -> bool {
86 self.obj_nr() >= 128
87 }
88}
89
90impl<T: ObjectQuery> SysexCompatible for T {
91 fn sysex_type(&self) -> AnySysexType {
92 ObjectQuery::sysex_type(self)
93 }
94
95 fn as_sysex(&self) -> Result<Vec<u8>, RytmError> {
96 let mut buffer = vec![0; RYTM_SYSEX_QUERY_SIZE];
97 let destination_buffer = buffer.as_mut_slice();
98 let meta: rytm_sys::ar_sysex_meta_t = self.as_sysex_meta().into();
99
100 // The count of return error codes from `rytm-sys` is far below 255.
101 #[allow(clippy::cast_possible_truncation)]
102 unsafe {
103 let return_code = rytm_sys::ar_sysex_request(
104 destination_buffer.as_mut_ptr(),
105 std::ptr::addr_of!(meta),
106 ) as u8;
107
108 if return_code != 0 {
109 return Err(SysexConversionError::from(return_code).into());
110 }
111 }
112
113 Ok(buffer)
114 }
115}