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)