Skip to main content

music21/instrument/
mod.rs

1//! Instruments: music21's `instrument` module.
2//!
3//! An [`Instrument`] is one of music21's instrument classes -- a piano, a
4//! clarinet in B-flat, a triangle -- with what an instrument of that class
5//! starts out as: its name and abbreviation, its General MIDI program and
6//! sound, the range it plays, the interval from what is written for it to
7//! what sounds, and for unpitched percussion the General MIDI drum it plays.
8//! The class survives as the instrument's *kind*, and a kind belongs to the
9//! families above it, so a clarinet is a woodwind and a triangle unpitched
10//! percussion ([`Instrument::is_a`]).
11//!
12//! An instrument is found by kind ([`Instrument::of_kind`]), by MIDI program
13//! ([`Instrument::from_midi_program`]) or by what a score calls it, in six
14//! languages and their abbreviations ([`Instrument::from_name`], music21's
15//! `fromString`), which also reads the key a transposing instrument is in:
16//! `"Clarinet in A"` is a clarinet sounding a minor third below what is
17//! written.
18//!
19//! What an instrument does to a stream it stands in is here too: moving the
20//! instruments notes keep as their own into the stream and back
21//! ([`unbundle_instruments`], [`bundle_instruments`]), folding together the
22//! instruments a part says twice ([`deduplicate`]) and splitting a score into
23//! a part for each instrument ([`partition_by_instrument`]).
24
25mod streams;
26mod tables;
27
28pub(crate) use streams::settle_by_offset;
29pub use streams::{bundle_instruments, deduplicate, partition_by_instrument, unbundle_instruments};
30
31use std::fmt;
32
33use crate::{
34    error::{Error, Result},
35    interval::Interval,
36    pitch::Pitch,
37};
38
39use tables::{KINDS, Kind};
40
41/// Which of music21's name tables [`Instrument::from_name`] reads: music21's
42/// `SearchLanguage`.
43#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
45pub enum SearchLanguage {
46    /// Every language and the abbreviations, English winning where two
47    /// disagree.
48    All,
49    /// English names.
50    English,
51    /// French names.
52    French,
53    /// German names.
54    German,
55    /// Italian names.
56    Italian,
57    /// Russian names, transliterated.
58    Russian,
59    /// Spanish names.
60    Spanish,
61    /// The standard abbreviations, an honorary language.
62    Abbreviation,
63}
64
65impl SearchLanguage {
66    /// Every language on its own, in music21's order, without
67    /// [`SearchLanguage::All`].
68    pub const EACH: [SearchLanguage; 7] = [
69        SearchLanguage::English,
70        SearchLanguage::French,
71        SearchLanguage::German,
72        SearchLanguage::Italian,
73        SearchLanguage::Russian,
74        SearchLanguage::Spanish,
75        SearchLanguage::Abbreviation,
76    ];
77
78    /// music21's name for the language: `"all"`, `"english"` and so on.
79    pub fn as_str(self) -> &'static str {
80        match self {
81            Self::All => "all",
82            Self::English => "english",
83            Self::French => "french",
84            Self::German => "german",
85            Self::Italian => "italian",
86            Self::Russian => "russian",
87            Self::Spanish => "spanish",
88            Self::Abbreviation => "abbreviation",
89        }
90    }
91
92    /// The language music21 calls by that name, in any case.
93    pub fn from_name(name: &str) -> Result<Self> {
94        let lower = name.to_lowercase();
95        std::iter::once(Self::All)
96            .chain(Self::EACH)
97            .find(|language| language.as_str() == lower)
98            .ok_or_else(|| {
99                Error::Instrument(format!("Chosen language {lower} not currently supported."))
100            })
101    }
102
103    fn table(self) -> &'static [(&'static str, &'static str)] {
104        match self {
105            Self::All => &tables::ALL,
106            Self::English => &tables::ENGLISH,
107            Self::French => &tables::FRENCH,
108            Self::German => &tables::GERMAN,
109            Self::Italian => &tables::ITALIAN,
110            Self::Russian => &tables::RUSSIAN,
111            Self::Spanish => &tables::SPANISH,
112            Self::Abbreviation => &tables::ABBREVIATION,
113        }
114    }
115}
116
117/// An instrument of one of music21's kinds: music21's `Instrument`.
118///
119/// ```
120/// use music21::instrument::{Instrument, SearchLanguage};
121///
122/// let clarinet = Instrument::from_name("Clarinet in A", SearchLanguage::All)?;
123/// assert_eq!(clarinet.kind(), "Clarinet");
124/// assert!(clarinet.is_a("WoodwindInstrument"));
125/// assert_eq!(clarinet.transposition().unwrap().directed_name(), "m-3");
126/// # Ok::<(), music21::Error>(())
127/// ```
128#[derive(Clone, Debug, PartialEq)]
129#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
130#[must_use]
131pub struct Instrument {
132    kind: String,
133    name: Option<String>,
134    abbreviation: Option<String>,
135    part_name: Option<String>,
136    part_abbreviation: Option<String>,
137    sound: Option<String>,
138    midi_program: Option<u8>,
139    midi_channel: Option<u8>,
140    lowest: Option<Pitch>,
141    highest: Option<Pitch>,
142    transposition: Option<Interval>,
143    percussion_map: bool,
144    percussion_pitch: Option<u8>,
145    string_pitches: Option<Vec<Pitch>>,
146    modifier: Option<String>,
147    #[cfg_attr(feature = "serde", serde(default))]
148    headless: Option<bool>,
149    #[cfg_attr(feature = "serde", serde(default))]
150    pitched: Option<bool>,
151    #[cfg_attr(feature = "serde", serde(default))]
152    part_id: Option<String>,
153    #[cfg_attr(feature = "serde", serde(default))]
154    instrument_id: Option<String>,
155}
156
157impl Default for Instrument {
158    /// music21's bare `Instrument`, which says nothing about itself.
159    fn default() -> Self {
160        Self::of_kind("Instrument").expect("music21's base class is in the table")
161    }
162}
163
164fn kind_named(class: &str) -> Option<&'static Kind> {
165    KINDS.iter().find(|kind| kind.class == class)
166}
167
168impl Instrument {
169    /// music21's bare `Instrument`, which says nothing about itself.
170    pub fn new() -> Self {
171        Self::default()
172    }
173
174    /// An instrument of the kind music21 names by that class, as a new one of
175    /// that class starts out: `"Piano"`, `"BassClarinet"`, `"Triangle"`.
176    pub fn of_kind(class: &str) -> Result<Self> {
177        let kind = kind_named(class)
178            .ok_or_else(|| Error::Instrument(format!("no instrument class {class}")))?;
179        let pitch = |name: Option<&str>| name.map(Pitch::from_name).transpose();
180        // A flag music21 sets in one class's `__init__` belongs to every
181        // kind below it too, so the whole chain is asked.
182        let flagged = |classes: &[&str]| {
183            std::iter::once(kind.class)
184                .chain(kind.parents.iter().copied())
185                .any(|class| classes.contains(&class))
186                .then_some(false)
187        };
188        Ok(Self {
189            kind: kind.class.to_string(),
190            name: kind.name.map(String::from),
191            abbreviation: kind.abbreviation.map(String::from),
192            part_name: None,
193            part_abbreviation: None,
194            sound: kind.sound.map(String::from),
195            midi_program: kind.midi_program,
196            midi_channel: kind.midi_channel,
197            lowest: pitch(kind.lowest)?,
198            highest: pitch(kind.highest)?,
199            transposition: kind.transposition.map(Interval::from_name).transpose()?,
200            percussion_map: kind.percussion_map,
201            percussion_pitch: kind.percussion_pitch,
202            string_pitches: tables::STRING_PITCHES
203                .iter()
204                .find(|(named, _)| *named == kind.class)
205                .map(|(_, names)| names.iter().map(Pitch::from_name).collect())
206                .transpose()?,
207            modifier: tables::MODIFIERS
208                .iter()
209                .find(|(named, ..)| *named == kind.class)
210                .map(|(_, modifier, ..)| modifier.to_string()),
211            headless: flagged(&tables::HEADLESS),
212            pitched: flagged(&tables::PITCHED),
213            part_id: None,
214            instrument_id: None,
215        })
216    }
217
218    /// The id of the part this instrument plays, as a score writes it:
219    /// music21's `partId`, `P1` in a score read from MusicXML.
220    pub fn part_id(&self) -> Option<&str> {
221        self.part_id.as_deref()
222    }
223
224    /// Sets the id of the part this instrument plays.
225    pub fn set_part_id(&mut self, id: Option<String>) {
226        self.part_id = id;
227    }
228
229    /// The instrument's own id within its part, `P1-I1`: music21's
230    /// `instrumentId`.
231    pub fn instrument_id(&self) -> Option<&str> {
232        self.instrument_id.as_deref()
233    }
234
235    /// Sets the instrument's own id.
236    pub fn set_instrument_id(&mut self, id: Option<String>) {
237        self.instrument_id = id;
238    }
239
240    /// Every kind of instrument music21 has a class for, families first.
241    pub fn kinds() -> impl Iterator<Item = &'static str> {
242        KINDS.iter().map(|kind| kind.class)
243    }
244
245    /// The instrument music21 makes of a General MIDI program, 0 to 127,
246    /// with its program set to that number: music21's
247    /// `instrumentFromMidiProgram`.
248    pub fn from_midi_program(program: u8) -> Result<Self> {
249        let class = tables::MIDI_PROGRAMS
250            .iter()
251            .find(|(number, _)| *number == program)
252            .map(|(_, class)| *class)
253            .ok_or_else(|| {
254                Error::Instrument(format!("No instrument found for MIDI program {program}"))
255            })?;
256        let mut made = Self::of_kind(class)?;
257        made.midi_program = Some(program);
258        Ok(made)
259    }
260
261    /// The instrument a score's name for one means, read the way music21's
262    /// `fromString` reads it.
263    ///
264    /// The name is taken apart into every run of its words, and the longest
265    /// run naming an instrument wins, a later one breaking a tie unless the
266    /// one found is already of its kind -- so `"Bb Piccolo Trumpet"` is a
267    /// trumpet, not a piccolo. The instrument is named with the text as
268    /// given. For an instrument that comes in several keys, a run
269    /// naming a pitch sets its transposition: `"Horn in F"`, `"Klarinette in
270    /// B."`.
271    pub fn from_name(text: &str, language: SearchLanguage) -> Result<Self> {
272        let table = language.table();
273        let cleaned: String = text
274            .replace('.', " ")
275            .to_lowercase()
276            .chars()
277            .filter(|c| !c.is_ascii_punctuation())
278            .collect();
279        let runs = word_runs(&cleaned);
280
281        let mut best: Option<(Self, String)> = None;
282        for run in &runs {
283            let Some((_, class)) = table.iter().find(|(name, _)| name == run) else {
284                continue;
285            };
286            let candidate = Self::of_kind(class)?;
287            let Some(candidate_name) = candidate.best_name().map(str::to_lowercase) else {
288                continue;
289            };
290            let replaces = match &best {
291                None => true,
292                Some((found, found_name)) => {
293                    candidate_name.split_whitespace().count()
294                        >= found_name.split_whitespace().count()
295                        && !found.is_a(class)
296                }
297            };
298            if replaces {
299                let mut named = candidate;
300                named.name = Some(text.to_string());
301                best = Some((named, candidate_name));
302            }
303        }
304        let Some((mut found, found_name)) = best else {
305            return Err(Error::Instrument(format!(
306                "Could not match string with instrument: {text}"
307            )));
308        };
309        let Some((_, keys)) = tables::TRANSPOSITIONS
310            .iter()
311            .find(|(name, _)| *name == found_name)
312        else {
313            return Ok(found);
314        };
315        for run in &runs {
316            let Some((_, pitch)) = tables::PITCH_NAMES.iter().find(|(name, _)| name == run) else {
317                continue;
318            };
319            if let Some((_, interval)) = keys.iter().find(|(key, _)| key == pitch) {
320                found.transposition = Some(Interval::from_name(interval)?);
321                break;
322            }
323        }
324        Ok(found)
325    }
326
327    /// The kind of instrument this is: music21's class name for it.
328    pub fn kind(&self) -> &str {
329        &self.kind
330    }
331
332    /// music21's own name for this kind of instrument, where it is one
333    /// music21 has a class for: the name as the table spells it, which is
334    /// what `is_a` and the class lineage answer with.
335    pub fn class_name(&self) -> Option<&'static str> {
336        kind_named(&self.kind).map(|kind| kind.class)
337    }
338
339    /// The families this kind belongs to, nearest first and ending with
340    /// `Instrument`: a clarinet is a `WoodwindInstrument`.
341    pub fn families(&self) -> &'static [&'static str] {
342        kind_named(&self.kind).map_or(&[], |kind| kind.parents)
343    }
344
345    /// Whether this is an instrument of that kind or of a kind in that
346    /// family, as music21's `isinstance` answers.
347    pub fn is_a(&self, class: &str) -> bool {
348        self.kind == class || self.families().contains(&class)
349    }
350
351    /// The instrument's name: music21's `instrumentName`.
352    pub fn name(&self) -> Option<&str> {
353        self.name.as_deref()
354    }
355
356    /// Sets the instrument's name, or takes it away.
357    pub fn set_name(&mut self, name: Option<String>) {
358        self.name = name;
359    }
360
361    /// The instrument's abbreviation: music21's `instrumentAbbreviation`.
362    pub fn abbreviation(&self) -> Option<&str> {
363        self.abbreviation.as_deref()
364    }
365
366    /// Sets the abbreviation, or takes it away.
367    pub fn set_abbreviation(&mut self, abbreviation: Option<String>) {
368        self.abbreviation = abbreviation;
369    }
370
371    /// What the part this instrument plays is called, where that is not
372    /// the instrument's name: music21's `partName`.
373    pub fn part_name(&self) -> Option<&str> {
374        self.part_name.as_deref()
375    }
376
377    /// Sets what the part is called.
378    pub fn set_part_name(&mut self, name: Option<String>) {
379        self.part_name = name;
380    }
381
382    /// The part's abbreviation: music21's `partAbbreviation`.
383    pub fn part_abbreviation(&self) -> Option<&str> {
384        self.part_abbreviation.as_deref()
385    }
386
387    /// Sets the part's abbreviation.
388    pub fn set_part_abbreviation(&mut self, abbreviation: Option<String>) {
389        self.part_abbreviation = abbreviation;
390    }
391
392    /// The best name there is for this instrument: the part's name, the
393    /// part's abbreviation, the instrument's name or its abbreviation, the
394    /// first of those given. music21's `bestName`.
395    pub fn best_name(&self) -> Option<&str> {
396        self.part_name
397            .as_deref()
398            .or(self.part_abbreviation.as_deref())
399            .or(self.name.as_deref())
400            .or(self.abbreviation.as_deref())
401    }
402
403    /// The sound's MusicXML id, `"wind.reed.clarinet.bflat"` and the like:
404    /// music21's `instrumentSound`.
405    pub fn sound(&self) -> Option<&str> {
406        self.sound.as_deref()
407    }
408
409    /// Sets the sound's id.
410    pub fn set_sound(&mut self, sound: Option<String>) {
411        self.sound = sound;
412    }
413
414    /// The General MIDI program, counted from nought: music21's `midiProgram`.
415    pub fn midi_program(&self) -> Option<u8> {
416        self.midi_program
417    }
418
419    /// Sets the General MIDI program.
420    pub fn set_midi_program(&mut self, program: Option<u8>) {
421        self.midi_program = program;
422    }
423
424    /// The MIDI channel, counted from nought, 9 being the drums: music21's
425    /// `midiChannel`.
426    pub fn midi_channel(&self) -> Option<u8> {
427        self.midi_channel
428    }
429
430    /// Sets the MIDI channel.
431    pub fn set_midi_channel(&mut self, channel: Option<u8>) {
432        self.midi_channel = channel;
433    }
434
435    /// The lowest note the instrument plays, as it sounds: music21's
436    /// `lowestNote`.
437    pub fn lowest(&self) -> Option<&Pitch> {
438        self.lowest.as_ref()
439    }
440
441    /// Sets the lowest note.
442    pub fn set_lowest(&mut self, pitch: Option<Pitch>) {
443        self.lowest = pitch;
444    }
445
446    /// The highest note the instrument plays: music21's `highestNote`.
447    pub fn highest(&self) -> Option<&Pitch> {
448        self.highest.as_ref()
449    }
450
451    /// Sets the highest note.
452    pub fn set_highest(&mut self, pitch: Option<Pitch>) {
453        self.highest = pitch;
454    }
455
456    /// The interval from what is written for the instrument to what sounds:
457    /// a B-flat clarinet sounds a major second below, `M-2`. music21's
458    /// `transposition`.
459    pub fn transposition(&self) -> Option<&Interval> {
460        self.transposition.as_ref()
461    }
462
463    /// Sets the transposition, or takes it away.
464    pub fn set_transposition(&mut self, interval: Option<Interval>) {
465        self.transposition = interval;
466    }
467
468    /// What sounds when `written` is played: the pitch moved by the
469    /// transposition, or itself for an instrument that does not transpose.
470    pub fn sounding(&self, written: &Pitch) -> Result<Pitch> {
471        match &self.transposition {
472            Some(interval) => interval.transpose_pitch(written),
473            None => Ok(written.clone()),
474        }
475    }
476
477    /// What is written for `sounding` to be heard.
478    pub fn written(&self, sounding: &Pitch) -> Result<Pitch> {
479        match &self.transposition {
480            Some(interval) => interval.reversed()?.transpose_pitch(sounding),
481            None => Ok(sounding.clone()),
482        }
483    }
484
485    /// Whether the instrument is one of General MIDI's percussion map:
486    /// music21's `inGMPercMap`.
487    pub fn in_percussion_map(&self) -> bool {
488        self.percussion_map
489    }
490
491    /// The General MIDI drum the instrument plays, for unpitched percussion:
492    /// music21's `percMapPitch`.
493    pub fn percussion_pitch(&self) -> Option<u8> {
494        self.percussion_pitch
495    }
496
497    /// Sets the General MIDI drum the instrument plays.
498    pub fn set_percussion_pitch(&mut self, pitch: Option<u8>) {
499        self.percussion_pitch = pitch;
500    }
501
502    /// Whether the drum is struck muted: music21's `muted`, which is the
503    /// *drum* it plays and not a flag beside it -- a muted conga drum is
504    /// key 62 where an open one is 63.
505    ///
506    /// Three of music21's classes carry it, and `None` here is an
507    /// instrument that is not one of them.
508    #[must_use]
509    pub fn muted(&self) -> Option<bool> {
510        let (muted, _, _) = Self::muting(&self.kind)?;
511        Some(self.percussion_pitch == Some(muted))
512    }
513
514    /// Strikes it muted or open, where it is an instrument that can be.
515    ///
516    /// A conga drum's muting is the high drum's alone, so asking it of the
517    /// low one does nothing, exactly as music21's setter does nothing
518    /// unless the modifier is `high`.
519    pub fn set_muted(&mut self, muted: bool) {
520        let Some((muted_key, open_key, only)) = Self::muting(&self.kind) else {
521            return;
522        };
523        if only.is_some_and(|modifier| self.modifier() != Some(modifier)) {
524            return;
525        }
526        self.percussion_pitch = Some(if muted { muted_key } else { open_key });
527    }
528
529    /// Whether the tambourine has no drum head -- the rock and pop kind, a
530    /// ring of jingles -- where a classical one is headed: music21's
531    /// `headless`, which only its `Tambourine` carries, so `None` is an
532    /// instrument that is not one.
533    ///
534    /// No document says it: MIDI and MusicXML both write a tambourine the
535    /// same either way, and music21's MIDI reader takes every one as
536    /// headless because most such files are rock.
537    #[must_use]
538    pub fn headless(&self) -> Option<bool> {
539        self.headless
540    }
541
542    /// Says whether the tambourine is headless, where it is a tambourine at
543    /// all; asked of anything else it does nothing, as [`Instrument::muted`]
544    /// does.
545    pub fn set_headless(&mut self, headless: bool) {
546        if self.headless.is_some() {
547            self.headless = Some(headless);
548        }
549    }
550
551    /// Whether the whistle sounds a definite pitch -- a tin whistle does, a
552    /// referee's does not: music21's `pitched`, which only its `Whistle`
553    /// carries, so `None` is an instrument that is not one.
554    #[must_use]
555    pub fn pitched(&self) -> Option<bool> {
556        self.pitched
557    }
558
559    /// Says whether the whistle is pitched, where it is a whistle at all.
560    pub fn set_pitched(&mut self, pitched: bool) {
561        if self.pitched.is_some() {
562            self.pitched = Some(pitched);
563        }
564    }
565
566    /// The pair of drums a kind is struck muted and open, and the modifier
567    /// its muting is of where it has one.
568    fn muting(kind: &str) -> Option<(u8, u8, Option<&'static str>)> {
569        tables::MUTED
570            .iter()
571            .find(|(class, _, _, _)| *class == kind)
572            .map(|(_, muted, open, only)| (*muted, *open, *only))
573    }
574
575    /// Says whether the instrument is in the General MIDI percussion map.
576    pub fn set_in_percussion_map(&mut self, in_map: bool) {
577        self.percussion_map = in_map;
578    }
579
580    /// The open strings, lowest first unless the tuning is reentrant, for a
581    /// string instrument music21 tunes: music21's `stringPitches`.
582    ///
583    /// ```
584    /// use music21::Instrument;
585    ///
586    /// let violin = Instrument::of_kind("Violin")?;
587    /// let open: Vec<String> = violin
588    ///     .string_pitches()
589    ///     .unwrap()
590    ///     .iter()
591    ///     .map(|pitch| pitch.name_with_octave())
592    ///     .collect();
593    /// assert_eq!(open, ["G3", "D4", "A4", "E5"]);
594    /// # Ok::<(), music21::Error>(())
595    /// ```
596    pub fn string_pitches(&self) -> Option<&[Pitch]> {
597        self.string_pitches.as_deref()
598    }
599
600    /// Retunes the open strings, a scordatura.
601    pub fn set_string_pitches(&mut self, pitches: Vec<Pitch>) {
602        self.string_pitches = Some(pitches);
603    }
604
605    /// How an unpitched percussion instrument is played, such as `"low"` for
606    /// a bongo or `"rimshot"` for a snare: music21's `modifier`.
607    pub fn modifier(&self) -> Option<&str> {
608        self.modifier.as_deref()
609    }
610
611    /// Sets how the instrument is played. A modifier its drum map knows
612    /// changes the drum it plays, and is written back as that drum's own name
613    /// for it, so a woodblock set to `"LO"` plays the low block and says
614    /// `"low"`.
615    ///
616    /// ```
617    /// use music21::Instrument;
618    ///
619    /// let mut woodblock = Instrument::of_kind("Woodblock")?;
620    /// woodblock.set_modifier("LO");
621    /// assert_eq!(woodblock.modifier(), Some("low"));
622    /// assert_eq!(woodblock.percussion_pitch(), Some(77));
623    /// # Ok::<(), music21::Error>(())
624    /// ```
625    pub fn set_modifier(&mut self, modifier: &str) {
626        let mut modifier = modifier.trim().to_lowercase();
627        let table = std::iter::once(self.kind.as_str())
628            .chain(self.families().iter().copied())
629            .find_map(|class| tables::MODIFIERS.iter().find(|(named, ..)| *named == class));
630        if self.percussion_map
631            && let Some((_, _, to_pitch, to_name)) = table
632            && let Some((_, pitch)) = to_pitch.iter().find(|(named, _)| *named == modifier)
633        {
634            self.percussion_pitch = Some(*pitch);
635            if let Some((_, named)) = to_name.iter().find(|(drum, _)| drum == pitch) {
636                modifier = (*named).to_string();
637            }
638        }
639        self.modifier = Some(modifier);
640    }
641
642    /// Takes a MIDI channel no instrument in `used` has, and returns it:
643    /// music21's `autoAssignMidiChannel`.
644    ///
645    /// Unpitched percussion goes to channel 9 when that is free; otherwise
646    /// the lowest free channel below `max_channels` that is not a drum
647    /// channel is taken. It is an error when every channel is in use; where
648    /// none is free the answer is 0 and no channel is set, as music21 answers.
649    pub fn auto_assign_midi_channel(&mut self, used: &[u8], max_channels: u8) -> Result<u8> {
650        let used: std::collections::BTreeSet<u8> = used.iter().copied().collect();
651        if self.is_a("UnpitchedPercussion") && !used.contains(&9) {
652            self.midi_channel = Some(9);
653            return Ok(9);
654        }
655        if used.is_empty() {
656            self.midi_channel = Some(0);
657            return Ok(0);
658        }
659        if used.len() >= usize::from(max_channels).saturating_sub(1) {
660            return Err(Error::Instrument(
661                "we are out of midi channels! help!".to_string(),
662            ));
663        }
664        for channel in 0..max_channels {
665            if used.contains(&channel) || channel % 16 == 9 {
666                continue;
667            }
668            self.midi_channel = Some(channel);
669            return Ok(channel);
670        }
671        Ok(0)
672    }
673
674    /// Every name a score may call this kind of instrument in one language,
675    /// or in each of them: music21's `getAllNamesForInstrument`.
676    ///
677    /// The names are those of the nearest kind in the instrument's family
678    /// that the tables name at all -- its own, or for a guitar, which they do
679    /// not name, a string instrument's -- whatever the instrument itself is
680    /// called.
681    pub fn all_names(&self, language: SearchLanguage) -> Vec<(SearchLanguage, Vec<&'static str>)> {
682        // An instrument no table names by its kind is looked up by its own
683        // name, as the class it would be.
684        let named = std::iter::once(self.kind.as_str())
685            .chain(self.families().iter().copied())
686            .find(|class| tables::ALL.iter().any(|(_, named)| named == class))
687            .or(self.name.as_deref());
688        let languages: &[SearchLanguage] = match language {
689            SearchLanguage::All => &SearchLanguage::EACH,
690            _ => std::slice::from_ref(&language),
691        };
692        languages
693            .iter()
694            .map(|language| {
695                let names = language
696                    .table()
697                    .iter()
698                    .filter(|(_, class)| Some(*class) == named)
699                    .map(|(name, _)| *name)
700                    .collect();
701                (*language, names)
702            })
703            .collect()
704    }
705}
706
707impl fmt::Display for Instrument {
708    /// music21's `str` of an instrument, less the part id a score gives it:
709    /// the part's name where it differs from the instrument's, then the
710    /// instrument's name.
711    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
712        if let Some(part) = &self.part_name
713            && self.name.as_ref() != Some(part)
714        {
715            write!(f, "{part}: ")?;
716        }
717        if let Some(name) = &self.name {
718            f.write_str(name)?;
719        }
720        Ok(())
721    }
722}
723
724/// Every run of consecutive words in `text`, shortest first and left to
725/// right within a length: music21's `_combinations`.
726fn word_runs(text: &str) -> Vec<String> {
727    let words: Vec<&str> = text.split_whitespace().collect();
728    let mut runs = Vec::new();
729    for size in 1..=words.len() {
730        for start in 0..=words.len() - size {
731            runs.push(words[start..start + size].join(" "));
732        }
733    }
734    runs
735}
736
737/// What an ensemble of so many players is called, from `"no performers"` to
738/// `"centet"`: music21's `ensembleNameBySize`, which is `None` past a hundred.
739pub fn ensemble_name_by_size(players: usize) -> Option<&'static str> {
740    tables::ENSEMBLE_NAMES.get(players).copied()
741}
742
743/// The instrument a key of the General MIDI percussion map plays, played
744/// the way that key says: music21's
745/// `midi.percussion.PercussionMapper.midiPitchToInstrument`.
746///
747/// A key is 0 to 127 and the map runs from 27 to 87. Where the instrument
748/// is played more than one way the modifier is the one that key plays, so
749/// key 36 is a bass drum played `"midi-1"` and key 35 the same drum played
750/// `"acoustic"`. Nine of the map's sounds are not instruments at all --
751/// a hand clap, a metronome click, a scratch -- and each of those is a
752/// bare unpitched percussion instrument under the sound's own name
753/// (music21's `namedSoundMapping`). A key the map says nothing of is an
754/// error, as it raises there; [`for_percussion_key_or_unmapped`] is
755/// music21's `raiseIfUnmapped=False`.
756///
757/// **The key is the drum, not the class's own.** music21 writes the key
758/// onto the instrument after the modifier, so a key that is the muted one
759/// of a pair stays muted where its class starts on the open one: key 62 is
760/// a conga drum with the *high* drum's modifier struck muted, where 63 is
761/// the same modifier struck open.
762///
763/// ```
764/// use music21::instrument::for_percussion_key;
765///
766/// let drum = for_percussion_key(36)?;
767/// assert_eq!(drum.kind(), "BassDrum");
768/// assert_eq!(drum.modifier(), Some("midi-1"));
769/// assert_eq!(drum.percussion_pitch(), Some(36));
770///
771/// let muted = for_percussion_key(62)?;
772/// assert_eq!((muted.modifier(), muted.muted()), (Some("high"), Some(true)));
773///
774/// let clap = for_percussion_key(39)?;
775/// assert_eq!(clap.name(), Some("Hand Clap"));
776///
777/// assert!(for_percussion_key(22).is_err());
778/// # Ok::<(), music21::Error>(())
779/// ```
780///
781/// # Errors
782///
783/// A key the map names neither an instrument nor a sound for.
784pub fn for_percussion_key(key: u8) -> Result<Instrument> {
785    percussion_key_instrument(key)
786        .ok_or_else(|| Error::Instrument(format!("{key} does not map to a valid instrument!")))?
787}
788
789/// The instrument a key of the General MIDI percussion map plays, and a
790/// bare unpitched percussion instrument carrying the key where the map
791/// names nothing at all: music21's `midiPitchToInstrument` asked with
792/// `raiseIfUnmapped=False`, which is how its MIDI reader asks.
793///
794/// ```
795/// use music21::instrument::for_percussion_key_or_unmapped;
796///
797/// let bare = for_percussion_key_or_unmapped(22);
798/// assert_eq!(bare.kind(), "UnpitchedPercussion");
799/// assert_eq!(bare.name(), Some("Percussion"));
800/// assert_eq!(bare.percussion_pitch(), Some(22));
801/// ```
802pub fn for_percussion_key_or_unmapped(key: u8) -> Instrument {
803    percussion_key_instrument(key)
804        .unwrap_or_else(|| {
805            Instrument::of_kind("UnpitchedPercussion").map(|mut bare| {
806                bare.set_percussion_pitch(Some(key));
807                bare
808            })
809        })
810        .unwrap_or_default()
811}
812
813/// Every key of the General MIDI percussion map that names an instrument
814/// class, in key order: music21's `reverseInstrumentMapping` read as its
815/// keys, which is how a caller listing the map asks for one class per key
816/// without the named sounds among them.
817pub fn percussion_keys() -> impl Iterator<Item = u8> {
818    tables::PERCUSSION_KEYS.iter().map(|(key, _)| *key)
819}
820
821/// Every General MIDI percussion sound that is no instrument, each with
822/// the name music21 gives it: music21's `namedSoundMapping`, a hand clap
823/// and a metronome among them.
824pub fn named_percussion_sounds() -> impl Iterator<Item = (u8, &'static str)> {
825    tables::NAMED_SOUNDS.iter().copied()
826}
827
828/// The instrument or the named sound a key plays, and nothing for a key
829/// the map says nothing of. The inner result is the class lookup, which
830/// cannot fail for a class in the table.
831fn percussion_key_instrument(key: u8) -> Option<Result<Instrument>> {
832    if let Some((_, kind)) = tables::PERCUSSION_KEYS
833        .iter()
834        .find(|(named, _)| *named == key)
835    {
836        return Some(Instrument::of_kind(kind).map(|mut made| {
837            // music21 writes the modifier that key plays onto the
838            // instrument it built, which moves the drum it plays with it,
839            // and then writes the key itself -- so a muted drum is the key
840            // struck and not the one its modifier names.
841            if let Some((_, _, _, by_key)) =
842                tables::MODIFIERS.iter().find(|(named, ..)| named == kind)
843                && let Some((_, modifier)) = by_key.iter().find(|(drum, _)| *drum == key)
844            {
845                made.set_modifier(modifier);
846            }
847            made.set_percussion_pitch(Some(key));
848            made
849        }));
850    }
851    let (_, sound) = tables::NAMED_SOUNDS
852        .iter()
853        .find(|(named, _)| *named == key)?;
854    Some(Instrument::of_kind("UnpitchedPercussion").map(|mut made| {
855        made.set_name(Some((*sound).to_string()));
856        made.set_percussion_pitch(Some(key));
857        made
858    }))
859}
860
861#[cfg(test)]
862mod tests {
863    use super::*;
864
865    // Every expectation here was read off music21 11.0.0b9.
866
867    #[test]
868    fn an_instrument_of_no_named_kind_is_looked_up_by_its_name() {
869        let mut violin = Instrument::new();
870        violin.set_name(Some("Violin".to_string()));
871        assert_eq!(
872            violin.all_names(SearchLanguage::English),
873            [(SearchLanguage::English, vec!["violin", "violins"])]
874        );
875    }
876
877    #[test]
878    fn an_instrument_starts_out_as_its_class_does() {
879        let piano = Instrument::of_kind("Piano").unwrap();
880        assert_eq!(piano.name(), Some("Piano"));
881        assert_eq!(piano.midi_program(), Some(0));
882        assert_eq!(piano.lowest().unwrap().name_with_octave(), "A0");
883        assert!(piano.is_a("KeyboardInstrument"));
884        let saxophone = Instrument::of_kind("AltoSaxophone").unwrap();
885        assert_eq!(saxophone.transposition().unwrap().directed_name(), "M-6");
886        assert_eq!(
887            saxophone
888                .sounding(&Pitch::from_name("C5").unwrap())
889                .unwrap()
890                .name_with_octave(),
891            "Eb4"
892        );
893        assert!(Instrument::of_kind("Kazoo").is_err());
894    }
895
896    #[test]
897    fn a_score_s_name_for_an_instrument_is_read_as_music21_reads_it() {
898        let found = |text: &str| Instrument::from_name(text, SearchLanguage::All).unwrap();
899        assert_eq!(found("Contrabassoon").kind(), "Contrabassoon");
900        assert_eq!(found("Bb Piccolo Trumpet").kind(), "Trumpet");
901        let clarinet = found("Klarinette in B.");
902        assert_eq!(clarinet.kind(), "Clarinet");
903        assert_eq!(clarinet.name(), Some("Klarinette in B."));
904        assert_eq!(clarinet.transposition().unwrap().directed_name(), "M-2");
905        assert_eq!(found("Cl.").kind(), "Clarinet");
906        assert!(Instrument::from_name("kazoo concerto", SearchLanguage::All).is_err());
907        assert!(Instrument::from_name("Klarinette", SearchLanguage::French).is_err());
908    }
909
910    #[test]
911    fn a_channel_is_found_as_music21_finds_one() {
912        let mut violin = Instrument::of_kind("Violin").unwrap();
913        assert_eq!(
914            violin
915                .auto_assign_midi_channel(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 16)
916                .unwrap(),
917            12
918        );
919        let mut drum = Instrument::of_kind("Triangle").unwrap();
920        assert_eq!(drum.auto_assign_midi_channel(&[0, 1], 16).unwrap(), 9);
921        let full: Vec<u8> = (0..16).collect();
922        assert!(violin.auto_assign_midi_channel(&full, 16).is_err());
923        assert_eq!(violin.auto_assign_midi_channel(&full, 32).unwrap(), 16);
924        assert_eq!(ensemble_name_by_size(4), Some("quartet"));
925    }
926
927    #[test]
928    fn a_percussion_key_is_the_drum_that_key_strikes_and_not_the_class_s_own() {
929        // music21 writes the key onto the instrument *after* the modifier,
930        // so a key that is the muted one of a pair stays the muted one
931        // where its class starts on the open one -- and a muted conga is
932        // key 62 with the *high* drum's modifier, 63 being the open high
933        // drum. Read off music21 11.0.0b14.
934        for (key, kind, modifier, muted) in [
935            (62u8, "CongaDrum", Some("high"), Some(true)),
936            (63, "CongaDrum", Some("high"), Some(false)),
937            (64, "CongaDrum", Some("low"), Some(false)),
938            (78, "Cuica", None, Some(true)),
939            (79, "Cuica", None, Some(false)),
940            (86, "Surdo", None, Some(true)),
941            (87, "Surdo", None, Some(false)),
942            (35, "BassDrum", Some("acoustic"), None),
943            (36, "BassDrum", Some("midi-1"), None),
944        ] {
945            let drum = for_percussion_key(key).expect("the map names a drum for it");
946            assert_eq!(
947                (
948                    drum.kind(),
949                    drum.modifier(),
950                    drum.percussion_pitch(),
951                    drum.muted()
952                ),
953                (kind, modifier, Some(key), muted),
954                "key {key}"
955            );
956        }
957    }
958
959    #[test]
960    fn a_sound_the_map_names_no_class_for_is_named_percussion() {
961        // music21's `namedSoundMapping`, new in v11: nine General MIDI
962        // sounds that are not instruments, each a bare unpitched
963        // percussion instrument under the sound's own name.
964        for (key, name) in [(27u8, "High Q"), (34, "Metronome Bell"), (39, "Hand Clap")] {
965            let sound = for_percussion_key(key).expect("the map names the sound");
966            assert_eq!(sound.kind(), "UnpitchedPercussion");
967            assert_eq!(sound.name(), Some(name));
968            assert_eq!(sound.percussion_pitch(), Some(key));
969            assert_eq!(sound.modifier(), None);
970        }
971        // A key the map says nothing of at all is refused, unless the
972        // caller asks for one anyway -- music21's `raiseIfUnmapped=False`,
973        // which keeps the key on an instrument saying only `Percussion`.
974        assert!(for_percussion_key(22).is_err());
975        let bare = for_percussion_key_or_unmapped(22);
976        assert_eq!(bare.kind(), "UnpitchedPercussion");
977        assert_eq!(bare.name(), Some("Percussion"));
978        assert_eq!(bare.percussion_pitch(), Some(22));
979    }
980
981    #[test]
982    fn two_instrument_classes_carry_a_flag_of_their_own() {
983        // music21 gives a tambourine `headless` and a whistle `pitched`,
984        // and nothing else either.
985        let tambourine = Instrument::of_kind("Tambourine").unwrap();
986        assert_eq!(tambourine.headless(), Some(false));
987        assert_eq!(tambourine.pitched(), None);
988        let whistle = Instrument::of_kind("Whistle").unwrap();
989        assert_eq!(whistle.pitched(), Some(false));
990        assert_eq!(whistle.headless(), None);
991        let mut piano = Instrument::of_kind("Piano").unwrap();
992        assert_eq!(piano.headless(), None);
993        piano.set_headless(true);
994        assert_eq!(piano.headless(), None);
995    }
996}