diff --git a/music21/instrument.py b/music21/instrument.py index 60a9a19a5..ecefc2216 100644 --- a/music21/instrument.py +++ b/music21/instrument.py @@ -10,7 +10,7 @@ # Ben Houge # Mark Gotham # -# Copyright: Copyright © 2009-2023 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2009-2024 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ ''' @@ -62,9 +62,9 @@ def unbundleInstruments(streamIn: stream.Stream, >>> s2 = instrument.unbundleInstruments(s) >>> s2.show('text') {0.0} - {0.0} + {0.0} {1.0} - {1.0} + {1.0} ''' if inPlace is True: s = streamIn @@ -245,8 +245,8 @@ def autoAssignMidiChannel(self, usedChannels: list[int], maxMidi=16): assigns the number to self.midiChannel and returns it as an int. - Note that midi channel 10 (9 in music21) is special, and - thus is skipped. + 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() @@ -255,6 +255,13 @@ def autoAssignMidiChannel(self, usedChannels: list[int], maxMidi=16): >>> 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] @@ -282,22 +289,25 @@ def autoAssignMidiChannel(self, usedChannels: list[int], maxMidi=16): Get around this by assinging higher channels: >>> i.autoAssignMidiChannel(used2, maxMidi=32) + 16 >>> i.midiChannel 16 - Changed in v.9 -- usedChannelList is required, add maxMidi as an optional parameter. + * 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 = set(usedChannels) + channelFilter = frozenset(usedChannels) - if not channelFilter: + 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: + elif len(channelFilter) >= maxMidi - 1: + # subtract one, since we are not using percussion channel (=9) raise InstrumentException('we are out of midi channels! help!') - elif 'UnpitchedPercussion' in self.classes: - self.midiChannel = 9 - return self.midiChannel else: for ch in range(maxMidi): if ch in channelFilter: diff --git a/music21/midi/translate.py b/music21/midi/translate.py index d37dd6590..12e0461ec 100644 --- a/music21/midi/translate.py +++ b/music21/midi/translate.py @@ -6,7 +6,7 @@ # Authors: Christopher Ariza # Michael Scott Asato Cuthbert # -# Copyright: Copyright © 2010-2023 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2010-2024 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ ''' @@ -369,7 +369,7 @@ def midiEventsToNote( >>> me1.channel = 10 >>> unp = midi.translate.midiEventsToNote(((dt1.time, me1), (dt2.time, me2))) >>> unp - + Access the `storedInstrument`: diff --git a/music21/percussion.py b/music21/percussion.py index b1198d0e9..7093032b0 100644 --- a/music21/percussion.py +++ b/music21/percussion.py @@ -31,21 +31,22 @@ class PercussionChord(chord.ChordBase): a :class:`~music21.chord.Chord` because one or more notes is an :class:`~music21.note.Unpitched` object. - >>> pChord = percussion.PercussionChord([note.Unpitched(displayName='D4'), note.Note('E5')]) + >>> vibraslapNote = note.Unpitched(displayName='D4', storedInstrument=instrument.Vibraslap()) + >>> pChord = percussion.PercussionChord([vibraslapNote, note.Note('E5')]) >>> pChord.isChord False Has notes, just like any ChordBase: >>> pChord.notes - (, ) + (, ) Assign them to another PercussionChord: >>> pChord2 = percussion.PercussionChord() >>> pChord2.notes = pChord.notes >>> pChord2.notes - (, ) + (, ) Don't attempt setting anything but Note or Unpitched objects as notes: diff --git a/music21/stream/base.py b/music21/stream/base.py index 8e03fbe21..4dc799a1b 100644 --- a/music21/stream/base.py +++ b/music21/stream/base.py @@ -1437,22 +1437,14 @@ def mergeAttributes(self, other: base.Music21Object): if hasattr(other, attr): setattr(self, attr, getattr(other, attr)) - @common.deprecated('v10', 'v11', 'Use `el in stream` instead of ' + @common.deprecated('v9.3', 'v11', 'Use `el in stream` instead of ' '`stream.hasElement(el)`') def hasElement(self, obj: base.Music21Object) -> bool: ''' + DEPRECATED: just use `el in stream` instead of `stream.hasElement(el)` + Return True if an element, provided as an argument, is contained in this Stream. - - This method is based on object equivalence, not parameter equivalence - of different objects. - - >>> s = stream.Stream() - >>> n1 = note.Note('g') - >>> n2 = note.Note('g#') - >>> s.append(n1) - >>> s.hasElement(n1) - True ''' return obj in self @@ -1473,7 +1465,7 @@ def hasElementOfClass(self, className, forceFlat=False): >>> s.hasElementOfClass('Measure') False - To be deprecated in v8 -- to be removed in v9, use: + To be deprecated in v10 -- to be removed in v11, use: >>> bool(s.getElementsByClass(meter.TimeSignature)) True @@ -1500,7 +1492,6 @@ def mergeElements(self, other, classFilterList=None): but manages locations properly, only copies elements, and permits filtering by class type. - >>> s1 = stream.Stream() >>> s2 = stream.Stream() >>> n1 = note.Note('f#') @@ -1567,8 +1558,7 @@ def index(self, el: base.Music21Object) -> int: Return the first matched index for the specified object. - Raises a StreamException if the object cannot - be found. + Raises a StreamException if the object cannot be found. >>> s = stream.Stream() >>> n1 = note.Note('G')