Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion music21/stream/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2699,6 +2699,35 @@ def storeAtEnd(self, itemOrList, ignoreSort=False):
# Streams cannot reside in end elements, thus do not update is flat
self.coreElementsChanged(updateIsFlat=False)

def isAtEnd(self, element: base.Music21Object) -> bool:
'''
Return True if `element` is stored at the end of this Stream
(via :meth:`~music21.stream.base.Stream.storeAtEnd`).

>>> s = stream.Stream()
>>> n = note.Note('C')
>>> s.append(n)
>>> b = bar.Barline()
>>> s.storeAtEnd(b)
>>> s.isAtEnd(b)
True
>>> s.isAtEnd(n)
False

If the element is not in this Stream, :exc:`~music21.sites.SitesException`
is raised:

>>> s.isAtEnd(note.Note('E'))
Traceback (most recent call last):
music21.sites.SitesException: an entry for this object 0x... is not stored in stream
<music21.stream.Stream 0x...>

* New in v11.

AI-assisted.
'''
return self.elementOffset(element, returnSpecial=True) is OffsetSpecial.AT_END

# --------------------------------------------------------------------------
# all the following call either insert() or append()

Expand Down Expand Up @@ -9500,7 +9529,10 @@ def findNextElementNotCoincident(
# is unsorted originally, this "looking ahead" could become O(n^2).
originallySorted = useStream.isSorted
rests_lacking_durations: list[note.Rest] = []
for i, e in enumerate(useStream._elements):
for i, e in enumerate(useStream.elements):
if useStream.isAtEnd(e):
# Elements stored at the end carry no quantizable offset.
continue
if processOffsets:
o = useStream.elementOffset(e)
sign = 1
Expand Down
46 changes: 46 additions & 0 deletions music21/stream/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -5063,6 +5063,52 @@ def testStoreAtEndFailures(self):
with self.assertRaises(StreamException):
s.storeAtEnd(b2)

def testIsAtEnd(self):
'''
https://github.com/cuthbertLab/music21/issues/1069

AI-assisted.
'''
s = Stream()
n = note.Note('C')
s.append(n)
b = bar.Barline()
s.storeAtEnd(b)
self.assertTrue(s.isAtEnd(b))
self.assertFalse(s.isAtEnd(n))

m = Measure()
nM = note.Note('D')
m.append(nM)
rb = bar.Barline('final')
m.storeAtEnd(rb)
self.assertTrue(m.isAtEnd(rb))
self.assertFalse(m.isAtEnd(nM))

outsider = note.Note('E')
with self.assertRaises(sites.SitesException):
s.isAtEnd(outsider)

def testQuantizeKeepsElementsStoredAtEnd(self):
'''
quantize() iterates .elements and skips elements stored at the end, so a
barline keeps its AT_END position instead of being given a real offset.

AI-assisted.
'''
s = Stream()
n = note.Note()
n.quarterLength = 0.26
s.repeatInsert(n, [0.1, 0.49, 0.9])
b = bar.Barline()
s.storeAtEnd(b)

q = s.quantize(processOffsets=True, processDurations=True, inPlace=False)

quantizedBarline = q._endElements[0]
self.assertTrue(q.isAtEnd(quantizedBarline))
self.assertEqual([q.elementOffset(e) for e in q._elements], [0.0, 0.5, 1.0])

def testElementsHighestTimeB(self):
'''
Test adding elements at the highest time position
Expand Down
Loading