Skip to main content

midi2/
ump_stream.rs

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                // end of the packet
207                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            // end of message
288            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}