def get_offset_from_key(key): tonic, mode = key.split(' ') tonic = tonic.replace('b', '-') if mode == 'major': k = Key(tonic) else: k = Key(tonic.lower()).relative return Pitch('C').midi - k.getTonic().midi
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 __init__(self, key=None, time=None, melody='', fingering=None): TinyNotationStream.__init__(self, melody, time) if key is not None: self.insert(0, Key(key)) if fingering is not None: for (note_index, finger) in fingering: self.notes[note_index].fingering = finger
def voiceProgression(key, chordProgression): """Voices a chord progression in a specified key using DP. Follows eighteenth-century voice leading procedures, as guided by the cost function defined in the `chordCost` and `progressionCost` functions. Returns a list of four-pitch chords, corresponding to successive Roman numerals in the chord progression. """ key = Key(key) if isinstance(chordProgression, str): chordProgression = list(filter(None, chordProgression.split())) dp = [{} for _ in chordProgression] for i, numeral in enumerate(chordProgression): chord = RomanNumeral(numeral, key) voicings = voiceChord(key, chord) if i == 0: for v in voicings: dp[0][v.pitches] = (chordCost(key, v), None) else: for v in voicings: best = (float("inf"), None) for pv_pitches, (pcost, _) in dp[i - 1].items(): pv = Chord(pv_pitches) ccost = pcost + progressionCost(key, pv, v) if ccost < best[0]: best = (ccost, pv_pitches) dp[i][v.pitches] = (best[0] + chordCost(key, v), best[1]) cur, (totalCost, _) = min(dp[-1].items(), key=lambda p: p[1][0]) ret = [] for i in reversed(range(len(chordProgression))): ret.append(Chord(cur, lyric=chordProgression[i])) cur = dp[i][cur][1] return list(reversed(ret)), totalCost
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 mixed_sequences(data_path, output_path): for (number, filename) in enumerate(os.listdir(data_path), start=1): try: full_path = os.path.join(data_path, filename) LOG.debug("Finding mixed sequences in piece {number}: {filename}.".format(**locals())) piece = parse(full_path) temp_abspath = os.path.join(output_path, TEMP_MIDI_NAME) piece.write('midi', temp_abspath) chord_per_measure = temperley.chord_per_measure(piece, temp_abspath) keys = temperley.key_sequence(temp_abspath) if len(set(keys)) > 1: continue # more than one key in piece is complicated... else: key_string = convertKeyStringToMusic21KeyString(keys[0].replace('b','-')) key = Key() key_pitch = key.getPitches()[0] instrument = lambda x: x.getInstrument().instrumentName.lower() guitar_parts = (e for e in piece if isinstance (e, Part) and \ ('guitar' in instrument(e) or \ 'gtr' in instrument(e)) and not \ 'bass' in instrument(e)) for part in guitar_parts: current_sequence = None last_note = None measures = [elem for elem in part if isinstance(elem, Measure)] if len(chord_per_measure) != len(measures): continue for chord, measure in zip(chord_per_measure, measures): chord_pitch = music21.pitch.Pitch(convertKeyStringToMusic21KeyString(chord)) for element in measure: if isinstance(element, Note): if not last_note: current_sequence = [(0, 0, notesToChromatic(key_pitch, element).semitones, key.mode, notesToChromatic(key_pitch, chord_pitch).semitones)] else: interval = notesToChromatic(last_note, element) entry = (current_sequence[-1][1], interval.semitones, notesToChromatic(key_pitch, element).semitones, key.mode, notesToChromatic(key_pitch, chord_pitch).semitones) current_sequence.append(entry) last_note = element elif isinstance(element, Rest) and element.quarterLength < 4: pass elif current_sequence: yield current_sequence current_sequence = None last_note = None except Exception: LOG.warning('Encountered exception in {filename}'.format(**locals()))
def to_music21_key(key_signature: KeySignature) -> Key: """Return a KeySignature object as a music21 Key object.""" if key_signature.root_str is not None: tonic = key_signature.root_str elif key_signature.root is not None: tonic = PITCH_NAMES[key_signature.root] elif key_signature.fifths is not None: if key_signature.mode is not None: offset = MODE_CENTERS[key_signature.mode] tonic = CIRCLE_OF_FIFTHS[key_signature.fifths + offset][1] else: tonic = CIRCLE_OF_FIFTHS[key_signature.fifths][1] else: raise ValueError( "One of `root`, `root_str` or `fifths` must be specified.") key = Key(tonic=tonic, mode=key_signature.mode) key.offset = key_signature.time return key
def getKeyFromString(key): """Cached method. Calls music21.key.Key().""" cachedGetKeyFromString.append(key) return Key(key)
def to_music21_key(key_signature: KeySignature) -> Key: """Return a KeySignature object as a music21 Key object.""" key = Key(tonic=PITCH_NAMES[key_signature.root], mode=key_signature.mode) key.offset = key_signature.time return key
def get_key(file): with open(file["path"], "r") as f: for line in f: m = key_signature_pattern.match(line) if m: return Key(sharpsToPitch(int(m.group(4))), m.group(5))
def mixed_sequences(data_path, output_path): for (number, filename) in enumerate(os.listdir(data_path), start=1): try: full_path = os.path.join(data_path, filename) LOG.debug("Finding mixed sequences in piece {number}: {filename}.". format(**locals())) piece = parse(full_path) temp_abspath = os.path.join(output_path, TEMP_MIDI_NAME) piece.write('midi', temp_abspath) chord_per_measure = temperley.chord_per_measure( piece, temp_abspath) keys = temperley.key_sequence(temp_abspath) if len(set(keys)) > 1: continue # more than one key in piece is complicated... else: key_string = convertKeyStringToMusic21KeyString( keys[0].replace('b', '-')) key = Key() key_pitch = key.getPitches()[0] instrument = lambda x: x.getInstrument().instrumentName.lower() guitar_parts = (e for e in piece if isinstance (e, Part) and \ ('guitar' in instrument(e) or \ 'gtr' in instrument(e)) and not \ 'bass' in instrument(e)) for part in guitar_parts: current_sequence = None last_note = None measures = [elem for elem in part if isinstance(elem, Measure)] if len(chord_per_measure) != len(measures): continue for chord, measure in zip(chord_per_measure, measures): chord_pitch = music21.pitch.Pitch( convertKeyStringToMusic21KeyString(chord)) for element in measure: if isinstance(element, Note): if not last_note: current_sequence = [ (0, 0, notesToChromatic( key_pitch, element).semitones, key.mode, notesToChromatic(key_pitch, chord_pitch).semitones) ] else: interval = notesToChromatic(last_note, element) entry = (current_sequence[-1][1], interval.semitones, notesToChromatic(key_pitch, element).semitones, key.mode, notesToChromatic( key_pitch, chord_pitch).semitones) current_sequence.append(entry) last_note = element elif isinstance(element, Rest) and element.quarterLength < 4: pass elif current_sequence: yield current_sequence current_sequence = None last_note = None except Exception: LOG.warning( 'Encountered exception in {filename}'.format(**locals()))