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