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#[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 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 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}