midi2/flex_data/
set_chord_name.rs

1use crate::{
2    detail::{common_properties, schema, BitOps},
3    flex_data::{self, UMP_MESSAGE_TYPE},
4    ux::u4,
5};
6
7const STATUS: u8 = 0x6;
8
9/// MIDI 2.0 Flex Data Set Chord Name Message
10///
11/// See the [module docs](crate::flex_data) for more info.
12#[midi2_proc::generate_message(Via(crate::flex_data::FlexData), FixedSize, MinSizeUmp(4))]
13struct SetChordName {
14    #[property(common_properties::UmpMessageTypeProperty<UMP_MESSAGE_TYPE>)]
15    ump_type: (),
16    #[property(common_properties::GroupProperty)]
17    group: crate::ux::u4,
18    #[property(flex_data::OptionalChannelProperty)]
19    optional_channel: Option<crate::ux::u4>,
20    #[property(flex_data::FormatProperty<{flex_data::COMPLETE_FORMAT}>)]
21    format: (),
22    #[property(flex_data::BankProperty<{flex_data::SETUP_AND_PERFORMANCE_BANK}>)]
23    bank: (),
24    #[property(flex_data::StatusProperty<{STATUS}>)]
25    status: (),
26    #[property(SharpsFlatsProperty<schema::Ump<0x0, 0xF000_0000, 0x0, 0x0>>)]
27    tonic_sharps_flats: SharpsFlats,
28    #[property(flex_data::tonic::TonicProperty<schema::Ump<0x0, 0x0F00_0000, 0x0, 0x0>>)]
29    tonic: flex_data::tonic::Tonic,
30    #[property(ChordTypeProperty<schema::Ump<0x0, 0x00FF_0000, 0x0, 0x0>>)]
31    chord_type: ChordType,
32    #[property(AlterationProperty<schema::Ump<0x0, 0x0000_FF00, 0x0, 0x0>>)]
33    chord_alteration1: Option<Alteration>,
34    #[property(AlterationProperty<schema::Ump<0x0, 0x0000_00FF, 0x0, 0x0>>)]
35    chord_alteration2: Option<Alteration>,
36    #[property(AlterationProperty<schema::Ump<0x0, 0x0, 0xFF00_0000, 0x0>>)]
37    chord_alteration3: Option<Alteration>,
38    #[property(AlterationProperty<schema::Ump<0x0, 0x0, 0x00FF_0000, 0x0>>)]
39    chord_alteration4: Option<Alteration>,
40    #[property(SharpsFlatsProperty<schema::Ump<0x0, 0x0, 0x0, 0xF000_0000>>)]
41    bass_sharps_flats: SharpsFlats,
42    #[property(flex_data::tonic::TonicProperty<schema::Ump<0x0, 0x0, 0x0, 0x0F00_0000>>)]
43    bass_note: flex_data::tonic::Tonic,
44    #[property(ChordTypeProperty<schema::Ump<0x0, 0x0, 0x0, 0x00FF_0000>>)]
45    bass_chord_type: ChordType,
46    #[property(AlterationProperty<schema::Ump<0x0, 0x0, 0x0, 0x0000_FF00>>)]
47    bass_alteration1: Option<Alteration>,
48    #[property(AlterationProperty<schema::Ump<0x0, 0x0, 0x0, 0x0000_00FF>>)]
49    bass_alteration2: Option<Alteration>,
50}
51
52impl<B: crate::buffer::Ump> flex_data::FlexDataMessage<B> for SetChordName<B> {}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum SharpsFlats {
56    DoubleSharp,
57    Sharp,
58    Natural,
59    Flat,
60    DoubleFlat,
61}
62
63struct SharpsFlatsProperty<S: schema::UmpSchema>(S);
64
65impl<B: crate::buffer::Ump, S: schema::UmpSchema> crate::detail::property::Property<B>
66    for SharpsFlatsProperty<S>
67{
68    type Type = SharpsFlats;
69}
70
71impl<'a, B: crate::buffer::Ump> crate::detail::property::ReadProperty<'a, B>
72    for SharpsFlatsProperty<schema::Ump<0x0, 0x0, 0x0, 0xF000_0000>>
73{
74    fn read(buffer: &'a B) -> Self::Type {
75        SharpsFlats::from_nibble(buffer.buffer()[3].nibble(0)).unwrap()
76    }
77    fn validate(buffer: &B) -> Result<(), crate::error::InvalidData> {
78        SharpsFlats::from_nibble(buffer.buffer()[3].nibble(0))?;
79        Ok(())
80    }
81}
82
83impl<B: crate::buffer::Ump + crate::buffer::BufferMut> crate::detail::property::WriteProperty<B>
84    for SharpsFlatsProperty<schema::Ump<0x0, 0x0, 0x0, 0xF000_0000>>
85{
86    fn write(buffer: &mut B, v: Self::Type) {
87        buffer.buffer_mut()[3].set_nibble(0, v.into_nibble());
88    }
89    fn validate(_: &Self::Type) -> Result<(), crate::error::InvalidData> {
90        Ok(())
91    }
92    fn default() -> Self::Type {
93        Default::default()
94    }
95}
96
97impl<'a, B: crate::buffer::Ump> crate::detail::property::ReadProperty<'a, B>
98    for SharpsFlatsProperty<schema::Ump<0x0, 0xF000_0000, 0x0, 0x0>>
99{
100    fn read(buffer: &'a B) -> Self::Type {
101        SharpsFlats::from_nibble(buffer.buffer()[1].nibble(0)).unwrap()
102    }
103    fn validate(buffer: &B) -> Result<(), crate::error::InvalidData> {
104        SharpsFlats::from_nibble(buffer.buffer()[1].nibble(0))?;
105        Ok(())
106    }
107}
108
109impl<B: crate::buffer::Ump + crate::buffer::BufferMut> crate::detail::property::WriteProperty<B>
110    for SharpsFlatsProperty<schema::Ump<0x0, 0xF000_0000, 0x0, 0x0>>
111{
112    fn write(buffer: &mut B, v: Self::Type) {
113        buffer.buffer_mut()[1].set_nibble(0, v.into_nibble());
114    }
115    fn validate(_: &Self::Type) -> Result<(), crate::error::InvalidData> {
116        Ok(())
117    }
118    fn default() -> Self::Type {
119        Default::default()
120    }
121}
122
123impl SharpsFlats {
124    fn from_nibble(nibble: u4) -> Result<SharpsFlats, crate::error::InvalidData> {
125        use SharpsFlats::*;
126        match u8::from(nibble) {
127            0x2 => Ok(DoubleSharp),
128            0x1 => Ok(Sharp),
129            0x0 => Ok(Natural),
130            0xF => Ok(Flat),
131            0xE => Ok(DoubleFlat),
132            _ => Err(crate::error::InvalidData(
133                "Couldn't interpret Sharps / Flats field",
134            )),
135        }
136    }
137    fn into_nibble(self) -> u4 {
138        use SharpsFlats::*;
139        u4::new(match self {
140            DoubleSharp => 0x2,
141            Sharp => 0x1,
142            Natural => 0x0,
143            Flat => 0xF,
144            DoubleFlat => 0xE,
145        })
146    }
147}
148
149impl core::default::Default for SharpsFlats {
150    /// Default value is [SharpsFlats::Natural]
151    fn default() -> Self {
152        SharpsFlats::Natural
153    }
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub enum ChordType {
158    ClearChord,
159    Major,
160    Major6th,
161    Major7th,
162    Major9th,
163    Major11th,
164    Major13th,
165    Minor,
166    Minor6th,
167    Minor7th,
168    Minor9th,
169    Minor11th,
170    Minor13th,
171    Dominant,
172    Dominant9th,
173    Dominant11th,
174    Dominant13th,
175    Augmented,
176    Augmented7th,
177    Diminished,
178    Diminished7th,
179    HalfDiminished,
180    MajorMinor,
181    Pedal,
182    Power,
183    Suspended2nd,
184    Suspended4th,
185}
186
187struct ChordTypeProperty<S: schema::UmpSchema>(S);
188
189impl<B: crate::buffer::Ump, S: schema::UmpSchema> crate::detail::property::Property<B>
190    for ChordTypeProperty<S>
191{
192    type Type = ChordType;
193}
194
195impl<'a, B: crate::buffer::Ump> crate::detail::property::ReadProperty<'a, B>
196    for ChordTypeProperty<schema::Ump<0x0, 0x00FF_0000, 0x0, 0x0>>
197{
198    fn validate(buffer: &B) -> Result<(), crate::error::InvalidData> {
199        ChordType::from_octet(buffer.buffer()[1].octet(1))?;
200        Ok(())
201    }
202    fn read(buffer: &'a B) -> Self::Type {
203        ChordType::from_octet(buffer.buffer()[1].octet(1)).unwrap()
204    }
205}
206
207impl<B: crate::buffer::Ump + crate::buffer::BufferMut> crate::detail::property::WriteProperty<B>
208    for ChordTypeProperty<schema::Ump<0x0, 0x00FF_0000, 0x0, 0x0>>
209{
210    fn validate(_v: &Self::Type) -> Result<(), crate::error::InvalidData> {
211        Ok(())
212    }
213    fn write(buffer: &mut B, v: Self::Type) {
214        buffer.buffer_mut()[1].set_octet(1, v.into_octet());
215    }
216    fn default() -> Self::Type {
217        Default::default()
218    }
219}
220
221impl<'a, B: crate::buffer::Ump> crate::detail::property::ReadProperty<'a, B>
222    for ChordTypeProperty<schema::Ump<0x0, 0x0, 0x0, 0x00FF_0000>>
223{
224    fn validate(buffer: &B) -> Result<(), crate::error::InvalidData> {
225        ChordType::from_octet(buffer.buffer()[3].octet(1))?;
226        Ok(())
227    }
228    fn read(buffer: &'a B) -> Self::Type {
229        ChordType::from_octet(buffer.buffer()[3].octet(1)).unwrap()
230    }
231}
232
233impl<B: crate::buffer::Ump + crate::buffer::BufferMut> crate::detail::property::WriteProperty<B>
234    for ChordTypeProperty<schema::Ump<0x0, 0x0, 0x0, 0x00FF_0000>>
235{
236    fn validate(_v: &Self::Type) -> Result<(), crate::error::InvalidData> {
237        Ok(())
238    }
239    fn write(buffer: &mut B, v: Self::Type) {
240        buffer.buffer_mut()[3].set_octet(1, v.into_octet());
241    }
242    fn default() -> Self::Type {
243        Default::default()
244    }
245}
246
247impl ChordType {
248    fn from_octet(octet: u8) -> Result<Self, crate::error::InvalidData> {
249        use ChordType::*;
250        match octet {
251            0x00 => Ok(ClearChord),
252            0x01 => Ok(Major),
253            0x02 => Ok(Major6th),
254            0x03 => Ok(Major7th),
255            0x04 => Ok(Major9th),
256            0x05 => Ok(Major11th),
257            0x06 => Ok(Major13th),
258            0x07 => Ok(Minor),
259            0x08 => Ok(Minor6th),
260            0x09 => Ok(Minor7th),
261            0x0A => Ok(Minor9th),
262            0x0B => Ok(Minor11th),
263            0x0C => Ok(Minor13th),
264            0x0D => Ok(Dominant),
265            0x0E => Ok(Dominant9th),
266            0x0F => Ok(Dominant11th),
267            0x10 => Ok(Dominant13th),
268            0x11 => Ok(Augmented),
269            0x12 => Ok(Augmented7th),
270            0x13 => Ok(Diminished),
271            0x14 => Ok(Diminished7th),
272            0x15 => Ok(HalfDiminished),
273            0x16 => Ok(MajorMinor),
274            0x17 => Ok(Pedal),
275            0x18 => Ok(Power),
276            0x19 => Ok(Suspended2nd),
277            0x1A => Ok(Suspended4th),
278            _ => Err(crate::error::InvalidData("Couldn't interpret Chord field")),
279        }
280    }
281
282    fn into_octet(self) -> u8 {
283        use ChordType::*;
284        match self {
285            ClearChord => 0x00,
286            Major => 0x01,
287            Major6th => 0x02,
288            Major7th => 0x03,
289            Major9th => 0x04,
290            Major11th => 0x05,
291            Major13th => 0x06,
292            Minor => 0x07,
293            Minor6th => 0x08,
294            Minor7th => 0x09,
295            Minor9th => 0x0A,
296            Minor11th => 0x0B,
297            Minor13th => 0x0C,
298            Dominant => 0x0D,
299            Dominant9th => 0x0E,
300            Dominant11th => 0x0F,
301            Dominant13th => 0x10,
302            Augmented => 0x11,
303            Augmented7th => 0x12,
304            Diminished => 0x13,
305            Diminished7th => 0x14,
306            HalfDiminished => 0x15,
307            MajorMinor => 0x16,
308            Pedal => 0x17,
309            Power => 0x18,
310            Suspended2nd => 0x19,
311            Suspended4th => 0x1A,
312        }
313    }
314}
315
316impl core::default::Default for ChordType {
317    /// Default value is [ChordType::ClearChord]
318    fn default() -> Self {
319        ChordType::ClearChord
320    }
321}
322
323#[derive(Debug, Clone, Copy, PartialEq, Eq)]
324pub enum Alteration {
325    Add(u4),
326    Subtract(u4),
327    Raise(u4),
328    Lower(u4),
329}
330
331struct AlterationProperty<S: schema::UmpSchema>(S);
332
333impl<B: crate::buffer::Ump, S: schema::UmpSchema> crate::detail::property::Property<B>
334    for AlterationProperty<S>
335{
336    type Type = Option<Alteration>;
337}
338
339macro_rules! alteration_property_impl {
340    ($ump1:expr,$ump2:expr,$ump3:expr,$ump4:expr,$buffer_index:expr,$octet_index:expr) => {
341        impl<'a, B: crate::buffer::Ump> crate::detail::property::ReadProperty<'a, B>
342            for AlterationProperty<schema::Ump<$ump1, $ump2, $ump3, $ump4>>
343        {
344            fn validate(buffer: &B) -> Result<(), crate::error::InvalidData> {
345                alteration_from_octet(buffer.buffer()[$buffer_index].octet($octet_index))?;
346                Ok(())
347            }
348            fn read(buffer: &'a B) -> Self::Type {
349                alteration_from_octet(buffer.buffer()[$buffer_index].octet($octet_index)).unwrap()
350            }
351        }
352
353        impl<B: crate::buffer::Ump + crate::buffer::BufferMut>
354            crate::detail::property::WriteProperty<B>
355            for AlterationProperty<schema::Ump<$ump1, $ump2, $ump3, $ump4>>
356        {
357            fn validate(_v: &Self::Type) -> Result<(), crate::error::InvalidData> {
358                Ok(())
359            }
360            fn write(buffer: &mut B, v: Self::Type) {
361                buffer.buffer_mut()[$buffer_index]
362                    .set_octet($octet_index, alteration_into_octet(v));
363            }
364            fn default() -> Self::Type {
365                Default::default()
366            }
367        }
368    };
369}
370
371alteration_property_impl!(0x0, 0x0000_FF00, 0x0, 0x0, 1, 2);
372alteration_property_impl!(0x0, 0x0000_00FF, 0x0, 0x0, 1, 3);
373alteration_property_impl!(0x0, 0x0, 0xFF00_0000, 0x0, 2, 0);
374alteration_property_impl!(0x0, 0x0, 0x00FF_0000, 0x0, 2, 1);
375alteration_property_impl!(0x0, 0x0, 0x0, 0x0000_FF00, 3, 2);
376alteration_property_impl!(0x0, 0x0, 0x0, 0x0000_00FF, 3, 3);
377
378fn alteration_from_octet(octet: u8) -> Result<Option<Alteration>, crate::error::InvalidData> {
379    use Alteration::*;
380    match u8::from(octet.nibble(0)) {
381        0x0 => Ok(None),
382        0x1 => Ok(Some(Add(octet.nibble(1)))),
383        0x2 => Ok(Some(Subtract(octet.nibble(1)))),
384        0x3 => Ok(Some(Raise(octet.nibble(1)))),
385        0x4 => Ok(Some(Lower(octet.nibble(1)))),
386        _ => Err(crate::error::InvalidData(
387            "Couldn't interpret alteration field",
388        )),
389    }
390}
391
392fn alteration_into_octet(alteration: Option<Alteration>) -> u8 {
393    use Alteration::*;
394    match alteration {
395        None => 0x0,
396        Some(Add(degree)) => 0x10 | u8::from(degree),
397        Some(Subtract(degree)) => 0x20 | u8::from(degree),
398        Some(Raise(degree)) => 0x30 | u8::from(degree),
399        Some(Lower(degree)) => 0x40 | u8::from(degree),
400    }
401}
402
403#[cfg(test)]
404mod tests {
405    use super::*;
406    use crate::{flex_data::tonic, traits::Grouped};
407    use pretty_assertions::assert_eq;
408
409    #[test]
410    fn setters() {
411        let mut message = SetChordName::<[u32; 4]>::new();
412        message.set_group(u4::new(0x7));
413        message.set_optional_channel(Some(u4::new(0xB)));
414        message.set_tonic_sharps_flats(SharpsFlats::Flat);
415        message.set_tonic(tonic::Tonic::G);
416        message.set_chord_type(ChordType::Major7th);
417        message.set_chord_alteration1(Some(Alteration::Raise(u4::new(0x5))));
418        message.set_chord_alteration2(Some(Alteration::Add(u4::new(0x9))));
419        message.set_chord_alteration3(Some(Alteration::Lower(u4::new(0xB))));
420        message.set_chord_alteration4(None);
421        message.set_bass_sharps_flats(SharpsFlats::Sharp);
422        message.set_bass_note(tonic::Tonic::A);
423        message.set_bass_chord_type(ChordType::Minor9th);
424        message.set_bass_alteration1(None);
425        message.set_bass_alteration2(Some(Alteration::Subtract(u4::new(0x0))));
426        assert_eq!(
427            message,
428            SetChordName([0xD70B_0006, 0xF703_3519, 0x4B00_0000, 0x110A_0020,]),
429        );
430    }
431
432    #[test]
433    fn channel() {
434        assert_eq!(
435            SetChordName::try_from(&[0xD70B_0006, 0xF703_3519, 0x4B00_0000, 0x110A_0020,][..])
436                .unwrap()
437                .optional_channel(),
438            Some(u4::new(0xB))
439        );
440    }
441
442    #[test]
443    fn tonic_sharps_flats() {
444        assert_eq!(
445            SetChordName::try_from(&[0xD70B_0006, 0xF703_3519, 0x4B00_0000, 0x110A_0020,][..])
446                .unwrap()
447                .tonic_sharps_flats(),
448            SharpsFlats::Flat,
449        );
450    }
451
452    #[test]
453    fn tonic() {
454        assert_eq!(
455            SetChordName::try_from(&[0xD70B_0006, 0xF703_3519, 0x4B00_0000, 0x110A_0020,][..])
456                .unwrap()
457                .tonic(),
458            tonic::Tonic::G,
459        );
460    }
461
462    #[test]
463    fn chord_type() {
464        assert_eq!(
465            SetChordName::try_from(&[0xD70B_0006, 0xF703_3519, 0x4B00_0000, 0x110A_0020,][..])
466                .unwrap()
467                .chord_type(),
468            ChordType::Major7th,
469        );
470    }
471
472    #[test]
473    fn chord_alteration1() {
474        assert_eq!(
475            SetChordName::try_from(&[0xD70B_0006, 0xF703_3519, 0x4B00_0000, 0x110A_0020,][..])
476                .unwrap()
477                .chord_alteration1(),
478            Some(Alteration::Raise(u4::new(0x5))),
479        );
480    }
481
482    #[test]
483    fn chord_alteration2() {
484        assert_eq!(
485            SetChordName::try_from(&[0xD70B_0006, 0xF703_3519, 0x4B00_0000, 0x110A_0020,][..])
486                .unwrap()
487                .chord_alteration2(),
488            Some(Alteration::Add(u4::new(0x9))),
489        );
490    }
491
492    #[test]
493    fn chord_alteration3() {
494        assert_eq!(
495            SetChordName::try_from(&[0xD70B_0006, 0xF703_3519, 0x4B00_0000, 0x110A_0020,][..])
496                .unwrap()
497                .chord_alteration3(),
498            Some(Alteration::Lower(u4::new(0xB))),
499        );
500    }
501
502    #[test]
503    fn chord_alteration4() {
504        assert_eq!(
505            SetChordName::try_from(&[0xD70B_0006, 0xF703_3519, 0x4B00_0000, 0x110A_0020,][..])
506                .unwrap()
507                .chord_alteration4(),
508            None,
509        );
510    }
511
512    #[test]
513    fn bass_sharps_flats() {
514        assert_eq!(
515            SetChordName::try_from(&[0xD70B_0006, 0xF703_3519, 0x4B00_0000, 0x110A_0020,][..])
516                .unwrap()
517                .bass_sharps_flats(),
518            SharpsFlats::Sharp,
519        );
520    }
521
522    #[test]
523    fn bass_note() {
524        assert_eq!(
525            SetChordName::try_from(&[0xD70B_0006, 0xF703_3519, 0x4B00_0000, 0x110A_0020,][..])
526                .unwrap()
527                .bass_note(),
528            tonic::Tonic::A,
529        );
530    }
531
532    #[test]
533    fn bass_chord_type() {
534        assert_eq!(
535            SetChordName::try_from(&[0xD70B_0006, 0xF703_3519, 0x4B00_0000, 0x110A_0020,][..])
536                .unwrap()
537                .bass_chord_type(),
538            ChordType::Minor9th,
539        );
540    }
541
542    #[test]
543    fn bass_alteration1() {
544        assert_eq!(
545            SetChordName::try_from(&[0xD70B_0006, 0xF703_3519, 0x4B00_0000, 0x110A_0020,][..])
546                .unwrap()
547                .bass_alteration1(),
548            None,
549        );
550    }
551
552    #[test]
553    fn bass_alteration2() {
554        assert_eq!(
555            SetChordName::try_from(&[0xD70B_0006, 0xF703_3519, 0x4B00_0000, 0x110A_0020,][..])
556                .unwrap()
557                .bass_alteration2(),
558            Some(Alteration::Subtract(u4::new(0x0))),
559        );
560    }
561}