#![allow(
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
pub mod types;
use crate::error::{RytmError, SysexConversionError};
use rytm_sys::{ar_global_t, ar_kit_t, ar_pattern_t, ar_settings_t, ar_sound_t};
use serde::{Deserialize, Serialize};
use std::ptr::addr_of_mut;
pub use types::*;
pub const PATTERN_SYSEX_SIZE: usize = 14988;
pub const KIT_SYSEX_SIZE: usize = 2998;
pub const SOUND_SYSEX_SIZE: usize = 201;
pub const SETTINGS_SYSEX_SIZE: usize = 2401;
pub const GLOBAL_SYSEX_SIZE: usize = 107;
pub const SONG_SYSEX_SIZE: usize = 1506;
const SYSEX_MESSAGE_TYPE_BYTE_INDEX: usize = 6;
pub const PATTERN_RAW_SIZE: usize = std::mem::size_of::<ar_pattern_t>();
pub const KIT_RAW_SIZE: usize = std::mem::size_of::<ar_kit_t>();
pub const SOUND_RAW_SIZE: usize = std::mem::size_of::<ar_sound_t>();
pub const SETTINGS_RAW_SIZE: usize = std::mem::size_of::<ar_settings_t>();
pub const GLOBAL_RAW_SIZE: usize = std::mem::size_of::<ar_global_t>();
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum AnySysexType {
Known(SysexType),
Unknown(u8),
}
impl From<SysexType> for AnySysexType {
fn from(t: SysexType) -> Self {
Self::Known(t)
}
}
impl From<u8> for AnySysexType {
fn from(t: u8) -> Self {
if let Ok(t) = SysexType::try_from_dump_id(t) {
return Self::from(t);
}
if let Ok(t) = SysexType::try_from(t) {
return Self::from(t);
}
Self::Unknown(t)
}
}
impl From<AnySysexType> for u8 {
fn from(t: AnySysexType) -> Self {
match t {
AnySysexType::Known(t) => t.into(),
AnySysexType::Unknown(t) => t,
}
}
}
pub trait SysexCompatible {
fn sysex_type(&self) -> AnySysexType;
fn as_sysex(&self) -> Result<Vec<u8>, RytmError>;
}
#[macro_export]
macro_rules! impl_sysex_compatible {
($object_type:ty, $object_raw_type:ty, $object_encoder_function:ident, $object_sysex_type:expr, $object_sysex_size:expr) => {
impl SysexCompatible for $object_type {
fn as_sysex(&self) -> Result<Vec<u8>, RytmError> {
let (sysex_meta, raw_object) = self.as_raw_parts();
let raw_size = std::mem::size_of::<$object_raw_type>();
let mut raw_buffer: Vec<u8> = Vec::with_capacity(raw_size);
unsafe {
let raw: *const u8 = (&raw_object as *const $object_raw_type).cast::<u8>();
for i in 0..raw_size {
raw_buffer.push(*raw.add(i));
}
}
let mut encoded_buffer_length: u32 = 0;
let mut encoded_buf = vec![0; $object_sysex_size];
let mut meta = sysex_meta.into();
let meta_ptr = &mut meta as *mut ar_sysex_meta_t;
unsafe {
let return_code = $object_encoder_function(
encoded_buf.as_mut_ptr(),
raw_buffer.as_ptr(),
std::mem::size_of::<$object_raw_type>() as u32,
&mut encoded_buffer_length as *mut u32,
meta_ptr,
) as u8;
if return_code != 0 {
return Err(SysexConversionError::from(return_code).into());
}
let chksum = ((encoded_buf[encoded_buf.len() - 5] as u16) << 8)
| encoded_buf[encoded_buf.len() - 4] as u16;
let size = ((encoded_buf[encoded_buf.len() - 3] as u16) << 8)
| encoded_buf[encoded_buf.len() - 2] as u16;
dbg!(size, chksum);
Ok(encoded_buf)
}
}
fn sysex_type(&self) -> AnySysexType {
$object_sysex_type.into()
}
}
};
}
pub fn decode_sysex_response_to_raw(response: &[u8]) -> Result<(Vec<u8>, SysexMeta), RytmError> {
if response.get(SYSEX_MESSAGE_TYPE_BYTE_INDEX).is_none() {
return Err(SysexConversionError::ShortRead.into());
}
let response_type = SysexType::try_from_dump_id(response[SYSEX_MESSAGE_TYPE_BYTE_INDEX])?;
let (expected_response_size, expected_raw_size) = match response_type {
SysexType::Pattern => (PATTERN_SYSEX_SIZE, PATTERN_RAW_SIZE),
SysexType::Kit => (KIT_SYSEX_SIZE, KIT_RAW_SIZE),
SysexType::Sound => (SOUND_SYSEX_SIZE, SOUND_RAW_SIZE),
SysexType::Settings => (SETTINGS_SYSEX_SIZE, SETTINGS_RAW_SIZE),
SysexType::Global => (GLOBAL_SYSEX_SIZE, GLOBAL_RAW_SIZE),
SysexType::Song => (SONG_SYSEX_SIZE, 1024 * 16),
};
if response.len() != expected_response_size {
return Err(
SysexConversionError::InvalidSize(expected_response_size, response.len()).into(),
);
}
let meta = SysexMeta::default();
let mut meta: rytm_sys::ar_sysex_meta_t = meta.into();
let mut src_buf = response.as_ptr();
#[allow(clippy::cast_possible_truncation)]
let mut src_buf_size = response.len() as u32;
let mut dst_buf_size = 0u32;
let mut dst_buf = vec![0_u8; expected_raw_size];
dbg!(&response[0..12]);
unsafe {
#[allow(clippy::cast_possible_truncation)]
let return_code = rytm_sys::ar_sysex_to_raw(
dst_buf.as_mut_slice().as_mut_ptr(),
addr_of_mut!(src_buf),
addr_of_mut!(src_buf_size),
addr_of_mut!(dst_buf_size),
addr_of_mut!(meta),
) as u8;
if return_code != 0 {
return Err(SysexConversionError::from(return_code).into());
}
}
Ok((dst_buf, SysexMeta::from(&meta)))
}