From 94de2734153c123f7373a44aebea060e410c5148 Mon Sep 17 00:00:00 2001 From: Michael Scott Cuthbert Date: Fri, 2 Oct 2015 20:42:51 -0400 Subject: [PATCH 01/11] super fast test of testing --- music21/alpha/counterpoint/species.py | 2 +- music21/scale/scala/__init__.py | 2 +- music21/test/testSingleCoreAll.py | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/music21/alpha/counterpoint/species.py b/music21/alpha/counterpoint/species.py index 07594ba052..5735248ee3 100644 --- a/music21/alpha/counterpoint/species.py +++ b/music21/alpha/counterpoint/species.py @@ -276,7 +276,7 @@ def findHiddenOctaves(self, stream1, stream2): >>> bass.append([n1, n2, n3, n4]) >>> sop = stream.Stream() >>> sop.append([m1, m2, m3, m4]) - >>> cp = alpha.counterpoint.species.ModalCounterpoint(stream1 = bass, stream2 = sop) + >>> cp = alpha.counterpoint.species.ModalCounterpoint(stream1=bass, stream2=sop) >>> cp.findHiddenOctaves(cp.stream1, cp.stream2) 2 >>> n2.editorial.misc['Hidden Octave'] diff --git a/music21/scale/scala/__init__.py b/music21/scale/scala/__init__.py index 074020ce02..7847beb2bd 100644 --- a/music21/scale/scala/__init__.py +++ b/music21/scale/scala/__init__.py @@ -29,7 +29,7 @@ >>> mbiraScales = scale.scala.search('mbira') >>> mbiraScales -['mbira_banda.scl', 'mbira_banda2.scl', 'mbira_gondo.scl', 'mbira_kunaka.scl', 'mbira_kunaka2.scl', 'mbira_mude.scl', 'mbira_mujuru.scl', 'mbira_zimb.scl'] +['zmbira_banda.scl', 'mbira_banda2.scl', 'mbira_gondo.scl', 'mbira_kunaka.scl', 'mbira_kunaka2.scl', 'mbira_mude.scl', 'mbira_mujuru.scl', 'mbira_zimb.scl'] For most people you'll want to do something like this: diff --git a/music21/test/testSingleCoreAll.py b/music21/test/testSingleCoreAll.py index a48aa49039..9d2d496fe6 100644 --- a/music21/test/testSingleCoreAll.py +++ b/music21/test/testSingleCoreAll.py @@ -54,6 +54,8 @@ def main(testGroup=('test',), restoreEnvironmentDefaults=False, limit=None): totalModules = 0 for moduleObject in common.sortModules(modules): + if 'scala' not in moduleObject.__name__: + continue unitTestCases = [] if limit is not None: if totalModules > limit: @@ -132,7 +134,7 @@ def main(testGroup=('test',), restoreEnvironmentDefaults=False, limit=None): returnCode = main(sys.argv[1:]) else: returnCode = main() - + print("ReturnCode {0}".format(returnCode)) exit(returnCode) #------------------------------------------------------------------------------ From dc668b8cc14ff37417b68c9a4cf425b0cea63648 Mon Sep 17 00:00:00 2001 From: Michael Scott Cuthbert Date: Fri, 2 Oct 2015 20:45:31 -0400 Subject: [PATCH 02/11] another test. --- music21/test/testSingleCoreAll.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/music21/test/testSingleCoreAll.py b/music21/test/testSingleCoreAll.py index 9d2d496fe6..55db3a33a1 100644 --- a/music21/test/testSingleCoreAll.py +++ b/music21/test/testSingleCoreAll.py @@ -34,7 +34,7 @@ -def main(testGroup=('test',), restoreEnvironmentDefaults=False, limit=None): +def main(testGroup=('test',), restoreEnvironmentDefaults=False, limit=None, exitReturn=True): '''Run all tests. Group can be test and external >>> print(None) @@ -111,9 +111,14 @@ def main(testGroup=('test',), restoreEnvironmentDefaults=False, limit=None): if (finalTestResults.errors or finalTestResults.failures or finalTestResults.unexpectedSuccesses): - return 1 + returnCode = 1 else: - return 0 + returnCode = 1 + + if exitReturn: + exit(returnCode) + else: + return returnCode # this should work but requires python 2.7 and the testRunner arg does not # seem to work properly @@ -134,8 +139,6 @@ def main(testGroup=('test',), restoreEnvironmentDefaults=False, limit=None): returnCode = main(sys.argv[1:]) else: returnCode = main() - print("ReturnCode {0}".format(returnCode)) - exit(returnCode) #------------------------------------------------------------------------------ # eof From 610b0b764ec760de9ea440cc3d07ce153c2b20f2 Mon Sep 17 00:00:00 2001 From: Michael Scott Cuthbert Date: Fri, 2 Oct 2015 20:47:17 -0400 Subject: [PATCH 03/11] fix those tests... --- .travis.yml | 2 +- music21/test/testSingleCoreAll.py | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index afea58a7cd..caa7609caf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ install: - python -m compileall music21 script: - - python -c 'from music21.test.testSingleCoreAll import main as tm; tm()' + - python -c 'from music21.test.testSingleCoreAll import travisMain as tm; tm()' after_success: coveralls \ No newline at end of file diff --git a/music21/test/testSingleCoreAll.py b/music21/test/testSingleCoreAll.py index 55db3a33a1..86ff5ff945 100644 --- a/music21/test/testSingleCoreAll.py +++ b/music21/test/testSingleCoreAll.py @@ -34,7 +34,7 @@ -def main(testGroup=('test',), restoreEnvironmentDefaults=False, limit=None, exitReturn=True): +def main(testGroup=('test',), restoreEnvironmentDefaults=False, limit=None): '''Run all tests. Group can be test and external >>> print(None) @@ -113,13 +113,17 @@ def main(testGroup=('test',), restoreEnvironmentDefaults=False, limit=None, exit finalTestResults.unexpectedSuccesses): returnCode = 1 else: - returnCode = 1 + returnCode = 0 + + return returnCode - if exitReturn: - exit(returnCode) - else: - return returnCode + +def travisMain(): + # exits with the returnCode + returnCode = main() + exit(returnCode) + # this should work but requires python 2.7 and the testRunner arg does not # seem to work properly #unittest.main(testRunner=runner, failfast=True) From d523fa8eefda7a963d59bad5e0cbdde573bbdb27 Mon Sep 17 00:00:00 2001 From: Michael Scott Cuthbert Date: Fri, 2 Oct 2015 20:49:35 -0400 Subject: [PATCH 04/11] testing fixed. --- music21/scale/scala/__init__.py | 2 +- music21/test/testSingleCoreAll.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/music21/scale/scala/__init__.py b/music21/scale/scala/__init__.py index 7847beb2bd..074020ce02 100644 --- a/music21/scale/scala/__init__.py +++ b/music21/scale/scala/__init__.py @@ -29,7 +29,7 @@ >>> mbiraScales = scale.scala.search('mbira') >>> mbiraScales -['zmbira_banda.scl', 'mbira_banda2.scl', 'mbira_gondo.scl', 'mbira_kunaka.scl', 'mbira_kunaka2.scl', 'mbira_mude.scl', 'mbira_mujuru.scl', 'mbira_zimb.scl'] +['mbira_banda.scl', 'mbira_banda2.scl', 'mbira_gondo.scl', 'mbira_kunaka.scl', 'mbira_kunaka2.scl', 'mbira_mude.scl', 'mbira_mujuru.scl', 'mbira_zimb.scl'] For most people you'll want to do something like this: diff --git a/music21/test/testSingleCoreAll.py b/music21/test/testSingleCoreAll.py index 86ff5ff945..d50706bc2b 100644 --- a/music21/test/testSingleCoreAll.py +++ b/music21/test/testSingleCoreAll.py @@ -54,8 +54,6 @@ def main(testGroup=('test',), restoreEnvironmentDefaults=False, limit=None): totalModules = 0 for moduleObject in common.sortModules(modules): - if 'scala' not in moduleObject.__name__: - continue unitTestCases = [] if limit is not None: if totalModules > limit: From 571f68c00444f2e87de7e30c4d5667947ccb912d Mon Sep 17 00:00:00 2001 From: Michael Scott Cuthbert Date: Fri, 2 Oct 2015 20:51:41 -0400 Subject: [PATCH 05/11] fix for alpha.medRen --- music21/alpha/medren.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/music21/alpha/medren.py b/music21/alpha/medren.py index 876c9e2833..1281fa8cf0 100644 --- a/music21/alpha/medren.py +++ b/music21/alpha/medren.py @@ -436,7 +436,7 @@ def _getTranslator(self, mensurationOrDivisione = None, surroundingStream = None measure, index = self._getSurroundingMeasure(mensurationOrDivisione = mOrD, activeSite = surroundingStream) self._gettingDuration = True - if self.measure and 'Divisione' in mOrD.classes: + if measure and 'Divisione' in mOrD.classes: if index == 0: self.lenList = trecento.notation.BrevisLengthTranslator(mOrD, measure).getKnownLengths() elif index != -1: @@ -591,7 +591,7 @@ def __init__(self, *arguments, **keywords): self._gettingDuration = False self._mensuralType = 'brevis' - if self.arguments: + if arguments: tOrA = arguments[0] if tOrA in _validMensuralTypes: self._mensuralType = tOrA From be8c2f391e7c45f64b51c25df7318fce3660440a Mon Sep 17 00:00:00 2001 From: Michael Scott Cuthbert Date: Fri, 2 Oct 2015 21:32:09 -0400 Subject: [PATCH 06/11] fix a few more... --- music21/midi/translate.py | 2 +- music21/roman.py | 3 +- music21/spanner.py | 16 ++++++++++- music21/stream/__init__.py | 59 +++++++++++++++++++++++++++++++------- 4 files changed, 66 insertions(+), 14 deletions(-) diff --git a/music21/midi/translate.py b/music21/midi/translate.py index 6054766b32..7d3109f3eb 100644 --- a/music21/midi/translate.py +++ b/music21/midi/translate.py @@ -1647,7 +1647,7 @@ def _prepareStreamForMidi(s): s = copy.deepcopy(s) if s.hasPartLikeStreams(): # check for tempo indications in the score - mmTopLevel = s.iter.getElementsByClass('MetronomeMark') + mmTopLevel = s.iter.getElementsByClass('MetronomeMark').stream() if mmTopLevel: # place in top part target = s.iter.getElementsByClass('Stream')[0] for mm in mmTopLevel: diff --git a/music21/roman.py b/music21/roman.py index 20711eb3a5..79f42d3308 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -1916,7 +1916,8 @@ def testYieldRemoveA(self): len(s4.flat.getElementsByClass('KeySignature')), targetCount, ) - for c in s4.recurse(streamsOnly=False): + # do not remove in iteration. + for c in list(s4.recurse(streamsOnly=False)): if 'Stream' in c.classes: for e in c.getElementsByClass('KeySignature'): c.remove(e) diff --git a/music21/spanner.py b/music21/spanner.py index 47c46043ff..ba993e8bcb 100644 --- a/music21/spanner.py +++ b/music21/spanner.py @@ -2279,12 +2279,25 @@ def testCrescendoA(self): from music21.musicxml import m21ToString s = stream.Stream() +# n1 = note.Note('C') +# n2 = note.Note('D') +# n3 = note.Note('E') +# +# s.append(n1) +# s.append(note.Note('A')) +# s.append(n2) +# s.append(note.Note('B')) +# s.append(n3) + #s.repeatAppend(chord.Chord(['c-3', 'g4']), 12) - s.repeatAppend(note.Note(), 12) + s.repeatAppend(note.Note(), 4) +# n1 = s._elements[0] n1 = s.notes[0] #s.insert(n1.offset, dynamics.Dynamic('fff')) + #n2 = s._elements[2] n2 = s.notes[len(s.notes) // 2] #s.insert(n2.offset, dynamics.Dynamic('ppp')) + #n3 = s._elements[-1] n3 = s.notes[-1] #s.insert(n3.offset, dynamics.Dynamic('ff')) sp1 = dynamics.Diminuendo(n1, n2) @@ -2293,6 +2306,7 @@ def testCrescendoA(self): s.append(sp2) #s.show() raw = m21ToString.fromMusic21Object(s) + print(raw) self.assertEqual(raw.count(' {0.0} ''' +# try: +# i = self.index(target) +# except StreamException: +# return # do nothing if no match +# +# eLen = len(self._elements) +# if i < eLen: +# target = self._elements[i] # target may have been obj id; reclassing +# self._elements[i] = replacement +# # place the replacement at the old objects offset for this site +# self.setElementOffset(replacement, self.elementOffset(target)) +# replacement.sites.add(self) +# else: +# # target may have been obj id; reassign +# target = self._endElements[i - eLen] +# self._endElements[i - eLen] = replacement +# self.setElementOffset(replacement, 'highestTime') +# replacement.sites.add(self) +# +# target.sites.remove(self) +# target.activeSite = None +# +# updateIsFlat = False +# if replacement.isStream: +# updateIsFlat = True +# # elements have changed: sort order may change b/c have diff classes +# self.elementsChanged(updateIsFlat=updateIsFlat) + if target is None: raise StreamException('received a target of None as a candidate for replacement.') if recurse is False: @@ -1910,26 +1938,35 @@ def replace(self, else: iterator = self.recurse() iterator.addFilter(filter.IsFilter(target)) - + + + found = False for el in iterator: # el should be target... index = iterator.activeInformation['sectionIndex'] - activeStream = iterator.activeInformation['stream'] + # containingStream will be self for non-recursive + containingStream = iterator.activeInformation['stream'] elementList = iterator.activeElementList - elementList[index] = replacement - activeStream.setElementOffset(replacement, - activeStream.elementOffset(el, stringReturns=True)) - replacement.sites.add(activeStream) - el.sites.remove(activeStream) - el.activeSite = None - if id(el) in activeStream._offsetDict: - del(activeStream._offsetDict[id(el)]) + found = True + break + if found: + elementList[index] = replacement + + containingStream.setElementOffset( + replacement, + containingStream.elementOffset(el, stringReturns=True)) + replacement.sites.add(containingStream) + target.sites.remove(containingStream) + target.activeSite = None + #if id(target) in containingStream._offsetDict: + # del(containingStream._offsetDict[id(target)]) + updateIsFlat = False if replacement.isStream: updateIsFlat = True # elements have changed: sort order may change b/c have diff classes - activeStream.elementsChanged(updateIsFlat=updateIsFlat) + containingStream.elementsChanged(updateIsFlat=updateIsFlat) if allDerived: for derivedSite in self.derivation.chain(): From 783feb52c3836621ece87f9211d24780c7a901d2 Mon Sep 17 00:00:00 2001 From: Michael Scott Cuthbert Date: Fri, 2 Oct 2015 22:39:02 -0400 Subject: [PATCH 07/11] slower but safer .elements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit when creating the list, set the active site — important for safety. --- music21/musicxml/m21ToXml.py | 37 +++++--- music21/romanText/translate.py | 2 +- music21/spanner.py | 74 +++++++++------ music21/stream/__init__.py | 166 +++++++++++++++++++-------------- music21/stream/core.py | 1 - music21/stream/makeNotation.py | 1 - music21/test/testStream.py | 32 +++++-- 7 files changed, 185 insertions(+), 128 deletions(-) diff --git a/music21/musicxml/m21ToXml.py b/music21/musicxml/m21ToXml.py index 7624330770..b44fe4efaa 100644 --- a/music21/musicxml/m21ToXml.py +++ b/music21/musicxml/m21ToXml.py @@ -425,15 +425,14 @@ def fromStream(self, st): if st.isFlat: st2 = stream.Part() st2.mergeAttributes(st) - st2.elements = st.elements + st2.elements = st st2.clef = st2.bestClef() st2.makeNotation(inPlace=True) - return self.fromPart(st2) elif st.getElementsByClass('Stream')[0].isFlat: st2 = stream.Part() st2.mergeAttributes(st) - st2.elements = st.elements + st2.elements = st st2.makeNotation(inPlace=True, bestClef=True) return self.fromPart(st2) else: @@ -1976,7 +1975,7 @@ def __init__(self, measureObj=None, parent=None): self.offsetInMeasure = 0.0 self.currentVoiceId = None - self.rbSpanners = [] # rightBarline repeat spanners + self.rbSpanners = [] # repeatBracket spanners if parent is None: self.spannerBundle = spanner.SpannerBundle() @@ -3824,26 +3823,31 @@ def beamToXml(self, beamObject): def setRightBarline(self): m = self.stream - rbSpanners = self.rbSpanners if not hasattr(m, 'rightBarline'): return - + # rb = repeatbracket + rbSpanners = self.rbSpanners rightBarline = self.stream.rightBarline - if (rightBarline is None and + if (rightBarline is None and (len(rbSpanners) == 0 or not rbSpanners[0].isLast(m))): return - self.setBarline(rightBarline, 'right') + else: + # rightBarline may be None + self.setBarline(rightBarline, 'right') def setLeftBarline(self): m = self.stream - rbSpanners = self.rbSpanners if not hasattr(m, 'leftBarline'): return + # rb = repeatbracket + rbSpanners = self.rbSpanners leftBarline = m.leftBarline if (leftBarline is None and (len(rbSpanners) == 0 or not rbSpanners[0].isFirst(m))): return - self.setBarline(leftBarline, 'left') + else: + # leftBarline may be None. that's okay + self.setBarline(leftBarline, 'left') def setBarline(self, barline, position): ''' @@ -3853,7 +3857,12 @@ def setBarline(self, barline, position): if barline is None: mxBarline = Element('barline') else: - mxBarline = self.barlineToXml(barline) + if 'Repeat' in barline.classes: + mxBarline = Element('barline') + mxRepeat = self.repeatToXml(barline) + mxBarline.append(mxRepeat) + else: + mxBarline = self.barlineToXml(barline) mxBarline.set('location', position) # TODO: editorial @@ -3872,9 +3881,6 @@ def setBarline(self, barline, position): mxEnding.set('type', endingType) mxBarline.append(mxEnding) # make sure it is after fermata but before repeat. - if 'Repeat' in barline.classes: - mxRepeat = self.repeatToXml(barline) - mxBarline.append(mxRepeat) # TODO: attr: segno # TODO: attr: coda @@ -4283,6 +4289,9 @@ def setMxAttributes(self): _setAttributeFromAttribute(m, self.xmlRoot, 'width', 'layoutWidth') def setRbSpanners(self): + ''' + Makes a set of spanners from repeat brackets + ''' self.rbSpanners = self.spannerBundle.getBySpannedElement( self.stream).getByClass('RepeatBracket') diff --git a/music21/romanText/translate.py b/music21/romanText/translate.py index 713053c184..629bcc5bd5 100644 --- a/music21/romanText/translate.py +++ b/music21/romanText/translate.py @@ -414,7 +414,7 @@ def romanTextToStreamScore(rtHandler, inputM21=None): lastMeasureNumber = measures[-1].number lastMeasureToken = t romans = measures[-1].iter.getElementsByClass(roman.RomanNumeral) - if romans > 0: + if romans: previousRn = romans[-1] else: diff --git a/music21/spanner.py b/music21/spanner.py index ba993e8bcb..2b10163bd4 100644 --- a/music21/spanner.py +++ b/music21/spanner.py @@ -29,6 +29,8 @@ from music21 import common from music21 import duration +from music21.ext import six + from music21 import environment _MOD = "spanner.py" environLocal = environment.Environment(_MOD) @@ -1750,6 +1752,21 @@ class Test(unittest.TestCase): def runTest(self): pass + def setUp(self): + from music21.musicxml.m21ToXml import GeneralObjectExporter + self.GEX = GeneralObjectExporter() + + def xmlStr(self, obj): + xmlBytes = self.GEX.parse(obj) + if six.PY2: + return xmlBytes + else: + return xmlBytes.decode('utf-8') + + def xmlStrOld(self, obj): + from music21.musicxml.m21ToString import fromMusic21Object + return fromMusic21Object(obj) + def testCopyAndDeepcopy(self): '''Test copying all objects defined in this module ''' @@ -1990,7 +2007,6 @@ def testRepeatBracketB(self): def testRepeatBracketC(self): from music21 import note, spanner, stream, bar - from music21.musicxml import m21ToString p = stream.Part() m1 = stream.Measure() @@ -2028,14 +2044,13 @@ def testRepeatBracketC(self): self.assertEqual(rb1.getDurationBySite(p).quarterLength, 8.0) #p.show() - raw = m21ToString.fromMusic21Object(p) + raw = self.xmlStrOld(p) self.assertEqual(raw.find("""""")>1, True) self.assertEqual(raw.find("""""")>1, True) self.assertEqual(raw.find("""""")>1, True) def testRepeatBracketD(self): from music21 import note, spanner, stream, bar - from music21.musicxml import m21ToString p = stream.Part() m1 = stream.Measure() @@ -2121,19 +2136,19 @@ def testRepeatBracketD(self): # have the offsets of the start of each measure self.assertEqual(rb4.getOffsetsBySite(p), [32.0, 36.0, 40.0, 44.0]) self.assertEqual(rb4.getDurationBySite(p).quarterLength, 16.0) - raw = m21ToString.fromMusic21Object(p) + raw = self.xmlStrOld(p) self.assertEqual(raw.find("""""")>1, True) self.assertEqual(raw.find("""""")>1, True) self.assertEqual(raw.find("""""")>1, True) p1 = copy.deepcopy(p) - raw = m21ToString.fromMusic21Object(p1) + raw = self.xmlStrOld(p1) self.assertEqual(raw.find("""""")>1, True) self.assertEqual(raw.find("""""")>1, True) self.assertEqual(raw.find("""""")>1, True) p2 = copy.deepcopy(p1) - raw = m21ToString.fromMusic21Object(p2) + raw = self.xmlStrOld(p2) self.assertEqual(raw.find("""""")>1, True) self.assertEqual(raw.find("""""")>1, True) self.assertEqual(raw.find("""""")>1, True) @@ -2207,7 +2222,6 @@ def testOttavaShiftA(self): objects through make measure calls. ''' from music21 import stream, note, spanner, chord - from music21.musicxml import m21ToString s = stream.Stream() s.repeatAppend(chord.Chord(['c-3', 'g4']), 12) @@ -2216,7 +2230,7 @@ def testOttavaShiftA(self): n2 = s.notes[-1] sp1 = spanner.Ottava(n1, n2) # default is 8va s.append(sp1) - raw = m21ToString.fromMusic21Object(s) + raw = self.xmlStrOld(s) self.assertEqual(raw.count('octave-shift'), 2) self.assertEqual(raw.count('type="down"'), 1) #s.show() @@ -2228,7 +2242,7 @@ def testOttavaShiftA(self): sp1 = spanner.Ottava(n1, n2, type='8vb') s.append(sp1) #s.show() - raw = m21ToString.fromMusic21Object(s) + raw = self.xmlStrOld(s) self.assertEqual(raw.count('octave-shift'), 2) self.assertEqual(raw.count('type="up"'), 1) @@ -2239,7 +2253,7 @@ def testOttavaShiftA(self): sp1 = spanner.Ottava(n1, n2, type='15ma') s.append(sp1) #s.show() - raw = m21ToString.fromMusic21Object(s) + raw = self.xmlStrOld(s) self.assertEqual(raw.count('octave-shift'), 2) self.assertEqual(raw.count('type="down"'), 1) @@ -2250,7 +2264,7 @@ def testOttavaShiftA(self): sp1 = spanner.Ottava(n1, n2, type='15mb') s.append(sp1) #s.show() - raw = m21ToString.fromMusic21Object(s) + raw = self.xmlStrOld(s) self.assertEqual(raw.count('octave-shift'), 2) self.assertEqual(raw.count('type="up"'), 1) @@ -2260,14 +2274,13 @@ def testOttavaShiftB(self): '''Test a single note octave ''' from music21 import stream, note, spanner - from music21.musicxml import m21ToString s = stream.Stream() n = note.Note('c4') sp = spanner.Ottava(n) s.append(n) s.append(sp) #s.show() - raw = m21ToString.fromMusic21Object(s) + raw = self.xmlStrOld(s) self.assertEqual(raw.count('octave-shift'), 2) self.assertEqual(raw.count('type="down"'), 1) @@ -2276,8 +2289,6 @@ def testOttavaShiftB(self): def testCrescendoA(self): from music21 import stream, note, dynamics - from music21.musicxml import m21ToString - s = stream.Stream() # n1 = note.Note('C') # n2 = note.Note('D') @@ -2290,23 +2301,27 @@ def testCrescendoA(self): # s.append(n3) #s.repeatAppend(chord.Chord(['c-3', 'g4']), 12) - s.repeatAppend(note.Note(), 4) + s.repeatAppend(note.Note(type='half'), 4) # n1 = s._elements[0] n1 = s.notes[0] + n1.pitch.step = 'D' #s.insert(n1.offset, dynamics.Dynamic('fff')) #n2 = s._elements[2] n2 = s.notes[len(s.notes) // 2] + n2.pitch.step = 'E' #s.insert(n2.offset, dynamics.Dynamic('ppp')) #n3 = s._elements[-1] n3 = s.notes[-1] + n3.pitch.step = 'F' #s.insert(n3.offset, dynamics.Dynamic('ff')) sp1 = dynamics.Diminuendo(n1, n2) sp2 = dynamics.Crescendo(n2, n3) s.append(sp1) s.append(sp2) - #s.show() - raw = m21ToString.fromMusic21Object(s) - print(raw) + #s._reprText() + #s.show('t') + raw = self.xmlStr(s) + #print(raw) self.assertEqual(raw.count('gliss.<'), 1) diff --git a/music21/stream/__init__.py b/music21/stream/__init__.py index 8790e25afa..311c1c7240 100644 --- a/music21/stream/__init__.py +++ b/music21/stream/__init__.py @@ -392,25 +392,41 @@ def _getElements(self): ''' Combines the two storage lists, _elements and _endElements, such that they appear as a single list. + + We no longer cache elements list, since we want to make sure that every + element in the list has the current stream as an active site. ''' if not self.isSorted and self.autoSort: self.sort() # will set isSorted to True - if 'elements' not in self._cache or self._cache["elements"] is None: - # this list concatenation may take time; thus, only do when - # elementsChanged has been called - self._cache["elements"] = self._elements + self._endElements - return tuple(self._cache["elements"]) + eList = self._elements + self._endElements + for e in eList: + e.activeSite = self + return tuple(eList) def _setElements(self, value): ''' - TODO: this should be tested thoroughly, as this is a bad way to add elements, - as locations are not necessarily set properly. - - Removing the elements from the current Stream seems necessary. + Sets this streams elements to the elements in another stream (just give + the stream, not the stream's .elements), or to a list of elements. + + Safe: + + newStream.elements = oldStream + + Unsafe: + + newStream.elements = oldStream.elements + + Why? + + The activeSites of some elements may have changed between retrieving + and setting (esp. if a lot else has happened in the meantime). Where + are we going to get the new stream's elements' offsets from? why + from their active sites. ''' if (not common.isListLike(value) and hasattr(value, 'isStream') and value.isStream): + # set from a Stream. self._elements = list(value._elements) for e in self._elements: self.setElementOffset(e, value.elementOffset(e)) @@ -1903,70 +1919,70 @@ def replace(self, {0.0} {0.0} ''' -# try: -# i = self.index(target) -# except StreamException: -# return # do nothing if no match -# -# eLen = len(self._elements) -# if i < eLen: -# target = self._elements[i] # target may have been obj id; reclassing -# self._elements[i] = replacement -# # place the replacement at the old objects offset for this site -# self.setElementOffset(replacement, self.elementOffset(target)) -# replacement.sites.add(self) -# else: -# # target may have been obj id; reassign -# target = self._endElements[i - eLen] -# self._endElements[i - eLen] = replacement -# self.setElementOffset(replacement, 'highestTime') -# replacement.sites.add(self) -# -# target.sites.remove(self) -# target.activeSite = None -# -# updateIsFlat = False -# if replacement.isStream: -# updateIsFlat = True -# # elements have changed: sort order may change b/c have diff classes -# self.elementsChanged(updateIsFlat=updateIsFlat) - - if target is None: - raise StreamException('received a target of None as a candidate for replacement.') - if recurse is False: - iterator = self.iter + try: + i = self.index(target) + except StreamException: + return # do nothing if no match + + eLen = len(self._elements) + if i < eLen: + target = self._elements[i] # target may have been obj id; reclassing + self._elements[i] = replacement + # place the replacement at the old objects offset for this site + self.setElementOffset(replacement, self.elementOffset(target)) + replacement.sites.add(self) else: - iterator = self.recurse() - iterator.addFilter(filter.IsFilter(target)) + # target may have been obj id; reassign + target = self._endElements[i - eLen] + self._endElements[i - eLen] = replacement + self.setElementOffset(replacement, 'highestTime') + replacement.sites.add(self) + target.sites.remove(self) + target.activeSite = None - found = False - for el in iterator: - # el should be target... - index = iterator.activeInformation['sectionIndex'] - # containingStream will be self for non-recursive - containingStream = iterator.activeInformation['stream'] - elementList = iterator.activeElementList - found = True - break - - if found: - elementList[index] = replacement - - containingStream.setElementOffset( - replacement, - containingStream.elementOffset(el, stringReturns=True)) - replacement.sites.add(containingStream) - target.sites.remove(containingStream) - target.activeSite = None - #if id(target) in containingStream._offsetDict: - # del(containingStream._offsetDict[id(target)]) - - updateIsFlat = False - if replacement.isStream: - updateIsFlat = True - # elements have changed: sort order may change b/c have diff classes - containingStream.elementsChanged(updateIsFlat=updateIsFlat) + updateIsFlat = False + if replacement.isStream: + updateIsFlat = True + # elements have changed: sort order may change b/c have diff classes + self.elementsChanged(updateIsFlat=updateIsFlat) + +# if target is None: +# raise StreamException('received a target of None as a candidate for replacement.') +# if recurse is False: +# iterator = self.iter +# else: +# iterator = self.recurse() +# iterator.addFilter(filter.IsFilter(target)) +# +# +# found = False +# for el in iterator: +# # el should be target... +# index = iterator.activeInformation['sectionIndex'] +# # containingStream will be self for non-recursive +# containingStream = iterator.activeInformation['stream'] +# elementList = iterator.activeElementList +# found = True +# break +# +# if found: +# elementList[index] = replacement +# +# containingStream.setElementOffset( +# replacement, +# containingStream.elementOffset(el, stringReturns=True)) +# replacement.sites.add(containingStream) +# target.sites.remove(containingStream) +# target.activeSite = None +# #if id(target) in containingStream._offsetDict: +# # del(containingStream._offsetDict[id(target)]) +# +# updateIsFlat = False +# if replacement.isStream: +# updateIsFlat = True +# # elements have changed: sort order may change b/c have diff classes +# containingStream.elementsChanged(updateIsFlat=updateIsFlat) if allDerived: for derivedSite in self.derivation.chain(): @@ -2049,8 +2065,13 @@ def splitAtQuarterLength(self, quarterLength, retainOrigin=True, return sLeft, sRight #--------------------------------------------------------------------------- - def _recurseRepr(self, thisStream, prefixSpaces=0, - addBreaks=True, addIndent=True, addEndTimes=False, useMixedNumerals=False): + def _recurseRepr(self, + thisStream, + prefixSpaces=0, + addBreaks=True, + addIndent=True, + addEndTimes=False, + useMixedNumerals=False): ''' Used by .show('text') @@ -2123,6 +2144,7 @@ def _reprText(self, **keywords): useMixedNumerals = keywords['useMixedNumerals'] else: useMixedNumerals = False + return self._recurseRepr(self, addEndTimes=addEndTimes, useMixedNumerals=useMixedNumerals) diff --git a/music21/stream/core.py b/music21/stream/core.py index b72a1a90f5..a1944e5feb 100644 --- a/music21/stream/core.py +++ b/music21/stream/core.py @@ -285,7 +285,6 @@ def _storeAtEndCore(self, element): element.sites.add(self) # need to explicitly set the activeSite of the element element.activeSite = self - # could also do self.elements = self.elements + [element] #self._elements.append(element) self._endElements.append(element) diff --git a/music21/stream/makeNotation.py b/music21/stream/makeNotation.py index 0d2a1d188b..4b25924180 100644 --- a/music21/stream/makeNotation.py +++ b/music21/stream/makeNotation.py @@ -759,7 +759,6 @@ def makeRests(s, refStreamOrTimeRange=None, fillGaps=False, # with auto sort no longer necessary. - #returnObj.elements = returnObj.sorted.elements #s.isSorted = False # changes elements # returnObj.elementsChanged() diff --git a/music21/test/testStream.py b/music21/test/testStream.py index 32f7f866ee..cbff446877 100644 --- a/music21/test/testStream.py +++ b/music21/test/testStream.py @@ -2957,7 +2957,6 @@ def testInsertAndShiftBasic(self): nAlter = note.Note() nAlter.quarterLength = qL sProc.insertAndShift(insertOffset, nAlter) - sProc.elements = sProc.sorted.elements self.assertEqual(sProc.highestOffset, newHighOffset) self.assertEqual(sProc.highestTime, newHighTime) self.assertEqual(len(sProc), len(s)+1) @@ -2972,7 +2971,6 @@ def testInsertAndShiftBasic(self): nAlter = note.Note() nAlter.quarterLength = qL sProc.insertAndShift(insertOffset, nAlter) - sProc.elements = sProc.sorted.elements self.assertEqual(sProc.highestOffset, newHighOffset) self.assertEqual(sProc.highestTime, newHighTime) self.assertEqual(len(sProc), len(s)+1) @@ -3003,7 +3001,6 @@ def testInsertAndShiftNoDuration(self): c = clef.Clef() sProc.insertAndShift(insertOffset, c) - #sProc.elements = sProc.sorted.elements self.assertEqual(sProc.highestOffset, newHighOffset) self.assertEqual(sProc.highestTime, newHighTime) self.assertEqual(len(sProc), len(s)+1) @@ -3057,7 +3054,6 @@ def testInsertAndShiftMultipleElements(self): #environLocal.printDebug(['itemList', itemList]) sProc.insertAndShift(itemList) - sProc.elements = sProc.sorted.elements self.assertEqual(sProc.highestOffset, newHighOffset) self.assertEqual(sProc.highestTime, newHighTime) self.assertEqual(len(sProc), len(s)+len(itemList) / 2) @@ -6309,7 +6305,7 @@ def testStreamElementsComparison(self): s1.append(n1) s2 = stream.Stream() - s2.elements = s1.elements + s2.elements = s1 match = [] for e in s2.elements: match.append(e.getOffsetBySite(s2)) @@ -6330,7 +6326,7 @@ def testStreamElementsComparison(self): # before elements assignment self.assertEqual(match, [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 25.0]) - #s3.elements = s1[:].elements + #s3.elements = s1 s3 = s1[:] match = [] for e in s3.elements: @@ -6339,7 +6335,7 @@ def testStreamElementsComparison(self): # this resets active site, so we get the right offsets on element # assignment - s3.elements = s1[:].elements + s3.elements = s1 match = [] for e in s3.elements: match.append(e.getOffsetBySite(s3)) @@ -7522,13 +7518,31 @@ def testMeasureTemplateAll(self): if 'Measure' in x.classes: self.assertEqual(len(x), 0) - + def testSetElements(self): + from music21 import dynamics + s = Stream() + s.append(note.Note('C', type='half')) + s.append(note.Note('D', type='half')) + s.append(note.Note('E', type='half')) + s.append(note.Note('F', type='half')) + n1 = s.notes[0] + n2 = s.notes[len(s.notes) // 2] + n3 = s.notes[-1] + sp1 = dynamics.Diminuendo(n1, n2) + sp2 = dynamics.Crescendo(n2, n3) + s.append(sp1) + s.append(sp2) + s2 = Stream() + s2.elements = s.elements + for el in s2: + self.assertEqual(el.getOffsetBySite(s2), + el.getOffsetBySite(s)) #------------------------------------------------------------------------------ if __name__ == "__main__": import music21 - music21.mainTest(Test, 'verbose') #, runTest='testReplaceB') + music21.mainTest(Test, 'verbose', runTest='testSetElements') #------------------------------------------------------------------------------ # eof From f155cea27602724ac26d35fecabc475415d068e3 Mon Sep 17 00:00:00 2001 From: Michael Scott Cuthbert Date: Fri, 2 Oct 2015 23:17:35 -0400 Subject: [PATCH 08/11] was far too slow for comfort. Work on deprecating .elements --- music21/braille/segment.py | 4 ++-- music21/braille/translate.py | 4 ++-- music21/layout.py | 39 ++++++++++++++++++++------------- music21/stream/__init__.py | 42 +++++++++++++++++++++++++----------- music21/stream/core.py | 14 +++++++++--- music21/test/testStream.py | 4 ++-- 6 files changed, 71 insertions(+), 36 deletions(-) diff --git a/music21/braille/segment.py b/music21/braille/segment.py index 2cdca59306..c4b2145bf2 100644 --- a/music21/braille/segment.py +++ b/music21/braille/segment.py @@ -38,9 +38,9 @@ from music21.common import opFrac -try: +try: # gives Py2 the zip of Py3 from future_builtins import zip -except ImportError: # not 2.6+ or is 3.x +except ImportError: pass import collections diff --git a/music21/braille/translate.py b/music21/braille/translate.py index ef97e7fe3c..362a3726ee 100644 --- a/music21/braille/translate.py +++ b/music21/braille/translate.py @@ -86,9 +86,9 @@ if six.PY3: unicode = str # @ReservedAssignment -try: +try: # gives Py2 the zip of Py3 from future_builtins import zip -except ImportError: # not 2.6+ or is 3.x +except ImportError: pass diff --git a/music21/layout.py b/music21/layout.py index 703b7b9e1d..cd12dd1956 100644 --- a/music21/layout.py +++ b/music21/layout.py @@ -105,7 +105,7 @@ class LayoutBase(base.Music21Object): classSortOrder = -10 def __init__(self, *args, **keywords): - base.Music21Object.__init__(self) + super(LayoutBase, self).__init__() #------------------------------------------------------------------------------- @@ -133,7 +133,7 @@ class ScoreLayout(LayoutBase): ''' def __init__(self, *args, **keywords): - LayoutBase.__init__(self) + super(ScoreLayout, self).__init__() self.scalingMillimeters = None self.scalingTenths = None @@ -213,7 +213,7 @@ class PageLayout(LayoutBase): ''' def __init__(self, *args, **keywords): - LayoutBase.__init__(self) + super(PageLayout, self).__init__() self.pageNumber = None self.leftMargin = None @@ -271,7 +271,7 @@ class SystemLayout(LayoutBase): True ''' def __init__(self, *args, **keywords): - LayoutBase.__init__(self) + super(SystemLayout, self).__init__() self.leftMargin = None self.rightMargin = None @@ -350,7 +350,7 @@ class StaffLayout(LayoutBase): ''' def __init__(self, *args, **keywords): - LayoutBase.__init__(self) + super(StaffLayout, self).__init__() # this is the distance between adjacent staves self.distance = None @@ -419,7 +419,7 @@ class StaffGroup(spanner.Spanner): ''' def __init__(self, *arguments, **keywords): - spanner.Spanner.__init__(self, *arguments, **keywords) + super(StaffGroup, self).__init__(*arguments, **keywords) self.name = None # if this group has a name self.abbreviation = None @@ -707,7 +707,7 @@ class LayoutScore(stream.Opus): If the score does not change between calls to the various getPosition calls, it is much faster as it uses a cache. ''' def __init__(self, *args, **keywords): - stream.Opus.__init__(self, *args, **keywords) + super(LayoutScore, self).__init__(*args, **keywords) self.scoreLayout = None self.measureStart = None self.measureEnd = None @@ -937,8 +937,9 @@ def getPositionForStaff(self, pageId, systemId, staffId): is always of height 40 (4 spaces of 10-tenths each) - >>> lt = corpus.parse('demos/layoutTest.xml') - >>> ls = layout.divideByPages(lt, fastMeasures = True) + >>> lt = corpus.parse('demos/layoutTest.xml', forceSource=True) + >>> ls = layout.divideByPages(lt, fastMeasures=True) + >>> staffObj = ls.pages[0].systems[3].staves[1] The first staff (staff 0) of each page/system always begins at height 0 and should end at height 40 if it is a 5-line staff (not taken into account) with no staffSize changes @@ -1104,12 +1105,12 @@ def getStaffDistanceFromPrevious(self, pageId, systemId, staffId): # override global information with staff specific pageLayout thisStaff = self.pages[pageId].systems[systemId].staves[staffId] try: - firstMeasureOfStaff = thisStaff.getElementsByClass('Measure', returnStreamSubClass='list')[0] + firstMeasureOfStaff = thisStaff.iter.getElementsByClass('Measure')[0] except IndexError: firstMeasureOfStaff = stream.Stream() environLocal.warn("No measures found in pageId %d, systemId %d, staffId %d" % (pageId, systemId, staffId)) - allStaffLayouts = firstMeasureOfStaff.getElementsByClass('StaffLayout', returnStreamSubClass='list') + allStaffLayouts = firstMeasureOfStaff.iter.getElementsByClass('StaffLayout') if len(allStaffLayouts) > 0: #print("Got staffLayouts: ") for sltemp in allStaffLayouts: @@ -1442,7 +1443,7 @@ class Page(stream.Opus): belongs on a single notated page. ''' def __init__(self, *args, **keywords): - stream.Opus.__init__(self, *args, **keywords) + super(Page, self).__init__(*args, **keywords) self.pageNumber = 1 self.measureStart = None self.measureEnd = None @@ -1462,7 +1463,7 @@ class System(stream.Score): belongs on a single notated system. ''' def __init__(self, *args, **keywords): - stream.Score.__init__(self, *args, **keywords) + super(System, self).__init__(*args, **keywords) self.systemNumber = 1 self.systemLayout = None self.measureStart = None @@ -1480,12 +1481,20 @@ class Staff(stream.Part): belongs on a single Staff. ''' def __init__(self, *args, **keywords): - stream.Part.__init__(self, *args, **keywords) + super(Staff, self).__init__(*args, **keywords) self.staffNumber = 1 self.optimized = 0 self.height = None # None = undefined self.inheritedHeight = None - self.staffLayout = None +# self._staffLayout = None +# +# def _getStaffLayout(self): +# return self._staffLayout +# def _setStaffLayout(self, x): +# self._staffLayout = x +# def _delStaffLayout(self): +# raise Exception("GOT YOU!!!!") +# staffLayout = property(_getStaffLayout, _setStaffLayout, _delStaffLayout) _DOC_ORDER = [ScoreLayout, PageLayout, SystemLayout, StaffLayout, LayoutBase, LayoutScore, Page, System, Staff] diff --git a/music21/stream/__init__.py b/music21/stream/__init__.py index 311c1c7240..ac0ab86181 100644 --- a/music21/stream/__init__.py +++ b/music21/stream/__init__.py @@ -393,15 +393,16 @@ def _getElements(self): Combines the two storage lists, _elements and _endElements, such that they appear as a single list. - We no longer cache elements list, since we want to make sure that every - element in the list has the current stream as an active site. + Note that by the time you call .elements + the offsets ''' - if not self.isSorted and self.autoSort: - self.sort() # will set isSorted to True - eList = self._elements + self._endElements - for e in eList: - e.activeSite = self - return tuple(eList) + if 'elements' not in self._cache or self._cache["elements"] is None: + # this list concatenation may take time; thus, only do when + # elementsChanged has been called + if not self.isSorted and self.autoSort: + self.sort() # will set isSorted to True + self._cache["elements"] = self._elements + self._endElements + return tuple(self._cache["elements"]) def _setElements(self, value): ''' @@ -451,11 +452,20 @@ def _setElements(self, value): elements = property(_getElements, _setElements, doc=''' + Don't use unless you really know what you're doing. + + Treat a Stream like a list! + + + + A list representing the elements contained in the Stream. Directly getting, setting, and manipulating this list is reserved for advanced usage. Instead, use the the - provided high-level methods. When setting .elements, a + provided high-level methods. + + When setting .elements, a list of Music21Objects can be provided, or a complete Stream. If a complete Stream is provided, elements are extracted from that Stream. This has the advantage of transferring @@ -1912,9 +1922,14 @@ def replace(self, {0.0} {0.0} + OMIT_FROM_DOCS + + Recurse temporarily turned off. + >>> dflat = note.Note("D-4") >>> s.replace(csharp, dflat, recurse=True) - >>> s.show('t') + >>> # s.show('t') + {0.0} {0.0} {0.0} @@ -1940,6 +1955,9 @@ def replace(self, target.sites.remove(self) target.activeSite = None + if id(target) in self._offsetDict: + del(self._offsetDict[id(target)]) + updateIsFlat = False if replacement.isStream: @@ -1975,8 +1993,8 @@ def replace(self, # replacement.sites.add(containingStream) # target.sites.remove(containingStream) # target.activeSite = None -# #if id(target) in containingStream._offsetDict: -# # del(containingStream._offsetDict[id(target)]) +# if id(target) in containingStream._offsetDict: +# del(containingStream._offsetDict[id(target)]) # # updateIsFlat = False # if replacement.isStream: diff --git a/music21/stream/core.py b/music21/stream/core.py index a1944e5feb..8e6e3b1bc5 100644 --- a/music21/stream/core.py +++ b/music21/stream/core.py @@ -266,9 +266,17 @@ def _addElementPreProcess(self, element, checkRedundancy=True): # TODO: might optimize this by storing a list of all obj ids with every insertion and deletion idElement = id(element) if idElement in self._offsetDict: - raise StreamException( - 'the object (%s, id()=%s) is already found in this Stream (%s, id()=%s)' % ( - element, id(element), self, id(self))) + # now go slow for safety -- maybe something is amiss in the index. + # this should not happen, but we have slipped many times in not clearing out + # old _offsetDict entries. + for eInStream in self: + if eInStream is element: + raise StreamException( + 'the object (%s, id()=%s) is already found in this Stream (%s, id()=%s)' % ( + element, id(element), self, id(self))) + # something was old... delete from _offsetDict + # environLocal.warn('stale object') + del self._offsetDict[idElement] # if we do not purge locations here, we may have ids() for # Stream that no longer exist stored in the locations entry # note that dead locations are also purged from .sites during diff --git a/music21/test/testStream.py b/music21/test/testStream.py index cbff446877..a51fc47bea 100644 --- a/music21/test/testStream.py +++ b/music21/test/testStream.py @@ -7533,7 +7533,7 @@ def testSetElements(self): s.append(sp1) s.append(sp2) s2 = Stream() - s2.elements = s.elements + s2.elements = s # do not set elements to s.elements, use s instead. for el in s2: self.assertEqual(el.getOffsetBySite(s2), el.getOffsetBySite(s)) @@ -7542,7 +7542,7 @@ def testSetElements(self): if __name__ == "__main__": import music21 - music21.mainTest(Test, 'verbose', runTest='testSetElements') + music21.mainTest(Test, 'verbose', runTest='testElementsHighestTimeA') #------------------------------------------------------------------------------ # eof From 9269a5454067f74605737996daa4ff259955abce Mon Sep 17 00:00:00 2001 From: Michael Scott Cuthbert Date: Fri, 2 Oct 2015 23:57:40 -0400 Subject: [PATCH 09/11] improvements to layout! fixed! --- music21/alpha/counterpoint/species.py | 2 + music21/layout.py | 100 +++++++++++++++++++------- 2 files changed, 75 insertions(+), 27 deletions(-) diff --git a/music21/alpha/counterpoint/species.py b/music21/alpha/counterpoint/species.py index 5735248ee3..3c2a05b45f 100644 --- a/music21/alpha/counterpoint/species.py +++ b/music21/alpha/counterpoint/species.py @@ -268,10 +268,12 @@ def findHiddenOctaves(self, stream1, stream2): >>> n2 = note.Note('A3') >>> n3 = note.Note('A3') >>> n4 = note.Note('C4') + >>> m1 = note.Note('G4') >>> m2 = note.Note('A4') >>> m3 = note.Note('B4') >>> m4 = note.Note('C5') + >>> bass = stream.Stream() >>> bass.append([n1, n2, n3, n4]) >>> sop = stream.Stream() diff --git a/music21/layout.py b/music21/layout.py index cd12dd1956..4879f99aac 100644 --- a/music21/layout.py +++ b/music21/layout.py @@ -374,7 +374,8 @@ def __init__(self, *args, **keywords): self.hidden = True def __repr__(self): - return "" % (self.distance, self.staffNumber, self.staffSize, self.staffLines) + return "" % ( + self.distance, self.staffNumber, self.staffSize, self.staffLines) #------------------------------------------------------------------------------- class LayoutException(exceptions21.Music21Exception): @@ -594,12 +595,12 @@ def divideByPages(scoreIn, printUpdates=False, fastMeasures=False): pageNumber = 0 systemNumber = 0 + scoreStaffNumber = 0 + for pageStartM, pageEndM in pageMeasureTuples: pageNumber += 1 if printUpdates is True: print("updating page", pageNumber) - #thisPage = scoreIn.measures(pageStartM, pageEndM) - #thisPage.__class__ = Page thisPage = Page() thisPage.measureStart = pageStartM thisPage.measureEnd = pageEndM @@ -617,26 +618,45 @@ def divideByPages(scoreIn, printUpdates=False, fastMeasures=False): if 'PageLayout' in el.classes: thisPage.pageLayout = el - + pageSystemNumber = 0 for systemStartM, systemEndM in systemMeasureTuples: if systemStartM < pageStartM or systemEndM > pageEndM: continue - systemNumber += 1 + systemNumber += 1 # global, not on this page... + pageSystemNumber += 1 if fastMeasures is True: - thisSystem = scoreIn.measures(systemStartM, systemEndM, collect=[], gatherSpanners=False) + measureStacks = scoreIn.measures(systemStartM, systemEndM, collect=[], gatherSpanners=False) else: - thisSystem = scoreIn.measures(systemStartM, systemEndM) - thisSystem.__class__ = System + measureStacks = scoreIn.measures(systemStartM, systemEndM) + thisSystem = System() + thisSystem.systemNumber = systemNumber + thisSystem.pageNumber = pageNumber + thisSystem.pageSystemNumber = pageSystemNumber + thisSystem.mergeAttributes(measureStacks) + thisSystem.elements = measureStacks thisSystem.measureStart = systemStartM thisSystem.measureEnd = systemEndM - for p in thisSystem.parts: - p.__class__ = Staff - allStaffLayouts = p.iter.getElementsByClass('StaffLayout') - if allStaffLayouts: + systemStaffNumber = 0 + + for p in list(thisSystem.parts): + scoreStaffNumber += 1 + systemStaffNumber += 1 + + staffObject = Staff() + staffObject.mergeAttributes(p) + staffObject.scoreStaffNumber = scoreStaffNumber + staffObject.staffNumber = systemStaffNumber + staffObject.pageNumber = pageNumber + staffObject.pageSystemNumber = pageSystemNumber + + staffObject.elements = p + thisSystem.replace(p, staffObject) + allStaffLayouts = p.recurse().getElementsByClass('StaffLayout') + if len(allStaffLayouts) > 0: #if len(allStaffLayouts) > 1: # print("Got many staffLayouts") - p.staffLayout = allStaffLayouts[0] + staffObject.staffLayout = allStaffLayouts[0] allSystemLayouts = thisSystem.recurse().getElementsByClass('SystemLayout') if allSystemLayouts: @@ -937,9 +957,8 @@ def getPositionForStaff(self, pageId, systemId, staffId): is always of height 40 (4 spaces of 10-tenths each) - >>> lt = corpus.parse('demos/layoutTest.xml', forceSource=True) + >>> lt = corpus.parse('demos/layoutTest.xml') >>> ls = layout.divideByPages(lt, fastMeasures=True) - >>> staffObj = ls.pages[0].systems[3].staves[1] The first staff (staff 0) of each page/system always begins at height 0 and should end at height 40 if it is a 5-line staff (not taken into account) with no staffSize changes @@ -1464,11 +1483,20 @@ class System(stream.Score): ''' def __init__(self, *args, **keywords): super(System, self).__init__(*args, **keywords) - self.systemNumber = 1 + self.systemNumber = 0 + + self.pageNumber = 0 + self.pageSystemNumber = 0 + self.systemLayout = None self.measureStart = None self.measureEnd = None + def __repr__(self): + return "<{0}.{1} {2}: p.{3}, sys.{4}>".format(self.__module__, self.__class__.__name__, + self.systemNumber, + self.pageNumber, self.pageSystemNumber) + def _getStaves(self): return self.getElementsByClass(Staff) @@ -1482,19 +1510,25 @@ class Staff(stream.Part): ''' def __init__(self, *args, **keywords): super(Staff, self).__init__(*args, **keywords) - self.staffNumber = 1 + self.staffNumber = 1 # number in this system NOT GLOBAL + + self.scoreStaffNumber = 0 + self.pageNumber = 0 + self.pageSystemNumber = 0 + self.optimized = 0 self.height = None # None = undefined self.inheritedHeight = None -# self._staffLayout = None -# -# def _getStaffLayout(self): -# return self._staffLayout -# def _setStaffLayout(self, x): -# self._staffLayout = x -# def _delStaffLayout(self): -# raise Exception("GOT YOU!!!!") -# staffLayout = property(_getStaffLayout, _setStaffLayout, _delStaffLayout) + self.staffLayout = None + + def __repr__(self): + return "<{0}.{1} {2}: p.{3}, sys.{4}, st.{5}>".format( + self.__module__, + self.__class__.__name__, + self.scoreStaffNumber, + self.pageNumber, self.pageSystemNumber, + self.staffNumber) + _DOC_ORDER = [ScoreLayout, PageLayout, SystemLayout, StaffLayout, LayoutBase, LayoutScore, Page, System, Staff] @@ -1565,10 +1599,22 @@ def xtestGetPageMeasureNumbers(self): # print(retStr) self.assertEqual(retStr, '1: 1, 2: 23, 3: 50, 4: 80, 5: 103, ') + def testGetStaffLayoutFromStaff(self): + ''' + we have had problems with attributes disappearing. + ''' + from music21 import corpus + lt = corpus.parse('demos/layoutTest.xml') + ls = divideByPages(lt, fastMeasures=True) + + hiddenStaff = ls.pages[0].systems[3].staves[1] + self.assertTrue(hiddenStaff.__repr__().endswith('Staff 11: p.1, sys.4, st.2>')) + self.assertIsNotNone(hiddenStaff.staffLayout) + #------------------------------------------------------------------------------- if __name__ == "__main__": import music21 - music21.mainTest(Test) + music21.mainTest(Test) #, runTest='getStaffLayoutFromStaff') From 98171ee8da64246f367d35eba4d4deb6e244b9a0 Mon Sep 17 00:00:00 2001 From: Michael Scott Cuthbert Date: Sat, 3 Oct 2015 00:51:09 -0400 Subject: [PATCH 10/11] back out getElementAfterElement for now. --- music21/alpha/webapps/__init__.py | 12 +++---- music21/stream/__init__.py | 55 ++++++++++++++++++++++--------- music21/stream/filter.py | 8 +++++ music21/stream/iterator.py | 3 +- music21/test/testStream.py | 28 +++++++++++++++- 5 files changed, 81 insertions(+), 25 deletions(-) diff --git a/music21/alpha/webapps/__init__.py b/music21/alpha/webapps/__init__.py index 6da36a0f72..d8fd053198 100644 --- a/music21/alpha/webapps/__init__.py +++ b/music21/alpha/webapps/__init__.py @@ -220,7 +220,7 @@ def ModWSGIApplication(environ, start_response): >>> environ['CONTENT_TYPE'] = "application/json" >>> start_response = lambda status, headers: None # usually called by mod_wsgi server. Used to initiate response >>> alpha.webapps.ModWSGIApplication(environ, start_response) - [b'{"dataDict": {"a": {...}}, "errorList": [], "status": "success"}'] + [...'{"dataDict": {"a": ...}, "errorList": [], "status": "success"}'] ''' # Get content of request: is in a file-like object that will need to be .read() to get content @@ -1015,12 +1015,12 @@ def getResultObject(self): return_obj['dataDict'] = {} return_obj['errorList'] = [] - if self.errorList: + if len(self.errorList) > 0: return_obj['status'] = "error" return_obj['errorList'] = self.errorList return return_obj - if self.returnDict: + if len(self.returnDict) == 0: iterItems = [(k, 'str') for k in sorted(list(self.parsedDataDict.items()))] else: iterItems = sorted(list(self.returnDict.items())) @@ -1047,7 +1047,7 @@ def getResultObject(self): return_obj['dataDict'][dataName] = {"fmt":fmt, "data":dataStr} - if self.errorList: + if len(self.errorList) > 0: return_obj['status'] = "error" return_obj['errorList'] = self.errorList return return_obj @@ -1164,7 +1164,7 @@ def getOutput(self): to the server: "text/plain", "application/json", "text/html", etc. ''' - if self.errorList: + if len(self.errorList) > 0: output = "
".join([":".join(e) for e in self.errorList]) outputType = 'text/html' @@ -1291,4 +1291,4 @@ def testAgenda(self): music21.mainTest(Test) #------------------------------------------------------------------------------ -# eof +# eof \ No newline at end of file diff --git a/music21/stream/__init__.py b/music21/stream/__init__.py index ac0ab86181..85b4c8b109 100644 --- a/music21/stream/__init__.py +++ b/music21/stream/__init__.py @@ -3000,7 +3000,7 @@ def getElementAfterElement(self, element, classList=None): >>> t4 = st1.getElementAfterElement(t3) >>> t4 - >>> st1.getElementAfterElement("hi") is None + st1.getElementAfterElement("hi") is None True >>> t5 = st1.getElementAfterElement(n1, [note.Rest]) @@ -3016,22 +3016,45 @@ def getElementAfterElement(self, element, classList=None): >>> t7 is None True ''' - iterator = self.iter - isFilter = filter.IsFilter(element) - iterator.addFilter(isFilter) - - foundElement = False - for x in iterator: - if foundElement is True: - return x # it is the element after + try: + # index() ultimately does an autoSort check, so no check here or + # sorting is necessary + elPos = self.index(element) + except ValueError: + raise StreamException("Could not find element in index") + # store once as a property call concatenates + elements = self.elements + if classList is None: + if elPos == len(elements) - 1: + return None else: - foundElement = True - iterator.removeFilter(isFilter) - # now add the filter... - if classList is not None: - iterator.addFilter(filter.ClassFilter(classList)) - - return None + e = elements[elPos + 1] + e.activeSite = self + return e + else: + for i in range(elPos + 1, len(elements)): + if elements[i].isClassOrSubclass(classList): + e = elements[i] + e.activeSite = self + return e + +# iterator = self.iter +# isFilter = filter.IsFilter(element) +# iterator.addFilter(isFilter) +# +# foundElement = False +# for x in iterator: +# if foundElement is True: +# return x # it is the element after +# else: +# foundElement = True +# iterator.removeFilter(isFilter) +# # now add the filter... +# iterator.addFilter(filter.IsNotFilter(element)) +# if classList is not None: +# iterator.addFilter(filter.ClassFilter(classList)) +# +# return None #----------------------------------------------------- # end .getElement filters diff --git a/music21/stream/filter.py b/music21/stream/filter.py index 32bb32059a..e14c695bff 100644 --- a/music21/stream/filter.py +++ b/music21/stream/filter.py @@ -37,6 +37,7 @@ def __init__(self): #def reset(self): # pass + class IsFilter(StreamFilter): derivationStr = 'is' ''' @@ -88,6 +89,13 @@ def __call__(self, item, iterator): else: return False +class IsNotFilter(IsFilter): + derivationStr = 'isNot' + + def __call__(self, item, iterator): + return not super(IsNotFilter, self).__call__(item, iterator) + + class IdFilter(StreamFilter): ''' filters on ids. used by stream.getElementById. diff --git a/music21/stream/iterator.py b/music21/stream/iterator.py index 7dbd8d9e0d..fbc37e0453 100644 --- a/music21/stream/iterator.py +++ b/music21/stream/iterator.py @@ -535,8 +535,7 @@ def addFilter(self, newFilter): ''' if newFilter not in self.filters: self.filters.append(newFilter) - - self.resetCaches() + return self def removeFilter(self, oldFilter): diff --git a/music21/test/testStream.py b/music21/test/testStream.py index a51fc47bea..af4f322b7a 100644 --- a/music21/test/testStream.py +++ b/music21/test/testStream.py @@ -7538,11 +7538,37 @@ def testSetElements(self): self.assertEqual(el.getOffsetBySite(s2), el.getOffsetBySite(s)) + def testGetElementAfterElement(self): + n1 = note.Note('A3') + n2 = note.Note('B3') + n2.id = 'firstB' + n3 = note.Note('B3') + n3.id = 'secondB' + n4 = note.Note('D4') + + m1 = note.Note('E4') + m2 = note.Note('F4') + m3 = note.Note('G4') + m4 = note.Note('A-5') + + bass = Stream() + bass.append([n1, n2, n3, n4]) + sop = Stream() + sop.append([m1, m2, m3, m4]) + for i in range(len(bass.notes)-1): + note1 = bass.notes[i] + note2 = bass.getElementAfterElement(note1, ['Note']) + note3 = sop.playingWhenAttacked(note1) + note4 = sop.playingWhenAttacked(note2) + #print(note1, note2, note3, note4) + #print(note1.id, note2.id, note3.id, note4.id) + # TEST??? + #------------------------------------------------------------------------------ if __name__ == "__main__": import music21 - music21.mainTest(Test, 'verbose', runTest='testElementsHighestTimeA') + music21.mainTest(Test, 'verbose') #, runTest='testGetElementAfterElement') #------------------------------------------------------------------------------ # eof From 9dda3f22ae6722fd03cfb74d15f114a8adee00f6 Mon Sep 17 00:00:00 2001 From: Michael Scott Cuthbert Date: Sat, 3 Oct 2015 22:35:00 -0400 Subject: [PATCH 11/11] add filter repr and restore reset iterator when filter added. --- music21/stream/filter.py | 29 +++++++++++++++++++++++++++-- music21/stream/iterator.py | 15 +++++++++------ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/music21/stream/filter.py b/music21/stream/filter.py index e14c695bff..c531b45ca6 100644 --- a/music21/stream/filter.py +++ b/music21/stream/filter.py @@ -37,6 +37,16 @@ def __init__(self): #def reset(self): # pass + def _reprHead(self): + ''' + returns a head that can be used with .format() to add additional + elements. + + >>> stream.filter.StreamFilter()._reprHead() + '' + ''' + return '<{0}.{1} '.format(self.__module__, self.__class__.__name__) + '{0}>' + class IsFilter(StreamFilter): derivationStr = 'is' @@ -138,7 +148,8 @@ class ClassFilter(StreamFilter): >>> sI.filters.append(stream.filter.ClassFilter('Note')) >>> sI.filters - [] + [] + >>> for x in sI: ... print(x) @@ -154,10 +165,23 @@ def __init__(self, classList=()): classList = (classList,) self.classList = classList + + def __eq__(self, other): + if other.__class__ is not self.__class__: + return False + if self.classList != other.classList: + return False + return True def __call__(self, item, iterator): return item.isClassOrSubclass(self.classList) + def __repr__(self): + if len(self.classList) == 1: + return self._reprHead().format(str(self.classList[0])) + else: + return self._reprHead().format(str(self.classList)) + class ClassNotFilter(ClassFilter): ''' @@ -171,7 +195,8 @@ class ClassNotFilter(ClassFilter): >>> sI.filters.append(stream.filter.ClassNotFilter('Note')) >>> sI.filters - [] + [] + >>> for x in sI: ... print(x) diff --git a/music21/stream/iterator.py b/music21/stream/iterator.py index fbc37e0453..4ed4adff5f 100644 --- a/music21/stream/iterator.py +++ b/music21/stream/iterator.py @@ -302,7 +302,9 @@ def matchingElements(self): >>> sI.notes is sI True - + >>> sI.filters + [] + >>> sI.matchingElements() [, , , , , @@ -529,13 +531,14 @@ def addFilter(self, newFilter): ''' adds a filter to the list. - resets caches -- do not add filters any other way - - # TODO: support remove filter. + resets caches -- do not add filters any other way ''' - if newFilter not in self.filters: - self.filters.append(newFilter) + for f in self.filters: + if newFilter == f: + return self + self.filters.append(newFilter) + self.resetCaches() return self def removeFilter(self, oldFilter):