midi_msg/
general_midi.rs

1use core::convert::TryFrom;
2
3#[cfg(feature = "std")]
4use strum::{Display, EnumIter, EnumString};
5
6/// Used to turn General MIDI level 1 or 2 on, or turn them off.
7///
8/// Used in [`UniversalNonRealTimeMsg::GeneralMidi`](crate::UniversalNonRealTimeMsg::GeneralMidi)
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum GeneralMidi {
11    GM1 = 1,
12    GM2 = 3,
13    Off = 2,
14}
15
16/// The instrument that should be played when applying a [`ChannelVoiceMsg::ProgramChange`](crate::ChannelVoiceMsg::ProgramChange).
17///
18/// Use `GMSoundSet::Sound as u8` to use as the program number. For example:
19///
20/// ```
21/// # use midi_msg::*;
22/// MidiMsg::ChannelVoice {
23///     channel: Channel::Ch1,
24///     msg: ChannelVoiceMsg::ProgramChange {
25///         program: GMSoundSet::Vibraphone as u8
26///     }
27/// };
28/// ```
29///
30/// Should not be used when targeting channel 10.
31///
32/// As defined in General MIDI System Level 1 (MMA0007 / RP003).
33#[cfg_attr(feature = "std", derive(EnumIter, Display, EnumString))]
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35#[repr(u8)]
36pub enum GMSoundSet {
37    AcousticGrandPiano = 0,
38    BrightAcousticPiano = 1,
39    ElectricGrandPiano = 2,
40    HonkytonkPiano = 3,
41    ElectricPiano1 = 4,
42    ElectricPiano2 = 5,
43    Harpsichord = 6,
44    Clavi = 7,
45    Celesta = 8,
46    Glockenspiel = 9,
47    MusicBox = 10,
48    Vibraphone = 11,
49    Marimba = 12,
50    Xylophone = 13,
51    TubularBells = 14,
52    Dulcimer = 15,
53    DrawbarOrgan = 16,
54    PercussiveOrgan = 17,
55    RockOrgan = 18,
56    ChurchOrgan = 19,
57    ReedOrgan = 20,
58    Accordion = 21,
59    Harmonica = 22,
60    TangoAccordion = 23,
61    AcousticGuitarNylon = 24,
62    AcousticGuitarSteel = 25,
63    ElectricGuitarJazz = 26,
64    ElectricGuitarClean = 27,
65    ElectricGuitarMuted = 28,
66    OverdrivenGuitar = 29,
67    DistortionGuitar = 30,
68    GuitarHarmonics = 31,
69    AcousticBass = 32,
70    ElectricBassFinger = 33,
71    ElectricBassPick = 34,
72    FretlessBass = 35,
73    SlapBass1 = 36,
74    SlapBass2 = 37,
75    SynthBass1 = 38,
76    SynthBass2 = 39,
77    Violin = 40,
78    Viola = 41,
79    Cello = 42,
80    Contrabass = 43,
81    TremoloStrings = 44,
82    PizzicatoStrings = 45,
83    OrchestralHarp = 46,
84    Timpani = 47,
85    StringEnsemble1 = 48,
86    StringEnsemble2 = 49,
87    SynthStrings1 = 50,
88    SynthStrings2 = 51,
89    ChoirAahs = 52,
90    VoiceOohs = 53,
91    SynthVoice = 54,
92    OrchestraHit = 55,
93    Trumpet = 56,
94    Trombone = 57,
95    Tuba = 58,
96    MutedTrumpet = 59,
97    FrenchHorn = 60,
98    BrassSection = 61,
99    SynthBrass1 = 62,
100    SynthBrass2 = 63,
101    SopranoSax = 64,
102    AltoSax = 65,
103    TenorSax = 66,
104    BaritoneSax = 67,
105    Oboe = 68,
106    EnglishHorn = 69,
107    Bassoon = 70,
108    Clarinet = 71,
109    Piccolo = 72,
110    Flute = 73,
111    Recorder = 74,
112    PanFlute = 75,
113    BlownBottle = 76,
114    Shakuhachi = 77,
115    Whistle = 78,
116    Ocarina = 79,
117    Lead1 = 80,
118    Lead2 = 81,
119    Lead3 = 82,
120    Lead4 = 83,
121    Lead5 = 84,
122    Lead6 = 85,
123    Lead7 = 86,
124    Lead8 = 87,
125    Pad1 = 88,
126    Pad2 = 89,
127    Pad3 = 90,
128    Pad4 = 91,
129    Pad5 = 92,
130    Pad6 = 93,
131    Pad7 = 94,
132    Pad8 = 95,
133    FX1 = 96,
134    FX2 = 97,
135    FX3 = 98,
136    FX4 = 99,
137    FX5 = 100,
138    FX6 = 101,
139    FX7 = 102,
140    FX8 = 103,
141    Sitar = 104,
142    Banjo = 105,
143    Shamisen = 106,
144    Koto = 107,
145    Kalimba = 108,
146    Bagpipe = 109,
147    Fiddle = 110,
148    Shanai = 111,
149    TinkleBell = 112,
150    Agogo = 113,
151    SteelDrums = 114,
152    Woodblock = 115,
153    TaikoDrum = 116,
154    MelodicTom = 117,
155    SynthDrum = 118,
156    ReverseCymbal = 119,
157    GuitarFretNoise = 120,
158    BreathNoise = 121,
159    Seashore = 122,
160    BirdTweet = 123,
161    TelephoneRing = 124,
162    Helicopter = 125,
163    Applause = 126,
164    Gunshot = 127,
165}
166
167impl TryFrom<u8> for GMSoundSet {
168    type Error = &'static str;
169
170    fn try_from(value: u8) -> Result<Self, Self::Error> {
171        if value > 127 {
172            return Err("Invalid value for GMSoundSet");
173        }
174        Ok(unsafe { core::mem::transmute::<u8, GMSoundSet>(value) })
175    }
176}
177
178/// The General MIDI percussion sound to play for a given note number when targeting
179/// Channel 10.
180///
181/// For example:
182///
183/// ```
184/// # use midi_msg::*;
185/// MidiMsg::ChannelVoice {
186///     channel: Channel::Ch10,
187///     msg: ChannelVoiceMsg::NoteOn {
188///         note: GMPercussionMap::Vibraslap as u8,
189///         velocity: 127
190///     }
191/// };
192/// ```
193///
194/// As defined in General MIDI System Level 1 (MMA0007 / RP003).
195#[cfg_attr(feature = "std", derive(EnumIter, Display, EnumString))]
196#[derive(Debug, Clone, Copy, PartialEq, Eq)]
197#[repr(u8)]
198pub enum GMPercussionMap {
199    AcousticBassDrum = 35,
200    BassDrum1 = 36,
201    SideStick = 37,
202    AcousticSnare = 38,
203    HandClap = 39,
204    ElectricSnare = 40,
205    LowFloorTom = 41,
206    ClosedHiHat = 42,
207    HighFloorTom = 43,
208    PedalHiHat = 44,
209    LowTom = 45,
210    OpenHiHat = 46,
211    LowMidTom = 47,
212    HiMidTom = 48,
213    CrashCymbal1 = 49,
214    HighTom = 50,
215    RideCymbal1 = 51,
216    ChineseCymbal = 52,
217    RideBell = 53,
218    Tambourine = 54,
219    SplashCymbal = 55,
220    Cowbell = 56,
221    CrashCymbal2 = 57,
222    Vibraslap = 58,
223    RideCymbal2 = 59,
224    HiBongo = 60,
225    LowBongo = 61,
226    MuteHiConga = 62,
227    OpenHiConga = 63,
228    LowConga = 64,
229    HighTimbale = 65,
230    LowTimbale = 66,
231    HighAgogo = 67,
232    LowAgogo = 68,
233    Cabasa = 69,
234    Maracas = 70,
235    ShortWhistle = 71,
236    LongWhistle = 72,
237    ShortGuiro = 73,
238    LongGuiro = 74,
239    Claves = 75,
240    HiWoodBlock = 76,
241    LowWoodBlock = 77,
242    MuteCuica = 78,
243    OpenCuica = 79,
244    MuteTriangle = 80,
245    OpenTriangle = 81,
246}
247
248impl TryFrom<u8> for GMPercussionMap {
249    type Error = &'static str;
250
251    fn try_from(value: u8) -> Result<Self, Self::Error> {
252        if !(35..=81).contains(&value) {
253            return Err("Invalid value for GMPercussionMap");
254        }
255        Ok(unsafe { core::mem::transmute::<u8, GMPercussionMap>(value) })
256    }
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262
263    #[cfg(feature = "std")]
264    use std::str::FromStr;
265    #[cfg(feature = "std")]
266    use strum::IntoEnumIterator;
267
268    #[cfg(feature = "std")]
269    #[test]
270    fn gm_iter() {
271        for (i, inst) in GMSoundSet::iter().enumerate() {
272            //println!("{:?} {}",inst, inst as u8);
273            assert_eq!(inst as u8, i as u8);
274        }
275    }
276
277    #[cfg(feature = "std")]
278    #[test]
279    fn gm_from_string() {
280        assert_eq!(
281            GMSoundSet::TenorSax,
282            GMSoundSet::from_str("TenorSax").unwrap()
283        );
284    }
285
286    #[cfg(feature = "std")]
287    #[test]
288    fn gm_display() {
289        assert_eq!("TenorSax", format!("{}", GMSoundSet::TenorSax));
290    }
291
292    #[cfg(feature = "std")]
293    #[test]
294    fn gm_tostring() {
295        assert_eq!("TenorSax", GMSoundSet::TenorSax.to_string());
296    }
297
298    #[test]
299    fn gm_as_u8() {
300        assert_eq!(0, GMSoundSet::AcousticGrandPiano as u8);
301
302        assert_eq!(127, GMSoundSet::Gunshot as u8);
303    }
304
305    #[test]
306    fn gm_from_u8() {
307        assert_eq!(
308            GMSoundSet::AcousticGrandPiano,
309            GMSoundSet::try_from(0).unwrap()
310        );
311        assert_eq!(GMSoundSet::Gunshot, GMSoundSet::try_from(127).unwrap());
312    }
313
314    #[test]
315    fn gm_from_u8_invalid() {
316        assert!(GMSoundSet::try_from(128).is_err());
317    }
318
319    #[test]
320    fn gm_percussion_as_u8() {
321        assert_eq!(35, GMPercussionMap::AcousticBassDrum as u8);
322        assert_eq!(81, GMPercussionMap::OpenTriangle as u8);
323    }
324
325    #[test]
326    fn gm_percussion_from_u8() {
327        assert_eq!(
328            GMPercussionMap::AcousticBassDrum,
329            GMPercussionMap::try_from(35).unwrap()
330        );
331        assert_eq!(
332            GMPercussionMap::OpenTriangle,
333            GMPercussionMap::try_from(81).unwrap()
334        );
335    }
336
337    #[test]
338    fn gm_percussion_from_u8_invalid() {
339        assert!(GMPercussionMap::try_from(34).is_err());
340        assert!(GMPercussionMap::try_from(82).is_err());
341    }
342
343    #[cfg(feature = "std")]
344    #[test]
345    fn percussion_iter() {
346        for (i, perc) in GMPercussionMap::iter().enumerate() {
347            //println!("{:?} {}",inst, inst as u8);
348            assert_eq!(perc as u8, (i + 35) as u8);
349        }
350    }
351}