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}