def verifyCounterpointVerbose(melody1: stream.Stream, melody2: stream.Stream) -> bool: rval = True melody1 = melody1.flat.notes melody2 = melody2.flat.notes final = melody1[-1] if melody1.duration != melody2.duration: print("Durations do not match up") return False if melody2.flat.notes[-1].pitch.pitchClass != final.pitch.pitchClass: print("Don't end on same note:", melody1[-1], melody2[-1]) rval = False if not (melody2[0].pitch.pitchClass == final.pitch.pitchClass or interval.pitch.Interval(melody2[0], final).name == 'P5'): print( "Melody 2 starts on {}, not on the final or a P5 to the final {}.", melody2[0].name, final) rval = False badVerticalIntervalList = [] badHorIntM1 = [] badHorIntM2 = [] for i in range(len(melody1)): #check that all the intervals are okay if Interval(melody1[i], melody2[i]).name not in allowedVerticalIntervals: badVerticalIntervalList.append( (i + 1, Interval(melody1[i], melody2[i]))) rval = False if i != 0 and Interval( melody2[i - 1], melody2[i]).name not in allowedHorizontalIntervals: badHorIntM2.append((i - 1, i, Interval(melody2[i - 1], melody2[i]))) if not len(badVerticalIntervalList) == 0: print("Bad vertical intervals:") for v in badVerticalIntervalList: print(v) if not len(badHorIntM2) == 0: print("Bad vertical intervals:") for v in badHorIntM1: print(v) return rval
def add_step_information(notes, keySignatures): """ This function will populate the step information into Mupix note objects, it is required because music21 will not keep key signature information in measure other than the measure the key is defined in when reading musicXML. The maintainers of music21 don't believe this is an issue and won't fix it, so this and others must exist. :param [notes]: A list of Mupix NoteObjects. :type [notes]: List :param [keySignatures]: A list of Mupix KeySignatureObjects. :type [keySignatures]: List :return [List]: The original list of Mupix NoteObjects (in order) with step information included. :rtype: List """ for key in keySignatures: key_name = key.step.upper() if key.mode == "major" else key.step.lower() for note in notes: if note.part == key.part and note.measure == key.measure: note.step = Interval(noteStart=Note(Key(key_name).asKey().tonic), noteEnd=note._music21_object).semitones % 12 return notes
def calculate_ambitus(item): """ Calculate the ambitus of a phrase, monomodal section, piece, etc. Parameters ---------- item The item for which to calculate the ambitus. This can be of any type, but it must have the attributes `lowest_note` and `note_of_final` defined. """ if item.note_of_final is None: return AmbitusType.UNDEFINED interval = Interval(item.note_of_final, item.lowest_note) if 0 >= interval.semitones >= -4: return AmbitusType.AUTHENTIC elif -5 >= interval.semitones > -12: return AmbitusType.PLAGAL elif interval.semitones == -12: # TODO: which ambitus should this have? return AmbitusType.PLAGAL else: raise Exception( # pragma: no cover "Check the logic in the ambitus calculation! " "We expect the lowest note to be less than an octave " "below the main final.")
def split_voices(lead, current): """ Given two sequential notes, if the lead note contains more voices than current, then duplicate the notes in current to match lead, producing continuous voice lines :param lead: Leading note :param current: Current node """ num_lead = get_number_of_voices(lead) num_current = get_number_of_voices(current) if current.isNote: return [ Note(current.pitch, quarterLength=current.duration.quarterLength) for i in range(num_lead) ] if num_lead == num_current: return [ Note(pitch, quarterLength=current.duration.quarterLength) for pitch in current.pitches ] if current.isChord and len(current.pitches) == 0: raise RuntimeError( "The system was able to parse the file, but detected an illegal construct: empty chord" ) middle = map(itemgetter(0), [ sorted([(c, abs(Interval(e, c).cents)) for c in current.pitches], key=itemgetter(1))[0] for e in lead.pitches[1:-1] ]) return [ Note(pitch, quarterLength=current.duration.quarterLength) for pitch in chain(current.pitches[0:1], middle, current.pitches[-1:]) ]
def png_to_smart_track(png_file_name: str, track_name: str, track_duration: int, threshold: int = 0) -> SmartTrack: res = SmartTrack(name=track_name, duration=track_duration) arr = Converter.png_to_array(png_file_name) d, m = divmod(track_duration, len(arr[0])) if m is not 0: raise ValueError( f'target track duration {track_duration} is not a multiple of png file width {len(arr[0])}') for pitch, row in enumerate(arr): seq = SmartSequence(duration=track_duration) crt_interval = None for i, v in enumerate(row): if v <= threshold: if crt_interval: crt_interval.end += d else: crt_interval = Interval(start=d * i, end=d * (i + 1)) else: if crt_interval: seq.append(crt_interval) crt_interval = None if crt_interval: seq.append(crt_interval) res.put_sequence(pitch, seq) return res
def get_ambitus(*, note_of_final, lowest_note): if note_of_final is None: return AmbitusType.UNDEFINED interval = Interval(note_of_final, lowest_note) if 0 >= interval.semitones >= -4: return AmbitusType.AUTHENTIC elif -5 >= interval.semitones > -12: return AmbitusType.PLAGAL else: raise Exception( # pragma: no cover "Check the logic in the ambitus calculation! " "We expect the lowest note to be less than an octave " "below the main final.")
def get_contour(note1, note2): if note1.isRest and note2.isRest: return 's' elif note1.isRest and not note2.isRest: return 'u' elif not note1.isRest and note2.isRest: return 'd' else: interval = Interval(note1, note2) cents = interval.cents if cents == 0: return 's' if cents > 0: return 'u' return 'd'
def generateMelody(cf: stream.Stream): cflist = list(cf.flat.notes) posslist = [] for note in cflist: newlist = [] for intstr in allowedVerticalIntervals: next = note.transpose(Interval(intstr)) #only attach pitches in the mode pitches = music21.scale.Scale.extractPitchList( music21.key.Key("C", "major").getScale()) if next.pitch.pitchClass in [p.pitchClass for p in pitches]: newlist.append(next) posslist.append(newlist) return posslist
def __init__(self, note1, note2): assert isinstance(note1, Note) assert isinstance(note2, Note) self.note1 = note1 self.note2 = note2 self.interval = Interval(self.note1, self.note2) self.pc1 = self.note1.name self.pc2 = self.note2.name self.semitones = abs(self.interval.semitones) self.direction = self.interval.direction if self.direction == Direction.ASCENDING: self.bottom_pc = self.pc1 self.top_pc = self.pc2 else: self.bottom_pc = self.pc2 self.top_pc = self.pc1
def transposeKeys(keys, newTonic): """Transpose a list of keys relative to a new tonic.""" referenceKey = Key(keys[0]) newTonicKey = Key(newTonic, mode=referenceKey.mode) intervalDiff = Interval(referenceKey.tonic, newTonicKey.tonic) transposedKeys = [newTonicKey.tonicPitchNameWithCase] for k in keys[1:]: localKey = Key(k) newLocalTonic = localKey.tonic.transpose(intervalDiff) newLocalKey = Key(newLocalTonic, mode=localKey.mode) if abs(newLocalKey.sharps) >= 7: newLocalKey = Key(newLocalTonic.getEnharmonic(), mode=localKey.mode) transposedKeys.append(newLocalKey.tonicPitchNameWithCase) transposedKeys = [k.replace("-", "b") for k in transposedKeys] return transposedKeys
def has_framing_interval(notes, interval_name): return Interval(notes[0], notes[-1]).name == interval_name
def freq_to_midi(freq): base_pitch = Pitch() current_interval = Interval( integer_interval_from(freq, base_pitch.frequency)) return base_pitch.transpose(current_interval).midi
def chord_transpose(chord, transposition): new_scs = chord.transposed_by(Interval(transposition)) return new_scs
def get_interval(note1, note2): if note1.isRest or note2.isRest: return 'rest' return str(Interval(note1, note2).cents)
def _get_transposed_pitch(pitch, interv): return pitch.transpose(Interval(interv), inPlace=False)
PartEnum.TENOR: (Pitch("C3"), Pitch("G4")), PartEnum.BASS: (Pitch("E2"), Pitch("C4")), } verticalHorizontalMapping = { IntervalV.ALTO_SOPRANO: (PartEnum.SOPRANO, PartEnum.ALTO), IntervalV.TENOR_SOPRANO: (PartEnum.TENOR, PartEnum.SOPRANO), IntervalV.TENOR_ALTO: (PartEnum.TENOR, PartEnum.ALTO), IntervalV.BASS_SOPRANO: (PartEnum.BASS, PartEnum.SOPRANO), IntervalV.BASS_ALTO: (PartEnum.BASS, PartEnum.ALTO), IntervalV.BASS_TENOR: (PartEnum.BASS, PartEnum.TENOR), } perfectUnison = Interval("P1") cachedGetChordFromPitches = [] cachedGetKeyFromString = [] cachedGetPitchFromString = [] cachedGetLeadingTone = [] cachedGetVerticalIntervalsFromPitches = [] cachedGetInterval = [] cachedIsTriad = [] cachedVoiceChord = [] cachedProgressionCost = [] cachedChordCost = [] @lru_cache(maxsize=None) def getChordFromPitches(pitches):
def getInterval(p1, p2): """Cached method. Calls music21.interval.Interval().""" cachedGetInterval.append((p1, p2)) pitch1 = getPitchFromString(p1) pitch2 = getPitchFromString(p2) return Interval(noteStart=pitch1, noteEnd=pitch2)
def transpose_to_c_tonic(score): tonic = score.analyze('key').tonic transpos_interval = Interval(tonic, Pitch('C')) score.transpose(transpos_interval, inPlace=True)