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