# ------------------------------------------------------------------------------
# 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)