1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
use crate::util::*;
use ascii::AsciiString;

/// Used to request and transmit sampler data.
/// Used by [`UniversalNonRealTimeMsg::SampleDump`](crate::UniversalNonRealTimeMsg::SampleDump).
#[derive(Debug, Clone, PartialEq)]
pub enum SampleDumpMsg {
    /// Request that the receiver send the given sample.
    Request {
        /// The ID of the sample, between 0-16383.
        sample_num: u16,
    },
    /// The first message in a sample dump, used to describe the data contained in the following packets.
    Header {
        /// The ID of the sample, between 0-16383.
        sample_num: u16,
        /// # of significant bits from 8-28.
        format: u8,
        /// Sample period (1/sample rate) in nanoseconds, 0-2097151
        period: u32,
        /// Sample length in words, 0-2097151
        length: u32,
        /// Sustain loop start point word number, 0-2097151
        sustain_loop_start: u32,
        /// Sustain loop end point word number, 0-2097151
        sustain_loop_end: u32,
        loop_type: LoopType,
    },
    /// A single packet of sample data.
    ///
    /// Use [`SampleDumpMsg::packet`] to construct.
    Packet {
        /// Running packet count, 0-127. Wraps back to 0
        running_count: u8,
        /// At most 120 7 bit words
        data: Vec<u8>,
    },
    /// Request that the receiver return data about the loop points for a given sample.
    LoopPointsRequest {
        /// The ID of the sample, between 0-16383.
        sample_num: u16,
        loop_num: LoopNumber,
    },
    /// Used to send additional loop points for a given sample.
    LoopPointTransmission {
        /// The ID of the sample, between 0-16383.
        sample_num: u16,
        loop_num: LoopNumber,
        loop_type: LoopType,
        /// Loop start address (in samples)
        start_addr: u32,
        /// Loop end address (in samples)
        end_addr: u32,
    },
}

impl SampleDumpMsg {
    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
        match self {
            Self::Header {
                sample_num,
                format,
                period,
                length,
                sustain_loop_start,
                sustain_loop_end,
                loop_type,
            } => {
                push_u14(*sample_num, v);
                v.push((*format).min(28).max(8));
                push_u21(*period, v);
                push_u21(*length, v);
                push_u21(*sustain_loop_start, v);
                push_u21(*sustain_loop_end, v);
                v.push(*loop_type as u8);
            }
            Self::Packet {
                running_count,
                data,
            } => {
                let mut p: [u8; 120] = [0; 120];
                for (i, b) in data.iter().enumerate() {
                    if i > 119 {
                        break;
                    }
                    p[i] = to_u7(*b);
                }
                v.push(to_u7(*running_count));
                v.extend_from_slice(&p);
                v.push(0); // Checksum <- Will be written over by `SystemExclusiveMsg.extend_midi`
            }
            Self::Request { sample_num } => {
                push_u14(*sample_num, v);
            }
            Self::LoopPointTransmission {
                sample_num,
                loop_num,
                loop_type,
                start_addr,
                end_addr,
            } => {
                push_u14(*sample_num, v);
                loop_num.extend_midi(v);
                v.push(*loop_type as u8);
                push_u21(*start_addr, v);
                push_u21(*end_addr, v);
            }
            Self::LoopPointsRequest {
                sample_num,
                loop_num,
            } => {
                push_u14(*sample_num, v);
                loop_num.extend_midi(v);
            }
        }
    }

    pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), &str> {
        Err("TODO: not implemented")
    }

    /// Construct a packet of exactly 120 7-bit "bytes".
    /// `num` is the number of this packet.
    pub fn packet(num: u32, mut data: [u8; 120]) -> Self {
        for d in data.iter_mut() {
            *d = to_u7(*d);
        }

        Self::Packet {
            running_count: (num % 128) as u8,
            data: data.to_vec(),
        }
    }
}

/// What loop a [`SampleDumpMsg`] or [`ExtendedSampleDumpMsg`] is referring to.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum LoopNumber {
    /// A loop with the given ID, 0-16382.
    Loop(u16),
    /// Used by [`SampleDumpMsg::LoopPointsRequest`] to request all loops.
    RequestAll,
    /// Used by [`SampleDumpMsg::LoopPointTransmission`] to indicate that all loops should be deleted.
    DeleteAll,
}

impl LoopNumber {
    fn extend_midi(&self, v: &mut Vec<u8>) {
        match self {
            Self::RequestAll => {
                v.push(0x7F);
                v.push(0x7F);
            }
            Self::DeleteAll => {
                v.push(0x7F);
                v.push(0x7F);
            }
            Self::Loop(x) => push_u14(*x, v),
        }
    }
}

/// The type of loop being described by a [`SampleDumpMsg`].
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum LoopType {
    /// Forward only
    Forward = 0,
    /// Backward forward
    BiDirectional = 1,
    /// Do not loop
    Off = 127,
}

/// The extended sample dump messages described in CA-019, used to allow for longer, named samples.
/// Used by [`UniversalNonRealTimeMsg::SampleDump`](crate::UniversalNonRealTimeMsg::SampleDump).
#[derive(Debug, Clone, PartialEq)]
pub enum ExtendedSampleDumpMsg {
    Header {
        /// The ID of the sample, between 0-16383.
        sample_num: u16,
        /// # of significant bits from 8-28
        format: u8,
        /// Sample rate in Hz. The f64 is used to approximate the two 28bit fixed point sent over the wire.
        sample_rate: f64,
        /// Sample length in words, 0-34359738368
        length: u64,
        /// Sustain loop start point word number, 0-34359738367
        sustain_loop_start: u64,
        /// Sustain loop end point word number, 0-34359738367
        sustain_loop_end: u64,
        loop_type: ExtendedLoopType,
        /// Number of audio channels, 0-127
        num_channels: u8,
    },
    /// Request the given sample's name.
    SampleNameRequest {
        /// The ID of the sample, between 0-16383.
        sample_num: u16,
    },
    /// Describe the name of a given sample.
    SampleName {
        /// The ID of the sample, between 0-16383.
        sample_num: u16,
        /// An up to 127 character name.
        name: AsciiString,
    },
    /// Request that the receiver return data about the loop points for a given sample.
    LoopPointsRequest {
        /// The ID of the sample, between 0-16383.
        sample_num: u16,
        loop_num: LoopNumber,
    },
    /// Used to send additional loop points for a given sample.
    LoopPointTransmission {
        /// The ID of the sample, between 0-16383.
        sample_num: u16,
        loop_num: LoopNumber,
        loop_type: ExtendedLoopType,
        /// Loop start address (in samples)
        start_addr: u64,
        /// Loop end address (in samples)
        end_addr: u64,
    },
}

impl ExtendedSampleDumpMsg {
    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
        match self {
            Self::Header {
                sample_num,
                format,
                sample_rate,
                length,
                sustain_loop_start,
                sustain_loop_end,
                loop_type,
                num_channels,
            } => {
                push_u14(*sample_num, v);
                v.push((*format).min(28).max(8));
                let sample_rate = sample_rate.max(0.0);
                let sample_rate_integer = sample_rate.floor();
                push_u28(sample_rate_integer as u32, v);
                push_u28(
                    ((sample_rate - sample_rate_integer) * 2.0f64.powi(28)) as u32,
                    v,
                );
                push_u35((*length).min(34359738368), v);
                push_u35((*sustain_loop_start).min(34359738367), v);
                push_u35((*sustain_loop_end).min(34359738367), v);
                v.push(*loop_type as u8);
                push_u7(*num_channels, v);
            }
            Self::LoopPointTransmission {
                sample_num,
                loop_num,
                loop_type,
                start_addr,
                end_addr,
            } => {
                push_u14(*sample_num, v);
                loop_num.extend_midi(v);
                v.push(*loop_type as u8);
                push_u35(*start_addr, v);
                push_u35(*end_addr, v);
            }
            Self::LoopPointsRequest {
                sample_num,
                loop_num,
            } => {
                push_u14(*sample_num, v);
                loop_num.extend_midi(v);
            }
            Self::SampleName { sample_num, name } => {
                push_u14(*sample_num, v);
                v.push(0); // Language tag length (0 is the only allowable value)
                let len = name.len().min(127);
                v.push(len as u8);
                v.extend_from_slice(&name.as_bytes()[0..len]);
            }
            Self::SampleNameRequest { sample_num } => {
                push_u14(*sample_num, v);
            }
        }
    }

    pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), &str> {
        Err("TODO: not implemented")
    }
}

/// The type of loop being described by a [`SampleDumpMsg`].
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ExtendedLoopType {
    /// A forward, unidirectional loop
    Forward = 0x00,
    /// Loop starts playing forward, gets to end and plays backward, repeating
    BiDirectional = 0x01,
    /// A unidirectional loop, upon key release the rest of the sample is played
    ForwardRelease = 0x02,
    /// A bidirectional loop, upon key release the rest of the sample is played
    BiDirectionalRelease = 0x03,
    /// A backward, unidirectional loop
    Backward = 0x40,
    /// Like BiDirectional, but starts playing in reverse
    BackwardBiDirectional = 0x41,
    /// A backward unidirectional loop, upon key release the rest of the sample after the loop is played backwards
    BackwardRelease = 0x42,
    /// A bidirectional loop, starting from the end playing backward, upon key release the rest of the sample after the loop is played backwards
    BackwardBiDirectionalRelease = 0x43,
    /// Backwards one-shot, no looping
    BackwardOneShot = 0x7E,
    /// Forwards one-shot, no looping
    OneShot = 0x7F,
}

#[cfg(test)]
mod tests {
    use crate::*;

    #[test]
    fn serialize_sample_dump_msg() {
        assert_eq!(
            MidiMsg::SystemExclusive {
                msg: SystemExclusiveMsg::UniversalNonRealTime {
                    device: DeviceID::AllCall,
                    msg: UniversalNonRealTimeMsg::ExtendedSampleDump(
                        ExtendedSampleDumpMsg::Header {
                            sample_num: 5,
                            format: 8,
                            sample_rate: 4000.5,
                            length: 2u64.pow(30),
                            sustain_loop_start: 2u64.pow(10),
                            sustain_loop_end: 2u64.pow(20),
                            loop_type: ExtendedLoopType::BiDirectionalRelease,
                            num_channels: 2
                        }
                    ),
                },
            }
            .to_midi(),
            vec![
                0xF0, 0x7E, 0x7F, // All call
                0x05, 0x05, // ExtendedSampleDump header
                05, 00, // Sample number
                8,  // format,
                0b0100000, 0b0011111, 0, 0, // 4000 LSB first
                0, 0, 0, 0x40, // 0.5 LSB first
                0, 0, 0, 0, 0b0000100, // Length
                0, 0b0001000, 0, 0, 0, // Sustain loop start
                0, 0, 0b1000000, 0, 0,    // Sustain loop end
                0x03, // Loop type
                2,    // Num channels
                0xF7
            ]
        );
    }
}