1use crate::{
2 buffer::{BufferMut, Ump},
3 detail::property,
4};
5
6mod device_identity;
7mod end_of_clip;
8mod endpoint_discovery;
9mod endpoint_info;
10mod endpoint_name;
11mod function_block_discovery;
12mod function_block_info;
13mod function_block_name;
14mod packet;
15mod product_instance_id;
16mod start_of_clip;
17mod stream_configuration_notification;
18mod stream_configuration_request;
19
20pub use device_identity::*;
21pub use end_of_clip::*;
22pub use endpoint_discovery::*;
23pub use endpoint_info::*;
24pub use endpoint_name::*;
25pub use function_block_discovery::*;
26pub use function_block_info::*;
27pub use function_block_name::FunctionBlockName;
28pub use packet::{Format, Packet};
29pub use product_instance_id::*;
30pub use start_of_clip::*;
31pub use stream_configuration_notification::*;
32pub use stream_configuration_request::*;
33
34pub(crate) const UMP_MESSAGE_TYPE: u8 = 0xF;
35const COMPLETE_FORMAT: u8 = 0x0;
36const START_FORMAT: u8 = 0x1;
37const CONTINUE_FORMAT: u8 = 0x2;
38const END_FORMAT: u8 = 0x3;
39
40#[derive(
41 derive_more::From,
42 midi2_proc::Data,
43 midi2_proc::Packets,
44 midi2_proc::RebufferFrom,
45 midi2_proc::TryRebufferFrom,
46 Clone,
47 Copy,
48 Debug,
49 PartialEq,
50 Eq,
51)]
52#[non_exhaustive]
53pub enum UmpStream<B: crate::buffer::Ump> {
54 DeviceIdentity(device_identity::DeviceIdentity<B>),
55 EndOfClip(end_of_clip::EndOfClip<B>),
56 EndpointDiscovery(endpoint_discovery::EndpointDiscovery<B>),
57 EndpointInfo(endpoint_info::EndpointInfo<B>),
58 EndpointName(endpoint_name::EndpointName<B>),
59 FunctionBlockDiscovery(function_block_discovery::FunctionBlockDiscovery<B>),
60 FunctionBlockInfo(function_block_info::FunctionBlockInfo<B>),
61 FunctionBlockName(function_block_name::FunctionBlockName<B>),
62 ProductInstanceId(product_instance_id::ProductInstanceId<B>),
63 StartOfClip(start_of_clip::StartOfClip<B>),
64 StreamConfigurationNotification(
65 stream_configuration_notification::StreamConfigurationNotification<B>,
66 ),
67 StreamConfigurationRequest(stream_configuration_request::StreamConfigurationRequest<B>),
68}
69
70impl<'a> TryFrom<&'a [u32]> for UmpStream<&'a [u32]> {
71 type Error = crate::error::InvalidData;
72 fn try_from(value: &'a [u32]) -> Result<Self, Self::Error> {
73 use UmpStream::*;
74 if value.is_empty() {
75 return Err(crate::error::InvalidData(
76 crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT,
77 ));
78 };
79 Ok(match status_from_buffer(value) {
80 device_identity::STATUS => {
81 DeviceIdentity(device_identity::DeviceIdentity::try_from(value)?)
82 }
83 end_of_clip::STATUS => EndOfClip(end_of_clip::EndOfClip::try_from(value)?),
84 endpoint_discovery::STATUS => {
85 EndpointDiscovery(endpoint_discovery::EndpointDiscovery::try_from(value)?)
86 }
87 endpoint_info::STATUS => EndpointInfo(endpoint_info::EndpointInfo::try_from(value)?),
88 endpoint_name::STATUS => EndpointName(endpoint_name::EndpointName::try_from(value)?),
89 function_block_discovery::STATUS => FunctionBlockDiscovery(
90 function_block_discovery::FunctionBlockDiscovery::try_from(value)?,
91 ),
92 function_block_info::STATUS => {
93 FunctionBlockInfo(function_block_info::FunctionBlockInfo::try_from(value)?)
94 }
95 function_block_name::STATUS => {
96 FunctionBlockName(function_block_name::FunctionBlockName::try_from(value)?)
97 }
98 product_instance_id::STATUS => {
99 ProductInstanceId(product_instance_id::ProductInstanceId::try_from(value)?)
100 }
101 start_of_clip::STATUS => StartOfClip(start_of_clip::StartOfClip::try_from(value)?),
102 stream_configuration_notification::STATUS => StreamConfigurationNotification(
103 stream_configuration_notification::StreamConfigurationNotification::try_from(
104 value,
105 )?,
106 ),
107 stream_configuration_request::STATUS => StreamConfigurationRequest(
108 stream_configuration_request::StreamConfigurationRequest::try_from(value)?,
109 ),
110 _ => Err(crate::error::InvalidData(
111 "Couldn't interpret flex data status / bank fields",
112 ))?,
113 })
114 }
115}
116
117struct StatusProperty<const STATUS: u16>;
118
119impl<const STATUS: u16, B: Ump> property::Property<B> for StatusProperty<STATUS> {
120 type Type = ();
121}
122
123impl<'a, const STATUS: u16, B: Ump> property::ReadProperty<'a, B> for StatusProperty<STATUS> {
124 fn read(_buffer: &'a B) -> Self::Type {}
125 fn validate(buffer: &B) -> Result<(), crate::error::InvalidData> {
126 if buffer
127 .buffer()
128 .chunks_exact(4)
129 .all(|packet| status_from_buffer(packet) == STATUS)
130 {
131 Ok(())
132 } else {
133 Err(crate::error::InvalidData("Incorrect message status"))
134 }
135 }
136}
137
138impl<const STATUS: u16, B: Ump + BufferMut> property::WriteProperty<B> for StatusProperty<STATUS> {
139 fn validate(_v: &Self::Type) -> Result<(), crate::error::InvalidData> {
140 Ok(())
141 }
142 fn write(buffer: &mut B, _v: Self::Type) {
143 for packet in buffer.buffer_mut().chunks_exact_mut(4) {
144 packet[0] &= !0x03FF_0000;
145 packet[0] |= (STATUS as u32) << 16;
146 }
147 }
148 fn default() -> Self::Type {}
149}
150
151struct ConsistentFormatsProperty;
152
153impl<B: Ump> property::Property<B> for ConsistentFormatsProperty {
154 type Type = ();
155}
156
157impl<'a, B: Ump> property::ReadProperty<'a, B> for ConsistentFormatsProperty {
158 fn read(_buffer: &'a B) -> Self::Type {}
159
160 fn validate(buffer: &B) -> Result<(), crate::error::InvalidData> {
161 use crate::detail::helpers::validate_sysex_group_statuses;
162 use crate::detail::BitOps;
163
164 validate_sysex_group_statuses(
165 buffer.buffer(),
166 |p| u8::from(p[0].crumb(2)) == COMPLETE_FORMAT,
167 |p| u8::from(p[0].crumb(2)) == START_FORMAT,
168 |p| u8::from(p[0].crumb(2)) == CONTINUE_FORMAT,
169 |p| u8::from(p[0].crumb(2)) == END_FORMAT,
170 4,
171 crate::ux::u4::new(UMP_MESSAGE_TYPE),
172 )
173 }
174}
175
176impl<B: Ump + BufferMut> property::WriteProperty<B> for ConsistentFormatsProperty {
177 fn default() -> Self::Type {}
178 fn write(buffer: &mut B, _v: Self::Type) {
179 set_format_fields(buffer.buffer_mut())
180 }
181 fn validate(_v: &Self::Type) -> Result<(), crate::error::InvalidData> {
182 Ok(())
183 }
184}
185
186struct TextWriteStrProperty<'a, const OFFSET: usize>(core::marker::PhantomData<&'a u8>);
187
188impl<'a, const OFFSET: usize, B: Ump> property::Property<B> for TextWriteStrProperty<'a, OFFSET> {
189 type Type = &'a str;
190}
191
192impl<const OFFSET: usize, B: Ump + BufferMut> property::WriteProperty<B>
193 for TextWriteStrProperty<'_, OFFSET>
194{
195 fn write(buffer: &mut B, text: Self::Type) {
196 use crate::detail::BitOps;
197
198 let mut packet_index = 0;
199 let mut byte_index = 0;
200
201 for b in text.as_bytes() {
202 buffer.buffer_mut()[packet_index * 4 + (byte_index + 2 + OFFSET) / 4]
203 .set_octet((byte_index + 2 + OFFSET) % 4, *b);
204
205 if byte_index == 13 - OFFSET {
206 packet_index += 1;
208 byte_index = 0;
209 } else {
210 byte_index += 1;
211 }
212 }
213 }
214 fn default() -> Self::Type {
215 ""
216 }
217 fn validate(_v: &Self::Type) -> Result<(), crate::error::InvalidData> {
218 Ok(())
219 }
220}
221
222impl<const OFFSET: usize, B: Ump + BufferMut> property::ResizeProperty<B>
223 for TextWriteStrProperty<'_, OFFSET>
224{
225 fn resize(buffer: &mut B, value: &Self::Type)
226 where
227 B: crate::buffer::BufferResize,
228 {
229 let buffer_size = required_buffer_size_for_str::<OFFSET>(value);
230 buffer.resize(buffer_size);
231 clear_payload::<OFFSET>(buffer.buffer_mut());
232
233 write_message_header_data(buffer.buffer_mut(), buffer_size);
234 set_format_fields(buffer.buffer_mut());
235 }
236
237 fn try_resize(buffer: &mut B, value: &Self::Type) -> Result<(), crate::error::BufferOverflow>
238 where
239 B: crate::buffer::BufferTryResize,
240 {
241 let buffer_size = required_buffer_size_for_str::<OFFSET>(value);
242 buffer.try_resize(buffer_size)?;
243 clear_payload::<OFFSET>(buffer.buffer_mut());
244
245 write_message_header_data(buffer.buffer_mut(), buffer_size);
246 set_format_fields(buffer.buffer_mut());
247
248 Ok(())
249 }
250}
251
252pub struct TextBytesIterator<'a> {
253 buffer: &'a [u32],
254 packet_index: usize,
255 byte_index: usize,
256 offset: usize,
257}
258
259impl core::iter::Iterator for TextBytesIterator<'_> {
260 type Item = u8;
261 fn next(&mut self) -> Option<Self::Item> {
262 while !self.finished() && self.value() == 0 {
263 self.advance();
264 }
265 if self.finished() {
266 return None;
267 }
268 let ret = Some(self.value());
269 self.advance();
270 ret
271 }
272
273 fn size_hint(&self) -> (usize, Option<usize>) {
274 ((self.buffer.len() - 1) * 14, Some(self.buffer.len() * 14))
275 }
276}
277
278impl core::iter::FusedIterator for TextBytesIterator<'_> {}
279
280impl TextBytesIterator<'_> {
281 fn finished(&self) -> bool {
282 self.buffer.len() / 4 <= self.packet_index
283 }
284 fn advance(&mut self) {
285 self.byte_index += 1;
286 if self.byte_index == 14 - self.offset {
287 self.packet_index += 1;
289 self.byte_index = 0;
290 }
291 }
292 fn value(&mut self) -> u8 {
293 use crate::detail::BitOps;
294 let buffer_index = self.packet_index * 4 + (self.byte_index + 2 + self.offset) / 4;
295 let byte_index = (self.byte_index + 2 + self.offset) % 4;
296 self.buffer[buffer_index].octet(byte_index)
297 }
298}
299
300struct TextReadBytesProperty<'a>(core::marker::PhantomData<&'a u8>);
301
302impl<'a, B: Ump> property::Property<B> for TextReadBytesProperty<'a> {
303 type Type = TextBytesIterator<'a>;
304}
305
306impl<'a, B: 'a + Ump> property::ReadProperty<'a, B> for TextReadBytesProperty<'a> {
307 fn read(buffer: &'a B) -> <Self as property::Property<B>>::Type {
308 TextBytesIterator {
309 buffer: buffer.buffer(),
310 packet_index: 0,
311 byte_index: 0,
312 offset: 0,
313 }
314 }
315 fn validate(_buffer: &B) -> Result<(), crate::error::InvalidData> {
316 Ok(())
317 }
318}
319
320#[cfg(feature = "std")]
321struct TextReadStringProperty;
322
323#[cfg(feature = "std")]
324impl<B: Ump> property::Property<B> for TextReadStringProperty {
325 type Type = std::string::String;
326}
327
328#[cfg(feature = "std")]
329impl<'a, B: Ump> property::ReadProperty<'a, B> for TextReadStringProperty {
330 fn read(buffer: &'a B) -> Self::Type {
331 let bytes = TextReadBytesProperty::read(buffer).collect();
332 std::string::String::from_utf8(bytes).unwrap()
333 }
334 fn validate(buffer: &B) -> Result<(), crate::error::InvalidData> {
335 let bytes = TextReadBytesProperty::read(buffer).collect();
336 std::string::String::from_utf8(bytes).map_err(|_| {
337 crate::error::InvalidData("Payload bytes do not represent a valid utf string")
338 })?;
339 Ok(())
340 }
341}
342
343fn set_format_fields(buffer: &mut [u32]) {
344 use crate::detail::BitOps;
345 use crate::ux::u2;
346
347 let mut packets = buffer
348 .chunks_exact_mut(4)
349 .take_while(|packet| u8::from(packet[0].nibble(0)) == UMP_MESSAGE_TYPE)
350 .peekable();
351
352 let Some(first) = packets.next() else {
353 panic!("Can't be called with empty buffer");
354 };
355
356 if packets.peek().is_some() {
357 first[0].set_crumb(2, u2::new(START_FORMAT));
358 } else {
359 first[0].set_crumb(2, u2::new(COMPLETE_FORMAT));
360 }
361
362 while let Some(packet) = packets.next() {
363 if packets.peek().is_some() {
364 packet[0].set_crumb(2, u2::new(CONTINUE_FORMAT));
365 } else {
366 packet[0].set_crumb(2, u2::new(END_FORMAT));
367 }
368 }
369}
370
371fn clear_payload<const OFFSET: usize>(buffer: &mut [u32]) {
372 debug_assert!(OFFSET < 2);
373 for packet in buffer.chunks_exact_mut(4) {
374 use crate::detail::BitOps;
375 if OFFSET < 1 {
376 packet[0].set_octet(2, 0);
377 }
378 if OFFSET < 2 {
379 packet[0].set_octet(3, 0);
380 }
381 packet[1] = 0x0;
382 packet[2] = 0x0;
383 packet[3] = 0x0;
384 }
385}
386
387fn required_buffer_size_for_str<const OFFSET: usize>(s: &str) -> usize {
388 let str_size = s.len();
389 let packet_capacity = 14 - OFFSET;
390 if str_size.is_multiple_of(packet_capacity) {
391 if str_size == 0 {
392 4
393 } else {
394 str_size * 4 / packet_capacity
395 }
396 } else {
397 4 * (str_size / packet_capacity + 1)
398 }
399}
400
401fn write_message_header_data(buffer: &mut [u32], size: usize) {
402 use crate::detail::BitOps;
403 use crate::ux::u4;
404
405 let status = status_from_buffer(buffer);
406
407 for packet in buffer[..size].chunks_exact_mut(4) {
408 packet[0].set_nibble(0, u4::new(UMP_MESSAGE_TYPE));
409 packet[0] &= !0x03FF_0000;
410 packet[0] |= (status as u32) << 16;
411 }
412
413 for packet in buffer[size..].chunks_exact_mut(4) {
414 packet[0] = 0x0;
415 }
416}
417
418fn message_size<B: crate::buffer::Ump>(buffer: &B) -> usize {
419 use crate::detail::BitOps;
420
421 buffer
422 .buffer()
423 .chunks_exact(4)
424 .position(|p| {
425 let format: u8 = p[0].crumb(2).into();
426 format == COMPLETE_FORMAT || format == END_FORMAT
427 })
428 .expect("Message is in an invalid state. Couldn't find end packet.")
429 * 4
430 + 4
431}
432
433fn status_from_buffer(buffer: &[u32]) -> u16 {
434 ((buffer[0] & 0x03FF_0000) >> 16) as u16
435}
436
437#[cfg(test)]
438mod tests {
439 use super::*;
440 use pretty_assertions::assert_eq;
441
442 #[test]
443 fn try_from_data() {
444 assert_eq!(
445 UmpStream::try_from(
446 &[
447 0xF403_5268,
448 0x7974_686D,
449 0x5265_7665,
450 0x6C61_7469,
451 0xF803_6F6E,
452 0x3A20_4265,
453 0x6174_7320,
454 0x4265_796F,
455 0xF803_6E64,
456 0x2042_6F75,
457 0x6E64_6172,
458 0x6965_73F0,
459 0xFC03_9F8C,
460 0x8DF0_9FA5,
461 0x81F0_9F9A,
462 0x8000_0000,
463 ][..]
464 ),
465 Ok(UmpStream::EndpointName(
466 endpoint_name::EndpointName::try_from(
467 &[
468 0xF403_5268,
469 0x7974_686D,
470 0x5265_7665,
471 0x6C61_7469,
472 0xF803_6F6E,
473 0x3A20_4265,
474 0x6174_7320,
475 0x4265_796F,
476 0xF803_6E64,
477 0x2042_6F75,
478 0x6E64_6172,
479 0x6965_73F0,
480 0xFC03_9F8C,
481 0x8DF0_9FA5,
482 0x81F0_9F9A,
483 0x8000_0000,
484 ][..]
485 )
486 .unwrap()
487 ))
488 );
489 }
490
491 #[test]
492 fn packets() {
493 use crate::Packets;
494
495 let message = UmpStream::try_from(
496 &[
497 0xF403_5268,
498 0x7974_686D,
499 0x5265_7665,
500 0x6C61_7469,
501 0xF803_6F6E,
502 0x3A20_4265,
503 0x6174_7320,
504 0x4265_796F,
505 0xF803_6E64,
506 0x2042_6F75,
507 0x6E64_6172,
508 0x6965_73F0,
509 0xFC03_9F8C,
510 0x8DF0_9FA5,
511 0x81F0_9F9A,
512 0x8000_0000,
513 ][..],
514 )
515 .unwrap();
516
517 let mut packets = message.packets();
518 assert_eq!(
519 &*packets.next().unwrap(),
520 &[0xF403_5268, 0x7974_686D, 0x5265_7665, 0x6C61_7469,][..],
521 );
522 assert_eq!(
523 &*packets.next().unwrap(),
524 &[0xF803_6F6E, 0x3A20_4265, 0x6174_7320, 0x4265_796F,][..],
525 );
526 assert_eq!(
527 &*packets.next().unwrap(),
528 &[0xF803_6E64, 0x2042_6F75, 0x6E64_6172, 0x6965_73F0,][..],
529 );
530 assert_eq!(
531 &*packets.next().unwrap(),
532 &[0xFC03_9F8C, 0x8DF0_9FA5, 0x81F0_9F9A, 0x8000_0000,][..],
533 );
534 assert_eq!(packets.next(), None,);
535 }
536}