def testBasicD(self): from music21 import stream, note, converter, spanner import copy s = stream.Stream() n1 = note.Note('d2', quarterLength=2.0) n2 = note.Note('e2', quarterLength=2.0) sp = spanner.Slur(n1, n2) s.append(n1) s.append(n2) s.append(sp) # the deepcopy is what creates the bug in the preservation of a weakref #temp = converter.freezeStr(s) sCopy = copy.deepcopy(s) temp = converter.freezeStr(sCopy) post = converter.thawStr(temp) self.assertEqual(len(post.notes), 2) self.assertEqual(str(post.notes[0].pitch), 'D2') spPost = post.spanners[0] self.assertEqual(spPost.getSpannedElements(), [ post.notes[0], post.notes[1]]) self.assertEqual(spPost.getSpannedElementIds(), [ id(post.notes[0]), id(post.notes[1])])
def testStreams02(self): # based on Stream.testAddSlurByMelisma(self): #from music21 import corpus, spanner nStart = None nEnd = None ex = corpus.parse('luca/gloria').parts['cantus'].measures(1, 11) exFlatNotes = ex.flat.notesAndRests.stream() nLast = exFlatNotes[-1] for i, n in enumerate(exFlatNotes): if i < len(exFlatNotes) - 1: nNext = exFlatNotes[i + 1] else: continue if n.lyrics: nStart = n # if next is a begin, then this is an end elif nStart is not None and nNext.lyrics and n.tie is None: nEnd = n elif nNext is nLast: nEnd = n if nStart is not None and nEnd is not None: nStart.addLyric(nStart.beatStr) ex.insert(spanner.Slur(nStart, nEnd)) nStart = None nEnd = None for sp in ex.spanners.getElementsByClass('Slur'): #environLocal.printDebug(['sp', n.nameWithOctave, sp]) n = sp.getFirst()
def testFreezeThawWithSpanner(self): from music21 import stream, note, spanner s = stream.Stream() sDummy = stream.Stream() n = note.Note() sl1 = spanner.Slur([n]) s.insert(0.0, sl1) s.insert(2.0, n) sDummy.insert(3.0, n) self.assertIs(s.spanners[0].getFirst(), s.notes[0]) sf = StreamFreezer(s) out = sf.writeStr(fmt='jsonpickle') # easier to read... del s del sDummy del n st = StreamThawer() st.openStr(out) outStream = st.stream self.assertEqual(len(outStream), 2) self.assertEqual(outStream.notes[0].offset, 2.0) self.assertIs(outStream.spanners[0].getFirst(), outStream.notes[0])
def addSlur(self): """Adds a slur to the neumes notes""" notes = self.notes.elements if len(notes) > 1: slur = spanner.Slur(notes) slur.priority = -1 self.insert(0, slur)
def testSpannersWrite(self): p = converter.parse("tinynotation: 4/4 c4 d e f g a b c' b a g2") listNotes = list(p.recurse().notes) c = listNotes[0] d = listNotes[1] sl1 = spanner.Slur([c, d]) p.insert(0.0, sl1) f = listNotes[3] g = listNotes[4] a = listNotes[5] sl2 = spanner.Slur([f, g, a]) p.insert(0.0, sl2) c2 = listNotes[6] g2 = listNotes[-1] sl3 = spanner.Slur([c2, g2]) p.insert(0.0, sl3) self.assertEqual(self.getXml(p).count('<slur '), 6)
def setDurationForObject(self, generalNote, durationInfo): ''' generalNote could be a Note, Chord, or Rest DurationInfo is a string like: Whole,Dotted,Slur ''' from music21 import noteworthy dictionaries = noteworthy.dictionaries parts = durationInfo.split(',') lengthnote = parts[0] thisNoteIsSlurred = False durationObject = duration.Duration( dictionaries["dictionaryNoteLength"][lengthnote]) for kk in parts: if kk == "Grace": #print "GRACE NOTE" # Now it doesn't work, the function for grace notes have to be added here environLocal.warn('skipping grace note') return elif kk == "Slur": #print "SLUR" if self.withinSlur is False: self.beginningSlurNote = generalNote thisNoteIsSlurred = True elif kk == "Dotted": durationObject.dots = 1 elif kk == "DblDotted": durationObject.dots = 2 elif kk == "Triplet" or kk == "Triplet=First" or kk == "Triplet=End": tup = duration.Tuplet(3, 2, durationObject.type) durationObject.appendTuplet(tup) generalNote.duration = durationObject # if Slur if self.withinSlur is True and thisNoteIsSlurred is False: music21SlurObj = spanner.Slur(self.beginningSlurNote, generalNote) self.currentMeasure.append(music21SlurObj) self.withinSlur = False elif thisNoteIsSlurred is True: self.withinSlur = True else: self.withinSlur = False
def testBasicF(self): from music21 import stream, note, converter, spanner s = stream.Score() s.repeatAppend(note.Note('G4'), 5) for i, syl in enumerate(['se-', 'ri-', 'al-', 'iz-', 'ing']): s.notes[i].addLyric(syl) s.append(spanner.Slur(s.notes[0], s.notes[-1])) # file writing #converter.freeze(s, fmt='pickle', fp='/_scratch/test.p') data = converter.freezeStr(s, fmt='pickle') sPost = converter.thawStr(data) self.assertEqual(len(sPost.notes), 5)
def testSpannerSerializationOfNotesNotInPickle(self): ''' test to see if spanners serialize properly if they contain notes not in the pickle... ''' from music21 import stream, spanner, converter from music21 import note n1 = note.Note("D4") n2 = note.Note("E4") n3 = note.Note("F4") slur1 = spanner.Slur([n1, n2]) s = stream.Part() s.insert(0, n3) s.insert(0, slur1) data = converter.freezeStr(s, fmt='pickle') unused_s2 = converter.thawStr(data)
def testStreams01(self): ''' Basic stream issues ''' #from music21 import note, stream, clef, metadata, spanner #==== "fig-df02" # Storing, Ordering, and Timing Elements n1 = note.Note('g3', type='half') n2 = note.Note('d4', type='half') cf1 = clef.AltoClef() m1 = stream.Measure(number=1) m1.append([n1, n2]) m1.insert(0, cf1) # the measure has three elements assert len(m1) == 3 # the offset returned is the most-recently set assert n2.offset == 2.0 # automatic sorting positions Clef first assert m1[0] == cf1 # list-like indices follow sort order assert m1.index(n2) == 2 # can find an element based on a given offset assert m1.getElementAtOrBefore(3) == n2 n3 = note.Note('g#3', quarterLength=0.5) n4 = note.Note('d-4', quarterLength=3.5) m2 = stream.Measure(number=2) m2.append([n3, n4]) # appended position is after n3 assert n4.offset == .5 assert m2.highestOffset == .5 # can access objects on elements assert m2[1].duration.quarterLength == 3.5 # the Stream duration is the highest offset + duration assert m2.duration.quarterLength == 4 p1 = stream.Part() p1.append([m1, m2]) # the part has 2 components assert len(p1) == 2 # the Stream duration is the highest offset + durations assert p1.duration.quarterLength == 8 # can access Notes from Part using multiple indices assert p1[1][0].pitch.nameWithOctave == 'G#3' s1 = stream.Score() s1.append(p1) md1 = metadata.Metadata(title='The music21 Stream') s1.insert(0, md1) # calling show by default renders musicxml output #s1.show() #==== "fig-df02" end #==== "fig-df03" # Positioning the Same Element in Multiple Containers # show positioning the same element in multiple containers # do not yet use a flat representation s2 = stream.Stream() s3 = stream.Stream() s2.insert(10, n2) s3.insert(40, n2) # the offset attribute returns the last assigned assert n2.offset == 40 # we can provide a site to finde a location-specific offset assert n2.getOffsetBySite(m1) == 2.0 assert n2.getOffsetBySite(s2) == 10 # the None site provides a default offset assert set(n2.sites.get()) == set([None, m1, s2, s3]) # the same instance is found in all Streams assert m1.hasElement(n2) == True assert s2.hasElement(n2) == True assert s3.hasElement(n2) == True # only offset is independent to each location n2.pitch.transpose('-M2', inPlace=True) assert s2[s2.index(n2)].nameWithOctave == 'C4' assert s3[s3.index(n2)].nameWithOctave == 'C4' assert m1[m1.index(n2)].nameWithOctave == 'C4' # the transposition is maintained in the original context #s1.show() #==== "fig-df03" end #==== "fig-df04" # Simultaneous Access to Hierarchical and Flat Representations #s1.flat.show('t') # lengths show the number of elements; indices are sequential s1Flat = s1.flat assert len(s1) == 2 assert len(s1Flat) == 6 assert s1Flat[4] == n3 assert s1Flat[5] == n4 # adding another Part to the Score results in a different flat representation n5 = note.Note('a#1', quarterLength=2.5) n6 = note.Note('b2', quarterLength=1.5) m4 = stream.Measure(number=2) m4.append([n5, n6]) r1 = note.Rest(type='whole') cf2 = clef.bestClef(m4) # = BassClef m3 = stream.Measure(number=1) m3.append([cf2, r1]) p2 = stream.Part() p2.append([m3, m4]) s1.insert(0, p2) assert 'BassClef' in cf2.classes # objects are sorted by offset s1Flat = s1.flat assert len(s1) == 3 assert len(s1.flat) == 10 assert s1Flat[6] == n3 assert s1Flat[7] == n5 assert s1Flat[8] == n4 assert s1Flat[9] == n6 # the F-sharp in m. 2 now as offsets for both flat non-flat sites assert n3.getOffsetBySite(m2) == 0 assert n3.getOffsetBySite(s1Flat) == 4 # the B in m. 2 now as offsets for both flat non-flat sites assert n6.getOffsetBySite(m4) == 2.5 assert n6.getOffsetBySite(s1Flat) == 6.5 #s1.show() #==== "fig-df04" end #==== "fig-df05" # Iterating and Filtering Elements by Class # get the Clef object, and report its sign, from Measure 1 assert m1.getElementsByClass('Clef').stream()[0].sign == 'C' # collect into a list the sign of all clefs in the flat Score assert [cf.sign for cf in s1.flat.getElementsByClass('Clef')] == ['C', 'F'] # collect the offsets Measures in the first part assert [e.offset for e in p1.elements] == [0.0, 4.0] # collect the offsets of Note in the first part flattened assert [e.offset for e in p1.flat.notesAndRests] == [0.0, 2.0, 4.0, 4.5] # collect the offsets of Notes in all parts flattened assert [e.offset for e in s1.flat.notesAndRests ] == [0.0, 0.0, 2.0, 4.0, 4.0, 4.5, 6.5] # get all pitch names match = [] for e in s1.flat.getElementsByClass('Note').stream(): match.append(e.pitch.nameWithOctave) assert match == ['G3', 'C4', 'G#3', 'A#1', 'D-4', 'B2'] # collect all Notes and transpose up a perfect fifth for n in s1.flat.getElementsByClass('Note').stream(): n.transpose('P5', inPlace=True) # check that all pitches are correctly transposed match = [] for e in s1.flat.getElementsByClass('Note').stream(): match.append(e.pitch.nameWithOctave) assert match == ['D4', 'G4', 'D#4', 'E#2', 'A-4', 'F#3'] #s1.show() #==== "fig-df05" end #==== "fig-df06" # Searching by Locations and Contexts # a Note can always find a Clef self.assertIs(n4.getContextByClass('Clef'), cf1) # must search oldest sites first assert n6.getContextByClass('Clef', sortByCreationTime='reverse') == cf2 # # a Note can find their Measure number from a flat Part # match = [] # for e in p1.flat.getElementsByClass('Note'): # match.append(e.getContextByClass('Measure').number) # assert match == [1, 1, 2, 2] # all Notes can find their Measure number from a flat Score match = [] for e in s1.flat.notesAndRests: match.append([e.name, e.getContextByClass('Measure').number]) assert match == [['D', 1], ['rest', 1], ['G', 1], ['D#', 2], ['E#', 2], ['A-', 2], ['F#', 2]] #==== "fig-df06" end #==== "fig-df06" # Non-Hierarchical Object Associations #oldIds = [] #for idKey in n1.sites.siteDict: # print (idKey, n1.sites.siteDict[idKey].isDead) # oldIds.append(idKey) #print("-------") # Spanners can be positioned in Parts or Measures sp1 = spanner.Slur([n1, n4]) p1.append(sp1) sp2 = spanner.Slur([n5, n6]) m4.insert(0, sp2) #print(id(sp1), id(sp1.spannerStorage), n1.sites.siteDict[id(sp1.spannerStorage)].isDead) #if id(sp1.spannerStorage) in oldIds: # print ("******!!!!!!!!!*******") # Elements can report on what Spanner they belong to ss1 = n1.getSpannerSites() self.assertTrue(sp1 in ss1, (ss1, sp1)) ss6 = n6.getSpannerSites() assert sp2 in ss6 # p1Flat = p1.flat # assert sp1.getDurationSpanBySite(p1Flat) == [0.0, 8.0] # # p2Flat = p2.flat # assert sp2.getDurationSpanBySite(p2Flat) == [4.0, 8.0] #s1.show() #==== "fig-df06" end # additional tests self.assertEqual(m1.clef, cf1)