Skip to main content

midi_msg/system_exclusive/
sample_dump.rs

1use crate::parse_error::*;
2use crate::util::*;
3use alloc::vec::Vec;
4use bstr::BString;
5
6/// Used to request and transmit sampler data.
7/// Used by [`UniversalNonRealTimeMsg::SampleDump`](crate::UniversalNonRealTimeMsg::SampleDump).
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum SampleDumpMsg {
11    /// Request that the receiver send the given sample.
12    Request {
13        /// The ID of the sample, between 0-16383.
14        sample_num: u16,
15    },
16    /// The first message in a sample dump, used to describe the data contained in the following packets.
17    Header {
18        /// The ID of the sample, between 0-16383.
19        sample_num: u16,
20        /// # of significant bits from 8-28.
21        format: u8,
22        /// Sample period (1/sample rate) in nanoseconds, 0-2097151
23        period: u32,
24        /// Sample length in words, 0-2097151
25        length: u32,
26        /// Sustain loop start point word number, 0-2097151
27        sustain_loop_start: u32,
28        /// Sustain loop end point word number, 0-2097151
29        sustain_loop_end: u32,
30        loop_type: LoopType,
31    },
32    /// A single packet of sample data.
33    ///
34    /// Use [`SampleDumpMsg::packet`] to construct.
35    Packet {
36        /// Running packet count, 0-127. Wraps back to 0
37        running_count: u8,
38        /// At most 120 7 bit words
39        data: Vec<u8>,
40    },
41    /// Request that the receiver return data about the loop points for a given sample.
42    LoopPointsRequest {
43        /// The ID of the sample, between 0-16383.
44        sample_num: u16,
45        loop_num: LoopNumber,
46    },
47    /// Used to send additional loop points for a given sample.
48    LoopPointTransmission {
49        /// The ID of the sample, between 0-16383.
50        sample_num: u16,
51        loop_num: LoopNumber,
52        loop_type: LoopType,
53        /// Loop start address (in samples)
54        start_addr: u32,
55        /// Loop end address (in samples)
56        end_addr: u32,
57    },
58}
59
60impl SampleDumpMsg {
61    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
62        match self {
63            Self::Header {
64                sample_num,
65                format,
66                period,
67                length,
68                sustain_loop_start,
69                sustain_loop_end,
70                loop_type,
71            } => {
72                push_u14(*sample_num, v);
73                v.push((*format).clamp(8, 28));
74                push_u21(*period, v);
75                push_u21(*length, v);
76                push_u21(*sustain_loop_start, v);
77                push_u21(*sustain_loop_end, v);
78                v.push(*loop_type as u8);
79            }
80            Self::Packet {
81                running_count,
82                data,
83            } => {
84                let mut p: [u8; 120] = [0; 120];
85                for (i, b) in data.iter().enumerate() {
86                    if i > 119 {
87                        break;
88                    }
89                    p[i] = to_u7(*b);
90                }
91                v.push(to_u7(*running_count));
92                v.extend_from_slice(&p);
93                v.push(0); // Checksum <- Will be written over by `SystemExclusiveMsg.extend_midi`
94            }
95            Self::Request { sample_num } => {
96                push_u14(*sample_num, v);
97            }
98            Self::LoopPointTransmission {
99                sample_num,
100                loop_num,
101                loop_type,
102                start_addr,
103                end_addr,
104            } => {
105                push_u14(*sample_num, v);
106                loop_num.extend_midi(v);
107                v.push(*loop_type as u8);
108                push_u21(*start_addr, v);
109                push_u21(*end_addr, v);
110            }
111            Self::LoopPointsRequest {
112                sample_num,
113                loop_num,
114            } => {
115                push_u14(*sample_num, v);
116                loop_num.extend_midi(v);
117            }
118        }
119    }
120
121    #[allow(dead_code)]
122    pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
123        Err(ParseError::NotImplemented("SampleDumpMsg"))
124    }
125
126    /// Construct a packet of exactly 120 7-bit "bytes".
127    /// `num` is the number of this packet.
128    pub fn packet(num: u32, mut data: [u8; 120]) -> Self {
129        for d in data.iter_mut() {
130            *d = to_u7(*d);
131        }
132
133        Self::Packet {
134            running_count: (num % 128) as u8,
135            data: data.to_vec(),
136        }
137    }
138}
139
140/// What loop a [`SampleDumpMsg`] or [`ExtendedSampleDumpMsg`] is referring to.
141#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
142#[derive(Debug, Copy, Clone, PartialEq, Eq)]
143pub enum LoopNumber {
144    /// A loop with the given ID, 0-16382.
145    Loop(u16),
146    /// Used by [`SampleDumpMsg::LoopPointsRequest`] to request all loops.
147    RequestAll,
148    /// Used by [`SampleDumpMsg::LoopPointTransmission`] to indicate that all loops should be deleted.
149    DeleteAll,
150}
151
152impl LoopNumber {
153    fn extend_midi(&self, v: &mut Vec<u8>) {
154        match self {
155            Self::RequestAll => {
156                v.push(0x7F);
157                v.push(0x7F);
158            }
159            Self::DeleteAll => {
160                v.push(0x7F);
161                v.push(0x7F);
162            }
163            Self::Loop(x) => push_u14(*x, v),
164        }
165    }
166}
167
168/// The type of loop being described by a [`SampleDumpMsg`].
169#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
170#[derive(Debug, Copy, Clone, PartialEq, Eq)]
171pub enum LoopType {
172    /// Forward only
173    Forward = 0,
174    /// Backward forward
175    BiDirectional = 1,
176    /// Do not loop
177    Off = 127,
178}
179
180/// The extended sample dump messages described in CA-019, used to allow for longer, named samples.
181/// Used by [`UniversalNonRealTimeMsg::SampleDump`](crate::UniversalNonRealTimeMsg::SampleDump).
182#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
183#[derive(Debug, Clone, PartialEq)]
184pub enum ExtendedSampleDumpMsg {
185    Header {
186        /// The ID of the sample, between 0-16383.
187        sample_num: u16,
188        /// # of significant bits from 8-28
189        format: u8,
190        /// Sample rate in Hz. The f64 is used to approximate the two 28bit fixed point sent over the wire.
191        sample_rate: f64,
192        /// Sample length in words, 0-34359738368
193        length: u64,
194        /// Sustain loop start point word number, 0-34359738367
195        sustain_loop_start: u64,
196        /// Sustain loop end point word number, 0-34359738367
197        sustain_loop_end: u64,
198        loop_type: ExtendedLoopType,
199        /// Number of audio channels, 0-127
200        num_channels: u8,
201    },
202    /// Request the given sample's name.
203    SampleNameRequest {
204        /// The ID of the sample, between 0-16383.
205        sample_num: u16,
206    },
207    /// Describe the name of a given sample.
208    SampleName {
209        /// The ID of the sample, between 0-16383.
210        sample_num: u16,
211        /// An up to 127 character name.
212        name: BString,
213    },
214    /// Request that the receiver return data about the loop points for a given sample.
215    LoopPointsRequest {
216        /// The ID of the sample, between 0-16383.
217        sample_num: u16,
218        loop_num: LoopNumber,
219    },
220    /// Used to send additional loop points for a given sample.
221    LoopPointTransmission {
222        /// The ID of the sample, between 0-16383.
223        sample_num: u16,
224        loop_num: LoopNumber,
225        loop_type: ExtendedLoopType,
226        /// Loop start address (in samples)
227        start_addr: u64,
228        /// Loop end address (in samples)
229        end_addr: u64,
230    },
231}
232
233impl ExtendedSampleDumpMsg {
234    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
235        match self {
236            Self::Header {
237                sample_num,
238                format,
239                sample_rate,
240                length,
241                sustain_loop_start,
242                sustain_loop_end,
243                loop_type,
244                num_channels,
245            } => {
246                push_u14(*sample_num, v);
247                v.push((*format).clamp(8, 28));
248                let sample_rate = sample_rate.max(0.0);
249                let sample_rate_integer = (sample_rate as u64) as f64; // for lack of no_std f64 floor
250                push_u28(sample_rate_integer as u32, v);
251                push_u28(
252                    ((sample_rate - sample_rate_integer) * ((1 << 28) as f64)) as u32,
253                    v,
254                );
255                push_u35((*length).min(34359738368), v);
256                push_u35((*sustain_loop_start).min(34359738367), v);
257                push_u35((*sustain_loop_end).min(34359738367), v);
258                v.push(*loop_type as u8);
259                push_u7(*num_channels, v);
260            }
261            Self::LoopPointTransmission {
262                sample_num,
263                loop_num,
264                loop_type,
265                start_addr,
266                end_addr,
267            } => {
268                push_u14(*sample_num, v);
269                loop_num.extend_midi(v);
270                v.push(*loop_type as u8);
271                push_u35(*start_addr, v);
272                push_u35(*end_addr, v);
273            }
274            Self::LoopPointsRequest {
275                sample_num,
276                loop_num,
277            } => {
278                push_u14(*sample_num, v);
279                loop_num.extend_midi(v);
280            }
281            Self::SampleName { sample_num, name } => {
282                push_u14(*sample_num, v);
283                v.push(0); // Language tag length (0 is the only allowable value)
284                let len = name.len().min(127);
285                v.push(len as u8);
286                v.extend_from_slice(&name[0..len]);
287            }
288            Self::SampleNameRequest { sample_num } => {
289                push_u14(*sample_num, v);
290            }
291        }
292    }
293
294    #[allow(dead_code)]
295    pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
296        Err(ParseError::NotImplemented("ExtendedSampleDumpMsg"))
297    }
298}
299
300/// The type of loop being described by a [`SampleDumpMsg`].
301#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
302#[derive(Debug, Copy, Clone, PartialEq, Eq)]
303pub enum ExtendedLoopType {
304    /// A forward, unidirectional loop
305    Forward = 0x00,
306    /// Loop starts playing forward, gets to end and plays backward, repeating
307    BiDirectional = 0x01,
308    /// A unidirectional loop, upon key release the rest of the sample is played
309    ForwardRelease = 0x02,
310    /// A bidirectional loop, upon key release the rest of the sample is played
311    BiDirectionalRelease = 0x03,
312    /// A backward, unidirectional loop
313    Backward = 0x40,
314    /// Like BiDirectional, but starts playing in reverse
315    BackwardBiDirectional = 0x41,
316    /// A backward unidirectional loop, upon key release the rest of the sample after the loop is played backwards
317    BackwardRelease = 0x42,
318    /// A bidirectional loop, starting from the end playing backward, upon key release the rest of the sample after the loop is played backwards
319    BackwardBiDirectionalRelease = 0x43,
320    /// Backwards one-shot, no looping
321    BackwardOneShot = 0x7E,
322    /// Forwards one-shot, no looping
323    OneShot = 0x7F,
324}
325
326#[cfg(test)]
327mod tests {
328    use crate::*;
329    use alloc::vec;
330
331    #[test]
332    fn serialize_sample_dump_msg() {
333        assert_eq!(
334            MidiMsg::SystemExclusive {
335                msg: SystemExclusiveMsg::UniversalNonRealTime {
336                    device: DeviceID::AllCall,
337                    msg: UniversalNonRealTimeMsg::ExtendedSampleDump(
338                        ExtendedSampleDumpMsg::Header {
339                            sample_num: 5,
340                            format: 8,
341                            sample_rate: 4000.5,
342                            length: 2u64.pow(30),
343                            sustain_loop_start: 2u64.pow(10),
344                            sustain_loop_end: 2u64.pow(20),
345                            loop_type: ExtendedLoopType::BiDirectionalRelease,
346                            num_channels: 2
347                        }
348                    ),
349                },
350            }
351            .to_midi(),
352            vec![
353                0xF0, 0x7E, 0x7F, // All call
354                0x05, 0x05, // ExtendedSampleDump header
355                0x5, 00, // Sample number
356                8,  // format,
357                0b0100000, 0b0011111, 0, 0, // 4000 LSB first
358                0, 0, 0, 0x40, // 0.5 LSB first
359                0, 0, 0, 0, 0b0000100, // Length
360                0, 0b0001000, 0, 0, 0, // Sustain loop start
361                0, 0, 0b1000000, 0, 0,    // Sustain loop end
362                0x03, // Loop type
363                2,    // Num channels
364                0xF7
365            ]
366        );
367    }
368}