Source code for music21.analysis.segmentByRests

# ------------------------------------------------------------------------------
# Name:         segmentByRests.py
# Purpose:      Break up a part into its contiguous melodies.
#
# Authors:      Mark Gotham
#               Michael Scott Asato Cuthbert
#
# Copyright:    Copyright © 2018 Michael Scott Asato Cuthbert
# License:      BSD, see license.txt
# ------------------------------------------------------------------------------
from __future__ import annotations

import unittest

from music21 import clef
from music21 import converter
from music21 import environment
from music21 import note
from music21 import interval

environLocal = environment.Environment('analysis.segmentByRests')

# ------------------------------------------------------------------------------
[docs] class Segmenter: ''' Given a work or part, returns a list of melodic segments or intervals. '''
[docs] @classmethod def getSegmentsList(cls, workOrPart, removeEmptyLists=True): # noinspection PyShadowingNames ''' Segments a part by its rests (and clefs) and returns a list of lists where each sublist is one segment of contiguous notes. NB Uses .recurse() internally. >>> example = converter.parse("tinyNotation: C4 r D E r r F r G r A B r c") >>> segments = analysis.segmentByRests.Segmenter.getSegmentsList(example) >>> segments [[<music21.note.Note C>], [<music21.note.Note D>, <music21.note.Note E>], [<music21.note.Note F>], [<music21.note.Note G>], [<music21.note.Note A>, <music21.note.Note B>], [<music21.note.Note C>]] ''' segments = [] thisSegment = [] partNotes = workOrPart.recurse().getElementsByClass(['Note', 'Rest', 'Clef']) for i in range(len(partNotes)): n = partNotes[i] if isinstance(n, note.Note): thisSegment.append(n) # for final segment as workOrPart usually ends with a note not clef or rest if i == len(partNotes) - 1: segments.append(thisSegment) if isinstance(n, (note.Rest, clef.Clef)): segments.append(thisSegment) thisSegment = [] continue # Optionally: Remove the empty sublists given by rests if removeEmptyLists: for segment in segments[::-1]: if not segment: segments.remove(segment) return segments
[docs] @classmethod def getIntervalList(cls, workOrPart): ''' Given a work or part, returns a list of intervals between contiguous notes. NB Uses .recurse() internally so if called on a work then returns a list of lists with one list per part. >>> example = converter.parse("tinyNotation: 4/4 E4 r F G A r g c r c") >>> intList = analysis.segmentByRests.Segmenter.getIntervalList(example) >>> [x.name for x in intList] ['M2', 'M2', 'P5'] ''' intervalList = [] elementList = workOrPart.recurse().getElementsByClass(['Note', 'Rest', 'Clef']) for i in range(len(elementList) - 1): n1 = elementList[i] if isinstance(n1, (note.Rest, clef.Clef)): continue n2 = elementList[i + 1] if isinstance(n2, (note.Rest, clef.Clef)): continue intervalObj = interval.Interval(n1, n2) intervalList.append(intervalObj) return intervalList
# ------------------------------------------------------------------------------ class Test(unittest.TestCase): def testGetSegmentsList(self): ex = converter.parse('tinyNotation: E4 r F G A r g c r c') segments = Segmenter.getSegmentsList(ex) self.assertIsInstance(segments[0], list) self.assertEqual(segments[1][0].name, 'F') def testGetIntervalList(self): ex = converter.parse('tinyNotation: E4 r F G A r g c r c') intervalList = Segmenter.getIntervalList(ex) self.assertEqual(intervalList[0].name, 'M2') self.assertIsInstance(intervalList, list) # ----------------------------------------------------------------------------- if __name__ == '__main__': import music21 music21.mainTest(Test)