Source code for music21.instrument

# ------------------------------------------------------------------------------
# Name:         instrument.py
# Purpose:      Class for basic instrument information
#
# Authors:      Michael Scott Asato Cuthbert
#               Neena Parikh
#               Christopher Ariza
#               Jose Cabal-Ugaz
#               Ben Houge
#               Mark Gotham
#
# Copyright:    Copyright © 2009-2024 Michael Scott Asato Cuthbert
# License:      BSD, see license.txt
# ------------------------------------------------------------------------------
'''
This module represents instruments through objects that contain general information
such as Metadata for instrument names, classifications, transpositions and default
MIDI program numbers.  It also contains information specific to each instrument
or instrument family, such as string pitches, etc.  Information about instrumental
ensembles is also included here though it may later be separated out into its own
ensemble.py module.
'''
from __future__ import annotations

from collections import OrderedDict
from collections.abc import Iterable
import importlib
import unittest
import typing as t

from music21 import base
from music21 import common
from music21 import environment
from music21.exceptions21 import InstrumentException
from music21 import interval
from music21 import note
from music21 import pitch
from music21.tree.trees import OffsetTree

if t.TYPE_CHECKING:
    from music21 import stream

environLocal = environment.Environment('instrument')


[docs] def unbundleInstruments(streamIn: stream.Stream, *, inPlace=False) -> stream.Stream|None: # noinspection PyShadowingNames ''' Takes a :class:`~music21.stream.Stream` that has :class:`~music21.note.NotRest` objects and moves their `.storedInstrument` attributes to a new Stream (unless inPlace=True). >>> up1 = note.Unpitched() >>> up1.storedInstrument = instrument.BassDrum() >>> up2 = note.Unpitched() >>> up2.storedInstrument = instrument.Cowbell() >>> s = stream.Stream() >>> s.append(up1) >>> s.append(up2) >>> s2 = instrument.unbundleInstruments(s) >>> s2.show('text') {0.0} <music21.instrument.BassDrum 'Bass Drum'> {0.0} <music21.note.Unpitched 'Bass Drum'> {1.0} <music21.instrument.Cowbell 'Cowbell'> {1.0} <music21.note.Unpitched 'Cowbell'> ''' if inPlace is True: s = streamIn else: s = streamIn.coreCopyAsDerivation('unbundleInstruments') for thisObj in s: if isinstance(thisObj, note.NotRest): # eventually also unbundle each note of chord, but need new voices i = thisObj.storedInstrument if i is not None: off = thisObj.offset s.insert(off, i) if inPlace is False: return s
[docs] def bundleInstruments(streamIn: stream.Stream, *, inPlace=False) -> stream.Stream|None: # noinspection PyShadowingNames ''' >>> up1 = note.Unpitched() >>> up1.storedInstrument = instrument.BassDrum() >>> upUnknownInstrument = note.Unpitched() >>> up2 = note.Unpitched() >>> up2.storedInstrument = instrument.Cowbell() >>> s = stream.Stream() >>> s.append(up1) >>> s.append(upUnknownInstrument) >>> s.append(up2) >>> s2 = instrument.unbundleInstruments(s) >>> s3 = instrument.bundleInstruments(s2) >>> for test in s3: ... print(test.storedInstrument) Bass Drum Bass Drum Cowbell ''' if inPlace is True: s = streamIn else: s = streamIn.coreCopyAsDerivation('bundleInstruments') lastInstrument = None for thisObj in s: if 'Instrument' in thisObj.classes: lastInstrument = thisObj s.remove(thisObj) elif isinstance(thisObj, note.NotRest): thisObj.storedInstrument = lastInstrument if inPlace is False: return s
[docs] class Instrument(base.Music21Object): ''' Base class for all musical instruments. Designed for subclassing, though usually a more specific instrument class (such as StringInstrument) would be better to subclass. Some defined attributes for instruments include: * partId * partName * partAbbreviation * instrumentId * instrumentName * instrumentAbbreviation * midiProgram (0-indexed) * midiChannel (0-indexed) * lowestNote (a note object or a string for _written_ pitch) * highestNote (a note object or a string for _written_ pitch) * transposition (an interval object) * inGMPercMap (bool -- if it uses the GM percussion map) * soundfontFn (filepath to a sound font, optional) ''' classSortOrder = -25 def __init__(self, instrumentName: str|None = None, **keywords): super().__init__(**keywords) self.partId: str|None = None self._partIdIsRandom = False self.partName: str|None = None self.partAbbreviation: str|None = None self.printPartName: bool|None = None # True = yes, False = no, None = let others decide self.printPartAbbreviation: bool|None = None self.instrumentId: str|None = None # apply to midi and instrument self._instrumentIdIsRandom = False self.instrumentName: str|None = instrumentName self.instrumentAbbreviation: str|None = None self.midiProgram: int|None = None # 0-indexed self.midiChannel: int|None = None # 0-indexed self.instrumentSound: str|None = None self.lowestNote: pitch.Pitch|None = None self.highestNote: pitch.Pitch|None = None # define interval to go from written to sounding self.transposition: interval.Interval|None = None self.inGMPercMap = False self.soundfontFn = None # if defined def __str__(self): msg = [] if self.partId is not None: msg.append(f'{self.partId}: ') if self.partName is not None and self.partName != self.instrumentName: msg.append(f'{self.partName}: ') if self.instrumentName is not None: msg.append(self.instrumentName) return ''.join(msg) def _reprInternal(self): return repr(str(self)) def __deepcopy__(self, memo=None): new = common.defaultDeepcopy(self, memo) if self._partIdIsRandom: new.partIdRandomize() if self._instrumentIdIsRandom: new.instrumentIdRandomize() return new
[docs] def bestName(self) -> str|None: ''' Find a viable name, looking first at instrument, then part, then abbreviations. ''' if self.partName is not None: return self.partName elif self.partAbbreviation is not None: return self.partAbbreviation elif self.instrumentName is not None: return self.instrumentName elif self.instrumentAbbreviation is not None: return self.instrumentAbbreviation else: return None
[docs] def partIdRandomize(self): ''' Force a unique id by using an MD5. ''' idNew = f'P{common.getMd5()}' # environLocal.printDebug(['incrementing instrument from', # self.partId, 'to', idNew]) self.partId = idNew self._partIdIsRandom = True
[docs] def instrumentIdRandomize(self): ''' Force a unique id by using an MD5. ''' idNew = f'I{common.getMd5()}' # environLocal.printDebug(['incrementing instrument from', # self.partId, 'to', idNew]) self.instrumentId = idNew self._instrumentIdIsRandom = True
[docs] def autoAssignMidiChannel(self, usedChannels: list[int], maxMidi=16): ''' Assign an unused midi channel given a list of used channels. Music21 uses 0-indexed MIDI channels. Assigns the number to self.midiChannel and returns it as an int. Note that the Percussion MIDI channel (9 in music21, 10 in 1-16 numbering) is special, and thus is skipped. >>> used = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11] >>> i = instrument.Violin() >>> i.autoAssignMidiChannel(used) 12 >>> i.midiChannel 12 Note that used is unchanged after calling this and would need to be updated manually. >>> used [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11] Unpitched percussion will be set to 9, so long as it's not in the filter list: >>> used = [0] >>> i = instrument.Maracas() >>> i.autoAssignMidiChannel(used) 9 >>> i.midiChannel 9 >>> used = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> i = instrument.Woodblock() >>> i.autoAssignMidiChannel(used) 11 >>> i.midiChannel 11 If all 16 channels are used, an exception is raised: >>> used2 = range(16) >>> i = instrument.Instrument() >>> i.autoAssignMidiChannel(used2) Traceback (most recent call last): music21.exceptions21.InstrumentException: we are out of midi channels! help! Get around this by assigning higher channels: >>> i.autoAssignMidiChannel(used2, maxMidi=32) 16 >>> i.midiChannel 16 * Changed in v.9 -- usedChannelList is required, add maxMidi as an optional parameter. various small tweaks for corner cases. ''' # NOTE: this is used in musicxml output, not in midi output channelFilter = frozenset(usedChannels) if 'UnpitchedPercussion' in self.classes and 9 not in channelFilter: self.midiChannel = 9 return self.midiChannel elif not channelFilter: self.midiChannel = 0 return self.midiChannel elif len(channelFilter) >= maxMidi - 1: # subtract one, since we are not using percussion channel (=9) raise InstrumentException('we are out of midi channels! help!') else: for ch in range(maxMidi): if ch in channelFilter: continue elif ch % 16 == 9: continue # skip 10 / percussion for now else: self.midiChannel = ch return self.midiChannel return 0
# raise InstrumentException('we are out of midi channels and this ' # 'was not already detected PROGRAM BUG!') # ------------------------------------------------------------------------------
[docs] class KeyboardInstrument(Instrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Keyboard' self.instrumentAbbreviation = 'Kb' self.instrumentSound = 'keyboard.piano'
[docs] class Piano(KeyboardInstrument): ''' >>> p = instrument.Piano() >>> p.instrumentName 'Piano' >>> p.midiProgram 0 ''' def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Piano' self.instrumentAbbreviation = 'Pno' self.midiProgram = 0 self.lowestNote = pitch.Pitch('A0') self.highestNote = pitch.Pitch('C8')
[docs] class Harpsichord(KeyboardInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Harpsichord' self.instrumentAbbreviation = 'Hpschd' self.midiProgram = 6 self.instrumentSound = 'keyboard.harpsichord' self.lowestNote = pitch.Pitch('F1') self.highestNote = pitch.Pitch('F6')
[docs] class Clavichord(KeyboardInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Clavichord' self.instrumentAbbreviation = 'Clv' self.midiProgram = 7 self.instrumentSound = 'keyboard.clavichord'
# TODO: self.lowestNote = pitch.Pitch('') # TODO: self.highestNote = pitch.Pitch('')
[docs] class Celesta(KeyboardInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Celesta' self.instrumentAbbreviation = 'Clst' self.midiProgram = 8 self.instrumentSound = 'keyboard.celesta'
[docs] class Sampler(KeyboardInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Sampler' self.instrumentAbbreviation = 'Samp' self.midiProgram = 55
[docs] class ElectricPiano(Piano): ''' >>> p = instrument.ElectricPiano() >>> p.instrumentName 'Electric Piano' >>> p.midiProgram 2 ''' def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Electric Piano' self.instrumentAbbreviation = 'E.Pno' self.midiProgram = 2
# ------------------------------------------------------------------------------
[docs] class Organ(Instrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Organ' self.midiProgram = 19 self.instrumentSound = 'keyboard.organ'
[docs] class PipeOrgan(Organ): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Pipe Organ' self.instrumentAbbreviation = 'P Org' self.midiProgram = 19 self.instrumentSound = 'keyboard.organ.pipe' self.lowestNote = pitch.Pitch('C2') self.highestNote = pitch.Pitch('C6')
[docs] class ElectricOrgan(Organ): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Electric Organ' self.instrumentAbbreviation = 'Elec Org' self.midiProgram = 16 self.lowestNote = pitch.Pitch('C2') self.highestNote = pitch.Pitch('C6')
[docs] class ReedOrgan(Organ): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Reed Organ' self.instrumentAbbreviation = 'R Org' self.midiProgram = 20 self.instrumentSound = 'keyboard.organ.reed' self.lowestNote = pitch.Pitch('C2') self.highestNote = pitch.Pitch('C6')
[docs] class Accordion(Organ): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Accordion' self.instrumentAbbreviation = 'Acc' self.midiProgram = 21 self.instrumentSound = 'keyboard.accordion' self.lowestNote = pitch.Pitch('F3') self.highestNote = pitch.Pitch('A6')
[docs] class Harmonica(Instrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Harmonica' self.instrumentAbbreviation = 'Hmca' self.midiProgram = 22 self.instrumentSound = 'wind.reed.harmonica' self.lowestNote = pitch.Pitch('C3') self.highestNote = pitch.Pitch('C6')
# -----------------------------------------------------
[docs] class StringInstrument(Instrument): def __init__(self, **keywords): super().__init__(**keywords) self._stringPitches = None self._cachedPitches = None self.instrumentName = 'StringInstrument' self.instrumentAbbreviation = 'Str' self.midiProgram = 48 def _getStringPitches(self): if hasattr(self, '_cachedPitches') and self._cachedPitches is not None: return self._cachedPitches elif not hasattr(self, '_stringPitches'): raise InstrumentException('cannot get stringPitches for these instruments') else: self._cachedPitches = [pitch.Pitch(x) for x in self._stringPitches] return self._cachedPitches def _setStringPitches(self, newPitches): if newPitches and (hasattr(newPitches[0], 'step') or newPitches[0] is None): # newPitches is pitchObjects or something self._stringPitches = newPitches self._cachedPitches = newPitches else: self._cachedPitches = None self._stringPitches = newPitches stringPitches = property(_getStringPitches, _setStringPitches, doc=''' stringPitches is a property that stores a list of Pitches (or pitch names, such as "C4") that represent the pitch of the open strings from lowest to highest.[*] >>> vln1 = instrument.Violin() >>> [str(p) for p in vln1.stringPitches] ['G3', 'D4', 'A4', 'E5'] instrument.stringPitches are full pitch objects, not just names: >>> [x.octave for x in vln1.stringPitches] [3, 4, 4, 5] Scordatura for Scelsi's violin concerto *Anahit*. (N.B. that string to pitch conversion is happening automatically) >>> vln1.stringPitches = ['G3', 'G4', 'B4', 'D4'] (`[*]In some tuning methods such as reentrant tuning on the ukulele, lute, or five-string banjo the order might not strictly be from lowest to highest. The same would hold true for certain violin scordatura pieces, such as some of Biber's *Mystery Sonatas*`) ''')
[docs] class Violin(StringInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Violin' self.instrumentAbbreviation = 'Vln' self.midiProgram = 40 self.instrumentSound = 'strings.violin' self.lowestNote = pitch.Pitch('G3') self._stringPitches = ['G3', 'D4', 'A4', 'E5']
[docs] class Viola(StringInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Viola' self.instrumentAbbreviation = 'Vla' self.midiProgram = 41 self.instrumentSound = 'strings.viola' self.lowestNote = pitch.Pitch('C3') self._stringPitches = ['C3', 'G3', 'D4', 'A4']
[docs] class Violoncello(StringInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Violoncello' self.instrumentAbbreviation = 'Vc' self.midiProgram = 42 self.instrumentSound = 'strings.cello' self.lowestNote = pitch.Pitch('C2') self._stringPitches = ['C2', 'G2', 'D3', 'A3']
[docs] class Contrabass(StringInstrument): ''' For the Contrabass (or double bass), the stringPitches attribute refers to the sounding pitches of each string; whereas the lowestNote attribute refers to the lowest written note. ''' def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Contrabass' self.instrumentAbbreviation = 'Cb' self.midiProgram = 43 self.instrumentSound = 'strings.contrabass' self.lowestNote = pitch.Pitch('E2') self._stringPitches = ['E1', 'A1', 'D2', 'G2'] self.transposition = interval.Interval('P-8')
[docs] class Harp(StringInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Harp' self.instrumentAbbreviation = 'Hp' self.midiProgram = 46 self.instrumentSound = 'pluck.harp' self.lowestNote = pitch.Pitch('C1') self.highestNote = pitch.Pitch('G#7')
[docs] class Guitar(StringInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Guitar' self.instrumentAbbreviation = 'Gtr' self.midiProgram = 24 # default -- Acoustic self.instrumentSound = 'pluck.guitar' self.lowestNote = pitch.Pitch('E2') self._stringPitches = ['E2', 'A2', 'D3', 'G3', 'B3', 'E4']
[docs] class AcousticGuitar(Guitar): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Acoustic Guitar' self.instrumentAbbreviation = 'Ac Gtr' self.midiProgram = 24 self.instrumentSound = 'pluck.guitar.acoustic'
[docs] class ElectricGuitar(Guitar): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Electric Guitar' self.instrumentAbbreviation = 'Elec Gtr' self.midiProgram = 26 self.instrumentSound = 'pluck.guitar.electric'
[docs] class AcousticBass(Guitar): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Acoustic Bass' self.instrumentAbbreviation = 'Ac b' self.midiProgram = 32 self.instrumentSound = 'pluck.bass.acoustic' self.lowestNote = pitch.Pitch('E1') self._stringPitches = ['E1', 'A1', 'D2', 'G2']
[docs] class ElectricBass(Guitar): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Electric Bass' self.instrumentAbbreviation = 'Elec b' self.midiProgram = 33 self.instrumentSound = 'pluck.bass.electric' self.lowestNote = pitch.Pitch('E1') self._stringPitches = ['E1', 'A1', 'D2', 'G2']
[docs] class FretlessBass(Guitar): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Fretless Bass' self.instrumentAbbreviation = 'Fretl b' self.midiProgram = 35 self.instrumentSound = 'pluck.bass.fretless' self.lowestNote = pitch.Pitch('E1') self._stringPitches = ['E1', 'A1', 'D2', 'G2']
[docs] class Mandolin(StringInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Mandolin' self.instrumentAbbreviation = 'Mdln' self.instrumentSound = 'pluck.mandolin' self.lowestNote = pitch.Pitch('G3') self._stringPitches = ['G3', 'D4', 'A4', 'E5']
[docs] class Ukulele(StringInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Ukulele' self.instrumentAbbreviation = 'Uke' self.instrumentSound = 'pluck.ukulele' self.lowestNote = pitch.Pitch('C4') self._stringPitches = ['G4', 'C4', 'E4', 'A4']
[docs] class Banjo(StringInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Banjo' self.instrumentAbbreviation = 'Bjo' self.instrumentSound = 'pluck.banjo' self.midiProgram = 105 self.lowestNote = pitch.Pitch('C3') self._stringPitches = ['C3', 'G3', 'D4', 'A4'] self.transposition = interval.Interval('P-8')
[docs] class Lute(StringInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Lute' self.instrumentAbbreviation = 'Lte' self.instrumentSound = 'pluck.lute' self.midiProgram = 24
[docs] class Sitar(StringInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Sitar' self.instrumentAbbreviation = 'Sit' self.instrumentSound = 'pluck.sitar' self.midiProgram = 104
[docs] class Shamisen(StringInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Shamisen' self.instrumentAbbreviation = 'Shmsn' self.instrumentSound = 'pluck.shamisen' self.midiProgram = 106
[docs] class Koto(StringInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Koto' self.instrumentAbbreviation = 'Koto' self.instrumentSound = 'pluck.koto' self.midiProgram = 107
# ------------------------------------------------------------------------------
[docs] class WoodwindInstrument(Instrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Woodwind' self.instrumentAbbreviation = 'Ww'
[docs] class Flute(WoodwindInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Flute' self.instrumentAbbreviation = 'Fl' self.instrumentSound = 'wind.flutes.flute' self.midiProgram = 73 self.lowestNote = pitch.Pitch('C4') # Occasionally (rarely) B3
[docs] class Piccolo(Flute): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Piccolo' self.instrumentAbbreviation = 'Picc' self.instrumentSound = 'wind.flutes.piccolo' self.midiProgram = 72 self.lowestNote = pitch.Pitch('D4') # Occasionally (rarely) C4 self.transposition = interval.Interval('P8')
[docs] class Recorder(Flute): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Recorder' self.instrumentAbbreviation = 'Rec' self.instrumentSound = 'wind.flutes.recorder' self.midiProgram = 74 self.lowestNote = pitch.Pitch('F4')
[docs] class PanFlute(Flute): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Pan Flute' self.instrumentAbbreviation = 'P Fl' self.instrumentSound = 'wind.flutes.panpipes' self.midiProgram = 75
[docs] class Shakuhachi(Flute): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Shakuhachi' self.instrumentAbbreviation = 'Shk Fl' self.instrumentSound = 'wind.flutes.shakuhachi' self.midiProgram = 77
[docs] class Whistle(Flute): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Whistle' self.instrumentAbbreviation = 'Whs' self.instrumentSound = 'wind.flutes.whistle' self.inGMPercMap = True # TODO: why is this not inheriting from UnpitchedPercussion if we're giving it percMapPitch? self.percMapPitch = 71 self.midiProgram = 78
[docs] class Ocarina(Flute): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Ocarina' self.instrumentAbbreviation = 'Oc' self.instrumentSound = 'wind.flutes.ocarina' self.midiProgram = 79
[docs] class Oboe(WoodwindInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Oboe' self.instrumentAbbreviation = 'Ob' self.instrumentSound = 'wind.reed.oboe' self.midiProgram = 68 self.lowestNote = pitch.Pitch('B-3')
[docs] class EnglishHorn(WoodwindInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'English Horn' self.instrumentAbbreviation = 'Eng Hn' self.instrumentSound = 'wind.reed.english-horn' self.midiProgram = 69 self.lowestNote = pitch.Pitch('B3') self.transposition = interval.Interval('P-5')
[docs] class Clarinet(WoodwindInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Clarinet' self.instrumentAbbreviation = 'Cl' self.instrumentSound = 'wind.reed.clarinet' self.midiProgram = 71 self.lowestNote = pitch.Pitch('E3') self.transposition = interval.Interval('M-2')
[docs] class BassClarinet(Clarinet): ''' >>> bcl = instrument.BassClarinet() >>> bcl.instrumentName 'Bass clarinet' >>> bcl.midiProgram 71 >>> 'WoodwindInstrument' in bcl.classes True ''' def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Bass clarinet' self.instrumentAbbreviation = 'Bs Cl' self.instrumentSound = 'wind.reed.clarinet.bass' self.lowestNote = pitch.Pitch('E-3') self.transposition = interval.Interval('M-9')
[docs] class Bassoon(WoodwindInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Bassoon' self.instrumentAbbreviation = 'Bsn' self.instrumentSound = 'wind.reed.bassoon' self.midiProgram = 70 self.lowestNote = pitch.Pitch('B-1')
[docs] class Contrabassoon(Bassoon): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Contrabassoon' self.instrumentAbbreviation = 'C Bsn' self.instrumentSound = 'wind.reed.bassoon' self.midiProgram = 70 self.lowestNote = pitch.Pitch('B-1')
[docs] class Saxophone(WoodwindInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Saxophone' self.instrumentAbbreviation = 'Sax' self.instrumentSound = 'wind.reed.saxophone' self.midiProgram = 65 self.lowestNote = pitch.Pitch('B-3')
[docs] class SopranoSaxophone(Saxophone): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Soprano Saxophone' self.instrumentAbbreviation = 'S Sax' self.instrumentSound = 'wind.reed.saxophone.soprano' self.midiProgram = 64 self.transposition = interval.Interval('M-2')
[docs] class AltoSaxophone(Saxophone): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Alto Saxophone' self.instrumentAbbreviation = 'A Sax' self.instrumentSound = 'wind.reed.saxophone.alto' self.midiProgram = 65 self.transposition = interval.Interval('M-6')
[docs] class TenorSaxophone(Saxophone): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Tenor Saxophone' self.instrumentAbbreviation = 'T Sax' self.instrumentSound = 'wind.reed.saxophone.tenor' self.midiProgram = 66 self.transposition = interval.Interval('M-9')
[docs] class BaritoneSaxophone(Saxophone): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Baritone Saxophone' self.instrumentAbbreviation = 'Bar Sax' self.instrumentSound = 'wind.reed.saxophone.baritone' self.midiProgram = 67 self.transposition = interval.Interval('M-13')
[docs] class Bagpipes(WoodwindInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Bagpipes' self.instrumentAbbreviation = 'Bag' self.instrumentSound = 'wind.pipes.bagpipes' self.midiProgram = 109
[docs] class Shehnai(WoodwindInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Shehnai' self.instrumentAbbreviation = 'Shn' # another spelling is 'Shehnai' self.instrumentSound = 'wind.reed.shenai' self.midiProgram = 111
# ------------------------------------------------------------------------------
[docs] class BrassInstrument(Instrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Brass' self.instrumentAbbreviation = 'Brs' self.midiProgram = 61
[docs] class Horn(BrassInstrument): ''' >>> hn = instrument.Horn() >>> hn.instrumentName 'Horn' >>> hn.midiProgram 60 >>> 'BrassInstrument' in hn.classes True ''' def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Horn' self.instrumentAbbreviation = 'Hn' self.instrumentSound = 'brass.french-horn' self.midiProgram = 60 self.lowestNote = pitch.Pitch('C2') self.transposition = interval.Interval('P-5')
[docs] class Trumpet(BrassInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Trumpet' self.instrumentAbbreviation = 'Tpt' self.instrumentSound = 'brass.trumpet' self.midiProgram = 56 self.lowestNote = pitch.Pitch('F#3') self.transposition = interval.Interval('M-2')
[docs] class Trombone(BrassInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Trombone' self.instrumentAbbreviation = 'Trb' self.instrumentSound = 'brass.trombone' self.midiProgram = 57 self.lowestNote = pitch.Pitch('E2')
[docs] class BassTrombone(Trombone): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Bass Trombone' self.instrumentAbbreviation = 'BTrb' self.instrumentSound = 'brass.trombone.bass' self.lowestNote = pitch.Pitch('B-1')
[docs] class Tuba(BrassInstrument): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Tuba' self.instrumentAbbreviation = 'Tba' self.instrumentSound = 'brass.tuba' self.midiProgram = 58 self.lowestNote = pitch.Pitch('D1')
# ------------
[docs] class Percussion(Instrument): def __init__(self, **keywords): super().__init__(**keywords) self.inGMPercMap = False self.percMapPitch = None self.instrumentName = 'Percussion' self.instrumentAbbreviation = 'Perc'
[docs] class PitchedPercussion(Percussion): pass
[docs] class UnpitchedPercussion(Percussion): def __init__(self, **keywords): super().__init__(**keywords) self._modifier = None self._modifierToPercMapPitch = {} self._percMapPitchToModifier = {} self.midiChannel = 9 # 0-indexed, i.e. MIDI channel 10 def _getModifier(self): return self._modifier def _setModifier(self, modifier): modifier = modifier.lower().strip() # BEN: to-do, pull out hyphens, spaces, etc. if self.inGMPercMap is True and modifier.lower() in self._modifierToPercMapPitch: self.percMapPitch = self._modifierToPercMapPitch[modifier.lower()] # normalize modifiers if self.percMapPitch in self._percMapPitchToModifier: modifier = self._percMapPitchToModifier[self.percMapPitch] self._modifier = modifier modifier = property(_getModifier, _setModifier, doc=''' Returns or sets the modifier for this instrument. A modifier could be something like "low-floor" for a TomTom or "rimshot" for a SnareDrum. If the modifier is in the object's ._modifierToPercMapPitch dictionary then changing the modifier also changes the .percMapPitch for the object >>> bd = instrument.BongoDrums() >>> bd.modifier 'high' >>> bd.percMapPitch 60 >>> bd.modifier = 'low' >>> bd.percMapPitch 61 Variations on modifiers can also be used and they get normalized: >>> wb1 = instrument.Woodblock() >>> wb1.percMapPitch 76 >>> wb1.modifier = 'LO' >>> wb1.percMapPitch 77 >>> wb1.modifier # n.b. -- not LO 'low' ''')
[docs] class Vibraphone(PitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Vibraphone' self.instrumentAbbreviation = 'Vbp' self.instrumentSound = 'pitched-percussion.vibraphone' self.midiProgram = 11
[docs] class Marimba(PitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Marimba' self.instrumentAbbreviation = 'Mar' self.instrumentSound = 'pitched-percussion.marimba' self.midiProgram = 12
[docs] class Xylophone(PitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Xylophone' self.instrumentAbbreviation = 'Xyl.' self.instrumentSound = 'pitched-percussion.xylophone' self.midiProgram = 13
[docs] class Glockenspiel(PitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Glockenspiel' self.instrumentAbbreviation = 'Gsp' self.instrumentSound = 'pitched-percussion.glockenspiel' self.midiProgram = 9
[docs] class ChurchBells(PitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Church Bells' self.instrumentAbbreviation = 'Bells' self.instrumentSound = 'metal.bells.church' self.midiProgram = 14
[docs] class TubularBells(PitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Tubular Bells' self.instrumentAbbreviation = 'Tbells' self.instrumentSound = 'pitched-percussion.tubular-bells' self.midiProgram = 14
[docs] class Gong(PitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Gong' self.instrumentAbbreviation = 'Gng' self.instrumentSound = 'metal.gong'
[docs] class Handbells(PitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Handbells' self.instrumentAbbreviation = 'Hbells' self.instrumentSound = 'pitched-percussion.handbells'
[docs] class Dulcimer(PitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Dulcimer' self.instrumentAbbreviation = 'Dlc' self.instrumentSound = 'pluck.dulcimer' self.midiProgram = 15
[docs] class SteelDrum(PitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Steel Drum' self.instrumentAbbreviation = 'St Dr' self.instrumentSound = 'metal.steel-drums' self.midiProgram = 114
[docs] class Timpani(PitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Timpani' self.instrumentAbbreviation = 'Timp' self.instrumentSound = 'drum.timpani' self.midiProgram = 47
[docs] class Kalimba(PitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Kalimba' self.instrumentAbbreviation = 'Kal' self.instrumentSound = 'pitched-percussion.kalimba' self.midiProgram = 108
[docs] class Woodblock(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Woodblock' self.instrumentAbbreviation = 'Wd Bl' self.instrumentSound = 'wood.wood-block' self.inGMPercMap = True self.midiProgram = 115 self._modifier = 'high' self._modifierToPercMapPitch = {'high': 76, 'low': 77, 'hi': 76, 'lo': 77} self._percMapPitchToModifier = {76: 'high', 77: 'low'} self.percMapPitch = self._modifierToPercMapPitch[self._modifier]
[docs] class TempleBlock(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Temple Block' self.instrumentAbbreviation = 'Temp Bl' self.instrumentSound = 'wood.temple-block'
[docs] class Castanets(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Castanets' self.instrumentAbbreviation = 'Cas' self.instrumentSound = 'wood.castanets'
[docs] class Maracas(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Maracas' self.inGMPercMap = True self.percMapPitch = 70 self.instrumentAbbreviation = 'Mrcs' self.instrumentSound = 'rattle.maraca'
[docs] class Vibraslap(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Vibraslap' self.instrumentAbbreviation = 'Vbslp' self.instrumentSound = 'rattle.vibraslap' self.inGMPercMap = True self.percMapPitch = 58
# BEN: Standardize Cymbals as plural
[docs] class Cymbals(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Cymbals' self.instrumentAbbreviation = 'Cym'
[docs] class FingerCymbals(Cymbals): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Finger Cymbals' self.instrumentAbbreviation = 'Fing Cym' self.instrumentSound = 'metal.cymbal.finger'
[docs] class CrashCymbals(Cymbals): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Crash Cymbals' self.instrumentAbbreviation = 'Cym' self.instrumentSound = 'metal.cymbal.crash' self.inGMPercMap = True self._modifier = '1' self._modifierToPercMapPitch = {'1': 49, '2': 57, } self._percMapPitchToModifier = {49: '1', 57: '2', } self.percMapPitch = self._modifierToPercMapPitch[self._modifier]
[docs] class SuspendedCymbal(Cymbals): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Suspended Cymbal' self.instrumentAbbreviation = 'Sus Cym' self.instrumentSound = 'metal.cymbal.suspended'
[docs] class SizzleCymbal(Cymbals): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Sizzle Cymbal' self.instrumentAbbreviation = 'Siz Cym' self.instrumentSound = 'metal.cymbal.sizzle'
[docs] class SplashCymbals(Cymbals): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Splash Cymbals' self.instrumentAbbreviation = 'Spl Cym' self.instrumentSound = 'metal.cymbal.splash'
[docs] class RideCymbals(Cymbals): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Ride Cymbals' self.instrumentAbbreviation = 'Ride Cym' self.instrumentSound = 'metal.cymbal.ride'
[docs] class HiHatCymbal(Cymbals): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Hi-Hat Cymbal' self.instrumentSound = 'metal.hi-hat' self.inGMPercMap = True self._modifier = 'pedal' self._modifierToPercMapPitch = {'pedal': 44, 'open': 46, 'closed': 42, } self._percMapPitchToModifier = {44: 'pedal', 46: 'open', 42: 'closed', } self.percMapPitch = self._modifierToPercMapPitch[self._modifier] self.instrumentAbbreviation = 'Hi-Hat'
[docs] class Triangle(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Triangle' self.instrumentAbbreviation = 'Tri' self.instrumentSound = 'metal.triangle' self.inGMPercMap = True self._modifier = 'open' self._modifierToPercMapPitch = {'open': 81, 'mute': 80, } self._percMapPitchToModifier = {80: 'mute', 81: 'open', } self.percMapPitch = self._modifierToPercMapPitch[self._modifier]
[docs] class Cowbell(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Cowbell' self.instrumentAbbreviation = 'Cwb' self.instrumentSound = 'metal.bells.cowbell' self.inGMPercMap = True self.percMapPitch = 56
[docs] class Agogo(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Agogo' self.instrumentAbbreviation = 'Ago' self.instrumentSound = 'metal.bells.agogo' self.inGMPercMap = True self.percMapPitch = 67 self.midiProgram = 113
[docs] class TamTam(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Tam-Tam' self.instrumentAbbreviation = 'Tam-t' self.instrumentSound = 'metal.tamtam'
[docs] class SleighBells(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Sleigh Bells' self.instrumentAbbreviation = 'Slgh Bl' self.instrumentSound = 'metal.bells.sleigh-bells'
[docs] class SnareDrum(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Snare Drum' self.instrumentAbbreviation = 'Sn Dr' self.instrumentSound = 'drum.snare-drum' self.inGMPercMap = True self._modifier = 'acoustic' self._modifierToPercMapPitch = {'acoustic': 38, 'side': 37, 'electric': 40, } self._percMapPitchToModifier = {38: 'acoustic', 37: 'side', 40: 'electric', } self.percMapPitch = self._modifierToPercMapPitch[self._modifier]
[docs] class TenorDrum(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Tenor Drum' self.instrumentAbbreviation = 'Ten Dr' self.instrumentSound = 'drum.tenor-drum'
[docs] class BongoDrums(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Bongo Drums' self.instrumentAbbreviation = 'Bgo Dr' self.instrumentSound = 'drum.bongo' self.inGMPercMap = True self._modifier = 'high' self._modifierToPercMapPitch = {'high': 60, 'low': 61} self._percMapPitchToModifier = {60: 'high', 61: 'low'} self.percMapPitch = self._modifierToPercMapPitch[self._modifier]
[docs] class TomTom(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Tom-Tom' self.instrumentAbbreviation = 'Tom' self.instrumentSound = 'drum.tom-tom' self.inGMPercMap = True self._modifier = 'low floor' self._modifierToPercMapPitch = {'low floor': 41, 'high floor': 43, 'low': 45, 'low-mid': 47, 'high-mid': 48, 'high': 50} self._percMapPitchToModifier = {41: 'low floor', 43: 'high floor', 45: 'low', 47: 'low-mid', 48: 'high-mid', 50: 'high'} self.percMapPitch = self._modifierToPercMapPitch[self._modifier]
[docs] class Timbales(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Timbales' self.instrumentAbbreviation = 'Tim' self.instrumentSound = 'drum.timbale' self.inGMPercMap = True self._modifier = 'high' self._modifierToPercMapPitch = {'high': 65, 'low': 66} self._percMapPitchToModifier = {65: 'high', 66: 'low'} self.percMapPitch = self._modifierToPercMapPitch[self._modifier]
[docs] class CongaDrum(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Conga Drum' self.instrumentAbbreviation = 'Cga Dr' self.instrumentSound = 'drum.conga' self.inGMPercMap = True self._modifier = 'low' self._modifierToPercMapPitch = {'low': 64, 'mute high': 62, 'open high': 63} self._percMapPitchToModifier = {64: 'low', 62: 'mute high', 63: 'open high'} self.percMapPitch = self._modifierToPercMapPitch[self._modifier]
[docs] class BassDrum(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Bass Drum' self.instrumentAbbreviation = 'B Dr' self.instrumentSound = 'drum.bass-drum' self.inGMPercMap = True self._modifier = 'acoustic' self._modifierToPercMapPitch = {'acoustic': 35, '1': 36} self._percMapPitchToModifier = {35: 'acoustic', 36: '1'} self.percMapPitch = self._modifierToPercMapPitch[self._modifier]
[docs] class Taiko(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Taiko' self.instrumentAbbreviation = 'Tko' self.instrumentSound = 'drum.taiko' self.midiProgram = 116
[docs] class Tambourine(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Tambourine' self.instrumentAbbreviation = 'Tmbn' self.instrumentSound = 'drum.tambourine' self.inGMPercMap = True self.percMapPitch = 54
[docs] class Whip(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Whip' self.instrumentAbbreviation = 'Whip' self.instrumentSound = 'effect.whip'
[docs] class Ratchet(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Ratchet' self.instrumentAbbreviation = 'Ratc' self.instrumentSound = 'rattle.ratchet'
[docs] class Siren(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Siren' self.instrumentAbbreviation = 'Siren' self.instrumentSound = 'effect.siren'
[docs] class SandpaperBlocks(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Sandpaper Blocks' self.instrumentAbbreviation = 'Sand Bl' self.instrumentSound = 'wood.sand-block'
[docs] class WindMachine(UnpitchedPercussion): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Wind Machine' self.instrumentAbbreviation = 'Wind Mach' self.instrumentSound = 'effect.wind'
# -----------------------------------------------------
[docs] class Vocalist(Instrument): ''' n.b. called Vocalist to not be confused with stream.Voice ''' def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Voice' self.instrumentAbbreviation = 'V' self.midiProgram = 53
[docs] class Soprano(Vocalist): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Soprano' self.instrumentAbbreviation = 'S' self.instrumentSound = 'voice.soprano'
[docs] class MezzoSoprano(Soprano): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Mezzo-Soprano' self.instrumentAbbreviation = 'Mez' self.instrumentSound = 'voice.mezzo-soprano'
[docs] class Alto(Vocalist): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Alto' self.instrumentAbbreviation = 'A' self.instrumentSound = 'voice.alto'
[docs] class Tenor(Vocalist): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Tenor' self.instrumentAbbreviation = 'T' self.instrumentSound = 'voice.tenor'
[docs] class Baritone(Vocalist): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Baritone' self.instrumentAbbreviation = 'Bar' self.instrumentSound = 'voice.baritone'
[docs] class Bass(Vocalist): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Bass' self.instrumentAbbreviation = 'B' self.instrumentSound = 'voice.bass'
[docs] class Choir(Vocalist): def __init__(self, **keywords): super().__init__(**keywords) self.instrumentName = 'Choir' self.instrumentAbbreviation = 'Ch' self.instrumentSound = 'voice.choir' self.midiProgram = 52
# -----------------------------------------------------
[docs] class Conductor(Instrument): ''' Presently used only for tracking the MIDI track containing tempo, key signature, and related metadata. ''' def __init__(self, **keywords): super().__init__(instrumentName='Conductor', **keywords)
# ----------------------------------------------------------------------------- # noinspection SpellCheckingInspection ensembleNamesBySize = ['no performers', 'solo', 'duet', 'trio', 'quartet', 'quintet', 'sextet', 'septet', 'octet', 'nonet', 'dectet', 'undectet', 'duodectet', 'tredectet', 'quattuordectet', 'quindectet', 'sexdectet', 'septendectet', 'octodectet', 'novemdectet', 'vigetet', 'unvigetet', 'duovigetet', 'trevigetet', 'quattuorvigetet', 'quinvigetet', 'sexvigetet', 'septenvigetet', 'octovigetet', 'novemvigetet', 'trigetet', 'untrigetet', 'duotrigetet', 'tretrigetet', 'quottuortrigetet', 'quintrigetet', 'sextrigetet', 'septentrigetet', 'octotrigetet', 'novemtrigetet', 'quadragetet', 'unquadragetet', 'duoquadragetet', 'trequadragetet', 'quattuorquadragetet', 'quinquadragetet', 'sexquadragetet', 'octoquadragetet', 'octoquadragetet', 'novemquadragetet', 'quinquagetet', 'unquinquagetet', 'duoquinquagetet', 'trequinguagetet', 'quattuorquinquagetet', 'quinquinquagetet', 'sexquinquagetet', 'septenquinquagetet', 'octoquinquagetet', 'novemquinquagetet', 'sexagetet', 'undexagetet', 'duosexagetet', 'tresexagetet', 'quoattuorsexagetet', 'quinsexagetet', 'sexsexagetet', 'septensexagetet', 'octosexagetet', 'novemsexagetet', 'septuagetet', 'unseptuagetet', 'duoseptuagetet', 'treseptuagetet', 'quattuorseptuagetet', 'quinseptuagetet', 'sexseptuagetet', 'septenseptuagetet', 'octoseptuagetet', 'novemseptuagetet', 'octogetet', 'unoctogetet', 'duooctogetet', 'treoctogetet', 'quattuoroctogetet', 'quinoctogetet', 'sexoctogetet', 'septoctogetet', 'octooctogetet', 'novemoctogetet', 'nonagetet', 'unnonagetet', 'duononagetet', 'trenonagetet', 'quattuornonagetet', 'quinnonagetet', 'sexnonagetet', 'septennonagetet', 'octononagetet', 'novemnonagetet', 'centet']
[docs] def ensembleNameBySize(number): ''' Return the name of a generic ensemble with "number" players: >>> instrument.ensembleNameBySize(4) 'quartet' >>> instrument.ensembleNameBySize(1) 'solo' >>> instrument.ensembleNameBySize(83) 'treoctogetet' ''' if number > 100: return 'large ensemble' elif number < 0: raise InstrumentException('okay, you are on your own for this one buddy') else: return ensembleNamesBySize[int(number)]
[docs] def deduplicate(s: stream.Stream, inPlace: bool = False) -> stream.Stream: ''' Check every offset in `s` for multiple instrument instances. If the `.partName` can be standardized across instances, i.e. if each instance has the same value or `None`, and likewise for `.instrumentName`, standardize the attributes. Further, and only if the above conditions are met, if there are two instances of the same class, remove all but one; if at least one generic `Instrument` instance is found at the same offset as one or more specific instruments, remove the generic `Instrument` instances. Two `Instrument` instances: >>> i1 = instrument.Instrument(instrumentName='Semi-Hollow Body') >>> i2 = instrument.Instrument() >>> i2.partName = 'Electric Guitar' >>> s1 = stream.Stream() >>> s1.insert(4, i1) >>> s1.insert(4, i2) >>> list(s1.getInstruments()) [<music21.instrument.Instrument 'Semi-Hollow Body'>, <music21.instrument.Instrument 'Electric Guitar: '>] >>> post = instrument.deduplicate(s1) >>> list(post.getInstruments()) [<music21.instrument.Instrument 'Electric Guitar: Semi-Hollow Body'>] One `Instrument` instance and one subclass instance, with `inPlace` and parts: >>> from music21.stream import Score, Part >>> i3 = instrument.Instrument() >>> i3.partName = 'Piccolo' >>> i4 = instrument.Piccolo() >>> s2 = stream.Score() >>> p1 = stream.Part() >>> p1.append([i3, i4]) >>> p2 = stream.Part() >>> p2.append([instrument.Flute(), instrument.Flute()]) >>> s2.insert(0, p1) >>> s2.insert(0, p2) >>> list(p1.getInstruments()) [<music21.instrument.Instrument 'Piccolo: '>, <music21.instrument.Piccolo 'Piccolo'>] >>> list(p2.getInstruments()) [<music21.instrument.Flute 'Flute'>, <music21.instrument.Flute 'Flute'>] >>> s2 = instrument.deduplicate(s2, inPlace=True) >>> list(p1.getInstruments()) [<music21.instrument.Piccolo 'Piccolo'>] >>> list(p2.getInstruments()) [<music21.instrument.Flute 'Flute'>] ''' from music21 import stream if inPlace: returnObj = s else: returnObj = s.coreCopyAsDerivation('instrument.deduplicate') if not returnObj.hasPartLikeStreams(): substreams: Iterable[stream.Stream] = [returnObj] else: substreams = returnObj.getElementsByClass(stream.Stream) for sub in substreams: oTree = OffsetTree(sub[Instrument].stream()) for o in oTree: if len(o) == 1: continue notNonePartNames = {i.partName for i in o if i.partName is not None} notNoneInstNames = {i.instrumentName for i in o if i.instrumentName is not None} # Proceed only if 0-1 part name AND 0-1 instrument name candidates if len(notNonePartNames) > 1 or len(notNoneInstNames) > 1: continue partName = None for pName in notNonePartNames: partName = pName instrumentName = None for iName in notNoneInstNames: instrumentName = iName classes = {inst.__class__ for inst in o} # Case: 2+ instances of the same class if len(classes) == 1: surviving = None # Treat first as the surviving instance and standardize name for inst in o: inst.partName = partName inst.instrumentName = instrumentName surviving = inst break # Remove remaining instruments for inst in o: if inst is surviving: continue sub.remove(inst, recurse=True) # Case: mixed classes: standardize names # Remove instances of generic `Instrument` if found else: for inst in o: if inst.__class__ == Instrument: sub.remove(inst, recurse=True) else: inst.partName = partName inst.instrumentName = instrumentName return returnObj
# For lookup by MIDI Program # TODOs should be resolved with another mapping from MIDI program # to .instrumentSound MIDI_PROGRAM_TO_INSTRUMENT = { 0: Piano, 1: Piano, 2: ElectricPiano, 3: Piano, 4: ElectricPiano, 5: ElectricPiano, 6: Harpsichord, 7: Clavichord, 8: Celesta, 9: Glockenspiel, 10: Glockenspiel, # TODO: MusicBox 11: Vibraphone, 12: Marimba, 13: Xylophone, 14: TubularBells, 15: Dulcimer, 16: ElectricOrgan, # TODO: instrumentSound 17: ElectricOrgan, # TODO: instrumentSound 18: ElectricOrgan, # TODO: instrumentSound 19: PipeOrgan, 20: ReedOrgan, 21: Accordion, 22: Harmonica, 23: Accordion, # TODO: instrumentSound 24: AcousticGuitar, # TODO: instrumentSound 25: AcousticGuitar, # TODO: instrumentSound 26: ElectricGuitar, # TODO: instrumentSound 27: ElectricGuitar, # TODO: instrumentSound 28: ElectricGuitar, # TODO: instrumentSound 29: ElectricGuitar, # TODO: instrumentSound 30: ElectricGuitar, # TODO: instrumentSound 31: ElectricGuitar, # TODO: instrumentSound 32: AcousticBass, 33: ElectricBass, 34: ElectricBass, # TODO: instrumentSound 35: FretlessBass, 36: ElectricBass, # TODO: instrumentSound 37: ElectricBass, # TODO: instrumentSound 38: ElectricBass, # TODO: instrumentSound 39: ElectricBass, # TODO: instrumentSound 40: Violin, 41: Viola, 42: Violoncello, 43: Contrabass, 44: StringInstrument, # TODO: instrumentSound 45: StringInstrument, # TODO: instrumentSound 46: Harp, 47: Timpani, 48: StringInstrument, # TODO: instrumentSound 49: StringInstrument, # TODO: instrumentSound 50: StringInstrument, # TODO: instrumentSound 51: StringInstrument, # TODO: instrumentSound 52: Choir, # TODO: instrumentSound 53: Vocalist, # TODO: instrumentSound 54: Vocalist, # TODO: instrumentSound 55: Sampler, 56: Trumpet, 57: Trombone, 58: Tuba, 59: Trumpet, # TODO: instrumentSound 60: Horn, 61: BrassInstrument, # TODO: instrumentSound 62: BrassInstrument, # TODO: instrumentSound 63: BrassInstrument, # TODO: instrumentSound 64: SopranoSaxophone, 65: AltoSaxophone, 66: TenorSaxophone, 67: BaritoneSaxophone, 68: Oboe, 69: EnglishHorn, 70: Bassoon, 71: Clarinet, 72: Piccolo, 73: Flute, 74: Recorder, 75: PanFlute, 76: PanFlute, # TODO: 76: Bottle 77: Shakuhachi, 78: Whistle, 79: Ocarina, 80: Sampler, # TODO: all Sampler here and below: instrumentSound 81: Sampler, 82: Sampler, 83: Sampler, 84: Sampler, 85: Sampler, 86: Sampler, 87: Sampler, 88: Sampler, 89: Sampler, 90: Sampler, 91: Sampler, 92: Sampler, 93: Sampler, 94: Sampler, 95: Sampler, 96: Sampler, 97: Sampler, 98: Sampler, 99: Sampler, 100: Sampler, 101: Sampler, 102: Sampler, 103: Sampler, 104: Sitar, 105: Banjo, 106: Shamisen, 107: Koto, 108: Kalimba, 109: Bagpipes, 110: Violin, # TODO: instrumentSound 111: Shehnai, 112: Glockenspiel, # TODO: 112: Tinkle Bell 113: Agogo, 114: SteelDrum, 115: Woodblock, 116: Taiko, 117: TomTom, 118: Sampler, # TODO: instrumentSound # debatable if this should be drum? 119: Sampler, 120: Sampler, 121: Sampler, 122: Sampler, 123: Sampler, 124: Sampler, 125: Sampler, 126: Sampler, 127: Sampler, }
[docs] def instrumentFromMidiProgram(number: int) -> Instrument: ''' Return the instrument with "number" as its assigned MIDI program. Notice any of the values 0-5 will return Piano. Lookups are performed against `instrument.MIDI_PROGRAM_TO_INSTRUMENT`. >>> instrument.instrumentFromMidiProgram(4) <music21.instrument.ElectricPiano 'Electric Piano'> >>> instrument.instrumentFromMidiProgram(21) <music21.instrument.Accordion 'Accordion'> >>> instrument.instrumentFromMidiProgram(500) Traceback (most recent call last): music21.exceptions21.InstrumentException: No instrument found for MIDI program 500 >>> instrument.instrumentFromMidiProgram('43') Traceback (most recent call last): TypeError: Expected int, got <class 'str'> ''' try: class_ = MIDI_PROGRAM_TO_INSTRUMENT[number] inst = class_() inst.midiProgram = number # TODO: if midiProgram in MIDI_PROGRAM_SOUND_MAP: # inst.instrumentSound = MIDI_PROGRAM_SOUND_MAP[midiProgram] except KeyError as e: if not isinstance(number, int): raise TypeError(f'Expected int, got {type(number)}') from e raise InstrumentException(f'No instrument found for MIDI program {number}') from e return inst
[docs] def partitionByInstrument(streamObj: stream.Stream) -> stream.Stream: # noinspection PyShadowingNames ''' Given a single Stream, or a Score or similar multi-part structure, partition into a Part for each unique Instrument, joining events possibly from different parts. >>> p1 = converter.parse("tinynotation: 4/4 c4 d e f g a b c' c1") >>> p2 = converter.parse("tinynotation: 4/4 C#4 D# E# F# G# A# B# c# C#1") >>> p1.getElementsByClass(stream.Measure)[0].insert(0.0, instrument.Piccolo()) >>> p1.getElementsByClass(stream.Measure)[0].insert(2.0, instrument.AltoSaxophone()) >>> p1.getElementsByClass(stream.Measure)[1].insert(3.0, instrument.Piccolo()) >>> p2.getElementsByClass(stream.Measure)[0].insert(0.0, instrument.Trombone()) >>> p2.getElementsByClass(stream.Measure)[0].insert(3.0, instrument.Piccolo()) # not likely >>> p2.getElementsByClass(stream.Measure)[1].insert(1.0, instrument.Trombone()) >>> s = stream.Score() >>> s.insert(0, p1) >>> s.insert(0, p2) >>> s.show('text') {0.0} <music21.stream.Part ...> {0.0} <music21.stream.Measure 1 offset=0.0> {0.0} <music21.instrument.Piccolo 'Piccolo'> {0.0} <music21.clef.TrebleClef> {0.0} <music21.meter.TimeSignature 4/4> {0.0} <music21.note.Note C> {1.0} <music21.note.Note D> {2.0} <music21.instrument.AltoSaxophone 'Alto Saxophone'> {2.0} <music21.note.Note E> {3.0} <music21.note.Note F> {4.0} <music21.stream.Measure 2 offset=4.0> {0.0} <music21.note.Note G> {1.0} <music21.note.Note A> {2.0} <music21.note.Note B> {3.0} <music21.instrument.Piccolo 'Piccolo'> {3.0} <music21.note.Note C> {8.0} <music21.stream.Measure 3 offset=8.0> {0.0} <music21.note.Note C> {4.0} <music21.bar.Barline type=final> {0.0} <music21.stream.Part ...> {0.0} <music21.stream.Measure 1 offset=0.0> {0.0} <music21.instrument.Trombone 'Trombone'> {0.0} <music21.clef.BassClef> {0.0} <music21.meter.TimeSignature 4/4> {0.0} <music21.note.Note C#> {1.0} <music21.note.Note D#> {2.0} <music21.note.Note E#> {3.0} <music21.instrument.Piccolo 'Piccolo'> {3.0} <music21.note.Note F#> {4.0} <music21.stream.Measure 2 offset=4.0> {0.0} <music21.note.Note G#> {1.0} <music21.instrument.Trombone 'Trombone'> {1.0} <music21.note.Note A#> {2.0} <music21.note.Note B#> {3.0} <music21.note.Note C#> {8.0} <music21.stream.Measure 3 offset=8.0> {0.0} <music21.note.Note C#> {4.0} <music21.bar.Barline type=final> >>> s2 = instrument.partitionByInstrument(s) >>> len(s2.parts) 3 # TODO: this step might not be necessary: >>> for p in s2.parts: ... p.makeRests(fillGaps=True, inPlace=True) # TODO: this step SHOULD not be necessary (.template()): >>> for p in s2.parts: ... p.makeMeasures(inPlace=True) ... p.makeTies(inPlace=True) >>> s2.show('text') {0.0} <music21.stream.Part Piccolo> {0.0} <music21.stream.Measure 1 offset=0.0> {0.0} <music21.instrument.Piccolo 'Piccolo'> {0.0} <music21.clef.TrebleClef> {0.0} <music21.meter.TimeSignature 4/4> {0.0} <music21.note.Note C> {1.0} <music21.note.Note D> {2.0} <music21.note.Rest quarter> {3.0} <music21.note.Note F#> {4.0} <music21.stream.Measure 2 offset=4.0> {0.0} <music21.note.Note G#> {1.0} <music21.note.Rest half> {3.0} <music21.note.Note C> {8.0} <music21.stream.Measure 3 offset=8.0> {0.0} <music21.note.Note C> {4.0} <music21.bar.Barline type=final> {0.0} <music21.stream.Part Alto Saxophone> {0.0} <music21.stream.Measure 1 offset=0.0> {0.0} <music21.instrument.AltoSaxophone 'Alto Saxophone'> {0.0} <music21.clef.TrebleClef> {0.0} <music21.meter.TimeSignature 4/4> {0.0} <music21.note.Rest half> {2.0} <music21.note.Note E> {3.0} <music21.note.Note F> {4.0} <music21.stream.Measure 2 offset=4.0> {0.0} <music21.note.Note G> {1.0} <music21.note.Note A> {2.0} <music21.note.Note B> {3.0} <music21.bar.Barline type=final> {0.0} <music21.stream.Part Trombone> {0.0} <music21.stream.Measure 1 offset=0.0> {0.0} <music21.instrument.Trombone 'Trombone'> {0.0} <music21.clef.BassClef> {0.0} <music21.meter.TimeSignature 4/4> {0.0} <music21.note.Note C#> {1.0} <music21.note.Note D#> {2.0} <music21.note.Note E#> {3.0} <music21.note.Rest quarter> {4.0} <music21.stream.Measure 2 offset=4.0> {0.0} <music21.note.Rest quarter> {1.0} <music21.note.Note A#> {2.0} <music21.note.Note B#> {3.0} <music21.note.Note C#> {8.0} <music21.stream.Measure 3 offset=8.0> {0.0} <music21.note.Note C#> {4.0} <music21.bar.Barline type=final> * Changed in v8: returns the original stream if there are no instruments. TODO: parts should be in Score Order. Coincidence that this almost works. TODO: use proper recursion to make a copy of the stream. TODO: final barlines should be aligned. ''' from music21 import stream if not streamObj.hasPartLikeStreams(): # place in a score for uniform operations s = stream.Score() s.insert(0, streamObj.flatten()) else: s = stream.Score() # append flat parts for sub in streamObj.getElementsByClass(stream.Stream): s.insert(0, sub.flatten()) # first, let's extend the duration of each instrument to match stream for sub in s.getElementsByClass(stream.Stream): sub.extendDuration('Instrument', inPlace=True) # first, find all unique instruments instrumentIterator = s[Instrument] if not instrumentIterator: return s # no partition is available names: OrderedDict[str, dict[str, t.Any]] = OrderedDict() # store unique names for instrumentObj in instrumentIterator: # matching here by instrument name if instrumentObj.instrumentName not in names: names[instrumentObj.instrumentName or ''] = {'Instrument': instrumentObj} # just store one instance # create a return object that has a part for each instrument post = stream.Score() for iName in names: p = stream.Part() p.id = iName # add the instrument instance p.insert(0, names[iName]['Instrument']) # store a handle to this part names[iName]['Part'] = p post.insert(0, p) # iterate over flat sources; get events within each defined instrument # add to corresponding part for el in s: if not el.isStream: post.insert(el.offset, el) subStream = el for i in subStream.getElementsByClass(Instrument): start = i.offset # duration will have been set with sub.extendDuration above end = i.offset + i.duration.quarterLength # get destination Part p = names[i.instrumentName or '']['Part'] coll = subStream.getElementsByOffset( start, end, # do not include elements that start at the end includeEndBoundary=False, mustFinishInSpan=False, mustBeginInSpan=True ) # add to part at original offset # do not gather instrument for e in coll.getElementsNotOfClass(Instrument): try: p.insert(subStream.elementOffset(e), e) except stream.StreamException: pass # it is possible to enter an element twice because the getElementsByOffset # might return something twice if it's at the same offset as the # instrument switch for inst in post.recurse().getElementsByClass(Instrument): inst.duration.quarterLength = 0 return post
def _combinations(instrumentString): ''' Find all combinations of instrumentString. Remove all punctuation. ''' sampleList = instrumentString.split() allComb = [] for size in range(1, len(sampleList) + 1): for i in range(len(sampleList) - size + 1): allComb.append(' '.join(sampleList[i:i + size])) return allComb
[docs] class SearchLanguage(common.enums.StrEnum): ALL = 'all' ENGLISH = 'english' FRENCH = 'french' GERMAN = 'german' ITALIAN = 'italian' RUSSIAN = 'russian' SPANISH = 'spanish' ABBREVIATION = 'abbreviation'
[docs] def fromString(instrumentString: str, language: SearchLanguage = SearchLanguage.ALL): ''' Given a string with instrument content (from an orchestral score for example), attempts to return an appropriate :class:`~music21.instrument.Instrument`. >>> t1 = instrument.fromString('Clarinet 2 in A') >>> t1 <music21.instrument.Clarinet 'Clarinet 2 in A'> >>> t1.transposition <music21.interval.Interval m-3> >>> t2 = instrument.fromString('Clarinetto 3') >>> t2 <music21.instrument.Clarinet 'Clarinetto 3'> >>> t3 = instrument.fromString('flauto 2') >>> t3 <music21.instrument.Flute 'flauto 2'> Excess information is ignored, and the useful information can be extracted correctly as long as it's sequential. >>> t4 = instrument.fromString('I <3 music saxofono tenore go beavers') >>> t4 <music21.instrument.TenorSaxophone 'I <3 music saxofono tenore go beavers'> Some more demos: >>> t5 = instrument.fromString('Bb Clarinet') >>> t5 <music21.instrument.Clarinet 'Bb Clarinet'> >>> t5.transposition <music21.interval.Interval M-2> >>> t6 = instrument.fromString('Clarinet in B-flat') >>> t5.__class__ == t6.__class__ True >>> t5.transposition == t6.transposition True >>> t7 = instrument.fromString('B-flat Clarinet.') >>> t5.__class__ == t7.__class__ and t5.transposition == t7.transposition True >>> t8 = instrument.fromString('Eb Clarinet') >>> t5.__class__ == t8.__class__ True >>> t8.transposition <music21.interval.Interval m3> Note that because of the ubiquity of B-flat clarinets and trumpets, and the rareness of B-natural forms of those instruments, this gives a B-flat, not B-natural clarinet, using the German form: >>> t9 = instrument.fromString('Klarinette in B.') >>> t9 <music21.instrument.Clarinet 'Klarinette in B.'> >>> t9.transposition <music21.interval.Interval M-2> Use "H" or "b-natural" to get an instrument in B-major. Or donate one to me, and I'll change this back! Standard abbreviations are acceptable: >>> t10 = instrument.fromString('Cl in B-flat') >>> t10 <music21.instrument.Clarinet 'Cl in B-flat'> >>> t10.transposition <music21.interval.Interval M-2> This should work with or without a terminal period (for both 'Cl' and 'Cl.'): >>> t11 = instrument.fromString('Cl. in B-flat') >>> t11.__class__ == t10.__class__ True Previously an exact instrument name was not always working: >>> instrument.fromString('Flute') <music21.instrument.Flute 'Flute'> This common MIDI instrument was not previously working: >>> instrument.fromString('Choir (Aahs)') <music21.instrument.Choir 'Choir (Aahs)'> By default, this function searches over all stored instrument names. This includes multiple languages as well as the abbreviations (an honorary 'language' for these purposes). Alternatively, you can specify the language to search using the `language` argument. (New in v7.3.) >>> t12 = instrument.fromString('Klarinette', language=instrument.SearchLanguage.GERMAN) >>> t12 <music21.instrument.Clarinet 'Klarinette'> This case works because the name 'Klarinette' is a recognised instrument name in German and appears in the German language list. If you search for a German name like 'Klarinette' on the French list (language='french'), then it won't be found and an InstrumentException will be raised. An InstrumentException is also raised if the specified language is not one of those currently supported: 'english', 'french', 'german', 'italian', 'russian', 'spanish', and 'abbreviation'. Note that the language string is not case-sensitive, so 'French' is also fine. ''' from music21.languageExcerpts import instrumentLookup language = language.lower() if language not in SearchLanguage: raise InstrumentException(f'Chosen language {language} not currently supported.') sourceDict = getattr(instrumentLookup, language + 'ToClassName') instrumentStringOrig = instrumentString instrumentString = instrumentString.replace('.', ' ') # sic, before removePunctuation instrumentString = instrumentString.lower() # previously run on each substring separately instrumentString = common.removePunctuation(instrumentString) allCombinations = _combinations(instrumentString) # First task: Find the best instrument. bestInstrument = None bestName: str = '' this_module = importlib.import_module('music21.instrument') for substring in allCombinations: try: className = sourceDict[substring] thisInstClass = getattr(this_module, className) # In case users have overridden the module and imported more things if base.Music21Object not in thisInstClass.__mro__: # pragma: no cover raise KeyError thisInstrument = thisInstClass() thisBestName = thisInstrument.bestName().lower() if (bestInstrument is None or len(thisBestName.split()) >= len(bestName.split()) and not isinstance(bestInstrument, thisInstClass)): # priority is also given to same length instruments which fall later # on in the string (i.e. Bb Piccolo Trumpet) bestInstrument = thisInstrument bestInstrument.instrumentName = instrumentStringOrig bestName = thisBestName except KeyError: pass if bestInstrument is None: raise InstrumentException( f'Could not match string with instrument: {instrumentStringOrig}') if bestName not in instrumentLookup.transposition: return bestInstrument # A transposition table is defined for the instrument. # Second task: Determine appropriate transposition (if any) for substring in allCombinations: try: bestPitch = instrumentLookup.pitchFullNameToName[substring.lower()] bestInterval = instrumentLookup.transposition[bestName][bestPitch] bestInstrument.transposition = interval.Interval(bestInterval) break except KeyError: pass return bestInstrument
def _getKeys(classNameString: str, language: SearchLanguage = SearchLanguage.ALL): ''' Retrieve the key or keys (variant instrument names) from an instrumentLookup dict, given the language (which instrumentLookup dict) and value (classNameString). Returns all relevant keys as a list of strings (empty if no matches). ''' from music21.languageExcerpts import instrumentLookup sourceDict = getattr(instrumentLookup, language + 'ToClassName') returns = [] for key, value in sourceDict.items(): if classNameString == value: returns.append(key) return returns
[docs] def getAllNamesForInstrument(instrumentClass: Instrument, language: SearchLanguage = SearchLanguage.ALL): ''' Retrieves all currently stored names for a given instrument. The instrumentClass should be a valid music21 :class:`~music21.instrument.Instrument`. By default, this function searches over all supported languages including instrument name abbreviations (an honorary 'language' for these purposes), and returns a dict with keys for the language tested and values as a list of strings for any names in that language. >>> instrument.getAllNamesForInstrument(instrument.Flute()) {'english': ['flute', 'flutes', 'transverse flute'], 'french': ['flûte', 'flûte traversière', 'flûtes', 'grande flûte'], 'german': ['flöte', 'flöten', 'querflöte'], 'italian': ['flauti', 'flauto', 'flauto traverso'], 'russian': ['fleita'], 'spanish': ['flauta', 'flauta de boehm', 'flauta de concierto', 'flauta traversa', 'flauta travesera', 'flautas'], 'abbreviation': ['fl']} Alternatively, you can specify the language to search using the `language` argument. >>> instrument.getAllNamesForInstrument(instrument.Flute(), language='german') {'german': ['flöte', 'flöten', 'querflöte']} An InstrumentException is raised if the specified language is not one of those currently supported: 'english', 'french', 'german', 'italian', 'russian', 'spanish', and 'abbreviation'. Note that the language string is not case-sensitive, so 'German' is also fine. ''' language = language.lower() instrumentNameDict = {} instrumentClassName = instrumentClass.instrumentName or '' if language == SearchLanguage.ALL: for lang in SearchLanguage: if lang is SearchLanguage.ALL: continue # skip the 'all' combination, handle the languages separately. instrumentNameDict[str(lang)] = _getKeys(instrumentClassName, lang) elif language not in SearchLanguage: raise InstrumentException(f'Chosen language {language} not currently supported.') else: # one, valid language instrumentNameDict[language] = _getKeys(instrumentClassName, SearchLanguage(language)) return instrumentNameDict
# ------------------------------------------------------------------------------ class TestExternal(unittest.TestCase): pass class Test(unittest.TestCase): def testCopyAndDeepcopy(self): from music21.test.commonTest import testCopyAll testCopyAll(self, globals()) def testMusicXMLExport(self): from music21 import stream s1 = stream.Stream() i1 = Violin() i1.partName = 'test' s1.append(i1) s1.repeatAppend(note.Note(), 10) # s.show() s2 = stream.Stream() i2 = Piano() i2.partName = 'test2' s2.append(i2) s2.repeatAppend(note.Note('g4'), 10) s3 = stream.Score() s3.insert(0, s1) s3.insert(0, s2) # s3.show() def testPartitionByInstrumentA(self): from music21 import instrument from music21 import stream # basic case of instruments in Parts s = stream.Score() p1 = stream.Part() p1.append(instrument.Piano()) p2 = stream.Part() p2.append(instrument.Piccolo()) s.insert(0, p1) s.insert(0, p2) post = instrument.partitionByInstrument(s) self.assertEqual(len(post), 2) self.assertEqual(len(post.flatten().getElementsByClass(instrument.Instrument)), 2) # post.show('t') # one Stream with multiple instruments s = stream.Stream() s.insert(0, instrument.PanFlute()) s.insert(20, instrument.ReedOrgan()) post = instrument.partitionByInstrument(s) self.assertEqual(len(post), 2) self.assertEqual(len(post[instrument.Instrument]), 2) # post.show('t') def testPartitionByInstrumentB(self): from music21 import instrument from music21 import stream # basic case of instruments in Parts s = stream.Score() p1 = stream.Part() p1.append(instrument.Piano()) p1.repeatAppend(note.Note(), 6) p2 = stream.Part() p2.append(instrument.Piccolo()) p2.repeatAppend(note.Note(), 12) s.insert(0, p1) s.insert(0, p2) post = instrument.partitionByInstrument(s) self.assertEqual(len(post), 2) self.assertEqual(len(post[instrument.Instrument]), 2) self.assertEqual(len(post.parts[0].notes), 6) self.assertEqual(len(post.parts[1].notes), 12) def testPartitionByInstrumentC(self): from music21 import instrument from music21 import stream # basic case of instruments in Parts s = stream.Score() p1 = stream.Part() p1.append(instrument.Piano()) p1.repeatAppend(note.Note('a'), 6) # will go in next available offset p1.append(instrument.AcousticGuitar()) p1.repeatAppend(note.Note('b'), 3) p2 = stream.Part() p2.append(instrument.Piccolo()) p2.repeatAppend(note.Note('c'), 2) p2.append(instrument.Flute()) p2.repeatAppend(note.Note('d'), 4) s.insert(0, p1) s.insert(0, p2) post = instrument.partitionByInstrument(s) self.assertEqual(len(post), 4) # 4 instruments self.assertEqual(len(post[instrument.Instrument]), 4) self.assertEqual(post.parts[0].getInstrument().instrumentName, 'Piano') self.assertEqual(len(post.parts[0].notes), 6) self.assertEqual(post.parts[1].getInstrument().instrumentName, 'Acoustic Guitar') self.assertEqual(len(post.parts[1].notes), 3) self.assertEqual(post.parts[2].getInstrument().instrumentName, 'Piccolo') self.assertEqual(len(post.parts[2].notes), 2) self.assertEqual(post.parts[3].getInstrument().instrumentName, 'Flute') self.assertEqual(len(post.parts[3].notes), 4) # environLocal.printDebug(['post processing']) # post.show('t') def testPartitionByInstrumentD(self): from music21 import instrument from music21 import stream # basic case of instruments in Parts s = stream.Score() p1 = stream.Part() p1.append(instrument.Piano()) p1.repeatAppend(note.Note('a'), 6) # will go in next available offset p1.append(instrument.AcousticGuitar()) p1.repeatAppend(note.Note('b'), 3) p1.append(instrument.Piano()) p1.repeatAppend(note.Note('e'), 5) p2 = stream.Part() p2.append(instrument.Piccolo()) p2.repeatAppend(note.Note('c'), 2) p2.append(instrument.Flute()) p2.repeatAppend(note.Note('d'), 4) p2.append(instrument.Piano()) p2.repeatAppend(note.Note('f'), 1) s.insert(0, p1) s.insert(0, p2) post = instrument.partitionByInstrument(s) self.assertEqual(len(post), 4) # 4 instruments self.assertEqual(len(post[instrument.Instrument]), 4) # piano spans are joined together self.assertEqual(post.parts[0].getInstrument().instrumentName, 'Piano') self.assertEqual(len(post.parts[0].notes), 12) self.assertEqual([n.offset for n in post.parts[0].notes], [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 9.0, 10.0, 11.0, 12.0, 13.0]) # environLocal.printDebug(['post processing']) # post.show('t') def testPartitionByInstrumentE(self): from music21 import instrument from music21 import stream # basic case of instruments in Parts # s = stream.Score() p1 = stream.Part() p1.append(instrument.Piano()) p1.repeatAppend(note.Note('a'), 6) # will go in next available offset p1.append(instrument.AcousticGuitar()) p1.repeatAppend(note.Note('b'), 3) p1.append(instrument.Piano()) p1.repeatAppend(note.Note('e'), 5) p1.append(instrument.Piccolo()) p1.repeatAppend(note.Note('c'), 2) p1.append(instrument.Flute()) p1.repeatAppend(note.Note('d'), 4) p1.append(instrument.Piano()) p1.repeatAppend(note.Note('f'), 1) s = p1 post = instrument.partitionByInstrument(s) self.assertEqual(len(post), 4) # 4 instruments self.assertEqual(len(post[instrument.Instrument]), 4) # piano spans are joined together self.assertEqual(post.parts[0].getInstrument().instrumentName, 'Piano') self.assertEqual(len(post.parts[0].notes), 12) offsetList = [] ppn = post.parts[0].notes for n in ppn: offsetList.append(n.offset) self.assertEqual(offsetList, [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 9.0, 10.0, 11.0, 12.0, 13.0, 20.0]) def testPartitionByInstrumentF(self): from music21 import instrument from music21 import stream s1 = stream.Stream() s1.append(instrument.AcousticGuitar()) s1.append(note.Note()) s1.append(instrument.Tuba()) s1.append(note.Note()) post = instrument.partitionByInstrument(s1) self.assertEqual(len(post), 2) # 4 instruments # def testPartitionByInstrumentDocTest(self): # ''' # For debugging the doctest. # ''' # from music21 import instrument, converter, stream # p1 = converter.parse("tinynotation: 4/4 c4 d e f g a b c' c1") # p2 = converter.parse("tinynotation: 4/4 C#4 D# E# F# G# A# B# c# C#1") # # p1.getElementsByClass(stream.Measure)[0].insert(0.0, instrument.Piccolo()) # p1.getElementsByClass(stream.Measure)[0].insert(2.0, instrument.AltoSaxophone()) # p1.getElementsByClass(stream.Measure)[1].insert(3.0, instrument.Piccolo()) # # p2.getElementsByClass(stream.Measure)[0].insert(0.0, instrument.Trombone()) # p2.getElementsByClass(stream.Measure)[0].insert(3.0, instrument.Piccolo()) # not likely. # p2.getElementsByClass(stream.Measure)[1].insert(1.0, instrument.Trombone()) # # s = stream.Score() # s.insert(0, p1) # s.insert(0, p2) # s2 = instrument.partitionByInstrument(s) # for p in s2.parts: # p.makeRests(fillGaps=True, inPlace=True) def testLanguageChoice(self): from music21 import instrument # fromString testString = 'Klarinette' # German name # Works when language not specified self.assertEqual(instrument.fromString(testString).instrumentName, testString) workingExamples = ['german', # Works with correct language for the term 'German' # Not case-sensitive, so 'German' is also fine ] for langStr in workingExamples: instrName = instrument.fromString(testString, language=langStr).instrumentName self.assertEqual(instrName, testString) failingExamples = ['french', # Error when the language doesn't match the term 'finnish' # Error for unsupported language ] for langStr in failingExamples: self.assertRaises(InstrumentException, instrument.fromString, testString, language=langStr) # getAllNamesForInstrument inst = instrument.Flute() # Working example self.assertEqual(instrument.getAllNamesForInstrument(inst, language=SearchLanguage.ABBREVIATION), {'abbreviation': ['fl']}) # Error for unsupported language self.assertRaises(InstrumentException, instrument.getAllNamesForInstrument, inst, language='finnish') # ------------------------------------------------------------------------------ # define presented order in documentation _DOC_ORDER = [Instrument] if __name__ == '__main__': # sys.arg test options will be used in mainTest() import music21 music21.mainTest(Test)