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}