Source code for music21.percussion

# ------------------------------------------------------------------------------
# Name:         percussion.py
# Purpose:      music21 classes for representing unpitched events
#
# Authors:      Jacob Tyler Walls
#               Michael Scott Asato Cuthbert
#               Christopher Ariza
#
# Copyright:    Copyright © 2006-2019 Michael Scott Asato Cuthbert
# License:      BSD, see license.txt
# ------------------------------------------------------------------------------
'''
This module contains classes for representing unpitched events such as percussion.
For backwards compatibility reasons, the module does not contain the actual
:class:`~music21.note.Unpitched` which is in the `note` module.
'''
from __future__ import annotations

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

from music21 import common
from music21 import chord
from music21 import note


if t.TYPE_CHECKING:
    from music21 import pitch


[docs] class PercussionChord(chord.ChordBase): ''' A representation of one or (normally) more than one unpitched object sharing a stem and treated like a "chord." It may also contain standard notes (pitches) for cases such as a percussionist playing a cymbal and marimba at the same time. It is a :class:`~music21.chord.ChordBase` and `:class:`~music21.note.NotRest` subclass that is NOT a :class:`~music21.chord.Chord` because one or more notes is an :class:`~music21.note.Unpitched` object. >>> vibraslapNote = note.Unpitched(displayName='D4', storedInstrument=instrument.Vibraslap()) >>> pChord = percussion.PercussionChord([vibraslapNote, note.Note('E5')]) >>> pChord <music21.percussion.PercussionChord [Vibraslap E5]> >>> pChord.isChord False A PercussionChord has "notes," just like anything deriving from ChordBase. Some of these are Unpitched objects. >>> pChord.notes (<music21.note.Unpitched 'Vibraslap'>, <music21.note.Note E>) The same notes can also be assigned to another PercussionChord: >>> pChord2 = percussion.PercussionChord() >>> pChord2.notes = pChord.notes >>> pChord2.notes (<music21.note.Unpitched 'Vibraslap'>, <music21.note.Note E>) Don't attempt setting anything but Note or Unpitched objects as notes: >>> pChord2.notes = [note.Rest()] Traceback (most recent call last): TypeError: every element of notes must be a note.Note or note.Unpitched object **Equality** Two PercussionChord objects are equal if their notes are equal *and in the same order* (this is different from Chord, but necessary because we cannot compare based just on pitch equality). >>> pChord == pChord2 True >>> pChord3 = percussion.PercussionChord([note.Unpitched('D4')]) >>> pChord == pChord3 False OMIT_FROM_DOCS See the repr of an empty percussion chord: >>> percussion.PercussionChord() <music21.percussion.PercussionChord object at 0x...> This is in OMIT ''' isChord = False def __deepcopy__(self, memo=None): new = super().__deepcopy__(memo=memo) for n in new._notes: n._chordAttached = new return new
[docs] def __eq__(self, other): ''' Returns True if all the notes are equal and in the same order. ''' if not super().__eq__(other): return False # super ensures that both have same number of notes. for my_n, other_n in zip(self.notes, other.notes): if my_n != other_n: return False return True
def __hash__(self): return id(self) >> 4 @property def notes(self) -> tuple[note.NotRest, ...]: return tuple(self._notes) @notes.setter def notes(self, newNotes: Iterable[note.Unpitched|note.Note]) -> None: ''' Sets notes to an iterable of Note or Unpitched objects. ''' if not common.isIterable(newNotes): raise TypeError('notes must be set with an iterable') if not all(isinstance(n, (note.Unpitched, note.Note)) for n in newNotes): raise TypeError('every element of notes must be a note.Note or note.Unpitched object') self._notes.clear() self.add(newNotes) def _reprInternal(self) -> str: if not self.notes: return super()._reprInternal() allNotes = [] for thisNote in self.notes: if isinstance(thisNote, note.Note): allNotes.append(thisNote.nameWithOctave) elif isinstance(thisNote, note.Unpitched): if thisNote.storedInstrument: allNotes.append(str(thisNote.storedInstrument.instrumentName)) else: allNotes.append(f'unpitched[{thisNote.displayName}]') return '[' + ' '.join(allNotes) + ']' @property def pitches(self) -> tuple[pitch.Pitch, ...]: ''' Get or set a list or tuple of all Pitch objects in this PercussionChord. Unpitched members (that at most have only display pitches) are ignored. >>> pChord = percussion.PercussionChord([note.Unpitched(displayName='D4'), note.Note('E5')]) >>> pChord.pitches (<music21.pitch.Pitch E5>,) >>> pChord.pitches = [60] >>> pChord.pitches (<music21.pitch.Pitch C4>,) Notice that setting pitches has now just cleared any existing notes, pitched or unpitched: >>> pChord.notes (<music21.note.Note C>,) ''' pitches: tuple[pitch.Pitch, ...] = tuple( component.pitch for component in self._notes if isinstance(component, note.Note)) return pitches @pitches.setter def pitches(self, value: t.Iterable[pitch.Pitch]) -> None: self._notes = [] # TODO: individual ties are not being retained here for p in value: # assumes value is an iterable of pitches or something to pass to Note __init__ self._notes.append(note.Note(p))
class Test(unittest.TestCase): pass if __name__ == '__main__': import music21 music21.mainTest(Test)