1# -*- coding: utf-8 -*-
2# -----------------------------------------------------------------------------
3# Name:         stream/base.py
4# Purpose:      base classes for dealing with groups of positioned objects
5#
6# Authors:      Michael Scott Cuthbert
7#               Christopher Ariza
8#               Josiah Wolf Oberholtzer
9#               Evan Lynch
10#
11# Copyright:    Copyright © 2008-2021 Michael Scott Cuthbert and the music21 Project
12# License:      BSD, see license.txt
13# -----------------------------------------------------------------------------
14'''
15The :class:`~music21.stream.Stream` and its subclasses,
16a subclass of the :class:`~music21.base.Music21Object`,
17is the fundamental container of offset-positioned notation and
18musical elements in music21. Common Stream subclasses, such
19as the :class:`~music21.stream.Measure`
20and :class:`~music21.stream.Score` objects, are defined in
21this module.
22'''
23import collections
24import copy
25import itertools
26import math
27import os
28import pathlib
29import unittest
30import sys
31
32from collections import namedtuple
33from fractions import Fraction
34from math import isclose
35from typing import Dict, Union, List, Optional, Set, Tuple, Sequence, TypeVar
36
37from music21 import base
38
39from music21 import bar
40from music21 import common
41from music21 import clef
42from music21 import chord
43from music21 import defaults
44from music21 import derivation
45from music21 import duration
46from music21 import exceptions21
47from music21 import interval
48from music21 import key
49from music21 import metadata
50from music21 import meter
51from music21 import note
52from music21 import pitch  # for typing
53from music21 import tie
54from music21 import repeat
55from music21 import sites
56from music21 import style
57from music21 import tempo
58
59from music21.stream import core
60from music21.stream import makeNotation
61from music21.stream import streamStatus
62from music21.stream import iterator
63from music21.stream import filters
64
65from music21.common.numberTools import opFrac
66from music21.common.enums import GatherSpanners, OffsetSpecial
67
68from music21 import environment
69
70environLocal = environment.Environment('stream')
71
72StreamException = exceptions21.StreamException
73ImmutableStreamException = exceptions21.ImmutableStreamException
74
75T = TypeVar('T')
76StreamType = TypeVar('StreamType', bound='music21.stream.Stream')
77
78BestQuantizationMatch = namedtuple('BestQuantizationMatch',
79    ['error', 'tick', 'match', 'signedError', 'divisor'])
80
81class StreamDeprecationWarning(UserWarning):
82    # Do not subclass Deprecation warning, because these
83    # warnings need to be passed to users...
84    pass
85
86
87# -----------------------------------------------------------------------------
88# Metaclass
89_OffsetMap = collections.namedtuple('OffsetMap', ['element', 'offset', 'endTime', 'voiceIndex'])
90
91
92# -----------------------------------------------------------------------------
93
94
95class Stream(core.StreamCoreMixin, base.Music21Object):
96    '''
97    This is the fundamental container for Music21Objects;
98    objects may be ordered and/or placed in time based on
99    offsets from the start of this container.
100
101    As a subclass of Music21Object, Streams have offsets,
102    priority, id, and groups.
103
104    Streams may be embedded within other Streams. As each
105    Stream can have its own offset, when Streams are
106    embedded the offset of an element is relatively only
107    to its parent Stream. The :meth:`~music21.stream.Stream.flatten`
108    and method provides access to a flat version of all
109    embedded Streams, with offsets relative to the
110    top-level Stream.
111
112    The Stream :attr:`~music21.stream.Stream.elements` attribute
113    returns the contents of the Stream as a list. Direct access
114    to, and manipulation of, the elements list is not recommended.
115    Instead, use the host of high-level methods available.
116
117    The Stream, like all Music21Objects, has a
118    :class:`music21.duration.Duration` that is usually the
119    "release" time of the chronologically last element in the Stream
120    (that is, the highest onset plus the duration of
121    any element in the Stream).
122    The duration, however, can be "unlinked" and explicitly
123    set independent of the Stream's contents.
124
125    The first element passed to the Stream is an optional single
126    Music21Object or a list, tuple, or other Stream of Music21Objects
127    which is used to populate the Stream by inserting each object at
128    its :attr:`~music21.base.Music21Object.offset`
129    property. One special case is when every such object, such as a newly created
130    one, has no offset. Then, so long as the entire list is not composed of
131    non-Measure Stream subclasses representing synchrony like Parts or Voices,
132    each element is appended, creating a sequence of elements in time,
133    rather than synchrony.
134
135    Other arguments and keywords are ignored, but are
136    allowed so that subclassing the Stream is easier.
137
138    >>> s1 = stream.Stream()
139    >>> s1.append(note.Note('C#4', type='half'))
140    >>> s1.append(note.Note('D5', type='quarter'))
141    >>> s1.duration.quarterLength
142    3.0
143    >>> for thisNote in s1.notes:
144    ...     print(thisNote.octave)
145    ...
146    4
147    5
148
149    This is a demonstration of creating a Stream with other elements,
150    including embedded Streams (in this case, :class:`music21.stream.Part`,
151    a Stream subclass):
152
153    >>> c1 = clef.TrebleClef()
154    >>> c1.offset = 0.0
155    >>> c1.priority = -1
156    >>> n1 = note.Note('E-6', type='eighth')
157    >>> n1.offset = 1.0
158    >>> p1 = stream.Part()
159    >>> p1.offset = 0.0
160    >>> p1.id = 'embeddedPart'
161    >>> p1.append(note.Rest())  # quarter rest
162    >>> s2 = stream.Stream([c1, n1, p1])
163    >>> s2.duration.quarterLength
164    1.5
165    >>> s2.show('text')
166    {0.0} <music21.clef.TrebleClef>
167    {0.0} <music21.stream.Part embeddedPart>
168        {0.0} <music21.note.Rest quarter>
169    {1.0} <music21.note.Note E->
170
171    New in v7 -- providing a single element now works:
172
173    >>> s = stream.Stream(meter.TimeSignature())
174    >>> s.first()
175    <music21.meter.TimeSignature 4/4>
176
177    New in v7 -- providing a list of objects or Measures or Scores (but not other Stream
178    subclasses such as Parts or Voices) now positions sequentially, i.e. appends:
179
180    >>> s2 = stream.Measure([note.Note(), note.Note(), bar.Barline()])
181    >>> s2.show('text')
182    {0.0} <music21.note.Note C>
183    {1.0} <music21.note.Note C>
184    {2.0} <music21.bar.Barline type=regular>
185
186    A list of measures will let each be appended:
187
188    >>> m1 = stream.Measure(n1, number=1)
189    >>> m2 = stream.Measure(note.Rest(), number=2)
190    >>> s3 = stream.Part([m1, m2])
191    >>> s3.show('text')
192    {0.0} <music21.stream.Measure 1 offset=0.0>
193        {1.0} <music21.note.Note E->
194    {1.5} <music21.stream.Measure 2 offset=1.5>
195        {0.0} <music21.note.Rest quarter>
196
197    Here, every element is a Stream that's not a Measure, so we instead insert:
198
199    >>> s4 = stream.Score([stream.PartStaff(n1), stream.PartStaff(note.Rest())])
200    >>> s4.show('text')
201    {0.0} <music21.stream.PartStaff 0x...>
202        {1.0} <music21.note.Note E->
203    {0.0} <music21.stream.PartStaff 0x...>
204        {0.0} <music21.note.Rest quarter>
205
206    Create nested streams in one fell swoop:
207
208    >>> s5 = stream.Score(stream.Part(stream.Measure(chord.Chord('C2 A2'))))
209    >>> s5.show('text')
210    {0.0} <music21.stream.Part 0x...>
211        {0.0} <music21.stream.Measure 0 offset=0.0>
212            {0.0} <music21.chord.Chord C2 A2>
213    '''
214    # this static attributes offer a performance boost over other
215    # forms of checking class
216    isStream = True
217    isMeasure = False
218    classSortOrder = -20
219    recursionType = 'elementsFirst'
220
221    _styleClass = style.StreamStyle
222
223    # define order to present names in documentation; use strings
224    _DOC_ORDER = ['append', 'insert', 'storeAtEnd', 'insertAndShift',
225                  'recurse', 'flat',
226                  'notes', 'pitches',
227                  'transpose',
228                  'augmentOrDiminish', 'scaleOffsets', 'scaleDurations']
229    # documentation for all attributes (not properties or methods)
230    _DOC_ATTR = {
231        'recursionType': '''
232            Class variable:
233
234            String of ('elementsFirst' (default), 'flatten', 'elementsOnly)
235            that decides whether the stream likely holds relevant
236            contexts for the elements in it.
237
238            Define this for a stream class, not an individual object.
239
240            see :meth:`~music21.base.Music21Object.contextSites`
241            ''',
242        'isSorted': '''
243            Boolean describing whether the Stream is sorted or not.
244            ''',
245        'autoSort': '''
246            Boolean describing whether the Stream is automatically sorted by
247            offset whenever necessary.
248            ''',
249        'isFlat': '''
250            Boolean describing whether this Stream contains embedded
251            sub-Streams or Stream subclasses (not flat).
252            ''',
253        'definesExplicitSystemBreaks': '''
254            Boolean that says whether all system breaks in the piece are
255            explicitly defined.  Only used on musicxml output (maps to the
256            musicxml <supports attribute="new-system"> tag) and only if this is
257            the outermost Stream being shown
258            ''',
259        'definesExplicitPageBreaks': '''
260            Boolean that says whether all page breaks in the piece are
261            explicitly defined.  Only used on musicxml output (maps to the
262            musicxml <supports attribute="new-page"> tag) and only if this is
263            the outermost Stream being shown
264            ''',
265    }
266
267    def __init__(self, givenElements=None, *args, **keywords):
268        base.Music21Object.__init__(self, **keywords)
269        core.StreamCoreMixin.__init__(self)
270
271        self.streamStatus = streamStatus.StreamStatus(self)
272        self._unlinkedDuration = None
273
274        self.autoSort = True
275
276        # these should become part of style or something else...
277        self.definesExplicitSystemBreaks = False
278        self.definesExplicitPageBreaks = False
279
280        # property for transposition status;
281        self._atSoundingPitch = 'unknown'  # True, False, or unknown
282
283        # experimental
284        self._mutable = True
285
286        if givenElements is None:
287            return
288
289        if isinstance(givenElements, base.Music21Object) or not common.isIterable(givenElements):
290            givenElements = [givenElements]
291
292        # Append rather than insert if every offset is 0.0
293        # but not if every element is a stream subclass other than a Measure or Score
294        # (i.e. Part or Voice generally, but even Opus theoretically)
295        # because these classes usually represent synchrony
296        append: bool = False
297        try:
298            append = all(e.offset == 0.0 for e in givenElements)
299        except AttributeError:
300            pass  # appropriate failure will be raised by coreGuardBeforeAddElement()
301        if append and all(
302                (e.isStream and e.classSet.isdisjoint((Measure, Score)))
303                for e in givenElements):
304            append = False
305
306        for e in givenElements:
307            self.coreGuardBeforeAddElement(e)
308            if append:
309                self.coreAppend(e)
310            else:
311                self.coreInsert(e.offset, e)
312
313        self.coreElementsChanged()
314
315    def _reprInternal(self):
316        if self.id is not None:
317            if self.id != id(self) and str(self.id) != str(id(self)):
318                return str(self.id)
319            else:
320                return hex(self.id)
321        else:  # pragma: no cover
322            return ''
323
324    def write(self, fmt=None, fp=None, **keywords):
325        # ...    --- see base.py calls .write(
326        if self.isSorted is False and self.autoSort:  # pragma: no cover
327            self.sort()
328        return super().write(fmt=fmt, fp=fp, **keywords)
329
330    def show(self, fmt=None, app=None, **keywords):
331        # ...    --- see base.py calls .write(
332        if self.isSorted is False and self.autoSort:
333            self.sort()
334        return super().show(fmt=fmt, app=app, **keywords)
335
336    # --------------------------------------------------------------------------
337    # sequence like operations
338
339    def __len__(self):
340        '''
341        Get the total number of elements in the Stream.
342        This method does not recurse into and count elements in contained Streams.
343
344        >>> import copy
345        >>> a = stream.Stream()
346        >>> for x in range(4):
347        ...     n = note.Note('G#')
348        ...     n.offset = x * 3
349        ...     a.insert(n)
350        >>> len(a)
351        4
352
353        >>> b = stream.Stream()
354        >>> for x in range(4):
355        ...     b.insert(copy.deepcopy(a))  # append streams
356        >>> len(b)
357        4
358        >>> len(b.flatten())
359        16
360
361        Includes end elements:
362
363        >>> b.storeAtEnd(bar.Barline('double'))
364        >>> len(b)
365        5
366        '''
367        return len(self._elements) + len(self._endElements)
368
369    def __iter__(self):
370        '''
371        The Stream iterator, used in all for
372        loops and similar iteration routines. This method returns the
373        specialized :class:`music21.stream.StreamIterator` class, which
374        adds necessary Stream-specific features.
375        '''
376        return iterator.StreamIterator(self)
377
378    @property
379    def iter(self):
380        '''
381        The Stream iterator, used in all for
382        loops and similar iteration routines. This method returns the
383        specialized :class:`music21.stream.StreamIterator` class, which
384        adds necessary Stream-specific features.
385
386        Generally you don't need this, just iterate over a stream, but it is necessary
387        to add custom filters to an iterative search before iterating.
388        '''
389        return self.__iter__()
390
391    def __getitem__(self, k):
392        '''
393        Get a Music21Object from the Stream using a variety of keys or indices.
394
395        If an int is given, the Music21Object at the index is returned, as if it were a list
396        or tuple:
397
398        >>> c = note.Note('C')
399        >>> d = note.Note('D')
400        >>> e = note.Note('E')
401        >>> r1 = note.Rest()
402        >>> f = note.Note('F')
403        >>> g = note.Note('G')
404        >>> r2 = note.Rest()
405        >>> a = note.Note('A')
406        >>> s = stream.Stream([c, d, e, r1, f, g, r2, a])
407
408        >>> s[0]
409        <music21.note.Note C>
410        >>> s[-1]
411        <music21.note.Note A>
412
413        Out of range notes raise an IndexError:
414
415        >>> s[99]
416        Traceback (most recent call last):
417        IndexError: attempting to access index 99 while elements is of size 8
418
419        If a slice of indices is given, a list of elements is returned, as if the Stream
420        were a list or Tuple.
421
422        >>> subslice = s[2:5]
423        >>> subslice
424        [<music21.note.Note E>, <music21.note.Rest quarter>, <music21.note.Note F>]
425        >>> len(subslice)
426        3
427        >>> s[1].offset
428        1.0
429        >>> subslice[1].offset
430        3.0
431
432
433        If a class is given then an iterator of elements
434        that match the requested class(es) is returned, similar
435        to `Stream().recurse().getElementsByClass()`.
436
437        >>> len(s)
438        8
439        >>> len(s[note.Rest])
440        2
441        >>> len(s[note.Note])
442        6
443
444        >>> for n in s[note.Note]:
445        ...     print(n.name, end=' ')
446        C D E F G A
447
448        Note that this iterator is recursive by default.
449
450        >>> c_sharp = note.Note('C#')
451        >>> v = stream.Voice([c_sharp])
452        >>> s.insert(0.5, c_sharp)
453
454        >>> len(s[note.Note])
455        7
456
457
458        The actual object returned by `s[module.Class]` is a
459        :class:`~music21.stream.iterator.RecursiveIterator` and has all the functions
460        available on it:
461
462        >>> s[note.Note]
463        <...>
464
465        If no elements of the class are found, no error is raised in version 7:
466
467        >>> list(s[layout.StaffLayout])
468        []
469
470
471        If the key is a string, it is treated as a `querySelector` as defined in
472        :meth:`~music21.stream.iterator.getElementsByQuerySelector`, namely that bare strings
473        are treated as class names, strings beginning with `#` are id-queries, and strings
474        beginning with `.` are group queries.
475
476        We can set some ids and groups for demonstrating.
477
478        >>> a.id = 'last_a'
479        >>> c.groups.append('ghost')
480        >>> e.groups.append('ghost')
481
482        'Note' is treated as a class name and returns a `RecursiveIterator`:
483
484        >>> for n in s['Note']:
485        ...     print(n.name, end=' ')
486        C C# D E F G A
487
488        '.ghost', because it begins with `.`, is treated as a class name and
489        returns a `RecursiveIterator`:
490
491
492        >>> for n in s['.ghost']:
493        ...     print(n.name, end=' ')
494        C E
495
496        A query selector with a `#`:
497
498        >>> s['#last_a']
499        <music21.note.Note A>
500
501        >>> s['#nothing'] is None
502        True
503
504
505        Any other query raises a TypeError:
506
507        >>> s[0.5]
508        Traceback (most recent call last):
509        TypeError: Streams can get items by int, slice, class, or string query; got <class 'float'>
510
511
512        Changed in v7:
513
514          - out of range indexes now raise an IndexError, not StreamException
515          - strings ('Note', '#id', '.group') are now treated like a query selector.
516          - slices with negative indices now supported
517          - Unsupported types now raise TypeError
518          - Class and Group searches now return a recursive `StreamIterator` rather than a `Stream`
519          - Slice searches now return a list of elements rather than a `Stream`
520        '''
521        # need to sort if not sorted, as this call may rely on index positions
522        if not self.isSorted and self.autoSort:
523            self.sort()  # will set isSorted to True
524
525        if isinstance(k, int):
526            match = None
527            # handle easy and most common case first
528            if 0 <= k < len(self._elements):
529                match = self._elements[k]
530            # if using negative indices, or trying to access end elements,
531            # then need to use .elements property
532            else:
533                try:
534                    match = self.elements[k]
535                except IndexError:
536                    raise IndexError(
537                        f'attempting to access index {k} '
538                        + f'while elements is of size {len(self.elements)}'
539                    )
540            # setting active site as cautionary measure
541            self.coreSelfActiveSite(match)
542            return match
543
544        elif isinstance(k, slice):  # get a slice of index values
545            # manually inserting elements is critical to setting the element
546            # locations
547            searchElements = self._elements
548            if (k.start is not None and k.start < 0) or (k.stop is not None and k.stop < 0):
549                # Must use .elements property to incorporate end elements
550                searchElements = self.elements
551
552            return searchElements[k]
553
554        elif isinstance(k, type):
555            return self.recurse().getElementsByClass(k)
556
557        elif isinstance(k, str):
558            querySelectorIterator = self.recurse().getElementsByQuerySelector(k)
559            if '#' in k:
560                # an id anywhere in the query selector should return only one element
561                return querySelectorIterator.first()
562            else:
563                return querySelectorIterator
564
565        raise TypeError(
566            f'Streams can get items by int, slice, class, or string query; got {type(k)}'
567        )
568
569    def first(self) -> Optional[base.Music21Object]:
570        '''
571        Return the first element of a Stream.  (Added for compatibility with StreamIterator)
572        Or None if the Stream is empty.
573
574        Unlike s.iter().first(), which is a significant performance gain, s.first() is the
575        same speed as s[0], except for not raising an IndexError.
576
577        >>> nC = note.Note('C4')
578        >>> nD = note.Note('D4')
579        >>> s = stream.Stream()
580        >>> s.append([nC, nD])
581        >>> s.first()
582        <music21.note.Note C>
583
584        >>> empty = stream.Stream()
585        >>> print(empty.first())
586        None
587
588        New in v7.
589        '''
590        try:
591            return self[0]
592        except IndexError:
593            return None
594
595    def last(self) -> Optional[base.Music21Object]:
596        '''
597        Return the last element of a Stream.  (Added for compatibility with StreamIterator)
598        Or None if the Stream is empty.
599
600        s.first() is the same speed as s[-1], except for not raising an IndexError.
601
602        >>> nC = note.Note('C4')
603        >>> nD = note.Note('D4')
604        >>> s = stream.Stream()
605        >>> s.append([nC, nD])
606        >>> s.last()
607        <music21.note.Note D>
608
609        >>> empty = stream.Stream()
610        >>> print(empty.last())
611        None
612
613        New in v7.
614        '''
615        try:
616            return self[-1]
617        except IndexError:
618            return None
619
620    def __contains__(self, el):
621        '''
622        Returns True if `el` definitely is in the stream and False otherwise.
623
624        >>> nC = note.Note('C4')
625        >>> nD = note.Note('D4')
626        >>> s = stream.Stream()
627        >>> s.append(nC)
628        >>> nC in s
629        True
630        >>> nD in s
631        False
632
633        Note that we match on actual `id()` equality (`x is y`) and not on
634        `==` equality.
635
636        >>> nC2 = note.Note('C4')
637        >>> nC == nC2
638        True
639        >>> nC2 in s
640        False
641
642        To get the latter, compare on `.elements` which uses Python's
643        default `__contains__` for tuples.
644
645        >>> nC2 in s.elements
646        True
647        '''
648        for sEl in self.elements:  # for speed do not set active sites
649            if el is sEl:
650                return True
651        return False
652
653    @property
654    def elements(self) -> Tuple[base.Music21Object]:
655        '''
656        .elements is a Tuple representing the elements contained in the Stream.
657
658        Directly getting, setting, and manipulating this Tuple is
659        reserved for advanced usage. Instead, use the the
660        provided high-level methods.  The elements retrieved here may not
661        have this stream as an activeSite, therefore they might not be properly ordered.
662
663        In other words:  Don't use unless you really know what you're doing.
664        Treat a Stream like a list!
665
666        When setting .elements, a list of Music21Objects can be provided, or a complete Stream.
667        If a complete Stream is provided, elements are extracted
668        from that Stream. This has the advantage of transferring
669        offset correctly and getting elements stored at the end.
670
671        >>> a = stream.Stream()
672        >>> a.repeatInsert(note.Note('C'), list(range(10)))
673        >>> b = stream.Stream()
674        >>> b.repeatInsert(note.Note('D'), list(range(10)))
675        >>> b.offset = 6
676        >>> c = stream.Stream()
677        >>> c.repeatInsert(note.Note('E'), list(range(10)))
678        >>> c.offset = 12
679        >>> b.insert(c)
680        >>> b.isFlat
681        False
682
683        >>> a.isFlat
684        True
685
686        Assigning from a Stream works well, and is actually much safer than assigning
687        from `.elements` of the other Stream, since the active sites may have changed
688        of that stream's elements in the meantime.
689
690        >>> a.elements = b
691        >>> a.isFlat
692        False
693
694        >>> len(a.recurse().notes) == len(b.recurse().notes) == 20
695        True
696        '''
697        # combines _elements and _endElements into one.
698        if 'elements' not in self._cache or self._cache['elements'] is None:
699            # this list concatenation may take time; thus, only do when
700            # coreElementsChanged has been called
701            if not self.isSorted and self.autoSort:
702                self.sort()  # will set isSorted to True
703            self._cache['elements'] = self._elements + self._endElements
704        return tuple(self._cache['elements'])
705
706    @elements.setter
707    def elements(self, value: Union['Stream', Sequence[base.Music21Object]]):
708        '''
709        Sets this stream's elements to the elements in another stream (just give
710        the stream, not the stream's .elements), or to a list of elements.
711
712        Safe:
713
714        newStream.elements = oldStream
715
716        Unsafe:
717
718        newStream.elements = oldStream.elements
719
720        Why?
721
722        The activeSites of some elements may have changed between retrieving
723        and setting (esp. if a lot else has happened in the meantime). Where
724        are we going to get the new stream's elements' offsets from? why
725        from their active sites! So don't do this!
726        '''
727        if (not common.isListLike(value)
728                and hasattr(value, 'isStream')
729                and value.isStream):
730            # set from a Stream. Best way to do it
731            self._offsetDict = {}
732            self._elements = list(value._elements)  # copy list.
733            for e in self._elements:
734                self.coreSetElementOffset(e, value.elementOffset(e), addElement=True)
735                e.sites.add(self)
736                self.coreSelfActiveSite(e)
737            self._endElements = list(value._endElements)
738            for e in self._endElements:
739                self.coreSetElementOffset(e,
740                                      value.elementOffset(e, returnSpecial=True),
741                                      addElement=True)
742                e.sites.add(self)
743                self.coreSelfActiveSite(e)
744        else:
745            # replace the complete elements list
746            self._elements = list(value)
747            self._endElements = []
748            self._offsetDict = {}
749            for e in self._elements:
750                self.coreSetElementOffset(e, e.offset, addElement=True)
751                e.sites.add(self)
752                self.coreSelfActiveSite(e)
753        self.coreElementsChanged()
754
755    def __setitem__(self, k, value):
756        '''
757        Insert an item at a currently filled index position,
758        as represented in the elements list.
759
760        >>> a = stream.Stream()
761        >>> a.repeatInsert(note.Note('C'), list(range(10)))
762        >>> b = stream.Stream()
763        >>> b.repeatInsert(note.Note('C'), list(range(10)))
764        >>> b.offset = 6
765        >>> c = stream.Stream()
766        >>> c.repeatInsert(note.Note('C'), list(range(10)))
767        >>> c.offset = 12
768        >>> b.insert(c)
769        >>> a.isFlat
770        True
771        >>> a[3] = b
772        >>> a.isFlat
773        False
774        '''
775        # remove old value at this position
776        oldValue = self._elements[k]
777        del self._offsetDict[id(oldValue)]
778        oldValue.sites.remove(self)
779        oldValue.activeSite = None
780
781        # assign in new position
782        self._elements[k] = value
783        self.coreSetElementOffset(value, value.offset, addElement=True)
784        self.coreSelfActiveSite(value)
785        # must get native offset
786
787        value.sites.add(self)
788        if isinstance(value, Stream):
789            # know that this is now not flat
790            self.coreElementsChanged(updateIsFlat=False)  # set manually
791            self.isFlat = False
792        else:
793            # cannot be sure if this is flat, as we do not know if
794            # we replaced a Stream at this index
795            self.coreElementsChanged()
796
797    def __delitem__(self, k):
798        '''
799        Delete element at an index position. Index positions are based
800        on positions in self.elements.
801
802        >>> a = stream.Stream()
803        >>> a.repeatInsert(note.Note('C'), list(range(10)))
804        >>> del a[0]
805        >>> len(a)
806        9
807        '''
808        del self._elements[k]
809        self.coreElementsChanged()
810
811    def __add__(self: T, other: 'Stream') -> T:
812        '''
813        Add, or concatenate, two Streams.
814
815        Presently, this does not manipulate the offsets of the incoming elements
816        to actually be at the end of the Stream. This may be a problem that
817        makes this method not so useful?
818
819        >>> a = stream.Part()
820        >>> a.repeatInsert(note.Note('C'), [0, 1])
821        >>> b = stream.Stream()
822        >>> b.repeatInsert(note.Note('G'), [0, 1, 2])
823        >>> c = a + b
824        >>> c.pitches  # autoSort is True, thus a sorted version results
825        [<music21.pitch.Pitch C>,
826         <music21.pitch.Pitch G>,
827         <music21.pitch.Pitch C>,
828         <music21.pitch.Pitch G>,
829         <music21.pitch.Pitch G>]
830        >>> len(c.notes)
831        5
832
833        The autoSort of the first stream becomes the autoSort of the
834        destination.  The class of the first becomes the class of the destination.
835
836        >>> a.autoSort = False
837        >>> d = a + b
838        >>> [str(p) for p in d.pitches]
839        ['C', 'C', 'G', 'G', 'G']
840        >>> d.__class__.__name__
841        'Part'
842
843        Works with Streams with Store at end, which does put both at the end.
844
845        >>> a.autoSort = True
846        >>> a.storeAtEnd(bar.Barline('final'))
847        >>> b.storeAtEnd(clef.TrebleClef())
848        >>> f = a + b
849        >>> f.show('text')
850        {0.0} <music21.note.Note C>
851        {0.0} <music21.note.Note G>
852        {1.0} <music21.note.Note C>
853        {1.0} <music21.note.Note G>
854        {2.0} <music21.note.Note G>
855        {3.0} <music21.bar.Barline type=final>
856        {3.0} <music21.clef.TrebleClef>
857        '''
858        if other is None or not isinstance(other, Stream):
859            raise TypeError('cannot concatenate a Stream with a non-Stream')
860
861        s = self.cloneEmpty(derivationMethod='__add__')
862        # may want to keep activeSite of source Stream?
863        # s.elements = self._elements + other._elements
864        # need to iterate over elements and re-assign to create new locations
865        for e in self._elements:
866            s.insert(self.elementOffset(e), e)
867        for e in other._elements:
868            s.insert(other.elementOffset(e), e)
869
870        for e in self._endElements:
871            s.storeAtEnd(e)
872        for e in other._endElements:
873            s.storeAtEnd(e)
874
875        # s.coreElementsChanged()
876        return s
877
878    def __bool__(self):
879        '''
880        As a container class, Streams return True if they are non-empty
881        and False if they are empty:
882
883        >>> def testBool(s):
884        ...    if s:
885        ...        return True
886        ...    else:
887        ...        return False
888
889        >>> s = stream.Stream()
890        >>> testBool(s)
891        False
892        >>> s.append(note.Note())
893        >>> testBool(s)
894        True
895        >>> s.append(note.Note())
896        >>> testBool(s)
897        True
898
899        >>> s = stream.Stream()
900        >>> s.storeAtEnd(bar.Barline('final'))
901        >>> testBool(s)
902        True
903        '''
904        if self._elements:  # blindingly faster than if len(self._elements) > 0
905            return True    # and even about 5x faster than if any(self._elements)
906        if self._endElements:
907            return True
908        return False
909
910    # ------------------------------
911    @property
912    def clef(self) -> Optional['music21.clef.Clef']:
913        '''
914        Finds or sets a :class:`~music21.clef.Clef` at offset 0.0 in the Stream
915        (generally a Measure):
916
917        >>> m = stream.Measure()
918        >>> m.number = 10
919        >>> m.clef = clef.TrebleClef()
920        >>> thisTrebleClef = m.clef
921        >>> thisTrebleClef.sign
922        'G'
923        >>> thisTrebleClef.getOffsetBySite(m)
924        0.0
925
926        Setting the clef for the measure a second time removes the previous clef
927        from the measure and replaces it with the new one:
928
929        >>> m.clef = clef.BassClef()
930        >>> m.clef.sign
931        'F'
932
933        And the TrebleClef is no longer in the measure:
934
935        >>> thisTrebleClef.getOffsetBySite(m)
936        Traceback (most recent call last):
937        music21.sites.SitesException: an entry for this object <music21.clef.TrebleClef> is not
938              stored in stream <music21.stream.Measure 10 offset=0.0>
939
940        The `.clef` appears in a `.show()` or other call
941        just like any other element
942
943        >>> m.append(note.Note('D#', type='whole'))
944        >>> m.show('text')
945        {0.0} <music21.clef.BassClef>
946        {0.0} <music21.note.Note D#>
947        '''
948        clefList = self.getElementsByClass('Clef').getElementsByOffset(0)
949        # casting to list added 20microseconds...
950        return clefList.first()
951
952    @clef.setter
953    def clef(self, clefObj: Optional['music21.clef.Clef']):
954        # if clef is None; remove object?
955        oldClef = self.clef
956        if oldClef is not None:
957            # environLocal.printDebug(['removing clef', oldClef])
958            junk = self.pop(self.index(oldClef))
959        if clefObj is None:
960            # all that is needed is to remove the old clef
961            # there is no new clef - suppresses the clef of a stream
962            return
963        self.insert(0.0, clefObj)
964
965    @property
966    def timeSignature(self) -> Optional['music21.meter.TimeSignature']:
967        '''
968        Gets or sets the timeSignature at offset 0.0 of the Stream (generally a Measure)
969
970        >>> m1 = stream.Measure(number=1)
971        >>> m1.timeSignature = meter.TimeSignature('2/4')
972        >>> m1.timeSignature.numerator, m1.timeSignature.denominator
973        (2, 4)
974        >>> m1.show('text')
975        {0.0} <music21.meter.TimeSignature 2/4>
976
977        Setting timeSignature to None removes any TimeSignature at offset 0.0:
978
979        >>> m1.timeSignature = None
980        >>> m1.elements
981        ()
982
983
984        Only the time signature at offset 0 is found:
985
986        >>> m2 = stream.Measure(number=2)
987        >>> m2.insert(0.0, meter.TimeSignature('5/4'))
988        >>> m2.insert(2.0, meter.TimeSignature('7/4'))
989        >>> ts = m2.timeSignature
990        >>> ts.numerator, ts.denominator
991        (5, 4)
992
993        >>> m2.timeSignature = meter.TimeSignature('2/8')
994        >>> m2.timeSignature
995        <music21.meter.TimeSignature 2/8>
996
997        After setting a new `.timeSignature`, the old one is no longer in the Stream:
998
999        >>> ts in m2
1000        False
1001
1002        This property is not recursive, so a Part will not have the time signature of
1003        the measure within it:
1004
1005        >>> p = stream.Part()
1006        >>> p.append(m2)
1007        >>> p.timeSignature is None
1008        True
1009        '''
1010        # there could be more than one
1011        tsList = self.getElementsByClass('TimeSignature').getElementsByOffset(0)
1012        # environLocal.printDebug([
1013        #    'matched Measure classes of type TimeSignature', tsList, len(tsList)])
1014        # only return timeSignatures at offset = 0.0
1015        return tsList.first()
1016
1017    @timeSignature.setter
1018    def timeSignature(self, tsObj: Optional['music21.meter.TimeSignature']):
1019        oldTimeSignature = self.timeSignature
1020        if oldTimeSignature is not None:
1021            # environLocal.printDebug(['removing ts', oldTimeSignature])
1022            junk = self.pop(self.index(oldTimeSignature))
1023        if tsObj is None:
1024            # all that is needed is to remove the old time signature
1025            # there is no new time signature - suppresses the time signature of a stream
1026            return
1027        self.insert(0, tsObj)
1028
1029    @property
1030    def keySignature(self) -> Optional['music21.key.KeySignature']:
1031        '''
1032        Find or set a Key or KeySignature at offset 0.0 of a stream.
1033
1034        >>> a = stream.Measure()
1035        >>> a.keySignature = key.KeySignature(-2)
1036        >>> ks = a.keySignature
1037        >>> ks.sharps
1038        -2
1039        >>> a.show('text')
1040        {0.0} <music21.key.KeySignature of 2 flats>
1041
1042        A key.Key object can be used instead of key.KeySignature,
1043        since the former derives from the latter.
1044
1045        >>> a.keySignature = key.Key('E', 'major')
1046        >>> for k in a:
1047        ...     print(k.offset, repr(k))
1048        0.0 <music21.key.Key of E major>
1049
1050        Notice that setting a new key signature replaces any previous ones:
1051
1052        >>> len(a.getElementsByClass('KeySignature'))
1053        1
1054
1055        `.keySignature` can be set to None:
1056
1057        >>> a.keySignature = None
1058        >>> a.keySignature is None
1059        True
1060        '''
1061        try:
1062            return next(self.iter().getElementsByClass('KeySignature').getElementsByOffset(0))
1063        except StopIteration:
1064            return None
1065
1066    @keySignature.setter
1067    def keySignature(self, keyObj: Optional['music21.key.KeySignature']):
1068        '''
1069        >>> a = stream.Measure()
1070        >>> a.keySignature = key.KeySignature(6)
1071        >>> a.keySignature.sharps
1072        6
1073        '''
1074        oldKey = self.keySignature
1075        if oldKey is not None:
1076            # environLocal.printDebug(['removing key', oldKey])
1077            junk = self.pop(self.index(oldKey))
1078        if keyObj is None:
1079            # all that is needed is to remove the old key signature
1080            # there is no new key signature - suppresses the key signature of a stream
1081            return
1082        self.insert(0, keyObj)
1083
1084    @property
1085    def staffLines(self) -> int:
1086        '''
1087        Returns the number of staffLines for the Stream, as defined by
1088        the first StaffLayout object found at offset 0 that defines staffLines
1089
1090        >>> m = stream.Measure()
1091        >>> m.staffLines
1092        5
1093        >>> m.staffLines = 4
1094        >>> m.staffLines
1095        4
1096        >>> m.show('text')
1097        {0.0} <music21.layout.StaffLayout distance None, staffNumber None,
1098                  staffSize None, staffLines 4>
1099
1100        >>> staffLayout = m.getElementsByClass('StaffLayout').first()
1101        >>> staffLayout.staffLines = 1
1102        >>> m.staffLines
1103        1
1104
1105        >>> p = stream.Part()
1106        >>> p.insert(0, m)
1107        >>> p.staffLines
1108        1
1109
1110        >>> p2 = stream.Part()
1111        >>> m0 = stream.Measure()
1112        >>> m0.insert(0, note.Note(type='whole'))
1113        >>> p2.append(m0)
1114        >>> p2.append(m)
1115        >>> p2.staffLines
1116        5
1117
1118        OMIT_FROM_DOCS
1119
1120        Check that staffLayout is altered by staffLayout setter:
1121
1122        >>> m.staffLines = 2
1123        >>> staffLayout.staffLines
1124        2
1125
1126        '''
1127        staffLayouts = self.recurse().getElementsByClass('StaffLayout')
1128        sl: 'music21.layout.StaffLayout'
1129        for sl in staffLayouts:
1130            if sl.getOffsetInHierarchy(self) > 0:
1131                break
1132            if sl.staffLines is not None:
1133                return sl.staffLines
1134        return 5
1135
1136    @staffLines.setter
1137    def staffLines(self, newStaffLines: int):
1138        staffLayouts = self.recurse().getElementsByOffset(0.0).getElementsByClass('StaffLayout')
1139        if not staffLayouts:
1140            from music21 import layout
1141            sl: 'music21.layout.StaffLayout' = layout.StaffLayout(staffLines=newStaffLines)
1142            self.insert(0.0, sl)
1143        else:
1144            firstLayout = staffLayouts.first()
1145            firstLayout.staffLines = newStaffLines
1146
1147    def clear(self) -> None:
1148        '''
1149        Remove all elements in a stream.
1150
1151        >>> m = stream.Measure(number=3)
1152        >>> m.append(note.Note('C'))
1153        >>> m.storeAtEnd(bar.Barline('final'))
1154        >>> len(m)
1155        2
1156        >>> m.clear()
1157        >>> len(m)
1158        0
1159
1160        Does not remove any other attributes
1161
1162        >>> m.number
1163        3
1164        '''
1165        self.elements = []
1166
1167    def cloneEmpty(self: StreamType, derivationMethod: Optional[str] = None) -> StreamType:
1168        '''
1169        Create a Stream that is identical to this one except that the elements are empty
1170        and set derivation.
1171
1172        >>> p = stream.Part()
1173        >>> p.autoSort = False
1174        >>> p.id = 'hi'
1175        >>> p.insert(0, note.Note())
1176        >>> q = p.cloneEmpty(derivationMethod='demo')
1177        >>> q.autoSort
1178        False
1179        >>> q
1180        <music21.stream.Part hi>
1181        >>> q.derivation.origin is p
1182        True
1183        >>> q.derivation.method
1184        'demo'
1185        >>> len(q)
1186        0
1187        '''
1188        returnObj: StreamType = self.__class__()
1189        returnObj.derivation.client = returnObj
1190        returnObj.derivation.origin = self
1191        if derivationMethod is not None:
1192            returnObj.derivation.method = derivationMethod
1193        returnObj.mergeAttributes(self)  # get groups, optional id
1194        return returnObj
1195
1196    def mergeAttributes(self, other: 'Stream'):
1197        '''
1198        Merge relevant attributes from the Other stream into this one.
1199
1200        >>> s = stream.Stream()
1201        >>> s.append(note.Note())
1202        >>> s.autoSort = False
1203        >>> s.id = 'hi'
1204        >>> t = stream.Stream()
1205        >>> t.mergeAttributes(s)
1206        >>> t.autoSort
1207        False
1208        >>> t
1209        <music21.stream.Stream hi>
1210        >>> len(t)
1211        0
1212        '''
1213        super().mergeAttributes(other)
1214
1215        for attr in ('autoSort', 'isSorted', 'definesExplicitSystemBreaks',
1216                     'definesExplicitPageBreaks', '_atSoundingPitch', '_mutable'):
1217            if hasattr(other, attr):
1218                setattr(self, attr, getattr(other, attr))
1219
1220    def hasElement(self, obj):
1221        '''
1222        Return True if an element, provided as an argument, is contained in
1223        this Stream.
1224
1225        This method is based on object equivalence, not parameter equivalence
1226        of different objects.
1227
1228        >>> s = stream.Stream()
1229        >>> n1 = note.Note('g')
1230        >>> n2 = note.Note('g#')
1231        >>> s.append(n1)
1232        >>> s.hasElement(n1)
1233        True
1234        '''
1235        objId = id(obj)
1236        return self.coreHasElementByMemoryLocation(objId)
1237
1238    def hasElementOfClass(self, className, forceFlat=False):
1239        '''
1240        Given a single class name as string,
1241        return True or False if an element with the
1242        specified class is found.
1243
1244        Only a single class name can be given.
1245
1246        >>> s = stream.Stream()
1247        >>> s.append(meter.TimeSignature('5/8'))
1248        >>> s.append(note.Note('d-2'))
1249        >>> s.insert(dynamics.Dynamic('fff'))
1250        >>> s.hasElementOfClass('TimeSignature')
1251        True
1252        >>> s.hasElementOfClass('Measure')
1253        False
1254
1255        To be deprecated in v.7 -- to be removed in version 8, use:
1256
1257        >>> bool(s.getElementsByClass('TimeSignature'))
1258        True
1259        >>> bool(s.getElementsByClass('Measure'))
1260        False
1261
1262        forceFlat does nothing, while getElementsByClass can be done on recurse()
1263        '''
1264        # environLocal.printDebug(['calling hasElementOfClass()', className])
1265        for e in self.elements:
1266            if className in e.classSet:
1267                return True
1268        return False
1269
1270    def mergeElements(self, other, classFilterList=None):
1271        '''
1272        Given another Stream, store references of each element
1273        in the other Stream in this Stream. This does not make
1274        copies of any elements, but simply stores all of them in this Stream.
1275
1276        Optionally, provide a list of classes to include with the `classFilter` list.
1277
1278        This method provides functionality like a shallow copy,
1279        but manages locations properly, only copies elements,
1280        and permits filtering by class type.
1281
1282
1283        >>> s1 = stream.Stream()
1284        >>> s2 = stream.Stream()
1285        >>> n1 = note.Note('f#')
1286        >>> n2 = note.Note('g')
1287        >>> s1.append(n1)
1288        >>> s1.append(n2)
1289        >>> s2.mergeElements(s1)
1290        >>> len(s2)
1291        2
1292        >>> s1[0] is s2[0]
1293        True
1294        >>> s1[1] is s2[1]
1295        True
1296
1297        >>> viola = instrument.Viola()
1298        >>> trumpet = instrument.Trumpet()
1299        >>> s1.insert(0, viola)
1300        >>> s1.insert(0, trumpet)
1301        >>> s2.mergeElements(s1, classFilterList=('BrassInstrument',))
1302        >>> len(s2)
1303        3
1304        >>> viola in s2
1305        False
1306        '''
1307        if classFilterList is not None:
1308            classFilterSet = set(classFilterList)
1309        else:
1310            classFilterSet = None
1311
1312        for e in other._elements:
1313            # self.insert(other.offset, e)
1314            if classFilterList is not None:
1315                if classFilterSet.intersection(e.classSet):
1316                    self.coreInsert(other.elementOffset(e), e)
1317            else:
1318                self.coreInsert(other.elementOffset(e), e)
1319
1320            # for c in classFilterList:
1321            #     if c in e.classes:
1322            #         match = True
1323            #         break
1324            #
1325            # if len(classFilterList) == 0 or match:
1326            #     self.insert(e.getOffsetBySite(other), e)
1327
1328        for e in other._endElements:
1329            if classFilterList is not None:
1330                if classFilterSet.intersection(e.classSet):
1331                    self.coreStoreAtEnd(e)
1332            else:
1333                self.coreStoreAtEnd(e)
1334
1335            # match = False
1336            # for c in classFilterList:
1337            #     if c in e.classes:
1338            #         match = True
1339            #         break
1340            # if len(classFilterList) == 0 or match:
1341            #     self.storeAtEnd(e)
1342        self.coreElementsChanged()
1343
1344    def index(self, el):
1345        '''
1346        Return the first matched index for
1347        the specified object.
1348
1349        Raises a StreamException if cannot
1350        be found.
1351
1352        >>> s = stream.Stream()
1353        >>> n1 = note.Note('g')
1354        >>> n2 = note.Note('g#')
1355
1356        >>> s.insert(0, n1)
1357        >>> s.insert(5, n2)
1358        >>> len(s)
1359        2
1360        >>> s.index(n1)
1361        0
1362        >>> s.index(n2)
1363        1
1364
1365        >>> n3 = note.Note('a')
1366        >>> s.index(n3)
1367        Traceback (most recent call last):
1368        music21.exceptions21.StreamException: cannot find object (<music21.note.Note A>) in Stream
1369        '''
1370        if not self.isSorted and self.autoSort:
1371            self.sort()  # will set isSorted to True
1372
1373        if 'index' in self._cache and self._cache['index'] is not None:
1374            try:
1375                return self._cache['index'][id(el)]
1376            except KeyError:
1377                pass  # not in cache
1378        else:
1379            self._cache['index'] = {}
1380
1381        objId = id(el)
1382
1383        count = 0
1384
1385        for e in self._elements:
1386            if e is el:
1387                self._cache['index'][objId] = count
1388                return count
1389            count += 1
1390        for e in self._endElements:
1391            if e is el:
1392                self._cache['index'][objId] = count
1393                return count  # this is the index
1394            count += 1  # cumulative indices
1395        raise StreamException(f'cannot find object ({el}) in Stream')
1396
1397    def remove(self,
1398               targetOrList: Union[base.Music21Object, List[base.Music21Object]],
1399               *,
1400               shiftOffsets=False,
1401               recurse=False):
1402        # noinspection PyShadowingNames
1403        '''
1404        Remove an object from this Stream. Additionally, this Stream is
1405        removed from the object's sites in :class:`~music21.sites.Sites`.
1406
1407        If a list of objects is passed, they will all be removed.
1408        If shiftOffsets is True, then offsets will be
1409        corrected after object removal. It is more efficient to pass
1410        a list of objects than to call remove on
1411        each object individually if shiftOffsets is True.
1412
1413        >>> import copy
1414        >>> s = stream.Stream()
1415        >>> n1 = note.Note('g')
1416        >>> n2 = note.Note('g#')
1417
1418        Copies of an object are not the same as the object
1419
1420        >>> n3 = copy.deepcopy(n2)
1421        >>> s.insert(10, n1)
1422        >>> s.insert(5, n2)
1423        >>> s.remove(n1)
1424        >>> len(s)
1425        1
1426        >>> s.insert(20, n3)
1427        >>> s.remove(n3)
1428        >>> [e for e in s] == [n2]
1429        True
1430
1431        No error is raised if the target is not found.
1432
1433        >>> s.remove(n3)
1434
1435        >>> s2 = stream.Stream()
1436        >>> c = clef.TrebleClef()
1437        >>> n1, n2, n3, n4 = note.Note('a'), note.Note('b'), note.Note('c'), note.Note('d')
1438        >>> n5, n6, n7, n8 = note.Note('e'), note.Note('f'), note.Note('g'), note.Note('a')
1439        >>> s2.insert(0.0, c)
1440        >>> s2.append([n1, n2, n3, n4, n5, n6, n7, n8])
1441        >>> s2.remove(n1, shiftOffsets=True)
1442        >>> s2.show('text')
1443        {0.0} <music21.clef.TrebleClef>
1444        {0.0} <music21.note.Note B>
1445        {1.0} <music21.note.Note C>
1446        {2.0} <music21.note.Note D>
1447        {3.0} <music21.note.Note E>
1448        {4.0} <music21.note.Note F>
1449        {5.0} <music21.note.Note G>
1450        {6.0} <music21.note.Note A>
1451
1452        >>> s2.remove([n3, n6, n4], shiftOffsets=True)
1453        >>> s2.show('text')
1454        {0.0} <music21.clef.TrebleClef>
1455        {0.0} <music21.note.Note B>
1456        {1.0} <music21.note.Note E>
1457        {2.0} <music21.note.Note G>
1458        {3.0} <music21.note.Note A>
1459
1460        With the recurse=True parameter, we can remove elements deeply nested.
1461        However, shiftOffsets
1462        does not work with recurse=True yet.
1463
1464        >>> p1 = stream.Part()
1465        >>> m1 = stream.Measure(number=1)
1466        >>> c = clef.BassClef()
1467        >>> m1.insert(0, c)
1468        >>> m1.append(note.Note(type='whole'))
1469        >>> p1.append(m1)
1470        >>> m2 = stream.Measure(number=2)
1471        >>> n2 = note.Note('D', type='half')
1472        >>> m2.append(n2)
1473        >>> n3 = note.Note(type='half')
1474        >>> m2.append(n3)
1475        >>> p1.append(m2)
1476        >>> p1.show('text')
1477        {0.0} <music21.stream.Measure 1 offset=0.0>
1478            {0.0} <music21.clef.BassClef>
1479            {0.0} <music21.note.Note C>
1480        {4.0} <music21.stream.Measure 2 offset=4.0>
1481            {0.0} <music21.note.Note D>
1482            {2.0} <music21.note.Note C>
1483
1484        Without recurse=True:
1485
1486        >>> p1.remove(n2)
1487        >>> p1.show('text')
1488        {0.0} <music21.stream.Measure 1 offset=0.0>
1489            {0.0} <music21.clef.BassClef>
1490            {0.0} <music21.note.Note C>
1491        {4.0} <music21.stream.Measure 2 offset=4.0>
1492            {0.0} <music21.note.Note D>
1493            {2.0} <music21.note.Note C>
1494
1495        With recurse=True:
1496
1497        >>> p1.remove(n2, recurse=True)
1498        >>> p1.show('text')
1499        {0.0} <music21.stream.Measure 1 offset=0.0>
1500            {0.0} <music21.clef.BassClef>
1501            {0.0} <music21.note.Note C>
1502        {4.0} <music21.stream.Measure 2 offset=4.0>
1503            {2.0} <music21.note.Note C>
1504
1505        With recurse=True and a list to remove:
1506
1507        >>> p1.remove([c, n3], recurse=True)
1508        >>> p1.show('text')
1509        {0.0} <music21.stream.Measure 1 offset=0.0>
1510            {0.0} <music21.note.Note C>
1511        {4.0} <music21.stream.Measure 2 offset=4.0>
1512        <BLANKLINE>
1513
1514
1515        Can also remove elements stored at end:
1516
1517        >>> streamWithBarline = stream.Stream(note.Note())
1518        >>> barline = bar.Barline('final')
1519        >>> streamWithBarline.storeAtEnd(barline)
1520        >>> barline in streamWithBarline
1521        True
1522        >>> streamWithBarline.remove(barline)
1523        >>> barline in streamWithBarline
1524        False
1525
1526        Changed in v5.3 -- firstMatchOnly removed -- impossible to have element
1527        in stream twice.  recurse and shiftOffsets changed to keywordOnly arguments
1528        '''
1529        # experimental
1530        if self._mutable is False:  # pragma: no cover
1531            raise ImmutableStreamException('Cannot remove from an immutable stream')
1532        # TODO: Next to clean up... a doozy -- filter out all the different options.
1533
1534        # TODO: Add a renumber measures option
1535        # TODO: Shift offsets if recurse is True
1536        if shiftOffsets is True and recurse is True:  # pragma: no cover
1537            raise StreamException(
1538                'Cannot do both shiftOffsets and recurse search at the same time...yet')
1539
1540        if not common.isListLike(targetOrList):
1541            targetList = [targetOrList]
1542        elif len(targetOrList) > 1:
1543            try:
1544                targetList = sorted(targetOrList, key=self.elementOffset)
1545            except sites.SitesException:
1546                # will not be found if recursing, it's not such a big deal...
1547                targetList = targetOrList
1548        else:
1549            targetList = targetOrList
1550
1551        shiftDur = 0.0  # for shiftOffsets
1552
1553        for i, target in enumerate(targetList):
1554            try:
1555                indexInStream = self.index(target)
1556            except StreamException as se:
1557                if not isinstance(target, base.Music21Object):
1558                    raise TypeError(f'{target} is not a Music21Object; got {type(target)}') from se
1559                if recurse is True:
1560                    for s in self.recurse(streamsOnly=True):
1561                        try:
1562                            indexInStream = s.index(target)
1563                            s.remove(target)
1564                            break
1565                        except (StreamException, sites.SitesException):
1566                            continue
1567                # recursion matched or didn't or wasn't run. either way no need for rest...
1568                continue
1569
1570            # Anything that messes with ._elements or ._endElements should be in core.py
1571            #     TODO: move it...
1572            matchedEndElement = False
1573            baseElementCount = len(self._elements)
1574            matchOffset = 0.0  # to avoid possibility of undefined
1575
1576            if indexInStream < baseElementCount:
1577                match = self._elements.pop(indexInStream)
1578            else:
1579                match = self._endElements.pop(indexInStream - baseElementCount)
1580                matchedEndElement = True
1581
1582            if match is not None:
1583                if shiftOffsets is True:
1584                    matchOffset = self.elementOffset(match)
1585
1586                try:
1587                    del self._offsetDict[id(match)]
1588                except KeyError:  # pragma: no cover
1589                    pass
1590                self.coreElementsChanged(clearIsSorted=False)
1591                match.sites.remove(self)
1592                match.activeSite = None
1593
1594            if shiftOffsets is True and matchedEndElement is False:
1595                matchDuration = match.duration.quarterLength
1596                shiftedRegionStart = matchOffset + matchDuration
1597                if (i + 1) < len(targetList):
1598                    shiftedRegionEnd = self.elementOffset(targetList[i + 1])
1599                else:
1600                    shiftedRegionEnd = self.duration.quarterLength
1601
1602                shiftDur += matchDuration
1603                if shiftDur != 0.0:
1604                    # can this be done with recurse???
1605                    for e in self.getElementsByOffset(shiftedRegionStart,
1606                                                      shiftedRegionEnd,
1607                                                      includeEndBoundary=False,
1608                                                      mustFinishInSpan=False,
1609                                                      mustBeginInSpan=True):
1610
1611                        elementOffset = self.elementOffset(e)
1612                        self.coreSetElementOffset(e, elementOffset - shiftDur)
1613            # if renumberMeasures is True and matchedEndElement is False:
1614            #     pass  # This should maybe just call a function renumberMeasures
1615        self.coreElementsChanged(clearIsSorted=False)
1616
1617    def pop(self, index: int) -> base.Music21Object:
1618        '''
1619        Return and remove the object found at the
1620        user-specified index value. Index values are
1621        those found in `elements` and are not necessary offset order.
1622
1623        >>> a = stream.Stream()
1624        >>> a.repeatInsert(note.Note('C'), list(range(10)))
1625        >>> junk = a.pop(0)
1626        >>> len(a)
1627        9
1628        '''
1629        eLen = len(self._elements)
1630        # if less then base length, its in _elements
1631        if index < eLen:
1632            post = self._elements.pop(index)
1633        else:  # it is in the _endElements
1634            post = self._endElements.pop(index - eLen)
1635
1636        self.coreElementsChanged(clearIsSorted=False)
1637
1638        try:
1639            del self._offsetDict[id(post)]
1640        except KeyError:  # pragma: no cover
1641            pass
1642
1643        post.sites.remove(self)
1644        post.activeSite = None
1645        return post
1646
1647    def _removeIteration(self, streamIterator):
1648        '''
1649        helper method to remove different kinds of elements.
1650        '''
1651        popDict = {'_elements': [],
1652                   '_endElements': []
1653                   }
1654        for unused_el in streamIterator:
1655            ai = streamIterator.activeInformation
1656            popDict[ai['iterSection']].append(ai['sectionIndex'])
1657
1658        # do not pop while iterating...
1659
1660        for section in popDict:
1661            sectionList = getattr(self, section)  # self._elements or self._endElements
1662            popList = popDict[section]
1663            for popIndex in reversed(popList):
1664                removeElement = sectionList.pop(popIndex)
1665                try:
1666                    del self._offsetDict[id(removeElement)]
1667                except KeyError:  # pragma: no cover
1668                    pass
1669
1670                # TODO: to make recursive, store a tuple of active sites and index
1671                removeElement.sites.remove(self)
1672                removeElement.activeSite = None
1673
1674        # call elements changed once; sorted arrangement has not changed
1675        self.coreElementsChanged(clearIsSorted=False)
1676
1677    def removeByClass(self, classFilterList) -> None:
1678        '''
1679        Remove all elements from the Stream
1680        based on one or more classes given
1681        in a list.
1682
1683        >>> s = stream.Stream()
1684        >>> s.append(meter.TimeSignature('4/4'))
1685        >>> s.repeatAppend(note.Note('C'), 8)
1686        >>> len(s)
1687        9
1688        >>> s.removeByClass('GeneralNote')
1689        >>> len(s)
1690        1
1691        >>> len(s.notes)
1692        0
1693
1694        Test that removing from end elements works.
1695
1696        >>> s = stream.Measure()
1697        >>> s.append(meter.TimeSignature('4/4'))
1698        >>> s.repeatAppend(note.Note('C'), 4)
1699        >>> s.rightBarline = bar.Barline('final')
1700        >>> len(s)
1701        6
1702        >>> s.removeByClass('Barline')
1703        >>> len(s)
1704        5
1705        '''
1706        elFilter = self.iter().getElementsByClass(classFilterList)
1707        self._removeIteration(elFilter)
1708
1709    def removeByNotOfClass(self, classFilterList):
1710        '''
1711        Remove all elements not of the specified
1712        class or subclass in the Stream in place.
1713
1714        >>> s = stream.Stream()
1715        >>> s.append(meter.TimeSignature('4/4'))
1716        >>> s.repeatAppend(note.Note('C'), 8)
1717        >>> len(s)
1718        9
1719        >>> s.removeByNotOfClass('TimeSignature')
1720        >>> len(s)
1721        1
1722        >>> len(s.notes)
1723        0
1724        '''
1725        elFilter = self.iter().getElementsNotOfClass(classFilterList)
1726        return self._removeIteration(elFilter)
1727
1728    def _deepcopySubclassable(self, memo=None, ignoreAttributes=None, removeFromIgnore=None):
1729        # NOTE: this is a performance critical operation
1730        defaultIgnoreSet = {'_offsetDict', 'streamStatus', '_elements', '_endElements', '_cache',
1731                            }
1732        if ignoreAttributes is None:
1733            ignoreAttributes = defaultIgnoreSet
1734        else:  # pragma: no cover
1735            ignoreAttributes = ignoreAttributes | defaultIgnoreSet
1736        new = super()._deepcopySubclassable(memo, ignoreAttributes, removeFromIgnore)
1737
1738        if removeFromIgnore is not None:  # pragma: no cover
1739            ignoreAttributes = ignoreAttributes - removeFromIgnore
1740
1741        if '_offsetDict' in ignoreAttributes:
1742            newValue = {}
1743            setattr(new, '_offsetDict', newValue)
1744        # all subclasses of Music21Object that define their own
1745        # __deepcopy__ methods must be sure to not try to copy activeSite
1746        if '_offsetDict' in self.__dict__:
1747            newValue = {}
1748            setattr(new, '_offsetDict', newValue)
1749        if 'streamStatus' in ignoreAttributes:
1750            # update the client
1751            if self.streamStatus is not None:
1752                # storedClient = self.streamStatus.client  # should be self
1753                # self.streamStatus.client = None
1754                newValue = copy.deepcopy(self.streamStatus)
1755                newValue.client = new
1756                setattr(new, 'streamStatus', newValue)
1757                # self.streamStatus.client = storedClient
1758        if '_elements' in ignoreAttributes:
1759            # must manually add elements to new Stream
1760            for e in self._elements:
1761                # environLocal.printDebug(['deepcopy()', e, 'old', old, 'id(old)', id(old),
1762                #     'new', new, 'id(new)', id(new), 'old.hasElement(e)', old.hasElement(e),
1763                #     'e.activeSite', e.activeSite, 'e.getSites()', e.getSites(), 'e.getSiteIds()',
1764                #     e.getSiteIds()], format='block')
1765                #
1766                # this will work for all with __deepcopy___
1767                # get the old offset from the activeSite Stream
1768                # user here to provide new offset
1769                #
1770                # new.insert(e.getOffsetBySite(old), newElement,
1771                #            ignoreSort=True)
1772                offset = self.elementOffset(e)
1773                if not e.isStream:
1774                    # noinspection PyArgumentList
1775                    newElement = copy.deepcopy(e, memo)
1776                else:  # this prevents needing to make multiple replacements of spanner bundles
1777                    newElement = e._deepcopySubclassable(memo)
1778
1779                # ## TEST on copying!!!!
1780                # if isinstance(newElement, note.Note):
1781                #     newElement.pitch.ps += 2.0
1782                new.coreInsert(offset, newElement, ignoreSort=True)
1783        if '_endElements' in ignoreAttributes:
1784            # must manually add elements to
1785            for e in self._endElements:
1786                # this will work for all with __deepcopy___
1787                # get the old offset from the activeSite Stream
1788                # user here to provide new offset
1789
1790                # noinspection PyArgumentList
1791                new.coreStoreAtEnd(copy.deepcopy(e, memo))
1792
1793        new.coreElementsChanged()
1794
1795        return new
1796
1797    def __deepcopy__(self, memo=None):
1798        '''
1799        Deepcopy the stream from copy.deepcopy()
1800        '''
1801        # does not purgeOrphans -- q: is that a bug or by design?
1802        new = self._deepcopySubclassable(memo)
1803        if new._elements:
1804            self._replaceSpannerBundleForDeepcopy(new)
1805
1806        # purging these orphans works in nearly all cases, but there are a few
1807        # cases where we rely on a Stream having access to Stream it was
1808        # part of after deepcopying
1809        # new.purgeOrphans()
1810        return new
1811
1812    def _replaceSpannerBundleForDeepcopy(self, new):
1813        # perform the spanner bundle replacement on the outer stream.
1814        # caching this is CRUCIAL! using new.spannerBundle every time below added
1815        # 40% to the test suite time!
1816        newSpannerBundle = new.spannerBundle
1817        # only proceed if there are spanners, otherwise creating semiFlat
1818        if not newSpannerBundle:
1819            return
1820        # iterate over complete semi-flat (need containers); find
1821        # all new/old pairs
1822        for e in new.recurse(includeSelf=False):
1823            # update based on id of old object, and ref to new object
1824            if 'music21.spanner.Spanner' in e.classSet:
1825                continue
1826            if e.derivation.method != '__deepcopy__':
1827                continue
1828
1829            origin = e.derivation.origin
1830            if origin is None:  # pragma: no cover
1831                continue  # should not happen...
1832
1833            if origin.sites.hasSpannerSite():
1834                # environLocal.printDebug(['Stream.__deepcopy__', 'replacing component to', e])
1835                # this will clear and replace the proper locations on
1836                # the SpannerStorage Stream
1837                newSpannerBundle.replaceSpannedElement(origin, e)
1838
1839                # need to remove the old SpannerStorage Stream from this element;
1840                # however, all we have here is the new Spanner and new elements
1841                # this must be done here, not when originally copying
1842                e.purgeOrphans(excludeStorageStreams=False)
1843
1844    def setElementOffset(
1845        self,
1846        element: base.Music21Object,
1847        offset: Union[int, float, Fraction, str],
1848    ):
1849        '''
1850        Sets the Offset for an element that is already in a given stream.
1851
1852        Setup a note in two different streams at two different offsets:
1853
1854        >>> n = note.Note('B-4')
1855        >>> s = stream.Stream(id='Stream1')
1856        >>> s.insert(10, n)
1857        >>> n.offset
1858        10.0
1859        >>> n.activeSite.id
1860        'Stream1'
1861
1862        >>> s2 = stream.Stream(id='Stream2')
1863        >>> s2.insert(30, n)
1864        >>> n.activeSite.id
1865        'Stream2'
1866
1867        Now change the note's offset in Stream1:
1868
1869        >>> s.setElementOffset(n, 20.0)
1870
1871        This call has the effect of switching the `activeSite` of `n` to `s`.
1872
1873        >>> n.activeSite.id
1874        'Stream1'
1875        >>> n.offset
1876        20.0
1877        >>> n.getOffsetBySite(s)
1878        20.0
1879
1880        If the element is not in the Stream, raises a StreamException:
1881
1882        >>> n2 = note.Note('D')
1883        >>> s.setElementOffset(n2, 30.0)
1884        Traceback (most recent call last):
1885        music21.exceptions21.StreamException: Cannot set the offset for element
1886            <music21.note.Note D>, not in Stream <music21.stream.Stream Stream1>.
1887
1888        * Changed in v5.5 -- also sets .activeSite for the element
1889
1890        * In v6.7 -- also runs coreElementsChanged()
1891
1892        * In v7. -- addElement is removed; see
1893            :meth:`~music21.stream.core.StreamCoreMixin.coreSetElementOffset`
1894        '''
1895        self.coreSetElementOffset(element,
1896                                  offset,
1897                                  )
1898        # might change sorting, but not flatness.  Maybe other things can be False too.
1899        self.coreElementsChanged(updateIsFlat=False)
1900
1901    def elementOffset(self, element, returnSpecial=False):
1902        '''
1903        Return the offset as an opFrac (float or Fraction) from the offsetMap.
1904        highly optimized for speed.
1905
1906        >>> m = stream.Measure(number=1)
1907        >>> m.append(note.Note('C'))
1908        >>> d = note.Note('D')
1909        >>> m.append(d)
1910        >>> m.elementOffset(d)
1911        1.0
1912
1913        If returnSpecial is True then returns like OffsetSpecial.AT_END are allowed.
1914
1915        >>> b = bar.Barline()
1916        >>> m.storeAtEnd(b)
1917        >>> m.elementOffset(b)
1918        2.0
1919        >>> m.elementOffset(b, returnSpecial=True)
1920        <OffsetSpecial.AT_END>
1921
1922        Unlike element.getOffsetBySite(self), this method will NOT follow derivation chains
1923        and in fact will raise a sites.SitesException
1924
1925        >>> import copy
1926        >>> p = stream.Part(id='sPart')
1927        >>> p.insert(20, m)
1928        >>> m.getOffsetBySite(p)
1929        20.0
1930        >>> p.elementOffset(m)
1931        20.0
1932
1933        >>> mCopy = copy.deepcopy(m)
1934        >>> mCopy.number = 10
1935        >>> mCopy.derivation
1936        <Derivation of <music21.stream.Measure 10 offset=0.0> from
1937            <music21.stream.Measure 1 offset=20.0> via '__deepcopy__'>
1938        >>> mCopy.getOffsetBySite(p)
1939        20.0
1940        >>> p.elementOffset(mCopy)
1941        Traceback (most recent call last):
1942        music21.sites.SitesException: an entry for this object 0x... is not stored in
1943            stream <music21.stream.Part sPart>
1944
1945        Performance note: because it will not follow derivation chains, and does
1946        not need to unwrap a weakref, this method
1947        should usually be about 3x faster than element.getOffsetBySite(self) --
1948        currently 600ns instead of 1.5 microseconds.
1949        '''
1950        try:
1951            # 2.3 million times found in TestStream
1952            o = self._offsetDict[id(element)][0]
1953            # if returnedElement is not element:  # stale reference...
1954            #    o = None  #  0 in TestStream -- not worth testing
1955        except KeyError:  # 445k - 442,443 = 3k in TestStream
1956            for idElement in self._offsetDict:  # slower search
1957                o, returnedElement = self._offsetDict[idElement]
1958                if element is returnedElement:
1959                    # MSC 2021 -- it is possible this no longer ever happens,
1960                    #    currently uncovered in Coverage.
1961                    break
1962            else:
1963                raise base.SitesException(
1964                    f'an entry for this object 0x{id(element):x} is not stored in stream {self}')
1965
1966        # OffsetSpecial.__contains__() is more expensive, so try to fail fast
1967        if isinstance(o, str) and returnSpecial is False and o in OffsetSpecial:
1968            try:
1969                return getattr(self, o)
1970            except AttributeError:  # pragma: no cover
1971                raise base.SitesException(
1972                    'attempted to retrieve a bound offset with a string '
1973                    + f'attribute that is not supported: {o}')
1974        else:
1975            return o
1976
1977    def insert(self,
1978               offsetOrItemOrList,
1979               itemOrNone=None,
1980               *,
1981               ignoreSort=False,
1982               setActiveSite=True
1983               ):
1984        '''
1985        Inserts an item(s) at the given offset(s).
1986
1987        If `ignoreSort` is True then the inserting does not
1988        change whether the Stream is sorted or not (much faster if you're
1989        going to be inserting dozens
1990        of items that don't change the sort status)
1991
1992        The `setActiveSite` parameter should nearly always be True; only for
1993        advanced Stream manipulation would you not change
1994        the activeSite after inserting an element.
1995
1996        Has three forms: in the two argument form, inserts an element at the given offset:
1997
1998        >>> st1 = stream.Stream()
1999        >>> st1.insert(32, note.Note('B-'))
2000        >>> st1.highestOffset
2001        32.0
2002
2003        In the single argument form with an object, inserts the element at its stored offset:
2004
2005        >>> n1 = note.Note('C#')
2006        >>> n1.offset = 30.0
2007        >>> st1 = stream.Stream()
2008        >>> st1.insert(n1)
2009        >>> st2 = stream.Stream()
2010        >>> st2.insert(40.0, n1)
2011        >>> n1.getOffsetBySite(st1)
2012        30.0
2013
2014        In single argument form with a list, the list should contain pairs that alternate
2015        offsets and items; the method then, obviously, inserts the items
2016        at the specified offsets:
2017
2018        >>> n1 = note.Note('G')
2019        >>> n2 = note.Note('F#')
2020        >>> st3 = stream.Stream()
2021        >>> st3.insert([1.0, n1, 2.0, n2])
2022        >>> n1.getOffsetBySite(st3)
2023        1.0
2024        >>> n2.getOffsetBySite(st3)
2025        2.0
2026        >>> len(st3)
2027        2
2028
2029        Raises an error if offset is not a number
2030
2031        >>> stream.Stream().insert('l', note.Note('B'))
2032        Traceback (most recent call last):
2033        music21.exceptions21.StreamException: Offset 'l' must be a number.
2034
2035        ...or if the object is not a music21 object (or a list of them)
2036
2037        >>> stream.Stream().insert(3.3, 'hello')
2038        Traceback (most recent call last):
2039        music21.exceptions21.StreamException: to put a non Music21Object in a stream,
2040            create a music21.ElementWrapper for the item
2041
2042        The error message is slightly different in the one-element form:
2043
2044        >>> stream.Stream().insert('hello')
2045        Traceback (most recent call last):
2046        music21.exceptions21.StreamException: Cannot insert item 'hello' to
2047            stream -- is it a music21 object?
2048        '''
2049        # environLocal.printDebug(['self', self, 'offsetOrItemOrList',
2050        #              offsetOrItemOrList, 'itemOrNone', itemOrNone,
2051        #             'ignoreSort', ignoreSort, 'setActiveSite', setActiveSite])
2052        # normal approach: provide offset and item
2053        if itemOrNone is not None:
2054            offset = offsetOrItemOrList
2055            item = itemOrNone
2056        elif itemOrNone is None and isinstance(offsetOrItemOrList, list):
2057            i = 0
2058            while i < len(offsetOrItemOrList):
2059                offset = offsetOrItemOrList[i]
2060                item = offsetOrItemOrList[i + 1]
2061                # recursively calling insert() here
2062                self.insert(offset, item, ignoreSort=ignoreSort)
2063                i += 2
2064            return
2065        # assume first arg is item, and that offset is local offset of object
2066        else:
2067            item = offsetOrItemOrList
2068            # offset = item.offset
2069            # this is equivalent to:
2070            try:
2071                activeSite = item.activeSite
2072                offset = item.getOffsetBySite(activeSite)
2073            except AttributeError:
2074                raise StreamException(f'Cannot insert item {item!r} to stream '
2075                                      + '-- is it a music21 object?')
2076
2077        # if not common.isNum(offset):
2078        try:  # using float conversion instead of isNum for performance
2079            offset = float(offset)
2080        except (ValueError, TypeError):
2081            raise StreamException(f'Offset {offset!r} must be a number.')
2082
2083        element = item
2084
2085        # checks if element is self, among other checks
2086        self.coreGuardBeforeAddElement(element)
2087        # main insert procedure here
2088
2089        storeSorted = self.coreInsert(offset, element,
2090                                      ignoreSort=ignoreSort, setActiveSite=setActiveSite)
2091        updateIsFlat = False
2092        if element.isStream:
2093            updateIsFlat = True
2094        self.coreElementsChanged(updateIsFlat=updateIsFlat)
2095        if ignoreSort is False:
2096            self.isSorted = storeSorted
2097
2098    def insertIntoNoteOrChord(self, offset, noteOrChord, chordsOnly=False):
2099        # noinspection PyShadowingNames
2100        '''
2101        Insert a Note or Chord into an offset position in this Stream.
2102        If there is another Note or Chord in this position,
2103        create a new Note or Chord that combines the pitches of the
2104        inserted chord. If there is a Rest in this position,
2105        the Rest is replaced by the Note or Chord. The duration of the
2106        previously-found chord will remain the same in the new Chord.
2107
2108        >>> n1 = note.Note('D4')
2109        >>> n1.duration.quarterLength = 2.0
2110        >>> r1 = note.Rest()
2111        >>> r1.duration.quarterLength = 2.0
2112        >>> c1 = chord.Chord(['C4', 'E4'])
2113        >>> s = stream.Stream()
2114        >>> s.append(n1)
2115        >>> s.append(r1)
2116        >>> s.append(c1)
2117        >>> s.show('text')
2118        {0.0} <music21.note.Note D>
2119        {2.0} <music21.note.Rest half>
2120        {4.0} <music21.chord.Chord C4 E4>
2121
2122        Save the original Streams for later
2123
2124        >>> import copy
2125        >>> s2 = copy.deepcopy(s)
2126        >>> s3 = copy.deepcopy(s)
2127        >>> s4 = copy.deepcopy(s)
2128
2129        Notice that the duration of the inserted element is not taken into
2130        consideration and the original element is not broken up,
2131        as it would be in chordify().  But Chords and Notes are created...
2132
2133        >>> for i in [0.0, 2.0, 4.0]:
2134        ...     s.insertIntoNoteOrChord(i, note.Note('F#4'))
2135        >>> s.show('text')
2136        {0.0} <music21.chord.Chord D4 F#4>
2137        {2.0} <music21.note.Note F#>
2138        {4.0} <music21.chord.Chord C4 E4 F#4>
2139
2140        if chordsOnly is set to True then no notes are returned, only chords, but
2141        untouched notes are left alone:
2142
2143        >>> s2.insert(5.0, note.Note('E##4'))
2144        >>> for i in [0.0, 2.0, 4.0]:
2145        ...     s2.insertIntoNoteOrChord(i, note.Note('F#4'), chordsOnly=True)
2146        >>> s2.show('text')
2147        {0.0} <music21.chord.Chord D4 F#4>
2148        {2.0} <music21.chord.Chord F#4>
2149        {4.0} <music21.chord.Chord C4 E4 F#4>
2150        {5.0} <music21.note.Note E##>
2151
2152        A chord inserted on top of a note always changes the note into a chord:
2153
2154        >>> s2.insertIntoNoteOrChord(5.0, chord.Chord('F#4 G-4'))
2155        >>> s2.show('text')
2156        {0.0} <music21.chord.Chord D4 F#4>
2157        {2.0} <music21.chord.Chord F#4>
2158        {4.0} <music21.chord.Chord C4 E4 F#4>
2159        {5.0} <music21.chord.Chord E##4 F#4 G-4>
2160
2161        Chords can also be inserted into rests:
2162
2163        >>> s3.getElementsByOffset(2.0).first()
2164        <music21.note.Rest half>
2165        >>> s3.insertIntoNoteOrChord(2.0, chord.Chord('C4 E4 G#4'))
2166        >>> s3.show('text')
2167        {0.0} <music21.note.Note D>
2168        {2.0} <music21.chord.Chord C4 E4 G#4>
2169        {4.0} <music21.chord.Chord C4 E4>
2170
2171        Despite the variable name, a rest could be inserted into a noteOrChord.
2172        It does nothing to existing notes or chords, and just adds a new rest
2173        afterwards.
2174
2175        >>> s4.show('text', addEndTimes=True)
2176        {0.0 - 2.0} <music21.note.Note D>
2177        {2.0 - 4.0} <music21.note.Rest half>
2178        {4.0 - 5.0} <music21.chord.Chord C4 E4>
2179
2180        >>> for i in [0.0, 4.0, 6.0]:  # skipping 2.0 for now
2181        ...     r = note.Rest(type='quarter')
2182        ...     s4.insertIntoNoteOrChord(i, r)
2183        >>> r2 = note.Rest(type='quarter')
2184        >>> s4.insertIntoNoteOrChord(2.0, r)
2185        >>> s4.show('text', addEndTimes=True)
2186        {0.0 - 2.0} <music21.note.Note D>
2187        {2.0 - 4.0} <music21.note.Rest half>
2188        {4.0 - 5.0} <music21.chord.Chord C4 E4>
2189        {6.0 - 7.0} <music21.note.Rest quarter>
2190
2191        Notice that (1) the original duration and not the new duration is used, unless
2192        there is no element at that place, and (2) if an element is put into a place where
2193        no existing element was found, then it will be found in the new Stream, but if it
2194        is placed on top of an existing element, the original element or a new copy will remain:
2195
2196        >>> r in s4
2197        True
2198        >>> r2 in s4
2199        False
2200
2201        If a Stream has more than one note, chord, or rest at that position,
2202        currently an error is raised.  This may change later:
2203
2204        >>> s5 = stream.Stream()
2205        >>> s5.insert(0, note.Note('C##4'))
2206        >>> s5.insert(0, note.Note('E--4'))
2207        >>> s5.insertIntoNoteOrChord(0, note.Note('D4'))
2208        Traceback (most recent call last):
2209        music21.exceptions21.StreamException: more than one element found at the specified offset
2210        '''
2211        # could use duration of Note to get end offset span
2212        targets = list(
2213            self.getElementsByOffset(
2214                offset,
2215                offset + noteOrChord.quarterLength,  # set end to dur of supplied
2216                includeEndBoundary=False,
2217                mustFinishInSpan=False,
2218                mustBeginInSpan=True
2219            ).notesAndRests
2220        )
2221        removeTarget = None
2222        # environLocal.printDebug(['insertIntoNoteOrChord', [e for e in targets]])
2223        if len(targets) == 1:
2224            pitches = []      # avoid an undefined variable warning...
2225            components = []   # ditto
2226
2227            target = targets[0]  # assume first
2228            removeTarget = target
2229            if isinstance(target, note.Rest):
2230                if isinstance(noteOrChord, note.Note):
2231                    pitches = [noteOrChord.pitch]
2232                    components = [noteOrChord]
2233                elif isinstance(noteOrChord, chord.Chord):
2234                    pitches = list(noteOrChord.pitches)
2235                    components = list(noteOrChord)
2236            if isinstance(target, note.Note):
2237                # if a note, make it into a chord
2238                if isinstance(noteOrChord, note.Note):
2239                    pitches = [target.pitch, noteOrChord.pitch]
2240                    components = [target, noteOrChord]
2241                elif isinstance(noteOrChord, chord.Chord):
2242                    pitches = [target.pitch] + list(noteOrChord.pitches)
2243                    components = [target] + list(noteOrChord)
2244                else:
2245                    pitches = [target.pitch]
2246                    components = [target]
2247            if isinstance(target, chord.Chord):
2248                # if a chord, make it into a chord
2249                if isinstance(noteOrChord, note.Note):
2250                    pitches = list(target.pitches) + [noteOrChord.pitch]
2251                    components = list(target) + [noteOrChord]
2252                elif isinstance(noteOrChord, chord.Chord):
2253                    pitches = list(target.pitches) + list(noteOrChord.pitches)
2254                    components = list(target) + list(noteOrChord)
2255                else:
2256                    pitches = list(target.pitches)
2257                    components = list(target)
2258
2259            if len(pitches) > 1 or chordsOnly is True:
2260                finalTarget = chord.Chord(pitches)
2261            elif len(pitches) == 1:
2262                finalTarget = note.Note(pitches[0])
2263            else:
2264                finalTarget = note.Rest()
2265
2266            finalTarget.expressions = target.expressions
2267            finalTarget.articulations = target.articulations
2268            finalTarget.duration = target.duration
2269            # append lyrics list
2270            if hasattr(target, 'lyrics'):
2271                for ly in target.lyrics:
2272                    if ly.text not in ('', None):
2273                        finalTarget.addLyric(ly.text)
2274            # finalTarget.lyrics = target.lyrics
2275            if hasattr(finalTarget, 'stemDirection') and hasattr(target, 'stemDirection'):
2276                finalTarget.stemDirection = target.stemDirection
2277            if hasattr(finalTarget, 'noteheadFill') and hasattr(target, 'noteheadFill'):
2278                finalTarget.noteheadFill = target.noteheadFill
2279
2280            # fill component details
2281            if isinstance(finalTarget, chord.Chord):
2282                for i, n in enumerate(finalTarget):
2283                    nPrevious = components[i]
2284                    n.noteheadFill = nPrevious.noteheadFill
2285
2286        elif len(targets) > 1:
2287            raise StreamException('more than one element found at the specified offset')
2288        else:
2289            finalTarget = noteOrChord
2290
2291        if removeTarget is not None:
2292            self.remove(removeTarget)
2293        # insert normally, nothing to handle
2294        self.insert(offset, finalTarget, ignoreSort=False, setActiveSite=True)
2295
2296    def append(self, others):
2297        '''
2298        Add a Music21Object (including another Stream) to the end of the current Stream.
2299
2300        If given a list, will append each element in order after the previous one.
2301
2302        The "end" of the stream is determined by the `highestTime` property
2303        (that is the latest "release" of an object, or directly after the last
2304        element ends).
2305
2306        Runs fast for multiple addition and will preserve isSorted if True
2307
2308        >>> a = stream.Stream()
2309        >>> notes = []
2310        >>> for x in range(3):
2311        ...     n = note.Note('G#')
2312        ...     n.duration.quarterLength = 3
2313        ...     notes.append(n)
2314        >>> a.append(notes[0])
2315        >>> a.highestOffset, a.highestTime
2316        (0.0, 3.0)
2317        >>> a.append(notes[1])
2318        >>> a.highestOffset, a.highestTime
2319        (3.0, 6.0)
2320        >>> a.append(notes[2])
2321        >>> a.highestOffset, a.highestTime
2322        (6.0, 9.0)
2323        >>> notes2 = []
2324
2325        Notes' naive offsets will
2326        change when they are added to a stream.
2327
2328        >>> for x in range(3):
2329        ...     n = note.Note('A-')
2330        ...     n.duration.quarterLength = 3
2331        ...     n.offset = 0
2332        ...     notes2.append(n)
2333        >>> a.append(notes2)  # add em all again
2334        >>> a.highestOffset, a.highestTime
2335        (15.0, 18.0)
2336        >>> a.isSequence()
2337        True
2338
2339        Adding a note that already has an offset set does nothing different
2340        from above! That is, it is still added to the end of the Stream:
2341
2342        >>> n3 = note.Note('B-')
2343        >>> n3.offset = 1
2344        >>> n3.duration.quarterLength = 3
2345        >>> a.append(n3)
2346        >>> a.highestOffset, a.highestTime
2347        (18.0, 21.0)
2348        >>> n3.getOffsetBySite(a)
2349        18.0
2350
2351        Prior to v5.7 there was a bug where appending a `Clef` after a `KeySignature`
2352        or a `Measure` after a `KeySignature`, etc. would not cause sorting to be re-run.
2353        This bug is now fixed.
2354
2355        >>> s = stream.Stream()
2356        >>> s.append([meter.TimeSignature('4/4'),
2357        ...           clef.TrebleClef()])
2358        >>> s.elements[0]
2359        <music21.clef.TrebleClef>
2360        >>> s.show('text')
2361        {0.0} <music21.clef.TrebleClef>
2362        {0.0} <music21.meter.TimeSignature 4/4>
2363
2364        >>> s.append(metadata.Metadata(composer='Cage'))
2365        >>> s.show('text')
2366        {0.0} <music21.metadata.Metadata object at 0x11ca356a0>
2367        {0.0} <music21.clef.TrebleClef>
2368        {0.0} <music21.meter.TimeSignature 4/4>
2369        '''
2370        # store and increment highest time for insert offset
2371        highestTime = self.highestTime
2372        if not common.isListLike(others):
2373            # back into a list for list processing if single
2374            others = [others]
2375
2376        clearIsSorted = False
2377        if self._elements:
2378            lastElement = self._elements[-1]
2379        else:
2380            lastElement = None
2381
2382        updateIsFlat = False
2383        for e in others:
2384            try:
2385                if e.isStream:  # any on that is a Stream req update
2386                    updateIsFlat = True
2387            except AttributeError:
2388                raise StreamException(
2389                    f'The object you tried to add to the Stream, {e!r}, '
2390                    + 'is not a Music21Object.  Use an ElementWrapper object '
2391                    + 'if this is what you intend')
2392            self.coreGuardBeforeAddElement(e)
2393            # add this Stream as a location for the new elements, with the
2394            # the offset set to the current highestTime
2395            self.coreSetElementOffset(e, highestTime, addElement=True)
2396            e.sites.add(self)
2397            # need to explicitly set the activeSite of the element
2398            self.coreSelfActiveSite(e)
2399            self._elements.append(e)
2400
2401            if e.duration.quarterLength != 0:
2402                # environLocal.printDebug(['incrementing highest time',
2403                #                         'e.duration.quarterLength',
2404                #                          e.duration.quarterLength])
2405                highestTime += e.duration.quarterLength
2406            if lastElement is not None and not lastElement.duration.quarterLength:
2407                if (e.priority < lastElement.priority
2408                        or e.classSortOrder < lastElement.classSortOrder):
2409                    clearIsSorted = True
2410            lastElement = e
2411
2412        # does not normally change sorted state
2413        if clearIsSorted:
2414            storeSorted = False
2415        else:
2416            storeSorted = self.isSorted
2417
2418        # we cannot keep the index cache here b/c we might
2419        self.coreElementsChanged(updateIsFlat=updateIsFlat)
2420        self.isSorted = storeSorted
2421        self._setHighestTime(opFrac(highestTime))  # call after to store in cache
2422
2423    def storeAtEnd(self, itemOrList, ignoreSort=False):
2424        '''
2425        Inserts an item or items at the end of the Stream,
2426        stored in the special box (called _endElements).
2427
2428        This method is useful for putting things such as
2429        right bar lines or courtesy clefs that should always
2430        be at the end of a Stream no matter what else is appended
2431        to it.
2432
2433        As sorting is done only by priority and class,
2434        it cannot avoid setting isSorted to False.
2435
2436        >>> s = stream.Stream()
2437        >>> b = bar.Repeat()
2438        >>> s.storeAtEnd(b)
2439        >>> b in s
2440        True
2441        >>> s.elementOffset(b)
2442        0.0
2443        >>> s.elementOffset(b, returnSpecial=True)
2444        <OffsetSpecial.AT_END>
2445
2446        Only elements of zero duration can be stored.  Otherwise a
2447        `StreamException` is raised.
2448        '''
2449        if isinstance(itemOrList, list):
2450            for item in itemOrList:
2451                # recursively calling insert() here
2452                self.storeAtEnd(item, ignoreSort=ignoreSort)
2453            return
2454        else:
2455            item = itemOrList
2456
2457        element = item
2458        # checks if element is self, among other checks
2459        self.coreGuardBeforeAddElement(element)
2460
2461        # cannot support elements with Durations in the highest time list
2462        if element.duration.quarterLength != 0:
2463            raise StreamException('cannot insert an object with a non-zero '
2464                                  + 'Duration into the highest time elements list')
2465
2466        self.coreStoreAtEnd(element)
2467        # Streams cannot reside in end elements, thus do not update is flat
2468        self.coreElementsChanged(updateIsFlat=False)
2469
2470    # --------------------------------------------------------------------------
2471    # all the following call either insert() or append()
2472
2473    def insertAndShift(self, offsetOrItemOrList, itemOrNone=None):
2474        '''
2475        Insert an item at a specified or native offset,
2476        and shift any elements found in the Stream to start at
2477        the end of the added elements.
2478
2479        This presently does not shift elements that have durations
2480        that extend into the lowest insert position.
2481
2482        >>> st1 = stream.Stream()
2483        >>> st1.insertAndShift(32, note.Note('B'))
2484        >>> st1.highestOffset
2485        32.0
2486        >>> st1.insertAndShift(32, note.Note('C'))
2487        >>> st1.highestOffset
2488        33.0
2489        >>> st1.show('text', addEndTimes=True)
2490        {32.0 - 33.0} <music21.note.Note C>
2491        {33.0 - 34.0} <music21.note.Note B>
2492
2493        Let's insert an item at the beginning, note that
2494        since the C and B are not affected, they do not shift.
2495
2496        >>> st1.insertAndShift(0, note.Note('D'))
2497        >>> st1.show('text', addEndTimes=True)
2498        {0.0 - 1.0} <music21.note.Note D>
2499        {32.0 - 33.0} <music21.note.Note C>
2500        {33.0 - 34.0} <music21.note.Note B>
2501
2502        But if we insert something again at the beginning of the stream,
2503        everything after the first shifted note begins shifting, so the
2504        C and the B shift even though there is a gap there.  Normally
2505        there's no gaps in a stream, so this will not be a factor:
2506
2507        >>> st1.insertAndShift(0, note.Note('E'))
2508        >>> st1.show('text', addEndTimes=True)
2509        {0.0 - 1.0} <music21.note.Note E>
2510        {1.0 - 2.0} <music21.note.Note D>
2511        {33.0 - 34.0} <music21.note.Note C>
2512        {34.0 - 35.0} <music21.note.Note B>
2513
2514        In the single argument form with an object, inserts the element at its stored offset:
2515
2516        >>> n1 = note.Note('C#')
2517        >>> n1.offset = 30.0
2518        >>> n2 = note.Note('D#')
2519        >>> n2.offset = 30.0
2520        >>> st1 = stream.Stream()
2521        >>> st1.insertAndShift(n1)
2522        >>> st1.insertAndShift(n2)  # will shift offset of n1
2523        >>> n1.getOffsetBySite(st1)
2524        31.0
2525        >>> n2.getOffsetBySite(st1)
2526        30.0
2527        >>> st1.show('text', addEndTimes=True)
2528        {30.0 - 31.0} <music21.note.Note D#>
2529        {31.0 - 32.0} <music21.note.Note C#>
2530
2531        >>> st2 = stream.Stream()
2532        >>> st2.insertAndShift(40.0, n1)
2533        >>> st2.insertAndShift(40.0, n2)
2534        >>> n1.getOffsetBySite(st2)
2535        41.0
2536
2537        In single argument form with a list, the list should contain pairs that alternate
2538        offsets and items; the method then, obviously, inserts the items
2539        at the specified offsets:
2540
2541        >>> n1 = note.Note('G-')
2542        >>> n2 = note.Note('F-')
2543        >>> st3 = stream.Stream()
2544        >>> st3.insertAndShift([1.0, n1, 2.0, n2])
2545        >>> n1.getOffsetBySite(st3)
2546        1.0
2547        >>> n2.getOffsetBySite(st3)
2548        2.0
2549        >>> len(st3)
2550        2
2551        >>> st3.show('text', addEndTimes=True)
2552        {1.0 - 2.0} <music21.note.Note G->
2553        {2.0 - 3.0} <music21.note.Note F->
2554
2555        N.B. -- using this method on a list assumes that you'll be inserting
2556        contiguous objects; you can't shift things that are separated, as this
2557        following FAILED example shows.
2558
2559        >>> n1 = note.Note('G', type='half')
2560        >>> st4 = stream.Stream()
2561        >>> st4.repeatAppend(n1, 3)
2562        >>> st4.insertAndShift([2.0, note.Note('e'), 4.0, note.Note('f')])
2563        >>> st4.show('text')
2564        {0.0} <music21.note.Note G>
2565        {2.0} <music21.note.Note E>
2566        {4.0} <music21.note.Note F>
2567        {5.0} <music21.note.Note G>
2568        {7.0} <music21.note.Note G>
2569
2570        As an FYI, there is no removeAndShift() function, so the opposite of
2571        insertAndShift(el) is remove(el, shiftOffsets=True).
2572        '''
2573        # need to find the highest time after the insert
2574        if itemOrNone is not None:  # we have an offset and an element
2575            insertObject = itemOrNone
2576            qL = insertObject.duration.quarterLength
2577            offset = offsetOrItemOrList
2578            lowestOffsetInsert = offset
2579            highestTimeInsert = offset + qL
2580        elif itemOrNone is None and isinstance(offsetOrItemOrList, list):
2581            # need to find which has the highest endtime (combined offset and dur)
2582            insertList = offsetOrItemOrList
2583            highestTimeInsert = 0.0
2584            lowestOffsetInsert = None
2585            i = 0
2586            while i < len(insertList):
2587                o = insertList[i]
2588                e = insertList[i + 1]
2589                qL = e.duration.quarterLength
2590                if o + qL > highestTimeInsert:
2591                    highestTimeInsert = o + qL
2592                if lowestOffsetInsert is None or o < lowestOffsetInsert:
2593                    lowestOffsetInsert = o
2594                i += 2
2595        else:  # using native offset
2596            # if hasattr(offsetOrItemOrList, 'duration'):
2597            insertObject = offsetOrItemOrList
2598            qL = insertObject.duration.quarterLength
2599            # should this be getOffsetBySite(None)?
2600            highestTimeInsert = insertObject.offset + qL
2601            lowestOffsetInsert = insertObject.offset
2602
2603        # this shift is the additional time to move due to the duration
2604        # of the newly inserted elements
2605
2606        # environLocal.printDebug(['insertAndShift()',
2607        #                         'adding one or more elements',
2608        #                         'lowestOffsetInsert', lowestOffsetInsert,
2609        #                         'highestTimeInsert', highestTimeInsert])
2610
2611        # are not assuming that elements are ordered
2612        # use getElementAtOrAfter() in the future
2613        lowestElementToShift = None
2614        lowestGap = None
2615        for e in self._elements:
2616            o = self.elementOffset(e)
2617            # gap is distance from offset to insert point; tells if shift is
2618            # necessary
2619            gap = o - lowestOffsetInsert
2620            if gap < 0:  # no shifting necessary
2621                continue
2622            # only process elements whose offsets are after the lowest insert
2623            if lowestGap is None or gap < lowestGap:
2624                lowestGap = gap
2625                lowestElementToShift = e
2626
2627        if lowestElementToShift is not None:
2628            lowestOffsetToShift = self.elementOffset(lowestElementToShift)
2629            shiftPos = highestTimeInsert - lowestOffsetToShift
2630        else:
2631            shiftPos = 0
2632
2633        if shiftPos <= 0:
2634            pass  # no need to move any objects
2635        # See stream.tests.Test.testInsertAndShiftNoDuration
2636        #      clef insertion at offset 3 which gives shiftPos < 0
2637        else:
2638            # need to move all the elements already in this stream
2639            for e in self._elements:
2640                o = self.elementOffset(e)
2641                # gap is distance from offset to insert point; tells if shift is
2642                # necessary
2643                gap = o - lowestOffsetInsert
2644                # only process elements whose offsets are after the lowest insert
2645                if gap >= 0.0:
2646                    # environLocal.printDebug(['insertAndShift()', e, 'offset', o,
2647                    #                         'gap:', gap, 'shiftDur:', shiftDur,
2648                    #                         'shiftPos:', shiftPos, 'o+shiftDur', o+shiftDur,
2649                    #                         'o+shiftPos', o+shiftPos])
2650
2651                    # need original offset, shiftDur, plus the distance from the start
2652                    self.coreSetElementOffset(e, o + shiftPos)
2653        # after shifting all the necessary elements, append new ones
2654        # these will not be in order
2655        self.coreElementsChanged()
2656        self.insert(offsetOrItemOrList, itemOrNone)
2657
2658    # --------------------------------------------------------------------------
2659    # searching and replacing routines
2660
2661    def setDerivationMethod(self, derivationMethod, recurse=False):
2662        '''
2663        Sets the .derivation.method for each element in the Stream
2664        if it has a .derivation object.
2665
2666        >>> import copy
2667        >>> s = converter.parse('tinyNotation: 2/4 c2 d e f')
2668        >>> s2 = copy.deepcopy(s)
2669        >>> s2.recurse().notes[-1].derivation
2670        <Derivation of <music21.note.Note F> from <music21.note.Note F> via '__deepcopy__'>
2671        >>> s2.setDerivationMethod('exampleCopy', recurse=True)
2672        >>> s2.recurse().notes[-1].derivation
2673        <Derivation of <music21.note.Note F> from <music21.note.Note F> via 'exampleCopy'>
2674
2675        Without recurse:
2676
2677        >>> s = converter.parse('tinyNotation: 2/4 c2 d e f')
2678        >>> s2 = copy.deepcopy(s)
2679        >>> s2.setDerivationMethod('exampleCopy')
2680        >>> s2.recurse().notes[-1].derivation
2681        <Derivation of <music21.note.Note F> from <music21.note.Note F> via '__deepcopy__'>
2682        '''
2683        if recurse:
2684            sIter = self.recurse()
2685        else:
2686            sIter = self.iter()
2687
2688        for el in sIter:
2689            if el.derivation is not None:
2690                el.derivation.method = derivationMethod
2691
2692    def replace(self,
2693                target: base.Music21Object,
2694                replacement: base.Music21Object,
2695                *,
2696                recurse: bool = False,
2697                allDerived: bool = True) -> None:
2698        '''
2699        Given a `target` object, replace it with
2700        the supplied `replacement` object.
2701
2702        Does nothing if target cannot be found. Raises StreamException if replacement
2703        is already in the stream.
2704
2705        If `allDerived` is True (as it is by default), all sites (stream) that
2706        this this stream derives from and also
2707        have a reference for the replacement will be similarly changed.
2708        This is useful for altering both a flat and nested representation.
2709
2710        >>> cSharp = note.Note('C#4')
2711        >>> s = stream.Stream()
2712        >>> s.insert(0, cSharp)
2713        >>> dFlat = note.Note('D-4')
2714        >>> s.replace(cSharp, dFlat)
2715        >>> s.show('t')
2716        {0.0} <music21.note.Note D->
2717
2718        If allDerived is True then all streams that this stream comes from get changed
2719        (but not non-derived streams)
2720
2721        >>> otherStream = stream.Stream()
2722        >>> otherStream.insert(0, dFlat)
2723        >>> f = note.Note('F4')
2724        >>> sf = s.flatten()
2725        >>> sf is not s
2726        True
2727        >>> sf.replace(dFlat, f, allDerived=True)
2728        >>> sf[0] is f
2729        True
2730        >>> s[0] is f
2731        True
2732        >>> otherStream[0] is dFlat
2733        True
2734
2735        Note that it does not work the other way: if we made the replacement on `s`
2736        then `sf`, the flattened representation, would not be changed, since `s`
2737        does not derive from `sf` but vice-versa.
2738
2739        With `recurse=True`, a stream can replace an element that is
2740        further down in the hierarchy.  First let's set up a
2741        nested score:
2742
2743        >>> s = stream.Score()
2744        >>> p = stream.Part(id='part1')
2745        >>> s.append(p)
2746        >>> m = stream.Measure()
2747        >>> p.append(m)
2748        >>> cSharp = note.Note('C#4')
2749        >>> m.append(cSharp)
2750        >>> s.show('text')
2751        {0.0} <music21.stream.Part part1>
2752            {0.0} <music21.stream.Measure 0 offset=0.0>
2753                {0.0} <music21.note.Note C#>
2754
2755        Now make a deep-nested replacement
2756
2757        >>> dFlat = note.Note('D-4')
2758        >>> s.replace(cSharp, dFlat, recurse=True)
2759        >>> s.show('text')
2760        {0.0} <music21.stream.Part part1>
2761            {0.0} <music21.stream.Measure 0 offset=0.0>
2762                {0.0} <music21.note.Note D->
2763
2764        Changed by v.5:
2765
2766        allTargetSites RENAMED to allDerived -- only searches in derivation chain.
2767
2768        Changed in v5.3 -- firstMatchOnly removed -- impossible to have element
2769        in stream twice.  recurse and shiftOffsets changed to keywordOnly arguments
2770
2771        Changed in v6 -- recurse works
2772
2773        Changed in v7 -- raises StreamException if replacement is already in the stream.
2774        '''
2775        def replaceDerived(startSite=self):
2776            if not allDerived:
2777                return
2778            for derivedSite in startSite.derivation.chain():
2779                for subsite in derivedSite.recurse(streamsOnly=True, includeSelf=True):
2780                    if subsite in target.sites:
2781                        subsite.replace(target,
2782                                        replacement,
2783                                        recurse=recurse,
2784                                        allDerived=False)
2785
2786        try:
2787            i = self.index(replacement)
2788        except StreamException:
2789            # good. now continue.
2790            pass
2791        else:
2792            raise StreamException(f'{replacement} already in {self}')
2793
2794        try:
2795            i = self.index(target)
2796        except StreamException:
2797            if recurse:
2798                container = self.containerInHierarchy(target, setActiveSite=False)
2799                if container is not None:
2800                    container.replace(target, replacement, allDerived=allDerived)
2801                replaceDerived()
2802                if container is not None:
2803                    replaceDerived(startSite=container)
2804            return  # do nothing if no match
2805
2806        eLen = len(self._elements)
2807        if i < eLen:
2808            target = self._elements[i]  # target may have been obj id; re-classing
2809            self._elements[i] = replacement
2810            # place the replacement at the old objects offset for this site
2811            self.coreSetElementOffset(replacement, self.elementOffset(target), addElement=True)
2812            replacement.sites.add(self)
2813        else:
2814            # target may have been obj id; reassign
2815            target = self._endElements[i - eLen]
2816            self._endElements[i - eLen] = replacement
2817
2818            self.coreSetElementOffset(replacement, OffsetSpecial.AT_END, addElement=True)
2819            replacement.sites.add(self)
2820
2821        target.sites.remove(self)
2822        target.activeSite = None
2823        if id(target) in self._offsetDict:
2824            del(self._offsetDict[id(target)])
2825
2826        updateIsFlat = False
2827        if replacement.isStream:
2828            updateIsFlat = True
2829        # elements have changed: sort order may change b/c have diff classes
2830        self.coreElementsChanged(updateIsFlat=updateIsFlat)
2831
2832        replaceDerived()
2833
2834    def splitAtDurations(self, *, recurse=False) -> base._SplitTuple:
2835        '''
2836        Overrides base method :meth:`~music21.base.Music21Object.splitAtDurations`
2837        so that once each element in the stream having a complex duration is split
2838        into similar, shorter elements representing each duration component,
2839        the original element is actually replaced in the stream where it was found
2840        with those new elements.
2841
2842        Returns a 1-tuple containing itself, for consistency with the superclass method.
2843
2844        >>> s = stream.Stream()
2845        >>> s.insert(note.Note(quarterLength=5.0))
2846        >>> post = s.splitAtDurations()
2847        >>> post
2848        (<music21.stream.Stream 0x10955ceb0>,)
2849        >>> [n.duration for n in s]
2850        [<music21.duration.Duration 4.0>, <music21.duration.Duration 1.0>]
2851
2852        Unless `recurse=True`, notes in substreams will not be found.
2853
2854        >>> s2 = stream.Score()
2855        >>> p = stream.Part([note.Note(quarterLength=5)])
2856        >>> s2.append(p)
2857        >>> s2.splitAtDurations()
2858        (<music21.stream.Score 0x10d12f100>,)
2859        >>> [n.duration for n in s2.recurse().notes]
2860        [<music21.duration.Duration 5.0>]
2861        >>> s2.splitAtDurations(recurse=True)
2862        (<music21.stream.Score 0x10d12f100>,)
2863        >>> [n.duration for n in s2.recurse().notes]
2864        [<music21.duration.Duration 4.0>, <music21.duration.Duration 1.0>]
2865
2866        `recurse=True` should not be necessary to find elements in streams
2867        without substreams, such as a loose Voice:
2868
2869        >>> v = stream.Voice([note.Note(quarterLength=5.5)], id=1)
2870        >>> v.splitAtDurations()
2871        (<music21.stream.Voice 1>,)
2872        >>> [n.duration for n in v.notes]
2873        [<music21.duration.Duration 4.0>, <music21.duration.Duration 1.5>]
2874
2875        But a Voice in a Measure (most common) will not be found without `recurse`:
2876
2877        >>> m = stream.Measure()
2878        >>> v2 = stream.Voice([note.Note(quarterLength=5.25)])
2879        >>> m.insert(v2)
2880        >>> m.splitAtDurations()
2881        (<music21.stream.Measure 0 offset=0.0>,)
2882        >>> [n.duration for n in m.recurse().notes]
2883        [<music21.duration.Duration 5.25>]
2884
2885        For any spanner containing the element being removed, the first or last of the
2886        replacing components replaces the removed element
2887        (according to whether it was first or last in the spanner.)
2888
2889        >>> s3 = stream.Stream()
2890        >>> n1 = note.Note(quarterLength=5)
2891        >>> n2 = note.Note(quarterLength=5)
2892        >>> s3.append([n1, n2])
2893        >>> s3.insert(0, spanner.Slur([n1, n2]))
2894        >>> post = s3.splitAtDurations()
2895        >>> s3.spanners.first().getFirst() is n1
2896        False
2897        >>> s3.spanners.first().getFirst().duration
2898        <music21.duration.Duration 4.0>
2899        >>> s3.spanners.first().getLast().duration
2900        <music21.duration.Duration 1.0>
2901
2902        Does not act on rests where `.fullMeasure` is True or 'always',
2903        nor when `.fullMeasure` is 'auto' and the duration equals the `.barDuration`.
2904        This is because full measure rests are usually represented
2905        as a single whole rest regardless of their duration.
2906
2907        >>> r = note.Rest(quarterLength=5.0)
2908        >>> r.fullMeasure = 'auto'
2909        >>> v = stream.Voice(r)
2910        >>> m = stream.Measure(v)
2911        >>> result = m.splitAtDurations(recurse=True)
2912        >>> list(result[0][note.Rest])
2913        [<music21.note.Rest 5ql>]
2914
2915        Here is a rest that doesn't fill the measure:
2916
2917        >>> m.insert(0, meter.TimeSignature('6/4'))
2918        >>> result = m.splitAtDurations(recurse=True)
2919        >>> list(result[0][note.Rest])
2920        [<music21.note.Rest whole>, <music21.note.Rest quarter>]
2921
2922        But by calling it a full-measure rest, we won't try to split it:
2923
2924        >>> r2 = note.Rest(quarterLength=5.0)
2925        >>> r2.fullMeasure = True
2926        >>> m2 = stream.Measure(r2)
2927        >>> m2.insert(0, meter.TimeSignature('6/4'))
2928        >>> result = m2.splitAtDurations()
2929        >>> list(result[0][note.Rest])
2930        [<music21.note.Rest 5ql>]
2931        '''
2932
2933        def processContainer(container: Stream):
2934            for complexObj in container.getElementsNotOfClass(['Stream', 'Variant', 'Spanner']):
2935                if complexObj.duration.type != 'complex':
2936                    continue
2937                if isinstance(complexObj, note.Rest) and complexObj.fullMeasure in (True, 'always'):
2938                    continue
2939                if isinstance(complexObj, note.Rest) and complexObj.fullMeasure == 'auto':
2940                    if container.isMeasure and (complexObj.duration == container.barDuration):
2941                        continue
2942                    elif ('Voice' in container.classes
2943                          and container.activeSite
2944                          and container.activeSite.isMeasure
2945                          and complexObj.duration == container.activeSite.barDuration
2946                          ):
2947                        continue
2948
2949                insertPoint = complexObj.offset
2950                objList = complexObj.splitAtDurations()
2951
2952                container.replace(complexObj, objList[0])
2953                insertPoint += objList[0].quarterLength
2954
2955                for subsequent in objList[1:]:
2956                    container.insert(insertPoint, subsequent)
2957                    insertPoint += subsequent.quarterLength
2958
2959                # Replace elements in spanners
2960                for sp in complexObj.getSpannerSites():
2961                    if sp.getFirst() is complexObj:
2962                        sp.replaceSpannedElement(complexObj, objList[0])
2963                    if sp.getLast() is complexObj:
2964                        sp.replaceSpannedElement(complexObj, objList[-1])
2965
2966                container.streamStatus.beams = False
2967
2968        # Handle "loose" objects in self (usually just Measure or Voice)
2969        processContainer(self)
2970        # Handle inner streams
2971        if recurse:
2972            for innerStream in self.recurse(
2973                    includeSelf=False, streamsOnly=True, restoreActiveSites=True):
2974                processContainer(innerStream)
2975
2976        return base._SplitTuple((self,))
2977
2978    def splitAtQuarterLength(self,
2979                             quarterLength,
2980                             *,
2981                             retainOrigin=True,
2982                             addTies=True,
2983                             displayTiedAccidentals=False,
2984                             searchContext=True):
2985        '''
2986        This method overrides the method on Music21Object to provide
2987        similar functionality for Streams.
2988
2989        Most arguments are passed to Music21Object.splitAtQuarterLength.
2990
2991        Changed in v7. -- all but quarterLength are keyword only
2992        '''
2993        quarterLength = opFrac(quarterLength)
2994        if retainOrigin:
2995            sLeft = self
2996        else:
2997            sLeft = self.coreCopyAsDerivation('splitAtQuarterLength')
2998        # create empty container for right-hand side
2999        sRight = self.__class__()
3000
3001        # if this is a Measure or Part, transfer clefs, ts, and key
3002        if sLeft.isMeasure:
3003            timeSignatures = sLeft.getTimeSignatures(
3004                searchContext=searchContext,
3005                returnDefault=False,
3006            )
3007            if timeSignatures:
3008                sRight.keySignature = copy.deepcopy(timeSignatures[0])
3009            if searchContext:
3010                keySignatures = sLeft.getContextByClass(key.KeySignature)
3011                if keySignatures is not None:
3012                    keySignatures = [keySignatures]
3013            else:
3014                keySignatures = sLeft.getElementsByClass(key.KeySignature)
3015            if keySignatures:
3016                sRight.keySignature = copy.deepcopy(keySignatures[0])
3017            endClef = sLeft.getContextByClass('Clef')
3018            if endClef is not None:
3019                sRight.clef = copy.deepcopy(endClef)
3020
3021        if quarterLength > sLeft.highestTime:  # nothing to do
3022            return sLeft, sRight
3023
3024        # use quarterLength as start time
3025        targets = sLeft.getElementsByOffset(
3026            quarterLength,
3027            sLeft.highestTime,
3028            includeEndBoundary=True,
3029            mustFinishInSpan=False,
3030            includeElementsThatEndAtStart=False,
3031            mustBeginInSpan=False
3032        )
3033
3034        targetSplit = []
3035        targetMove = []
3036        # find all those that need to split v. those that need to be moved
3037        for t in targets:
3038            # if target starts before the boundary, it needs to be split
3039            if sLeft.elementOffset(t) < quarterLength:
3040                targetSplit.append(t)
3041            else:
3042                targetMove.append(t)
3043
3044        # environLocal.printDebug(['split', targetSplit, 'move', targetMove])
3045
3046        for t in targetSplit:
3047            # must retain original, as a deepcopy, if necessary, has
3048            # already been made
3049
3050            # the split point needs to be relative to this element's start
3051            qlSplit = quarterLength - sLeft.elementOffset(t)
3052            unused_eLeft, eRight = t.splitAtQuarterLength(
3053                qlSplit,
3054                retainOrigin=True,
3055                addTies=addTies,
3056                displayTiedAccidentals=displayTiedAccidentals)
3057            # do not need to insert eLeft, as already positioned and
3058            # altered in-place above
3059            # it is assumed that anything cut will start at zero
3060            sRight.insert(0, eRight)
3061
3062        for t in targetMove:
3063            sRight.insert(t.getOffsetBySite(sLeft) - quarterLength, t)
3064            sLeft.remove(t)
3065
3066        return sLeft, sRight
3067
3068    # --------------------------------------------------------------------------
3069    def recurseRepr(self,
3070                    *,
3071                    prefixSpaces=0,
3072                    addBreaks=True,
3073                    addIndent=True,
3074                    addEndTimes=False,
3075                    useMixedNumerals=False):
3076        '''
3077        Used by .show('text') to display a stream's contents with offsets.
3078
3079        >>> s1 = stream.Stream()
3080        >>> s2 = stream.Stream()
3081        >>> s3 = stream.Stream()
3082        >>> n1 = note.Note()
3083        >>> s3.append(n1)
3084        >>> s2.append(s3)
3085        >>> s1.append(s2)
3086        >>> post = s1.recurseRepr(addBreaks=False, addIndent=False)
3087        >>> post
3088        '{0.0} <music21.stream.Stream ...> / {0.0} <...> / {0.0} <music21.note.Note C>'
3089
3090        Made public in v7.  Always calls on self.
3091        '''
3092        def singleElement(in_element,
3093                          in_indent,
3094                          ) -> str:
3095            offGet = in_element.getOffsetBySite(self)
3096            if useMixedNumerals:
3097                off = common.mixedNumeral(offGet)
3098            else:
3099                off = common.strTrimFloat(offGet)
3100            if addEndTimes is False:
3101                return in_indent + '{' + off + '} ' + repr(in_element)
3102            else:
3103                ql = offGet + in_element.duration.quarterLength
3104                if useMixedNumerals:
3105                    qlStr = common.mixedNumeral(ql)
3106                else:
3107                    qlStr = common.strTrimFloat(ql)
3108                return in_indent + '{' + off + ' - ' + qlStr + '} ' + repr(in_element)
3109
3110        msg = []
3111        insertSpaces = 4
3112        for element in self:
3113            if addIndent:
3114                indent = ' ' * prefixSpaces
3115            else:
3116                indent = ''
3117
3118            # if isinstance(element, Stream):
3119            if element.isStream:
3120                msg.append(singleElement(element, indent))
3121                msg.append(
3122                    element.recurseRepr(prefixSpaces=prefixSpaces + insertSpaces,
3123                                        addBreaks=addBreaks,
3124                                        addIndent=addIndent,
3125                                        addEndTimes=addEndTimes,
3126                                        useMixedNumerals=useMixedNumerals)
3127                )
3128            else:
3129                msg.append(singleElement(element, indent))
3130        if addBreaks:
3131            msg = '\n'.join(msg)
3132        else:  # use slashes with spaces
3133            msg = ' / '.join(msg)
3134        return msg
3135
3136    def _reprText(self, *, addEndTimes=False, useMixedNumerals=False):
3137        '''
3138        Return a text representation. This methods can be overridden by
3139        subclasses to provide alternative text representations.
3140
3141        This is used by .show('text')
3142        '''
3143        return self.recurseRepr(addEndTimes=addEndTimes,
3144                                useMixedNumerals=useMixedNumerals)
3145
3146    def _reprTextLine(self, *, addEndTimes=False, useMixedNumerals=False):
3147        '''
3148        Return a text representation without line breaks.
3149        This methods can be overridden by subclasses to
3150        provide alternative text representations.
3151        '''
3152        return self.recurseRepr(addEndTimes=addEndTimes,
3153                                useMixedNumerals=useMixedNumerals,
3154                                addBreaks=False,
3155                                addIndent=False)
3156
3157    # --------------------------------------------------------------------------
3158    # display methods; in the same manner as show() and write()
3159
3160    def plot(self, *args, **keywords):
3161        '''
3162        Given a method and keyword configuration arguments, create and display a plot.
3163
3164        Note: plot() requires the Python package matplotlib to be installed.
3165
3166        For details on arguments this function takes, see
3167        :ref:`User's Guide, Chapter 22: Graphing <usersGuide_22_graphing>`.
3168
3169        >>> s = corpus.parse('demos/two-parts.xml') #_DOCS_HIDE
3170        >>> thePlot = s.plot('pianoroll', doneAction=None) #_DOCS_HIDE
3171        >>> #_DOCS_SHOW s = corpus.parse('bach/bwv57.8')
3172        >>> #_DOCS_SHOW thePlot = s.plot('pianoroll')
3173
3174        .. image:: images/HorizontalBarPitchSpaceOffset.*
3175            :width: 600
3176
3177        '''
3178        # import is here to avoid import of matplotlib problems
3179        from music21 import graph
3180        # first ordered arg can be method type
3181        return graph.plotStream(self, *args, **keywords)
3182
3183    def analyze(self, *args, **keywords):
3184        '''
3185        Runs a particular analytical method on the contents of the
3186        stream to find its ambitus (range) or key.
3187
3188        *  ambitus -- runs :class:`~music21.analysis.discrete.Ambitus`
3189        *  key -- runs :class:`~music21.analysis.discrete.KrumhanslSchmuckler`
3190
3191        Some of these methods can take additional arguments.  For details on
3192        these arguments, see
3193        :func:`~music21.analysis.discrete.analyzeStream`.
3194
3195        Example:
3196
3197
3198        >>> s = corpus.parse('bach/bwv66.6')
3199        >>> s.analyze('ambitus')
3200        <music21.interval.Interval m21>
3201        >>> s.analyze('key')
3202        <music21.key.Key of f# minor>
3203
3204        Example: music21 allows you to automatically run an
3205        analysis to get the key of a piece or excerpt not
3206        based on the key signature but instead on the
3207        frequency with which some notes are used as opposed
3208        to others (first described by Carol Krumhansl).  For
3209        instance, a piece with mostly Cs and Gs, some Fs,
3210        and Ds, but fewer G#s, C#s, etc. is more likely to
3211        be in the key of C major than in D-flat major
3212        (or A minor, etc.).  You can easily get this analysis
3213        from a stream by running:
3214
3215        >>> myStream = corpus.parse('luca/gloria')
3216        >>> analyzedKey = myStream.analyze('key')
3217        >>> analyzedKey
3218        <music21.key.Key of F major>
3219
3220        analyzedKey is a :class:`~music21.key.Key`
3221        object with a few extra parameters.
3222        correlationCoefficient shows how well this key fits the
3223        profile of a piece in that key:
3224
3225        >>> analyzedKey.correlationCoefficient
3226        0.86715...
3227
3228        `alternateInterpretations` is a list of the other
3229        possible interpretations sorted from most likely to least:
3230
3231        >>> analyzedKey.alternateInterpretations
3232        [<music21.key.Key of d minor>,
3233         <music21.key.Key of C major>,
3234         <music21.key.Key of g minor>,
3235         ...]
3236
3237        Each of these can be examined in turn to see its correlation coefficient:
3238
3239        >>> analyzedKey.alternateInterpretations[1].correlationCoefficient
3240        0.788528...
3241        >>> analyzedKey.alternateInterpretations[22].correlationCoefficient
3242        -0.86728...
3243        '''
3244
3245        from music21.analysis import discrete
3246        # pass this stream to the analysis procedure
3247        return discrete.analyzeStream(self, *args, **keywords)
3248
3249    # --------------------------------------------------------------------------
3250    # methods that act on individual elements without requiring
3251    # coreElementsChanged to fire
3252    def addGroupForElements(self, group, classFilter=None, *, recurse=False):
3253        '''
3254        Add the group to the groups attribute of all elements.
3255        if `classFilter` is set then only those elements whose objects
3256        belong to a certain class (or for Streams which are themselves of
3257        a certain class) are set.
3258
3259        >>> a = stream.Stream()
3260        >>> a.repeatAppend(note.Note('A-'), 30)
3261        >>> a.repeatAppend(note.Rest(), 30)
3262        >>> a.addGroupForElements('flute')
3263        >>> a[0].groups
3264        ['flute']
3265        >>> a.addGroupForElements('quietTime', note.Rest)
3266        >>> a[0].groups
3267        ['flute']
3268        >>> a[50].groups
3269        ['flute', 'quietTime']
3270        >>> a[1].groups.append('quietTime')  # set one note to it
3271        >>> a[1].step = 'B'
3272        >>> b = a.getElementsByGroup('quietTime')
3273        >>> len(b)
3274        31
3275        >>> c = b.getElementsByClass(note.Note)
3276        >>> len(c)
3277        1
3278        >>> c[0].name
3279        'B-'
3280
3281        If recurse is True then all sub-elements will get the group:
3282
3283        >>> s = converter.parse('tinyNotation: 4/4 c4 d e f g a b- b')
3284        >>> s.addGroupForElements('scaleNote', 'Note')
3285        >>> s.recurse().notes[3].groups
3286        []
3287        >>> s.addGroupForElements('scaleNote', 'Note', recurse=True)
3288        >>> s.recurse().notes[3].groups
3289        ['scaleNote']
3290
3291        No group will be added more than once:
3292
3293        >>> s.addGroupForElements('scaleNote', 'Note', recurse=True)
3294        >>> s.recurse().notes[3].groups
3295        ['scaleNote']
3296
3297        Added in v6.7.1 -- recurse
3298        '''
3299        sIterator = self.iter() if not recurse else self.recurse()
3300        if classFilter is not None:
3301            sIterator = sIterator.addFilter(filters.ClassFilter(classFilter))
3302        for el in sIterator:
3303            if group not in el.groups:
3304                el.groups.append(group)
3305
3306    # --------------------------------------------------------------------------
3307    # getElementsByX(self): anything that returns a collection of Elements
3308    #  formerly always returned a Stream; turning to Iterators in September 2015
3309
3310    def getElementsByClass(self, classFilterList) -> iterator.StreamIterator:
3311        '''
3312        Return a StreamIterator that will iterate over Elements that match one
3313        or more classes in the `classFilterList`. A single class
3314        can also used for the `classFilterList` parameter instead of a List.
3315
3316        >>> a = stream.Score()
3317        >>> a.repeatInsert(note.Rest(), list(range(10)))
3318        >>> for x in range(4):
3319        ...     n = note.Note('G#')
3320        ...     n.offset = x * 3
3321        ...     a.insert(n)
3322        >>> found = a.getElementsByClass(note.Note)
3323        >>> found
3324        <music21.stream.iterator.StreamIterator for Score:0x104f2f400 @:0>
3325
3326        >>> len(found)
3327        4
3328        >>> found[0].pitch.accidental.name
3329        'sharp'
3330
3331        >>> foundStream = found.stream()
3332        >>> isinstance(foundStream, stream.Score)
3333        True
3334
3335
3336        Notice that we do not find elements that are in
3337        sub-streams of the main Stream.  We'll add 15 more rests
3338        in a sub-stream and they won't be found:
3339
3340        >>> b = stream.Stream()
3341        >>> b.repeatInsert(note.Rest(), list(range(15)))
3342        >>> a.insert(b)
3343        >>> found = a.getElementsByClass(note.Rest)
3344        >>> len(found)
3345        10
3346
3347        To find them either (1) use `.flatten()` to get at everything:
3348
3349        >>> found = a.flatten().getElementsByClass(note.Rest)
3350        >>> len(found)
3351        25
3352
3353        Or, (2) recurse over the main stream and call .getElementsByClass
3354        on each one.  Notice that the first subStream is actually the outermost
3355        Stream:
3356
3357        >>> totalFound = 0
3358        >>> for subStream in a.recurse(streamsOnly=True, includeSelf=True):
3359        ...     found = subStream.getElementsByClass(note.Rest)
3360        ...     totalFound += len(found)
3361        >>> totalFound
3362        25
3363
3364        The class name of the Stream created is the same as the original:
3365
3366        >>> found = a.getElementsByClass(note.Note).stream()
3367        >>> found.__class__.__name__
3368        'Score'
3369
3370        ...except if `returnStreamSubClass` is False, which makes the method
3371        return a generic Stream:
3372
3373        >>> found = a.getElementsByClass(note.Rest).stream(returnStreamSubClass=False)
3374        >>> found.__class__.__name__
3375        'Stream'
3376
3377
3378        Make a list from a StreamIterator:
3379
3380        >>> foundList = list(a.recurse().getElementsByClass(note.Rest))
3381        >>> len(foundList)
3382        25
3383        '''
3384        return self.iter().getElementsByClass(classFilterList, returnClone=False)
3385
3386    def getElementsNotOfClass(self, classFilterList) -> iterator.StreamIterator:
3387        '''
3388        Return a list of all Elements that do not
3389        match the one or more classes in the `classFilterList`.
3390
3391        In lieu of a list, a single class can be used as the `classFilterList` parameter.
3392
3393        >>> a = stream.Stream()
3394        >>> a.repeatInsert(note.Rest(), list(range(10)))
3395        >>> for x in range(4):
3396        ...     n = note.Note('G#')
3397        ...     n.offset = x * 3
3398        ...     a.insert(n)
3399        >>> found = a.getElementsNotOfClass(note.Note)
3400        >>> len(found)
3401        10
3402
3403        >>> b = stream.Stream()
3404        >>> b.repeatInsert(note.Rest(), list(range(15)))
3405        >>> a.insert(b)
3406
3407        Here, it gets elements from within a stream
3408        this probably should not do this, as it is one layer lower
3409
3410        >>> found = a.flatten().getElementsNotOfClass(note.Rest)
3411        >>> len(found)
3412        4
3413        >>> found = a.flatten().getElementsNotOfClass(note.Note)
3414        >>> len(found)
3415        25
3416        '''
3417        return self.iter().getElementsNotOfClass(classFilterList, returnClone=False)
3418
3419    def getElementsByGroup(self, groupFilterList) -> iterator.StreamIterator:
3420        '''
3421        >>> n1 = note.Note('C')
3422        >>> n1.groups.append('trombone')
3423        >>> n2 = note.Note('D')
3424        >>> n2.groups.append('trombone')
3425        >>> n2.groups.append('tuba')
3426        >>> n3 = note.Note('E')
3427        >>> n3.groups.append('tuba')
3428        >>> s1 = stream.Stream()
3429        >>> s1.append(n1)
3430        >>> s1.append(n2)
3431        >>> s1.append(n3)
3432        >>> tboneSubStream = s1.getElementsByGroup('trombone')
3433        >>> for thisNote in tboneSubStream:
3434        ...     print(thisNote.name)
3435        C
3436        D
3437        >>> tubaSubStream = s1.getElementsByGroup('tuba')
3438        >>> for thisNote in tubaSubStream:
3439        ...     print(thisNote.name)
3440        D
3441        E
3442
3443        OMIT_FROM_DOCS
3444        # TODO: group comparisons are not YET case insensitive.
3445        '''
3446        return self.iter().getElementsByGroup(groupFilterList, returnClone=False)
3447
3448    def getElementById(self, elementId) -> Optional[base.Music21Object]:
3449        '''
3450        Returns the first encountered element for a given id. Return None
3451        if no match. Note: this uses the id attribute stored on elements,
3452        which may not be the same as id(e).
3453
3454        >>> a = stream.Stream()
3455        >>> ew = note.Note()
3456        >>> a.insert(0, ew)
3457        >>> a[0].id = 'green'
3458        >>> None == a.getElementById(3)
3459        True
3460        >>> a.getElementById('green').id
3461        'green'
3462        >>> a.getElementById('Green').id  # case does not matter
3463        'green'
3464
3465        Getting an element by getElementById changes its activeSite
3466
3467        >>> b = stream.Stream()
3468        >>> b.append(ew)
3469        >>> ew.activeSite is b
3470        True
3471        >>> ew2 = a.getElementById('green')
3472        >>> ew2 is ew
3473        True
3474        >>> ew2.activeSite is a
3475        True
3476        >>> ew.activeSite is a
3477        True
3478
3479        Changed in v7. -- remove classFilter.
3480        '''
3481        sIterator = self.iter().addFilter(filters.IdFilter(elementId))
3482        for e in sIterator:
3483            return e
3484        return None
3485
3486    def getElementsByOffset(
3487        self,
3488        offsetStart,
3489        offsetEnd=None,
3490        *,
3491        includeEndBoundary=True,
3492        mustFinishInSpan=False,
3493        mustBeginInSpan=True,
3494        includeElementsThatEndAtStart=True,
3495        classList=None
3496    ) -> iterator.StreamIterator:
3497        '''
3498        Returns a StreamIterator containing all Music21Objects that
3499        are found at a certain offset or within a certain
3500        offset time range (given the `offsetStart` and (optional) `offsetEnd` values).
3501
3502        There are several attributes that govern how this range is
3503        determined:
3504
3505
3506        If `mustFinishInSpan` is True then an event that begins
3507        between offsetStart and offsetEnd but which ends after offsetEnd
3508        will not be included.  The default is False.
3509
3510
3511        For instance, a half note at offset 2.0 will be found in
3512        getElementsByOffset(1.5, 2.5) or getElementsByOffset(1.5, 2.5,
3513        mustFinishInSpan=False) but not by getElementsByOffset(1.5, 2.5,
3514        mustFinishInSpan=True).
3515
3516        The `includeEndBoundary` option determines if an element
3517        begun just at the offsetEnd should be included.  For instance,
3518        the half note at offset 2.0 above would be found by
3519        getElementsByOffset(0, 2.0) or by getElementsByOffset(0, 2.0,
3520        includeEndBoundary=True) but not by getElementsByOffset(0, 2.0,
3521        includeEndBoundary=False).
3522
3523        Setting includeEndBoundary to False at the same time as
3524        mustFinishInSpan is set to True is probably NOT what you want to do
3525        unless you want to find things like clefs at the end of the region
3526        to display as courtesy clefs.
3527
3528        The `mustBeginInSpan` option determines whether notes or other
3529        objects that do not begin in the region but are still sounding
3530        at the beginning of the region are excluded.  The default is
3531        True -- that is, these notes will not be included.
3532        For instance the half note at offset 2.0 from above would not be found by
3533        getElementsByOffset(3.0, 3.5) or getElementsByOffset(3.0, 3.5,
3534        mustBeginInSpan=True) but it would be found by
3535        getElementsByOffset(3.0, 3.5, mustBeginInSpan=False)
3536
3537        Setting includeElementsThatEndAtStart to False is useful for zeroLength
3538        searches that set mustBeginInSpan == False to not catch notes that were
3539        playing before the search but that end just before the end of the search type.
3540        This setting is *ignored* for zero-length searches.
3541        See the code for allPlayingWhileSounding for a demonstration.
3542
3543        This chart, and the examples below, demonstrate the various
3544        features of getElementsByOffset.  It is one of the most complex
3545        methods of music21 but also one of the most powerful, so it
3546        is worth learning at least the basics.
3547
3548            .. image:: images/getElementsByOffset.*
3549                :width: 600
3550
3551
3552        >>> st1 = stream.Stream()
3553        >>> n0 = note.Note('C')
3554        >>> n0.duration.type = 'half'
3555        >>> n0.offset = 0
3556        >>> st1.insert(n0)
3557        >>> n2 = note.Note('D')
3558        >>> n2.duration.type = 'half'
3559        >>> n2.offset = 2
3560        >>> st1.insert(n2)
3561        >>> out1 = st1.getElementsByOffset(2)
3562        >>> len(out1)
3563        1
3564        >>> out1[0].step
3565        'D'
3566
3567        >>> out2 = st1.getElementsByOffset(1, 3)
3568        >>> len(out2)
3569        1
3570        >>> out2[0].step
3571        'D'
3572        >>> out3 = st1.getElementsByOffset(1, 3, mustFinishInSpan=True)
3573        >>> len(out3)
3574        0
3575        >>> out4 = st1.getElementsByOffset(1, 2)
3576        >>> len(out4)
3577        1
3578        >>> out4[0].step
3579        'D'
3580        >>> out5 = st1.getElementsByOffset(1, 2, includeEndBoundary=False)
3581        >>> len(out5)
3582        0
3583        >>> out6 = st1.getElementsByOffset(1, 2, includeEndBoundary=False, mustBeginInSpan=False)
3584        >>> len(out6)
3585        1
3586        >>> out6[0].step
3587        'C'
3588        >>> out7 = st1.getElementsByOffset(1, 3, mustBeginInSpan=False)
3589        >>> len(out7)
3590        2
3591        >>> [el.step for el in out7]
3592        ['C', 'D']
3593
3594
3595        Note, that elements that end at the start offset are included if mustBeginInSpan is False
3596
3597        >>> out8 = st1.getElementsByOffset(2, 4, mustBeginInSpan=False)
3598        >>> len(out8)
3599        2
3600        >>> [el.step for el in out8]
3601        ['C', 'D']
3602
3603        To change this behavior set includeElementsThatEndAtStart=False
3604
3605        >>> out9 = st1.getElementsByOffset(2, 4,
3606        ...                     mustBeginInSpan=False, includeElementsThatEndAtStart=False)
3607        >>> len(out9)
3608        1
3609        >>> [el.step for el in out9]
3610        ['D']
3611
3612
3613        Note how zeroLengthSearches implicitly set includeElementsThatEndAtStart=False.
3614        These two are the same:
3615
3616        >>> out1 = st1.getElementsByOffset(2, mustBeginInSpan=False)
3617        >>> out2 = st1.getElementsByOffset(2, 2, mustBeginInSpan=False)
3618        >>> len(out1) == len(out2) == 1
3619        True
3620        >>> out1[0] is out2[0] is n2
3621        True
3622
3623        But this is different:
3624
3625        >>> out3 = st1.getElementsByOffset(2, 2.1, mustBeginInSpan=False)
3626        >>> len(out3)
3627        2
3628        >>> out3[0] is n0
3629        True
3630
3631        Explicitly setting includeElementsThatEndAtStart=False does not get the
3632        first note:
3633
3634        >>> out4 = st1.getElementsByOffset(2, 2.1, mustBeginInSpan=False,
3635        ...                                includeElementsThatEndAtStart=False)
3636        >>> len(out4)
3637        1
3638        >>> out4[0] is n2
3639        True
3640
3641
3642
3643        Testing multiple zero-length elements with mustBeginInSpan:
3644
3645        >>> tc = clef.TrebleClef()
3646        >>> ts = meter.TimeSignature('4/4')
3647        >>> ks = key.KeySignature(2)
3648        >>> s = stream.Stream()
3649        >>> s.insert(0.0, tc)
3650        >>> s.insert(0.0, ts)
3651        >>> s.insert(0.0, ks)
3652        >>> len(s.getElementsByOffset(0.0, mustBeginInSpan=True))
3653        3
3654        >>> len(s.getElementsByOffset(0.0, mustBeginInSpan=False))
3655        3
3656
3657        OMIT_FROM_DOCS
3658
3659        >>> a = stream.Stream()
3660        >>> n = note.Note('G')
3661        >>> n.quarterLength = 0.5
3662        >>> a.repeatInsert(n, list(range(8)))
3663        >>> b = stream.Stream()
3664        >>> b.repeatInsert(a, [0, 3, 6])
3665        >>> c = b.getElementsByOffset(2, 6.9)
3666        >>> len(c)
3667        2
3668        >>> c = b.flatten().getElementsByOffset(2, 6.9)
3669        >>> len(c)
3670        10
3671
3672
3673        Same test as above, but with floats
3674
3675        >>> out1 = st1.getElementsByOffset(2.0)
3676        >>> len(out1)
3677        1
3678        >>> out1[0].step
3679        'D'
3680        >>> out2 = st1.getElementsByOffset(1.0, 3.0)
3681        >>> len(out2)
3682        1
3683        >>> out2[0].step
3684        'D'
3685        >>> out3 = st1.getElementsByOffset(1.0, 3.0, mustFinishInSpan=True)
3686        >>> len(out3)
3687        0
3688        >>> out3b = st1.getElementsByOffset(0.0, 3.001, mustFinishInSpan=True)
3689        >>> len(out3b)
3690        1
3691        >>> out3b[0].step
3692        'C'
3693        >>> out3b = st1.getElementsByOffset(1.0, 3.001,
3694        ...                                 mustFinishInSpan=True, mustBeginInSpan=False)
3695        >>> len(out3b)
3696        1
3697        >>> out3b[0].step
3698        'C'
3699
3700
3701        >>> out4 = st1.getElementsByOffset(1.0, 2.0)
3702        >>> len(out4)
3703        1
3704        >>> out4[0].step
3705        'D'
3706        >>> out5 = st1.getElementsByOffset(1.0, 2.0, includeEndBoundary=False)
3707        >>> len(out5)
3708        0
3709        >>> out6 = st1.getElementsByOffset(1.0, 2.0,
3710        ...                                includeEndBoundary=False, mustBeginInSpan=False)
3711        >>> len(out6)
3712        1
3713        >>> out6[0].step
3714        'C'
3715        >>> out7 = st1.getElementsByOffset(1.0, 3.0, mustBeginInSpan=False)
3716        >>> len(out7)
3717        2
3718        >>> [el.step for el in out7]
3719        ['C', 'D']
3720
3721        Changed in v5.5: all arguments changing behavior are keyword only.
3722        '''
3723        sIterator = self.iter().getElementsByOffset(
3724            offsetStart=offsetStart,
3725            offsetEnd=offsetEnd,
3726            includeEndBoundary=includeEndBoundary,
3727            mustFinishInSpan=mustFinishInSpan,
3728            mustBeginInSpan=mustBeginInSpan,
3729            includeElementsThatEndAtStart=includeElementsThatEndAtStart)
3730        if classList is not None:
3731            sIterator = sIterator.getElementsByClass(classList)
3732        return sIterator
3733
3734    def getElementAtOrBefore(self, offset, classList=None) -> Optional[base.Music21Object]:
3735        # noinspection PyShadowingNames
3736        '''
3737        Given an offset, find the element at this offset,
3738        or with the offset less than and nearest to.
3739
3740        Return one element or None if no elements are at or preceded by this
3741        offset.
3742
3743        If the `classList` parameter is used, it should be a
3744        list of class names or strings, and only objects that
3745        are instances of
3746        these classes or subclasses of these classes will be returned.
3747
3748        >>> stream1 = stream.Stream()
3749        >>> x = note.Note('D4')
3750        >>> x.id = 'x'
3751        >>> y = note.Note('E4')
3752        >>> y.id = 'y'
3753        >>> z = note.Rest()
3754        >>> z.id = 'z'
3755
3756        >>> stream1.insert(20, x)
3757        >>> stream1.insert(10, y)
3758        >>> stream1.insert( 0, z)
3759
3760        >>> b = stream1.getElementAtOrBefore(21)
3761        >>> b.offset, b.id
3762        (20.0, 'x')
3763
3764        >>> b = stream1.getElementAtOrBefore(19)
3765        >>> b.offset, b.id
3766        (10.0, 'y')
3767
3768        >>> b = stream1.getElementAtOrBefore(0)
3769        >>> b.offset, b.id
3770        (0.0, 'z')
3771        >>> b = stream1.getElementAtOrBefore(0.1)
3772        >>> b.offset, b.id
3773        (0.0, 'z')
3774
3775
3776        You can give a list of acceptable classes to return, and non-matching
3777        elements will be ignored
3778
3779        >>> c = stream1.getElementAtOrBefore(100, [clef.TrebleClef, note.Rest])
3780        >>> c.offset, c.id
3781        (0.0, 'z')
3782
3783        Getting an object via getElementAtOrBefore sets the activeSite
3784        for that object to the Stream, and thus sets its offset
3785
3786        >>> stream2 = stream.Stream()
3787        >>> stream2.insert(100.5, x)
3788        >>> x.offset
3789        100.5
3790        >>> d = stream1.getElementAtOrBefore(20)
3791        >>> d is x
3792        True
3793        >>> x.activeSite is stream1
3794        True
3795        >>> x.offset
3796        20.0
3797
3798
3799        If no element is before the offset, returns None
3800
3801        >>> s = stream.Stream()
3802        >>> s.insert(10, note.Note('E'))
3803        >>> print(s.getElementAtOrBefore(9))
3804        None
3805
3806        The sort order of returned items is the reverse
3807        of the normal sort order, so that, for instance,
3808        if there's a clef and a note at offset 20,
3809        getting the object before offset 21 will give
3810        you the note, and not the clef, since clefs
3811        sort before notes:
3812
3813        >>> clef1 = clef.BassClef()
3814        >>> stream1.insert(20, clef1)
3815        >>> e = stream1.getElementAtOrBefore(21)
3816        >>> e
3817        <music21.note.Note D>
3818        '''
3819        # NOTE: this is a performance critical method
3820
3821        # TODO: switch to trees
3822        candidates = []
3823        offset = opFrac(offset)
3824        nearestTrailSpan = offset  # start with max time
3825
3826        sIterator = self.iter()
3827        if classList:
3828            sIterator = sIterator.getElementsByClass(classList)
3829
3830        # need both _elements and _endElements
3831        for e in sIterator:
3832            span = opFrac(offset - self.elementOffset(e))
3833            # environLocal.printDebug(['e span check', span, 'offset', offset,
3834            #   'e.offset', e.offset, 'self.elementOffset(e)', self.elementOffset(e), 'e', e])
3835            if span < 0:
3836                continue
3837            elif span == 0:
3838                candidates.append((span, e))
3839                nearestTrailSpan = span
3840            else:
3841                # do this comparison because may be out of order
3842                if span <= nearestTrailSpan:
3843                    candidates.append((span, e))
3844                    nearestTrailSpan = span
3845        # environLocal.printDebug(['getElementAtOrBefore(), e candidates', candidates])
3846        if candidates:
3847            candidates.sort(key=lambda x: (-1 * x[0], x[1].sortTuple()))
3848            # TODO: this sort has side effects -- see ICMC2011 -- sorting clef vs. note, etc.
3849            self.coreSelfActiveSite(candidates[-1][1])
3850            return candidates[-1][1]
3851        else:
3852            return None
3853
3854    def getElementBeforeOffset(self, offset, classList=None) -> Optional[base.Music21Object]:
3855        '''
3856        Get element before (and not at) a provided offset.
3857
3858        If the `classList` parameter is used, it should be a
3859        list of class names or strings, and only objects that
3860        are instances of
3861        these classes or subclasses of these classes will be returned.
3862
3863        >>> stream1 = stream.Stream()
3864        >>> x = note.Note('D4')
3865        >>> x.id = 'x'
3866        >>> y = note.Note('E4')
3867        >>> y.id = 'y'
3868        >>> z = note.Rest()
3869        >>> z.id = 'z'
3870        >>> stream1.insert(20, x)
3871        >>> stream1.insert(10, y)
3872        >>> stream1.insert( 0, z)
3873
3874        >>> b = stream1.getElementBeforeOffset(21)
3875        >>> b.offset, b.id
3876        (20.0, 'x')
3877        >>> b = stream1.getElementBeforeOffset(20)
3878        >>> b.offset, b.id
3879        (10.0, 'y')
3880
3881        >>> b = stream1.getElementBeforeOffset(10)
3882        >>> b.offset, b.id
3883        (0.0, 'z')
3884
3885        >>> b = stream1.getElementBeforeOffset(0)
3886        >>> b is None
3887        True
3888        >>> b = stream1.getElementBeforeOffset(0.1)
3889        >>> b.offset, b.id
3890        (0.0, 'z')
3891
3892        >>> w = note.Note('F4')
3893        >>> w.id = 'w'
3894        >>> stream1.insert( 0, w)
3895
3896        This should get w because it was inserted last.
3897
3898        >>> b = stream1.getElementBeforeOffset(0.1)
3899        >>> b.offset, b.id
3900        (0.0, 'w')
3901
3902        But if we give it a lower priority than z then z will appear first.
3903
3904        >>> w.priority = z.priority - 1
3905        >>> b = stream1.getElementBeforeOffset(0.1)
3906        >>> b.offset, b.id
3907        (0.0, 'z')
3908        '''
3909        # NOTE: this is a performance critical method
3910        candidates = []
3911        offset = opFrac(offset)
3912        nearestTrailSpan = offset  # start with max time
3913
3914        sIterator = self.iter()
3915        if classList:
3916            sIterator = sIterator.getElementsByClass(classList)
3917
3918        for e in sIterator:
3919            span = opFrac(offset - self.elementOffset(e))
3920            # environLocal.printDebug(['e span check', span, 'offset', offset,
3921            #     'e.offset', e.offset, 'self.elementOffset(e)', self.elementOffset(e), 'e', e])
3922            # by forcing <= here, we are sure to get offsets not at zero
3923            if span <= 0:  # the e is after this offset
3924                continue
3925            else:  # do this comparison because may be out of order
3926                if span <= nearestTrailSpan:
3927                    candidates.append((span, e))
3928                    nearestTrailSpan = span
3929        # environLocal.printDebug(['getElementBeforeOffset(), e candidates', candidates])
3930        if candidates:
3931            candidates.sort(key=lambda x: (-1 * x[0], x[1].sortTuple()))
3932            self.coreSelfActiveSite(candidates[-1][1])
3933            return candidates[-1][1]
3934        else:
3935            return None
3936
3937    # def getElementAfterOffset(self, offset, classList=None):
3938    #    '''Get element after a provided offset
3939    #
3940    #    TODO: write this
3941    #    '''
3942    #    raise Exception('not yet implemented')
3943    #
3944    #
3945    # def getElementBeforeElement(self, element, classList=None):
3946    #    '''given an element, get the element before
3947    #
3948    #    TODO: write this
3949    #    '''
3950    #    raise Exception('not yet implemented')
3951
3952    def getElementAfterElement(self, element, classList=None):
3953        '''
3954        Given an element, get the next element.  If classList is specified,
3955        check to make sure that the element is an instance of the class list
3956
3957        >>> st1 = stream.Stream()
3958        >>> n1 = note.Note('C4')
3959        >>> n2 = note.Note('D4')
3960        >>> r3 = note.Rest()
3961        >>> st1.append([n1, n2, r3])
3962        >>> t2 = st1.getElementAfterElement(n1)
3963        >>> t2 is n2
3964        True
3965        >>> t3 = st1.getElementAfterElement(t2)
3966        >>> t3 is r3
3967        True
3968        >>> t4 = st1.getElementAfterElement(t3)
3969        >>> t4
3970
3971        >>> t5 = st1.getElementAfterElement(n1, [note.Rest])
3972        >>> t5
3973        <music21.note.Rest quarter>
3974        >>> t5 is r3
3975        True
3976        >>> t6 = st1.getElementAfterElement(n1, [note.Rest, note.Note])
3977        >>> t6 is n2
3978        True
3979
3980        >>> t7 = st1.getElementAfterElement(r3)
3981        >>> t7 is None
3982        True
3983
3984
3985        If the element is not in the stream, it will raise a StreamException:
3986
3987        >>> st1.getElementAfterElement(note.Note('C#'))
3988        Traceback (most recent call last):
3989        music21.exceptions21.StreamException:
3990            cannot find object (<music21.note.Note C#>) in Stream
3991
3992        '''
3993        if classList is not None:
3994            classSet = set(classList)
3995        else:
3996            classSet = None
3997
3998        # index() ultimately does an autoSort check, so no check here or
3999        # sorting is necessary
4000        # also, this will raise a StreamException if element is not in the Stream
4001        elPos = self.index(element)
4002
4003        # store once as a property call concatenates
4004        elements = self.elements
4005        if classList is None:
4006            if elPos == len(elements) - 1:
4007                return None
4008            else:
4009                e = elements[elPos + 1]
4010                self.coreSelfActiveSite(e)
4011                return e
4012        else:
4013            for i in range(elPos + 1, len(elements)):
4014                if classList is None or classSet.intersection(elements[i].classSet):
4015                    e = elements[i]
4016                    self.coreSelfActiveSite(e)
4017                    return e
4018
4019    # ----------------------------------------------------
4020    # end .getElement filters
4021
4022    # -------------------------------------------------------------------------
4023    # routines for obtaining specific types of elements from a Stream
4024    # _getNotes and _getPitches are found with the interval routines
4025
4026    def measures(self,
4027                 numberStart,
4028                 numberEnd,
4029                 collect=('Clef', 'TimeSignature', 'Instrument', 'KeySignature'),
4030                 gatherSpanners=GatherSpanners.ALL,
4031                 indicesNotNumbers=False):
4032        '''
4033        Get a region of Measures based on a start and end Measure number
4034        where the boundary numbers are both included.
4035
4036        That is, a request for measures 4 through 10 will return 7 Measures, numbers 4 through 10.
4037
4038        Additionally, any number of associated classes can be gathered from the context
4039        and put into the measure.  By default we collect the Clef, TimeSignature, KeySignature,
4040        and Instrument so that there is enough context to perform.  (See getContextByClass()
4041        and .previous() for definitions of the context)
4042
4043        While all elements in the source are the original elements in the extracted region,
4044        new Measure objects are created and returned.
4045
4046        >>> bachIn = corpus.parse('bach/bwv66.6')
4047        >>> bachExcerpt = bachIn.parts[0].measures(1, 3)
4048        >>> len(bachExcerpt.getElementsByClass('Measure'))
4049        3
4050
4051        Because bwv66.6 has a pickup measure, and we requested to start at measure 1,
4052        this is NOT true:
4053
4054        >>> firstExcerptMeasure = bachExcerpt.getElementsByClass('Measure').first()
4055        >>> firstBachMeasure = bachIn.parts[0].getElementsByClass('Measure').first()
4056        >>> firstExcerptMeasure is firstBachMeasure
4057        False
4058        >>> firstBachMeasure.number
4059        0
4060        >>> firstExcerptMeasure.number
4061        1
4062
4063
4064        To get all measures from the beginning, go ahead and always request measure 0 to x,
4065        there will be no error if there is not a pickup measure.
4066
4067        >>> bachExcerpt = bachIn.parts[0].measures(0, 3)
4068        >>> excerptNote = bachExcerpt.getElementsByClass('Measure').first().notes.first()
4069        >>> originalNote = bachIn.parts[0].recurse().notes[0]
4070        >>> excerptNote is originalNote
4071        True
4072
4073        if `indicesNotNumbers` is True, then it ignores defined measureNumbers and
4074        uses 0-indexed measure objects and half-open range.  For instance, if you have a piece
4075        that goes "m1, m2, m3, m4, ..." (like a standard piece without pickups, then
4076        `.measures(1, 3, indicesNotNumbers=True)` would return measures 2 and 3, because
4077        it is interpreted as the slice from object with index 1, which is measure 2 (m1 has
4078        index 0) up to but NOT including the object with index 3, which is measure 4.
4079        IndicesNotNumbers is like a Python-slice.
4080
4081        >>> bachExcerpt2 = bachIn.parts[0].measures(0, 2, indicesNotNumbers=True)
4082        >>> for m in bachExcerpt2.getElementsByClass('Measure'):
4083        ...     print(m)
4084        ...     print(m.number)
4085        <music21.stream.Measure 0 offset=0.0>
4086        0
4087        <music21.stream.Measure 1 offset=1.0>
4088        1
4089
4090
4091        If `numberEnd=None` then it is interpreted as the last measure of the stream:
4092
4093        >>> bachExcerpt3 = bachIn.parts[0].measures(7, None)
4094        >>> for m in bachExcerpt3.getElementsByClass('Measure'):
4095        ...     print(m)
4096        <music21.stream.Measure 7 offset=0.0>
4097        <music21.stream.Measure 8 offset=4.0>
4098        <music21.stream.Measure 9 offset=8.0>
4099
4100        Note that the offsets in the new stream are shifted so that the first measure
4101        in the excerpt begins at 0.0
4102
4103        The measure elements are the same objects as the original:
4104
4105        >>> lastExcerptMeasure = bachExcerpt3.getElementsByClass('Measure').last()
4106        >>> lastOriginalMeasure = bachIn.parts[0].getElementsByClass('Measure').last()
4107        >>> lastExcerptMeasure is lastOriginalMeasure
4108        True
4109
4110        At the beginning of the Stream returned, before the measures will be some additional
4111        objects so that the context is properly preserved:
4112
4113        >>> for thing in bachExcerpt3:
4114        ...     print(thing)
4115        P1: Soprano: Instrument 1
4116        <music21.clef.TrebleClef>
4117        f# minor
4118        <music21.meter.TimeSignature 4/4>
4119        <music21.stream.Measure 7 offset=0.0>
4120        <music21.stream.Measure 8 offset=4.0>
4121        <music21.stream.Measure 9 offset=8.0>
4122
4123        Collecting gets the most recent element in the context of the stream:
4124
4125        >>> bachIn.parts[0].insert(10, key.Key('D-'))
4126        >>> bachExcerpt4 = bachIn.parts[0].measures(7, None)
4127        >>> for thing in bachExcerpt4:
4128        ...     print(thing)
4129        P1: Soprano: Instrument 1
4130        <music21.clef.TrebleClef>
4131        D- major
4132        ...
4133
4134
4135        What is collected is determined by the "collect" iterable.  To collect nothing
4136        send an empty list:
4137
4138        >>> bachExcerpt5 = bachIn.parts[0].measures(8, None, collect=[])
4139        >>> for thing in bachExcerpt5:
4140        ...     print(thing)
4141        <music21.stream.Measure 8 offset=0.0>
4142        <music21.stream.Measure 9 offset=4.0>
4143
4144
4145        If a stream has measure suffixes, then Streams having that suffix or no suffix
4146        are returned.
4147
4148        >>> p = stream.Part()
4149        >>> mSuffix3 = stream.Measure(number=3)
4150        >>> mSuffix4 = stream.Measure(number=4)
4151        >>> mSuffix4a = stream.Measure(number=4)
4152        >>> mSuffix4a.numberSuffix = 'a'
4153        >>> mSuffix4b = stream.Measure(number=4)
4154        >>> mSuffix4b.numberSuffix = 'b'
4155        >>> mSuffix5 = stream.Measure(number=5)
4156        >>> mSuffix5a = stream.Measure(number=5)
4157        >>> mSuffix5a.numberSuffix = 'a'
4158        >>> mSuffix6 = stream.Measure(number=6)
4159        >>> p.append([mSuffix3, mSuffix4, mSuffix4a, mSuffix4b, mSuffix5, mSuffix5a, mSuffix6])
4160        >>> suffixExcerpt = p.measures('4b', 6)
4161        >>> suffixExcerpt.show('text')
4162        {0.0} <music21.stream.Measure 4 offset=0.0>
4163        <BLANKLINE>
4164        {0.0} <music21.stream.Measure 4b offset=0.0>
4165        <BLANKLINE>
4166        {0.0} <music21.stream.Measure 5 offset=0.0>
4167        <BLANKLINE>
4168        {0.0} <music21.stream.Measure 5a offset=0.0>
4169        <BLANKLINE>
4170        {0.0} <music21.stream.Measure 6 offset=0.0>
4171        <BLANKLINE>
4172        >>> suffixExcerpt2 = p.measures(3, '4a')
4173        >>> suffixExcerpt2.show('text')
4174        {0.0} <music21.stream.Measure 3 offset=0.0>
4175        <BLANKLINE>
4176        {0.0} <music21.stream.Measure 4 offset=0.0>
4177        <BLANKLINE>
4178        {0.0} <music21.stream.Measure 4a offset=0.0>
4179        <BLANKLINE>
4180
4181
4182        Changed in v.7 -- If `gatherSpanners` is True or GatherSpanners.ALL (default),
4183        then just the spanners pertaining to the requested measure region
4184        are provided, rather than the entire bundle from the source.
4185
4186        >>> from music21.common.enums import GatherSpanners
4187        >>> beachIn = corpus.parse('beach')
4188        >>> beachExcerpt = beachIn.measures(3, 4, gatherSpanners=GatherSpanners.ALL)
4189        >>> len(beachExcerpt.spannerBundle)
4190        8
4191        >>> len(beachIn.spannerBundle)
4192        93
4193
4194        Changed in v.7 -- does not create measures automatically.
4195
4196        OMIT_FROM_DOCS
4197
4198        Ensure that layout.StaffGroup objects are present:
4199
4200        >>> for sp in beachExcerpt.spannerBundle.getByClass('StaffGroup'):
4201        ...    print(sp)
4202        <music21.layout.StaffGroup <music21.stream.PartStaff P5-Staff1><... P5-Staff2>>
4203        <music21.layout.StaffGroup <music21.stream.Part Soprano I><...Alto II>>
4204        '''
4205
4206        def hasMeasureNumberInformation(measureIterator):
4207            '''
4208            Many people create streams where every number is zero.
4209            This will check for that as quickly as possible.
4210            '''
4211            uniqueMeasureNumbers = set()
4212            for m in measureIterator:
4213                try:
4214                    mNumber = int(m.number)
4215                except ValueError:  # pragma: no cover
4216                    # should never happen.
4217                    raise StreamException(f'found problematic measure for numbering: {m}')
4218                uniqueMeasureNumbers.add(mNumber)
4219                if len(uniqueMeasureNumbers) > 1:
4220                    break
4221            if len(uniqueMeasureNumbers) > 1:
4222                return True
4223            elif len(uniqueMeasureNumbers.union({0})) > 1:
4224                return True  # is there a number other than zero? (or any number at all
4225            else:
4226                return False
4227
4228        returnObj = self.cloneEmpty(derivationMethod='measures')
4229        srcObj = self
4230
4231        mStreamIter = self.getElementsByClass('Measure')
4232
4233        # FIND THE CORRECT ORIGINAL MEASURE OBJECTS
4234        # for indicesNotNumbers, this is simple...
4235        if indicesNotNumbers:
4236            matches = mStreamIter[numberStart:numberEnd]
4237        else:
4238            hasUniqueMeasureNumbers = hasMeasureNumberInformation(mStreamIter)
4239
4240            # unused...
4241            # originalNumberStart = numberStart
4242            # originalNumberEnd = numberEnd
4243            startSuffix = None
4244            endSuffix = None
4245            if isinstance(numberStart, str):
4246                numberStart, startSuffixTemp = common.getNumFromStr(numberStart)
4247                if startSuffixTemp:
4248                    startSuffix = startSuffixTemp
4249                numberStart = int(numberStart)
4250
4251            if isinstance(numberEnd, str):
4252                numberEnd, endSuffixTemp = common.getNumFromStr(numberEnd)
4253                if endSuffixTemp:
4254                    endSuffix = endSuffixTemp
4255                numberEnd = int(numberEnd)
4256
4257            if numberEnd is not None:
4258                matchingMeasureNumbers = set(range(numberStart, numberEnd + 1))
4259
4260                if hasUniqueMeasureNumbers:
4261                    matches = [m for m in mStreamIter if m.number in matchingMeasureNumbers]
4262                else:
4263                    matches = [m for i, m in enumerate(mStreamIter)
4264                                    if i + 1 in matchingMeasureNumbers]
4265            else:
4266                if hasUniqueMeasureNumbers:
4267                    matches = [m for m in mStreamIter if m.number >= numberStart]
4268                else:
4269                    matches = [m for i, m in enumerate(mStreamIter)
4270                                        if i + 1 >= numberStart]
4271
4272            if startSuffix is not None:
4273                oldMatches = matches
4274                matches = []
4275                for m in oldMatches:
4276                    if m.number != numberStart:
4277                        matches.append(m)
4278                    elif not m.numberSuffix:
4279                        matches.append(m)
4280                    elif m.numberSuffix >= startSuffix:
4281                        matches.append(m)
4282
4283            if endSuffix is not None:
4284                oldMatches = matches
4285                matches = []
4286                for m in oldMatches:
4287                    if m.number != numberEnd:
4288                        matches.append(m)
4289                    elif not m.numberSuffix:
4290                        matches.append(m)
4291                    elif m.numberSuffix <= endSuffix:
4292                        matches.append(m)
4293
4294        if not matches:
4295            startMeasure = None
4296            startOffset = 0  # does not matter; could be any number...
4297        else:
4298            startMeasure = matches[0]
4299            startOffset = startMeasure.getOffsetBySite(srcObj)
4300
4301        for className in collect:
4302            # first, see if it is in this Measure
4303            if (startMeasure is None
4304                    or startMeasure.recurse().getElementsByClass(className).getElementsByOffset(0)):
4305                continue
4306
4307            # placing missing objects in outer container, not Measure
4308            found = startMeasure.getContextByClass(className)
4309            if found is not None:
4310                if startMeasure is not None:
4311                    found.priority = startMeasure.priority - 1
4312                    # TODO: This should not change global priority on found, but
4313                    #   instead priority, like offset, should be a per-site attribute
4314                returnObj.coreInsert(0, found)
4315
4316        for m in matches:
4317            mOffset = m.getOffsetBySite(srcObj) - startOffset
4318            returnObj.coreInsert(mOffset, m)
4319
4320        # used coreInsert
4321        returnObj.coreElementsChanged()
4322
4323        if gatherSpanners:  # True, GatherSpanners.ALL, or GatherSpanners.COMPLETE_ONLY
4324            requireAllPresent = (gatherSpanners is GatherSpanners.COMPLETE_ONLY)
4325            returnObj.coreGatherMissingSpanners(
4326                requireAllPresent=requireAllPresent,
4327                constrainingSpannerBundle=self.spannerBundle,
4328            )
4329
4330        # environLocal.printDebug(['len(returnObj.flatten())', len(returnObj.flatten())])
4331        return returnObj
4332
4333    def measure(self,
4334                measureNumber,
4335                collect=('Clef', 'TimeSignature', 'Instrument', 'KeySignature'),
4336                indicesNotNumbers=False) -> Optional['music21.stream.Measure']:
4337        '''
4338        Given a measure number, return a single
4339        :class:`~music21.stream.Measure` object if the Measure number exists, otherwise return None.
4340
4341        This method is distinguished from :meth:`~music21.stream.Stream.measures`
4342        in that this method returns a single Measure object, not a Stream containing
4343        one or more Measure objects.
4344
4345
4346        >>> a = corpus.parse('bach/bwv324.xml')
4347        >>> a.parts[0].measure(3)
4348        <music21.stream.Measure 3 offset=8.0>
4349
4350        See :meth:`~music21.stream.Stream.measures` for an explanation of collect and
4351        indicesNotNumbers
4352
4353        To get the last measure of a piece, use -1 as a measureNumber -- this will turn
4354        on indicesNotNumbers if it is off:
4355
4356        >>> a.parts[0].measure(-1)
4357        <music21.stream.Measure 9 offset=38.0>
4358
4359        Getting a non-existent measure will return None:
4360
4361        >>> print(a.parts[0].measure(99))
4362        None
4363
4364        OMIT_FROM_DOCS
4365
4366        >>> sf = a.parts[0].flatten()
4367        >>> sf.measure(2) is None
4368        True
4369        '''
4370        if not isinstance(measureNumber, str) and measureNumber < 0:
4371            indicesNotNumbers = True
4372
4373        startMeasureNumber = measureNumber
4374        endMeasureNumber = measureNumber
4375        if indicesNotNumbers:
4376            endMeasureNumber += 1
4377            if startMeasureNumber == -1:
4378                endMeasureNumber = None
4379
4380        # we must be able to obtain a measure from this (not a flat)
4381        # representation (e.g., this is a Stream or Part, not a Score)
4382        if self.getElementsByClass('Measure'):
4383            # environLocal.printDebug(['got measures from getElementsByClass'])
4384            s = self.measures(startMeasureNumber,
4385                              endMeasureNumber,
4386                              collect=collect,
4387                              indicesNotNumbers=indicesNotNumbers)
4388            measureIter = s.getElementsByClass('Measure')
4389            m: Optional[Measure]
4390            # noinspection PyTypeChecker
4391            m = measureIter.first()
4392            if m is None:  # not 'if not m' because m might be an empty measure.
4393                return None
4394            else:
4395                self.coreSelfActiveSite(m)
4396                # ^^ this sets its offset to something meaningful...
4397                return m
4398        else:
4399            # environLocal.printDebug(['got not measures from getElementsByClass'])
4400            return None
4401
4402    def template(self,
4403                 *,
4404                 fillWithRests=True,
4405                 removeClasses=None,
4406                 retainVoices=True,
4407                 ):
4408        '''
4409        Return a new Stream based on this one, but without the notes and other elements
4410        but keeping instruments, clefs, keys, etc.
4411
4412        Classes to remove are specified in `removeClasses`.
4413
4414        If this Stream contains measures, return a new Stream
4415        with new Measures populated with the same characteristics of those found in this Stream.
4416
4417        >>> b = corpus.parse('bwv66.6')
4418        >>> sopr = b.parts[0]
4419        >>> soprEmpty = sopr.template()
4420        >>> soprEmpty.show('text')
4421        {0.0} <music21.instrument.Instrument 'P1: Soprano: Instrument 1'>
4422        {0.0} <music21.stream.Measure 0 offset=0.0>
4423            {0.0} <music21.clef.TrebleClef>
4424            {0.0} <music21.key.Key of f# minor>
4425            {0.0} <music21.meter.TimeSignature 4/4>
4426            {0.0} <music21.note.Rest quarter>
4427        {1.0} <music21.stream.Measure 1 offset=1.0>
4428            {0.0} <music21.note.Rest whole>
4429        {5.0} <music21.stream.Measure 2 offset=5.0>
4430            {0.0} <music21.note.Rest whole>
4431        {9.0} <music21.stream.Measure 3 offset=9.0>
4432            {0.0} <music21.layout.SystemLayout>
4433            {0.0} <music21.note.Rest whole>
4434        {13.0} <music21.stream.Measure 4 offset=13.0>
4435        ...
4436
4437
4438        Really make empty with `fillWithRests=False`
4439
4440        >>> alto = b.parts[1]
4441        >>> altoEmpty = alto.template(fillWithRests=False)
4442        >>> altoEmpty.show('text')
4443        {0.0} <music21.instrument.Instrument 'P2: Alto: Instrument 2'>
4444        {0.0} <music21.stream.Measure 0 offset=0.0>
4445            {0.0} <music21.clef.TrebleClef>
4446            {0.0} <music21.key.Key of f# minor>
4447            {0.0} <music21.meter.TimeSignature 4/4>
4448        {1.0} <music21.stream.Measure 1 offset=1.0>
4449        <BLANKLINE>
4450        {5.0} <music21.stream.Measure 2 offset=5.0>
4451        <BLANKLINE>
4452        {9.0} <music21.stream.Measure 3 offset=9.0>
4453            {0.0} <music21.layout.SystemLayout>
4454        ...
4455
4456
4457        `removeClasses` can be a list or set of classes to remove.  By default it is
4458        ['GeneralNote', 'Dynamic', 'Expression']
4459
4460        >>> tenor = b.parts[2]
4461        >>> tenorNoClefsSignatures = tenor.template(fillWithRests=False,
4462        ...       removeClasses=['Clef', 'KeySignature', 'TimeSignature', 'Instrument'])
4463        >>> tenorNoClefsSignatures.show('text')
4464        {0.0} <music21.stream.Measure 0 offset=0.0>
4465            {0.0} <music21.note.Note A>
4466            {0.5} <music21.note.Note B>
4467        {1.0} <music21.stream.Measure 1 offset=1.0>
4468            {0.0} <music21.note.Note C#>
4469            {1.0} <music21.note.Note B>
4470            {2.0} <music21.note.Note A>
4471            {3.0} <music21.note.Note B>
4472        {5.0} <music21.stream.Measure 2 offset=5.0>
4473        ...
4474
4475        Setting removeClasses to True removes everything that is not a Stream:
4476
4477        >>> bass = b.parts[3]
4478        >>> bassEmpty = bass.template(fillWithRests=False, removeClasses=True)
4479        >>> bassEmpty.show('text')
4480        {0.0} <music21.stream.Measure 0 offset=0.0>
4481        <BLANKLINE>
4482        {1.0} <music21.stream.Measure 1 offset=1.0>
4483        <BLANKLINE>
4484        {5.0} <music21.stream.Measure 2 offset=5.0>
4485        <BLANKLINE>
4486        {9.0} <music21.stream.Measure 3 offset=9.0>
4487        <BLANKLINE>
4488        {13.0} <music21.stream.Measure 4 offset=13.0>
4489        <BLANKLINE>
4490        ...
4491
4492        On the whole score:
4493
4494        >>> b.template().show('text')
4495        {0.0} <music21.metadata.Metadata object at 0x106151940>
4496        {0.0} <music21.stream.Part Soprano>
4497            {0.0} <music21.instrument.Instrument 'P1: Soprano: Instrument 1'>
4498            {0.0} <music21.stream.Measure 0 offset=0.0>
4499                {0.0} <music21.clef.TrebleClef>
4500                {0.0} <music21.key.Key of f# minor>
4501                {0.0} <music21.meter.TimeSignature 4/4>
4502                {0.0} <music21.note.Rest quarter>
4503            {1.0} <music21.stream.Measure 1 offset=1.0>
4504                {0.0} <music21.note.Rest whole>
4505                ...
4506            {33.0} <music21.stream.Measure 9 offset=33.0>
4507                {0.0} <music21.note.Rest dotted-half>
4508                {3.0} <music21.bar.Barline type=final>
4509        {0.0} <music21.stream.Part Alto>
4510            {0.0} <music21.instrument.Instrument 'P2: Alto: Instrument 2'>
4511            {0.0} <music21.stream.Measure 0 offset=0.0>
4512                {0.0} <music21.clef.TrebleClef>
4513                {0.0} <music21.key.Key of f# minor>
4514                {0.0} <music21.meter.TimeSignature 4/4>
4515                {0.0} <music21.note.Rest quarter>
4516            {1.0} <music21.stream.Measure 1 offset=1.0>
4517                {0.0} <music21.note.Rest whole>
4518            ...
4519            {33.0} <music21.stream.Measure 9 offset=33.0>
4520                {0.0} <music21.note.Rest dotted-half>
4521                {3.0} <music21.bar.Barline type=final>
4522        {0.0} <music21.layout.StaffGroup ...>
4523
4524
4525        If `retainVoices` is False (default True) then Voice streams are treated
4526        differently from all other Streams and are removed.  All elements in the
4527        voice are removed even if they do not match the `classList`:
4528
4529        >>> p = stream.Part(id='part0')
4530        >>> m1 = stream.Measure(number=1)
4531        >>> v1 = stream.Voice(id='voice1')
4532        >>> v1.insert(0, note.Note('E', quarterLength=4.0))
4533        >>> v2 = stream.Voice(id='voice2')
4534        >>> v2.insert(0, note.Note('G', quarterLength=2.0))
4535        >>> m1.insert(0, v1)
4536        >>> m1.insert(0, v2)
4537        >>> m2 = stream.Measure(number=2)
4538        >>> m2.insert(0, note.Note('D', quarterLength=4.0))
4539        >>> p.append([m1, m2])
4540        >>> pt = p.template(retainVoices=False)
4541        >>> pt.show('text')
4542        {0.0} <music21.stream.Measure 1 offset=0.0>
4543            {0.0} <music21.note.Rest whole>
4544        {4.0} <music21.stream.Measure 2 offset=4.0>
4545            {0.0} <music21.note.Rest whole>
4546        >>> pt[0][0].quarterLength
4547        4.0
4548
4549        Developer note -- if you just want a copy of a Score with new
4550        Part and Measure objects, but you don't care that the notes, etc.
4551        inside are the same objects as the original (i.e., you do not
4552        plan to manipulate them, or you want the manipulations to
4553        return to the original objects), using .template() is several
4554        times faster than a deepcopy of the stream (about 4x faster
4555        on bwv66.6)
4556
4557        Changed in v7. -- all arguments are keyword only.
4558        '''
4559        out = self.cloneEmpty(derivationMethod='template')
4560        if removeClasses is None:
4561            removeClasses = {'GeneralNote', 'Dynamic', 'Expression'}
4562        elif common.isIterable(removeClasses):
4563            removeClasses = set(removeClasses)
4564
4565        restInfo = {'offset': None, 'endTime': None}
4566
4567        def optionalAddRest():
4568            # six.PY3  nonlocal currentRest  would remove the need for restInfo struct
4569            if not fillWithRests:
4570                return
4571            if restInfo['offset'] is None:
4572                return
4573
4574            restQL = restInfo['endTime'] - restInfo['offset']
4575            restObj = note.Rest(quarterLength=restQL)
4576            out.coreInsert(restInfo['offset'], restObj)
4577            restInfo['offset'] = None
4578            restInfo['endTime'] = None
4579
4580        for el in self:
4581            elOffset = self.elementOffset(el, returnSpecial=True)
4582            if el.isStream and (retainVoices or ('Voice' not in el.classes)):
4583                optionalAddRest()
4584                outEl = el.template(fillWithRests=fillWithRests,
4585                                    removeClasses=removeClasses,
4586                                    retainVoices=retainVoices)
4587                if elOffset != OffsetSpecial.AT_END:
4588                    out.coreInsert(elOffset, outEl)
4589                else:  # pragma: no cover
4590                    # should not have streams stored at end.
4591                    out.coreStoreAtEnd(outEl)
4592
4593            elif (removeClasses is True
4594                    or el.classSet.intersection(removeClasses)
4595                    or (not retainVoices and 'Voice' in el.classes)):
4596                # remove this element
4597                if fillWithRests and el.duration.quarterLength:
4598                    endTime = elOffset + el.duration.quarterLength
4599                    if restInfo['offset'] is None:
4600                        restInfo['offset'] = elOffset
4601                        restInfo['endTime'] = endTime
4602                    elif endTime > restInfo['endTime']:
4603                        restInfo['endTime'] = endTime
4604            else:
4605                optionalAddRest()
4606                elNew = copy.deepcopy(el)
4607                if elOffset != OffsetSpecial.AT_END:
4608                    out.coreInsert(elOffset, elNew)
4609                else:
4610                    out.coreStoreAtEnd(elNew)
4611
4612        # Replace old measures in spanners with new measures
4613        # Example: out is a Part, out.spannerBundle has RepeatBrackets spanning measures
4614        # TODO: when dropping support for Py3.9 add strict=True
4615        for oldM, newM in zip(
4616            self.getElementsByClass('Measure'),
4617            out.getElementsByClass('Measure')
4618        ):
4619            out.spannerBundle.replaceSpannedElement(oldM, newM)
4620
4621        optionalAddRest()
4622        out.coreElementsChanged()
4623
4624        return out
4625
4626    def measureOffsetMap(self, classFilterList=None):
4627        '''
4628        If this Stream contains Measures, returns an OrderedDict
4629        whose keys are the offsets of the start of each measure
4630        and whose values are a list of references
4631        to the :class:`~music21.stream.Measure` objects that start
4632        at that offset.
4633
4634        Even in normal music there may be more than
4635        one Measure starting at each offset because each
4636        :class:`~music21.stream.Part` might define its own Measure.
4637        However, you are unlikely to encounter such things unless you
4638        run Score.flatten(retainContainers=True).
4639
4640        The offsets are always measured relative to the
4641        calling Stream (self).
4642
4643        You can specify a `classFilterList` argument as a list of classes
4644        to find instead of Measures.  But the default will of course
4645        find Measure objects.
4646
4647        Example 1: This Bach chorale is in 4/4 without a pickup, so
4648        as expected, measures are found every 4 offsets, until the
4649        weird recitation in m. 7 which in our edition lasts 10 beats
4650        and thus causes a gap in measureOffsetMap from 24.0 to 34.0.
4651
4652        .. image:: images/streamMeasureOffsetMapBWV324.*
4653            :width: 572
4654
4655
4656        >>> chorale = corpus.parse('bach/bwv324.xml')
4657        >>> alto = chorale.parts['alto']
4658        >>> altoMeasures = alto.measureOffsetMap()
4659        >>> altoMeasures
4660        OrderedDict([(0.0, [<music21.stream.Measure 1 offset=0.0>]),
4661                     (4.0, [<music21.stream.Measure 2 offset=4.0>]),
4662                     (8.0, [<music21.stream.Measure 3 offset=8.0>]),
4663                     ...
4664                     (38.0, [<music21.stream.Measure 9 offset=38.0>])])
4665        >>> list(altoMeasures.keys())
4666        [0.0, 4.0, 8.0, 12.0, 16.0, 20.0, 24.0, 34.0, 38.0]
4667
4668        altoMeasures is a dictionary of the measures
4669        that are found in the alto part, so we can get
4670        the measure beginning on offset 4.0 (measure 2)
4671        and display it (though it's the only measure
4672        found at offset 4.0, there might be others as
4673        in example 2, so we need to call altoMeasures[4.0][0]
4674        to get this measure.):
4675
4676        >>> altoMeasures[4.0]
4677        [<music21.stream.Measure 2 offset=4.0>]
4678        >>> altoMeasures[4.0][0].show('text')
4679        {0.0} <music21.note.Note D>
4680        {1.0} <music21.note.Note D#>
4681        {2.0} <music21.note.Note E>
4682        {3.0} <music21.note.Note F#>
4683
4684        Example 2: How to get all the measures from all parts (not the
4685        most efficient way, but it works!):
4686
4687        >>> mom = chorale.measureOffsetMap()
4688        >>> mom
4689        OrderedDict([(0.0, [<music21.stream.Measure 1 offset=0.0>,
4690                            <music21.stream.Measure 1 offset=0.0>,
4691                            <music21.stream.Measure 1 offset=0.0>,
4692                            <music21.stream.Measure 1 offset=0.0>]),
4693                      (4.0, [<music21.stream.Measure 2 offset=4.0>,
4694                             ...])])
4695        >>> for measure_obj in mom[8.0]:
4696        ...     print(measure_obj, measure_obj.getContextByClass('Part').id)
4697        <music21.stream.Measure 3 offset=8.0> Soprano
4698        <music21.stream.Measure 3 offset=8.0> Alto
4699        <music21.stream.Measure 3 offset=8.0> Tenor
4700        <music21.stream.Measure 3 offset=8.0> Bass
4701
4702        OMIT_FROM_DOCS
4703
4704        see important examples in testMeasureOffsetMap() and
4705        testMeasureOffsetMapPostTie()
4706        '''
4707        if classFilterList is None:
4708            classFilterList = [Measure]
4709        elif not isinstance(classFilterList, (list, tuple)):
4710            classFilterList = [classFilterList]
4711
4712        # environLocal.printDebug(['calling measure offsetMap()'])
4713
4714        # environLocal.printDebug([classFilterList])
4715        offsetMap = {}
4716        # first, try to get measures
4717        # this works best of this is a Part or Score
4718        if Measure in classFilterList or 'Measure' in classFilterList:
4719            for m in self.getElementsByClass('Measure'):
4720                offset = self.elementOffset(m)
4721                if offset not in offsetMap:
4722                    offsetMap[offset] = []
4723                # there may be more than one measure at the same offset
4724                offsetMap[offset].append(m)
4725
4726        # try other classes
4727        for className in classFilterList:
4728            if className in [Measure or 'Measure']:  # do not redo
4729                continue
4730            for e in self.getElementsByClass(className):
4731                # environLocal.printDebug(['calling measure offsetMap(); e:', e])
4732                # NOTE: if this is done on Notes, this can take an extremely
4733                # long time to process
4734                # 'reverse' here is a reverse sort, where oldest objects are returned
4735                # first
4736                m = e.getContextByClass('Measure')  # , sortByCreationTime='reverse')
4737                if m is None:  # pragma: no cover
4738                    # hard to think of a time this would happen...But...
4739                    continue
4740                # assuming that the offset returns the proper offset context
4741                # this is, the current offset may not be the stream that
4742                # contains this Measure; its current activeSite
4743                offset = m.offset
4744                if offset not in offsetMap:
4745                    offsetMap[offset] = []
4746                if m not in offsetMap[offset]:
4747                    offsetMap[offset].append(m)
4748
4749        orderedOffsetMap = collections.OrderedDict(sorted(offsetMap.items(), key=lambda o: o[0]))
4750        return orderedOffsetMap
4751
4752    def _getFinalBarline(self):
4753        # if we have part-like streams, process each part
4754        if self.hasPartLikeStreams():
4755            post = []
4756            for p in self.getElementsByClass('Stream'):
4757                post.append(p._getFinalBarline())
4758            return post  # a list of barlines
4759        # core routines for a single Stream
4760        else:
4761            if self.hasMeasures():
4762                return self.getElementsByClass('Measure').last().rightBarline
4763            elif hasattr(self, 'rightBarline'):
4764                return self.rightBarline
4765            else:
4766                return None
4767
4768    def _setFinalBarline(self, value):
4769        # if we have part-like streams, process each part
4770        if self.hasPartLikeStreams():
4771            if not common.isListLike(value):
4772                value = [value]
4773            for i, p in enumerate(self.getElementsByClass('Stream')):
4774                # set final barline w/ mod iteration of value list
4775                bl = value[i % len(value)]
4776                # environLocal.printDebug(['enumerating measures', i, p, 'setting barline', bl])
4777                p._setFinalBarline(bl)
4778            return
4779
4780        # core routines for a single Stream
4781        if self.hasMeasures():
4782            self.getElementsByClass('Measure').last().rightBarline = value
4783        elif hasattr(self, 'rightBarline'):
4784            self.rightBarline = value  # pylint: disable=attribute-defined-outside-init
4785        # do nothing for other streams
4786
4787    finalBarline = property(_getFinalBarline, _setFinalBarline, doc='''
4788        Get or set the final barline of this Stream's Measures,
4789        if and only if there are Measures defined as elements in this Stream.
4790        This method will not create Measures if none exist.
4791
4792        >>> p = stream.Part()
4793        >>> m1 = stream.Measure()
4794        >>> m1.rightBarline = bar.Barline('double')
4795        >>> p.append(m1)
4796        >>> p.finalBarline
4797        <music21.bar.Barline type=double>
4798        >>> m2 = stream.Measure()
4799        >>> m2.rightBarline = bar.Barline('final')
4800        >>> p.append(m2)
4801        >>> p.finalBarline
4802        <music21.bar.Barline type=final>
4803
4804        This property also works on Scores that contain one or more Parts.
4805        In that case a list of barlines can be used to set the final barline.
4806
4807        >>> s = corpus.parse('bwv66.6')
4808        >>> s.finalBarline
4809        [<music21.bar.Barline type=final>,
4810         <music21.bar.Barline type=final>,
4811         <music21.bar.Barline type=final>,
4812         <music21.bar.Barline type=final>]
4813
4814        >>> s.finalBarline = 'none'
4815        >>> s.finalBarline
4816        [<music21.bar.Barline type=none>,
4817         <music21.bar.Barline type=none>,
4818         <music21.bar.Barline type=none>,
4819         <music21.bar.Barline type=none>]
4820
4821
4822        Getting or setting a final barline on a Measure (or another Stream
4823        with a rightBarline attribute) is the same as getting or setting the rightBarline.
4824
4825        >>> m = stream.Measure()
4826        >>> m.finalBarline is None
4827        True
4828        >>> m.finalBarline = 'final'
4829        >>> m.finalBarline
4830        <music21.bar.Barline type=final>
4831        >>> m.rightBarline
4832        <music21.bar.Barline type=final>
4833
4834        Getting on a generic Stream, Voice, or Opus always returns a barline of None,
4835        and setting on a generic Stream, Voice, or Opus always returns None:
4836
4837        >>> s = stream.Stream()
4838        >>> s.finalBarline is None
4839        True
4840        >>> s.finalBarline = 'final'
4841        >>> s.finalBarline is None
4842        True
4843
4844        Changed in v6.3 -- does not raise an exception if queried or set on a measure-less stream.
4845        Previously raised a StreamException
4846        ''')
4847
4848    @property
4849    def voices(self):
4850        '''
4851        Return all :class:`~music21.stream.Voices` objects
4852        in an iterator
4853
4854        >>> s = stream.Stream()
4855        >>> s.insert(0, stream.Voice())
4856        >>> s.insert(0, stream.Voice())
4857        >>> len(s.voices)
4858        2
4859        '''
4860        return self.getElementsByClass('Voice')
4861
4862    @property
4863    def spanners(self):
4864        '''
4865        Return all :class:`~music21.spanner.Spanner` objects
4866        (things such as Slurs, long trills, or anything that
4867        connects many objects)
4868        in an Iterator
4869
4870        >>> s = stream.Stream()
4871        >>> s.insert(0, spanner.Slur())
4872        >>> s.insert(0, spanner.Slur())
4873        >>> len(s.spanners)
4874        2
4875        '''
4876        return self.getElementsByClass('Spanner')
4877
4878    # --------------------------------------------------------------------------
4879    # handling transposition values and status
4880
4881    @property
4882    def atSoundingPitch(self) -> Union[bool, str]:
4883        '''
4884        Get or set the atSoundingPitch status, that is whether the
4885        score is at concert pitch or may have transposing instruments
4886        that will not sound as notated.
4887
4888        Valid values are True, False, and 'unknown'.
4889
4890        Note that setting "atSoundingPitch" does not actually transpose the notes. See
4891        `toSoundingPitch()` for that information.
4892
4893        >>> s = stream.Stream()
4894        >>> s.atSoundingPitch = True
4895        >>> s.atSoundingPitch = False
4896        >>> s.atSoundingPitch = 'unknown'
4897        >>> s.atSoundingPitch
4898        'unknown'
4899        >>> s.atSoundingPitch = 'junk'
4900        Traceback (most recent call last):
4901        music21.exceptions21.StreamException: not a valid at sounding pitch value: junk
4902        '''
4903        return self._atSoundingPitch
4904
4905    @atSoundingPitch.setter
4906    def atSoundingPitch(self, value: Union[bool, str]):
4907        if value in [True, False, 'unknown']:
4908            self._atSoundingPitch = value
4909        else:
4910            raise StreamException(f'not a valid at sounding pitch value: {value}')
4911
4912    def _transposeByInstrument(self,
4913                               reverse=False,
4914                               inPlace=False,
4915                               transposeKeySignature=True):
4916        '''
4917        Transpose the Stream according to each instrument's transposition.
4918
4919        If reverse is False, the transposition will happen in the direction
4920        opposite of what is specified by the Instrument. for instance,
4921        for changing a concert score to a transposed score or for
4922        extracting parts.
4923
4924        TODO: Fill out -- expose publicly?
4925        '''
4926        if not inPlace:  # make a copy
4927            returnObj = copy.deepcopy(self)
4928        else:
4929            returnObj = self
4930
4931        instrument_stream = returnObj.getInstruments(recurse=True)
4932        instrument_map: Dict['music21.instrument.Instrument', Union[float, Fraction]] = {}
4933        for inst in instrument_stream:
4934            # keep track of original durations of each instrument
4935            instrument_map[inst] = inst.duration.quarterLength
4936            # this loses the expression of duration, but should be fine for instruments.
4937
4938        instrument_stream.duration = returnObj.duration
4939        # inPlace=False here because we are only doing calculations
4940        # toWrittenPitch() shouldn't be inserting extra instruments
4941        instrument_stream = instrument_stream.extendDuration('Instrument', inPlace=False)
4942
4943        # store class filter list for transposition
4944        if transposeKeySignature:
4945            classFilterList = ('Note', 'Chord', 'KeySignature')
4946        else:
4947            classFilterList = ('Note', 'Chord')
4948
4949        for inst in instrument_stream:
4950            if inst.transposition is None:
4951                continue
4952            start = inst.offset
4953            end = start + inst.quarterLength
4954            focus = returnObj.flatten().getElementsByOffset(
4955                start,
4956                end,
4957                includeEndBoundary=False,
4958                mustFinishInSpan=False,
4959                mustBeginInSpan=True).stream()
4960            trans = inst.transposition
4961            if reverse:
4962                trans = trans.reverse()
4963            focus.transpose(trans, inPlace=True,
4964                            classFilterList=classFilterList)
4965
4966        # restore original durations
4967        for inst, original_ql in instrument_map.items():
4968            inst.duration.quarterLength = original_ql
4969
4970        return returnObj
4971
4972    def _treatAtSoundingPitch(self) -> Union[bool, str]:
4973        '''
4974        `atSoundingPitch` might be True, False, or 'unknown'. Given that
4975        setting the property does not automatically synchronize the corresponding
4976        property on contained or containing streams, any time a method relying on the
4977        value of `atSoundingPitch` such as :meth:`toSoundingPitch` visits a stream,
4978        it will need to resolve 'unknown' values or even possibly conflicting values.
4979
4980        If this stream's `.atSoundingPitch` is 'unknown', this helper method searches
4981        this stream's sites until a True or False
4982        value for `.atSoundingPitch` is found, since it is possible a user only manipulated
4983        the value on the top-level stream.
4984
4985        Then, contained streams are searched to verify that they do not contain
4986        conflicting values (i.e. .atSoundingPitch = True when the container has
4987        .atSoundingPitch = False). Conflicting values are resolved by converting
4988        the inner streams to written or sounding pitch as necessary to match this
4989        stream's value.
4990        '''
4991        at_sounding = self.atSoundingPitch
4992        if self.atSoundingPitch == 'unknown':
4993            for site in self.sites:
4994                if site.isStream and site.atSoundingPitch != 'unknown':
4995                    at_sounding = site.atSoundingPitch
4996                    break
4997            else:
4998                raise StreamException('atSoundingPitch is unknown: cannot transpose')
4999
5000        for substream in self.recurse(streamsOnly=True, includeSelf=False):
5001            if substream.atSoundingPitch == 'unknown':
5002                continue
5003            if substream.atSoundingPitch is False and at_sounding is True:
5004                substream.toSoundingPitch(inPlace=True)
5005            elif substream.atSoundingPitch is True and at_sounding is False:
5006                substream.toWrittenPitch(inPlace=True)
5007
5008        return at_sounding
5009
5010    def toSoundingPitch(self, *, inPlace=False):
5011        # noinspection PyShadowingNames
5012        '''
5013        If not at sounding pitch, transpose all Pitch
5014        elements to sounding pitch. The atSoundingPitch property
5015        is used to determine if transposition is necessary.
5016
5017        Affected by the presence of Instruments and by Ottava spanners
5018
5019        v2.0.10 changes -- inPlace is False; v. 5 returns None if inPlace=True
5020
5021        >>> sc = stream.Score()
5022        >>> p = stream.Part(id='barisax')
5023        >>> p.append(instrument.BaritoneSaxophone())
5024        >>> m = stream.Measure(number=1)
5025        >>> m.append(note.Note('A4'))
5026        >>> p.append(m)
5027        >>> sc.append(p)
5028        >>> sc.atSoundingPitch = False
5029
5030        >>> scSounding = sc.toSoundingPitch()
5031        >>> scSounding.show('text')
5032        {0.0} <music21.stream.Part barisax>
5033            {0.0} <music21.instrument.BaritoneSaxophone 'Baritone Saxophone'>
5034            {0.0} <music21.stream.Measure 1 offset=0.0>
5035                {0.0} <music21.note.Note C>
5036
5037        >>> scSounding.atSoundingPitch
5038        True
5039        >>> scSounding.parts[0].atSoundingPitch
5040        True
5041        >>> scSounding.recurse().notes[0].nameWithOctave
5042        'C3'
5043
5044        If 'atSoundingPitch' is unknown for this Stream and all of its parent Streams
5045        then will raise a StreamException:
5046
5047        >>> s = stream.Score()
5048        >>> p = stream.Part(id='partEmpty')
5049        >>> s.append(p)
5050        >>> p.toSoundingPitch()
5051        Traceback (most recent call last):
5052        music21.exceptions21.StreamException: atSoundingPitch is unknown: cannot transpose
5053        >>> s.atSoundingPitch = False
5054        >>> sp = p.toSoundingPitch()
5055        >>> sp
5056        <music21.stream.Part partEmpty>
5057        >>> sp.derivation.origin is p
5058        True
5059        '''
5060        if not inPlace:  # make a copy
5061            returnObj = self.coreCopyAsDerivation('toSoundingPitch')
5062        else:
5063            returnObj = self
5064
5065        if returnObj.hasPartLikeStreams() or 'Opus' in returnObj.classSet:
5066            for partLike in returnObj.getElementsByClass('Stream'):
5067                # call on each part
5068                partLike.toSoundingPitch(inPlace=True)
5069            returnObj.atSoundingPitch = True
5070            return returnObj if not inPlace else None
5071
5072        at_sounding = returnObj._treatAtSoundingPitch()
5073
5074        if at_sounding is False:
5075            # transposition defined on instrument goes from written to sounding
5076            returnObj._transposeByInstrument(reverse=False, inPlace=True)
5077            for container in returnObj.recurse(streamsOnly=True, includeSelf=True):
5078                container.atSoundingPitch = True
5079
5080        for ottava in returnObj.recurse().getElementsByClass('Ottava'):
5081            ottava.performTransposition()
5082
5083        if not inPlace:
5084            return returnObj  # the Stream or None
5085
5086    def toWrittenPitch(self, *, inPlace=False):
5087        '''
5088        If not at written pitch, transpose all Pitch elements to
5089        written pitch. The atSoundingPitch property is used to
5090        determine if transposition is necessary.
5091
5092        music21 v.3 changes -- inPlace=False, v. 5 -- returns None if inPlace=True
5093
5094        >>> sc = stream.Score()
5095        >>> p = stream.Part(id='baritoneSax')
5096        >>> p.append(instrument.BaritoneSaxophone())
5097        >>> m = stream.Measure(number=1)
5098        >>> m.append(note.Note('C3'))
5099        >>> p.append(m)
5100        >>> sc.append(p)
5101        >>> sc.atSoundingPitch = True
5102        >>> scWritten = sc.toWrittenPitch()
5103        >>> scWritten.show('text')
5104        {0.0} <music21.stream.Part baritoneSax>
5105            {0.0} <music21.instrument.BaritoneSaxophone 'Baritone Saxophone'>
5106            {0.0} <music21.stream.Measure 1 offset=0.0>
5107                {0.0} <music21.note.Note A>
5108        >>> scWritten.atSoundingPitch
5109        False
5110        >>> scWritten.parts[0].atSoundingPitch
5111        False
5112        >>> scWritten.recurse().notes[0].nameWithOctave
5113        'A4'
5114        '''
5115        if not inPlace:  # make a copy
5116            returnObj = self.coreCopyAsDerivation('toWrittenPitch')
5117        else:
5118            returnObj = self
5119
5120        if returnObj.hasPartLikeStreams() or 'Opus' in returnObj.classes:
5121            for partLike in returnObj.getElementsByClass('Stream'):
5122                # call on each part
5123                partLike.toWrittenPitch(inPlace=True)
5124            returnObj.atSoundingPitch = False
5125            return returnObj if not inPlace else None
5126
5127        at_sounding = returnObj._treatAtSoundingPitch()
5128
5129        if at_sounding is True:
5130            # need to reverse to go to written
5131            returnObj._transposeByInstrument(reverse=True, inPlace=True)
5132            for container in returnObj.recurse(streamsOnly=True, includeSelf=True):
5133                container.atSoundingPitch = False
5134
5135        for ottava in returnObj.recurse().getElementsByClass('Ottava'):
5136            ottava.undoTransposition()
5137
5138        if not inPlace:
5139            return returnObj
5140
5141    # --------------------------------------------------------------------------
5142    def getTimeSignatures(self, *,
5143                          searchContext=True,
5144                          returnDefault=True,
5145                          sortByCreationTime=True):
5146        '''
5147        Collect all :class:`~music21.meter.TimeSignature` objects in this stream.
5148        If no TimeSignature objects are defined, get a default (4/4 or whatever
5149        is defined in the defaults.py file).
5150
5151        >>> a = stream.Stream()
5152        >>> b = meter.TimeSignature('3/4')
5153        >>> a.insert(b)
5154        >>> a.repeatInsert(note.Note('C#'), list(range(10)))
5155        >>> c = a.getTimeSignatures()
5156        >>> len(c) == 1
5157        True
5158        '''
5159        # even if this is a Measure, the TimeSignature in the Stream will be
5160        # found
5161        # post = self.getElementsByClass(meter.TimeSignature)
5162        post = self.getElementsByClass('TimeSignature').stream()
5163
5164        # search activeSite Streams through contexts
5165        if not post and searchContext:
5166            # returns a single value
5167            post = self.cloneEmpty(derivationMethod='getTimeSignatures')
5168
5169            # sort by time to search the most recent objects
5170            obj = self.getContextByClass('TimeSignature', sortByCreationTime=sortByCreationTime)
5171            # obj = self.previous('TimeSignature')
5172            # environLocal.printDebug(['getTimeSignatures(): searching contexts: results', obj])
5173            if obj is not None:
5174                post.append(obj)
5175
5176        # if there is no timeSignature, we will look at any stream at offset 0:
5177        if not post:
5178            streamsAtStart = self.getElementsByOffset(0.0).getElementsByClass('Stream')
5179            for s in streamsAtStart:
5180                tss = s.getElementsByOffset(0.0).getElementsByClass('TimeSignature')
5181                for ts in tss:
5182                    post.append(ts)
5183
5184        # get a default and/or place default at zero if nothing at zero
5185        if returnDefault:
5186            if not post or post[0].offset > 0:
5187                ts = meter.TimeSignature('%s/%s' % (defaults.meterNumerator,
5188                                                    defaults.meterDenominatorBeatType))
5189                post.insert(0, ts)
5190        # environLocal.printDebug(['getTimeSignatures(): final result:', post[0]])
5191        return post
5192
5193    def getInstruments(self,
5194                       *,
5195                       searchActiveSite=True,
5196                       returnDefault=True,
5197                       recurse=False) -> 'music21.stream.Stream':
5198        '''
5199        Search this stream or activeSite streams for
5200        :class:`~music21.instrument.Instrument` objects, and return a new stream
5201        containing them. Otherwise, return a Stream containing a single default `Instrument`.
5202
5203        >>> p = stream.Part()
5204        >>> m = stream.Measure([note.Note()])
5205        >>> p.insert(0, m)
5206        >>> instrumentStream = p.getInstruments(returnDefault=True)
5207        >>> defaultInst = instrumentStream.first()
5208        >>> defaultInst
5209        <music21.instrument.Instrument ': '>
5210
5211        Insert the default instrument into the part:
5212
5213        >>> p.insert(0, defaultInst)
5214
5215        Searching the measure will find it only if the measure's active site is searched:
5216
5217        >>> search1 = p.measure(1).getInstruments(searchActiveSite=False, returnDefault=False)
5218        >>> search1.first() is None
5219        True
5220        >>> search2 = p.measure(1).getInstruments(searchActiveSite=True, returnDefault=False)
5221        >>> search2.first() is defaultInst
5222        True
5223        '''
5224        instObj = None
5225        if recurse:
5226            sIter = self.recurse()
5227        else:
5228            sIter = self.iter()
5229
5230        post = sIter.getElementsByClass('Instrument').stream()
5231        if post:
5232            # environLocal.printDebug(['found local instrument:', post[0]])
5233            instObj = post.first()
5234        else:
5235            if searchActiveSite:
5236                # if isinstance(self.activeSite, Stream) and self.activeSite != self:
5237                if (self.activeSite is not None
5238                    and self.activeSite.isStream
5239                        and self.activeSite is not self):
5240                    # environLocal.printDebug(['searching activeSite Stream',
5241                    #    self, self.activeSite])
5242                    instObj = self.activeSite.getInstrument(
5243                        searchActiveSite=searchActiveSite,
5244                        returnDefault=False)
5245                    if instObj is not None:
5246                        post.insert(0, instObj)
5247
5248        # if still not defined, get default
5249        if returnDefault and instObj is None:
5250            from music21.instrument import Instrument
5251            instObj = Instrument()
5252            # instObj.partId = defaults.partId  # give a default id
5253            # TODO: should this be changed to None? MSC 2015-12
5254            instObj.partName = defaults.partName  # give a default id
5255            post.insert(0, instObj)
5256
5257        # returns a Stream
5258        return post
5259
5260    def getInstrument(self,
5261                      *,
5262                      searchActiveSite=True,
5263                      returnDefault=True,
5264                      recurse=False) -> Optional['music21.instrument.Instrument']:
5265        '''
5266        Return the first Instrument found in this Stream, or None.
5267
5268        >>> s = stream.Score()
5269        >>> p1 = stream.Part()
5270        >>> p1.insert(instrument.Violin())
5271        >>> m1p1 = stream.Measure()
5272        >>> m1p1.append(note.Note('g'))
5273        >>> p1.append(m1p1)
5274
5275        >>> p2 = stream.Part()
5276        >>> p2.insert(instrument.Viola())
5277        >>> m1p2 = stream.Measure()
5278        >>> m1p2.append(note.Note('f#'))
5279        >>> p2.append(m1p2)
5280
5281        >>> s.insert(0, p1)
5282        >>> s.insert(0, p2)
5283        >>> p1.getInstrument(returnDefault=False).instrumentName
5284        'Violin'
5285        >>> p2.getInstrument(returnDefault=False).instrumentName
5286        'Viola'
5287
5288        Changed in v.7 -- added `recurse` (default False)
5289        '''
5290        post = self.getInstruments(searchActiveSite=searchActiveSite,
5291                                   returnDefault=returnDefault,
5292                                   recurse=recurse)
5293        return post.first()
5294
5295    @common.deprecated('v7', 'v8', 'use getElementsByClass() or getContextByClass() or bestClef()')
5296    def getClefs(self, searchActiveSite=False, searchContext=True,
5297                 returnDefault=True):  # pragma: no cover
5298        '''
5299        DEPRECATED in v7.
5300
5301        Collect all :class:`~music21.clef.Clef` objects in
5302        this Stream in a list. Optionally search the
5303        activeSite Stream and/or contexts.
5304
5305        If no Clef objects are defined, get a default
5306        using :meth:`~music21.clef.bestClef`
5307
5308
5309        >>> a = stream.Stream()
5310        >>> b = clef.AltoClef()
5311        >>> a.insert(0, b)
5312        >>> a.repeatInsert(note.Note('C#'), list(range(10)))
5313        >>> #_DOCS_SHOW c = a.getClefs()
5314        >>> #_DOCS_SHOW len(c) == 1
5315        True
5316        '''
5317        # TODO: activeSite searching is not yet implemented
5318        # this may not be useful unless a stream is flat
5319        post = list(self.getElementsByClass('Clef'))
5320
5321        # environLocal.printDebug(['getClefs(); count of local', len(post), post])
5322        if not post and searchActiveSite and self.activeSite is not None:
5323            # environLocal.printDebug(['getClefs(): search activeSite'])
5324            post = list(self.activeSite.getElementsByClass('Clef'))
5325
5326        if not post and searchContext:
5327            # returns a single element match
5328            # post = self.__class__()
5329            obj = self.getContextByClass('Clef')
5330            if obj is not None:
5331                post.append(obj)
5332
5333        # get a default and/or place default at zero if nothing at zero
5334        if returnDefault and (not post or post[0].offset > 0):
5335            # environLocal.printDebug(['getClefs(): using bestClef()'])
5336            post.insert(0, clef.bestClef(self))
5337        return post
5338
5339    @common.deprecated('v7', 'v8', 'use getElementsByClass() or getContextByClass()')
5340    def getKeySignatures(self, searchActiveSite=True, searchContext=True):  # pragma: no cover
5341        '''
5342        Collect all :class:`~music21.key.KeySignature` objects in this
5343        Stream in a new Stream. Optionally search the activeSite
5344        stream and/or contexts.
5345
5346        If no KeySignature objects are defined, returns an empty Stream
5347
5348        DEPRECATED in v7.
5349
5350        >>> a = stream.Stream()
5351        >>> b = key.KeySignature(3)
5352        >>> a.insert(0, b)
5353        >>> a.repeatInsert(note.Note('C#'), list(range(10)))
5354        >>> #_DOCS_SHOW c = a.getKeySignatures()
5355        >>> #_DOCS_SHOW len(c) == 1
5356        True
5357        '''
5358        # TODO: activeSite searching is not yet implemented
5359        # this may not be useful unless a stream is flat
5360        post = self.getElementsByClass('KeySignature')
5361        if not post and searchContext:
5362            # returns a single value
5363            post = self.cloneEmpty(derivationMethod='getKeySignatures')
5364            obj = self.getContextByClass(key.KeySignature)
5365            if obj is not None:
5366                post.append(obj)
5367
5368        # do nothing if empty
5369        if not post or post[0].offset > 0:
5370            pass
5371        return post
5372
5373    def invertDiatonic(self, inversionNote=note.Note('C4'), *, inPlace=False):
5374        '''
5375        inverts a stream diatonically around the given note (by default, middle C)
5376
5377        For pieces where the key signature
5378        does not change throughout the piece it is MUCH faster than
5379        for pieces where the key signature changes.
5380
5381        Here in this test, we put Ciconia's Quod Jactatur (a single voice
5382        piece that should have a canon solution: see trecento.quodJactatur)
5383        into 3 flats (instead of its original 1 flat) in measure 1, but
5384        into 5 sharps in measure 2 and then invert around F4, creating
5385        a new piece.
5386
5387        Changed in v. 5 -- inPlace is False by default.
5388
5389
5390        >>> qj = corpus.parse('ciconia/quod_jactatur').parts[0].measures(1, 2)
5391        >>> qj.id = 'measureExcerpt'
5392
5393        >>> qj.show('text')
5394        {0.0} <music21.instrument.Piano 'P1: MusicXML Part: Grand Piano'>
5395        {0.0} <music21.stream.Measure 1 offset=0.0>
5396            {0.0} <music21.layout.SystemLayout>
5397            {0.0} <music21.clef.Treble8vbClef>
5398            {0.0} <music21.key.Key of F major>
5399            {0.0} <music21.meter.TimeSignature 2/4>
5400            {0.0} <music21.note.Note C>
5401            {1.5} <music21.note.Note D>
5402        {2.0} <music21.stream.Measure 2 offset=2.0>
5403            {0.0} <music21.note.Note E>
5404            {0.5} <music21.note.Note D>
5405            {1.0} <music21.note.Note C>
5406            {1.5} <music21.note.Note D>
5407
5408        >>> qjFlat = qj.flatten()
5409        >>> k1 = qjFlat.getElementsByClass(key.KeySignature).first()
5410        >>> k3flats = key.KeySignature(-3)
5411        >>> qjFlat.replace(k1, k3flats, allDerived=True)
5412        >>> qj.getElementsByClass(stream.Measure)[1].insert(0, key.KeySignature(5))
5413
5414        >>> qj2 = qj.invertDiatonic(note.Note('F4'), inPlace=False)
5415        >>> qj2.show('text')
5416        {0.0} <music21.instrument.Piano 'P1: MusicXML Part: Grand Piano'>
5417        {0.0} <music21.stream.Measure 1 offset=0.0>
5418            {0.0} <music21.layout.SystemLayout>
5419            {0.0} <music21.clef.Treble8vbClef>
5420            {0.0} <music21.key.KeySignature of 3 flats>
5421            {0.0} <music21.meter.TimeSignature 2/4>
5422            {0.0} <music21.note.Note B->
5423            {1.5} <music21.note.Note A->
5424        {2.0} <music21.stream.Measure 2 offset=2.0>
5425            {0.0} <music21.key.KeySignature of 5 sharps>
5426            {0.0} <music21.note.Note G#>
5427            {0.5} <music21.note.Note A#>
5428            {1.0} <music21.note.Note B>
5429            {1.5} <music21.note.Note A#>
5430        '''
5431        if inPlace:
5432            returnStream = self
5433        else:
5434            returnStream = self.coreCopyAsDerivation('invertDiatonic')
5435
5436        keySigSearch = returnStream.recurse().getElementsByClass(key.KeySignature)
5437
5438        quickSearch = True
5439        if not keySigSearch:
5440            ourKey = key.Key('C')
5441        elif len(keySigSearch) == 1:
5442            ourKey = keySigSearch[0]
5443        else:
5444            ourKey = None  # for might be undefined warning
5445            quickSearch = False
5446
5447        inversionDNN = inversionNote.pitch.diatonicNoteNum
5448        for n in returnStream.recurse().getElementsByClass('NotRest'):
5449            n.pitch.diatonicNoteNum = (2 * inversionDNN) - n.pitch.diatonicNoteNum
5450            if quickSearch:  # use previously found
5451                n.pitch.accidental = ourKey.accidentalByStep(n.pitch.step)
5452            else:  # use context search
5453                ksActive = n.getContextByClass(key.KeySignature)
5454                n.pitch.accidental = ksActive.accidentalByStep(n.pitch.step)
5455
5456            if n.pitch.accidental is not None:
5457                n.pitch.accidental.displayStatus = None
5458
5459        if not inPlace:
5460            return returnStream
5461
5462    # -------------------------------------------------------------------------
5463    # offset manipulation
5464
5465    def shiftElements(self, offset, startOffset=None, endOffset=None, classFilterList=None):
5466        '''
5467        Add the given offset value to every offset of
5468        the objects found in the Stream. Objects that are
5469        specifically placed at the end of the Stream via
5470        .storeAtEnd() (such as right barlines) are
5471        not affected.
5472
5473        If startOffset is given then all elements before
5474        that offset will be shifted.  If endOffset is given
5475        then all elements at or after this offset will be
5476        shifted
5477
5478        >>> a = stream.Stream()
5479        >>> a.repeatInsert(note.Note('C'), list(range(10)))
5480        >>> a.shiftElements(30)
5481        >>> a.lowestOffset
5482        30.0
5483        >>> a.shiftElements(-10)
5484        >>> a.lowestOffset
5485        20.0
5486
5487        Use shiftElements to move elements after a change in
5488        duration:
5489
5490        >>> st2 = stream.Stream()
5491        >>> st2.insert(0, note.Note('D4', type='whole'))
5492        >>> st2.repeatInsert(note.Note('C4'), list(range(4, 8)))
5493        >>> st2.show('text')
5494        {0.0} <music21.note.Note D>
5495        {4.0} <music21.note.Note C>
5496        {5.0} <music21.note.Note C>
5497        {6.0} <music21.note.Note C>
5498        {7.0} <music21.note.Note C>
5499
5500        Now make the first note a dotted whole note and shift the rest by two quarters...
5501
5502        >>> firstNote = st2[0]
5503        >>> firstNoteOldQL = firstNote.quarterLength
5504        >>> firstNote.duration.dots = 1
5505        >>> firstNoteNewQL = firstNote.quarterLength
5506        >>> shiftAmount = firstNoteNewQL - firstNoteOldQL
5507        >>> shiftAmount
5508        2.0
5509
5510        >>> st2.shiftElements(shiftAmount, startOffset=4.0)
5511        >>> st2.show('text')
5512        {0.0} <music21.note.Note D>
5513        {6.0} <music21.note.Note C>
5514        {7.0} <music21.note.Note C>
5515        {8.0} <music21.note.Note C>
5516        {9.0} <music21.note.Note C>
5517
5518        A class filter list may be given.  It must be an iterable.
5519
5520        >>> st2.insert(7.5, key.Key('F'))
5521        >>> st2.shiftElements(2/3, startOffset=6.0, endOffset=8.0,
5522        ...                   classFilterList=[note.Note])
5523        >>> st2.show('text')
5524        {0.0} <music21.note.Note D>
5525        {6.6667} <music21.note.Note C>
5526        {7.5} <music21.key.Key of F major>
5527        {7.6667} <music21.note.Note C>
5528        {8.0} <music21.note.Note C>
5529        {9.0} <music21.note.Note C>
5530        '''
5531        # only want _elements, do not want _endElements
5532        for e in self._elements:
5533
5534            if startOffset is not None and self.elementOffset(e) < startOffset:
5535                continue
5536            if endOffset is not None and self.elementOffset(e) >= endOffset:
5537                continue
5538            if classFilterList is not None and e.classSet.isdisjoint(classFilterList):
5539                continue
5540
5541            self.coreSetElementOffset(e, opFrac(self.elementOffset(e) + offset))
5542
5543        self.coreElementsChanged()
5544
5545    def transferOffsetToElements(self):
5546        '''
5547        Transfer the offset of this stream to all
5548        internal elements; then set
5549        the offset of this stream to zero.
5550
5551        >>> a = stream.Stream()
5552        >>> a.repeatInsert(note.Note('C'), list(range(10)))
5553        >>> a.offset = 30
5554        >>> a.transferOffsetToElements()
5555        >>> a.lowestOffset
5556        30.0
5557        >>> a.offset
5558        0.0
5559        >>> a.offset = 20
5560        >>> a.transferOffsetToElements()
5561        >>> a.lowestOffset
5562        50.0
5563        '''
5564        self.shiftElements(self.offset)
5565        self.offset = 0.0
5566
5567    # -------------------------------------------------------------------------
5568    # utilities for creating large numbers of elements
5569
5570    def repeatAppend(self, item, numberOfTimes):
5571        '''
5572        Given an object and a number, run append that many times on
5573        a deepcopy of the object.
5574        numberOfTimes should of course be a positive integer.
5575
5576        >>> a = stream.Stream()
5577        >>> n = note.Note('D--')
5578        >>> n.duration.type = 'whole'
5579        >>> a.repeatAppend(n, 10)
5580        >>> a.show('text')
5581        {0.0} <music21.note.Note D-->
5582        {4.0} <music21.note.Note D-->
5583        {8.0} <music21.note.Note D-->
5584        {12.0} <music21.note.Note D-->
5585        ...
5586        {36.0} <music21.note.Note D-->
5587
5588        >>> a.duration.quarterLength
5589        40.0
5590        >>> a[9].offset
5591        36.0
5592        '''
5593        try:
5594            unused = item.isStream
5595            element = item
5596        # if not isinstance(item, music21.Music21Object):
5597        except AttributeError:
5598            # element = music21.ElementWrapper(item)
5599            raise StreamException('to put a non Music21Object in a stream, '
5600                                  + 'create a music21.ElementWrapper for the item')
5601        # # if not an element, embed
5602        # if not isinstance(item, music21.Music21Object):
5603        #     element = music21.ElementWrapper(item)
5604        # else:
5605        #     element = item
5606
5607        for unused_i in range(numberOfTimes):
5608            self.append(copy.deepcopy(element))
5609
5610    def repeatInsert(self, item, offsets):
5611        '''
5612        Given an object, create a deep copy of each object at
5613        each positions specified by
5614        the offset list:
5615
5616        >>> a = stream.Stream()
5617        >>> n = note.Note('G-')
5618        >>> n.quarterLength = 1
5619
5620        >>> a.repeatInsert(n, [0, 2, 3, 4, 4.5, 5, 6, 7, 8, 9, 10, 11, 12])
5621        >>> len(a)
5622        13
5623        >>> a[10].offset
5624        10.0
5625        '''
5626        if not common.isIterable(offsets):
5627            raise StreamException(f'must provide an iterable of offsets, not {offsets}')
5628
5629        try:
5630            unused = item.isStream
5631            element = item
5632        # if not isinstance(item, music21.Music21Object):
5633        except AttributeError:
5634            # if not an element, embed
5635            # element = music21.ElementWrapper(item)
5636            raise StreamException('to put a non Music21Object in a stream, '
5637                                  + 'create a music21.ElementWrapper for the item')
5638        # if not isinstance(item, music21.Music21Object):
5639        #     # if not an element, embed
5640        #     element = music21.ElementWrapper(item)
5641        # else:
5642        #     element = item
5643
5644        for offset in offsets:
5645            elementCopy = copy.deepcopy(element)
5646            self.coreInsert(offset, elementCopy)
5647        self.coreElementsChanged()
5648
5649    def extractContext(self, searchElement, before=4.0, after=4.0,
5650                       maxBefore=None, maxAfter=None):
5651        '''
5652        Extracts elements around the given element within (before)
5653        quarter notes and (after) quarter notes (default 4), and
5654        returns a new Stream.
5655
5656        >>> qn = note.Note(type='quarter')
5657        >>> qtrStream = stream.Stream()
5658        >>> qtrStream.repeatInsert(qn, [0, 1, 2, 3, 4, 5])
5659        >>> hn = note.Note(type='half')
5660        >>> hn.name = 'B-'
5661        >>> qtrStream.append(hn)
5662        >>> qtrStream.repeatInsert(qn, [8, 9, 10, 11])
5663        >>> hnStream = qtrStream.extractContext(hn, 1.0, 1.0)
5664        >>> hnStream.show('text')
5665        {5.0} <music21.note.Note C>
5666        {6.0} <music21.note.Note B->
5667        {8.0} <music21.note.Note C>
5668
5669        Changed in v7. -- forceOutputClass removed.
5670
5671        OMIT_FROM_DOCS
5672        TODO: maxBefore -- maximum number of elements to return before; etc.
5673        TODO: use .template to get new Stream
5674
5675        NOTE: RENAME: this probably should be renamed, as we use Context in a special way.
5676        Perhaps better is extractNeighbors?
5677
5678        '''
5679        display = self.cloneEmpty('extractContext')
5680
5681        found = None
5682        foundOffset = 0
5683        foundEnd = 0
5684        elements = self.elements
5685        for i in range(len(elements)):
5686            b = elements[i]
5687            if b.id == searchElement.id:
5688                found = i
5689                foundOffset = self.elementOffset(elements[i])
5690                foundEnd = foundOffset + elements[i].duration.quarterLength
5691            elif b is searchElement:
5692                found = i
5693                foundOffset = self.elementOffset(elements[i])
5694                foundEnd = foundOffset + elements[i].duration.quarterLength
5695        if found is None:
5696            raise StreamException('Could not find the element in the stream')
5697
5698        # handle _elements and _endElements independently
5699        for e in self._elements:
5700            o = self.elementOffset(e)
5701            if foundOffset - before <= o < foundEnd + after:
5702                display.coreInsert(o, e)
5703
5704        for e in self._endElements:
5705            o = self.elementOffset(e)
5706            if foundOffset - before <= o < foundEnd + after:
5707                display.coreStoreAtEnd(e)
5708        display.coreElementsChanged()
5709        return display
5710
5711    # --------------------------------------------------------------------------
5712    # transformations of self that return a new Stream
5713
5714    def _uniqueOffsetsAndEndTimes(self, offsetsOnly=False, endTimesOnly=False):
5715        '''
5716        Get a list of all offsets and endtimes
5717        of notes and rests in this stream.
5718
5719        Helper method for makeChords and Chordify
5720        run on .flatten().notesAndRests
5721
5722
5723        >>> s = stream.Score()
5724        >>> p1 = stream.Part()
5725        >>> p1.insert(4, note.Note('C#'))
5726        >>> p1.insert(5.3, note.Rest())
5727        >>> p2 = stream.Part()
5728        >>> p2.insert(2.12, note.Note('D-', type='half'))
5729        >>> p2.insert(5.5, note.Rest())
5730        >>> s.insert(0, p1)
5731        >>> s.insert(0, p2)
5732
5733        We will get a mix of float and fractions.Fraction() objects
5734
5735        >>> [str(o) for o in s.flatten()._uniqueOffsetsAndEndTimes()]
5736        ['53/25', '4.0', '103/25', '5.0', '53/10', '5.5', '63/10', '6.5']
5737
5738        Limit what is returned:
5739
5740        >>> [str(o) for o in s.flatten()._uniqueOffsetsAndEndTimes(offsetsOnly=True)]
5741        ['53/25', '4.0', '53/10', '5.5']
5742        >>> [str(o) for o in s.flatten()._uniqueOffsetsAndEndTimes(endTimesOnly=True)]
5743        ['103/25', '5.0', '63/10', '6.5']
5744
5745        And this is useless...  :-)
5746
5747        >>> s.flatten()._uniqueOffsetsAndEndTimes(offsetsOnly=True, endTimesOnly=True)
5748        []
5749
5750        '''
5751        offsetDictValues = self._offsetDict.values()
5752        if endTimesOnly:
5753            offsets = set()
5754        else:
5755            offsets = {opFrac(v[0]) for v in offsetDictValues}
5756
5757        if offsetsOnly:
5758            endTimes = set()
5759        else:
5760            endTimes = {opFrac(v[0] + v[1].duration.quarterLength)
5761                            for v in offsetDictValues}
5762        return sorted(offsets.union(endTimes))
5763
5764    @common.deprecated('v7', 'v8', 'use chordify() instead')
5765    def makeChords(self,
5766                   minimumWindowSize=0.125,
5767                   includePostWindow=True,
5768                   removeRedundantPitches=True,
5769                   useExactOffsets=False,
5770                   gatherArticulations=True,
5771                   gatherExpressions=True,
5772                   inPlace=False,
5773                   transferGroupsToPitches=False,
5774                   makeRests=True):  # pragma: no cover
5775        '''
5776        DEPRECATED in v7.  Use Chordify instead!
5777
5778        Gathers simultaneously sounding :class:`~music21.note.Note` objects
5779        into :class:`~music21.chord.Chord` objects, each of which
5780        contains all the pitches sounding together.
5781
5782        If useExactOffsets is True (default=False), then do an exact
5783        makeChords using the offsets in the piece.
5784        If this parameter is set, then minimumWindowSize is ignored.
5785
5786        This first example puts a part with three quarter notes (C4, D4, E4)
5787        together with a part consisting of a half note (C#5) and a
5788        quarter note (E#5) to make two Chords, the first containing the
5789        three :class:`~music21.pitch.Pitch` objects sounding at the
5790        beginning, the second consisting of the two Pitches sounding
5791        on offset 2.0 (beat 3):
5792
5793        >>> p1 = stream.Part()
5794        >>> p1.append([note.Note('C4', type='quarter'),
5795        ...            note.Note('D4', type='quarter'),
5796        ...            note.Note('E4', type='quarter'),
5797        ...            note.Note('B2', type='quarter')])
5798        >>> p2 = stream.Part()
5799        >>> p2.append([note.Note('C#5', type='half'),
5800        ...            note.Note('E#5', type='quarter'),
5801        ...            chord.Chord(['E4', 'G5', 'C#7'])])
5802        >>> sc1 = stream.Score()
5803        >>> sc1.insert(0, p1)
5804        >>> sc1.insert(0, p2)
5805        >>> #_DOCS_SHOW scChords = sc1.flatten().makeChords()
5806        >>> #_DOCS_SHOW scChords.show('text')
5807        {0.0} <music21.chord.Chord C4 C#5 D4>
5808        {2.0} <music21.chord.Chord E4 E#5>
5809        {3.0} <music21.chord.Chord B2 E4 G5 C#7>
5810
5811        The gathering of elements, starting from offset 0.0, uses
5812        the `minimumWindowSize`, in quarter lengths, to collect
5813        all Notes that start between 0.0 and the minimum window
5814        size (this permits overlaps within a minimum tolerance).
5815
5816        After collection, the maximum duration of collected elements
5817        is found; this duration is then used to set the new
5818        starting offset. A possible gap then results between the end
5819        of the window and offset specified by the maximum duration;
5820        these additional notes are gathered in a second pass if
5821        `includePostWindow` is True.
5822
5823        The new start offset is shifted to the larger of either
5824        the minimum window or the maximum duration found in the
5825        collected group. The process is repeated until all offsets
5826        are covered.
5827
5828        Each collection of Notes is formed into a Chord. The Chord
5829        is given the longest duration of all constituents, and is
5830        inserted at the start offset of the window from which it
5831        was gathered.
5832
5833        Chords can gather both articulations and expressions from found Notes
5834        using `gatherArticulations` and `gatherExpressions`.
5835
5836        If `transferGroupsToPitches` is True, and group defined on the source
5837        elements Groups object will be transferred to the Pitch
5838        objects contained in the resulting Chord.
5839
5840        The resulting Stream, if not in-place, can also gather
5841        additional objects by placing class names in the `collect` list.
5842        By default, TimeSignature and KeySignature objects are collected.
5843        '''
5844        if not inPlace:  # make a copy
5845            # since we do not return Scores, this probably should always be
5846            # a Stream
5847            # returnObj = Stream()
5848            # returnObj = self.__class__()  # for output
5849            returnObj = self.coreCopyAsDerivation('makeChords')
5850        else:
5851            returnObj = self
5852
5853        def dealWithSubNotes(chordLength, noteList):
5854            # environLocal.printDebug(['creating chord from noteList',
5855            #   noteList, 'inPlace', inPlace])
5856            c = chord.Chord()
5857            c.duration.quarterLength = chordLength
5858            # these are references, not copies, for now
5859            tempComponents = []
5860            for n in noteList:
5861                if n.isChord:
5862                    cSub = list(n)
5863                else:
5864                    cSub = [n]
5865
5866                if transferGroupsToPitches and n.groups:
5867                    for comp in cSub:
5868                        for g in n.groups:
5869                            comp.pitch.groups.append(g)
5870
5871                for comp in cSub:
5872                    tempComponents.append(comp)
5873
5874            c.pitches = [comp.pitch for comp in tempComponents]
5875            for comp in tempComponents:
5876                if comp.tie is not None:
5877                    c.setTie(comp.tie.type, comp.pitch)
5878
5879            if gatherArticulations:
5880                for n in noteList:
5881                    c.articulations += n.articulations
5882            if gatherExpressions:
5883                for n in noteList:
5884                    c.expressions += n.expressions
5885            # always remove all the previous elements
5886            for n in noteList:
5887                returnObj.remove(n)
5888            # remove all rests found in source
5889            for r in list(returnObj.getElementsByClass('Rest')):
5890                returnObj.remove(r)
5891
5892            if removeRedundantPitches:
5893                removedPitches = c.removeRedundantPitches(inPlace=True)
5894
5895                if transferGroupsToPitches:
5896                    for rem_p, cn in itertools.product(removedPitches, c):
5897                        if cn.pitch.nameWithOctave == rem_p.nameWithOctave:
5898                            # print(cn.pitch, rem_p)
5899                            # print(cn.pitch.groups, rem_p.groups)
5900                            cn.pitch.groups.extend(rem_p.groups)
5901            return c
5902        # environLocal.printDebug(['makeChords():',
5903        #              'transferGroupsToPitches', transferGroupsToPitches])
5904
5905        if returnObj.hasMeasures():
5906            # call on component measures
5907            for m in returnObj.getElementsByClass('Measure'):
5908                # offset values are not relative to measure; need to
5909                # shift by each measure's offset
5910                m.makeChords(
5911                    minimumWindowSize=minimumWindowSize,
5912                    includePostWindow=includePostWindow,
5913                    removeRedundantPitches=removeRedundantPitches,
5914                    gatherArticulations=gatherArticulations,
5915                    gatherExpressions=gatherExpressions,
5916                    transferGroupsToPitches=transferGroupsToPitches,
5917                    inPlace=True,
5918                    makeRests=makeRests
5919                )
5920            return returnObj  # exit
5921
5922        if returnObj.hasPartLikeStreams():
5923            # must get Streams, not Parts here
5924            for p in returnObj.getElementsByClass('Stream'):
5925                p.makeChords(
5926                    minimumWindowSize=minimumWindowSize,
5927                    includePostWindow=includePostWindow,
5928                    removeRedundantPitches=removeRedundantPitches,
5929                    gatherArticulations=gatherArticulations,
5930                    gatherExpressions=gatherExpressions,
5931                    transferGroupsToPitches=transferGroupsToPitches,
5932                    inPlace=True,
5933                    makeRests=makeRests
5934                )
5935            return returnObj  # exit
5936
5937        # TODO: gather lyrics as an option
5938        # define classes that are gathered; assume they have pitches
5939        # matchClasses = ['Note', 'Chord', 'Rest']
5940        matchClasses = ['Note', 'Chord']
5941        o = 0.0  # start at zero
5942        oTerminate = returnObj.highestOffset
5943
5944        # get temporary boundaries for making rests
5945        preHighestTime = returnObj.highestTime
5946        preLowestOffset = returnObj.lowestOffset
5947        # environLocal.printDebug(['got preLowest, preHighest', preLowestOffset, preHighestTime])
5948        if useExactOffsets is False:
5949            while True:  # TODO: Remove while True always...
5950                # get all notes within the start and the min window size
5951                oStart = o
5952                oEnd = oStart + minimumWindowSize
5953                subNotes = list(returnObj.getElementsByOffset(
5954                    oStart,
5955                    oEnd,
5956                    includeEndBoundary=False,
5957                    mustFinishInSpan=False,
5958                    mustBeginInSpan=True
5959                ).getElementsByClass(matchClasses))  # get once for speed
5960                # environLocal.printDebug(['subNotes', subNotes])
5961                qlMax: Optional[float] = None
5962                # get the max duration found from within the window
5963                if subNotes:
5964                    # get largest duration, use for duration of Chord, next span
5965                    qlMax = max([n.quarterLength for n in subNotes])
5966
5967                # if the max duration found in the window is greater than the min
5968                # window size, it is possible that there are notes that will not
5969                # be gathered; those starting at the end of this window but before
5970                # the max found duration (as that will become the start of the next
5971                # window
5972                # so: if ql > min window, gather notes between
5973                # oStart + minimumWindowSize and oStart + qlMax
5974                if (includePostWindow and qlMax is not None
5975                        and qlMax > minimumWindowSize):
5976                    subAdd = list(returnObj.getElementsByOffset(
5977                        oStart + minimumWindowSize,
5978                        oStart + qlMax,
5979                        includeEndBoundary=False,
5980                        mustFinishInSpan=False,
5981                        mustBeginInSpan=True
5982                    ).getElementsByClass(matchClasses))
5983                    # concatenate any additional notes found
5984                    subNotes += subAdd
5985
5986                # make subNotes into a chord
5987                if subNotes:
5988                    cOut = dealWithSubNotes(qlMax, subNotes)
5989                    # insert chord at start location
5990                    returnObj.coreInsert(o, cOut)
5991                # environLocal.printDebug(['len of returnObj', len(returnObj)])
5992                # shift offset to qlMax or minimumWindowSize
5993                if qlMax is not None and qlMax >= minimumWindowSize:
5994                    # update start offset to what was old boundary
5995                    # note: this assumes that the start of the longest duration
5996                    # was at oStart; it could have been between oStart and oEnd
5997                    o += qlMax
5998                else:
5999                    o += minimumWindowSize
6000                # end While loop conditions
6001                if o > oTerminate:
6002                    break
6003        else:  # useExactOffsets is True:
6004            onAndOffOffsets = self.flatten().notesAndRests.stream()._uniqueOffsetsAndEndTimes()
6005            # environLocal.printDebug(['makeChords: useExactOffsets=True;
6006            #   onAndOffOffsets:', onAndOffOffsets])
6007
6008            for i in range(len(onAndOffOffsets) - 1):
6009                # get all notes within the start and the min window size
6010                oStart = onAndOffOffsets[i]
6011                oEnd = onAndOffOffsets[i + 1]
6012                subNotes = list(returnObj.getElementsByOffset(
6013                    oStart,
6014                    oEnd,
6015                    includeEndBoundary=False,
6016                    mustFinishInSpan=False,
6017                    mustBeginInSpan=True
6018                ).getElementsByClass(matchClasses))
6019                # environLocal.printDebug(['subNotes', subNotes])
6020                # subNotes.show('t')
6021
6022                # make subNotes into a chord
6023                if subNotes:
6024                    cOut = dealWithSubNotes(oEnd - oStart, subNotes)
6025                    # insert chord at start location
6026                    returnObj.coreInsert(oStart, cOut)
6027
6028        # makeRests to fill any gaps produced by stripping
6029        # environLocal.printDebug(['pre makeRests show()'])
6030        returnObj.coreElementsChanged()
6031        if makeRests:
6032            returnObj.makeRests(
6033                refStreamOrTimeRange=(preLowestOffset, preHighestTime),
6034                fillGaps=True, inPlace=True)
6035        return returnObj
6036
6037    def chordify(
6038        self,
6039        *,
6040        addTies=True,
6041        addPartIdAsGroup=False,
6042        removeRedundantPitches=True,
6043        toSoundingPitch=True,
6044        copyPitches=True,
6045    ):
6046        # noinspection PyShadowingNames
6047        '''
6048        Create a chordal reduction of polyphonic music, where each
6049        change to a new pitch results in a new chord. If a Score or
6050        Part of Measures is provided, a Stream of Measures will be
6051        returned. If a flat Stream of notes, or a Score of such
6052        Streams is provided, no Measures will be returned.
6053
6054        If using chordify with chord symbols, ensure that the chord symbols
6055        have durations (by default the duration of a chord symbol object is 0, unlike
6056        a chord object). If harmony objects are not provided a duration, they
6057        will not be included in the chordified output pitches but may appear as chord symbol
6058        in notation on the score. To realize the chord symbol durations on a score, call
6059        :meth:`music21.harmony.realizeChordSymbolDurations` and pass in the score.
6060
6061        This functionality works by splitting all Durations in
6062        all parts, or if multi-part by all unique offsets. All
6063        simultaneous durations are then gathered into single chords.
6064
6065        If `addPartIdAsGroup` is True, all elements found in the
6066        Stream will have their source Part id added to the
6067        element's pitches' Group.  These groups names are useful
6068        for partially "de-chordifying" the output.  If the element chordifies to
6069        a :class:`~music21.chord.Chord` object, then the group will be found in each
6070        :class:`~music21.pitch.Pitch` element's .groups in Chord.pitches.  If the
6071        element chordifies to a single :class:`~music21.note.Note` then .pitch.groups
6072        will hold the group name.
6073
6074        The `addTies` parameter currently does not work for pitches in Chords.
6075
6076        If `toSoundingPitch` is True, all parts that define one or
6077        more transpositions will be transposed to sounding pitch before chordification.
6078        True by default.
6079
6080        >>> s = stream.Score()
6081        >>> p1 = stream.Part()
6082        >>> p1.id = 'part1'
6083        >>> p1.insert(4, note.Note('C#4'))
6084        >>> p1.insert(5.3, note.Rest())
6085        >>> p2 = stream.Part()
6086        >>> p2.id = 'part2'
6087        >>> p2.insert(2.12, note.Note('D-4', type='half'))
6088        >>> p2.insert(5.5, note.Rest())
6089        >>> s.insert(0, p1)
6090        >>> s.insert(0, p2)
6091        >>> s.show('text', addEndTimes=True)
6092        {0.0 - 6.3} <music21.stream.Part part1>
6093            {4.0 - 5.0} <music21.note.Note C#>
6094            {5.3 - 6.3} <music21.note.Rest quarter>
6095        {0.0 - 6.5} <music21.stream.Part part2>
6096            {2.12 - 4.12} <music21.note.Note D->
6097            {5.5 - 6.5} <music21.note.Rest quarter>
6098
6099        >>> cc = s.chordify()
6100
6101        >>> cc[3]
6102        <music21.chord.Chord C#4>
6103        >>> cc[3].duration.quarterLength
6104        Fraction(22, 25)
6105
6106        >>> cc.show('text', addEndTimes=True)
6107        {0.0 - 2.12} <music21.note.Rest 53/25ql>
6108        {2.12 - 4.0} <music21.chord.Chord D-4>
6109        {4.0 - 4.12} <music21.chord.Chord C#4 D-4>
6110        {4.12 - 5.0} <music21.chord.Chord C#4>
6111        {5.0 - 6.5} <music21.note.Rest dotted-quarter>
6112
6113        Here's how addPartIdAsGroup works:
6114
6115        >>> cc2 = s.chordify(addPartIdAsGroup=True)
6116        >>> cSharpDFlatChord = cc2[2]
6117        >>> for p in cSharpDFlatChord.pitches:
6118        ...     (str(p), p.groups)
6119        ('C#4', ['part1'])
6120        ('D-4', ['part2'])
6121
6122        >>> s = stream.Stream()
6123        >>> p1 = stream.Part()
6124        >>> p1.insert(0, harmony.ChordSymbol('Cm', quarterLength=4.0))
6125        >>> p1.insert(2, note.Note('C2'))
6126        >>> p1.insert(4, harmony.ChordSymbol('D', quarterLength=4.0))
6127        >>> p1.insert(7, note.Note('A2'))
6128        >>> s.insert(0, p1)
6129        >>> s.chordify().show('text')
6130        {0.0} <music21.chord.Chord C3 E-3 G3>
6131        {2.0} <music21.chord.Chord C2 C3 E-3 G3>
6132        {3.0} <music21.chord.Chord C3 E-3 G3>
6133        {4.0} <music21.chord.Chord D3 F#3 A3>
6134        {7.0} <music21.chord.Chord A2 D3 F#3 A3>
6135
6136        Note that :class:`~music21.harmony.ChordSymbol` objects can also be chordified:
6137
6138        >>> s = stream.Stream()
6139        >>> p2 = stream.Part()
6140        >>> p1 = stream.Part()
6141        >>> p2.insert(0, harmony.ChordSymbol('Cm', quarterLength=4.0))
6142        >>> p1.insert(2, note.Note('C2'))
6143        >>> p2.insert(4, harmony.ChordSymbol('D', quarterLength=4.0))
6144        >>> p1.insert(7, note.Note('A2'))
6145        >>> s.insert(0, p1)
6146        >>> s.insert(0, p2)
6147        >>> s.chordify().show('text')
6148        {0.0} <music21.chord.Chord C3 E-3 G3>
6149        {2.0} <music21.chord.Chord C2 C3 E-3 G3>
6150        {3.0} <music21.chord.Chord C3 E-3 G3>
6151        {4.0} <music21.chord.Chord D3 F#3 A3>
6152        {7.0} <music21.chord.Chord A2 D3 F#3 A3>
6153
6154        If addPartIdAsGroup is True, and there are redundant pitches,
6155        ensure that the merged pitch has both groups
6156
6157        >>> s = stream.Score()
6158        >>> p0 = stream.Part(id='p0')
6159        >>> p0.insert(0, note.Note('C4'))
6160        >>> p1 = stream.Part(id='p1')
6161        >>> p1.insert(0, note.Note('C4'))
6162        >>> s.insert(0, p0)
6163        >>> s.insert(0, p1)
6164        >>> s1 = s.chordify(addPartIdAsGroup=True)
6165        >>> c = s1.recurse().notes[0]
6166        >>> c
6167        <music21.chord.Chord C4>
6168        >>> c.pitches[0].groups
6169        ['p0', 'p1']
6170
6171        With copyPitches = False, then the original pitches are retained, which
6172        together with removeRedundantPitches=False can be a powerful tool for
6173        working back to the original score:
6174
6175        >>> n00 = note.Note('C4')
6176        >>> n01 = note.Note('E4')
6177        >>> n10 = note.Note('E4', type='half')
6178        >>> p0 = stream.Part(id='p0')
6179        >>> p1 = stream.Part(id='p1')
6180        >>> p0.append([n00, n01])
6181        >>> p1.append(n10)
6182        >>> s = stream.Score()
6183        >>> s.insert(0, p0)
6184        >>> s.insert(0, p1)
6185        >>> ss = s.chordify(removeRedundantPitches=False, copyPitches=False, addPartIdAsGroup=True)
6186        >>> ss.show('text')
6187        {0.0} <music21.chord.Chord C4 E4>
6188        {1.0} <music21.chord.Chord E4 E4>
6189
6190        >>> c1 = ss.recurse().notes[1]
6191        >>> for p in c1.pitches:
6192        ...     if 'p0' in p.groups:
6193        ...         p.step = 'G'  # make a complete triad
6194        >>> n01
6195        <music21.note.Note G>
6196
6197        Changes in v.5:
6198
6199        Runs a little faster for small scores and run a TON faster for big scores
6200        running in O(n) time not O(n^2)
6201
6202        no longer supported: displayTiedAccidentals=False,
6203
6204        Changes in v.6.3:
6205
6206        Added copyPitches
6207
6208        OMIT_FROM_DOCS
6209
6210        Test that chordifying works on a single stream.
6211
6212        >>> f2 = stream.Score()
6213        >>> f2.insert(0, metadata.Metadata())
6214        >>> f2.insert(0, note.Note('C4'))
6215        >>> f2.insert(0, note.Note('D#4'))
6216        >>> c = f2.chordify()
6217        >>> cn = c.notes
6218        >>> cn[0].pitches
6219        (<music21.pitch.Pitch C4>, <music21.pitch.Pitch D#4>)
6220        '''
6221        def chordifyOneMeasure(templateInner, streamToChordify):
6222            '''
6223            streamToChordify is either a Measure or a Score=MeasureSlice
6224            '''
6225            timespanTree = streamToChordify.asTimespans(classList=('GeneralNote',))
6226            allTimePoints = timespanTree.allTimePoints()
6227            if 0 not in allTimePoints:
6228                allTimePoints = (0,) + allTimePoints
6229
6230            for offset, endTime in zip(allTimePoints, allTimePoints[1:]):
6231                if isclose(offset, endTime, abs_tol=1e-7):
6232                    continue
6233                vert = timespanTree.getVerticalityAt(offset)
6234                quarterLength = endTime - offset
6235                if quarterLength < 0:  # pragma: no cover
6236                    environLocal.warn(
6237                        'Something is wrong with the verticality '
6238                        + f'in stream {templateInner!r}: {vert!r} '
6239                        + f'its endTime {endTime} is less than its offset {offset}'
6240                    )
6241
6242                chordOrRest = vert.makeElement(quarterLength,
6243                                               addTies=addTies,
6244                                               addPartIdAsGroup=addPartIdAsGroup,
6245                                               removeRedundantPitches=removeRedundantPitches,
6246                                               copyPitches=copyPitches,
6247                                               )
6248
6249                templateInner.coreInsert(opFrac(offset), chordOrRest)
6250            templateInner.coreElementsChanged()
6251            consolidateRests(templateInner)
6252
6253        def consolidateRests(templateInner):
6254            consecutiveRests = []
6255            for el in list(templateInner.getElementsByClass('GeneralNote')):
6256                if not isinstance(el, note.Rest):
6257                    removeConsecutiveRests(templateInner, consecutiveRests)
6258                    consecutiveRests = []
6259                else:
6260                    consecutiveRests.append(el)
6261            removeConsecutiveRests(templateInner, consecutiveRests)
6262
6263        def removeConsecutiveRests(templateInner, consecutiveRests):
6264            if len(consecutiveRests) < 2:
6265                return
6266            totalDuration = sum(r.duration.quarterLength for r in consecutiveRests)
6267            startOffset = templateInner.elementOffset(consecutiveRests[0])
6268            for r in consecutiveRests:
6269                templateInner.remove(r)
6270            rNew = note.Rest()
6271            rNew.duration.quarterLength = totalDuration
6272            templateInner.insert(startOffset, rNew)
6273
6274        # --------------------------------------
6275        if toSoundingPitch:
6276            # environLocal.printDebug(['at sounding pitch', allParts[0].atSoundingPitch])
6277            if (self.hasPartLikeStreams()
6278                     and self.getElementsByClass('Stream').first().atSoundingPitch is False):
6279                workObj = self.toSoundingPitch(inPlace=False)
6280            elif self.atSoundingPitch is False:
6281                workObj = self.toSoundingPitch(inPlace=False)
6282            else:
6283                workObj = self
6284        else:
6285            workObj = self
6286
6287        if self.hasPartLikeStreams():
6288            # use the measure boundaries of the first Part as a template.
6289            templateStream = workObj.getElementsByClass('Stream').first()
6290        else:
6291            templateStream = workObj
6292
6293        template = templateStream.template(fillWithRests=False,
6294                                           removeClasses=('GeneralNote',),
6295                                           retainVoices=False)
6296
6297        if template.hasMeasures():
6298            measureIterator = template.getElementsByClass('Measure')
6299            templateMeasure: 'Measure'
6300            for i, templateMeasure in enumerate(measureIterator):
6301                # measurePart is likely a Score (MeasureSlice), not a measure
6302                measurePart: 'Measure'
6303                measurePart = workObj.measure(i, collect=(), indicesNotNumbers=True)
6304                if measurePart is not None:
6305                    chordifyOneMeasure(templateMeasure, measurePart)
6306                else:
6307                    environLocal.warn(f'Malformed Part object, {workObj}, at measure index {i}')
6308        else:
6309            chordifyOneMeasure(template, workObj)
6310
6311        # accidental displayStatus needs to change.
6312        for p in template.pitches:
6313            if p.accidental is not None and p.accidental.displayType != 'even-tied':
6314                p.accidental.displayStatus = None
6315
6316        if (hasattr(workObj, 'metadata')
6317                and workObj.metadata is not None
6318                and workObj.hasPartLikeStreams() is True):
6319            template.insert(0, copy.deepcopy(workObj.metadata))
6320
6321        return template
6322
6323    def splitByClass(self, classObj, fx):
6324        # noinspection PyShadowingNames
6325        '''
6326        Given a stream, get all objects of type classObj and divide them into
6327        two new streams depending on the results of fx.
6328        Fx should be a lambda or other function on elements.
6329        All elements where fx returns True go in the first stream.
6330        All other elements are put in the second stream.
6331
6332        If classObj is None then all elements are returned.  ClassObj
6333        can also be a list of classes.
6334
6335        In this example, we will create 50 notes from midi note 30 (two
6336        octaves and a tritone below middle C) to midi note 80 (an octave
6337        and a minor sixth above middle C) and add them to a Stream.
6338        We then create a lambda function to split between those notes
6339        below middle C (midi note 60) and those above
6340        (google "lambda functions in Python" for more information on
6341        what these powerful tools are).
6342
6343
6344        >>> stream1 = stream.Stream()
6345        >>> for x in range(30, 81):
6346        ...     n = note.Note()
6347        ...     n.pitch.midi = x
6348        ...     stream1.append(n)
6349        >>> fx = lambda n: n.pitch.midi < 60
6350        >>> b, c = stream1.splitByClass(note.Note, fx)
6351
6352        Stream b now contains all the notes below middle C,
6353        that is, 30 notes, beginning with F#1 and ending with B3
6354        while Stream c has the 21 notes from C4 to A-5:
6355
6356        >>> len(b)
6357        30
6358        >>> (b[0].nameWithOctave, b[-1].nameWithOctave)
6359        ('F#1', 'B3')
6360        >>> len(c)
6361        21
6362        >>> (c[0].nameWithOctave, c[-1].nameWithOctave)
6363        ('C4', 'G#5')
6364        '''
6365        a = self.cloneEmpty(derivationMethod='splitByClass')
6366        b = self.cloneEmpty(derivationMethod='splitByClass')
6367        if classObj is not None:
6368            found = self.getElementsByClass(classObj).stream()
6369        else:
6370            found = self
6371        for e in found:
6372            if fx(e):
6373                a.coreInsert(found.elementOffset(e), e)  # provide an offset here
6374            else:
6375                b.coreInsert(found.elementOffset(e), e)
6376        for e in found._endElements:
6377            if fx(e):
6378                # a.storeAtEnd(e)
6379                a.coreStoreAtEnd(e)
6380            else:
6381                # b.storeAtEnd(e)
6382                b.coreStoreAtEnd(e)
6383        a.coreElementsChanged()
6384        b.coreElementsChanged()
6385        return a, b
6386
6387    def offsetMap(self, srcObj=None):
6388        '''
6389        Returns a list where each element is a NamedTuple
6390        consisting of the 'offset' of each element in a stream, the
6391        'endTime' (that is, the offset plus the duration) and the
6392        'element' itself.  Also contains a 'voiceIndex' entry which
6393        contains the voice number of the element, or None if there
6394        are no voices.
6395
6396        >>> n1 = note.Note(type='quarter')
6397        >>> c1 = clef.AltoClef()
6398        >>> n2 = note.Note(type='half')
6399        >>> s1 = stream.Stream()
6400        >>> s1.append([n1, c1, n2])
6401        >>> om = s1.offsetMap()
6402        >>> om[2].offset
6403        1.0
6404        >>> om[2].endTime
6405        3.0
6406        >>> om[2].element is n2
6407        True
6408        >>> om[2].voiceIndex
6409
6410
6411        Needed for makeMeasures and a few other places
6412
6413        The Stream source of elements is self by default,
6414        unless a `srcObj` is provided.
6415
6416
6417        >>> s = stream.Stream()
6418        >>> s.repeatAppend(note.Note(), 8)
6419        >>> for om in s.offsetMap():
6420        ...     om
6421        OffsetMap(element=<music21.note.Note C>, offset=0.0, endTime=1.0, voiceIndex=None)
6422        OffsetMap(element=<music21.note.Note C>, offset=1.0, endTime=2.0, voiceIndex=None)
6423        OffsetMap(element=<music21.note.Note C>, offset=2.0, endTime=3.0, voiceIndex=None)
6424        OffsetMap(element=<music21.note.Note C>, offset=3.0, endTime=4.0, voiceIndex=None)
6425        OffsetMap(element=<music21.note.Note C>, offset=4.0, endTime=5.0, voiceIndex=None)
6426        OffsetMap(element=<music21.note.Note C>, offset=5.0, endTime=6.0, voiceIndex=None)
6427        OffsetMap(element=<music21.note.Note C>, offset=6.0, endTime=7.0, voiceIndex=None)
6428        OffsetMap(element=<music21.note.Note C>, offset=7.0, endTime=8.0, voiceIndex=None)
6429        '''
6430        if srcObj is None:
6431            srcObj = self
6432        # assume that flat/sorted options will be set before processing
6433        offsetMap = []  # list of start, start+dur, element
6434        if srcObj.hasVoices():
6435            groups = []
6436            for i, v in enumerate(srcObj.voices):
6437                groups.append((v.flatten(), i))
6438            elsNotOfVoice = srcObj.getElementsNotOfClass('Voice')
6439            if len(elsNotOfVoice) > 0:
6440                groups.insert(0, (elsNotOfVoice, None))
6441        else:  # create a single collection
6442            groups = [(srcObj, None)]
6443        # environLocal.printDebug(['offsetMap', groups])
6444        for group, voiceIndex in groups:
6445            for e in group._elements:
6446                # do not include barlines
6447                if isinstance(e, bar.Barline):
6448                    continue
6449                dur = e.duration.quarterLength
6450                offset = group.elementOffset(e)
6451                endTime = opFrac(offset + dur)
6452                # NOTE: used to make a copy.copy of elements here;
6453                # this is not necessary b/c making deepcopy of entire Stream
6454                thisOffsetMap = _OffsetMap(e, offset, endTime, voiceIndex)
6455                # environLocal.printDebug(['offsetMap: thisOffsetMap', thisOffsetMap])
6456                offsetMap.append(thisOffsetMap)
6457                # offsetMap.append((offset, offset + dur, e, voiceIndex))
6458                # offsetMap.append([offset, offset + dur, copy.copy(e)])
6459        return offsetMap
6460
6461    def makeMeasures(
6462        self,
6463        meterStream=None,
6464        refStreamOrTimeRange=None,
6465        searchContext=False,
6466        innerBarline=None,
6467        finalBarline='final',
6468        bestClef=False,
6469        inPlace=False,
6470    ):
6471        '''
6472        Return a new stream (or if inPlace=True change in place) this
6473        Stream so that it has internal measures.
6474
6475        For more details, see :py:func:`~music21.stream.makeNotation.makeMeasures`.
6476        '''
6477        return makeNotation.makeMeasures(
6478            self,
6479            meterStream=meterStream,
6480            refStreamOrTimeRange=refStreamOrTimeRange,
6481            searchContext=searchContext,
6482            innerBarline=innerBarline,
6483            finalBarline=finalBarline,
6484            bestClef=bestClef,
6485            inPlace=inPlace,
6486        )
6487
6488    def makeRests(
6489        self,
6490        refStreamOrTimeRange=None,
6491        fillGaps=False,
6492        timeRangeFromBarDuration=False,
6493        inPlace=False,
6494        hideRests=False,
6495    ):
6496        '''
6497        Calls :py:func:`~music21.stream.makeNotation.makeRests`.
6498
6499        Changed in v.7, inPlace=False by default.
6500        '''
6501        return makeNotation.makeRests(
6502            self,
6503            refStreamOrTimeRange=refStreamOrTimeRange,
6504            fillGaps=fillGaps,
6505            timeRangeFromBarDuration=timeRangeFromBarDuration,
6506            inPlace=inPlace,
6507            hideRests=hideRests,
6508        )
6509
6510    def makeTies(self,
6511                 meterStream=None,
6512                 inPlace=False,
6513                 displayTiedAccidentals=False,
6514                 classFilterList=(note.GeneralNote,),
6515                 ):
6516        '''
6517        Calls :py:func:`~music21.stream.makeNotation.makeTies`.
6518
6519        Changed in v.4., inPlace=False by default.
6520        Added in v.7, `classFilterList`.
6521        '''
6522        return makeNotation.makeTies(
6523            self,
6524            meterStream=meterStream,
6525            inPlace=inPlace,
6526            displayTiedAccidentals=displayTiedAccidentals,
6527            classFilterList=classFilterList,
6528        )
6529
6530    def makeBeams(self, *, inPlace=False, setStemDirections=True, failOnNoTimeSignature=False):
6531        '''
6532        Return a new Stream, or modify the Stream in place, with beams applied to all
6533        notes.
6534
6535        See :py:func:`~music21.stream.makeNotation.makeBeams`.
6536
6537        New in v6.7 -- setStemDirections.
6538        New in v.7 -- failOnNoTimeSignature raises StreamException if no TimeSignature
6539        exists in the stream context from which to make measures.
6540        '''
6541        return makeNotation.makeBeams(
6542            self,
6543            inPlace=inPlace,
6544            setStemDirections=setStemDirections,
6545            failOnNoTimeSignature=failOnNoTimeSignature,
6546        )
6547
6548    def makeAccidentals(
6549        self,
6550        *,
6551        pitchPast: Optional[List[pitch.Pitch]] = None,
6552        pitchPastMeasure: Optional[List[pitch.Pitch]] = None,
6553        useKeySignature: Union[bool, key.KeySignature] = True,
6554        alteredPitches: Optional[List[pitch.Pitch]] = None,
6555        searchKeySignatureByContext: bool = False,
6556        cautionaryPitchClass: bool = True,
6557        cautionaryAll: bool = False,
6558        inPlace: bool = False,
6559        overrideStatus: bool = False,
6560        cautionaryNotImmediateRepeat: bool = True,
6561        tiePitchSet: Optional[Set[str]] = None
6562    ):
6563        '''
6564        A method to set and provide accidentals given various conditions and contexts.
6565
6566        `pitchPast` is a list of pitches preceding this pitch in this measure.
6567
6568        `pitchPastMeasure` is a list of pitches preceding this pitch but in a previous measure.
6569
6570
6571        If `useKeySignature` is True, a :class:`~music21.key.KeySignature` will be searched
6572        for in this Stream or this Stream's defined contexts. An alternative KeySignature
6573        can be supplied with this object and used for temporary pitch processing.
6574
6575        If `alteredPitches` is a list of modified pitches (Pitches with Accidentals) that
6576        can be directly supplied to Accidental processing. These are the same values obtained
6577        from a :class:`music21.key.KeySignature` object using the
6578        :attr:`~music21.key.KeySignature.alteredPitches` property.
6579
6580        If `cautionaryPitchClass` is True, comparisons to past accidentals are made regardless
6581        of register. That is, if a past sharp is found two octaves above a present natural,
6582        a natural sign is still displayed.
6583
6584        If `cautionaryAll` is True, all accidentals are shown.
6585
6586        If `overrideStatus` is True, this method will ignore any current `displayStatus` setting
6587        found on the Accidental. By default this does not happen. If `displayStatus` is set to
6588        None, the Accidental's `displayStatus` is set.
6589
6590        If `cautionaryNotImmediateRepeat` is True, cautionary accidentals will be displayed for
6591        an altered pitch even if that pitch had already been displayed as altered.
6592
6593        If `tiePitchSet` is not None it should be a set of `.nameWithOctave` strings
6594        to determine whether following accidentals should be shown because the last
6595        note of the same pitch had a start or continue tie.
6596
6597        If `searchKeySignatureByContext` is True then keySignatures from the context of the
6598        stream will be used if none found.
6599
6600        The :meth:`~music21.pitch.Pitch.updateAccidentalDisplay` method is used to determine if
6601        an accidental is necessary.
6602
6603        This will assume that the complete Stream is the context of evaluation. For smaller context
6604        ranges, call this on Measure objects.
6605
6606        If `inPlace` is True, this is done in-place; if `inPlace` is False,
6607        this returns a modified deep copy.
6608
6609        Changed in v.6: does not return anything if inPlace is True.
6610        Changed in v.7: default inPlace is False
6611
6612        All arguments are keyword only.
6613        '''
6614        if not inPlace:  # make a copy
6615            returnObj = self.coreCopyAsDerivation('makeAccidentals')
6616        else:
6617            returnObj = self
6618
6619        # need to reset these lists unless values explicitly provided
6620        if pitchPast is None:
6621            pitchPast = []
6622        if pitchPastMeasure is None:
6623            pitchPastMeasure = []
6624        # see if there is any key signatures to add to altered pitches
6625        if alteredPitches is None:
6626            alteredPitches = []
6627        addAlteredPitches: List[pitch.Pitch] = []
6628        if isinstance(useKeySignature, key.KeySignature):
6629            addAlteredPitches = useKeySignature.alteredPitches
6630        elif useKeySignature is True:  # get from defined contexts
6631            # will search local, then activeSite
6632            ksIter = None
6633            if searchKeySignatureByContext:
6634                ks = self.getContextByClass(key.KeySignature)
6635                if ks is not None:
6636                    ksIter = [ks]
6637            else:
6638                ksIter = self.getElementsByClass(key.KeySignature)
6639            if ksIter:
6640                # assume we want the first found; in some cases it is possible
6641                # that this may not be true
6642                addAlteredPitches = ksIter[0].alteredPitches
6643        alteredPitches += addAlteredPitches
6644        # environLocal.printDebug(['processing makeAccidentals() with alteredPitches:',
6645        #   alteredPitches])
6646
6647        # need to move through notes in order
6648        # recurse to capture notes in substreams: https://github.com/cuthbertLab/music21/issues/577
6649        noteIterator = returnObj.recurse().notesAndRests
6650
6651        # environLocal.printDebug(['alteredPitches', alteredPitches])
6652        # environLocal.printDebug(['pitchPast', pitchPast])
6653
6654        if tiePitchSet is None:
6655            tiePitchSet = set()
6656
6657        last_measure: Optional[Measure] = None
6658
6659        for e in noteIterator:
6660            if e.activeSite is not None and e.activeSite.isMeasure:
6661                if last_measure is not None and e.activeSite is not last_measure:
6662                    # New measure encountered: move pitchPast to
6663                    # pitchPastMeasure and clear pitchPast
6664                    pitchPastMeasure = pitchPast[:]
6665                    pitchPast = []
6666                last_measure = e.activeSite
6667            if isinstance(e, note.Note):
6668                if e.pitch.nameWithOctave in tiePitchSet:
6669                    lastNoteWasTied = True
6670                else:
6671                    lastNoteWasTied = False
6672
6673                e.pitch.updateAccidentalDisplay(
6674                    pitchPast=pitchPast,
6675                    pitchPastMeasure=pitchPastMeasure,
6676                    alteredPitches=alteredPitches,
6677                    cautionaryPitchClass=cautionaryPitchClass,
6678                    cautionaryAll=cautionaryAll,
6679                    overrideStatus=overrideStatus,
6680                    cautionaryNotImmediateRepeat=cautionaryNotImmediateRepeat,
6681                    lastNoteWasTied=lastNoteWasTied)
6682                pitchPast.append(e.pitch)
6683
6684                tiePitchSet.clear()
6685                if e.tie is not None and e.tie.type != 'stop':
6686                    tiePitchSet.add(e.pitch.nameWithOctave)
6687
6688            elif isinstance(e, chord.Chord):
6689                # add all chord elements to past first
6690                # when reading a chord, this will apply an accidental
6691                # if pitches in the chord suggest an accidental
6692                seenPitchNames = set()
6693
6694                for n in list(e):
6695                    p = n.pitch
6696                    if p.nameWithOctave in tiePitchSet:
6697                        lastNoteWasTied = True
6698                    else:
6699                        lastNoteWasTied = False
6700
6701                    p.updateAccidentalDisplay(
6702                        pitchPast=pitchPast,
6703                        pitchPastMeasure=pitchPastMeasure,
6704                        alteredPitches=alteredPitches,
6705                        cautionaryPitchClass=cautionaryPitchClass,
6706                        cautionaryAll=cautionaryAll,
6707                        overrideStatus=overrideStatus,
6708                        cautionaryNotImmediateRepeat=cautionaryNotImmediateRepeat,
6709                        lastNoteWasTied=lastNoteWasTied)
6710
6711                    if n.tie is not None and n.tie.type != 'stop':
6712                        seenPitchNames.add(p.nameWithOctave)
6713
6714                tiePitchSet.clear()
6715                for pName in seenPitchNames:
6716                    tiePitchSet.add(pName)
6717
6718                pitchPast += e.pitches
6719            else:
6720                tiePitchSet.clear()
6721
6722        returnObj.streamStatus.accidentals = True
6723
6724        if not inPlace:
6725            return returnObj
6726
6727    def haveAccidentalsBeenMade(self):
6728        # could be called: hasAccidentalDisplayStatusSet
6729        '''
6730        If Accidentals.displayStatus is None for all
6731        contained pitches, it as assumed that accidentals
6732        have not been set for display and/or makeAccidentals
6733        has not been run. If any Accidental has displayStatus
6734        other than None, this method returns True, regardless
6735        of if makeAccidentals has actually been run.
6736        '''
6737        return self.streamStatus.accidentals
6738
6739    def makeNotation(self,
6740                     *,
6741                     meterStream=None,
6742                     refStreamOrTimeRange=None,
6743                     inPlace=False,
6744                     bestClef=False,
6745                     pitchPast: Optional[List[pitch.Pitch]] = None,
6746                     pitchPastMeasure: Optional[List[pitch.Pitch]] = None,
6747                     useKeySignature: Union[bool, key.KeySignature] = True,
6748                     alteredPitches: Optional[List[pitch.Pitch]] = None,
6749                     cautionaryPitchClass: bool = True,
6750                     cautionaryAll: bool = False,
6751                     overrideStatus: bool = False,
6752                     cautionaryNotImmediateRepeat: bool = True,
6753                     tiePitchSet: Optional[Set[str]] = None
6754                     ):
6755        '''
6756        This method calls a sequence of Stream methods on this Stream to prepare
6757        notation, including creating voices for overlapped regions, Measures
6758        if necessary, creating ties, beams, accidentals, and tuplet brackets.
6759
6760        If `inPlace` is True, this is done in-place (changed in v7 -- returns None);
6761        if `inPlace` is False, this returns a modified deep copy.
6762
6763        The following additional parameters are documented on
6764        :meth:`~music21.stream.base.makeAccidentals`::
6765
6766            pitchPast
6767            pitchPastMeasure
6768            useKeySignature
6769            alteredPitches
6770            cautionaryPitchClass
6771            cautionaryAll
6772            overrideStatus
6773            cautionaryNotImmediateRepeat
6774            tiePitchSet
6775
6776
6777        >>> s = stream.Stream()
6778        >>> n = note.Note('g')
6779        >>> n.quarterLength = 1.5
6780        >>> s.repeatAppend(n, 10)
6781        >>> sMeasures = s.makeNotation()
6782        >>> len(sMeasures.getElementsByClass('Measure'))
6783        4
6784        >>> sMeasures.getElementsByClass('Measure').last().rightBarline.type
6785        'final'
6786        '''
6787        # determine what is the object to work on first
6788        if inPlace:
6789            returnStream = self
6790        else:
6791            returnStream = self.coreCopyAsDerivation('makeNotation')
6792
6793        # if 'finalBarline' in subroutineKeywords:
6794        #     lastBarlineType = subroutineKeywords['finalBarline']
6795        # else:
6796        #     lastBarlineType = 'final'
6797
6798        # retrieve necessary spanners; insert only if making a copy
6799        returnStream.coreGatherMissingSpanners(
6800            insert=not inPlace,
6801            # definitely do NOT put a constrainingSpannerBundle constraint
6802        )
6803        # only use inPlace arg on first usage
6804        if not self.hasMeasures():
6805            # only try to make voices if no Measures are defined
6806            returnStream.makeVoices(inPlace=True, fillGaps=True)
6807            # if this is not inPlace, it will return a newStream; if
6808            # inPlace, this returns None
6809            # use inPlace=True, as already established above
6810            returnStream.makeMeasures(
6811                meterStream=meterStream,
6812                refStreamOrTimeRange=refStreamOrTimeRange,
6813                inPlace=True,
6814                bestClef=bestClef)
6815
6816        measureStream = returnStream.getElementsByClass('Measure').stream()
6817        # environLocal.printDebug(['Stream.makeNotation(): post makeMeasures,
6818        #   length', len(returnStream)])
6819        if not measureStream:
6820            raise StreamException(
6821                f'no measures found in stream with {len(self)} elements')
6822
6823        # for now, calling makeAccidentals once per measures
6824        # pitches from last measure are passed
6825        # this needs to be called before makeTies
6826        # note that this functionality is also placed in Part
6827        if not measureStream.streamStatus.accidentals:
6828            makeNotation.makeAccidentalsInMeasureStream(
6829                measureStream,
6830                pitchPast=pitchPast,
6831                pitchPastMeasure=pitchPastMeasure,
6832                useKeySignature=useKeySignature,
6833                alteredPitches=alteredPitches,
6834                cautionaryPitchClass=cautionaryPitchClass,
6835                cautionaryAll=cautionaryAll,
6836                overrideStatus=overrideStatus,
6837                cautionaryNotImmediateRepeat=cautionaryNotImmediateRepeat,
6838                tiePitchSet=tiePitchSet)
6839
6840        measureStream.makeTies(meterStream, inPlace=True)
6841
6842        # measureStream.makeBeams(inPlace=True)
6843        if not measureStream.streamStatus.beams:
6844            try:
6845                measureStream.makeBeams(inPlace=True)
6846            except meter.MeterException as me:
6847                environLocal.warn(['skipping makeBeams exception', me])
6848
6849        # note: this needs to be after makeBeams, as placing this before
6850        # makeBeams was causing the duration's tuplet to lose its type setting
6851        # check for tuplet brackets one measure at a time
6852        # this means that they will never extend beyond one measure
6853        for m in measureStream:
6854            if not m.streamStatus.tuplets:
6855                makeNotation.makeTupletBrackets(m, inPlace=True)
6856
6857        if not inPlace:
6858            return returnStream
6859
6860    def extendDuration(self, objName, *, inPlace=False):
6861        '''
6862        Given a Stream and an object class name, go through the Stream
6863        and find each instance of the desired object. The time between
6864        adjacent objects is then assigned to the duration of each object.
6865        The last duration of the last object is assigned to extend to the
6866        end of the Stream.
6867
6868        If `inPlace` is True, this is done in-place; if `inPlace` is
6869        False, this returns a modified deep copy.
6870
6871        >>> stream1 = stream.Stream()
6872        >>> n = note.Note(type='quarter')
6873        >>> n.duration.quarterLength
6874        1.0
6875        >>> stream1.repeatInsert(n, [0, 10, 20, 30, 40])
6876
6877        >>> dyn = dynamics.Dynamic('ff')
6878        >>> stream1.insert(15, dyn)
6879        >>> stream1[-1].offset  # offset of last element
6880        40.0
6881        >>> stream1.duration.quarterLength  # total duration
6882        41.0
6883        >>> len(stream1)
6884        6
6885
6886        >>> stream2 = stream1.flatten().extendDuration(note.GeneralNote, inPlace=False)
6887        >>> len(stream2)
6888        6
6889        >>> stream2[0].duration.quarterLength
6890        10.0
6891
6892        The Dynamic does not affect the second note:
6893
6894        >>> stream2[1].offset
6895        10.0
6896        >>> stream2[1].duration.quarterLength
6897        10.0
6898
6899        >>> stream2[-1].duration.quarterLength  # or extend to end of stream
6900        1.0
6901        >>> stream2.duration.quarterLength
6902        41.0
6903        >>> stream2[-1].offset
6904        40.0
6905        '''
6906
6907        if not inPlace:  # make a copy
6908            returnObj = self.coreCopyAsDerivation('extendDuration')
6909        else:
6910            returnObj = self
6911
6912        qLenTotal = returnObj.duration.quarterLength
6913        elements = list(returnObj.getElementsByClass(objName))
6914
6915        # print(elements[-1], qLenTotal, elements[-1].duration)
6916        # print(_MOD, elements)
6917        for i in range(len(elements) - 1):
6918            # print(i, len(elements))
6919            span = returnObj.elementOffset(elements[i + 1]) - returnObj.elementOffset(elements[i])
6920            elements[i].duration.quarterLength = span
6921
6922        # handle last element
6923        # print(elements[-1], qLenTotal, elements[-1].duration)
6924        if elements:
6925            elements[-1].duration.quarterLength = (qLenTotal
6926                                                   - returnObj.elementOffset(elements[-1]))
6927            # print(elements[-1], elements[-1].duration)
6928        if not inPlace:
6929            return returnObj
6930
6931    @common.deprecated('v7', 'v8', 'call extendDurations() and getElementsByClass() separately')
6932    def extendDurationAndGetBoundaries(self, objName, *, inPlace=False):  # pragma: no cover
6933        '''
6934        DEPRECATED in v.7 -- to be removed in v.8
6935
6936        Extend the Duration of elements specified by objName;
6937        then, collect a dictionary for every matched element of objName class,
6938        where the matched element is the value and the key is the (start, end) offset value.
6939
6940        >>> from pprint import pprint as pp
6941        >>> s = stream.Stream()
6942        >>> s.insert(3, dynamics.Dynamic('mf'))
6943        >>> s.insert(7, dynamics.Dynamic('f'))
6944        >>> s.insert(12, dynamics.Dynamic('ff'))
6945        >>> #_DOCS_SHOW pp(s.extendDurationAndGetBoundaries('Dynamic'))
6946        {(3.0, 7.0): <music21.dynamics.Dynamic mf>,
6947         (7.0, 12.0): <music21.dynamics.Dynamic f>,
6948         (12.0, 12.0): <music21.dynamics.Dynamic ff>}
6949
6950
6951        TODO: only allow inPlace=True or delete or something, can't return two different things
6952        '''
6953        if not inPlace:  # make a copy
6954            returnObj = copy.deepcopy(self)
6955        else:
6956            returnObj = self
6957        returnObj.extendDuration(objName, inPlace=True)
6958        # TODO: use iteration.
6959        elements = returnObj.getElementsByClass(objName)
6960        boundaries = {}
6961        if not elements:
6962            raise StreamException('no elements of this class defined in this Stream')
6963
6964        for e in elements:
6965            start = returnObj.elementOffset(e)
6966            end = start + e.duration.quarterLength
6967            boundaries[(start, end)] = e
6968        return boundaries
6969
6970    def stripTies(
6971        self,
6972        inPlace=False,
6973        matchByPitch=True
6974    ):
6975        # noinspection PyShadowingNames
6976        '''
6977        Find all notes that are tied; remove all tied notes,
6978        then make the first of the tied notes have a duration
6979        equal to that of all tied constituents. Lastly,
6980        remove the formerly-tied notes.
6981
6982        This method can be used on Stream and Stream subclasses.
6983        When used on a stream containing Part-like substreams, as with many scores,
6984        :class:`~music21.stream.Part`, :class:`~music21.stream.Measure`, and other
6985        Stream subclasses are retained.
6986
6987        `inPlace` controls whether the input stream is modified or whether a deep copy
6988        is made. (New in v7, to conform to the rest of music21, `inPlace=True` returns `None`.)
6989
6990        Presently, this only works if tied notes are sequential in the same voice; ultimately
6991        this will need to look at .to and .from attributes (if they exist)
6992
6993        >>> a = stream.Stream()
6994        >>> n = note.Note()
6995        >>> n.quarterLength = 6
6996        >>> a.append(n)
6997        >>> m = a.makeMeasures()
6998        >>> m.makeTies(inPlace=True)
6999        >>> len(m.flatten().notes)
7000        2
7001
7002        >>> m = m.stripTies()
7003        >>> len(m.flatten().notes)
7004        1
7005
7006        In cases where notes are manipulated after initial tie creation,
7007        some chord members might lack ties. This will not prevent merging the tied notes
7008        if all the pitches match, and `matchByPitch=True` (default):
7009
7010        >>> c1 = chord.Chord('C4 E4')
7011        >>> c1.tie = tie.Tie('start')
7012
7013        >>> c2 = chord.Chord('C4 E4')
7014        >>> c2.tie = tie.Tie('stop')
7015
7016        >>> m = stream.Measure()
7017        >>> m.append([c1, c2])
7018
7019        >>> c1.add(note.Note('G4'))
7020        >>> c2.add(note.Note('G4'))
7021
7022        >>> c2.notes[-1].tie is None
7023        True
7024
7025        >>> strippedPitchMatching = m.stripTies()
7026        >>> len(strippedPitchMatching.flatten().notes)
7027        1
7028
7029        This can be prevented with `matchByPitch=False`, in which case every note,
7030        including each chord member, must have stop and/or continue tie types,
7031        which was not the case above:
7032
7033        >>> strippedMixedTieTypes = m.stripTies(matchByPitch=False)
7034        >>> len(strippedMixedTieTypes.flatten().notes)
7035        2
7036
7037        >>> c2.notes[0].tie = tie.Tie('stop')
7038        >>> c2.notes[1].tie = tie.Tie('stop')
7039        >>> c2.notes[2].tie = tie.Tie('stop')
7040        >>> strippedUniformTieTypes = m.stripTies(matchByPitch=False)
7041        >>> len(strippedUniformTieTypes.flatten().notes)
7042        1
7043
7044        Notice the matching happens even after altering the pitches:
7045
7046        >>> c3 = c2.transpose(6)
7047        >>> otherM = stream.Measure([c1, c3])
7048        >>> strippedTransposed = otherM.stripTies(matchByPitch=False)
7049        >>> len(strippedTransposed.flatten().notes)
7050        1
7051
7052        Changed in v.7 -- `matchByPitch` defaults True, and the following
7053        behavior defined regarding chords with a tie type "continue":
7054
7055        >>> c1.notes[0].tie = tie.Tie('continue')
7056        >>> c1.notes[1].tie = tie.Tie('start')
7057        >>> c1.notes[2].tie = tie.Tie('start')
7058
7059        Continue is accepted here as an ersatz-start:
7060
7061        >>> stripped1 = m.stripTies(matchByPitch=True)
7062        >>> len(stripped1.flatten().notes)
7063        1
7064
7065        But prepend an element so that it's considered as a tie continuation:
7066
7067        >>> c0 = chord.Chord('C4 E4 G4')
7068        >>> c0.tie = tie.Tie('start')
7069        >>> m2 = stream.Measure()
7070        >>> m2.append([c0, c1, c2])
7071
7072        Now the mixed tie types on c1 will only be connected to c2
7073        on the permissive option (`matchByPitch=True`):
7074
7075        >>> stripped2 = m2.stripTies(matchByPitch=True)
7076        >>> stripped2.elements
7077        (<music21.chord.Chord C4 E4 G4>,)
7078
7079        >>> stripped3 = m2.stripTies(matchByPitch=False)
7080        >>> stripped3.elements
7081        (<music21.chord.Chord C4 E4 G4>,
7082         <music21.chord.Chord C4 E4 G4>,
7083         <music21.chord.Chord C4 E4 G4>)
7084
7085        Now correct the tie types on c1 and try the strict option:
7086
7087        >>> c1.notes[0].tie = tie.Tie('continue')
7088        >>> c1.notes[1].tie = tie.Tie('continue')
7089        >>> c1.notes[2].tie = tie.Tie('continue')
7090        >>> stripped4 = m2.stripTies(matchByPitch=False)
7091        >>> stripped4.elements
7092        (<music21.chord.Chord C4 E4 G4>,)
7093
7094        Now replace the first element with just a single C4 note.
7095        The following chords will be merged with each other, but not with the single
7096        note, even on `matchByPitch=False`.
7097        (`matchByPitch=False` is permissive about pitch but strict about cardinality.)
7098
7099        >>> newC = note.Note('C4')
7100        >>> newC.tie = tie.Tie('start')
7101        >>> m2.replace(c0, newC)
7102        >>> stripped5 = m2.stripTies(matchByPitch=False)
7103        >>> stripped5.elements
7104        (<music21.note.Note C>, <music21.chord.Chord C4 E4 G4>)
7105        '''
7106        # environLocal.printDebug(['calling stripTies'])
7107        if not inPlace:  # make a copy
7108            returnObj = self.coreCopyAsDerivation('stripTies')
7109        else:
7110            returnObj = self
7111
7112        # Clear existing beaming because notes may be deleted at any level of hierarchy
7113        returnObj.streamStatus.beams = False
7114
7115        if returnObj.hasPartLikeStreams():
7116            # part-like does not necessarily mean that the next level down is a stream.Part
7117            # object or that this is a stream.Score object, so do not substitute
7118            # returnObj.parts for this...
7119            for p in returnObj.getElementsByClass('Stream'):
7120                # already copied if necessary; edit in place
7121                p.stripTies(inPlace=True, matchByPitch=matchByPitch)
7122            if not inPlace:
7123                return returnObj
7124            else:
7125                return  # exit
7126
7127        if returnObj.hasVoices():
7128            for v in returnObj.voices:
7129                # already copied if necessary; edit in place
7130                v.stripTies(inPlace=True, matchByPitch=matchByPitch)
7131            if not inPlace:
7132                return returnObj
7133            else:
7134                return  # exit
7135
7136        # need to just get .notesAndRests, as there may be other objects in the Measure
7137        # that come before the first Note, such as a SystemLayout object
7138        f = returnObj.flatten()
7139        notes = f.notesAndRests.stream()
7140
7141        posConnected = []  # temporary storage for index of tied notes
7142        posDelete = []  # store deletions to be processed later
7143
7144        def updateEndMatch(nInner) -> bool:
7145            '''
7146            updateEndMatch based on nList, iLast, matchByPitch, etc.
7147            '''
7148            # 2 cases before matchByPitch=False returns early.
7149
7150            # Case 1: nInner is not a chord, and it has a stop tie
7151            # can't trust chords, which only tell if SOME member has a tie
7152            # matchByPitch does not matter here
7153            # https://github.com/cuthbertLab/music21/issues/502
7154            if (hasattr(nInner, 'tie')
7155                    and not isinstance(nInner, chord.Chord)
7156                    and nInner.tie is not None
7157                    and nInner.tie.type == 'stop'):
7158                return True
7159            # Case 2: matchByPitch=False and all chord members have a stop tie
7160            # and checking cardinality passes (don't match chords to single notes)
7161            if (hasattr(nInner, 'tie')
7162                    and not matchByPitch
7163                    and isinstance(nInner, chord.Chord)
7164                    and None not in [inner_p.tie for inner_p in nInner.notes]
7165                    and {inner_p.tie.type for inner_p in nInner.notes} == {'stop'}
7166                    and nLast is not None and len(nLast.pitches) == len(nInner.pitches)):
7167                return True
7168
7169            # Now, matchByPitch
7170            # if we cannot find a stop tie, see if last note was connected
7171            # and this and the last note are the same pitch; this assumes
7172            # that connected and same pitch value is tied; this is not
7173            # frequently the case
7174            elif not matchByPitch:
7175                return False
7176
7177            # find out if the last index is in position connected
7178            # if the pitches are the same for each note
7179            if (nLast is not None
7180                    and iLast in posConnected
7181                    and hasattr(nLast, 'pitch')
7182                    and hasattr(nInner, 'pitch')
7183                    # before doing pitch comparison, need to
7184                    # make sure we're not comparing a Note to a Chord
7185                    and 'Chord' not in nLast.classes
7186                    and 'Chord' not in nInner.classes
7187                    and nLast.pitch == nInner.pitch):
7188                return True
7189            # looking for two chords of equal size
7190            if (nLast is not None
7191                    and not isinstance(nInner, note.Note)
7192                    and iLast in posConnected
7193                    and hasattr(nLast, 'pitches')
7194                    and hasattr(nInner, 'pitches')):
7195                if len(nLast.pitches) != len(nInner.pitches):
7196                    return False
7197
7198                for pitchIndex in range(len(nLast.pitches)):
7199                    if nLast.pitches[pitchIndex] != nInner.pitches[pitchIndex]:
7200                        return False
7201                return True
7202
7203            return False
7204
7205        def allTiesAreContinue(nr: note.NotRest) -> bool:
7206            if nr.tie is None:  # pragma: no cover
7207                return False
7208            if nr.tie.type != 'continue':
7209                return False
7210            # check every chord member, since tie type "continue" on a chord
7211            # only indicates that SOME member is tie-continue.
7212            if 'Chord' in nr.classes:
7213                for innerN in nr.notes:
7214                    if innerN.tie is None:
7215                        return False
7216                    if innerN.tie.type != 'continue':
7217                        return False
7218            return True
7219
7220        for i in range(len(notes)):
7221            endMatch = None  # can be True, False, or None
7222            n = notes[i]
7223            if i > 0:  # get i and n for the previous value
7224                iLast = i - 1
7225                nLast = notes[iLast]
7226            else:
7227                iLast = None
7228                nLast = None
7229
7230            # see if we have a tie and it is started
7231            # a start typed tie may not be a true start tie
7232            if (hasattr(n, 'tie')
7233                    and n.tie is not None
7234                    and n.tie.type == 'start'):
7235                # find a true start, add to known connected positions
7236                if iLast is None or iLast not in posConnected:
7237                    posConnected = [i]  # reset list with start
7238                # find a continuation: the last note was a tie
7239                # start and this note is a tie start (this may happen)
7240                elif iLast in posConnected:
7241                    posConnected.append(i)
7242                # a connection has been started or continued, so no endMatch
7243                endMatch = False
7244
7245            # a continue may or may not imply a connection
7246            elif (hasattr(n, 'tie')
7247                    and n.tie is not None
7248                    and n.tie.type == 'continue'):
7249                # is this actually a start?
7250                if not posConnected:
7251                    posConnected.append(i)
7252                    endMatch = False
7253                elif matchByPitch:
7254                    # try to match pitch against nLast
7255                    # updateEndMatch() checks for equal cardinality
7256                    tempEndMatch = updateEndMatch(n)
7257                    if tempEndMatch:
7258                        posConnected.append(i)
7259                        # ... and keep going.
7260                        endMatch = False
7261                    else:
7262                        # clear list and populate with this element
7263                        posConnected = [i]
7264                        endMatch = False
7265                elif allTiesAreContinue(n):
7266                    # uniform-continue suffices if not matchByPitch
7267                    # but still need to check cardinality
7268                    if nLast and (len(nLast.pitches) != len(n.pitches)):
7269                        # different sizes: clear list and populate with this element
7270                        # since allTiesAreContinue, it is okay to treat as ersatz-start
7271                        posConnected = [i]
7272                    else:
7273                        posConnected.append(i)
7274                    # either way, this was not a stop
7275                    endMatch = False
7276                else:
7277                    # only SOME ties on this chord are "continue": reject
7278                    posConnected = []
7279                    endMatch = False
7280
7281            # establish end condition
7282            if endMatch is None:  # not yet set, not a start or continue
7283                endMatch = updateEndMatch(n)
7284
7285            # process end condition
7286            if endMatch:
7287                posConnected.append(i)  # add this last position
7288                if len(posConnected) < 2:
7289                    # an open tie, not connected to anything
7290                    # should be an error; presently, just skipping
7291                    # raise StreamException('cannot consolidate ties when only one tie is present',
7292                    #    notes[posConnected[0]])
7293                    # environLocal.printDebug(
7294                    #   ['cannot consolidate ties when only one tie is present',
7295                    #     notes[posConnected[0]]])
7296                    posConnected = []
7297                    continue
7298
7299                # get sum of durations for all notes
7300                # do not include first; will add to later; do not delete
7301                durSum = 0
7302                for q in posConnected[1:]:  # all but the first
7303                    durSum += notes[q].quarterLength
7304                    posDelete.append(q)  # store for deleting later
7305                # dur sum should always be greater than zero
7306                if durSum == 0:
7307                    raise StreamException('aggregated ties have a zero duration sum')
7308                # change the duration of the first note to be self + sum
7309                # of all others
7310                qLen = notes[posConnected[0]].quarterLength
7311                notes[posConnected[0]].quarterLength = qLen + durSum
7312
7313                # set tie to None on first note
7314                notes[posConnected[0]].tie = None
7315
7316                # replace removed elements in spanners
7317                for sp in f.spanners:
7318                    for index in posConnected[1:]:
7319                        if notes[index] in sp:
7320                            sp.replaceSpannedElement(notes[index], notes[posConnected[0]])
7321
7322                posConnected = []  # reset to empty
7323
7324        # all results have been processed
7325        posDelete.reverse()  # start from highest and go down
7326
7327        for i in posDelete:
7328            # environLocal.printDebug(['removing note', notes[i]])
7329            # get the obj ref
7330            nTarget = notes[i]
7331            # Recurse rather than depend on the containers being Measures
7332            # https://github.com/cuthbertLab/music21/issues/266
7333            returnObj.remove(nTarget, recurse=True)
7334
7335        if not inPlace:
7336            return returnObj
7337
7338    def extendTies(self, ignoreRests=False, pitchAttr='nameWithOctave'):
7339        '''
7340        Connect any adjacent pitch space values that are the
7341        same with a Tie. Adjacent pitches can be Chords, Notes, or Voices.
7342
7343        If `ignoreRests` is True, rests that occur between events will not be
7344        considered in matching pitches.
7345
7346        The `pitchAttr` determines the pitch attribute that is
7347        used for comparison. Any valid pitch attribute name can be used.
7348        '''
7349        def _getNextElements(srcStream, currentIndex, targetOffset):
7350            # need to find next event that start at the appropriate offset
7351            if currentIndex == len(srcStream) - 1:  # assume flat
7352                # environLocal.printDebug(['_getNextElements: nothing to process',
7353                # currentIndex, len(srcStream.notes) ])
7354                return []  # nothing left
7355            # iterate over all possible elements
7356            if ignoreRests:
7357                # need to find the offset of the first thing that is not rest
7358                for j in range(currentIndex + 1, len(srcStream._elements)):
7359                    el = srcStream._elements[j]
7360                    if isinstance(el, note.NotRest):
7361                        # change target offset to this position
7362                        targetOffset = srcStream._elements[j].getOffsetBySite(
7363                            srcStream)
7364                        break
7365            match = srcStream.getElementsByOffset(targetOffset)
7366            # filter matched elements
7367            post = []
7368            for matchEl in match:
7369                if isinstance(matchEl, note.NotRest):
7370                    post.append(matchEl)
7371            return post
7372
7373        # take all flat elements; this will remove all voices; just use offset
7374        # position
7375        # do not need to worry about ._endElements
7376        srcFlat = self.flatten().notes.stream()
7377        for i, e in enumerate(srcFlat):
7378            pSrc = []
7379            if isinstance(e, note.Note):
7380                pSrc = [e]
7381            elif isinstance(e, chord.Chord):
7382                pSrc = list(e)  # get components
7383            else:
7384                continue
7385            # environLocal.printDebug(['examining', i, e])
7386            connections = _getNextElements(srcFlat, i,
7387                                           e.getOffsetBySite(srcFlat) + e.duration.quarterLength)
7388            # environLocal.printDebug(['possible connections', connections])
7389
7390            for p, m in itertools.product(pSrc, connections):
7391                # for each p, see if there is match in the next position
7392                # for each element, look for a pitch to match
7393                mSrc = []
7394                if isinstance(m, note.Note):
7395                    mSrc = [m]
7396                elif isinstance(m, chord.Chord):
7397                    mSrc = list(m)  # get components
7398                # final note comparison
7399                for q in mSrc:
7400                    if getattr(q.pitch, pitchAttr) == getattr(p.pitch, pitchAttr):
7401                        # create a tie from p to q
7402                        if p.tie is None:
7403                            p.tie = tie.Tie('start')
7404                        elif p.tie.type == 'stop':
7405                            p.tie.type = 'continue'
7406                        # if dst tie exists, assume it connects
7407                        q.tie = tie.Tie('stop')
7408                        break  # can only have one match from p to q
7409
7410    # --------------------------------------------------------------------------
7411
7412    def sort(self, force=False):
7413        '''
7414        Sort this Stream in place by offset, then priority, then
7415        standard class sort order (e.g., Clefs before KeySignatures before
7416        TimeSignatures).
7417
7418        Note that Streams automatically sort themselves unless
7419        autoSort is set to False (as in the example below)
7420
7421        If `force` is True, a sort will be attempted regardless of any other parameters.
7422
7423
7424        >>> n1 = note.Note('A')
7425        >>> n2 = note.Note('B')
7426        >>> s = stream.Stream()
7427        >>> s.autoSort = False
7428        >>> s.insert(100, n2)
7429        >>> s.insert(0, n1)  # now a has a lower offset by higher index
7430        >>> [n.name for n in s]
7431        ['B', 'A']
7432        >>> s[0].name
7433        'B'
7434        >>> s.sort()
7435        >>> s[0].name
7436        'A'
7437        >>> [n.name for n in s]
7438        ['A', 'B']
7439        '''
7440        # trust if this is sorted: do not sort again
7441        # experimental
7442        if (not self.isSorted and self._mutable) or force:
7443            self._elements.sort(key=lambda x: x.sortTuple(self))
7444            self._endElements.sort(key=lambda x: x.sortTuple(self))
7445
7446            # as sorting changes order, elements have changed;
7447            # need to clear cache, but flat status is the same
7448            self.coreElementsChanged(
7449                updateIsFlat=False,
7450                clearIsSorted=False,
7451                keepIndex=False,  # this is False by default, but just to be sure for later
7452            )
7453            self.isSorted = True
7454            # environLocal.printDebug(['_elements', self._elements])
7455
7456    def sorted(self):
7457        # noinspection PyShadowingNames
7458        '''
7459        (TL;DR: you probably do not need to call this method unless you have turned `.autoSort` to
7460        off.)
7461
7462        Returns a new Stream where all the elements are sorted according to offset time, then
7463        priority, then classSortOrder (so that, for instance, a Clef at offset 0 appears before
7464        a Note at offset 0).
7465
7466        If this Stream is not flat, then only the elements directly in the stream itself are sorted.
7467        To sort all, run myStream.flatten().sorted().
7468
7469        Changed in v7 -- made into a method, not a property.
7470
7471        For instance, here is an unsorted Stream:
7472
7473        >>> s = stream.Stream()
7474        >>> s.autoSort = False  # if True, sorting is automatic
7475        >>> s.insert(1, note.Note('D'))
7476        >>> s.insert(0, note.Note('C'))
7477        >>> s.show('text')
7478        {1.0} <music21.note.Note D>
7479        {0.0} <music21.note.Note C>
7480
7481
7482        But a sorted version of the Stream puts the C first:
7483
7484        >>> s.sorted().show('text')
7485        {0.0} <music21.note.Note C>
7486        {1.0} <music21.note.Note D>
7487
7488        While the original stream remains unsorted:
7489
7490        >>> s.show('text')
7491        {1.0} <music21.note.Note D>
7492        {0.0} <music21.note.Note C>
7493
7494
7495        OMIT_FROM_DOCS
7496
7497        >>> s = stream.Stream()
7498        >>> s.autoSort = False
7499        >>> s.repeatInsert(note.Note('C#'), [0, 2, 4])
7500        >>> s.repeatInsert(note.Note('D-'), [1, 3, 5])
7501        >>> s.isSorted
7502        False
7503        >>> g = ''
7504        >>> for myElement in s:
7505        ...    g += '%s: %s; ' % (myElement.offset, myElement.name)
7506        >>> g
7507        '0.0: C#; 2.0: C#; 4.0: C#; 1.0: D-; 3.0: D-; 5.0: D-; '
7508        >>> y = s.sorted()
7509        >>> y.isSorted
7510        True
7511        >>> g = ''
7512        >>> for myElement in y:
7513        ...    g += '%s: %s; ' % (myElement.offset, myElement.name)
7514        >>> g
7515        '0.0: C#; 1.0: D-; 2.0: C#; 3.0: D-; 4.0: C#; 5.0: D-; '
7516        >>> farRight = note.Note('E')
7517        >>> farRight.priority = 5
7518        >>> farRight.offset = 2.0
7519        >>> y.insert(farRight)
7520        >>> g = ''
7521        >>> for myElement in y:
7522        ...    g += '%s: %s; ' % (myElement.offset, myElement.name)
7523        >>> g
7524        '0.0: C#; 1.0: D-; 2.0: C#; 3.0: D-; 4.0: C#; 5.0: D-; 2.0: E; '
7525        >>> z = y.sorted()
7526        >>> g = ''
7527        >>> for myElement in z:
7528        ...    g += '%s: %s; ' % (myElement.offset, myElement.name)
7529        >>> g
7530        '0.0: C#; 1.0: D-; 2.0: C#; 2.0: E; 3.0: D-; 4.0: C#; 5.0: D-; '
7531        >>> z[2].name, z[3].name
7532        ('C#', 'E')
7533        '''
7534        cache_sorted = self._cache.get('sorted')
7535        if cache_sorted is not None:
7536            return cache_sorted
7537        shallowElements = copy.copy(self._elements)  # already a copy
7538        shallowEndElements = copy.copy(self._endElements)  # already a copy
7539        s = copy.copy(self)
7540        # assign directly to _elements, as we do not need to call
7541        # coreElementsChanged()
7542        s._elements = shallowElements
7543        s._endElements = shallowEndElements
7544
7545        for e in shallowElements + shallowEndElements:
7546            s.coreSetElementOffset(e, self.elementOffset(e), addElement=True)
7547            e.sites.add(s)
7548            # need to explicitly set activeSite
7549            s.coreSelfActiveSite(e)
7550        # now just sort this stream in place; this will update the
7551        # isSorted attribute and sort only if not already sorted
7552        s.sort()
7553        self._cache['sorted'] = s
7554        return s
7555
7556    def flatten(self, retainContainers=False):
7557        '''
7558        A very important method that returns a new Stream
7559        that has all sub-containers "flattened" within it,
7560        that is, it returns a new Stream where no elements nest within
7561        other elements.
7562
7563        Here is a simple example of the usefulness of .flatten().  We
7564        will create a Score with two Parts in it, each with two Notes:
7565
7566        >>> sc = stream.Score()
7567        >>> p1 = stream.Part()
7568        >>> p1.id = 'part1'
7569        >>> n1 = note.Note('C4')
7570        >>> n2 = note.Note('D4')
7571        >>> p1.append(n1)
7572        >>> p1.append(n2)
7573
7574        >>> p2 = stream.Part()
7575        >>> p2.id = 'part2'
7576        >>> n3 = note.Note('E4')
7577        >>> n4 = note.Note('F4')
7578        >>> p2.append(n3)
7579        >>> p2.append(n4)
7580
7581        >>> sc.insert(0, p1)
7582        >>> sc.insert(0, p2)
7583
7584        When we look at sc, we will see only the two parts:
7585
7586        >>> sc.elements
7587        (<music21.stream.Part part1>, <music21.stream.Part part2>)
7588
7589        We can get at the notes by using the indices of the
7590        stream to get the parts and then looking at the .elements
7591        there:
7592
7593        >>> sc[0].elements
7594        (<music21.note.Note C>, <music21.note.Note D>)
7595
7596        >>> sc.getElementById('part2').elements
7597        (<music21.note.Note E>, <music21.note.Note F>)
7598
7599        ...but if we want to get all the notes, storing their
7600        offsets related to the beginning of the containing stream,
7601        one way
7602        is via calling .flatten() on sc and looking at the elements
7603        there:
7604
7605        >>> sc.flatten().elements
7606        (<music21.note.Note C>, <music21.note.Note E>,
7607         <music21.note.Note D>, <music21.note.Note F>)
7608
7609        Flattening a stream is a great way to get at all the notes in
7610        a larger piece.  For instance if we load a four-part
7611        Bach chorale into music21 from the integrated corpus, it
7612        will appear at first that there are no notes in the piece:
7613
7614        >>> bwv66 = corpus.parse('bach/bwv66.6')
7615        >>> len(bwv66.notes)
7616        0
7617
7618        This is because all the notes in the piece lie within :class:`music21.stream.Measure`
7619        objects and those measures lie within :class:`music21.stream.Part`
7620        objects.  It'd be a pain to navigate all the way through all those
7621        objects just to count notes.  Fortunately we can get a Stream of
7622        all the notes in the piece with .flatten().notes and then use the
7623        length of that Stream to count notes:
7624
7625        >>> bwv66flat = bwv66.flatten()
7626        >>> len(bwv66flat.notes)
7627        165
7628
7629        When, as is commonly the case, we want to find all of the notes,
7630        but do not care to have offsets related to the origin of the stream,
7631        then `.recurse()` is a more efficient way of working:
7632
7633        >>> len(bwv66.recurse().notes)
7634        165
7635
7636        If `retainContainers=True` then a "semiFlat" version of the stream
7637        is returned where Streams are also included in the output stream.
7638
7639        In general you will not need to use this because `.recurse()` is
7640        more efficient and does not lead to problems of the same
7641        object appearing in the hierarchy more than once.
7642
7643        >>> n1 = note.Note('C5')
7644        >>> m1 = stream.Measure([n1], number='1a')
7645        >>> p1 = stream.Part([m1])
7646        >>> p1.id = 'part1'
7647
7648        >>> n2 = note.Note('D5')
7649        >>> m2 = stream.Measure([n2], number='1b')
7650        >>> p2 = stream.Part([m2])
7651        >>> p2.id = 'part2'
7652
7653        >>> sc = stream.Score([p1, p2])
7654
7655        `sf` will be the "semi-flattened" version of the score.
7656
7657        >>> sf = sc.flatten(retainContainers=True)
7658        >>> sf.elements
7659        (<music21.stream.Part part1>,
7660         <music21.stream.Measure 1a offset=0.0>,
7661         <music21.stream.Part part2>,
7662         <music21.stream.Measure 1b offset=0.0>,
7663         <music21.note.Note C>,
7664         <music21.note.Note D>)
7665        >>> sf[0]
7666        <music21.stream.Part part1>
7667
7668        Notice that these all return the same object:
7669
7670        >>> sf[0][0][0]
7671        <music21.note.Note C>
7672        >>> sf[1][0]
7673        <music21.note.Note C>
7674        >>> sf[4]
7675        <music21.note.Note C>
7676
7677        Unless it is important to get iterate in order from
7678        front of score to back of the score, you are generally better off using recurse
7679        instead of `.flatten(retainContainers=True)`, with `.getOffsetInHierarchy()`
7680        to figure out where in the score each element lies.
7681
7682        For instance, this is how we can iterate using recurse():
7683
7684        >>> for el in sc.recurse():
7685        ...     print(el)
7686        <music21.stream.Part part1>
7687        <music21.stream.Measure 1a offset=0.0>
7688        <music21.note.Note C>
7689        <music21.stream.Part part2>
7690        <music21.stream.Measure 1b offset=0.0>
7691        <music21.note.Note D>
7692
7693        If you look back to our simple example of four notes above,
7694        you can see that the E (the first note in part2) comes before the D
7695        (the second note of part1).  This is because the flat stream
7696        is automatically sorted like all streams are by default.  The
7697        next example shows how to change this behavior.
7698
7699        >>> s = stream.Stream()
7700        >>> s.autoSort = False
7701        >>> s.repeatInsert(note.Note('C#'), [0, 2, 4])
7702        >>> s.repeatInsert(note.Note('D-'), [1, 3, 5])
7703        >>> s.isSorted
7704        False
7705
7706        >>> g = ''
7707        >>> for myElement in s:
7708        ...    g += '%s: %s; ' % (myElement.offset, myElement.name)
7709        ...
7710
7711        >>> g
7712        '0.0: C#; 2.0: C#; 4.0: C#; 1.0: D-; 3.0: D-; 5.0: D-; '
7713
7714        >>> y = s.sorted()
7715        >>> y.isSorted
7716        True
7717
7718        >>> g = ''
7719        >>> for myElement in y:
7720        ...    g += '%s: %s; ' % (myElement.offset, myElement.name)
7721        ...
7722
7723        >>> g
7724        '0.0: C#; 1.0: D-; 2.0: C#; 3.0: D-; 4.0: C#; 5.0: D-; '
7725
7726        >>> q = stream.Stream()
7727        >>> for i in range(5):
7728        ...     p = stream.Stream()
7729        ...     p.repeatInsert(base.Music21Object(), [0, 1, 2, 3, 4])
7730        ...     q.insert(i * 10, p)
7731        ...
7732
7733        >>> len(q)
7734        5
7735
7736        >>> qf = q.flatten()
7737        >>> len(qf)
7738        25
7739        >>> qf[24].offset
7740        44.0
7741
7742        Note that combining `.flatten(retainContainers=True)` with pure `.flatten()`
7743        can lead to unstable Streams where the same object appears more than once,
7744        in violation of a `music21` lookup rule.
7745
7746        >>> sc.flatten(retainContainers=True).flatten().elements
7747        (<music21.note.Note C>,
7748         <music21.note.Note C>,
7749         <music21.note.Note C>,
7750         <music21.note.Note D>,
7751         <music21.note.Note D>,
7752         <music21.note.Note D>)
7753
7754        OMIT_FROM_DOCS
7755
7756        >>> r = stream.Stream()
7757        >>> for j in range(5):
7758        ...   q = stream.Stream()
7759        ...   for i in range(5):
7760        ...      p = stream.Stream()
7761        ...      p.repeatInsert(base.Music21Object(), [0, 1, 2, 3, 4])
7762        ...      q.insert(i * 10, p)
7763        ...   r.insert(j * 100, q)
7764
7765        >>> len(r)
7766        5
7767
7768        >>> len(r.flatten())
7769        125
7770
7771        >>> r.flatten()[124].offset
7772        444.0
7773        '''
7774        # environLocal.printDebug(['flatten(): self', self,
7775        #  'self.activeSite', self.activeSite])
7776        if retainContainers:
7777            method = 'semiFlat'
7778        else:
7779            method = 'flat'
7780
7781        cached_version = self._cache.get(method)
7782        if cached_version is not None:
7783            return cached_version
7784
7785        # this copy will have a shared sites object
7786        # note that copy.copy() in some cases seems to not cause secondary
7787        # problems that self.__class__() does
7788        sNew = copy.copy(self)
7789
7790        if sNew.id != id(sNew):
7791            sOldId = sNew.id
7792            if common.isNum(sOldId) and sOldId > defaults.minIdNumberToConsiderMemoryLocation:
7793                sOldId = hex(sOldId)
7794
7795            newId = str(sOldId) + '_' + method
7796            sNew.id = newId
7797
7798        sNew._derivation = derivation.Derivation(sNew)
7799        sNew._derivation.origin = self
7800        sNew.derivation.method = method
7801        # storing .elements in here necessitates
7802        # create a new, independent cache instance in the flat representation
7803        sNew._cache = {}
7804        sNew._offsetDict = {}
7805        sNew._elements = []
7806        sNew._endElements = []
7807        sNew.coreElementsChanged()
7808
7809        ri = iterator.RecursiveIterator(self,
7810                                        restoreActiveSites=False,
7811                                        includeSelf=False,
7812                                        ignoreSorting=True,
7813                                        )
7814        for e in ri:
7815            if e.isStream and not retainContainers:
7816                continue
7817            sNew.coreInsert(ri.currentHierarchyOffset(),
7818                             e,
7819                             setActiveSite=False)
7820        if not retainContainers:
7821            sNew.isFlat = True
7822
7823        if self.autoSort is True:
7824            sNew.sort()  # sort it immediately so that cache is not invalidated
7825        else:
7826            sNew.coreElementsChanged()
7827        # here, we store the source stream from which this stream was derived
7828        self._cache[method] = sNew
7829
7830        return sNew
7831
7832    @property
7833    def flat(self):
7834        '''
7835        A property that returns the same flattened representation as `.flatten()`
7836        as of music21 v7.
7837
7838        See :meth:`~music21.stream.base.Stream.flatten()` for documentation.
7839
7840        This property will be deprecated in v8 and removed in v9.
7841        '''
7842        return self.flatten(retainContainers=False)
7843
7844    @property
7845    def semiFlat(self):
7846        '''
7847        The same as `.flatten(retainContainers=True)`.  This
7848        property should be rarely used, in favor of `.recurse()`, and will
7849        be removed as a property in version 8.
7850        '''
7851        return self.flatten(retainContainers=True)
7852
7853    def recurse(self,
7854                *,
7855                streamsOnly=False,
7856                restoreActiveSites=True,
7857                classFilter=(),
7858                skipSelf=True,
7859                includeSelf=None):
7860        '''
7861        `.recurse()` is a fundamental method of music21 for getting into
7862        elements contained in a Score, Part, or Measure, where elements such as
7863        notes are contained in sub-Stream elements.
7864
7865        Returns an iterator that iterates over a list of Music21Objects
7866        contained in the Stream, starting with self's elements (unless
7867        skipSelf=False in which case, it starts with the element itself),
7868        and whenever finding a Stream subclass in self,
7869        that Stream subclass's elements.
7870
7871        Here's an example. Let's create a simple score.
7872
7873        >>> s = stream.Score(id='mainScore')
7874        >>> p0 = stream.Part(id='part0')
7875        >>> p1 = stream.Part(id='part1')
7876
7877        >>> m01 = stream.Measure(number=1)
7878        >>> m01.append(note.Note('C', type='whole'))
7879        >>> m02 = stream.Measure(number=2)
7880        >>> m02.append(note.Note('D', type='whole'))
7881        >>> m11 = stream.Measure(number=1)
7882        >>> m11.append(note.Note('E', type='whole'))
7883        >>> m12 = stream.Measure(number=2)
7884        >>> m12.append(note.Note('F', type='whole'))
7885
7886        >>> p0.append([m01, m02])
7887        >>> p1.append([m11, m12])
7888
7889        >>> s.insert(0, p0)
7890        >>> s.insert(0, p1)
7891        >>> s.show('text')
7892        {0.0} <music21.stream.Part part0>
7893            {0.0} <music21.stream.Measure 1 offset=0.0>
7894                {0.0} <music21.note.Note C>
7895            {4.0} <music21.stream.Measure 2 offset=4.0>
7896                {0.0} <music21.note.Note D>
7897        {0.0} <music21.stream.Part part1>
7898            {0.0} <music21.stream.Measure 1 offset=0.0>
7899                {0.0} <music21.note.Note E>
7900            {4.0} <music21.stream.Measure 2 offset=4.0>
7901                {0.0} <music21.note.Note F>
7902
7903        Now we could assign the `.recurse()` method to something,
7904        but that won't have much effect:
7905
7906        >>> sRecurse = s.recurse()
7907        >>> sRecurse
7908        <music21.stream.iterator.RecursiveIterator for Score:mainScore @:0>
7909
7910        So, that's not how we use `.recurse()`.  Instead use it in a `for` loop:
7911
7912        >>> for el in s.recurse():
7913        ...     tup = (el, el.offset, el.activeSite)
7914        ...     print(tup)
7915        (<music21.stream.Part part0>, 0.0, <music21.stream.Score mainScore>)
7916        (<music21.stream.Measure 1 offset=0.0>, 0.0, <music21.stream.Part part0>)
7917        (<music21.note.Note C>, 0.0, <music21.stream.Measure 1 offset=0.0>)
7918        (<music21.stream.Measure 2 offset=4.0>, 4.0, <music21.stream.Part part0>)
7919        (<music21.note.Note D>, 0.0, <music21.stream.Measure 2 offset=4.0>)
7920        (<music21.stream.Part part1>, 0.0, <music21.stream.Score mainScore>)
7921        (<music21.stream.Measure 1 offset=0.0>, 0.0, <music21.stream.Part part1>)
7922        (<music21.note.Note E>, 0.0, <music21.stream.Measure 1 offset=0.0>)
7923        (<music21.stream.Measure 2 offset=4.0>, 4.0, <music21.stream.Part part1>)
7924        (<music21.note.Note F>, 0.0, <music21.stream.Measure 2 offset=4.0>)
7925
7926        If we specify `includeSelf=True` then the original stream is also iterated:
7927
7928        >>> for el in s.recurse(includeSelf=True):
7929        ...     tup = (el, el.offset, el.activeSite)
7930        ...     print(tup)
7931        (<music21.stream.Score mainScore>, 0.0, None)
7932        (<music21.stream.Part part0>, 0.0, <music21.stream.Score mainScore>)
7933        (<music21.stream.Measure 1 offset=0.0>, 0.0, <music21.stream.Part part0>)
7934        (<music21.note.Note C>, 0.0, <music21.stream.Measure 1 offset=0.0>)
7935        ...
7936
7937        Notice that like calling `.show('text')`, the offsets are relative to their containers.
7938
7939        Compare the difference between putting `classFilter='Note'` and `.flatten().notes`:
7940
7941        >>> for el in s.recurse(classFilter='Note'):
7942        ...     tup = (el, el.offset, el.activeSite)
7943        ...     print(tup)
7944        (<music21.note.Note C>, 0.0, <music21.stream.Measure 1 offset=0.0>)
7945        (<music21.note.Note D>, 0.0, <music21.stream.Measure 2 offset=4.0>)
7946        (<music21.note.Note E>, 0.0, <music21.stream.Measure 1 offset=0.0>)
7947        (<music21.note.Note F>, 0.0, <music21.stream.Measure 2 offset=4.0>)
7948
7949        >>> for el in s.flatten().notes:
7950        ...     tup = (el, el.offset, el.activeSite)
7951        ...     print(tup)
7952        (<music21.note.Note C>, 0.0, <music21.stream.Score mainScore_flat>)
7953        (<music21.note.Note E>, 0.0, <music21.stream.Score mainScore_flat>)
7954        (<music21.note.Note D>, 4.0, <music21.stream.Score mainScore_flat>)
7955        (<music21.note.Note F>, 4.0, <music21.stream.Score mainScore_flat>)
7956
7957        If you don't need correct offsets or activeSites, set `restoreActiveSites` to `False`.
7958        Then the last offset/activeSite will be used.  It's a bit of a speedup, but leads to some
7959        bad code, so use it only in highly optimized situations.
7960
7961        We'll also test using multiple classes here... the Stream given is the same as the
7962        s.flatten().notes stream.
7963
7964        >>> for el in s.recurse(classFilter=('Note', 'Rest'), restoreActiveSites=False):
7965        ...     tup = (el, el.offset, el.activeSite)
7966        ...     print(tup)
7967        (<music21.note.Note C>, 0.0, <music21.stream.Score mainScore_flat>)
7968        (<music21.note.Note D>, 4.0, <music21.stream.Score mainScore_flat>)
7969        (<music21.note.Note E>, 0.0, <music21.stream.Score mainScore_flat>)
7970        (<music21.note.Note F>, 4.0, <music21.stream.Score mainScore_flat>)
7971
7972        So, this is pretty unreliable so don't use it unless the tiny speedup is worth it.
7973
7974        The other two attributes are pretty self-explanatory: `streamsOnly` will put only Streams
7975        in, while `includeSelf` will add the initial stream from recursion.  If the inclusion or
7976        exclusion of `self` is important to you, put it in explicitly.
7977
7978        >>> for el in s.recurse(includeSelf=False, streamsOnly=True):
7979        ...     tup = (el, el.offset, el.activeSite)
7980        ...     print(tup)
7981        (<music21.stream.Part part0>, 0.0, <music21.stream.Score mainScore>)
7982        (<music21.stream.Measure 1 offset=0.0>, 0.0, <music21.stream.Part part0>)
7983        (<music21.stream.Measure 2 offset=4.0>, 4.0, <music21.stream.Part part0>)
7984        (<music21.stream.Part part1>, 0.0, <music21.stream.Score mainScore>)
7985        (<music21.stream.Measure 1 offset=0.0>, 0.0, <music21.stream.Part part1>)
7986        (<music21.stream.Measure 2 offset=4.0>, 4.0, <music21.stream.Part part1>)
7987
7988        .. warning::
7989
7990            Remember that like all iterators, it is dangerous to alter
7991            the components of the Stream being iterated over during iteration.
7992            if you need to edit while recursing, list(s.recurse()) is safer.
7993
7994        Changed in v5.5 -- `skipSelf` is True as promised.  All attributes are keyword only.
7995        `includeSelf` is added and now preferred over `skipSelf`.  `skipSelf` will be removed
7996        in or after 2022 in v. 7 or v. 8.
7997        '''
7998        if includeSelf is None:
7999            includeSelf = not skipSelf
8000        ri = iterator.RecursiveIterator(self,
8001                                        streamsOnly=streamsOnly,
8002                                        restoreActiveSites=restoreActiveSites,
8003                                        includeSelf=includeSelf
8004                                        )
8005        if classFilter:
8006            ri.addFilter(filters.ClassFilter(classFilter), returnClone=False)
8007        return ri
8008
8009    def containerInHierarchy(self, el, *, setActiveSite=True) -> Optional['music21.stream.Stream']:
8010        '''
8011        Returns the container in a hierarchy that this element belongs to.
8012
8013        For instance, assume a Note (n) is in a Measure (m1) which is in a Part, in a Score (s1),
8014        and the Note is also in another hierarchy (say, a chordified version of a Score, s2).
8015        if s1.containerInHierarchy(n) is called, it will return m1,
8016        the Measure that contains the note.
8017
8018        Unless `setActiveSite` is False, n's activeSite will be set to m1, and
8019        its `.offset` will be the offset in `m1`.
8020
8021        >>> s1 = stream.Score(id='s1')
8022        >>> p1 = stream.Part()
8023        >>> m1 = stream.Measure(id='m1')
8024        >>> n = note.Note('D')
8025        >>> m1.append(n)
8026        >>> p1.append(m1)
8027        >>> s1.insert(0, p1)
8028        >>> s2 = stream.Stream(id='s2')
8029        >>> s2.append(n)
8030        >>> n.activeSite.id
8031        's2'
8032        >>> s1.containerInHierarchy(n).id
8033        'm1'
8034        >>> n.activeSite.id
8035        'm1'
8036        >>> n.activeSite = s2
8037        >>> s1.containerInHierarchy(n, setActiveSite=False).id
8038        'm1'
8039        >>> n.activeSite.id
8040        's2'
8041
8042        If the element cannot be found in the hierarchy then None is returned.
8043
8044        >>> s3 = stream.Stream()
8045        >>> s3.containerInHierarchy(n) is None
8046        True
8047        '''
8048        elSites = el.sites
8049        for s in self.recurse(includeSelf=True, streamsOnly=True, restoreActiveSites=False):
8050            if s in elSites:
8051                if setActiveSite:
8052                    s.coreSelfActiveSite(el)
8053                return s
8054        return None
8055
8056    def makeImmutable(self):
8057        '''
8058        Clean this Stream: for self and all elements, purge all dead locations
8059        and remove all non-contained sites. Further, restore all active sites.
8060        '''
8061        if self._mutable is not False:
8062            self.sort()  # must sort before making immutable
8063        for e in self.recurse(streamsOnly=True, includeSelf=False):
8064            # e.purgeLocations(rescanIsDead=True)
8065            # NOTE: calling this method was having the side effect of removing
8066            # sites from locations when a Note was both in a Stream and in
8067            # an Interval
8068            if e.isStream:
8069                e.sort()  # sort before making immutable
8070                e._mutable = False
8071        self._mutable = False
8072
8073    def makeMutable(self, recurse=True):
8074        self._mutable = True
8075        if recurse:
8076            for e in self.recurse(streamsOnly=True):
8077                # do not recurse, as will get all Stream
8078                e.makeMutable(recurse=False)
8079        self.coreElementsChanged()
8080
8081    # --------------------------------------------------------------------------
8082    # duration and offset methods and properties
8083
8084    @property
8085    def highestOffset(self):
8086        '''
8087        Get start time of element with the highest offset in the Stream.
8088        Note the difference between this property and highestTime
8089        which gets the end time of the highestOffset
8090
8091        >>> stream1 = stream.Stream()
8092        >>> for offset in [0, 4, 8]:
8093        ...     n = note.Note('G#', type='whole')
8094        ...     stream1.insert(offset, n)
8095        >>> stream1.highestOffset
8096        8.0
8097        >>> stream1.highestTime
8098        12.0
8099        '''
8100        # TODO: Perfect timespans candidate
8101        if 'HighestOffset' in self._cache and self._cache['HighestOffset'] is not None:
8102            pass  # return cache unaltered
8103        elif not self._elements:
8104            self._cache['HighestOffset'] = 0.0
8105        elif self.isSorted is True:
8106            eLast = self._elements[-1]
8107            self._cache['HighestOffset'] = self.elementOffset(eLast)
8108        else:  # iterate through all elements
8109            highestOffsetSoFar = None
8110            for e in self._elements:
8111                candidateOffset = self.elementOffset(e)
8112                if highestOffsetSoFar is None or candidateOffset > highestOffsetSoFar:
8113                    highestOffsetSoFar = candidateOffset
8114
8115            if highestOffsetSoFar is not None:
8116                self._cache['HighestOffset'] = float(highestOffsetSoFar)
8117            else:
8118                self._cache['HighestOffset'] = None
8119        return self._cache['HighestOffset']
8120
8121    def _setHighestTime(self, value):
8122        '''For internal use only.
8123        '''
8124        self._cache['HighestTime'] = value
8125
8126    @property
8127    def highestTime(self):
8128        '''
8129        Returns the maximum of all Element offsets plus their Duration
8130        in quarter lengths. This value usually represents the last
8131        "release" in the Stream.
8132
8133        Stream.duration is usually equal to the highestTime
8134        expressed as a Duration object, but it can be set separately
8135        for advanced operations.
8136
8137        Example: Insert a dotted half note at position 0 and see where
8138        it cuts off:
8139
8140        >>> p1 = stream.Stream()
8141        >>> p1.highestTime
8142        0.0
8143
8144        >>> n = note.Note('A-')
8145        >>> n.quarterLength = 3
8146        >>> p1.insert(0, n)
8147        >>> p1.highestTime
8148        3.0
8149
8150        Now insert in the same stream, the dotted half note
8151        at positions 1, 2, 3, 4 and see when the final note cuts off:
8152
8153        >>> p1.repeatInsert(n, [1, 2, 3, 4])
8154        >>> p1.highestTime
8155        7.0
8156
8157        Another example.
8158
8159        >>> n = note.Note('C#')
8160        >>> n.quarterLength = 3
8161
8162        >>> q = stream.Stream()
8163        >>> for i in [20, 0, 10, 30, 40]:
8164        ...    p = stream.Stream()
8165        ...    p.repeatInsert(n, [0, 1, 2, 3, 4])
8166        ...    q.insert(i, p)  # insert out of order
8167        >>> len(q.flatten())
8168        25
8169        >>> q.highestTime  # this works b/c the component Stream has an duration
8170        47.0
8171        >>> r = q.flatten()
8172
8173        Changed in v6.5 -- highestTime can return a Fraction.
8174
8175
8176        OMIT_FROM_DOCS
8177
8178        Make sure that the cache really is empty
8179        >>> 'HighestTime' in r._cache
8180        False
8181        >>> r.highestTime  # 44 + 3
8182        47.0
8183
8184        Can highestTime be a fraction?
8185
8186        >>> n = note.Note('D')
8187        >>> n.duration.quarterLength = 1/3
8188        >>> n.duration.quarterLength
8189        Fraction(1, 3)
8190
8191        >>> s = stream.Stream()
8192        >>> s.append(note.Note('C'))
8193        >>> s.append(n)
8194        >>> s.highestTime
8195        Fraction(4, 3)
8196        '''
8197        # TODO(msc) -- why is cache 'HighestTime' and not 'highestTime'?
8198        # environLocal.printDebug(['_getHighestTime', 'isSorted', self.isSorted, self])
8199
8200        # remove cache -- durations might change...
8201        if 'HighestTime' in self._cache and self._cache['HighestTime'] is not None:
8202            pass  # return cache unaltered
8203        elif not self._elements:
8204            # _endElements does not matter here, since ql > 0 on endElements not allowed.
8205            self._cache['HighestTime'] = 0.0
8206            return 0.0
8207        else:
8208            highestTimeSoFar = 0.0
8209            # TODO: optimize for a faster way of doing this.
8210            #     but cannot simply look at the last element even if isSorted
8211            #     because what if the penultimate
8212            #     element, with a
8213            #     lower offset has a longer duration than the last?
8214            #     Take the case where a whole note appears a 0.0, but a
8215            #     textExpression (ql=0) at 0.25 --
8216            #     isSorted would be true, but highestTime should be 4.0 not 0.25
8217            for e in self._elements:
8218                candidateOffset = (self.elementOffset(e)
8219                                   + e.duration.quarterLength)
8220                if candidateOffset > highestTimeSoFar:
8221                    highestTimeSoFar = candidateOffset
8222            self._cache['HighestTime'] = opFrac(highestTimeSoFar)
8223        return self._cache['HighestTime']
8224
8225    @property
8226    def lowestOffset(self):
8227        '''
8228        Get the start time of the Element with the lowest offset in the Stream.
8229
8230        >>> stream1 = stream.Stream()
8231        >>> for x in range(3, 5):
8232        ...     n = note.Note('G#')
8233        ...     stream1.insert(x, n)
8234        ...
8235        >>> stream1.lowestOffset
8236        3.0
8237
8238
8239        If the Stream is empty, then the lowest offset is 0.0:
8240
8241
8242        >>> stream2 = stream.Stream()
8243        >>> stream2.lowestOffset
8244        0.0
8245
8246
8247
8248        >>> p = stream.Stream()
8249        >>> p.repeatInsert(note.Note('D5'), [0, 1, 2, 3, 4])
8250        >>> q = stream.Stream()
8251        >>> q.repeatInsert(p, list(range(0, 50, 10)))
8252        >>> len(q.flatten())
8253        25
8254        >>> q.lowestOffset
8255        0.0
8256        >>> r = stream.Stream()
8257        >>> r.repeatInsert(q, list(range(97, 500, 100)))
8258        >>> len(r.flatten())
8259        125
8260        >>> r.lowestOffset
8261        97.0
8262        '''
8263        if 'lowestOffset' in self._cache and self._cache['LowestOffset'] is not None:
8264            pass  # return cache unaltered
8265        elif not self._elements:
8266            self._cache['LowestOffset'] = 0.0
8267        elif self.isSorted is True:
8268            eFirst = self._elements[0]
8269            self._cache['LowestOffset'] = self.elementOffset(eFirst)
8270        else:  # iterate through all elements
8271            minOffsetSoFar = None
8272            for e in self._elements:
8273                candidateOffset = self.elementOffset(e)
8274                if minOffsetSoFar is None or candidateOffset < minOffsetSoFar:
8275                    minOffsetSoFar = candidateOffset
8276            self._cache['LowestOffset'] = minOffsetSoFar
8277
8278            # environLocal.printDebug(['_getLowestOffset: iterated elements', min])
8279
8280        return self._cache['LowestOffset']
8281
8282    def _getDuration(self):
8283        '''
8284        Gets the duration of the whole stream (generally the highestTime, but can be set
8285        independently).
8286        '''
8287        if self._unlinkedDuration is not None:
8288            return self._unlinkedDuration
8289        elif 'Duration' in self._cache and self._cache['Duration'] is not None:
8290            # environLocal.printDebug(['returning cached duration'])
8291            return self._cache['Duration']
8292        else:
8293            # environLocal.printDebug(['creating new duration based on highest time'])
8294            self._cache['Duration'] = duration.Duration(quarterLength=self.highestTime)
8295            return self._cache['Duration']
8296
8297    def _setDuration(self, durationObj):
8298        '''
8299        Set the total duration of the Stream independently of the highestTime
8300        of the stream.  Useful to define the scope of the stream as independent
8301        of its constituted elements.
8302
8303        If set to None, then the default behavior of computing automatically from
8304        highestTime is reestablished.
8305        '''
8306        if isinstance(durationObj, duration.Duration):
8307            self._unlinkedDuration = durationObj
8308        elif durationObj is None:
8309            self._unlinkedDuration = None
8310        else:  # need to permit Duration object assignment here
8311            raise StreamException(f'this must be a Duration object, not {durationObj}')
8312
8313    duration = property(_getDuration, _setDuration, doc='''
8314        Returns the total duration of the Stream, from the beginning of the
8315        stream until the end of the final element.
8316        May be set independently by supplying a Duration object.
8317
8318        >>> a = stream.Stream()
8319        >>> q = note.Note(type='quarter')
8320        >>> a.repeatInsert(q, [0, 1, 2, 3])
8321        >>> a.highestOffset
8322        3.0
8323        >>> a.highestTime
8324        4.0
8325        >>> a.duration
8326        <music21.duration.Duration 4.0>
8327        >>> a.duration.quarterLength
8328        4.0
8329
8330
8331        Advanced usage: override the duration from what is set:
8332
8333        >>> newDuration = duration.Duration('half')
8334        >>> newDuration.quarterLength
8335        2.0
8336
8337        >>> a.duration = newDuration
8338        >>> a.duration.quarterLength
8339        2.0
8340
8341        Restore normal behavior by setting duration to None:
8342
8343        >>> a.duration = None
8344        >>> a.duration
8345        <music21.duration.Duration 4.0>
8346
8347        Note that the highestTime for the stream is the same
8348        whether duration is overridden or not:
8349
8350        >>> a.highestTime
8351        4.0
8352        ''')
8353
8354    def _setSeconds(self, value):
8355        pass
8356
8357    def _getSeconds(self):
8358        getTempoFromContext = False
8359        # need to find all tempo indications and the number of quarter lengths
8360        # under each
8361        tiStream = self.getElementsByClass('TempoIndication')
8362        offsetMetronomeMarkPairs = []
8363
8364        if not tiStream:
8365            getTempoFromContext = True
8366        else:
8367            for ti in tiStream:
8368                o = self.elementOffset(ti)
8369                # get the desired metronome mark from any of ti classes
8370                mm = ti.getSoundingMetronomeMark()
8371                offsetMetronomeMarkPairs.append([o, mm])
8372
8373        for i, (o, mm) in enumerate(offsetMetronomeMarkPairs):
8374            if i == 0 and o > 0.0:
8375                getTempoFromContext = True
8376            break  # just need first
8377
8378        if getTempoFromContext:
8379            ti = self.getContextByClass('TempoIndication')
8380            if ti is None:
8381                if self.highestTime != 0.0:
8382                    return float('nan')
8383                else:
8384                    return 0.0
8385            # insert at zero offset position, even though coming from
8386            # outside this stream
8387            mm = ti.getSoundingMetronomeMark()
8388            offsetMetronomeMarkPairs = [[0.0, mm]] + offsetMetronomeMarkPairs
8389        sec = 0.0
8390        for i, (o, mm) in enumerate(offsetMetronomeMarkPairs):
8391            # handle only one mm right away
8392            if len(offsetMetronomeMarkPairs) == 1:
8393                sec += mm.durationToSeconds(self.highestTime)
8394                break
8395            oStart, mmStart = o, mm
8396            # if not the last ti, get the next by index to get the offset
8397            if i < len(offsetMetronomeMarkPairs) - 1:
8398                # cases of two or more remain
8399                oEnd, unused_mmEnd = offsetMetronomeMarkPairs[i + 1]
8400            else:  # at the last
8401                oEnd = self.highestTime
8402            sec += mmStart.durationToSeconds(oEnd - oStart)
8403
8404        return sec
8405
8406    seconds = property(_getSeconds, _setSeconds, doc='''
8407        Get or set the duration of this Stream in seconds, assuming that
8408        this object contains a :class:`~music21.tempo.MetronomeMark` or
8409        :class:`~music21.tempo.MetricModulation`.
8410
8411        >>> s = corpus.parse('bwv66.6')  # piece without a tempo
8412        >>> sFlat = s.flatten()
8413        >>> t = tempo.MetronomeMark('adagio')
8414        >>> sFlat.insert(0, t)
8415        >>> sFlat.seconds
8416        38.57142857...
8417        >>> tFast = tempo.MetronomeMark('allegro')
8418        >>> sFlat.replace(t, tFast)
8419        >>> sFlat.seconds
8420        16.363...
8421
8422        Setting seconds on streams is not supported.  Ideally it would instead
8423        scale all elements to fit, but this is a long way off.
8424
8425        If a stream does not have a tempo-indication in it then the property
8426        returns 0.0 if an empty Stream (or self.highestTime is 0.0) or 'nan'
8427        if there are non-zero duration objects in the stream:
8428
8429        >>> s = stream.Stream()
8430        >>> s.seconds
8431        0.0
8432        >>> s.insert(0, clef.TrebleClef())
8433        >>> s.seconds
8434        0.0
8435        >>> s.append(note.Note(type='half'))
8436        >>> s.seconds
8437        nan
8438        >>> import math
8439        >>> math.isnan(s.seconds)
8440        True
8441
8442        Changed in v6.3 -- return nan rather than raising an exception.  Do not
8443        attempt to change seconds on a stream, as it did not do what you would expect.
8444        ''')
8445
8446    def metronomeMarkBoundaries(self, srcObj=None):
8447        '''
8448        Return a list of offset start, offset end,
8449        MetronomeMark triples for all TempoIndication objects found
8450        in this Stream or a Stream provided by srcObj.
8451
8452        If no MetronomeMarks are found, or an initial region does not
8453        have a MetronomeMark, a mark of quarter equal to 120 is provided as default.
8454
8455        Note that if other TempoIndication objects are defined,
8456        they will be converted to MetronomeMarks and returned here
8457
8458
8459        >>> s = stream.Stream()
8460        >>> s.repeatAppend(note.Note(), 8)
8461        >>> s.insert([6, tempo.MetronomeMark(number=240)])
8462        >>> s.metronomeMarkBoundaries()
8463        [(0.0, 6.0, <music21.tempo.MetronomeMark animato Quarter=120>),
8464         (6.0, 8.0, <music21.tempo.MetronomeMark Quarter=240>)]
8465        '''
8466        if srcObj is None:
8467            srcObj = self
8468        # get all tempo indications from the flat Stream;
8469        # using flat here may not always be desirable
8470        # may want to do a recursive upward search as well
8471        srcFlat = srcObj.flatten()
8472        tiStream = srcFlat.getElementsByClass('TempoIndication')
8473        mmBoundaries = []  # a  list of (start, end, mm)
8474
8475        # not sure if this should be taken from the flat representation
8476        highestTime = srcFlat.highestTime
8477        lowestOffset = srcFlat.lowestOffset
8478
8479        # if no tempo
8480        if not tiStream:
8481            mmDefault = tempo.MetronomeMark(number=120)  # a default
8482            mmBoundaries.append((lowestOffset, highestTime, mmDefault))
8483        # if just one tempo
8484        elif len(tiStream) == 1:
8485            # get offset from flat source
8486            o = tiStream[0].getOffsetBySite(srcFlat)
8487            mm = tiStream[0].getSoundingMetronomeMark()
8488            if o > lowestOffset:
8489                mmDefault = tempo.MetronomeMark(number=120)  # a default
8490                mmBoundaries.append((lowestOffset, o, mmDefault))
8491                mmBoundaries.append((o, highestTime, mm))
8492            else:
8493                mmBoundaries.append((lowestOffset, highestTime, mm))
8494        # one or more tempi
8495        else:
8496            offsetPairs = []
8497            for ti in tiStream:
8498                o = ti.getOffsetBySite(srcFlat)
8499                offsetPairs.append([o, ti.getSoundingMetronomeMark()])
8500            # fill boundaries
8501            # if lowest region not defined, supply as default
8502            if offsetPairs[0][0] > lowestOffset:
8503                mmDefault = tempo.MetronomeMark(number=120)  # a default
8504                mmBoundaries.append((lowestOffset, offsetPairs[0][0],
8505                                     mmDefault))
8506                mmBoundaries.append((offsetPairs[0][0], offsetPairs[1][0],
8507                                     offsetPairs[0][1]))
8508            else:  # just add the first range
8509                mmBoundaries.append((offsetPairs[0][0], offsetPairs[1][0],
8510                                     offsetPairs[0][1]))
8511            # add any remaining ranges, starting from the second; if last,
8512            # use the highest time as the boundary
8513            for i, (o, mm) in enumerate(offsetPairs):
8514                if i == 0:
8515                    continue  # already added first
8516                elif i == len(offsetPairs) - 1:  # last index
8517                    mmBoundaries.append((offsetPairs[i][0], highestTime,
8518                                         offsetPairs[i][1]))
8519                else:  # add with next boundary
8520                    mmBoundaries.append((offsetPairs[i][0], offsetPairs[i + 1][0],
8521                                         offsetPairs[i][1]))
8522
8523        # environLocal.printDebug(['self.metronomeMarkBoundaries()',
8524        # 'got mmBoundaries:', mmBoundaries])
8525        return mmBoundaries
8526
8527    def _accumulatedSeconds(self, mmBoundaries, oStart, oEnd):
8528        '''
8529        Given MetronomeMark boundaries, for any pair of offsets,
8530        determine the realized duration in seconds.
8531        '''
8532        # assume tt mmBoundaries are in order
8533        totalSeconds = 0.0
8534        activeStart = oStart
8535        activeEnd = None
8536        for s, e, mm in mmBoundaries:
8537            if s <= activeStart < e:
8538                # find time in this region
8539                if oEnd < e:  # if end within this region
8540                    activeEnd = oEnd
8541                else:  # if end after this region
8542                    activeEnd = e
8543                # environLocal.printDebug(['activeStart', activeStart,
8544                # 'activeEnd', activeEnd, 's, e, mm', s, e, mm])
8545                totalSeconds += mm.durationToSeconds(activeEnd - activeStart)
8546            else:
8547                continue
8548            if activeEnd == oEnd:
8549                break
8550            else:  # continue on
8551                activeStart = activeEnd
8552        return totalSeconds
8553
8554    def _getSecondsMap(self, srcObj=None):
8555        '''
8556        Return a list of dictionaries for all elements in this Stream,
8557        where each dictionary defines the real-time characteristics of
8558        the stored events. This will attempt to find
8559        all :class:`~music21.tempo.TempoIndication` subclasses and use these
8560        values to realize tempi. If no initial tempo is found,
8561        a tempo of 120 BPM will be provided.
8562        '''
8563        if srcObj is None:
8564            srcObj = self
8565        mmBoundaries = self.metronomeMarkBoundaries(srcObj=srcObj)
8566
8567        # not sure if this should be taken from the flat representation
8568        lowestOffset = srcObj.lowestOffset
8569
8570        secondsMap = []  # list of start, start+dur, element
8571        if srcObj.hasVoices():
8572            groups = []
8573            for i, v in enumerate(srcObj.voices):
8574                groups.append((v.flatten(), i))
8575        else:  # create a single collection
8576            groups = [(srcObj, None)]
8577
8578        # get accumulated time over many possible tempo changes for
8579        # start/end offset
8580        for group, voiceIndex in groups:
8581            for e in group:
8582                if isinstance(e, bar.Barline):
8583                    continue
8584                dur = e.duration.quarterLength
8585                offset = round(e.getOffsetBySite(group), 8)
8586                # calculate all time regions given this offset
8587
8588                # all stored values are seconds
8589                # noinspection PyDictCreation
8590                secondsDict = {}
8591                secondsDict['offsetSeconds'] = srcObj._accumulatedSeconds(
8592                    mmBoundaries, lowestOffset, offset)
8593                secondsDict['durationSeconds'] = srcObj._accumulatedSeconds(
8594                    mmBoundaries, offset, offset + dur)
8595                secondsDict['endTimeSeconds'] = (secondsDict['offsetSeconds']
8596                                                 + secondsDict['durationSeconds'])
8597                secondsDict['element'] = e
8598                secondsDict['voiceIndex'] = voiceIndex
8599                secondsMap.append(secondsDict)
8600        return secondsMap
8601
8602    # do not make a property decorator since _getSecondsMap takes arguments
8603    secondsMap = property(_getSecondsMap, doc='''
8604        Returns a list where each element is a dictionary
8605        consisting of the 'offsetSeconds' in seconds of each element in a Stream,
8606        the 'duration' in seconds, the 'endTimeSeconds' in seconds (that is, the offset
8607        plus the duration), and the 'element' itself. Also contains a 'voiceIndex' entry which
8608        contains the voice number of the element, or None if there
8609        are no voices.
8610
8611
8612        >>> mm1 = tempo.MetronomeMark(number=120)
8613        >>> n1 = note.Note(type='quarter')
8614        >>> c1 = clef.AltoClef()
8615        >>> n2 = note.Note(type='half')
8616        >>> s1 = stream.Stream()
8617        >>> s1.append([mm1, n1, c1, n2])
8618        >>> om = s1.secondsMap
8619        >>> om[3]['offsetSeconds']
8620        0.5
8621        >>> om[3]['endTimeSeconds']
8622        1.5
8623        >>> om[3]['element'] is n2
8624        True
8625        >>> om[3]['voiceIndex']
8626    ''')
8627
8628    # --------------------------------------------------------------------------
8629    # Metadata access
8630
8631    def _getMetadata(self):
8632        '''
8633
8634
8635        >>> a = stream.Stream()
8636        >>> a.metadata = metadata.Metadata()
8637        '''
8638        mdList = self.getElementsByClass('Metadata')
8639        # only return metadata that has an offset = 0.0
8640        mdList = mdList.getElementsByOffset(0)
8641        return mdList.first()
8642
8643    def _setMetadata(self, metadataObj):
8644        '''
8645
8646        >>> a = stream.Stream()
8647        >>> a.metadata = metadata.Metadata()
8648        '''
8649        oldMetadata = self._getMetadata()
8650        if oldMetadata is not None:
8651            # environLocal.printDebug(['removing old metadata', oldMetadata])
8652            junk = self.pop(self.index(oldMetadata))
8653
8654        if metadataObj is not None and isinstance(metadataObj, metadata.Metadata):
8655            self.insert(0, metadataObj)
8656
8657    metadata = property(_getMetadata, _setMetadata,
8658                        doc='''
8659        Get or set the :class:`~music21.metadata.Metadata` object
8660        found at the beginning (offset 0) of this Stream.
8661
8662        >>> s = stream.Stream()
8663        >>> s.metadata = metadata.Metadata()
8664        >>> s.metadata.composer = 'frank'
8665        >>> s.metadata.composer
8666        'frank'
8667        ''')
8668
8669    # --------------------------------------------------------------------------
8670    # these methods override the behavior inherited from base.py
8671
8672    def _getMeasureOffset(self):
8673        # this normally returns the offset of this object in its container
8674        # for now, simply return the offset for the activeSite
8675        return self.getOffsetBySite(self.activeSite)
8676
8677    @property
8678    def beat(self):
8679        '''
8680        beat returns None for a Stream.
8681        '''
8682        # this normally returns the beat within a measure; here, it could
8683        # be beats from the beginning?
8684        return None
8685
8686    @property
8687    def beatStr(self):
8688        '''
8689        unlike other Music21Objects, streams always have beatStr (beat string) of None
8690
8691        May change to '' soon.
8692        '''
8693        return None
8694
8695    @property
8696    def beatDuration(self):
8697        '''
8698        unlike other Music21Objects, streams always have beatDuration of None
8699        '''
8700        # this returns the duration of the active beat
8701        return None
8702
8703    @property
8704    def beatStrength(self):
8705        '''
8706        unlike other Music21Objects, streams always have beatStrength of None
8707        '''
8708        # this returns the accent weight of the active beat
8709        return None
8710
8711    def beatAndMeasureFromOffset(self, searchOffset, fixZeros=True):
8712        '''
8713        Returns a two-element tuple of the beat and the Measure object (or the first one
8714        if there are several at the same offset; unlikely but possible) for a given
8715        offset from the start of this Stream (that contains measures).
8716
8717        Recursively searches for measures.  Note that this method assumes that all parts
8718        have measures of consistent length.  If that's not the case, this
8719        method can be called on the relevant part.
8720
8721        This algorithm should work even for weird time signatures such as 2+3+2/8.
8722
8723
8724        >>> bach = corpus.parse('bach/bwv1.6')
8725        >>> bach.parts[0].measure(2).getContextByClass('TimeSignature')
8726        <music21.meter.TimeSignature 4/4>
8727        >>> returnTuples = []
8728        >>> for offset in [0.0, 1.0, 2.0, 5.0, 5.5]:
8729        ...     returnTuples.append(bach.beatAndMeasureFromOffset(offset))
8730        >>> returnTuples
8731        [(4.0, <music21.stream.Measure 0 offset=0.0>),
8732         (1.0, <music21.stream.Measure 1 offset=1.0>),
8733         (2.0, <music21.stream.Measure 1 offset=1.0>),
8734         (1.0, <music21.stream.Measure 2 offset=5.0>),
8735         (1.5, <music21.stream.Measure 2 offset=5.0>)]
8736
8737        To get just the measureNumber and beat, use a transformation like this:
8738        >>> [(beat, measureObj.number) for beat, measureObj in returnTuples]
8739        [(4.0, 0), (1.0, 1), (2.0, 1), (1.0, 2), (1.5, 2)]
8740
8741        Adapted from contributed code by Dmitri Tymoczko.  With thanks to DT.
8742        '''
8743        myStream = self
8744        if not myStream.hasMeasures():
8745            if myStream.hasPartLikeStreams():
8746                foundPart = False
8747                for subStream in myStream:
8748                    if not subStream.isStream:
8749                        continue
8750                    if subStream.hasMeasures():
8751                        foundPart = True
8752                        myStream = subStream
8753                        break
8754                if not foundPart:
8755                    raise StreamException('beatAndMeasureFromOffset: could not find any parts!')
8756                    # was return False
8757            else:
8758                if not myStream.hasMeasures():
8759                    raise StreamException('beatAndMeasureFromOffset: could not find any measures!')
8760                    # return False
8761        # Now we get the measure containing our offset.
8762        # In most cases this second part of the code does the job.
8763        myMeas = myStream.getElementAtOrBefore(searchOffset, classList=['Measure'])
8764        if myMeas is None:
8765            raise StreamException('beatAndMeasureFromOffset: no measure at that offset.')
8766        ts1 = myMeas.timeSignature
8767        if ts1 is None:
8768            ts1 = myMeas.getContextByClass('TimeSignature')
8769
8770        if ts1 is None:
8771            raise StreamException(
8772                'beatAndMeasureFromOffset: could not find a time signature for that place.')
8773        try:
8774            myBeat = ts1.getBeatProportion(searchOffset - myMeas.offset)
8775        except:
8776            raise StreamException(
8777                'beatAndMeasureFromOffset: offset is beyond the end of the piece')
8778        foundMeasureNumber = myMeas.number
8779        # deal with second half of partial measures...
8780
8781        # Now we deal with the problem case, where we have the second half of a partial measure.
8782        # These are
8783        # treated as unnumbered measures (or measures with suffix 'X') by notation programs,
8784        # even though they are
8785        # logically part of the previous measure.
8786        # The variable padBeats will represent extra beats we add to the front
8787        # of our partial measure
8788        numSuffix = myMeas.numberSuffix
8789        if numSuffix == '':
8790            numSuffix = None
8791
8792        if numSuffix is not None or (fixZeros and foundMeasureNumber == 0):
8793            prevMeas = myStream.getElementBeforeOffset(myMeas.offset, classList=['Measure'])
8794            if prevMeas:
8795                ts2 = prevMeas.getContextByClass('TimeSignature')
8796                if not ts2:
8797                    raise StreamException(
8798                        'beatAndMeasureFromOffset: partial measure found, '
8799                        + 'but could not find a time signature for the preceding measure')
8800                # foundMeasureNumber = prevMeas.number
8801
8802                # need this for chorales 197 and 280, where we
8803                # have a full-length measure followed by a pickup in
8804                # a new time signature
8805                if prevMeas.highestTime == ts2.barDuration.quarterLength:
8806                    padBeats = ts2.beatCount
8807                else:
8808                    padBeats = ts2.getBeatProportion(prevMeas.highestTime) - 1
8809                return (myBeat + padBeats, prevMeas)
8810            else:
8811                # partial measure at start of piece
8812                padBeats = ts1.getBeatProportion(
8813                    ts1.barDuration.quarterLength - myMeas.duration.quarterLength) - 1
8814                return (myBeat + padBeats, myMeas)
8815        else:
8816            return (myBeat, myMeas)
8817
8818    # --------------------------------------------------------------------------
8819    # transformations
8820
8821    def transpose(
8822        self,
8823        value,
8824        inPlace=False,
8825        recurse=True,
8826        classFilterList=None
8827    ):
8828        # noinspection PyShadowingNames
8829        '''
8830        Transpose all specified classes in the
8831        Stream by the
8832        user-provided value. If the value is an integer, the
8833        transposition is treated in half steps. If the value is
8834        a string, any Interval string specification can be
8835        provided.
8836
8837        returns a new Stream by default, but if the
8838        optional "inPlace" key is set to True then
8839        it modifies pitches in place.
8840
8841        TODO: for generic interval set accidental by key signature.
8842
8843        >>> aInterval = interval.Interval('d5')
8844
8845        >>> aStream = corpus.parse('bach/bwv324.xml')
8846        >>> part = aStream.parts[0]
8847        >>> [str(p) for p in aStream.parts[0].pitches[:10]]
8848        ['B4', 'D5', 'B4', 'B4', 'B4', 'B4', 'C5', 'B4', 'A4', 'A4']
8849
8850        >>> bStream = aStream.parts[0].flatten().transpose('d5')
8851        >>> [str(p) for p in bStream.pitches[:10]]
8852        ['F5', 'A-5', 'F5', 'F5', 'F5', 'F5', 'G-5', 'F5', 'E-5', 'E-5']
8853
8854        Test that aStream hasn't been changed:
8855
8856        >>> [str(p) for p in aStream.parts[0].pitches[:10]]
8857        ['B4', 'D5', 'B4', 'B4', 'B4', 'B4', 'C5', 'B4', 'A4', 'A4']
8858
8859        >>> cStream = bStream.flatten().transpose('a4')
8860        >>> [str(p) for p in cStream.pitches[:10]]
8861        ['B5', 'D6', 'B5', 'B5', 'B5', 'B5', 'C6', 'B5', 'A5', 'A5']
8862
8863        >>> cStream.flatten().transpose(aInterval, inPlace=True)
8864        >>> [str(p) for p in cStream.pitches[:10]]
8865        ['F6', 'A-6', 'F6', 'F6', 'F6', 'F6', 'G-6', 'F6', 'E-6', 'E-6']
8866        '''
8867        # only change the copy
8868        if not inPlace:
8869            post = self.coreCopyAsDerivation('transpose')
8870        else:
8871            post = self
8872        # for p in post.pitches:  # includes chords
8873        #     # do inplace transpositions on the deepcopy
8874        #     p.transpose(value, inPlace=True)
8875        #
8876        # for e in post.getElementsByClass(classFilterList=classFilterList):
8877        #     e.transpose(value, inPlace=True)
8878
8879        # this will get all elements at this level and downward.
8880        if recurse is True:
8881            sIterator = post.recurse()
8882        else:
8883            sIterator = post.__iter__()
8884
8885        if classFilterList:
8886            sIterator = sIterator.addFilter(filters.ClassFilter(classFilterList))
8887
8888        for e in sIterator:
8889            if e.isStream:
8890                continue
8891            if hasattr(e, 'transpose'):
8892                if (hasattr(value, 'classes')
8893                        and 'GenericInterval' in value.classes):
8894                    # do not transpose KeySignatures w/ Generic Intervals
8895                    if not isinstance(e, key.KeySignature) and hasattr(e, 'pitches'):
8896                        k = e.getContextByClass('KeySignature')
8897                        for p in e.pitches:
8898                            value.transposePitchKeyAware(p, k, inPlace=True)
8899                else:
8900                    e.transpose(value, inPlace=True)
8901        if not inPlace:
8902            return post
8903        else:
8904            return None
8905
8906    def scaleOffsets(self, amountToScale, *, anchorZero='lowest',
8907                     anchorZeroRecurse=None, inPlace=False):
8908        '''
8909        Scale all offsets by a multiplication factor given
8910        in `amountToScale`. Durations are not altered.
8911
8912        To augment or diminish a Stream,
8913        see the :meth:`~music21.stream.Stream.augmentOrDiminish` method.
8914
8915        The `anchorZero` parameter determines if and/or where
8916        the zero offset is established for the set of
8917        offsets in this Stream before processing.
8918        Offsets are shifted to make either the lower
8919        or upper values the new zero; then offsets are scaled;
8920        then the shifts are removed. Accepted values are None
8921        (no offset shifting), "lowest", or "highest".
8922
8923        The `anchorZeroRecurse` parameter determines the
8924        anchorZero for all embedded Streams, and Streams
8925        embedded within those Streams. If the lowest offset
8926        in an embedded Stream is non-zero, setting this value
8927        to None will a the space between the start of that
8928        Stream and the first element to be scaled. If the
8929        lowest offset in an embedded Stream is non-zero,
8930        setting this value to 'lowest' will not alter the
8931        space between the start of that Stream and the first
8932        element to be scaled.
8933
8934        To shift all the elements in a Stream, see the
8935        :meth:`~music21.stream.Stream.shiftElements` method.
8936
8937        Changed in v.5 -- inPlace is default False, and anchorZero, anchorZeroRecurse
8938        and inPlace are keyword only arguments.
8939        '''
8940        # if we have offsets at 0, 2, 4
8941        # we scale by 2, getting offsets at 0, 4, 8
8942
8943        # compare to offsets at 10, 12, 14
8944        # we scale by 2; if we do not anchor at lower, we get 20, 24, 28
8945        # if we anchor, we get 10, 14, 18
8946
8947        if not amountToScale > 0:
8948            raise StreamException('amountToScale must be greater than zero')
8949
8950        if not inPlace:  # make a copy
8951            returnObj = self.coreCopyAsDerivation('scaleOffsets')
8952        else:
8953            returnObj = self
8954
8955        # first, get the offset shift requested
8956        if anchorZero in ['lowest']:
8957            offsetShift = Fraction(returnObj.lowestOffset)
8958        elif anchorZero in ['highest']:
8959            offsetShift = Fraction(returnObj.highestOffset)
8960        elif anchorZero in [None]:
8961            offsetShift = Fraction(0, 1)
8962        else:
8963            raise StreamException(f'an anchorZero value of {anchorZero} is not accepted')
8964
8965        for e in returnObj._elements:
8966
8967            # subtract the offset shift (and lowestOffset of 80 becomes 0)
8968            # then apply the amountToScale
8969            o = (returnObj.elementOffset(e) - offsetShift) * amountToScale
8970            # after scaling, return the shift taken away
8971            o += offsetShift
8972
8973            # environLocal.printDebug(['changing offset', o, scalar, offsetShift])
8974
8975            returnObj.coreSetElementOffset(e, o)
8976            # need to look for embedded Streams, and call this method
8977            # on them, with inPlace , as already copied if
8978            # inPlace is != True
8979            # if hasattr(e, 'elements'):  # recurse time:
8980            if e.isStream:
8981                e.scaleOffsets(amountToScale,
8982                               anchorZero=anchorZeroRecurse,
8983                               anchorZeroRecurse=anchorZeroRecurse,
8984                               inPlace=True)
8985
8986        returnObj.coreElementsChanged()
8987        if not inPlace:
8988            return returnObj
8989
8990    def scaleDurations(self, amountToScale, *, inPlace=False):
8991        '''
8992        Scale all durations by a provided scalar. Offsets are not modified.
8993
8994        To augment or diminish a Stream, see the
8995        :meth:`~music21.stream.Stream.augmentOrDiminish` method.
8996
8997        We do not retain durations in any circumstance;
8998        if inPlace=False, two deepcopies of each duration are done.
8999        '''
9000        if not amountToScale > 0:
9001            raise StreamException('amountToScale must be greater than zero')
9002
9003        if not inPlace:  # make a copy
9004            returnObj = self.coreCopyAsDerivation('scaleDurations')
9005        else:
9006            returnObj = self
9007
9008        for e in returnObj.recurse().getElementsNotOfClass('Stream'):
9009            e.duration = e.duration.augmentOrDiminish(amountToScale)
9010
9011        if inPlace is not True:
9012            return returnObj
9013
9014    def augmentOrDiminish(self, amountToScale, *, inPlace=False):
9015        '''
9016        Given a number greater than zero,
9017        multiplies the current quarterLength of the
9018        duration of each element by this number
9019        as well as their offset and returns a new Stream.
9020        Or if `inPlace` is
9021        set to True, modifies the durations of each
9022        element within the stream.
9023
9024
9025        A number of 0.5 will halve the durations and relative
9026        offset positions; a number of 2 will double the
9027        durations and relative offset positions.
9028
9029
9030
9031        Note that the default for inPlace is the opposite
9032        of what it is for augmentOrDiminish on a Duration.
9033        This is done purposely to reflect the most common
9034        usage.
9035
9036
9037
9038        >>> s = stream.Stream()
9039        >>> n = note.Note()
9040        >>> s.repeatAppend(n, 10)
9041        >>> s.highestOffset, s.highestTime
9042        (9.0, 10.0)
9043        >>> s1 = s.augmentOrDiminish(2)
9044        >>> s1.highestOffset, s1.highestTime
9045        (18.0, 20.0)
9046        >>> s1 = s.augmentOrDiminish(0.5)
9047        >>> s1.highestOffset, s1.highestTime
9048        (4.5, 5.0)
9049        '''
9050        if not amountToScale > 0:
9051            raise StreamException('amountToScale must be greater than zero')
9052        if not inPlace:  # make a copy
9053            returnObj = self.coreCopyAsDerivation('augmentOrDiminish')
9054        else:
9055            returnObj = self
9056
9057        # inPlace is True as a copy has already been made if nec
9058        returnObj.scaleOffsets(amountToScale=amountToScale, anchorZero='lowest',
9059                               anchorZeroRecurse=None, inPlace=True)
9060        returnObj.scaleDurations(amountToScale=amountToScale, inPlace=True)
9061
9062        # do not need to call elements changed, as called in sub methods
9063        return returnObj
9064
9065    def quantize(
9066        self,
9067        quarterLengthDivisors=None,
9068        processOffsets=True,
9069        processDurations=True,
9070        inPlace=False,
9071        recurse=False,
9072    ):
9073        # noinspection PyShadowingNames
9074        '''
9075        Quantize time values in this Stream by snapping offsets
9076        and/or durations to the nearest multiple of a quarter length value
9077        given as one or more divisors of 1 quarter length. The quantized
9078        value found closest to a divisor multiple will be used.
9079
9080        The `quarterLengthDivisors` provides a flexible way to provide quantization
9081        settings. For example, (2,) will snap all events to eighth note grid.
9082        (4, 3) will snap events to sixteenth notes and eighth note triplets,
9083        whichever is closer. (4, 6) will snap events to sixteenth notes and
9084        sixteenth note triplets.  If quarterLengthDivisors is not specified then
9085        defaults.quantizationQuarterLengthDivisors is used.  The default is (4, 3).
9086
9087        `processOffsets` determines whether the Offsets are quantized.
9088
9089        `processDurations` determines whether the Durations are quantized.
9090
9091        Both are set to True by default.  Setting both to False does nothing to the Stream.
9092
9093        if `inPlace` is True, then the quantization is done on the Stream itself.  If False
9094        (default) then a new quantized Stream of the same class is returned.
9095
9096        If `recurse` is True, then all substreams are also quantized.
9097        If False (default), then only the highest level of the Stream is quantized.
9098
9099        Changed in v.7:
9100           - `recurse` defaults False
9101           - look-ahead approach to choosing divisors to avoid gaps when processing durations
9102
9103        >>> n = note.Note()
9104        >>> n.quarterLength = 0.49
9105        >>> s = stream.Stream()
9106        >>> s.repeatInsert(n, [0.1, 0.49, 0.9])
9107        >>> nShort = note.Note()
9108        >>> nShort.quarterLength = 0.26
9109        >>> s.repeatInsert(nShort, [1.49, 1.76])
9110
9111        >>> s.quantize([4], processOffsets=True, processDurations=True, inPlace=True)
9112        >>> [e.offset for e in s]
9113        [0.0, 0.5, 1.0, 1.5, 1.75]
9114        >>> [e.duration.quarterLength for e in s]
9115        [0.5, 0.5, 0.5, 0.25, 0.25]
9116
9117        The error in quantization is set in the editorial attribute for the note in
9118        two places `.offsetQuantizationError` and `.quarterLengthQuantizationError`
9119
9120        >>> [e.editorial.offsetQuantizationError for e in s.notes]
9121        [0.1, -0.01, -0.1, -0.01, 0.01]
9122        >>> [e.editorial.quarterLengthQuantizationError for e in s.notes]
9123        [-0.01, -0.01, -0.01, 0.01, 0.01]
9124
9125        with default quarterLengthDivisors...
9126
9127        >>> s = stream.Stream()
9128        >>> s.repeatInsert(n, [0.1, 0.49, 0.9])
9129        >>> nShort = note.Note()
9130        >>> nShort.quarterLength = 0.26
9131        >>> s.repeatInsert(nShort, [1.49, 1.76])
9132        >>> t = s.quantize(processOffsets=True, processDurations=True, inPlace=False)
9133        >>> [e.offset for e in t]
9134        [0.0, 0.5, 1.0, 1.5, 1.75]
9135        >>> [e.duration.quarterLength for e in t]
9136        [0.5, 0.5, 0.5, 0.25, 0.25]
9137
9138        Set `recurse=True` to quantize elements in substreams such as parts, measures, voices:
9139
9140        >>> myPart = converter.parse('tinynotation: c32 d32 e32 f32')
9141        >>> myPart.quantize(inPlace=True)
9142        >>> [e.offset for e in myPart.measure(1).notes]  # no change!
9143        [0.0, 0.125, 0.25, 0.375]
9144
9145        >>> myPart.quantize(inPlace=True, recurse=True)
9146        >>> [e.offset for e in myPart.measure(1).notes]
9147        [0.0, 0.0, 0.25, Fraction(1, 3)]
9148
9149        New in v.7: if both `processDurations` and `processOffsets` are True, then
9150        the next note's quantized offset is taken into account when quantizing the
9151        duration of the current note. This is to prevent unnecessary gaps from applying
9152        different quantization units to adjacent notes:
9153
9154        >>> s2 = stream.Stream()
9155        >>> nOddLength = note.Note(quarterLength=0.385)
9156        >>> s2.repeatInsert(nOddLength, [0, 0.5, 1, 1.5])
9157        >>> s2.show('t', addEndTimes=True)
9158            {0.0 - 0.385} <music21.note.Note C>
9159            {0.5 - 0.885} <music21.note.Note C>
9160            {1.0 - 1.385} <music21.note.Note C>
9161            {1.5 - 1.885} <music21.note.Note C>
9162
9163        Before v.7, this would have yielded four triplet-eighths (separated by 1/6 QL rests):
9164
9165        >>> s2.quantize(processOffsets=True, processDurations=True, inPlace=True)
9166        >>> s2.show('text', addEndTimes=True)
9167            {0.0 - 0.5} <music21.note.Note C>
9168            {0.5 - 1.0} <music21.note.Note C>
9169            {1.0 - 1.5} <music21.note.Note C>
9170            {1.5 - 1.8333} <music21.note.Note C>
9171
9172        OMIT_FROM_DOCS
9173
9174        Test changing defaults, running, and changing back...
9175
9176        >>> dd = defaults.quantizationQuarterLengthDivisors
9177        >>> defaults.quantizationQuarterLengthDivisors = (3,)
9178
9179        >>> u = s.quantize(processOffsets=True, processDurations=True, inPlace=False)
9180        >>> [e.offset for e in u]
9181        [0.0, Fraction(1, 3), 1.0, Fraction(4, 3), Fraction(5, 3)]
9182        >>> [e.duration.quarterLength for e in u]
9183        [Fraction(1, 3), Fraction(1, 3), Fraction(1, 3), Fraction(1, 3), Fraction(1, 3)]
9184
9185        Original unchanged because inPlace=False:
9186
9187        >>> [e.offset for e in s]
9188        [Fraction(1, 10), Fraction(49, 100), Fraction(9, 10), Fraction(149, 100), Fraction(44, 25)]
9189
9190        >>> defaults.quantizationQuarterLengthDivisors = dd
9191        >>> v = s.quantize(processOffsets=True, processDurations=True, inPlace=False)
9192        >>> [e.offset for e in v]
9193        [0.0, 0.5, 1.0, 1.5, 1.75]
9194        >>> [e.duration.quarterLength for e in v]
9195        [0.5, 0.5, 0.5, 0.25, 0.25]
9196        '''
9197        if quarterLengthDivisors is None:
9198            quarterLengthDivisors = defaults.quantizationQuarterLengthDivisors
9199
9200        # this presently is not trying to avoid overlaps that
9201        # result from quantization; this may be necessary
9202
9203        def bestMatch(target, divisors):
9204            found = []
9205            for div in divisors:
9206                match, error, signedErrorInner = common.nearestMultiple(target, (1 / div))
9207                # Sort by unsigned error, then "tick" (divisor expressed as QL, e.g. 0.25)
9208                found.append(BestQuantizationMatch(error, 1 / div, match, signedErrorInner, div))
9209            # get first, and leave out the error
9210            bestMatchTuple = sorted(found)[0]
9211            return bestMatchTuple
9212
9213        # if we have a min of 0.25 (sixteenth)
9214        # quarterLengthMin = quarterLengthDivisors[0]
9215
9216        if inPlace is False:
9217            returnStream = self.coreCopyAsDerivation('quantize')
9218        else:
9219            returnStream = self
9220
9221        useStreams = [returnStream]
9222        if recurse is True:
9223            useStreams = returnStream.recurse(streamsOnly=True, includeSelf=True)
9224
9225        rests_lacking_durations: List[note.Rest] = []
9226        for useStream in useStreams:
9227            for i, e in enumerate(useStream._elements):
9228                if processOffsets:
9229                    o = useStream.elementOffset(e)
9230                    sign = 1
9231                    if o < 0:
9232                        sign = -1
9233                        o = -1 * o
9234                    o_matchTuple = bestMatch(float(o), quarterLengthDivisors)
9235                    useStream.coreSetElementOffset(e, o_matchTuple.match * sign)
9236                    if hasattr(e, 'editorial') and o_matchTuple.signedError != 0:
9237                        e.editorial.offsetQuantizationError = o_matchTuple.signedError * sign
9238                if processDurations:
9239                    ql = e.duration.quarterLength
9240                    ql = max(ql, 0)  # negative ql possible in buggy MIDI files?
9241                    d_matchTuple = bestMatch(float(ql), quarterLengthDivisors)
9242                    # Check that any gaps from this quantized duration to the next onset
9243                    # are at least as large as the smallest quantization unit (largest divisor)
9244                    # If not, then re-quantize this duration with the divisor
9245                    # that will be used to quantize the next element's offset
9246                    if processOffsets and i + 1 < len(useStream._elements):
9247                        next_element = useStream._elements[i + 1]
9248                        next_offset = useStream.elementOffset(next_element)
9249                        look_ahead_result = bestMatch(float(next_offset), quarterLengthDivisors)
9250                        next_offset = look_ahead_result.match
9251                        next_divisor = look_ahead_result.divisor
9252                        if (0 < next_offset - (e.offset + d_matchTuple.match)
9253                                < 1 / max(quarterLengthDivisors)):
9254                            # Overwrite the earlier matchTuple with a better result
9255                            d_matchTuple = bestMatch(float(ql), (next_divisor,))
9256                    # Enforce nonzero duration for non-grace notes
9257                    if (d_matchTuple.match == 0
9258                            and isinstance(e, note.NotRest)
9259                            and not e.duration.isGrace):
9260                        e.quarterLength = 1 / max(quarterLengthDivisors)
9261                        if hasattr(e, 'editorial'):
9262                            e.editorial.quarterLengthQuantizationError = ql - e.quarterLength
9263                    elif d_matchTuple.match == 0 and isinstance(e, note.Rest):
9264                        rests_lacking_durations.append(e)
9265                    else:
9266                        e.duration.quarterLength = d_matchTuple.match
9267                        if hasattr(e, 'editorial') and d_matchTuple.signedError != 0:
9268                            e.editorial.quarterLengthQuantizationError = d_matchTuple.signedError
9269
9270            # end for e in ._elements
9271            # ran coreSetElementOffset
9272            useStream.coreElementsChanged(updateIsFlat=False)
9273
9274            for rest_to_remove in rests_lacking_durations:
9275                useStream.remove(rest_to_remove)
9276
9277        if inPlace is False:
9278            return returnStream
9279
9280    def expandRepeats(self, copySpanners=True):
9281        '''
9282        Expand this Stream with repeats. Nested repeats
9283        given with :class:`~music21.bar.Repeat` objects, or
9284        repeats and sections designated with
9285        :class:`~music21.repeat.RepeatExpression` objects, are all expanded.
9286
9287        This method always returns a new Stream, with
9288        deepcopies of all contained elements at all levels.
9289
9290        Uses the :class:`~music21.repeat.Expander` object in the `repeat` module.
9291
9292        TODO: DOC TEST
9293        '''
9294        if not self.hasMeasures():
9295            raise StreamException(
9296                'cannot process repeats on Stream that does not contain measures'
9297            )
9298
9299        ex = repeat.Expander(self)
9300        post = ex.process()
9301
9302        # copy all non-repeats
9303        # do not copy repeat brackets
9304        for e in self.getElementsNotOfClass('Measure'):
9305            if 'RepeatBracket' not in e.classes:
9306                eNew = copy.deepcopy(e)  # assume that this is needed
9307                post.insert(self.elementOffset(e), eNew)
9308
9309        # all elements at this level and in measures have been copied; now we
9310        # need to reconnect spanners
9311        if copySpanners:
9312            # environLocal.printDebug(['Stream.expandRepeats', 'copying spanners'])
9313            # spannerBundle = spanner.SpannerBundle(list(post.flatten().spanners))
9314            spannerBundle = post.spannerBundle
9315            # iterate over complete tree (need containers); find
9316            # all new/old pairs
9317            for e in post.recurse():
9318                # update based on last id, new object
9319                if e.sites.hasSpannerSite():
9320                    origin = e.derivation.origin
9321                    if (origin is not None
9322                            and e.derivation.method == '__deepcopy__'):
9323                        spannerBundle.replaceSpannedElement(origin, e)
9324
9325        return post
9326
9327    # --------------------------------------------------------------------------
9328    # slicing and recasting a note as many notes
9329
9330    def sliceByQuarterLengths(self, quarterLengthList, *, target=None,
9331                              addTies=True, inPlace=False):
9332        '''
9333        Slice all :class:`~music21.duration.Duration` objects on all Notes and Rests
9334        of this Stream.
9335        Duration are sliced according to values provided in `quarterLengthList` list.
9336        If the sum of these values is less than the Duration, the values are accumulated
9337        in a loop to try to fill the Duration. If a match cannot be found, an
9338        Exception is raised.
9339
9340        If `target` is None, the entire Stream is processed. Otherwise, only the element
9341        specified is manipulated.
9342
9343        '''
9344        if not inPlace:  # make a copy
9345            returnObj = self.coreCopyAsDerivation('sliceByQuarterLengths')
9346        else:
9347            returnObj = self
9348
9349        if returnObj.hasMeasures():
9350            # call on component measures
9351            for m in returnObj.getElementsByClass('Measure'):
9352                m.sliceByQuarterLengths(quarterLengthList,
9353                                        target=target, addTies=addTies, inPlace=True)
9354            returnObj.coreElementsChanged()
9355            return returnObj  # exit
9356
9357        if returnObj.hasPartLikeStreams():
9358            for p in returnObj.getElementsByClass('Part'):
9359                p.sliceByQuarterLengths(quarterLengthList,
9360                                        target=target, addTies=addTies, inPlace=True)
9361            returnObj.coreElementsChanged()
9362            return returnObj  # exit
9363
9364        if not common.isListLike(quarterLengthList):
9365            quarterLengthList = [quarterLengthList]
9366
9367        if target is not None:
9368            # get the element out of return obj
9369            # need to use self.index to get index value
9370            eToProcess = [returnObj[self.index(target)]]
9371        else:  # get elements list from Stream
9372            eToProcess = returnObj.notesAndRests
9373
9374        for e in eToProcess:
9375            # if qlList values are greater than the found duration, skip
9376            if opFrac(sum(quarterLengthList)) > e.quarterLength:
9377                continue
9378            elif not opFrac(sum(quarterLengthList)) == e.quarterLength:
9379                # try to map a list that is of sufficient duration
9380                qlProcess = []
9381                i = 0
9382                while True:
9383                    qlProcess.append(
9384                        quarterLengthList[i % len(quarterLengthList)])
9385                    i += 1
9386                    sumQL = opFrac(sum(qlProcess))
9387                    if sumQL >= e.quarterLength:
9388                        break
9389            else:
9390                qlProcess = quarterLengthList
9391
9392            # environLocal.printDebug(['got qlProcess', qlProcess,
9393            # 'for element', e, e.quarterLength])
9394
9395            if not opFrac(sum(qlProcess)) == e.quarterLength:
9396                raise StreamException(
9397                    'cannot map quarterLength list into element Duration: %s, %s' % (
9398                        sum(qlProcess), e.quarterLength))
9399
9400            post = e.splitByQuarterLengths(qlProcess, addTies=addTies)
9401            # remove e from the source
9402            oInsert = e.getOffsetBySite(returnObj)
9403            returnObj.remove(e)
9404            for eNew in post:
9405                returnObj.coreInsert(oInsert, eNew)
9406                oInsert = opFrac(oInsert + eNew.quarterLength)
9407
9408        returnObj.coreElementsChanged()
9409
9410        if not inPlace:
9411            return returnObj
9412
9413    def sliceByGreatestDivisor(self, *, addTies=True, inPlace=False):
9414        '''
9415        Slice all :class:`~music21.duration.Duration` objects on all Notes and Rests of this Stream.
9416        Duration are sliced according to the approximate GCD found in all durations.
9417        '''
9418        # when operating on a Stream, this should take all durations found
9419        # and use the approximateGCD to get a min duration; then, call sliceByQuarterLengths
9420
9421        if not inPlace:  # make a copy
9422            returnObj = self.coreCopyAsDerivation('sliceByGreatestDivisor')
9423        else:
9424            returnObj = self
9425
9426        if returnObj.hasMeasures():
9427            # call on component measures
9428            for m in returnObj.getElementsByClass('Measure'):
9429                m.sliceByGreatestDivisor(addTies=addTies, inPlace=True)
9430            return returnObj  # exit
9431
9432        uniqueQuarterLengths = []
9433        for e in returnObj.notesAndRests:
9434            if e.quarterLength not in uniqueQuarterLengths:
9435                uniqueQuarterLengths.append(e.quarterLength)
9436
9437        # environLocal.printDebug(['unique quarter lengths', uniqueQuarterLengths])
9438
9439        # will raise an exception if no gcd can be found
9440        divisor = common.approximateGCD(uniqueQuarterLengths)
9441
9442        # process in place b/c a copy, if necessary, has already been made
9443        returnObj.sliceByQuarterLengths(quarterLengthList=[divisor],
9444                                        target=None, addTies=addTies, inPlace=True)
9445
9446        if not inPlace:
9447            return returnObj
9448
9449    def sliceAtOffsets(
9450        self,
9451        offsetList,
9452        target=None,
9453        addTies=True,
9454        inPlace=False,
9455        displayTiedAccidentals=False
9456    ):
9457        # noinspection PyShadowingNames
9458        '''
9459        Given a list of quarter lengths, slice and optionally tie all
9460        Music21Objects crossing these points.
9461
9462        >>> s = stream.Stream()
9463        >>> n = note.Note()
9464        >>> n.duration.type = 'whole'
9465        >>> s.append(n)
9466        >>> post = s.sliceAtOffsets([1, 2, 3], inPlace=True)
9467        >>> [(e.offset, e.quarterLength) for e in s]
9468        [(0.0, 1.0), (1.0, 1.0), (2.0, 1.0), (3.0, 1.0)]
9469        '''
9470        if not inPlace:  # make a copy
9471            returnObj = self.coreCopyAsDerivation('sliceAtOffsets')
9472        else:
9473            returnObj = self
9474
9475        if returnObj.hasMeasures():
9476            # call on component measures
9477            for m in returnObj.getElementsByClass('Measure'):
9478                # offset values are not relative to measure; need to
9479                # shift by each measure's offset
9480                offsetListLocal = [o - m.getOffsetBySite(returnObj) for o in offsetList]
9481                m.sliceAtOffsets(offsetList=offsetListLocal,
9482                                 addTies=addTies,
9483                                 inPlace=True,
9484                                 displayTiedAccidentals=displayTiedAccidentals)
9485            return returnObj  # exit
9486
9487        if returnObj.hasPartLikeStreams():
9488            # part-like requires getting Streams, not Parts
9489            for p in returnObj.getElementsByClass('Stream'):
9490                offsetListLocal = [o - p.getOffsetBySite(returnObj) for o in offsetList]
9491                p.sliceAtOffsets(offsetList=offsetListLocal,
9492                                 addTies=addTies,
9493                                 inPlace=True,
9494                                 displayTiedAccidentals=displayTiedAccidentals)
9495            return returnObj  # exit
9496
9497        # list of start, start+dur, element, all in abs offset time
9498        offsetMap = self.offsetMap(returnObj)
9499
9500        offsetList = [opFrac(o) for o in offsetList]
9501
9502        for ob in offsetMap:
9503            # if target is defined, only modify that object
9504            e, oStart, oEnd, unused_voiceCount = ob
9505            if target is not None and id(e) != id(target):
9506                continue
9507
9508            cutPoints = []
9509            oStart = opFrac(oStart)
9510            oEnd = opFrac(oEnd)
9511
9512            for o in offsetList:
9513                if oStart < o < oEnd:
9514                    cutPoints.append(o)
9515            # environLocal.printDebug(['cutPoints', cutPoints, 'oStart', oStart, 'oEnd', oEnd])
9516            if cutPoints:
9517                # remove old
9518                # eProc = returnObj.remove(e)
9519                eNext = e
9520                oStartNext = oStart
9521                for o in cutPoints:
9522                    oCut = o - oStartNext
9523                    unused_eComplete, eNext = eNext.splitAtQuarterLength(
9524                        oCut,
9525                        retainOrigin=True,
9526                        addTies=addTies,
9527                        displayTiedAccidentals=displayTiedAccidentals
9528                    )
9529                    # only need to insert eNext, as eComplete was modified
9530                    # in place due to retainOrigin option
9531                    # insert at o, not oCut (duration into element)
9532                    returnObj.coreInsert(o, eNext)
9533                    oStartNext = o
9534        returnObj.coreElementsChanged()
9535        if inPlace is False:
9536            return returnObj
9537
9538    def sliceByBeat(self,
9539                    target=None,
9540                    addTies=True,
9541                    inPlace=False,
9542                    displayTiedAccidentals=False):
9543        '''
9544        Slice all elements in the Stream that have a Duration at
9545        the offsets determined to be the beat from the local TimeSignature.
9546
9547        Changed in v7. -- return None if inPlace is True
9548        '''
9549
9550        if not inPlace:  # make a copy
9551            returnObj = self.coreCopyAsDerivation('sliceByBeat')
9552        else:
9553            returnObj = self
9554
9555        if returnObj.hasMeasures():
9556            # call on component measures
9557            for m in returnObj.getElementsByClass('Measure'):
9558                m.sliceByBeat(target=target,
9559                              addTies=addTies,
9560                              inPlace=True,
9561                              displayTiedAccidentals=displayTiedAccidentals)
9562            return returnObj  # exit
9563
9564        if returnObj.hasPartLikeStreams():
9565            for p in returnObj.getElementsByClass('Part'):
9566                p.sliceByBeat(target=target,
9567                              addTies=addTies,
9568                              inPlace=True,
9569                              displayTiedAccidentals=displayTiedAccidentals)
9570            return returnObj  # exit
9571
9572        # this will return a default
9573        # using this method to work on Stream, not just Measures
9574        tsStream = returnObj.getTimeSignatures(returnDefault=True)
9575
9576        if not tsStream:
9577            raise StreamException('no time signature was found')
9578
9579        if len(tsStream) > 1:
9580            raise StreamException('not yet implemented: slice by changing time signatures')
9581
9582        offsetList = tsStream[0].getBeatOffsets()
9583        returnObj.sliceAtOffsets(offsetList,
9584                                 target=target,
9585                                 addTies=addTies,
9586                                 inPlace=True,
9587                                 displayTiedAccidentals=displayTiedAccidentals)
9588
9589        if not inPlace:
9590            return returnObj
9591
9592    # --------------------------------------------------------------------------
9593    # get boolean information from the Stream
9594
9595    def hasMeasures(self):
9596        '''
9597        Return a boolean value showing if this Stream contains Measures.
9598
9599
9600        >>> s = stream.Stream()
9601        >>> s.repeatAppend(note.Note(), 8)
9602        >>> s.hasMeasures()
9603        False
9604        >>> s.makeMeasures(inPlace=True)
9605        >>> len(s.getElementsByClass('Measure'))
9606        2
9607        >>> s.hasMeasures()
9608        True
9609        '''
9610        if 'hasMeasures' not in self._cache or self._cache['hasMeasures'] is None:
9611            post = False
9612            # do not need to look in endElements
9613            for obj in self._elements:
9614                # if obj is a Part, we have multi-parts
9615                if getattr(obj, 'isMeasure', False):
9616                    post = True
9617                    break  # only need one
9618            self._cache['hasMeasures'] = post
9619        return self._cache['hasMeasures']
9620
9621    def hasVoices(self):
9622        '''
9623        Return a boolean value showing if this Stream contains Voices
9624        '''
9625        if 'hasVoices' not in self._cache or self._cache['hasVoices'] is None:
9626            post = False
9627            # do not need to look in endElements
9628            for obj in self._elements:
9629                # if obj is a Part, we have multi-parts
9630                if 'Voice' in obj.classes:
9631                    post = True
9632                    break  # only need one
9633            self._cache['hasVoices'] = post
9634        return self._cache['hasVoices']
9635
9636    def hasPartLikeStreams(self):
9637        '''
9638        Return a boolean value showing if this Stream contains any Parts,
9639        or Part-like sub-Streams.
9640
9641        Part-like sub-streams are Streams that contain Measures or Notes.
9642        And where no sub-stream begins at an offset besides zero.
9643
9644        >>> s = stream.Score()
9645        >>> s.hasPartLikeStreams()
9646        False
9647        >>> p1 = stream.Part()
9648        >>> p1.repeatAppend(note.Note(), 8)
9649        >>> s.insert(0, p1)
9650        >>> s.hasPartLikeStreams()
9651        True
9652
9653        A stream that has a measure in it is not a part-like stream.
9654
9655        >>> s = stream.Score()
9656        >>> m1 = stream.Measure()
9657        >>> m1.repeatAppend(note.Note(), 4)
9658        >>> s.append(m1)
9659        >>> s.hasPartLikeStreams()
9660        False
9661
9662
9663        A stream with a single generic Stream substream at the beginning has part-like Streams...
9664
9665        >>> s = stream.Score()
9666        >>> m1 = stream.Stream()
9667        >>> m1.repeatAppend(note.Note(), 4)
9668        >>> s.append(m1)
9669        >>> s.hasPartLikeStreams()
9670        True
9671
9672
9673        Adding another though makes it not part-like.
9674
9675        >>> m2 = stream.Stream()
9676        >>> m2.repeatAppend(note.Note(), 4)
9677        >>> s.append(m2)
9678        >>> s.hasPartLikeStreams()
9679        False
9680
9681        Flat objects do not have part-like Streams:
9682
9683        >>> sf = s.flatten()
9684        >>> sf.hasPartLikeStreams()
9685        False
9686        '''
9687        if 'hasPartLikeStreams' not in self._cache or self._cache['hasPartLikeStreams'] is None:
9688            multiPart = False
9689            if not self.isFlat:  # if flat, does not have parts!
9690                # do not need to look in endElements
9691                obj: base.Music21Object
9692                for obj in self.getElementsByClass('Stream'):
9693                    # if obj is a Part, we have multi-parts
9694                    if 'Part' in obj.classes:
9695                        multiPart = True
9696                        break
9697
9698                    elif 'Measure' in obj.classes or 'Voice' in obj.classes:
9699                        multiPart = False
9700                        break
9701
9702                    elif obj.offset != 0.0:
9703                        multiPart = False
9704                        break
9705
9706                    # if components are streams of Notes or Measures,
9707                    # than assume this is like a Part
9708                    elif (obj.getElementsByClass('Measure')
9709                          or obj.notesAndRests):
9710                        multiPart = True
9711            self._cache['hasPartLikeStreams'] = multiPart
9712        return self._cache['hasPartLikeStreams']
9713
9714    def isTwelveTone(self):
9715        '''
9716        Return true if this Stream only employs twelve-tone equal-tempered pitch values.
9717
9718
9719        >>> s = stream.Stream()
9720        >>> s.append(note.Note('G#4'))
9721        >>> s.isTwelveTone()
9722        True
9723        >>> s.append(note.Note('G~4'))
9724        >>> s.isTwelveTone()
9725        False
9726        '''
9727        for p in self.pitches:
9728            if not p.isTwelveTone():
9729                return False
9730        return True
9731
9732    def isWellFormedNotation(self) -> bool:
9733        # noinspection PyShadowingNames
9734        '''
9735        Return True if, given the context of this Stream or Stream subclass,
9736        contains what appears to be well-formed notation. This often means
9737        the formation of Measures, or a Score that contains Part with Measures.
9738
9739
9740        >>> s = corpus.parse('bwv66.6')
9741        >>> s.isWellFormedNotation()
9742        True
9743        >>> s.parts[0].isWellFormedNotation()
9744        True
9745        >>> s.parts[0].getElementsByClass('Measure').first().isWellFormedNotation()
9746        True
9747
9748        >>> s2 = stream.Score()
9749        >>> m = stream.Measure()
9750        >>> s2.append(m)
9751        >>> s2.isWellFormedNotation()
9752        False
9753
9754        >>> o = stream.Opus([s])
9755        >>> o.isWellFormedNotation()
9756        True
9757        >>> o2 = stream.Opus([s2])
9758        >>> o2.isWellFormedNotation()
9759        False
9760
9761        Only Measures and Voices are allowed to contain notes and rests directly:
9762        >>> m.isWellFormedNotation()
9763        True
9764        >>> s2.append(note.Rest())
9765        >>> s2.isWellFormedNotation()
9766        False
9767        '''
9768        def allSubstreamsHaveMeasures(testStream):
9769            return all(s.hasMeasures() for s in testStream.getElementsByClass('Stream'))
9770
9771        # if a measure or voice, we assume we are well-formed
9772        if 'Measure' in self.classes or 'Voice' in self.classes:
9773            return True
9774        # all other Stream classes are not well-formed if they have "loose" notes
9775        elif self.getElementsByClass('GeneralNote'):
9776            return False
9777        elif 'Part' in self.classes:
9778            if self.hasMeasures():
9779                return True
9780        elif 'Opus' in self.classes:
9781            return all(allSubstreamsHaveMeasures(s) for s in self.scores)
9782        elif self.hasPartLikeStreams():
9783            return allSubstreamsHaveMeasures(self)
9784        # all other conditions are not well-formed notation
9785        return False
9786
9787    # --------------------------------------------------------------------------
9788    @property
9789    def notesAndRests(self):
9790        '''
9791        The notesAndRests property of a Stream returns a `StreamIterator`
9792        that consists only of the :class:`~music21.note.GeneralNote` objects found in
9793        the stream.  The new Stream will contain
9794        mostly notes and rests (including
9795        :class:`~music21.note.Note`,
9796        :class:`~music21.chord.Chord`,
9797        :class:`~music21.note.Rest`) but also their subclasses, such as
9798        `Harmony` objects (`ChordSymbols`, `FiguredBass`), etc.
9799
9800
9801        >>> s1 = stream.Stream()
9802        >>> k1 = key.KeySignature(0)  # key of C
9803        >>> n1 = note.Note('B')
9804        >>> r1 = note.Rest()
9805        >>> c1 = chord.Chord(['A', 'B-'])
9806        >>> s1.append([k1, n1, r1, c1])
9807        >>> s1.show('text')
9808        {0.0} <music21.key.KeySignature of no sharps or flats>
9809        {0.0} <music21.note.Note B>
9810        {1.0} <music21.note.Rest quarter>
9811        {2.0} <music21.chord.Chord A B->
9812
9813        `.notesAndRests` removes the `KeySignature` object but keeps the `Rest`.
9814
9815        >>> notes1 = s1.notesAndRests.stream()
9816        >>> notes1.show('text')
9817        {0.0} <music21.note.Note B>
9818        {1.0} <music21.note.Rest quarter>
9819        {2.0} <music21.chord.Chord A B->
9820
9821        The same caveats about `Stream` classes and `.flatten()` in `.notes` apply here.
9822        '''
9823        if 'notesAndRests' not in self._cache or self._cache['notesAndRests'] is None:
9824            # environLocal.printDebug(['updating noteAndRests cache:', str(self), id(self)])
9825            noteIterator = self.getElementsByClass('GeneralNote')
9826            noteIterator.overrideDerivation = 'notesAndRests'
9827            self._cache['notesAndRests'] = noteIterator
9828        return self._cache['notesAndRests']
9829
9830        # return self.getElementsByClass([note.GeneralNote, chord.Chord])
9831        # using string class names is import for some test contexts where
9832        # absolute class name matching fails
9833        # return self.getElementsByClass(['GeneralNote', 'Chord'])
9834
9835    @property
9836    def notes(self):
9837        '''
9838        The notes property of a Stream returns an iterator
9839        that consists only of the notes (that is,
9840        :class:`~music21.note.Note`,
9841        :class:`~music21.chord.Chord`, etc.) found
9842        in the stream. This excludes :class:`~music21.note.Rest` objects.
9843
9844
9845        >>> p1 = stream.Part()
9846        >>> k1 = key.KeySignature(0)  # key of C
9847        >>> n1 = note.Note('B')
9848        >>> r1 = note.Rest()
9849        >>> c1 = chord.Chord(['A', 'B-'])
9850        >>> p1.append([k1, n1, r1, c1])
9851        >>> p1.show('text')
9852        {0.0} <music21.key.KeySignature of no sharps or flats>
9853        {0.0} <music21.note.Note B>
9854        {1.0} <music21.note.Rest quarter>
9855        {2.0} <music21.chord.Chord A B->
9856
9857        >>> noteStream = p1.notes.stream()
9858        >>> noteStream.show('text')
9859        {0.0} <music21.note.Note B>
9860        {2.0} <music21.chord.Chord A B->
9861
9862        Notice that `.notes` returns a :class:`~music21.stream.iterator.StreamIterator` object
9863
9864        >>> p1.notes
9865        <music21.stream.iterator.StreamIterator for Part:0x105b56128 @:0>
9866
9867        Let's add a measure to `p1`:
9868
9869        >>> m1 = stream.Measure()
9870        >>> n2 = note.Note('D')
9871        >>> m1.insert(0, n2)
9872        >>> p1.append(m1)
9873
9874        Now note that `n2` is *not* found in `p1.notes`
9875
9876        >>> p1.notes.stream().show('text')
9877        {0.0} <music21.note.Note B>
9878        {2.0} <music21.chord.Chord A B->
9879
9880        We need to call `p1.flatten().notes` to find it:
9881
9882        >>> p1.flatten().notes.stream().show('text')
9883        {0.0} <music21.note.Note B>
9884        {2.0} <music21.chord.Chord A B->
9885        {3.0} <music21.note.Note D>
9886
9887        (Technical note: All elements of class NotRest are being found
9888        right now.  This will eventually change to also filter out
9889        Unpitched objects, so that all elements returned by
9890        `.notes` have a `.pitches` attribute.
9891        '''
9892        if 'notes' not in self._cache or self._cache['notes'] is None:
9893            noteIterator = self.getElementsByClass('NotRest')
9894            noteIterator.overrideDerivation = 'notes'
9895            self._cache['notes'] = noteIterator
9896        return self._cache['notes']
9897
9898    @property
9899    def pitches(self):
9900        '''
9901        Returns all :class:`~music21.pitch.Pitch` objects found in any
9902        element in the Stream as a Python List. Elements such as
9903        Streams, and Chords will have their Pitch objects accumulated as
9904        well. For that reason, a flat representation is not required.
9905
9906        Pitch objects are returned in a List, not a Stream.  This usage
9907        differs from the .notes property, but makes sense since Pitch
9908        objects usually have by default a Duration of zero. This is an important difference
9909        between them and :class:`music21.note.Note` objects.
9910
9911        key.Key objects are subclasses of Scales, which DO have a .pitches list, but
9912        they are specifically exempt from looking for pitches, since that is unlikely
9913        what someone wants here.
9914
9915        N.B., TODO: This may turn to an Iterator soon.
9916
9917        >>> from music21 import corpus
9918        >>> a = corpus.parse('bach/bwv324.xml')
9919        >>> partOnePitches = a.parts[0].pitches
9920        >>> len(partOnePitches)
9921        25
9922        >>> partOnePitches[0]
9923        <music21.pitch.Pitch B4>
9924        >>> [str(p) for p in partOnePitches[0:10]]
9925        ['B4', 'D5', 'B4', 'B4', 'B4', 'B4', 'C5', 'B4', 'A4', 'A4']
9926
9927
9928        Note that the pitches returned above are
9929        objects, not text:
9930
9931        >>> partOnePitches[0].octave
9932        4
9933
9934        Since all elements with a `.pitches` list are returned and streams themselves have
9935        `.pitches` properties (the docs you are reading now), pitches from embedded streams
9936        are also returned.  Flattening a stream is not necessary.  Whether this is a
9937        feature or a bug is in the eye of the beholder.
9938
9939        >>> len(a.pitches)
9940        104
9941
9942        Chords get their pitches found as well:
9943
9944        >>> c = chord.Chord(['C4', 'E4', 'G4'])
9945        >>> n = note.Note('F#4')
9946        >>> m = stream.Measure()
9947        >>> m.append(n)
9948        >>> m.append(c)
9949        >>> m.pitches
9950        [<music21.pitch.Pitch F#4>, <music21.pitch.Pitch C4>,
9951         <music21.pitch.Pitch E4>, <music21.pitch.Pitch G4>]
9952        '''
9953        post = []
9954        for e in self.elements:
9955            if isinstance(e, key.Key):
9956                continue  # has .pitches but should not be added
9957            if hasattr(e, 'pitch'):
9958                post.append(e.pitch)
9959            # both Chords and Stream have a pitches properties; this just
9960            # causes a recursive pitch gathering
9961            elif hasattr(e, 'pitches'):
9962                post.extend(list(e.pitches))
9963        return post
9964
9965    # --------------------------------------------------------------------------
9966    # interval routines
9967
9968    def findConsecutiveNotes(self,
9969                             skipRests=False,
9970                             skipChords=False,
9971                             skipUnisons=False,
9972                             skipOctaves=False,
9973                             skipGaps=False,
9974                             getOverlaps=False,
9975                             noNone=False, **keywords):
9976        r'''
9977        Returns a list of consecutive *pitched* Notes in a Stream.
9978
9979        A single "None" is placed in the list
9980        at any point there is a discontinuity (such as if there is a
9981        rest between two pitches), unless the `noNone` parameter is True.
9982
9983        How to determine consecutive pitches is a little tricky and there are many options:
9984
9985            The `skipUnisons` parameter uses the midi-note value (.ps) to determine unisons,
9986            so enharmonic transitions (F# -> Gb) are
9987            also skipped if `skipUnisons` is true.  We believe that this is the most common usage.
9988            However, because
9989            of this, you cannot completely be sure that the
9990            x.findConsecutiveNotes() - x.findConsecutiveNotes(skipUnisons=True)
9991            will give you the number of P1s in the piece, because there could be
9992            d2's in there as well.
9993
9994        See test.TestStream.testFindConsecutiveNotes() for usage details.
9995
9996        This example is adapted from the tutorials/Examples page.
9997
9998        >>> s = converter.parse("tinynotation: 4/4 f8 d'8~ d'8 d'8~ d'4 b'-8 a'-8 a-8")
9999        >>> m = s.measure(1)
10000        >>> m.findConsecutiveNotes(skipUnisons=True, skipOctaves=True,
10001        ...                        skipRests=True, noNone=True)
10002        [<music21.note.Note F>, <music21.note.Note D>,
10003         <music21.note.Note B->, <music21.note.Note A->]
10004
10005        >>> m.findConsecutiveNotes(skipUnisons=False,
10006        ...                        skipRests=True, noNone=True)
10007        [<music21.note.Note F>, <music21.note.Note D>,
10008         <music21.note.Note D>, <music21.note.Note D>, <music21.note.Note D>,
10009         <music21.note.Note B->, <music21.note.Note A->]
10010
10011        Changed in v7 -- now finds notes in Voices without requiring `getOverlaps=True`
10012        and iterates over Parts rather than flattening.
10013        If `noNone=False`, inserts `None` when backing up to scan a subsequent voice or part.
10014
10015        OMIT_FROM_DOCS
10016
10017        N.B. for chords, currently, only the first pitch is tested for unison.
10018        this is a bug TODO: FIX
10019
10020        (\*\*kwargs is there so that other methods that pass along dicts to
10021        findConsecutiveNotes don't have to remove
10022        their own args; this method is used in melodicIntervals.)
10023        '''
10024        if self.isSorted is False and self.autoSort:
10025            self.sort()
10026        returnList = []
10027        lastStart = 0.0
10028        lastEnd = 0.0
10029        lastContainerEnd = 0.0
10030        lastWasNone = False
10031        lastPitch = None
10032        if skipOctaves is True:
10033            skipUnisons = True  # implied
10034
10035        for container in self.recurse(streamsOnly=True, includeSelf=True):
10036            if (container.offset < lastContainerEnd
10037                    and container.getElementsByClass(note.GeneralNote)
10038                    and noNone is False):
10039                returnList.append(None)
10040                lastWasNone = True
10041                lastPitch = None
10042
10043            lastStart = 0.0
10044            lastEnd = 0.0
10045
10046            # NB: NOT a recursive search
10047            # do not want to capture Measure containing only Voices,
10048            # Part containing only Measures, etc.
10049            if container.getElementsByClass(note.GeneralNote):
10050                lastContainerEnd = container.highestTime
10051
10052            # Filter out all but notes and rests
10053            for e in container.getElementsByClass(note.GeneralNote):
10054                if (lastWasNone is False
10055                        and skipGaps is False
10056                        and e.offset > lastEnd):
10057                    if not noNone:
10058                        returnList.append(None)
10059                        lastWasNone = True
10060                if hasattr(e, 'pitch'):
10061                    # if (skipUnisons is False or isinstance(lastPitch, list)
10062                    if not(skipUnisons is False
10063                            or isinstance(lastPitch, tuple)
10064                            or lastPitch is None
10065                            or (hasattr(lastPitch, 'pitchClass')
10066                                and e.pitch.pitchClass != lastPitch.pitchClass)
10067                            or (skipOctaves is False
10068                                and hasattr(lastPitch, 'pitchClass')
10069                                and e.pitch.ps != lastPitch.ps)):
10070                        continue
10071                    if getOverlaps is False and e.offset < lastEnd:
10072                        continue
10073
10074                    returnList.append(e)
10075                    if e.offset < lastEnd:  # is an overlap...
10076                        continue
10077
10078                    lastStart = e.offset
10079                    if hasattr(e, 'duration'):
10080                        lastEnd = opFrac(lastStart + e.duration.quarterLength)
10081                    else:
10082                        lastEnd = lastStart
10083                    lastWasNone = False
10084                    lastPitch = e.pitch
10085
10086                # if we have a chord
10087                elif hasattr(e, 'pitches') and len(e.pitches) > 1:
10088                    if skipChords is True:
10089                        if lastWasNone is False and not noNone:
10090                            returnList.append(None)
10091                            lastWasNone = True
10092                            lastPitch = None
10093                    # if we have a chord
10094                    elif (not (skipUnisons is True
10095                                and isinstance(lastPitch, (list, tuple))
10096                                and e.pitches[0].ps == lastPitch[0].ps
10097                               ) and (getOverlaps is True or e.offset >= lastEnd)):
10098                        returnList.append(e)
10099                        if e.offset < lastEnd:  # is an overlap...
10100                            continue
10101
10102                        lastStart = e.offset
10103                        if hasattr(e, 'duration'):
10104                            lastEnd = opFrac(lastStart + e.duration.quarterLength)
10105                        else:
10106                            lastEnd = lastStart
10107                        # this is the case where the last pitch is a
10108                        # a list
10109                        lastPitch = e.pitches
10110                        lastWasNone = False
10111
10112                elif (skipRests is False
10113                      and isinstance(e, note.Rest)
10114                      and lastWasNone is False):
10115                    if noNone is False:
10116                        returnList.append(None)
10117                        lastWasNone = True
10118                        lastPitch = None
10119                elif skipRests is True and isinstance(e, note.Rest):
10120                    lastEnd = opFrac(e.offset + e.duration.quarterLength)
10121
10122        if lastWasNone is True:
10123            returnList.pop()  # removes the last-added element
10124        return returnList
10125
10126    def melodicIntervals(self, *skipArgs, **skipKeywords):
10127        '''
10128        Returns a Stream of :class:`~music21.interval.Interval` objects
10129        between Notes (and by default, Chords) that follow each other in a stream.
10130        the offset of the Interval is the offset of the beginning of the interval
10131        (if two notes are adjacent, then this offset is equal to the offset of
10132        the second note, but if skipRests is set to True or there is a gap
10133        in the Stream, then these two numbers
10134        will be different).
10135
10136        See :meth:`~music21.stream.Stream.findConsecutiveNotes` in this class for
10137        a discussion of what is meant by default for "consecutive notes", and
10138        which keywords such as skipChords, skipRests, skipUnisons, etc. can be
10139        used to change that behavior.
10140
10141        The interval between a Note and a Chord (or between two chords) is the
10142        interval to the first pitch of the Chord (pitches[0]) which is usually the lowest.
10143        For more complex interval calculations,
10144        run :meth:`~music21.stream.Stream.findConsecutiveNotes` and then calculate
10145        your own intervals directly.
10146
10147        Returns an empty Stream if there are not at least two elements found by
10148        findConsecutiveNotes.
10149
10150
10151        >>> s1 = converter.parse("tinynotation: 3/4 c4 d' r b b'", makeNotation=False)
10152        >>> #_DOCS_SHOW s1.show()
10153
10154        .. image:: images/streamMelodicIntervals1.*
10155            :width: 246
10156
10157        >>> intervalStream1 = s1.melodicIntervals()
10158        >>> intervalStream1.show('text')
10159        {1.0} <music21.interval.Interval M9>
10160        {4.0} <music21.interval.Interval P8>
10161
10162        >>> M9 = intervalStream1[0]
10163        >>> M9.noteStart.nameWithOctave, M9.noteEnd.nameWithOctave
10164        ('C4', 'D5')
10165
10166        Using the skip attributes from :meth:`~music21.stream.Stream.findConsecutiveNotes`,
10167        we can alter which intervals are reported:
10168
10169        >>> intervalStream2 = s1.melodicIntervals(skipRests=True, skipOctaves=True)
10170        >>> intervalStream2.show('text')
10171        {1.0} <music21.interval.Interval M9>
10172        {2.0} <music21.interval.Interval m-3>
10173
10174        >>> m3 = intervalStream2[1]
10175        >>> m3.directedNiceName
10176        'Descending Minor Third'
10177        '''
10178        returnList = self.findConsecutiveNotes(**skipKeywords)
10179        if len(returnList) < 2:
10180            return self.cloneEmpty(derivationMethod='melodicIntervals')
10181
10182        returnStream = self.cloneEmpty(derivationMethod='melodicIntervals')
10183        for i in range(len(returnList) - 1):
10184            firstNote = returnList[i]
10185            secondNote = returnList[i + 1]
10186            # returnList could contain None to represent a rest
10187            if firstNote is None or secondNote is None:
10188                continue
10189            # Protect against empty chords
10190            if not (firstNote.pitches and secondNote.pitches):
10191                continue
10192            if chord.Chord in firstNote.classSet:
10193                noteStart = firstNote.notes[0]
10194            else:
10195                noteStart = firstNote
10196            if chord.Chord in secondNote.classSet:
10197                noteEnd = secondNote.notes[0]
10198            else:
10199                noteEnd = secondNote
10200            # Prefer Note objects over Pitch objects so that noteStart is set correctly
10201            returnInterval = interval.Interval(noteStart, noteEnd)
10202            returnInterval.offset = opFrac(firstNote.offset + firstNote.quarterLength)
10203            returnInterval.duration = duration.Duration(opFrac(
10204                secondNote.offset - returnInterval.offset))
10205            returnStream.insert(returnInterval)
10206
10207        return returnStream
10208
10209    # --------------------------------------------------------------------------
10210    def _getDurSpan(self, flatStream):
10211        '''
10212        Given a flat stream, create a list of the start and end
10213        times (as a tuple pair) of all elements in the Stream.
10214
10215
10216        >>> a = stream.Stream()
10217        >>> a.repeatInsert(note.Note(type='half'), [0, 1, 2, 3, 4])
10218        >>> a._getDurSpan(a.flatten())
10219        [(0.0, 2.0), (1.0, 3.0), (2.0, 4.0), (3.0, 5.0), (4.0, 6.0)]
10220        '''
10221        post = []
10222        for e in flatStream:
10223            dur = e.duration.quarterLength
10224            durSpan = (e.offset, opFrac(e.offset + dur))
10225            post.append(durSpan)
10226        # assume this is already sorted
10227        # index found here will be the same as elementsSorted
10228        return post
10229
10230    def _durSpanOverlap(self, a, b, includeEndBoundary=False):
10231        '''
10232        Compare two durSpans and find overlaps; optionally,
10233        include coincident boundaries. a and b are sorted to permit any ordering.
10234
10235        If an element ends at 3.0 and another starts at 3.0, this may or may not
10236        be considered an overlap. The includeCoincidentEnds parameter determines
10237        this behaviour, where ending and starting 3.0 being a type of overlap
10238        is set by the includeEndBoundary being True.
10239
10240
10241        >>> sc = stream.Stream()
10242        >>> sc._durSpanOverlap((0, 5), (4, 12), False)
10243        True
10244        >>> sc._durSpanOverlap((0, 10), (11, 12), False)
10245        False
10246        >>> sc._durSpanOverlap((11, 12), (0, 10), False)
10247        False
10248        >>> sc._durSpanOverlap((0, 3), (3, 6), False)
10249        False
10250        >>> sc._durSpanOverlap((0, 3), (3, 6), True)
10251        True
10252        '''
10253        durSpans = [a, b]
10254        # sorting will ensure that leading numbers are ordered from low to high
10255        durSpans.sort()
10256        found = False
10257
10258        if includeEndBoundary:
10259            # if the start of b is before the end of a
10260            # if durSpans[1][0] <= durSpans[0][1]:
10261            if durSpans[1][0] <= durSpans[0][1]:
10262                found = True
10263        else:  # do not include coincident boundaries
10264            # if durSpans[1][0] < durSpans[0][1]:
10265            if durSpans[1][0] < durSpans[0][1]:
10266                found = True
10267        return found
10268
10269    def _findLayering(self):
10270        '''
10271        Find any elements in an elementsSorted list that have
10272        durations that cause overlaps.
10273
10274        Returns a lists that has elements with overlaps,
10275        all index values that match are included in that list.
10276
10277        See testOverlaps, in unit tests, for examples.
10278
10279        Used in getOverlaps inside makeVoices.
10280        '''
10281        flatStream = self.flatten()
10282        if flatStream.isSorted is False:
10283            flatStream = flatStream.sorted()
10284        # these may not be sorted
10285        durSpanSorted = self._getDurSpan(flatStream)
10286        # According to the above comment, the spans may not be sorted
10287        # so we sort them to be sure, but keep track of their original indices
10288        durSpanSortedIndex = list(enumerate(durSpanSorted))
10289        durSpanSortedIndex.sort()
10290
10291        # create a list with an entry for each element
10292        # in each entry, provide indices of all other elements that overlap
10293        overlapMap = [[] for dummy in range(len(durSpanSorted))]
10294
10295        for i in range(len(durSpanSortedIndex)):
10296            src = durSpanSortedIndex[i]
10297            for j in range(i + 1, len(durSpanSortedIndex)):
10298                dst = durSpanSortedIndex[j]
10299                if self._durSpanOverlap(src[1], dst[1]):
10300                    overlapMap[src[0]].append(dst[0])
10301                    overlapMap[dst[0]].append(src[0])
10302                else:
10303                    break
10304
10305        # Preserve exact same behaviour as earlier code.
10306        # It is unclear if anything depends on the individual lists being sorted.
10307        for ls in overlapMap:
10308            ls.sort()
10309        return overlapMap
10310
10311    def _consolidateLayering(self, layeringMap):
10312        '''
10313        Given a stream of flat elements and a map of equal length with lists of
10314        index values that meet a given condition (overlap or simultaneities),
10315        organize into a dictionary by the relevant or first offset
10316        '''
10317        flatStream = self.flatten()
10318        if flatStream.isSorted is False:
10319            flatStream = flatStream.sorted()
10320
10321        if len(layeringMap) != len(flatStream):
10322            raise StreamException('layeringMap must be the same length as flatStream')
10323
10324        post = {}
10325        for i in range(len(layeringMap)):
10326            # print('examining i:', i)
10327            indices = layeringMap[i]
10328            if not indices:
10329                continue
10330
10331            srcElementObj = flatStream[i]
10332            srcOffset = srcElementObj.offset
10333            dstOffset = None
10334            # print('found indices', indices)
10335            # check indices
10336            for j in indices:  # indices of other elements that overlap
10337                elementObj = flatStream[j]
10338                # check if this object has been stored anywhere yet
10339                # if so, use the offset of where it was stored to
10340                # to store the src element below
10341                store = True
10342                for k in post:
10343                    # this comparison needs to be based on object id, not
10344                    # matching equality
10345                    if id(elementObj) in [id(e) for e in post[k]]:
10346                        # if elementObj in post[key]:
10347                        store = False
10348                        dstOffset = k
10349                        break
10350                if dstOffset is None:
10351                    dstOffset = srcOffset
10352                if store:
10353                    # print('storing offset', dstOffset)
10354                    if dstOffset not in post:
10355                        post[dstOffset] = []  # create dictionary entry
10356                    post[dstOffset].append(elementObj)
10357
10358            # check if this object has been stored anywhere yet
10359            store = True
10360            for k in post:
10361                if id(srcElementObj) in [id(e) for e in post[k]]:
10362                    # if srcElementObj in post[key]:
10363                    store = False
10364                    break
10365            # dst offset may have been set when looking at indices
10366            if store:
10367                if dstOffset is None:
10368                    dstOffset = srcOffset
10369                if dstOffset not in post:
10370                    post[dstOffset] = []  # create dictionary entry
10371                # print('storing offset', dstOffset)
10372                post[dstOffset].append(srcElementObj)
10373        # print(post)
10374        return post
10375
10376    def findGaps(self):
10377        # noinspection PyShadowingNames
10378        '''
10379        Returns either (1) a Stream containing empty Music21Objects
10380        whose offsets and durations
10381        are the length of gaps in the Stream
10382        or (2) None if there are no gaps.
10383
10384        N.B. there may be gaps in the flattened representation of the stream
10385        but not in the unflattened.  Hence why "isSequence" calls self.flatten().isGapless
10386
10387        >>> s = stream.Stream()
10388        >>> s.insert(1.0, note.Note('E', type='half'))
10389        >>> s.insert(5.0, note.Note('F', type='whole'))
10390        >>> s.storeAtEnd(bar.Barline('final'))
10391        >>> gapStream = s.findGaps()
10392        >>> gapStream.show('text', addEndTimes=True)
10393        {0.0 - 1.0} <music21.note.Rest quarter>
10394        {3.0 - 5.0} <music21.note.Rest half>
10395
10396        Returns None if not gaps:
10397
10398        >>> s2 = stream.Stream()
10399        >>> s2.append(note.Note('G'))
10400        >>> s2.findGaps() is None
10401        True
10402
10403        Changed in v7. -- gapStream is filled with rests instead of Music21Objects
10404        '''
10405        if 'findGaps' in self._cache and self._cache['findGaps'] is not None:
10406            return self._cache['findGaps']
10407
10408        gapStream = self.cloneEmpty(derivationMethod='findGaps')
10409
10410        highestCurrentEndTime = 0.0
10411        for e in self:
10412            eOffset = self.elementOffset(e, returnSpecial=True)
10413            if eOffset == OffsetSpecial.AT_END:
10414                break
10415            if eOffset > highestCurrentEndTime:
10416                gapElement = note.Rest()
10417                gapQuarterLength = opFrac(eOffset - highestCurrentEndTime)
10418                gapElement.duration.quarterLength = gapQuarterLength
10419                gapStream.insert(highestCurrentEndTime, gapElement, ignoreSort=True)
10420            eDur = e.duration.quarterLength
10421            highestCurrentEndTime = opFrac(max(highestCurrentEndTime, eOffset + eDur))
10422
10423        # TODO: Is this even necessary, we do insert the elements in sorted order
10424        #     and the stream is empty at the start
10425        gapStream.sort()
10426
10427        if not gapStream:
10428            return None
10429        else:
10430            self._cache['findGaps'] = gapStream
10431            return gapStream
10432
10433    @property
10434    def isGapless(self):
10435        '''
10436        Returns True if there are no gaps between the lowest offset and the highest time.
10437        Otherwise returns False
10438
10439
10440        >>> s = stream.Stream()
10441        >>> s.append(note.Note('C'))
10442        >>> s.append(note.Note('D'))
10443        >>> s.isGapless
10444        True
10445        >>> s.insert(10.0, note.Note('E'))
10446        >>> s.isGapless
10447        False
10448
10449        OMIT_FROM_DOCS
10450
10451        Test cache:
10452
10453        >>> s.isGapless
10454        False
10455        '''
10456        if 'isGapless' in self._cache and self._cache['isGapless'] is not None:
10457            return self._cache['isGapless']
10458        else:
10459            if self.findGaps() is None:
10460                self._cache['isGapless'] = True
10461                return True
10462            else:
10463                self._cache['isGapless'] = False
10464                return False
10465
10466    def getOverlaps(self):
10467        '''
10468        Find any elements that overlap. Overlapping might include elements
10469        that have zero-length duration simultaneous.
10470
10471        This method returns a dictionary, where keys
10472        are the start time of the first overlap and
10473        value are a list of all objects included in
10474        that overlap group.
10475
10476        This example demonstrates that end-joining overlaps do not count.
10477
10478        >>> a = stream.Stream()
10479        >>> for x in range(4):
10480        ...     n = note.Note('G#')
10481        ...     n.duration = duration.Duration('quarter')
10482        ...     n.offset = x * 1
10483        ...     a.insert(n)
10484        ...
10485        >>> d = a.getOverlaps()
10486        >>> len(d)
10487        0
10488
10489        Notes starting at the same time overlap:
10490
10491        >>> a = stream.Stream()
10492        >>> for x in [0, 0, 0, 0, 13, 13, 13]:
10493        ...     n = note.Note('G#')
10494        ...     n.duration = duration.Duration('half')
10495        ...     n.offset = x
10496        ...     a.insert(n)
10497        ...
10498        >>> d = a.getOverlaps()
10499        >>> len(d[0])
10500        4
10501        >>> len(d[13])
10502        3
10503        >>> a = stream.Stream()
10504        >>> for x in [0, 0, 0, 0, 3, 3, 3]:
10505        ...     n = note.Note('G#')
10506        ...     n.duration = duration.Duration('whole')
10507        ...     n.offset = x
10508        ...     a.insert(n)
10509        ...
10510
10511        Default is to not include coincident boundaries
10512
10513        >>> d = a.getOverlaps()
10514        >>> len(d[0])
10515        7
10516
10517        '''
10518        overlapMap = self._findLayering()
10519        # environLocal.printDebug(['overlapMap', overlapMap])
10520
10521        return self._consolidateLayering(overlapMap)
10522
10523    def isSequence(self):
10524        '''A stream is a sequence if it has no overlaps.
10525
10526
10527        >>> a = stream.Stream()
10528        >>> for x in [0, 0, 0, 0, 3, 3, 3]:
10529        ...     n = note.Note('G#')
10530        ...     n.duration = duration.Duration('whole')
10531        ...     n.offset = x * 1
10532        ...     a.insert(n)
10533        ...
10534        >>> a.isSequence()
10535        False
10536
10537        OMIT_FROM_DOCS
10538        TODO: check that co-incident boundaries are properly handled
10539
10540        >>> a = stream.Stream()
10541        >>> for x in [0, 4, 8.0]:
10542        ...     n = note.Note('G#')
10543        ...     n.duration = duration.Duration('whole')
10544        ...     a.append(n)
10545        ...
10546        >>> a.isSequence()
10547        True
10548        '''
10549        overlapMap = self._findLayering()
10550        post = True
10551        for indexList in overlapMap:
10552            if indexList:
10553                post = False
10554                break
10555        return post
10556
10557    # --------------------------------------------------------------------------
10558    # routines for dealing with relationships to other streams.
10559    # Formerly in twoStreams.py
10560
10561    def simultaneousAttacks(self, stream2):
10562        '''
10563        returns an ordered list of offsets where elements are started (attacked)
10564        at the same time in both self and stream2.
10565
10566        In this example, we create one stream of Qtr, Half, Qtr, and one of Half, Qtr, Qtr.
10567        There are simultaneous attacks at offset 0.0 (the beginning) and at offset 3.0,
10568        but not at 1.0 or 2.0:
10569
10570
10571        >>> st1 = stream.Stream()
10572        >>> st2 = stream.Stream()
10573        >>> st1.append([note.Note(type='quarter'),
10574        ...             note.Note(type='half'),
10575        ...             note.Note(type='quarter')])
10576        >>> st2.append([note.Note(type='half'),
10577        ...             note.Note(type='quarter'),
10578        ...             note.Note(type='quarter')])
10579        >>> print(st1.simultaneousAttacks(st2))
10580        [0.0, 3.0]
10581        '''
10582        stream1Offsets = iterator.OffsetIterator(self)
10583        stream2Offsets = iterator.OffsetIterator(stream2)
10584
10585        returnKey = {}
10586
10587        for thisList in stream1Offsets:
10588            thisOffset = self.elementOffset(thisList[0])
10589            returnKey[thisOffset] = 1
10590
10591        for thatList in stream2Offsets:
10592            thatOffset = thatList[0].getOffsetBySite(stream2)
10593            if thatOffset in returnKey:
10594                returnKey[thatOffset] += 1
10595
10596        returnList = []
10597        for foundOffset in sorted(returnKey):
10598            if returnKey[foundOffset] >= 2:
10599                returnList.append(foundOffset)
10600        return returnList
10601
10602        # this method was supposed to be faster, but actually 2000 times slower on op133
10603
10604        # sOuter = Stream()
10605        # for e in self:
10606        #     sOuter.coreInsert(e.offset, e)
10607        # for e in stream2:
10608        #     sOuter.coreInsert(e.offset, e)
10609        # sOuter.coreElementsChanged(updateIsFlat=False)
10610        # sOuterTree = sOuter.asTree(flatten=False, groupOffsets=True)
10611        # return sorted(sOuterTree.simultaneityDict().keys())
10612
10613    def attachIntervalsBetweenStreams(self, cmpStream):
10614        # noinspection PyShadowingNames
10615        '''
10616        For each element in self, creates an interval.Interval object in the element's
10617        editorial that is the interval between it and the element in cmpStream that
10618        is sounding at the moment the element in srcStream is attacked.
10619
10620        Remember that if you are comparing two streams with measures, etc.,
10621        you'll need to flatten each stream as follows:
10622
10623        >>> #_DOCS_SHOW stream1.flatten().attachIntervalsBetweenStreams(stream2.flatten())
10624
10625        Example usage:
10626
10627
10628        >>> s1 = converter.parse('tinynotation: 7/4 C4 d8 e f# g A2 d2', makeNotation=False)
10629        >>> s2 = converter.parse('tinynotation: 7/4 g4 e8 d c4   a2 r2', makeNotation=False)
10630        >>> s1.attachIntervalsBetweenStreams(s2)
10631        >>> for n in s1.notes:
10632        ...     if n.editorial.harmonicInterval is None:
10633        ...         print('None')  # if other voice had a rest...
10634        ...     else:
10635        ...         print(n.editorial.harmonicInterval.directedName)
10636        P12
10637        M2
10638        M-2
10639        A-4
10640        P-5
10641        P8
10642        None
10643        '''
10644        for n in self.notes:
10645            # get simultaneous elements form other stream
10646            simultEls = cmpStream.getElementsByOffset(self.elementOffset(n),
10647                                                      mustBeginInSpan=False,
10648                                                      mustFinishInSpan=False)
10649            if simultEls:
10650                for simultNote in simultEls.notes:
10651                    interval1 = None
10652                    try:
10653                        interval1 = interval.notesToInterval(n, simultNote)
10654                        n.editorial.harmonicInterval = interval1
10655                    except exceptions21.Music21Exception:
10656                        pass
10657                    if interval1 is not None:
10658                        break  # inner loop
10659
10660    def attachMelodicIntervals(self):
10661        '''
10662        For each element in self, creates an interval.Interval object in the element's
10663        editorial that is the interval between it and the previous element in the stream. Thus,
10664        the first element will have a value of None.
10665
10666        DEPRECATED sometime soon.  A replacement to come presently.
10667
10668        >>> s1 = converter.parse('tinyNotation: 7/4 C4 d8 e f# g A2 d2', makeNotation=False)
10669        >>> s1.attachMelodicIntervals()
10670        >>> for n in s1.notes:
10671        ...     if n.editorial.melodicInterval is None:
10672        ...         print('None')
10673        ...     else:
10674        ...         print(n.editorial.melodicInterval.directedName)
10675        None
10676        M9
10677        M2
10678        M2
10679        m2
10680        m-7
10681        P4
10682
10683        >>> s = stream.Stream()
10684        >>> s.append(note.Note('C'))
10685        >>> s.append(note.Note('D'))
10686        >>> s.append(note.Rest(quarterLength=4.0))
10687        >>> s.append(note.Note('D'))
10688        >>> s.attachMelodicIntervals()
10689        >>> for n in s.notes:
10690        ...     if n.editorial.melodicInterval is None:
10691        ...         print('None')  # if other voice had a rest...
10692        ...     else:
10693        ...         print(n.editorial.melodicInterval.directedName)
10694        None
10695        M2
10696        P1
10697        '''
10698
10699        notes = self.notes.stream()
10700        currentObject = notes[0]
10701        previousObject = None
10702        while currentObject is not None:
10703            if (previousObject is not None
10704                    and isinstance(currentObject, note.Note)
10705                    and isinstance(previousObject, note.Note)):
10706                currentObject.editorial.melodicInterval = interval.notesToInterval(
10707                    previousObject, currentObject)
10708            previousObject = currentObject
10709            currentObject = currentObject.next()
10710
10711    def playingWhenAttacked(self, el, elStream=None):
10712        '''
10713        Given an element (from another Stream) returns the single element
10714        in this Stream that is sounding while the given element starts.
10715
10716        If there are multiple elements sounding at the moment it is
10717        attacked, the method returns the first element of the same class
10718        as this element, if any. If no element
10719        is of the same class, then the first element encountered is
10720        returned. For more complex usages, use allPlayingWhileSounding.
10721
10722        Returns None if no elements fit the bill.
10723
10724        The optional elStream is the stream in which el is found.
10725        If provided, el's offset
10726        in that Stream is used.  Otherwise, the current offset in
10727        el is used.  It is just
10728        in case you are paranoid that el.offset might not be what
10729        you want, because of some fancy manipulation of
10730        el.activeSite
10731
10732        >>> n1 = note.Note('G#')
10733        >>> n2 = note.Note('D#')
10734        >>> s1 = stream.Stream()
10735        >>> s1.insert(20.0, n1)
10736        >>> s1.insert(21.0, n2)
10737
10738        >>> n3 = note.Note('C#')
10739        >>> s2 = stream.Stream()
10740        >>> s2.insert(20.0, n3)
10741        >>> s1.playingWhenAttacked(n3)
10742        <music21.note.Note G#>
10743
10744        >>> n3.setOffsetBySite(s2, 20.5)
10745        >>> s1.playingWhenAttacked(n3)
10746        <music21.note.Note G#>
10747
10748        >>> n3.setOffsetBySite(s2, 21.0)
10749        >>> n3.offset
10750        21.0
10751        >>> s1.playingWhenAttacked(n3)
10752        <music21.note.Note D#>
10753
10754        If there is more than one item at the same time in the other stream
10755        then the first item matching the same class is returned, even if
10756        another element has a closer offset.
10757
10758        >>> n3.setOffsetBySite(s2, 20.5)
10759        >>> s1.insert(20.5, clef.BassClef())
10760        >>> s1.playingWhenAttacked(n3)
10761        <music21.note.Note G#>
10762        >>> fc = clef.FClef()  # superclass of BassClef
10763        >>> s2.insert(20.5, fc)
10764        >>> s1.playingWhenAttacked(fc)
10765        <music21.clef.BassClef>
10766
10767        But since clefs have zero duration, moving the FClef ever so slightly
10768        will find the note instead
10769
10770        >>> fc.setOffsetBySite(s2, 20.6)
10771        >>> s1.playingWhenAttacked(fc)
10772        <music21.note.Note G#>
10773
10774
10775        Optionally, specify the site to get the offset from:
10776
10777        >>> n3.setOffsetBySite(s2, 21.0)
10778        >>> n3.setOffsetBySite(None, 100)
10779        >>> n3.activeSite = None
10780        >>> s1.playingWhenAttacked(n3) is None
10781        True
10782        >>> s1.playingWhenAttacked(n3, s2).name
10783        'D#'
10784        '''
10785        if elStream is not None:  # bit of safety
10786            elOffset = el.getOffsetBySite(elStream)
10787        else:
10788            elOffset = el.offset
10789
10790        otherElements = self.getElementsByOffset(elOffset, mustBeginInSpan=False)
10791        if not otherElements:
10792            return None
10793        elif len(otherElements) == 1:
10794            return otherElements[0]
10795        else:
10796            for thisEl in otherElements:
10797                if isinstance(thisEl, el.__class__):
10798                    return thisEl
10799            return otherElements[0]
10800
10801    def allPlayingWhileSounding(self, el, elStream=None):
10802        '''
10803        Returns a new Stream of elements in this stream that sound
10804        at the same time as `el`, an element presumably in another Stream.
10805
10806        The offset of this new Stream is set to el's offset, while the
10807        offset of elements within the Stream are adjusted relative to
10808        their position with respect to the start of el.  Thus, a note
10809        that is sounding already when el begins would have a negative
10810        offset.  The duration of otherStream is forced
10811        to be the length of el -- thus a note sustained after el ends
10812        may have a release time beyond that of the duration of the Stream.
10813
10814        As above, elStream is an optional Stream to look up el's offset in.  Use
10815        this to work on an element in another part.
10816
10817        The method always returns a Stream, but it might be an empty Stream.
10818
10819        OMIT_FROM_DOCS
10820        TODO: write: requireClass:
10821        Takes as an optional parameter "requireClass".  If this parameter
10822        is boolean True then only elements
10823        of the same class as el are added to the new Stream.  If requireClass
10824        is list, it is used like
10825        classList in elsewhere in stream to provide a list of classes that the
10826        el must be a part of.
10827
10828        '''
10829        if elStream is not None:  # bit of safety
10830            elOffset = el.getOffsetBySite(elStream)
10831        else:
10832            elOffset = el.offset
10833        elEnd = elOffset + el.quarterLength
10834
10835        if elEnd != elOffset:  # i.e. not zero length
10836            otherElements = self.getElementsByOffset(
10837                elOffset,
10838                elEnd,
10839                mustBeginInSpan=False,
10840                includeEndBoundary=False,
10841                includeElementsThatEndAtStart=False).stream()
10842        else:
10843            otherElements = self.getElementsByOffset(elOffset,
10844                                                     mustBeginInSpan=False).stream()
10845
10846        otherElements.offset = elOffset
10847        otherElements.quarterLength = el.quarterLength
10848        for thisEl in otherElements:
10849            thisEl.offset = thisEl.offset - elOffset
10850
10851        return otherElements
10852
10853    # --------------------------------------------------------------------------
10854    # voice processing routines
10855
10856    def makeVoices(self, *, inPlace=False, fillGaps=True):
10857        '''
10858        If this Stream has overlapping Notes or Chords, this method will isolate
10859        all overlaps in unique Voices, and place those Voices in the Stream.
10860
10861        >>> s = stream.Stream()
10862        >>> s.insert(0, note.Note('C4', quarterLength=4))
10863        >>> s.repeatInsert(note.Note('b-4', quarterLength=0.5), [x * 0.5 for x in list(range(8))])
10864        >>> s.makeVoices(inPlace=True)
10865        >>> len(s.voices)
10866        2
10867        >>> [n.pitch for n in s.voices[0].notes]
10868        [<music21.pitch.Pitch C4>]
10869        >>> [str(n.pitch) for n in s.voices[1].notes]
10870        ['B-4', 'B-4', 'B-4', 'B-4', 'B-4', 'B-4', 'B-4', 'B-4']
10871
10872        Changed in v7 -- if `fillGaps=True` and called on an incomplete measure,
10873        makes trailing rests in voices. This scenario occurs when parsing MIDI.
10874        '''
10875        # this method may not always
10876        # produce the optimal voice assignment based on context (register
10877        # chord formation, etc
10878        if not inPlace:  # make a copy
10879            returnObj = self.coreCopyAsDerivation('makeVoices')
10880        else:
10881            returnObj = self
10882        # must be sorted
10883        if not returnObj.isSorted:
10884            returnObj.sort()
10885        olDict = returnObj.notes.stream().getOverlaps()
10886        # environLocal.printDebug(['makeVoices(): olDict', olDict])
10887        # find the max necessary voices by finding the max number
10888        # of elements in each group; these may not all be necessary
10889        maxVoiceCount = 1
10890        for group in olDict.values():
10891            if len(group) > maxVoiceCount:
10892                maxVoiceCount = len(group)
10893        if maxVoiceCount == 1:  # nothing to do here
10894            if not inPlace:
10895                return returnObj
10896            return None
10897
10898        # store all voices in a list
10899        voices = []
10900        for dummy in range(maxVoiceCount):
10901            voices.append(Voice())  # add voice classes
10902
10903        # iterate through all elements; if not in an overlap, place in
10904        # voice 1, otherwise, distribute
10905        for e in returnObj.notes:
10906            o = e.getOffsetBySite(returnObj)
10907            # cannot match here by offset, as olDict keys are representative
10908            # of the first overlapped offset, not all contained offsets
10909            # if o not in olDict:  # place in a first voices
10910            #    voices[0].insert(o, e)
10911            # find a voice to place in
10912            # as elements are sorted, can use the highest time
10913            # else:
10914            for v in voices:
10915                if v.highestTime <= o:
10916                    v.insert(o, e)
10917                    break
10918            # remove from source
10919            returnObj.remove(e)
10920        # remove any unused voices (possible if overlap group has sus)
10921        for v in voices:
10922            if v:  # skip empty voices
10923                returnObj.insert(0, v)
10924        if fillGaps:
10925            returnObj.makeRests(fillGaps=True,
10926                                inPlace=True,
10927                                timeRangeFromBarDuration=True,
10928                                )
10929        # remove rests in returnObj
10930        returnObj.removeByClass('Rest')
10931        # elements changed will already have been called
10932        if not inPlace:
10933            return returnObj
10934
10935    def _maxVoiceCount(self, *, countById=False):
10936        '''
10937        Returns the maximum number of voices in a part.  Used by voicesToParts.
10938        Minimum returned is 1.  If `countById` is True, returns a tuple of
10939        (maxVoiceCount, voiceIdList)
10940
10941        >>> import copy
10942
10943        >>> p0 = stream.Part()
10944        >>> p0._maxVoiceCount()
10945        1
10946        >>> v0 = stream.Voice(id='v0')
10947        >>> p0.insert(0, v0)
10948        >>> p0._maxVoiceCount()
10949        1
10950        >>> v1 = stream.Voice(id='v1')
10951        >>> p0.insert(0, v1)
10952        >>> p0._maxVoiceCount()
10953        2
10954
10955        >>> p1 = stream.Part()
10956        >>> m1 = stream.Measure(number=1)
10957        >>> p1.insert(0, m1)
10958        >>> p1._maxVoiceCount()
10959        1
10960        >>> m1.insert(0, v0)
10961        >>> p1._maxVoiceCount()
10962        1
10963        >>> m1.insert(0, v1)
10964        >>> p1._maxVoiceCount()
10965        2
10966        >>> m2 = stream.Measure(number=2)
10967        >>> p1.append(m2)
10968        >>> p1._maxVoiceCount()
10969        2
10970        >>> v01 = copy.copy(v0)
10971        >>> v11 = stream.Voice(id='v11')
10972        >>> m2.insert(0, v01)
10973        >>> m2.insert(0, v11)
10974        >>> p1._maxVoiceCount()
10975        2
10976        >>> v2 = stream.Voice(id='v2')
10977        >>> m2.insert(0, v2)
10978        >>> p1._maxVoiceCount()
10979        3
10980
10981        If `countById` is True then different voice ids create different parts.
10982
10983        >>> mvc, vIds = p1._maxVoiceCount(countById=True)
10984        >>> mvc
10985        4
10986        >>> vIds
10987        ['v0', 'v1', 'v11', 'v2']
10988
10989        >>> v01.id = 'v01'
10990        >>> mvc, vIds = p1._maxVoiceCount(countById=True)
10991        >>> mvc
10992        5
10993        >>> vIds
10994        ['v0', 'v1', 'v01', 'v11', 'v2']
10995        '''
10996        voiceCount = 1
10997        voiceIds = []
10998
10999        if self.hasMeasures():
11000            for m in self.getElementsByClass('Measure'):
11001                mVoices = m.voices
11002                mVCount = len(mVoices)
11003                if not countById:
11004                    voiceCount = max(mVCount, voiceCount)
11005                else:
11006                    for v in mVoices:
11007                        if v.id not in voiceIds:
11008                            voiceIds.append(v.id)
11009
11010        elif self.hasVoices():
11011            voices = self.voices
11012            if not countById:
11013                voiceCount = len(voices)
11014            else:
11015                voiceIds = [v.id for v in voices]
11016        else:  # if no measure or voices, get one part
11017            voiceCount = 1
11018
11019        voiceCount = max(voiceCount, len(voiceIds))
11020
11021        if not countById:
11022            return voiceCount
11023        else:
11024            return voiceCount, voiceIds
11025
11026    def voicesToParts(self, *, separateById=False):
11027        # noinspection PyShadowingNames
11028        '''
11029        If this Stream defines one or more voices,
11030        extract each into a Part, returning a Score.
11031
11032        If this Stream has no voices, return the Stream as a Part within a Score.
11033
11034        >>> c = corpus.parse('demos/two-voices')
11035        >>> c.show('t')
11036        {0.0} <music21.text.TextBox 'Music21 Fr...'>
11037        {0.0} <music21.text.TextBox 'Music21'>
11038        {0.0} <music21.metadata.Metadata object at 0x109ce1630>
11039        {0.0} <music21.stream.Part Piano>
11040            {0.0} <music21.instrument.Instrument 'P1: Piano: '>
11041            {0.0} <music21.stream.Measure 1 offset=0.0>
11042                {0.0} <music21.layout.PageLayout>
11043                {0.0} <music21.layout.SystemLayout>
11044                ...
11045                {0.0} <music21.clef.BassClef>
11046                {0.0} <music21.key.Key of D major>
11047                {0.0} <music21.meter.TimeSignature 4/4>
11048                {0.0} <music21.stream.Voice 3>
11049                    {0.0} <music21.note.Note E>
11050                    ...
11051                    {3.0} <music21.note.Rest quarter>
11052                {0.0} <music21.stream.Voice 4>
11053                    {0.0} <music21.note.Note F#>
11054                    ...
11055                    {3.5} <music21.note.Note B>
11056            {4.0} <music21.stream.Measure 2 offset=4.0>
11057                {0.0} <music21.stream.Voice 3>
11058                    {0.0} <music21.note.Note E>
11059                    ...
11060                    {3.0} <music21.note.Rest quarter>
11061                {0.0} <music21.stream.Voice 4>
11062                    {0.0} <music21.note.Note E>
11063                    ...
11064                    {3.5} <music21.note.Note A>
11065            {8.0} <music21.stream.Measure 3 offset=8.0>
11066                {0.0} <music21.note.Rest whole>
11067                {4.0} <music21.bar.Barline type=final>
11068        {0.0} <music21.layout.ScoreLayout>
11069
11070        >>> ce = c.voicesToParts()
11071        >>> ce.show('t')
11072        {0.0} <music21.stream.Part Piano-v0>
11073            {0.0} <music21.instrument.Instrument 'P1: Piano: '>
11074            {0.0} <music21.stream.Measure 1 offset=0.0>
11075                {0.0} <music21.clef.TrebleClef>
11076                {0.0} <music21.key.Key of D major>
11077                {0.0} <music21.meter.TimeSignature 4/4>
11078                {0.0} <music21.note.Note E>
11079                ...
11080                {3.0} <music21.note.Rest quarter>
11081            {4.0} <music21.stream.Measure 2 offset=4.0>
11082                {0.0} <music21.note.Note E>
11083                ...
11084                {3.0} <music21.note.Rest quarter>
11085            {8.0} <music21.stream.Measure 3 offset=8.0>
11086                {0.0} <music21.note.Rest whole>
11087                {4.0} <music21.bar.Barline type=final>
11088        {0.0} <music21.stream.Part Piano-v1>
11089            {0.0} <music21.instrument.Instrument 'P1: Piano: '>
11090            {0.0} <music21.stream.Measure 1 offset=0.0>
11091                {0.0} <music21.clef.BassClef>
11092                {0.0} <music21.key.Key of D major>
11093                ...
11094                {3.5} <music21.note.Note B>
11095            {4.0} <music21.stream.Measure 2 offset=4.0>
11096                {0.0} <music21.note.Note E>
11097                ...
11098                {3.5} <music21.note.Note A>
11099            {8.0} <music21.stream.Measure 3 offset=8.0>
11100                {0.0} <music21.bar.Barline type=final>
11101        <BLANKLINE>
11102
11103        If `separateById` is True then all voices with the same id
11104        will be connected to the same Part, regardless of order
11105        they appear in the measure.
11106
11107        Compare the previous output:
11108
11109        >>> p0pitches = ce.parts[0].pitches
11110        >>> p1pitches = ce.parts[1].pitches
11111        >>> ' '.join([p.nameWithOctave for p in p0pitches])
11112        'E4 D#4 D#4 E4 F#4 E4 B3 B3 E4 E4'
11113        >>> ' '.join([p.nameWithOctave for p in p1pitches])
11114        'F#2 F#3 E3 E2 D#2 D#3 B2 B3 E2 E3 D3 D2 C#2 C#3 A2 A3'
11115
11116        Swap voice ids in first measure:
11117
11118        >>> m0 = c.parts[0].getElementsByClass('Measure').first()
11119        >>> m0.voices[0].id, m0.voices[1].id
11120        ('3', '4')
11121        >>> m0.voices[0].id = '4'
11122        >>> m0.voices[1].id = '3'
11123
11124        Now run voicesToParts with `separateById=True`
11125
11126        >>> ce = c.voicesToParts(separateById=True)
11127        >>> p0pitches = ce.parts[0].pitches
11128        >>> p1pitches = ce.parts[1].pitches
11129        >>> ' '.join([p.nameWithOctave for p in p0pitches])
11130        'E4 D#4 D#4 E4 F#4 E2 E3 D3 D2 C#2 C#3 A2 A3'
11131        >>> ' '.join([p.nameWithOctave for p in p1pitches])
11132        'F#2 F#3 E3 E2 D#2 D#3 B2 B3 E4 B3 B3 E4 E4'
11133
11134        Note that the second and subsequent measure's pitches were changed
11135        not the first, because separateById aligns the voices according to
11136        order first encountered, not by sorting the Ids.
11137        '''
11138        s = Score()
11139        # s.metadata = self.metadata
11140
11141        # if this is a Score, call this recursively on each Part, then
11142        # add all parts to one Score
11143        if self.hasPartLikeStreams():
11144            # part-like does not necessarily mean .parts
11145            for p in self.getElementsByClass('Stream'):
11146                sSub = p.voicesToParts(separateById=separateById)
11147                for pSub in sSub:
11148                    s.insert(0, pSub)
11149            return s
11150
11151        # need to find maximum voice count
11152        mvcReturn = self._maxVoiceCount(countById=separateById)
11153        if not separateById:
11154            partCount = mvcReturn
11155            voiceIds = []
11156        else:
11157            partCount, voiceIds = mvcReturn
11158
11159        # environLocal.printDebug(['voicesToParts(): got partCount', partCount])
11160
11161        # create parts, naming ids by voice id?
11162        partDict = {}
11163
11164        for i in range(partCount):
11165            p = Part()
11166            s.insert(0, p)
11167            if not separateById:
11168                p.id = str(self.id) + '-v' + str(i)
11169                partDict[i] = p
11170            else:
11171                voiceId = voiceIds[i]
11172                p.id = str(self.id) + '-' + voiceId
11173                partDict[voiceId] = p
11174
11175        def doOneMeasureWithVoices(mInner):
11176            '''
11177            This is the main routine for dealing with the most common
11178            and most difficult voice set.
11179            '''
11180            mActive = Measure()
11181            mActive.mergeAttributes(mInner)  # get groups, optional id
11182            # merge everything except Voices; this will get
11183            # clefs
11184            mActive.mergeElements(
11185                mInner,
11186                classFilterList=(
11187                    'Barline', 'TimeSignature', 'Clef', 'KeySignature',
11188                )
11189            )
11190
11191            # vIndexInner = 0 should not be necessary, but pylint warns on loop variables
11192            # that could possibly be undefined used out of the loop.
11193            vIndexInner = 0
11194
11195            seenIdsThisMeasure = set()
11196            for vIndexInner, vInner in enumerate(mInner.voices):
11197                # TODO(msc): fix bugs if same voice id appears twice in same measure
11198
11199                # make an independent copy
11200                mNewInner = copy.deepcopy(mActive)
11201                # merge all elements from the voice
11202                mNewInner.mergeElements(vInner)
11203                # insert in the appropriate part
11204                vId = vInner.id
11205                if not separateById:
11206                    pInner = partDict[vIndexInner]
11207                else:
11208                    seenIdsThisMeasure.add(vId)
11209                    pInner = partDict[vId]
11210                pInner.insert(self.elementOffset(mInner), mNewInner)
11211
11212            # vIndexInner is now the number of voices - 1.  Fill empty voices
11213            if not separateById:
11214                for emptyIndex in range(vIndexInner + 1, partCount):
11215                    pInner = partDict[emptyIndex]
11216                    pInner.insert(self.elementOffset(mInner), copy.deepcopy(mActive))
11217            else:
11218                for voiceIdInner in partDict:
11219                    if voiceIdInner in seenIdsThisMeasure:
11220                        continue
11221                    pInner = partDict[voiceIdInner]
11222                    pInner.insert(self.elementOffset(mInner), copy.deepcopy(mActive))
11223
11224        # Place references to any instruments from the original part into the new parts
11225        for p in s.parts:
11226            p.mergeElements(self, classFilterList=('Instrument',))
11227
11228        if self.hasMeasures():
11229            for m in self.getElementsByClass('Measure'):
11230                if m.hasVoices():
11231                    doOneMeasureWithVoices(m)
11232                # if a measure does not have voices, simply populate
11233                # with elements and append
11234                else:
11235                    mNew = Measure()
11236                    mNew.mergeAttributes(m)  # get groups, optional id
11237                    # get all elements
11238                    mNew.mergeElements(m)
11239                    # always place in top-part
11240                    s.parts[0].insert(self.elementOffset(m), mNew)
11241                    for i in range(1, partCount):
11242                        mEmpty = Measure()
11243                        mEmpty.mergeAttributes(m)
11244                        # Propagate bar, meter, key elements to lower parts
11245                        mEmpty.mergeElements(m, classFilterList=('Barline',
11246                                            'TimeSignature', 'KeySignature'))
11247                        s.parts[i].insert(self.elementOffset(m), mEmpty)
11248        # if part has no measures but has voices, contents of each voice go into the part
11249        elif self.hasVoices():
11250            for vIndex, v in enumerate(self.voices):
11251                s.parts[vIndex].mergeElements(v)
11252        # if just a Stream of elements, add to a part
11253        else:
11254            s.parts[0].mergeElements(self)
11255
11256        # there is no way to assure proper clef information, so using
11257        # best clef here is desirable.
11258        for p in s.parts:
11259            # only add clef if measures are defined; otherwise, assume
11260            # best clef will be assigned later
11261            if p.hasMeasures():
11262                # place in first measure
11263                p.getElementsByClass('Measure').first().clef = clef.bestClef(p, recurse=True)
11264        return s
11265
11266    def explode(self):
11267        '''
11268        Create a multi-part extraction from a single polyphonic Part.
11269
11270        Currently just runs :meth:`~music21.stream.Stream.voicesToParts`
11271        but that will change as part explosion develops, and this
11272        method will use our best available quick method for part
11273        extraction.
11274        '''
11275        return self.voicesToParts()
11276
11277    def flattenUnnecessaryVoices(self, *, force=False, inPlace=False):
11278        '''
11279        If this Stream defines one or more internal voices, do the following:
11280
11281        * If there is more than one voice, and a voice has no elements,
11282          remove that voice.
11283        * If there is only one voice left that has elements, place those
11284          elements in the parent Stream.
11285        * If `force` is True, even if there is more than one Voice left,
11286          all voices will be flattened.
11287
11288        Changed in v. 5 -- inPlace is default False and a keyword only arg.
11289
11290        >>> s = stream.Stream(note.Note())
11291        >>> s.insert(0, note.Note())
11292        >>> s.insert(0, note.Note())
11293        >>> s.makeVoices(inPlace=True)
11294        >>> len(s.voices)
11295        3
11296
11297        >>> s.remove(s.voices[1].notes[0], recurse=True)
11298        >>> s.remove(s.voices[2].notes[0], recurse=True)
11299        >>> voicesFlattened = s.flattenUnnecessaryVoices()
11300        >>> len(voicesFlattened.voices)
11301        0
11302        '''
11303        if not self.voices:
11304            return None  # do not make copy; return immediately
11305
11306        if not inPlace:  # make a copy
11307            returnObj = copy.deepcopy(self)
11308        else:
11309            returnObj = self
11310
11311        # collect voices for removal and for flattening
11312        remove = []
11313        flatten = []
11314        for v in returnObj.voices:
11315            if not v:  # might add other criteria
11316                remove.append(v)
11317            else:
11318                flatten.append(v)
11319
11320        for v in remove:
11321            returnObj.remove(v)
11322
11323        if len(flatten) == 1 or force:  # always flatten 1
11324            for v in flatten:  # usually one unless force
11325                # get offset of voice in returnObj
11326                shiftOffset = v.getOffsetBySite(returnObj)
11327                for e in v.elements:
11328                    # insert shift + offset w/ voice
11329                    returnObj.coreInsert(shiftOffset + e.getOffsetBySite(v), e)
11330                returnObj.remove(v)
11331            returnObj.coreElementsChanged()
11332
11333        if not inPlace:
11334            return returnObj
11335        else:
11336            return None
11337
11338    # --------------------------------------------------------------------------
11339    # Lyric control
11340    # might be overwritten in base.splitAtDurations, but covered with a check
11341    # pylint: disable=method-hidden
11342    def lyrics(self, ignoreBarlines=True, recurse=False, skipTies=False):
11343        # noinspection PyShadowingNames
11344        '''
11345        Returns a dict of lists of lyric objects (with the keys being
11346        the lyric numbers) found in self. Each list will have an element for each
11347        note in the stream (which may be a note.Lyric() or None).
11348        By default, this method automatically
11349        recurses through measures, but not other container streams.
11350
11351
11352        >>> s = converter.parse('tinynotation: 4/4 a4 b c d   e f g a', makeNotation=False)
11353        >>> someLyrics = ['this', 'is', 'a', 'list', 'of', 'eight', 'lyric', 'words']
11354        >>> for n, lyric in zip(s.notes, someLyrics):
11355        ...     n.lyric = lyric
11356
11357
11358        >>> s.lyrics()
11359        {1: [<music21.note.Lyric number=1 syllabic=single text='this'>, ...,
11360             <music21.note.Lyric number=1 syllabic=single text='words'>]}
11361
11362        >>> s.notes[3].lyric = None
11363        >>> s.lyrics()[1]
11364        [<music21.note.Lyric number=1 syllabic=single text='this'>, ..., None, ...,
11365         <music21.note.Lyric number=1 syllabic=single text='words'>]
11366
11367        If ignoreBarlines is True, it will behave as if the elements in measures are all
11368        in a flattened stream (note that this is not stream.flatten()
11369        as it does not copy the elements)
11370        together without measure containers. This means that even if recurse is
11371        False, lyrics() will still essentially recurse through measures.
11372
11373        >>> s.makeMeasures(inPlace=True)
11374        >>> s.lyrics()[1]
11375        [<music21.note.Lyric number=1 syllabic=single text='this'>, ..., None, ...,
11376         <music21.note.Lyric number=1 syllabic=single text='words'>]
11377
11378        >>> list(s.lyrics(ignoreBarlines=False).keys())
11379        []
11380
11381        If recurse is True, this method will recurse through all container streams and
11382        build a nested list structure mirroring the hierarchy of the stream.
11383        Note that if ignoreBarlines is True, measure structure will not be reflected
11384        in the hierarchy, although if ignoreBarlines is False, it will.
11385
11386        Note that streams which do not contain any instance of a lyric number will not
11387        appear anywhere in the final list (not as a [] or otherwise).
11388
11389        >>> scr = stream.Score(s)
11390
11391        >>> scr.lyrics(ignoreBarlines=False, recurse=True)[1]
11392        [[[<music21.note.Lyric number=1 syllabic=single text='this'>, <...'is'>, <...'a'>, None],
11393          [<...'of'>, <...'eight'>, <...'lyric'>, <...'words'>]]]
11394
11395        Notice that the measures are nested in the part which is nested in the score.
11396
11397        >>> scr.lyrics(ignoreBarlines=True, recurse=True)[1]
11398        [[<music21.note.Lyric number=1 syllabic=single text='this'>, <...'is'>, <...'a'>, None,
11399          <...'of'>, <...'eight'>, <...'lyric'>, <...'words'>]]
11400
11401        Notice that this time, the measure structure is ignored.
11402
11403        >>> list(scr.lyrics(ignoreBarlines=True, recurse=False).keys())
11404        []
11405
11406        '''
11407        returnLists = {}
11408        numNotes = 0
11409
11410        # --------------------
11411
11412        # noinspection PyShadowingNames
11413        def appendLyricsFromNote(n, returnLists, numNonesToAppend):
11414            if not n.lyrics:
11415                for k in returnLists:
11416                    returnLists[k].append(None)
11417                return
11418
11419            addLyricNums = []
11420            for ly in n.lyrics:
11421                if ly.number not in returnLists:
11422                    returnLists[ly.number] = [None for dummy in range(numNonesToAppend)]
11423                returnLists[ly.number].append(ly)
11424                addLyricNums.append(ly.number)
11425            for k in returnLists:
11426                if k not in addLyricNums:
11427                    returnLists[k].append(None)
11428
11429        # -----------------------
11430        # TODO: use new recurse
11431        for e in self:
11432            eClasses = e.classes
11433            if ignoreBarlines is True and 'Measure' in eClasses:
11434                m = e
11435                for n in m.notes:
11436                    if skipTies is True:
11437                        if n.tie is None or n.tie.type == 'start':
11438                            appendLyricsFromNote(n, returnLists, numNotes)
11439                            numNotes += 1
11440                        else:
11441                            pass  # do nothing if end tie and skipTies is True
11442                    else:
11443                        appendLyricsFromNote(n, returnLists, numNotes)
11444                        numNotes += 1
11445
11446            elif recurse is True and 'Stream' in eClasses:
11447                s = e
11448                sublists = s.lyrics(ignoreBarlines=ignoreBarlines, recurse=True, skipTies=skipTies)
11449                for k in sublists:
11450                    if k not in returnLists:
11451                        returnLists[k] = []
11452                    returnLists[k].append(sublists[k])
11453            elif 'NotRest' in eClasses:  # elif 'Stream' not in eClasses and hasattr(e, 'lyrics'):
11454                # noinspection PyTypeChecker
11455                n: 'music21.note.NotRest' = e
11456                if skipTies is True:
11457                    if n.tie is None or n.tie.type == 'start':
11458                        appendLyricsFromNote(n, returnLists, numNotes)
11459                        numNotes += 1
11460                    else:
11461                        pass  # do nothing if end tie and skipTies is True
11462                else:
11463                    appendLyricsFromNote(n, returnLists, numNotes)
11464                    numNotes += 1
11465            else:
11466                # e is a stream
11467                # (could be a measure if ignoreBarlines is False) and recurse is False
11468                pass  # do nothing
11469
11470        return returnLists
11471
11472    # --------------------------------------------------------------------------
11473    # Variant control
11474    @property
11475    def variants(self):
11476        '''
11477        Return a Stream containing all :class:`~music21.variant.Variant` objects in this Stream.
11478
11479        >>> s = stream.Stream()
11480        >>> s.repeatAppend(note.Note('C4'), 8)
11481        >>> v1 = variant.Variant([note.Note('D#4'), note.Note('F#4')])
11482        >>> s.insert(3, v1)
11483
11484        >>> varStream = s.variants
11485        >>> len(varStream)
11486        1
11487        >>> varStream[0] is v1
11488        True
11489        >>> len(s.variants[0])
11490        2
11491
11492        Note that the D# and F# aren't found in the original Stream's pitches
11493
11494        >>> [str(p) for p in s.pitches]
11495        ['C4', 'C4', 'C4', 'C4', 'C4', 'C4', 'C4', 'C4']
11496
11497        DEPRECATED in v7.
11498        '''
11499        if 'variants' not in self._cache or self._cache['variants'] is None:
11500            self._cache['variants'] = self.getElementsByClass('Variant').stream(
11501                returnStreamSubClass=False)
11502        return self._cache['variants']
11503
11504    # ---- Variant Activation Methods
11505
11506    def activateVariants(self, group=None, *, matchBySpan=True, inPlace=False):
11507        '''
11508        For any :class:`~music21.variant.Variant` objects defined in this Stream
11509        (or selected by matching the `group` parameter),
11510        replace elements defined in the Variant with those in the calling Stream.
11511        Elements replaced will be gathered into a new Variant
11512        given the group 'default'. If a variant is activated with
11513        .replacementDuration different from its length, the appropriate elements
11514        in the stream will have their offsets shifted, and measure numbering
11515        will be fixed. If matchBySpan is True, variants with lengthType
11516        'replacement' will replace all of the elements in the
11517        replacement region of comparable class. If matchBySpan is False,
11518        elements will be swapped in when a match is found between an element
11519        in the variant and an element in the replacement region of the string.
11520
11521        >>> sStr   = 'd4 e4 f4 g4   a2 b-4 a4    g4 a8 g8 f4 e4    d2 a2              '
11522        >>> v1Str  = '              a2. b-8 a8 '
11523        >>> v2Str1 = '                                             d4 f4 a2 '
11524        >>> v2Str2 = '                                                      d4 f4 AA2 '
11525
11526        >>> sStr += "d4 e4 f4 g4    a2 b-4 a4    g4 a8 b-8 c'4 c4    f1"
11527
11528        >>> s = converter.parse('tinynotation: 4/4 ' + sStr, makeNotation=False)
11529        >>> s.makeMeasures(inPlace=True)  # maybe not necessary?
11530        >>> v1stream = converter.parse('tinynotation: 4/4 ' + v1Str, makeNotation=False)
11531        >>> v2stream1 = converter.parse('tinynotation: 4/4 ' + v2Str1, makeNotation=False)
11532        >>> v2stream2 = converter.parse('tinynotation: 4/4 ' + v2Str2, makeNotation=False)
11533
11534
11535        >>> v1 = variant.Variant()
11536        >>> v1measure = stream.Measure()
11537        >>> v1.insert(0.0, v1measure)
11538        >>> for e in v1stream.notesAndRests:
11539        ...    v1measure.insert(e.offset, e)
11540
11541        >>> v2 = variant.Variant()
11542        >>> v2measure1 = stream.Measure()
11543        >>> v2measure2 = stream.Measure()
11544        >>> v2.insert(0.0, v2measure1)
11545        >>> v2.insert(4.0, v2measure2)
11546        >>> for e in v2stream1.notesAndRests:
11547        ...    v2measure1.insert(e.offset, e)
11548        >>> for e in v2stream2.notesAndRests:
11549        ...    v2measure2.insert(e.offset, e)
11550
11551        >>> v3 = variant.Variant()
11552        >>> v2.replacementDuration = 4.0
11553        >>> v3.replacementDuration = 4.0
11554        >>> v1.groups = ['docVariants']
11555        >>> v2.groups = ['docVariants']
11556        >>> v3.groups = ['docVariants']
11557
11558        >>> s.insert(4.0, v1)    # replacement variant
11559        >>> s.insert(12.0, v2)  # insertion variant (2 bars replace 1 bar)
11560        >>> s.insert(20.0, v3)  # deletion variant (0 bars replace 1 bar)
11561        >>> s.show('text')
11562        {0.0} <music21.stream.Measure 1 offset=0.0>
11563            {0.0} <music21.clef.TrebleClef>
11564            {0.0} <music21.meter.TimeSignature 4/4>
11565            {0.0} <music21.note.Note D>
11566            {1.0} <music21.note.Note E>
11567            {2.0} <music21.note.Note F>
11568            {3.0} <music21.note.Note G>
11569        {4.0} <music21.variant.Variant object of length 4.0>
11570        {4.0} <music21.stream.Measure 2 offset=4.0>
11571            {0.0} <music21.note.Note A>
11572            {2.0} <music21.note.Note B->
11573            {3.0} <music21.note.Note A>
11574        {8.0} <music21.stream.Measure 3 offset=8.0>
11575            {0.0} <music21.note.Note G>
11576            {1.0} <music21.note.Note A>
11577            {1.5} <music21.note.Note G>
11578            {2.0} <music21.note.Note F>
11579            {3.0} <music21.note.Note E>
11580        {12.0} <music21.variant.Variant object of length 8.0>
11581        {12.0} <music21.stream.Measure 4 offset=12.0>
11582            {0.0} <music21.note.Note D>
11583            {2.0} <music21.note.Note A>
11584        {16.0} <music21.stream.Measure 5 offset=16.0>
11585            {0.0} <music21.note.Note D>
11586            {1.0} <music21.note.Note E>
11587            {2.0} <music21.note.Note F>
11588            {3.0} <music21.note.Note G>
11589        {20.0} <music21.variant.Variant object of length 0.0>
11590        {20.0} <music21.stream.Measure 6 offset=20.0>
11591            {0.0} <music21.note.Note A>
11592            {2.0} <music21.note.Note B->
11593            {3.0} <music21.note.Note A>
11594        {24.0} <music21.stream.Measure 7 offset=24.0>
11595            {0.0} <music21.note.Note G>
11596            {1.0} <music21.note.Note A>
11597            {1.5} <music21.note.Note B->
11598            {2.0} <music21.note.Note C>
11599            {3.0} <music21.note.Note C>
11600        {28.0} <music21.stream.Measure 8 offset=28.0>
11601            {0.0} <music21.note.Note F>
11602            {4.0} <music21.bar.Barline type=final>
11603
11604        >>> docVariant = s.activateVariants('docVariants')
11605
11606        >>> #_DOCS_SHOW s.show()
11607
11608        .. image:: images/stream_activateVariants1.*
11609            :width: 600
11610
11611        >>> #_DOCS_SHOW docVariant.show()
11612
11613        .. image:: images/stream_activateVariants2.*
11614            :width: 600
11615
11616        >>> docVariant.show('text')
11617        {0.0} <music21.stream.Measure 1 offset=0.0>
11618            {0.0} <music21.clef.TrebleClef>
11619            {0.0} <music21.meter.TimeSignature 4/4>
11620            {0.0} <music21.note.Note D>
11621            {1.0} <music21.note.Note E>
11622            {2.0} <music21.note.Note F>
11623            {3.0} <music21.note.Note G>
11624        {4.0} <music21.variant.Variant object of length 4.0>
11625        {4.0} <music21.stream.Measure 2 offset=4.0>
11626            {0.0} <music21.note.Note A>
11627            {3.0} <music21.note.Note B->
11628            {3.5} <music21.note.Note A>
11629        {8.0} <music21.stream.Measure 3 offset=8.0>
11630            {0.0} <music21.note.Note G>
11631            {1.0} <music21.note.Note A>
11632            {1.5} <music21.note.Note G>
11633            {2.0} <music21.note.Note F>
11634            {3.0} <music21.note.Note E>
11635        {12.0} <music21.variant.Variant object of length 4.0>
11636        {12.0} <music21.stream.Measure 4 offset=12.0>
11637            {0.0} <music21.note.Note D>
11638            {1.0} <music21.note.Note F>
11639            {2.0} <music21.note.Note A>
11640        {16.0} <music21.stream.Measure 5 offset=16.0>
11641            {0.0} <music21.note.Note D>
11642            {1.0} <music21.note.Note F>
11643            {2.0} <music21.note.Note A>
11644        {20.0} <music21.stream.Measure 6 offset=20.0>
11645            {0.0} <music21.note.Note D>
11646            {1.0} <music21.note.Note E>
11647            {2.0} <music21.note.Note F>
11648            {3.0} <music21.note.Note G>
11649        {24.0} <music21.variant.Variant object of length 4.0>
11650        {24.0} <music21.stream.Measure 7 offset=24.0>
11651            {0.0} <music21.note.Note G>
11652            {1.0} <music21.note.Note A>
11653            {1.5} <music21.note.Note B->
11654            {2.0} <music21.note.Note C>
11655            {3.0} <music21.note.Note C>
11656        {28.0} <music21.stream.Measure 8 offset=28.0>
11657            {0.0} <music21.note.Note F>
11658            {4.0} <music21.bar.Barline type=final>
11659
11660        After a variant group has been activated, the regions it replaced are
11661        stored as variants with the group 'default'.
11662        It should be noted that this means .activateVariants should rarely if
11663        ever be used on a stream which is returned
11664        by activateVariants because the group information is lost.
11665
11666        >>> defaultVariant = docVariant.activateVariants('default')
11667        >>> #_DOCS_SHOW defaultVariant.show()
11668
11669        .. image:: images/stream_activateVariants3.*
11670            :width: 600
11671
11672        >>> defaultVariant.show('text')
11673        {0.0} <music21.stream.Measure 1 offset=0.0>
11674            {0.0} <music21.clef.TrebleClef>
11675            {0.0} <music21.meter.TimeSignature 4/4>
11676            {0.0} <music21.note.Note D>
11677            {1.0} <music21.note.Note E>
11678            {2.0} <music21.note.Note F>
11679            {3.0} <music21.note.Note G>
11680        {4.0} <music21.variant.Variant object of length 4.0>
11681        {4.0} <music21.stream.Measure 2 offset=4.0>
11682            {0.0} <music21.note.Note A>
11683            {2.0} <music21.note.Note B->
11684            {3.0} <music21.note.Note A>
11685        {8.0} <music21.stream.Measure 3 offset=8.0>
11686            {0.0} <music21.note.Note G>
11687            {1.0} <music21.note.Note A>
11688            {1.5} <music21.note.Note G>
11689            {2.0} <music21.note.Note F>
11690            {3.0} <music21.note.Note E>
11691        {12.0} <music21.variant.Variant object of length 8.0>
11692        {12.0} <music21.stream.Measure 4 offset=12.0>
11693            {0.0} <music21.note.Note D>
11694            {2.0} <music21.note.Note A>
11695        {16.0} <music21.stream.Measure 5 offset=16.0>
11696            {0.0} <music21.note.Note D>
11697            {1.0} <music21.note.Note E>
11698            {2.0} <music21.note.Note F>
11699            {3.0} <music21.note.Note G>
11700        {20.0} <music21.variant.Variant object of length 0.0>
11701        {20.0} <music21.stream.Measure 6 offset=20.0>
11702            {0.0} <music21.note.Note A>
11703            {2.0} <music21.note.Note B->
11704            {3.0} <music21.note.Note A>
11705        {24.0} <music21.stream.Measure 7 offset=24.0>
11706            {0.0} <music21.note.Note G>
11707            {1.0} <music21.note.Note A>
11708            {1.5} <music21.note.Note B->
11709            {2.0} <music21.note.Note C>
11710            {3.0} <music21.note.Note C>
11711        {28.0} <music21.stream.Measure 8 offset=28.0>
11712            {0.0} <music21.note.Note F>
11713            {4.0} <music21.bar.Barline type=final>
11714        '''
11715        if not inPlace:  # make a copy if inPlace is False
11716            returnObj = self.coreCopyAsDerivation('activateVariants')
11717        else:
11718            returnObj = self
11719
11720        # Define Lists to cache variants
11721        elongationVariants = []
11722        deletionVariants = []
11723
11724        # Loop through all variants, deal with replacement variants and
11725        # save insertion and deletion for later.
11726        for v in returnObj.variants:
11727            if group is not None:
11728                if group not in v.groups:
11729                    continue  # skip those that are not part of this group
11730
11731            lengthType = v.lengthType
11732
11733            # save insertions to perform last
11734            if lengthType == 'elongation':
11735                elongationVariants.append(v)
11736            # save deletions to perform after replacements
11737            elif lengthType == 'deletion':
11738                deletionVariants.append(v)
11739            # Deal with cases in which variant is the same length as what it replaces first.
11740            elif lengthType == 'replacement':
11741                returnObj._insertReplacementVariant(v, matchBySpan)
11742
11743        # Now deal with deletions before insertion variants.
11744        # For keeping track of which measure numbers have been removed
11745        deletedMeasures = []
11746        # For keeping track of where new measures without measure numbers have been inserted,
11747        # will be a list of tuples (measureNumberPrior, [List, of, inserted, measures])
11748        insertedMeasures = []
11749        # For keeping track of the sections that are deleted
11750        # (so the offset gap can be closed later)
11751        deletedRegionsForRemoval = []
11752        for v in deletionVariants:
11753            (deletedRegion, vDeletedMeasures, vInsertedMeasuresTuple
11754             ) = returnObj._insertDeletionVariant(v, matchBySpan)  # deletes and inserts
11755            deletedRegionsForRemoval.append(deletedRegion)  # Saves the deleted region
11756            deletedMeasures.extend(vDeletedMeasures)  # Saves the deleted measure numbers
11757            # saves the inserted numberless measures (this will be empty unless there are
11758            # more bars in the variant than in the replacement region, which is unlikely
11759            # for a deletion variant.
11760            insertedMeasures.append(vInsertedMeasuresTuple)
11761
11762        # Squeeze out the gaps that were saved.
11763        returnObj._removeOrExpandGaps(deletedRegionsForRemoval, isRemove=True, inPlace=True)
11764
11765        # Before we can deal with insertions, we have to expand the stream to make space.
11766        insertionRegionsForExpansion = []  # For saving the insertion regions
11767        # go through all elongation variants to find the insertion regions.
11768        for v in elongationVariants:
11769            lengthDifference = v.replacementDuration - v.containedHighestTime
11770            insertionStart = v.getOffsetBySite(returnObj) + v.replacementDuration
11771            # Saves the information for each gap to be expanded
11772            insertionRegionsForExpansion.append((insertionStart, -1 * lengthDifference, [v]))
11773
11774        # Expands the appropriate gaps in the stream.
11775        returnObj._removeOrExpandGaps(insertionRegionsForExpansion, isRemove=False, inPlace=True)
11776        # Now deal with elongation variants properly
11777        for v in elongationVariants:
11778            (vInsertedMeasuresTuple, vDeletedMeasures
11779             ) = returnObj._insertInsertionVariant(v, matchBySpan)  # deletes and inserts
11780            insertedMeasures.append(vInsertedMeasuresTuple)
11781            # Saves the numberless inserted measures
11782            # Saves deleted measures if any (it is unlikely that there will be unless
11783            # there are fewer measures in the variant than the replacement region,
11784            # which is unlikely for an elongation variant)
11785            deletedMeasures.extend(vDeletedMeasures)
11786
11787        # Now fix measure numbers given the saved information
11788        returnObj._fixMeasureNumbers(deletedMeasures, insertedMeasures)
11789
11790        # have to clear cached variants, as they are no longer the same
11791        returnObj.coreElementsChanged()
11792
11793        if not inPlace:
11794            return returnObj
11795        else:
11796            return None
11797
11798    def _insertReplacementVariant(self, v, matchBySpan=True):
11799        # noinspection PyShadowingNames
11800        '''
11801        Helper function for activateVariants. Activates variants which are the same size there the
11802        region they replace.
11803
11804
11805        >>> v = variant.Variant()
11806        >>> variantDataM1 = [('b', 'eighth'), ('c', 'eighth'),
11807        ...                  ('a', 'quarter'), ('a', 'quarter'),('b', 'quarter')]
11808        >>> variantDataM2 = [('c', 'quarter'), ('d', 'quarter'), ('e', 'quarter'), ('e', 'quarter')]
11809        >>> variantData = [variantDataM1, variantDataM2]
11810        >>> for d in variantData:
11811        ...    m = stream.Measure()
11812        ...    for pitchName,durType in d:
11813        ...        n = note.Note(pitchName)
11814        ...        n.duration.type = durType
11815        ...        m.append(n)
11816        ...    v.append(m)
11817        >>> v.groups = ['paris']
11818        >>> v.replacementDuration = 8.0
11819
11820        >>> s = stream.Stream()
11821        >>> streamDataM1 = [('a', 'quarter'), ('b', 'quarter'), ('a', 'quarter'), ('g', 'quarter')]
11822        >>> streamDataM2 = [('b', 'eighth'), ('c', 'quarter'), ('a', 'eighth'),
11823        ...                 ('a', 'quarter'), ('b', 'quarter')]
11824        >>> streamDataM3 = [('c', 'quarter'), ('b', 'quarter'), ('a', 'quarter'), ('a', 'quarter')]
11825        >>> streamDataM4 = [('c', 'quarter'), ('b', 'quarter'), ('a', 'quarter'), ('a', 'quarter')]
11826        >>> streamData = [streamDataM1, streamDataM2, streamDataM3, streamDataM4]
11827        >>> for d in streamData:
11828        ...    m = stream.Measure()
11829        ...    for pitchName,durType in d:
11830        ...        n = note.Note(pitchName)
11831        ...        n.duration.type = durType
11832        ...        m.append(n)
11833        ...    s.append(m)
11834        >>> s.insert(4.0, v)
11835
11836        >>> deletedMeasures, insertedMeasuresTuple = s._insertReplacementVariant(v)
11837        >>> deletedMeasures
11838        []
11839        >>> insertedMeasuresTuple
11840        (0, [])
11841        >>> s.show('text')
11842        {0.0} <music21.stream.Measure 0 offset=0.0>
11843            {0.0} <music21.note.Note A>
11844            {1.0} <music21.note.Note B>
11845            {2.0} <music21.note.Note A>
11846            {3.0} <music21.note.Note G>
11847        {4.0} <music21.variant.Variant object of length 8.0>
11848        {4.0} <music21.stream.Measure 0 offset=4.0>
11849            {0.0} <music21.note.Note B>
11850            {0.5} <music21.note.Note C>
11851            {1.0} <music21.note.Note A>
11852            {2.0} <music21.note.Note A>
11853            {3.0} <music21.note.Note B>
11854        {8.0} <music21.stream.Measure 0 offset=8.0>
11855            {0.0} <music21.note.Note C>
11856            {1.0} <music21.note.Note D>
11857            {2.0} <music21.note.Note E>
11858            {3.0} <music21.note.Note E>
11859        {12.0} <music21.stream.Measure 0 offset=12.0>
11860            {0.0} <music21.note.Note C>
11861            {1.0} <music21.note.Note B>
11862            {2.0} <music21.note.Note A>
11863            {3.0} <music21.note.Note A>
11864        '''
11865        from music21 import variant
11866
11867        removed = variant.Variant()  # replacement variant
11868        removed.groups = ['default']  # for now, default
11869        vStart = self.elementOffset(v)
11870        # this method matches and removes on an individual basis
11871        if not matchBySpan:
11872            targetsMatched = 0
11873            for e in v.elements:  # get components in the Variant
11874                # get target offset relative to Stream
11875                oInStream = vStart + e.getOffsetBySite(v.containedSite)
11876                # get all elements at this offset, force a class match
11877                targets = self.getElementsByOffset(oInStream).getElementsByClass(e.classes[0])
11878                # only replace if we match the start
11879                if targets:
11880                    targetsMatched += 1
11881                    # always assume we just want the first one?
11882                    targetToReplace = targets[0]
11883                    # environLocal.printDebug(['matchBySpan', matchBySpan,
11884                    #     'found target to replace:', targetToReplace])
11885                    # remove the target, place in removed Variant
11886                    removed.append(targetToReplace)
11887                    self.remove(targetToReplace)
11888                    # extract the variant component and insert into place
11889                    self.insert(oInStream, e)
11890
11891                    if getattr(targetToReplace, 'isMeasure', False):
11892                        e.number = targetToReplace.number
11893            # only remove old and add removed if we matched
11894            if targetsMatched > 0:
11895                # remove the original variant
11896                self.remove(v)
11897                # place newly contained elements in position
11898                self.insert(vStart, removed)
11899
11900        # matching by span means that we remove all elements with the
11901        # span defined by the variant
11902        else:
11903            deletedMeasures = []
11904            insertedMeasures = []
11905            highestNumber = None
11906
11907            targets = v.replacedElements(self)
11908
11909            # this will always remove elements before inserting
11910            for e in targets:
11911                # need to get time relative to variant container's position
11912                oInVariant = self.elementOffset(e) - vStart
11913                removed.insert(oInVariant, e)
11914                # environLocal.printDebug(
11915                #     ['matchBySpan', matchBySpan, 'activateVariants', 'removing', e])
11916                self.remove(e)
11917                if getattr(e, 'isMeasure', False):
11918                    # Save deleted measure numbers.
11919                    deletedMeasures.append(e.number)
11920
11921            for e in v.elements:
11922                oInStream = vStart + e.getOffsetBySite(v.containedSite)
11923                self.insert(oInStream, e)
11924                if getattr(e, 'isMeasure', False):
11925                    if deletedMeasures:  # If there measure numbers left to use, use them.
11926                        # Assign the next highest deleted measure number
11927                        e.number = deletedMeasures.pop(False)
11928                        # Save the highest number used so far (for use in the case
11929                        # that there are extra measures with no numbers at the end)
11930                        highestNumber = e.number
11931
11932                    else:
11933                        e.number = 0
11934                        # If no measure numbers left, add this
11935                        # numberless measure to insertedMeasures
11936                        insertedMeasures.append(e)
11937            # remove the source variant
11938            self.remove(v)
11939            # place newly contained elements in position
11940            self.insert(vStart, removed)
11941
11942            # If deletedMeasures != [], then there were more deleted measures than
11943            # inserted and the remaining numbers in deletedMeasures are those that were removed.
11944            return deletedMeasures, (highestNumber, insertedMeasures)
11945            # In the case that the variant and stream are in the same time-signature,
11946            # this should return []
11947
11948    def _insertDeletionVariant(self, v, matchBySpan=True):
11949        # noinspection PyShadowingNames
11950        '''
11951        Helper function for activateVariants used for inserting variants that are shorter than
11952        the region they replace. Inserts elements in the variant and deletes elements in the
11953        replaced region but does not close gaps.
11954
11955        Returns a tuple describing the region where elements were removed, the
11956        gap is left behind to be dealt with by _removeOrExpandGaps.
11957        Tuple is of form (startOffset, lengthOfDeletedRegion, []). The empty list is
11958        expected by _removeOrExpandGaps
11959        and describes the list of elements which should be exempted from shifting
11960        for a particular gap. In the
11961        case of deletion, no elements need be exempted.
11962
11963        >>> v = variant.Variant()
11964        >>> variantDataM1 = [('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'),
11965        ...                  ('a', 'quarter'),('b', 'quarter')]
11966        >>> variantDataM2 = [('c', 'quarter'), ('d', 'quarter'),
11967        ...                  ('e', 'quarter'), ('e', 'quarter')]
11968        >>> variantData = [variantDataM1, variantDataM2]
11969        >>> for d in variantData:
11970        ...    m = stream.Measure()
11971        ...    for pitchName,durType in d:
11972        ...        n = note.Note(pitchName)
11973        ...        n.duration.type = durType
11974        ...        m.append(n)
11975        ...    v.append(m)
11976        >>> v.groups = ['paris']
11977        >>> v.replacementDuration = 12.0
11978
11979        >>> s = stream.Stream()
11980        >>> streamDataM1 = [('a', 'quarter'), ('b', 'quarter'), ('a', 'quarter'), ('g', 'quarter')]
11981        >>> streamDataM2 = [('b', 'eighth'), ('c', 'quarter'), ('a', 'eighth'),
11982        ...                 ('a', 'quarter'), ('b', 'quarter')]
11983        >>> streamDataM3 = [('c', 'quarter'), ('b', 'quarter'), ('a', 'quarter'), ('a', 'quarter')]
11984        >>> streamDataM4 = [('c', 'quarter'), ('b', 'quarter'), ('a', 'quarter'), ('a', 'quarter')]
11985        >>> streamData = [streamDataM1, streamDataM2, streamDataM3, streamDataM4]
11986        >>> for d in streamData:
11987        ...    m = stream.Measure()
11988        ...    for pitchName,durType in d:
11989        ...        n = note.Note(pitchName)
11990        ...        n.duration.type = durType
11991        ...        m.append(n)
11992        ...    s.append(m)
11993        >>> s.insert(4.0, v)
11994
11995
11996        >>> deletedRegion, deletedMeasures, insertedMeasuresTuple = s._insertDeletionVariant(v)
11997        >>> deletedRegion
11998        (12.0, 4.0, [])
11999        >>> deletedMeasures
12000        [0]
12001        >>> insertedMeasuresTuple
12002        (0, [])
12003        >>> s.show('text')
12004        {0.0} <music21.stream.Measure 0 offset=0.0>
12005            {0.0} <music21.note.Note A>
12006            {1.0} <music21.note.Note B>
12007            {2.0} <music21.note.Note A>
12008            {3.0} <music21.note.Note G>
12009        {4.0} <music21.variant.Variant object of length 12.0>
12010        {4.0} <music21.stream.Measure 0 offset=4.0>
12011            {0.0} <music21.note.Note B>
12012            {0.5} <music21.note.Note C>
12013            {1.0} <music21.note.Note A>
12014            {2.0} <music21.note.Note A>
12015            {3.0} <music21.note.Note B>
12016        {8.0} <music21.stream.Measure 0 offset=8.0>
12017            {0.0} <music21.note.Note C>
12018            {1.0} <music21.note.Note D>
12019            {2.0} <music21.note.Note E>
12020            {3.0} <music21.note.Note E>
12021
12022        '''
12023        from music21 import variant
12024
12025        deletedMeasures = []  # For keeping track of what measure numbers are deleted
12026        # length of the deleted region
12027        lengthDifference = v.replacementDuration - v.containedHighestTime
12028
12029        removed = variant.Variant()  # what group should this have?
12030        removed.groups = ['default']  # for now, default
12031        removed.replacementDuration = v.containedHighestTime
12032
12033        vStart = self.elementOffset(v)
12034        deletionStart = vStart + v.containedHighestTime
12035
12036        targets = v.replacedElements(self)
12037
12038        # this will always remove elements before inserting
12039        for e in targets:
12040            if getattr(e, 'isMeasure', False):  # if a measure is deleted, save its number
12041                deletedMeasures.append(e.number)
12042            oInVariant = self.elementOffset(e) - vStart
12043            removed.insert(oInVariant, e)
12044            self.remove(e)
12045
12046        # Next put in the elements from the variant
12047        highestNumber = None
12048        insertedMeasures = []
12049        for e in v.elements:
12050            if getattr(e, 'isMeasure', False):
12051                # If there are deleted numbers still saved, assign this measure the
12052                # next highest and remove it from the list.
12053                if deletedMeasures:
12054                    e.number = deletedMeasures.pop(False)
12055                    # Save the highest number assigned so far. If there are numberless
12056                    # inserted measures at the end, this will name where to begin numbering.
12057                    highestNumber = e.number
12058                else:
12059                    e.number = 0
12060                    # If there are no deleted numbers left (unlikely)
12061                    # save the inserted measures for renumbering later.
12062                    insertedMeasures.append(e)
12063
12064            oInStream = vStart + e.getOffsetBySite(v.containedSite)
12065            self.insert(oInStream, e)
12066
12067        # remove the source variant
12068        self.remove(v)
12069        # place newly contained elements in position
12070        self.insert(vStart, removed)
12071
12072        # each variant leaves a gap, this saves the required information about those gaps
12073        # In most cases, inserted measures should be [].
12074        return (deletionStart, lengthDifference, []), deletedMeasures, (
12075            highestNumber, insertedMeasures)
12076
12077    def _insertInsertionVariant(self, v, matchBySpan=True):
12078        # noinspection PyShadowingNames
12079        '''
12080        Helper function for activateVariants. _removeOrExpandGaps must be called on the
12081        expanded regions before this function
12082        or it will not work properly.
12083
12084
12085        >>> v = variant.Variant()
12086        >>> variantDataM1 = [('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'),
12087        ...                  ('a', 'quarter'),('b', 'quarter')]
12088        >>> variantDataM2 = [('c', 'quarter'), ('d', 'quarter'),
12089        ...                  ('e', 'quarter'), ('e', 'quarter')]
12090        >>> variantData = [variantDataM1, variantDataM2]
12091        >>> for d in variantData:
12092        ...    m = stream.Measure()
12093        ...    for pitchName,durType in d:
12094        ...        n = note.Note(pitchName)
12095        ...        n.duration.type = durType
12096        ...        m.append(n)
12097        ...    v.append(m)
12098        >>> v.groups = ['paris']
12099        >>> v.replacementDuration = 4.0
12100
12101        >>> s = stream.Stream()
12102        >>> streamDataM1 = [('a', 'quarter'), ('b', 'quarter'), ('a', 'quarter'), ('g', 'quarter')]
12103        >>> streamDataM2 = [('b', 'eighth'), ('c', 'quarter'), ('a', 'eighth'),
12104        ...                 ('a', 'quarter'), ('b', 'quarter')]
12105        >>> streamDataM3 = [('c', 'quarter'), ('b', 'quarter'), ('a', 'quarter'), ('a', 'quarter')]
12106        >>> streamDataM4 = [('c', 'quarter'), ('b', 'quarter'), ('a', 'quarter'), ('a', 'quarter')]
12107        >>> streamData = [streamDataM1, streamDataM2, streamDataM3, streamDataM4]
12108        >>> for d in streamData:
12109        ...    m = stream.Measure()
12110        ...    for pitchName,durType in d:
12111        ...        n = note.Note(pitchName)
12112        ...        n.duration.type = durType
12113        ...        m.append(n)
12114        ...    s.append(m)
12115        >>> s.insert(4.0, v)
12116
12117        >>> insertionRegionsForExpansion = [(8.0, 4.0, [v])]
12118        >>> s._removeOrExpandGaps(insertionRegionsForExpansion, isRemove=False, inPlace=True)
12119
12120        >>> insertedMeasuresTuple, deletedMeasures = s._insertInsertionVariant(v)
12121        >>> measurePrior, insertedMeasures = insertedMeasuresTuple
12122        >>> measurePrior
12123        0
12124        >>> len(insertedMeasures)
12125        1
12126
12127        >>> s.show('text')
12128        {0.0} <music21.stream.Measure 0 offset=0.0>
12129            {0.0} <music21.note.Note A>
12130            {1.0} <music21.note.Note B>
12131            {2.0} <music21.note.Note A>
12132            {3.0} <music21.note.Note G>
12133        {4.0} <music21.variant.Variant object of length 4.0>
12134        {4.0} <music21.stream.Measure 0 offset=4.0>
12135            {0.0} <music21.note.Note B>
12136            {0.5} <music21.note.Note C>
12137            {1.0} <music21.note.Note A>
12138            {2.0} <music21.note.Note A>
12139            {3.0} <music21.note.Note B>
12140        {8.0} <music21.stream.Measure 0 offset=8.0>
12141            {0.0} <music21.note.Note C>
12142            {1.0} <music21.note.Note D>
12143            {2.0} <music21.note.Note E>
12144            {3.0} <music21.note.Note E>
12145        {12.0} <music21.stream.Measure 0 offset=12.0>
12146            {0.0} <music21.note.Note C>
12147            {1.0} <music21.note.Note B>
12148            {2.0} <music21.note.Note A>
12149            {3.0} <music21.note.Note A>
12150        {16.0} <music21.stream.Measure 0 offset=16.0>
12151            {0.0} <music21.note.Note C>
12152            {1.0} <music21.note.Note B>
12153            {2.0} <music21.note.Note A>
12154            {3.0} <music21.note.Note A>
12155
12156        '''
12157        from music21 import variant
12158
12159        deletedMeasures = []
12160        removed = variant.Variant()  # what group should this have?
12161        removed.groups = ['default']  # for now, default
12162        removed.replacementDuration = v.containedHighestTime
12163        vStart = self.elementOffset(v)
12164
12165        # First deal with the elements in the overlapping section (limit by class)
12166        targets = v.replacedElements(self)
12167
12168        # this will always remove elements before inserting
12169        for e in targets:
12170            if getattr(e, 'isMeasure', False):  # Save deleted measure numbers.
12171                deletedMeasures.append(e.number)
12172            oInVariant = self.elementOffset(e) - vStart
12173            removed.insert(oInVariant, e)
12174            self.remove(e)
12175
12176        # Next put in the elements from the variant
12177        highestMeasure = None
12178        insertedMeasures = []
12179        for e in v.elements:
12180            if getattr(e, 'isMeasure', False):
12181                # If there are deleted measure numbers left, assign the next
12182                # inserted measure the next highest number and remove it.
12183                if deletedMeasures:
12184                    e.number = deletedMeasures.pop(False)
12185                    highestMeasure = e.number
12186                    # Save the highest number assigned so far
12187                    # so we know where to being numbering new measures.
12188                else:
12189                    e.number = 0
12190                    insertedMeasures.append(e)
12191                    # If there are no deleted measures, we have begun inserting as yet
12192                    # unnumbered measures, save which those are.
12193            oInStream = vStart + e.getOffsetBySite(v.containedSite)
12194            self.insert(oInStream, e)
12195
12196        if highestMeasure is None:
12197            # If the highestMeasure is None (which will occur if the variant is
12198            # a strict insertion and replaces no measures,
12199            # we need to choose the highest measure number prior to the variant.
12200            measuresToCheck = self.getElementsByOffset(0.0,
12201                                                       self.elementOffset(v),
12202                                                       includeEndBoundary=True,
12203                                                       mustFinishInSpan=False,
12204                                                       mustBeginInSpan=True,
12205                                                       ).getElementsByClass('Measure')
12206            highestMeasure = 0
12207            for m in measuresToCheck:
12208                if highestMeasure is None or m.number > highestMeasure:
12209                    highestMeasure = m.number
12210
12211        # remove the source variant
12212        self.remove(v)
12213        # place newly contained elements in position
12214        self.insert(vStart, removed)
12215
12216        return (highestMeasure, insertedMeasures), deletedMeasures
12217
12218    def _removeOrExpandGaps(self, listOffsetDurExemption,
12219                            isRemove=True, inPlace=False, exemptClasses=None):
12220        '''
12221        Helper for activateVariants. Takes a list of tuples in the form
12222        (startOffset, duration, [list, of, exempt, objects]). If isRemove is True,
12223        gaps with duration will be closed at each startOffset.
12224        Exempt objects are useful for gap-expansion with variants. The gap must push all objects
12225        that occur after the insertion ahead, but the variant object
12226        itself should not be moved except by other gaps. This is poorly written
12227        and should be re-written, but it is difficult to describe.
12228
12229
12230        >>> s = stream.Stream()
12231        >>> s.insert(5.0, note.Note('a'))
12232        >>> s.insert(10.0, note.Note('b'))
12233        >>> s.insert(11.0, note.Note('c'))
12234        >>> s.insert(12.0, note.Note('d'))
12235        >>> s.insert(13.0, note.Note('e'))
12236        >>> s.insert(20.0, note.Note('f'))
12237        >>> n = note.Note('g')
12238        >>> s.insert(15.0, n)
12239
12240        >>> sGapsRemoved = s._removeOrExpandGaps(
12241        ...             [(0.0, 5.0, []), (6.0, 4.0, []), (14.0, 6.0, [n])], isRemove=True)
12242        >>> sGapsRemoved.show('text')
12243        {0.0} <music21.note.Note A>
12244        {1.0} <music21.note.Note B>
12245        {2.0} <music21.note.Note C>
12246        {3.0} <music21.note.Note D>
12247        {4.0} <music21.note.Note E>
12248        {5.0} <music21.note.Note F>
12249        {15.0} <music21.note.Note G>
12250
12251        >>> sGapsExpanded = s._removeOrExpandGaps(
12252        ...            [(0.0, 5.0, []), (11.0, 5.0, []), (14.0, 1.0, [n])], isRemove=False)
12253        >>> sGapsExpanded.show('text')
12254        {10.0} <music21.note.Note A>
12255        {15.0} <music21.note.Note B>
12256        {21.0} <music21.note.Note C>
12257        {22.0} <music21.note.Note D>
12258        {23.0} <music21.note.Note E>
12259        {25.0} <music21.note.Note G>
12260        {31.0} <music21.note.Note F>
12261        '''
12262        if inPlace is True:
12263            returnObj = self
12264        else:
12265            returnObj = copy.deepcopy(self)
12266
12267        returnObjDuration = returnObj.duration.quarterLength
12268
12269        # If any classes should be exempt from gap closing or expanding, this deals with those.
12270        classList = []
12271        if exemptClasses is None:
12272            classList = None
12273        else:
12274            for e in returnObj.elements:
12275                if type(e) not in classList:  # pylint: disable=unidiomatic-typecheck
12276                    classList.append(type(e))
12277            for c in exemptClasses:
12278                if c in classList:
12279                    classList.remove(c)
12280
12281        if isRemove is True:
12282            shiftDur = 0.0
12283            listSorted = sorted(listOffsetDurExemption, key=lambda target: target[0])
12284            for i, durTuple in enumerate(listSorted):
12285                startOffset, durationAmount, exemptObjects = durTuple
12286                if i + 1 < len(listSorted):
12287                    endOffset = listSorted[i + 1][0]
12288                    includeEnd = False
12289                else:
12290                    endOffset = returnObjDuration
12291                    includeEnd = True
12292
12293                shiftDur = shiftDur + durationAmount
12294                for e in returnObj.getElementsByOffset(startOffset + durationAmount,
12295                                                       endOffset,
12296                                                       includeEndBoundary=includeEnd,
12297                                                       mustFinishInSpan=False,
12298                                                       mustBeginInSpan=True,
12299                                                       classList=classList):
12300
12301                    if e in exemptObjects:
12302                        continue
12303
12304                    elementOffset = e.getOffsetBySite(returnObj)
12305                    returnObj.coreSetElementOffset(e, elementOffset - shiftDur)
12306        else:
12307            shiftDur = 0.0
12308            shiftsDict = {}
12309            listSorted = sorted(listOffsetDurExemption, key=lambda target: target[0])
12310            for i, durTuple in enumerate(listSorted):
12311                startOffset, durationAmount, exemptObjects = durTuple
12312
12313                if i + 1 < len(listSorted):
12314                    endOffset = listSorted[i + 1][0]
12315                    includeEnd = False
12316                else:
12317                    endOffset = returnObjDuration
12318                    includeEnd = True
12319
12320                exemptShift = shiftDur
12321                shiftDur = shiftDur + durationAmount
12322                shiftsDict[startOffset] = (shiftDur, endOffset, includeEnd,
12323                                           exemptObjects, exemptShift)
12324
12325            for offset in sorted(shiftsDict, key=lambda off: -1 * off):
12326                shiftDur, endOffset, includeEnd, exemptObjects, exemptShift = shiftsDict[offset]
12327                for e in returnObj.getElementsByOffset(offset,
12328                                                       endOffset,
12329                                                       includeEndBoundary=includeEnd,
12330                                                       mustFinishInSpan=False,
12331                                                       mustBeginInSpan=True,
12332                                                       classList=classList):
12333
12334                    if e in exemptObjects:
12335                        elementOffset = e.getOffsetBySite(returnObj)
12336                        returnObj.coreSetElementOffset(e, elementOffset + exemptShift)
12337                        continue
12338
12339                    elementOffset = e.getOffsetBySite(returnObj)
12340                    returnObj.coreSetElementOffset(e, elementOffset + shiftDur)
12341
12342        # ran coreSetElementOffset
12343        returnObj.coreElementsChanged()
12344
12345        if inPlace is True:
12346            return
12347        else:
12348            return returnObj
12349
12350    def _fixMeasureNumbers(self, deletedMeasures, insertedMeasures):
12351        # noinspection PyShadowingNames
12352        '''
12353        Corrects the measures numbers of a string of measures given a list of measure numbers
12354        that have been deleted and a
12355        list of tuples (highest measure number below insertion, number of inserted measures).
12356
12357        >>> s = converter.parse('tinynotation: 4/4 d4 e4 f4 g4   a2 b-4 a4    g4 a8 g8 f4 e4  g1')
12358        >>> s[-1].offset = 20.0
12359        >>> s.show('text')
12360        {0.0} <music21.stream.Measure 1 offset=0.0>
12361            {0.0} <music21.clef.TrebleClef>
12362            {0.0} <music21.meter.TimeSignature 4/4>
12363            {0.0} <music21.note.Note D>
12364            {1.0} <music21.note.Note E>
12365            {2.0} <music21.note.Note F>
12366            {3.0} <music21.note.Note G>
12367        {4.0} <music21.stream.Measure 2 offset=4.0>
12368            {0.0} <music21.note.Note A>
12369            {2.0} <music21.note.Note B->
12370            {3.0} <music21.note.Note A>
12371        {8.0} <music21.stream.Measure 3 offset=8.0>
12372            {0.0} <music21.note.Note G>
12373            {1.0} <music21.note.Note A>
12374            {1.5} <music21.note.Note G>
12375            {2.0} <music21.note.Note F>
12376            {3.0} <music21.note.Note E>
12377        {20.0} <music21.stream.Measure 4 offset=20.0>
12378            {0.0} <music21.note.Note G>
12379            {4.0} <music21.bar.Barline type=final>
12380        >>> s.remove(s.measure(2))
12381        >>> s.show('text')
12382        {0.0} <music21.stream.Measure 1 offset=0.0>
12383            {0.0} <music21.clef.TrebleClef>
12384            ...
12385        {8.0} <music21.stream.Measure 3 offset=8.0>
12386            {0.0} <music21.note.Note G>
12387            ...
12388        {20.0} <music21.stream.Measure 4 offset=20.0>
12389            {0.0} <music21.note.Note G>
12390            {4.0} <music21.bar.Barline type=final>
12391        >>> deletedMeasures = [2]
12392        >>> m1 = stream.Measure()
12393        >>> m1.repeatAppend(note.Note('e'),4)
12394        >>> s.insert(12.0, m1)
12395        >>> m2 = stream.Measure()
12396        >>> m2.repeatAppend(note.Note('f'),4)
12397        >>> s.insert(16.0, m2)
12398        >>> s.show('text')
12399        {0.0} <music21.stream.Measure 1 offset=0.0>
12400            {0.0} <music21.clef.TrebleClef>
12401            ...
12402        {8.0} <music21.stream.Measure 3 offset=8.0>
12403            {0.0} <music21.note.Note G>
12404            ...
12405        {12.0} <music21.stream.Measure 0 offset=12.0>
12406            {0.0} <music21.note.Note E>
12407            {1.0} <music21.note.Note E>
12408            {2.0} <music21.note.Note E>
12409            {3.0} <music21.note.Note E>
12410        {16.0} <music21.stream.Measure 0 offset=16.0>
12411            {0.0} <music21.note.Note F>
12412            {1.0} <music21.note.Note F>
12413            {2.0} <music21.note.Note F>
12414            {3.0} <music21.note.Note F>
12415        {20.0} <music21.stream.Measure 4 offset=20.0>
12416            {0.0} <music21.note.Note G>
12417            {4.0} <music21.bar.Barline type=final>
12418        >>> insertedMeasures = [(3, [m1, m2])]
12419
12420        >>> s._fixMeasureNumbers(deletedMeasures, insertedMeasures)
12421        >>> s.show('text')
12422        {0.0} <music21.stream.Measure 1 offset=0.0>
12423            {0.0} <music21.clef.TrebleClef>
12424            {0.0} <music21.meter.TimeSignature 4/4>
12425            ...
12426        {8.0} <music21.stream.Measure 2 offset=8.0>
12427            {0.0} <music21.note.Note G>
12428            ...
12429        {12.0} <music21.stream.Measure 3 offset=12.0>
12430            {0.0} <music21.note.Note E>
12431            ...
12432        {16.0} <music21.stream.Measure 4 offset=16.0>
12433            {0.0} <music21.note.Note F>
12434            ...
12435        {20.0} <music21.stream.Measure 5 offset=20.0>
12436            {0.0} <music21.note.Note G>
12437            {4.0} <music21.bar.Barline type=final>
12438        >>> fixedNumbers = []
12439        >>> for m in s.getElementsByClass('Measure'):
12440        ...    fixedNumbers.append( m.number )
12441        >>> fixedNumbers
12442        [1, 2, 3, 4, 5]
12443
12444        '''
12445        deletedMeasures.extend(insertedMeasures)
12446        allMeasures = deletedMeasures
12447
12448        if not allMeasures:
12449            return
12450
12451        def measureNumberSortRoutine(numOrNumTuple):
12452            if isinstance(numOrNumTuple, tuple):
12453                return measureNumberSortRoutine(numOrNumTuple[0])
12454            elif numOrNumTuple is None:
12455                return -9999
12456            else:
12457                return numOrNumTuple
12458
12459        allMeasures.sort(key=measureNumberSortRoutine)
12460
12461        oldMeasures = self.getElementsByClass('Measure').stream()
12462        newMeasures = []
12463
12464        cumulativeNumberShift = 0
12465        oldCorrections = {}
12466        newCorrections = {}
12467        # the inserted measures must be treated differently than the original measures.
12468        # an inserted measure should not shift itself,
12469        # but it should shift measures with the same number.
12470        # However, inserted measures should still be shifted by every other correction.
12471
12472        # First collect dictionaries of shift boundaries and the amount of the shift.
12473        # at the same time, five un-numbered measures numbers that make sense.
12474        for measureNumber in allMeasures:
12475            if isinstance(measureNumber, tuple):  # tuple implies insertion
12476                measurePrior, extendedMeasures = measureNumber
12477                if not extendedMeasures:  # No measures were added, therefore no shift.
12478                    continue
12479                cumulativeNumberShift += len(extendedMeasures)
12480                nextMeasure = measurePrior + 1
12481                for m in extendedMeasures:
12482                    oldMeasures.remove(m)
12483                    newMeasures.append(m)
12484                    m.number = nextMeasure
12485                    nextMeasure += 1
12486                oldCorrections[measurePrior + 1] = cumulativeNumberShift
12487                newCorrections[nextMeasure] = cumulativeNumberShift
12488            else:  # integer implies deletion
12489                cumulativeNumberShift -= 1
12490                oldCorrections[measureNumber + 1] = cumulativeNumberShift
12491                newCorrections[measureNumber + 1] = cumulativeNumberShift
12492
12493        # Second, make corrections based on the dictionaries. The key is the measure number
12494        # above which measures should be shifted by the value up to the next key. It is easiest
12495        # to do this in reverse order so there is no overlapping.
12496        previousBoundary = None
12497        for k in sorted(oldCorrections, key=lambda x: -1 * x):
12498            shift = oldCorrections[k]
12499            for m in oldMeasures:
12500                if previousBoundary is None or m.number < previousBoundary:
12501                    if m.number >= k:
12502                        m.number = m.number + shift
12503            previousBoundary = k
12504
12505        previousBoundary = None
12506        for k in sorted(newCorrections, key=lambda x: -1 * x):
12507            shift = newCorrections[k]
12508            for m in newMeasures:
12509                if previousBoundary is None or m.number < previousBoundary:
12510                    if m.number >= k:
12511                        m.number = m.number + shift
12512            previousBoundary = k
12513
12514    def showVariantAsOssialikePart(self, containedPart, variantGroups, *, inPlace=False):
12515        # noinspection PyShadowingNames
12516        '''
12517        Takes a part within the score and a list of variant groups within that part.
12518        Puts the variant object
12519        in a part surrounded by hidden rests to mimic the appearance of an ossia despite limited
12520        musicXML support for ossia staves. Note that this will ignore variants with .lengthType
12521        'elongation' and 'deletion' as there is no good way to represent ossia staves like those
12522        by this method.
12523
12524        >>> sPartStr = 'd4 e4 f4 g4   a2 b-4 a4    g4 a8 g8 f4 e4    d2 a2 '
12525        >>> v1Str =    '              a2. b-8 a8 '
12526        >>> v2Str =    '                                             d4 f4 a2 '
12527
12528        >>> sPartStr += "d4 e4 f4 g4    a2 b-4 a4    g4 a8 b-8 c'4 c4    f1"
12529
12530        >>> sPartStream = converter.parse('tinynotation: 4/4 ' + sPartStr)
12531        >>> sPartStream.makeMeasures(inPlace=True)  # maybe not necessary?
12532        >>> v1stream = converter.parse('tinynotation: 4/4 ' + v1Str)
12533        >>> v2stream = converter.parse('tinynotation: 4/4 ' + v2Str)
12534
12535        >>> v1 = variant.Variant()
12536        >>> v1measure = stream.Measure()
12537        >>> v1.insert(0.0, v1measure)
12538        >>> for e in v1stream.notesAndRests:
12539        ...    v1measure.insert(e.offset, e)
12540
12541        >>> v2 = variant.Variant()
12542        >>> v2measure = stream.Measure()
12543        >>> v2.insert(0.0, v2measure)
12544        >>> for e in v2stream.notesAndRests:
12545        ...    v2measure.insert(e.offset, e)
12546
12547        >>> v3 = variant.Variant()
12548        >>> v2.replacementDuration = 4.0
12549        >>> v3.replacementDuration = 4.0
12550        >>> v1.groups = ['variant1']
12551        >>> v2.groups = ['variant2']
12552        >>> v3.groups = ['variant3']
12553
12554        >>> sPart = stream.Part()
12555        >>> for e in sPartStream:
12556        ...    sPart.insert(e.offset, e)
12557
12558        >>> sPart.insert(4.0, v1)
12559        >>> sPart.insert(12.0, v2)
12560        >>> sPart.insert(20.0, v3)  # This is a deletion variant and will be skipped
12561        >>> s = stream.Score()
12562        >>> s.insert(0.0, sPart)
12563        >>> streamWithOssia = s.showVariantAsOssialikePart(sPart,
12564        ...          ['variant1', 'variant2', 'variant3'], inPlace=False)
12565        >>> #_DOCS_SHOW streamWithOssia.show()
12566
12567        '''
12568        from music21 import variant
12569
12570        # containedPart must be in self, or an exception is raised.
12571        if not (containedPart in self):
12572            raise variant.VariantException(f'Could not find {containedPart} in {self}')
12573
12574        if inPlace is True:
12575            returnObj = self
12576            returnPart = containedPart
12577        else:
12578            returnObj = self.coreCopyAsDerivation('showVariantAsOssialikePart')
12579            containedPartIndex = self.parts.stream().index(containedPart)
12580            returnPart = returnObj.parts[containedPartIndex]
12581
12582        # First build a new part object that is the same length as returnPart
12583        # but entirely hidden rests.
12584        # This is done by copying the part and removing unnecessary objects
12585        # including irrelevant variants
12586        # but saving relevant variants.
12587        for variantGroup in variantGroups:
12588            newPart = copy.deepcopy(returnPart)
12589            expressedVariantsExist = False
12590            for e in newPart.elements:
12591                eClasses = e.classes
12592                if 'Variant' in eClasses:
12593                    elementGroups = e.groups
12594                    if (not(variantGroup in elementGroups)
12595                            or e.lengthType in ['elongation', 'deletion']):
12596                        newPart.remove(e)
12597                    else:
12598                        expressedVariantsExist = True
12599                elif 'GeneralNote' in eClasses:
12600                    nQuarterLength = e.duration.quarterLength
12601                    nOffset = e.getOffsetBySite(newPart)
12602                    newPart.remove(e)
12603                    r = note.Rest()
12604                    r.style.hideObjectOnPrint = True
12605                    r.duration.quarterLength = nQuarterLength
12606                    newPart.insert(nOffset, r)
12607                elif 'Measure' in eClasses:  # Recurse if measure
12608                    measureDuration = e.duration.quarterLength
12609                    for n in e.notesAndRests:
12610                        e.remove(n)
12611                    r = note.Rest()
12612                    r.duration.quarterLength = measureDuration
12613                    r.style.hideObjectOnPrint = True
12614                    e.insert(0.0, r)
12615
12616                e.style.hideObjectOnPrint = True
12617
12618            newPart.activateVariants(variantGroup, inPlace=True, matchBySpan=True)
12619            if expressedVariantsExist:
12620                returnObj.insert(0.0, newPart)
12621
12622        if inPlace:
12623            return
12624        else:
12625            return returnObj
12626
12627    def makeVariantBlocks(self):
12628        '''
12629        from music21 import *
12630        '''
12631        variantsToBeDone = self.variants.elements
12632
12633        for v in variantsToBeDone:
12634            startOffset = self.elementOffset(v)
12635            endOffset = v.replacementDuration + startOffset
12636            conflictingVariants = self.getElementsByOffset(offsetStart=startOffset,
12637                                                           offsetEnd=endOffset,
12638                                                           includeEndBoundary=False,
12639                                                           mustFinishInSpan=False,
12640                                                           mustBeginInSpan=True,
12641                                                           classList=['Variant'])
12642            for cV in conflictingVariants:
12643                oldReplacementDuration = cV.replacementDuration
12644                if self.elementOffset(cV) == startOffset:
12645                    continue  # do nothing
12646                else:
12647                    shiftOffset = self.elementOffset(cV) - startOffset
12648                    r = note.Rest()
12649                    r.duration.quarterLength = shiftOffset
12650                    r.style.hideObjectOnPrint = True
12651                    for el in cV._stream:
12652                        oldOffset = el.getOffsetBySite(cV._stream)
12653                        cV._stream.coreSetElementOffset(el, oldOffset + shiftOffset)
12654                    cV.coreElementsChanged()
12655                    cV.insert(0.0, r)
12656                    cV.replacementDuration = oldReplacementDuration
12657                    self.remove(cV)
12658                    self.insert(startOffset, cV)
12659                    variantsToBeDone.append(cV)
12660
12661
12662# -----------------------------------------------------------------------------
12663
12664
12665class Voice(Stream):
12666    '''
12667    A Stream subclass for declaring that all the music in the
12668    stream belongs to a certain "voice" for analysis or display
12669    purposes.
12670
12671    Note that both Finale's Layers and Voices as concepts are
12672    considered Voices here.
12673
12674    Voices have a sort order of 1 greater than time signatures
12675    '''
12676    recursionType = 'elementsFirst'
12677    classSortOrder = 5
12678
12679
12680# -----------------------------------------------------------------------------
12681
12682
12683class Measure(Stream):
12684    '''
12685    A representation of a Measure organized as a Stream.
12686
12687    All properties of a Measure that are Music21 objects are found as part of
12688    the Stream's elements.
12689
12690    Measure number can be explicitly set with the `number` keyword:
12691
12692    >>> m4 = stream.Measure(number=4)
12693    >>> m4
12694    <music21.stream.Measure 4 offset=0.0>
12695    >>> m4.number
12696    4
12697
12698    If passed a single integer as an argument, assumes that this int
12699    is the measure number.
12700
12701    >>> m5 = stream.Measure(5)
12702    >>> m5
12703    <music21.stream.Measure 5 offset=0.0>
12704
12705    Number can also be a string if there is a suffix:
12706
12707    >>> m4 = stream.Measure(number='4a')
12708    >>> m4
12709    <music21.stream.Measure 4a offset=0.0>
12710    >>> m4.numberSuffix
12711    'a'
12712
12713    Though they have all the features of general streams,
12714    Measures have specific attributes that allow for setting their number
12715    and numberSuffix, keep track of whether they have a different clef or
12716    key or timeSignature than previous measures, allow for padding (and pickups),
12717    and can be found as a "measure slice" within a score and parts.
12718    '''
12719    recursionType = 'elementsFirst'
12720    isMeasure = True
12721
12722    # define order to present names in documentation; use strings
12723    _DOC_ORDER = ['']
12724    # documentation for all attributes (not properties or methods)
12725    _DOC_ATTR = {
12726        'timeSignatureIsNew': '''
12727            Boolean describing if the TimeSignature
12728            is different than the previous Measure.''',
12729        'clefIsNew': 'Boolean describing if the Clef is different than the previous Measure.',
12730        'keyIsNew': 'Boolean describing if KeySignature is different than the previous Measure.',
12731        'number': '''
12732            A number representing the displayed or shown
12733            Measure number as presented in a written Score.''',
12734        'numberSuffix': '''
12735            If a Measure number has a string annotation, such as "a" or similar,
12736            this string is stored here. Note that in MusicXML, such
12737            suffixes often appear as
12738            prefixes to measure numbers.  In music21 (like most measure
12739            numbering systems), these
12740            numbers appear as suffixes.''',
12741        'layoutWidth': '''
12742            A suggestion for layout width, though most rendering systems do not support
12743            this designation. Use :class:`~music21.layout.SystemLayout`
12744            objects instead.''',
12745        'paddingLeft': '''
12746            defines empty space at the front of the measure for purposes of determining
12747            beat, etc for pickup/anacrusis bars.  In 4/4, a
12748            measure with a one-beat pickup
12749            note will have a `paddingLeft` of 3.0.
12750            (The name comes from the CSS graphical term
12751            for the amount of padding on the left side of a region.)''',
12752        'paddingRight': '''
12753            defines empty space at the end of the measure for purposes of determining
12754            whether or not a measure is filled.
12755            In 4/4, a piece beginning a one-beat pickup
12756            note will often have a final measure of three beats, instead of four.
12757            The final
12758            measure should have a `paddingRight` of 1.0.
12759            (The name comes from the CSS graphical term
12760            for the amount of padding on the right side of a region.)''',
12761    }
12762
12763    def __init__(self, *args, **keywords):
12764        if len(args) == 1 and isinstance(args[0], int) and 'number' not in keywords:
12765            keywords['number'] = args[0]
12766            args = ()
12767
12768        super().__init__(*args, **keywords)
12769
12770        # clef and timeSignature is defined as a property below
12771        self.timeSignatureIsNew = False
12772        self.clefIsNew = False
12773        self.keyIsNew = False
12774
12775        self.filled = False
12776
12777        # padding: defining a context for offsets contained within this Measure
12778        # padding defines dead regions of offsets into the measure
12779        # the paddingLeft is used by TimeSignature objects to determine beat
12780        # position; paddingRight defines a QL from the end of the time signature
12781        # to the last valid offset
12782        # paddingLeft is used to define pickup/anacrusis bars
12783        self.paddingLeft = 0
12784        self.paddingRight = 0
12785
12786        self.numberSuffix = None  # for measure 14a would be 'a'
12787        if 'number' in keywords:
12788            num = keywords['number']
12789            if isinstance(num, str):
12790                realNum, suffix = common.getNumFromStr(num)
12791                self.number = int(realNum)
12792                if suffix:
12793                    self.numberSuffix = suffix
12794            else:
12795                self.number = keywords['number']
12796        else:
12797            self.number = 0  # 0 means undefined or pickup
12798        # we can request layout width, using the same units used
12799        # in layout.py for systems; most musicxml readers do not support this
12800        # on input
12801        self.layoutWidth = None
12802
12803    # def addRepeat(self):
12804    #     # TODO: write
12805    #     pass
12806
12807    # def addTimeDependentDirection(self, time, direction):
12808    #     # TODO: write
12809    #     pass
12810
12811    def measureNumberWithSuffix(self):
12812        '''
12813        Return the measure `.number` with the `.numberSuffix` as a string.
12814
12815        >>> m = stream.Measure()
12816        >>> m.number = 4
12817        >>> m.numberSuffix = 'A'
12818        >>> m.measureNumberWithSuffix()
12819        '4A'
12820
12821        Test that it works as musicxml
12822
12823        >>> xml = musicxml.m21ToXml.GeneralObjectExporter().parse(m)
12824        >>> print(xml.decode('utf-8'))
12825        <?xml version="1.0"...?>
12826        ...
12827        <part id="...">
12828            <!--========================= Measure 4 ==========================-->
12829            <measure number="4A">
12830        ...
12831
12832        Test round tripping:
12833
12834        >>> s2 = converter.parseData(xml)
12835        >>> print(s2[stream.Measure].first().measureNumberWithSuffix())
12836        4A
12837
12838        Note that we use print here because in parsing the data will become a unicode string.
12839        '''
12840        if self.numberSuffix:
12841            return str(self.number) + self.numberSuffix
12842        else:
12843            return str(self.number)
12844
12845    def _reprInternal(self):
12846        return self.measureNumberWithSuffix() + f' offset={self.offset}'
12847
12848    # -------------------------------------------------------------------------
12849    def mergeAttributes(self, other):
12850        '''
12851        Given another Measure, configure all non-element attributes of this
12852        Measure with the attributes of the other Measure. No elements
12853        will be changed or copied.
12854
12855        This method is necessary because Measures, unlike some Streams,
12856        have attributes independent of any stored elements.
12857
12858        Overrides base.Music21Object.mergeAttributes
12859
12860        >>> m1 = stream.Measure()
12861        >>> m1.id = 'MyMeasure'
12862        >>> m1.clefIsNew = True
12863        >>> m1.number = 2
12864        >>> m1.numberSuffix = 'b'
12865        >>> m1.layoutWidth = 200
12866
12867        >>> m2 = stream.Measure()
12868        >>> m2.mergeAttributes(m1)
12869        >>> m2.layoutWidth
12870        200
12871        >>> m2.id
12872        'MyMeasure'
12873        >>> m2
12874        <music21.stream.Measure 2b offset=0.0>
12875
12876        Try with not another Measure...
12877
12878        >>> m3 = stream.Stream()
12879        >>> m3.id = 'hello'
12880        >>> m2.mergeAttributes(m3)
12881        >>> m2.id
12882        'hello'
12883        >>> m2.layoutWidth
12884        200
12885        '''
12886        # calling bass class sets id, groups
12887        super().mergeAttributes(other)
12888
12889        for attr in ('timeSignatureIsNew', 'clefIsNew', 'keyIsNew', 'filled',
12890                     'paddingLeft', 'paddingRight', 'number', 'numberSuffix', 'layoutWidth'):
12891            if hasattr(other, attr):
12892                setattr(self, attr, getattr(other, attr))
12893
12894    # -------------------------------------------------------------------------
12895    def makeNotation(self,
12896                     inPlace=False,
12897                     **subroutineKeywords):
12898        # noinspection PyShadowingNames
12899        '''
12900        This method calls a sequence of Stream methods on this
12901        :class:`~music21.stream.Measure` to prepare notation.
12902
12903        If `inPlace` is True, this is done in-place; if
12904        `inPlace` is False, this returns a modified deep copy.
12905
12906        >>> m = stream.Measure()
12907        >>> n1 = note.Note('g#')
12908        >>> n2 = note.Note('g')
12909        >>> m.append([n1, n2])
12910        >>> m.makeNotation(inPlace=True)
12911        >>> m.notes[1].pitch.accidental
12912        <music21.pitch.Accidental natural>
12913        '''
12914        # environLocal.printDebug(['Measure.makeNotation'])
12915        # TODO: this probably needs to look to see what processes need to be done;
12916        # for example, existing beaming may be destroyed.
12917
12918        # do this before deepcopy...
12919
12920        # assuming we are not trying to get context of previous measure
12921        if not inPlace:  # make a copy
12922            m = copy.deepcopy(self)
12923        else:
12924            m = self
12925
12926        srkCopy = subroutineKeywords.copy()
12927
12928        for illegalKey in ('meterStream', 'refStreamOrTimeRange', 'bestClef'):
12929            if illegalKey in srkCopy:
12930                del(srkCopy[illegalKey])
12931
12932        m.makeAccidentals(searchKeySignatureByContext=True, inPlace=True, **srkCopy)
12933        # makeTies is for cross-bar associations, and cannot be used
12934        # at just the measure level
12935        # m.makeTies(meterStream, inPlace=True)
12936
12937        # must have a time signature before calling make beams
12938        if m.timeSignature is None:
12939            # get a time signature if not defined, searching the context if
12940            # necessary
12941            contextMeters = m.getTimeSignatures(searchContext=True,
12942                                                returnDefault=False)
12943            defaultMeters = m.getTimeSignatures(searchContext=False,
12944                                                returnDefault=True)
12945            if contextMeters:
12946                ts = contextMeters[0]
12947            else:
12948                try:
12949                    ts = self.bestTimeSignature()
12950                except (StreamException, meter.MeterException):
12951                    # there must be one here
12952                    ts = defaultMeters[0]
12953            m.timeSignature = ts  # a Stream; get the first element
12954
12955        # environLocal.printDebug(['have time signature', m.timeSignature])
12956        if not m.streamStatus.beams:
12957            m.makeBeams(inPlace=True)
12958        if not m.streamStatus.tuplets:
12959            makeNotation.makeTupletBrackets(m, inPlace=True)
12960
12961        if not inPlace:
12962            return m
12963        else:
12964            return None
12965
12966    def barDurationProportion(self, barDuration=None):
12967        '''
12968        Return a floating point value greater than 0 showing the proportion
12969        of the bar duration that is filled based on the highest time of
12970        all elements. 0.0 is empty, 1.0 is filled; 1.5 specifies of an
12971        overflow of half.
12972
12973        Bar duration refers to the duration of the Measure as suggested by
12974        the `TimeSignature`. This value cannot be determined without a `TimeSignature`.
12975
12976        An already-obtained Duration object can be supplied with the `barDuration`
12977        optional argument.
12978
12979        >>> import copy
12980        >>> m = stream.Measure()
12981        >>> m.timeSignature = meter.TimeSignature('3/4')
12982        >>> n = note.Note()
12983        >>> n.quarterLength = 1
12984        >>> m.append(copy.deepcopy(n))
12985        >>> m.barDurationProportion()
12986        Fraction(1, 3)
12987        >>> m.append(copy.deepcopy(n))
12988        >>> m.barDurationProportion()
12989        Fraction(2, 3)
12990        >>> m.append(copy.deepcopy(n))
12991        >>> m.barDurationProportion()
12992        1.0
12993        >>> m.append(copy.deepcopy(n))
12994        >>> m.barDurationProportion()
12995        Fraction(4, 3)
12996        '''
12997        # passing a barDuration may save time in the lookup process
12998        if barDuration is None:
12999            barDuration = self.barDuration
13000        return opFrac(self.highestTime / barDuration.quarterLength)
13001
13002    def padAsAnacrusis(self, useGaps=True, useInitialRests=False):
13003        '''
13004        Given an incompletely filled Measure, adjust the `paddingLeft` value to to
13005        represent contained events as shifted to fill the right-most duration of the bar.
13006
13007        Calling this method will overwrite any previously set `paddingLeft` value,
13008        based on the current TimeSignature-derived `barDuration` attribute.
13009
13010        >>> m = stream.Measure()
13011        >>> m.timeSignature = meter.TimeSignature('3/4')
13012        >>> n = note.Note()
13013        >>> n.quarterLength = 1.0
13014        >>> m.append(n)
13015        >>> m.padAsAnacrusis()
13016        >>> m.paddingLeft
13017        2.0
13018
13019        >>> m.timeSignature = meter.TimeSignature('5/4')
13020        >>> m.padAsAnacrusis()
13021        >>> m.paddingLeft
13022        4.0
13023
13024
13025        Empty space at the beginning of the measure will not be taken in account:
13026
13027        >>> m = stream.Measure()
13028        >>> m.timeSignature = meter.TimeSignature('3/4')
13029        >>> n = note.Note(type='quarter')
13030        >>> m.insert(2.0, n)
13031        >>> m.padAsAnacrusis()
13032        >>> m.paddingLeft
13033        0
13034
13035        If useInitialRests is True, then rests at the beginning of the measure
13036        are removed.  This is especially useful for formats that don't give a
13037        way to specify a pickup measure (such as tinynotation) or software
13038        that generates incorrect opening measures.  So, to fix the problem before,
13039        put a rest at the beginning and call useInitialRests:
13040
13041        >>> r = note.Rest(type='half')
13042        >>> m.insert(0, r)
13043        >>> m.padAsAnacrusis(useInitialRests=True)
13044        >>> m.paddingLeft
13045        2.0
13046
13047        And the rest is gone!
13048
13049        >>> m.show('text')
13050        {0.0} <music21.meter.TimeSignature 3/4>
13051        {0.0} <music21.note.Note C>
13052
13053
13054        Only initial rests count for useInitialRests:
13055
13056        >>> m = stream.Measure()
13057        >>> m.timeSignature = meter.TimeSignature('3/4')
13058        >>> m.append(note.Rest(type='eighth'))
13059        >>> m.append(note.Rest(type='eighth'))
13060        >>> m.append(note.Note('C4', type='quarter'))
13061        >>> m.append(note.Rest(type='eighth'))
13062        >>> m.append(note.Note('D4', type='eighth'))
13063        >>> m.show('text')
13064        {0.0} <music21.meter.TimeSignature 3/4>
13065        {0.0} <music21.note.Rest eighth>
13066        {0.5} <music21.note.Rest eighth>
13067        {1.0} <music21.note.Note C>
13068        {2.0} <music21.note.Rest eighth>
13069        {2.5} <music21.note.Note D>
13070        >>> m.padAsAnacrusis(useInitialRests=True)
13071        >>> m.paddingLeft
13072        1.0
13073        >>> m.show('text')
13074        {0.0} <music21.meter.TimeSignature 3/4>
13075        {0.0} <music21.note.Note C>
13076        {1.0} <music21.note.Rest eighth>
13077        {1.5} <music21.note.Note D>
13078        '''
13079        if useInitialRests:
13080            removeList = []
13081            for gn in self.getElementsByClass('GeneralNote'):
13082                if not isinstance(gn, note.Rest):
13083                    break
13084                removeList.append(gn)
13085            if removeList:
13086                self.remove(removeList, shiftOffsets=True)
13087
13088        # note: may need to set paddingLeft to 0 before examining
13089
13090        # bar duration is that suggested by time signature; it may
13091        # may not be the same as Stream duration, which is based on contents
13092        barDuration = self.barDuration
13093        proportion = self.barDurationProportion(barDuration=barDuration)
13094        if proportion < 1:
13095            # get 1 complement
13096            proportionShift = 1 - proportion
13097            self.paddingLeft = opFrac(barDuration.quarterLength * proportionShift)
13098
13099            # shift = barDuration.quarterLength * proportionShift
13100            # environLocal.printDebug(['got anacrusis shift:', shift,
13101            #                    barDuration.quarterLength, proportion])
13102            # this will shift all elements
13103            # self.shiftElements(shift, classFilterList=(note.GeneralNote,))
13104        else:
13105            pass
13106            # environLocal.printDebug(['padAsAnacrusis() called; however,
13107            # no anacrusis shift necessary:', barDuration.quarterLength, proportion])
13108
13109    # --------------------------------------------------------------------------
13110    @property
13111    def barDuration(self):
13112        '''
13113        Return the bar duration, or the Duration specified by the TimeSignature,
13114        regardless of what elements are found in this Measure or the highest time.
13115        TimeSignature is found first within the Measure,
13116        or within a context based search.
13117
13118        To get the duration of the total length of elements, just use the
13119        `.duration` property.
13120
13121        Here we create a 3/4 measure and "over-stuff" it with five quarter notes.
13122        `barDuration` still gives a duration of 3.0, or a dotted quarter note,
13123        while `.duration` gives a whole note tied to a quarter.
13124
13125
13126        >>> m = stream.Measure()
13127        >>> m.timeSignature = meter.TimeSignature('3/4')
13128        >>> m.barDuration
13129        <music21.duration.Duration 3.0>
13130        >>> m.repeatAppend(note.Note(type='quarter'), 5)
13131        >>> m.barDuration
13132        <music21.duration.Duration 3.0>
13133        >>> m.duration
13134        <music21.duration.Duration 5.0>
13135
13136        The objects returned by `barDuration` and `duration` are
13137        full :class:`~music21.duration.Duration`
13138        objects, will all the relevant properties:
13139
13140        >>> m.barDuration.fullName
13141        'Dotted Half'
13142        >>> m.duration.fullName
13143        'Whole tied to Quarter (5 total QL)'
13144        '''
13145        # TODO: it is possible that this should be cached or exposed as a method
13146        #     as this search may take some time.
13147        if self.timeSignature is not None:
13148            ts = self.timeSignature
13149        else:  # do a context-based search
13150            tsStream = self.getTimeSignatures(searchContext=True,
13151                                              returnDefault=False,
13152                                              sortByCreationTime=True)
13153            if not tsStream:
13154                try:
13155                    ts = self.bestTimeSignature()
13156                except exceptions21.Music21Exception:
13157                    return duration.Duration(self.highestTime)
13158
13159                # raise StreamException(
13160                #   'cannot determine bar duration without a time signature reference')
13161            else:  # it is the first found
13162                ts = tsStream[0]
13163        return ts.barDuration
13164
13165    # --------------------------------------------------------------------------
13166    # Music21Objects are stored in the Stream's elements list
13167    # properties are provided to store and access these attribute
13168
13169    def bestTimeSignature(self):
13170        '''
13171        Given a Measure with elements in it,
13172        get a TimeSignature that contains all elements.
13173        Calls meter.bestTimeSignature(self)
13174
13175        Note: this does not yet accommodate triplets.
13176
13177
13178        We create a simple stream that should be in 3/4
13179
13180
13181        >>> s = converter.parse('C4 D4 E8 F8', format='tinyNotation', makeNotation=False)
13182        >>> m = stream.Measure()
13183        >>> for el in s:
13184        ...     m.insert(el.offset, el)
13185
13186        But there is no TimeSignature!
13187
13188        >>> m.show('text')
13189        {0.0} <music21.note.Note C>
13190        {1.0} <music21.note.Note D>
13191        {2.0} <music21.note.Note E>
13192        {2.5} <music21.note.Note F>
13193
13194        So, we get the best Time Signature and put it in the Stream.
13195
13196        >>> ts = m.bestTimeSignature()
13197        >>> ts
13198        <music21.meter.TimeSignature 3/4>
13199        >>> m.timeSignature = ts
13200        >>> m.show('text')
13201        {0.0} <music21.meter.TimeSignature 3/4>
13202        {0.0} <music21.note.Note C>
13203        {1.0} <music21.note.Note D>
13204        {2.0} <music21.note.Note E>
13205        {2.5} <music21.note.Note F>
13206
13207        For further details about complex time signatures, etc.
13208        see `meter.bestTimeSignature()`
13209
13210        '''
13211        return meter.bestTimeSignature(self)
13212
13213    def _getLeftBarline(self):
13214        barList = []
13215        # directly access _elements, as do not want to get any bars
13216        # in _endElements
13217        for e in self._elements:
13218            if isinstance(e, bar.Barline):  # take the first
13219                if self.elementOffset(e) == 0.0:
13220                    barList.append(e)
13221                    break
13222        if not barList:
13223            return None
13224        else:
13225            return barList[0]
13226
13227    def _setLeftBarline(self, barlineObj):
13228        insert = True
13229        if isinstance(barlineObj, str):
13230            barlineObj = bar.Barline(barlineObj)
13231            barlineObj.location = 'left'
13232        elif barlineObj is None:  # assume removal
13233            insert = False
13234        else:  # assume an Barline object
13235            barlineObj.location = 'left'
13236
13237        oldLeftBarline = self._getLeftBarline()
13238        if oldLeftBarline is not None:
13239            # environLocal.printDebug(['_setLeftBarline()', 'removing left barline'])
13240            junk = self.pop(self.index(oldLeftBarline))
13241        if insert:
13242            # environLocal.printDebug(['_setLeftBarline()',
13243            # 'inserting new left barline', barlineObj])
13244            self.insert(0, barlineObj)
13245
13246    leftBarline = property(_getLeftBarline,
13247                           _setLeftBarline,
13248                           doc='''
13249        Get or set the left barline, or the Barline object
13250        found at offset zero of the Measure.  Can be set either with a string
13251        representing barline style or a bar.Barline() object or None.
13252        Note that not all bars have
13253        barline objects here -- regular barlines don't need them.
13254        ''')
13255
13256    def _getRightBarline(self):
13257        # TODO: Move to Stream or make setting .rightBarline, etc. on Stream raise an exception...
13258        # look on _endElements
13259        barList = []
13260        for e in self._endElements:
13261            if isinstance(e, bar.Barline):  # take the first
13262                barList.append(e)
13263                break
13264        # barList = self.getElementsByClass(bar.Barline)
13265        if not barList:  # do this before searching for barQL
13266            return None
13267        else:
13268            return barList[0]
13269
13270    def _setRightBarline(self, barlineObj):
13271        insert = True
13272        if isinstance(barlineObj, str):
13273            barlineObj = bar.Barline(barlineObj)
13274            barlineObj.location = 'right'
13275        elif barlineObj is None:  # assume removal
13276            insert = False
13277        else:  # assume an Barline object
13278            barlineObj.location = 'right'
13279
13280        # if a repeat, setup direction if not assigned
13281        if barlineObj is not None and isinstance(barlineObj, bar.Repeat):
13282            # environLocal.printDebug(['got barline obj w/ direction', barlineObj.direction])
13283            if barlineObj.direction in ['start', None]:
13284                barlineObj.direction = 'end'
13285        oldRightBarline = self._getRightBarline()
13286
13287        if oldRightBarline is not None:
13288            # environLocal.printDebug(['_setRightBarline()', 'removing right barline'])
13289            junk = self.pop(self.index(oldRightBarline))
13290        # insert into _endElements
13291        if insert:
13292            self.storeAtEnd(barlineObj)
13293
13294        # environLocal.printDebug(['post _setRightBarline', barlineObj,
13295        #    'len of elements highest', len(self._endElements)])
13296
13297    rightBarline = property(_getRightBarline,
13298                            _setRightBarline,
13299                            doc='''
13300        Get or set the right barline, or the Barline object
13301        found at the offset equal to the bar duration.
13302
13303        >>> b = bar.Barline('final')
13304        >>> m = stream.Measure()
13305        >>> print(m.rightBarline)
13306        None
13307        >>> m.rightBarline = b
13308        >>> m.rightBarline.type
13309        'final'
13310
13311
13312        A string can also be used instead:
13313
13314        >>> c = converter.parse('tinynotation: 3/8 C8 D E F G A B4.')
13315        >>> c.measure(1).rightBarline = 'light-light'
13316        >>> c.measure(3).rightBarline = 'light-heavy'
13317        >>> #_DOCS_SHOW c.show()
13318
13319        .. image:: images/stream_barline_demo.*
13320            :width: 211
13321
13322        OMIT_FROM_DOCS
13323
13324        .measure currently isn't the same as the
13325        original measure...
13326
13327        ''')
13328
13329
13330class Part(Stream):
13331    '''
13332    A Stream subclass for designating music that is considered a single part.
13333
13334    When put into a Score object, Part objects are all collected in the `Score.parts`
13335    call.  Otherwise they mostly work like generic Streams.
13336
13337    Generally the hierarchy goes: Score > Part > Measure > Voice, but you are not
13338    required to stick to this.
13339
13340    Part groupings (piano braces, etc.) are found in the :ref:`moduleLayout` module
13341    in the :class:`~music21.layout.StaffGroup` Spanner object.
13342
13343    OMIT_FROM_DOCS
13344    Check that this is True and works for everything before suggesting that it works!
13345
13346    May be enclosed in a staff (for instance, 2nd and 3rd trombone
13347    on a single staff), may enclose staves (piano treble and piano bass),
13348    or may not enclose or be enclosed by a staff (in which case, it
13349    assumes that this part fits on one staff and shares it with no other
13350    part
13351    '''
13352    recursionType = 'flatten'
13353
13354    # _DOC_ATTR = {
13355    # }
13356
13357    def __init__(self, *args, **keywords):
13358        super().__init__(*args, **keywords)
13359        self._partName = None
13360        self._partAbbreviation = None
13361
13362    def _getPartName(self):
13363        if self._partName is not None:
13364            return self._partName
13365        elif '_partName' in self._cache:
13366            return self._cache['_partName']
13367        else:
13368            pn = None
13369            for e in self.recurse().getElementsByClass('Instrument'):
13370                pn = e.partName
13371                if pn is None:
13372                    pn = e.instrumentName
13373                if pn is not None:
13374                    break
13375            self._cache['_partName'] = pn
13376            return pn
13377
13378    def _setPartName(self, newName):
13379        self._partName = newName
13380
13381    partName = property(_getPartName, _setPartName, doc='''
13382        Gets or sets a string representing the name of this part
13383        as a whole (not counting instrument changes, etc.).
13384
13385        It can be set explicitly (or set on parsing) or it
13386        can take its name from the first :class:`~music21.instrument.Instrument` object
13387        encountered in the stream (or within a substream),
13388        first checking its .partName, then checking its .instrumentName
13389
13390        Can also return None.
13391
13392        >>> p = stream.Part()
13393        >>> p.partName is None
13394        True
13395        >>> cl = instrument.Clarinet()
13396        >>> p.insert(0, cl)
13397        >>> p.partName
13398        'Clarinet'
13399        >>> p.remove(cl)
13400        >>> p.partName is None
13401        True
13402        >>> p.insert(0, instrument.Flute())
13403        >>> p.partName
13404        'Flute'
13405        >>> p.partName = 'Reed 1'
13406        >>> p.partName
13407        'Reed 1'
13408
13409        Note that changing an instrument's .partName or .instrumentName while it
13410        is already in the Stream will not automatically update this unless
13411        .coreElementsChanged() is called or this Stream's elements are otherwise altered.
13412        This is because the value is cached so that O(n) searches through the Stream
13413        do not need to be done every time.
13414    ''')
13415
13416    def _getPartAbbreviation(self):
13417        if self._partAbbreviation is not None:
13418            return self._partAbbreviation
13419        elif '_partAbbreviation' in self._cache:
13420            return self._cache['_partAbbreviation']
13421        else:
13422            pn = None
13423            for e in self.recurse().getElementsByClass('Instrument'):
13424                pn = e.partAbbreviation
13425                if pn is None:
13426                    pn = e.instrumentAbbreviation
13427                if pn is not None:
13428                    break
13429            self._cache['_partAbbreviation'] = pn
13430            return pn
13431
13432    def _setPartAbbreviation(self, newName):
13433        self._partAbbreviation = newName
13434
13435    partAbbreviation = property(_getPartAbbreviation, _setPartAbbreviation, doc='''
13436        Gets or sets a string representing the abbreviated name of this part
13437        as a whole (not counting instrument changes, etc.).
13438
13439        It can be set explicitly (or set on parsing) or it
13440        can take its name from the first :class:`~music21.instrument.Instrument` object
13441        encountered in the stream (or within a substream),
13442        first checking its .partAbbreviation, then checking its .instrumentAbbreviation
13443
13444        Can also return None.
13445
13446        >>> p = stream.Part()
13447        >>> p.partAbbreviation is None
13448        True
13449        >>> cl = instrument.Clarinet()
13450        >>> p.insert(0, cl)
13451        >>> p.partAbbreviation
13452        'Cl'
13453        >>> p.remove(cl)
13454        >>> p.partAbbreviation is None
13455        True
13456        >>> p.insert(0, instrument.Flute())
13457        >>> p.partAbbreviation
13458        'Fl'
13459        >>> p.partAbbreviation = 'Rd 1'
13460        >>> p.partAbbreviation
13461        'Rd 1'
13462
13463        Note that changing an instrument's .partAbbreviation or .instrumentAbbreviation while it
13464        is already in the Stream will not automatically update this unless
13465        .coreElementsChanged() is called or this Stream's elements are otherwise altered.
13466        This is because the value is cached so that O(n) searches through the Stream
13467        do not need to be done every time.
13468    ''')
13469
13470    def makeAccidentals(
13471        self,
13472        *,
13473        alteredPitches=None,
13474        cautionaryPitchClass=True,
13475        cautionaryAll=False,
13476        inPlace=False,
13477        overrideStatus=False,
13478        cautionaryNotImmediateRepeat=True,
13479        tiePitchSet=None,
13480    ):
13481        '''
13482        This overridden method of Stream.makeAccidentals
13483        provides the management of passing pitches from
13484        a past Measure to each new measure for processing.
13485
13486        Changed in v.7 -- `inPlace` defaults False
13487        '''
13488        if not inPlace:  # make a copy
13489            returnObj = self.coreCopyAsDerivation('makeAccidentals')
13490        else:
13491            returnObj = self
13492        # process make accidentals for each measure
13493        measureStream = returnObj.getElementsByClass('Measure')
13494        makeNotation.makeAccidentalsInMeasureStream(
13495            measureStream,
13496            alteredPitches=alteredPitches,
13497            cautionaryPitchClass=cautionaryPitchClass,
13498            cautionaryAll=cautionaryAll,
13499            overrideStatus=overrideStatus,
13500            cautionaryNotImmediateRepeat=cautionaryNotImmediateRepeat,
13501            tiePitchSet=tiePitchSet,
13502        )
13503        if not inPlace:
13504            return returnObj
13505        else:  # in place
13506            return None
13507
13508
13509class PartStaff(Part):
13510    '''
13511    A Part subclass for designating music that is
13512    represented on a single staff but may only be one
13513    of many staves for a single part.
13514    '''
13515
13516
13517# class Performer(Stream):
13518#     '''
13519#     A Stream subclass for designating music to be performed by a
13520#     single Performer.  Should only be used when a single performer
13521#     performs on multiple parts.  E.g. Bass Drum and Triangle on separate
13522#     staves performed by one player.
13523#
13524#     a Part + changes of Instrument is fine for designating most cases
13525#     where a player changes instrument in a piece.  A part plus staves
13526#     with individual instrument changes could also be a way of designating
13527#     music that is performed by a single performer (see, for instance
13528#     the Piano doubling Celesta part in Lukas Foss's Time Cycle).  The
13529#     Performer Stream-subclass could be useful for analyses of, for instance,
13530#     how 5 percussionists chose to play a piece originally designated for 4
13531#     (or 6) percussionists in the score.
13532#     '''
13533#     # NOTE: not yet implemented
13534#     pass
13535
13536class System(Stream):
13537    '''
13538    Totally optional and used only in OMR and Capella: a designation that all the
13539    music in this Stream belongs in a single system.
13540
13541    The system object has two attributes, systemNumber (which number is it)
13542    and systemNumbering which says at what point the numbering of
13543    systems resets.  It can be either "Score" (default), "Opus", or "Page".
13544    '''
13545    systemNumber = 0
13546    systemNumbering = 'Score'  # or Page; when do system numbers reset?
13547
13548
13549class Score(Stream):
13550    '''
13551    A Stream subclass for handling multi-part music.
13552
13553    Almost totally optional (the largest containing Stream in a piece could be
13554    a generic Stream, or a Part, or a Staff).  And Scores can be
13555    embedded in other Scores (in fact, our original thought was to call
13556    this class a Fragment because of this possibility of continuous
13557    embedding; though it's probably better to embed a Score in an Opus),
13558    but we figure that many people will like calling the largest
13559    container a Score and that this will become a standard.
13560    '''
13561    recursionType = 'elementsOnly'
13562
13563    @property
13564    def parts(self):
13565        '''
13566        Return all :class:`~music21.stream.Part` objects in a :class:`~music21.stream.Score`.
13567
13568        It filters out all other things that might be in a Score object, such as Metadata
13569        returning just the Parts.
13570
13571
13572        >>> s = corpus.parse('bach/bwv66.6')
13573        >>> s.parts
13574        <music21.stream.iterator.StreamIterator for Score:0x104af3a58 @:0>
13575        >>> len(s.parts)
13576        4
13577
13578        OMIT_FROM_DOCS
13579
13580        Ensure that getting from cache still will reset the iteration.
13581
13582        >>> for i, p in enumerate(s.parts):
13583        ...     print(i, p)
13584        ...     break
13585        0 <music21.stream.Part Soprano>
13586
13587        >>> for i, p in enumerate(s.parts):
13588        ...     print(i, p)
13589        ...     break
13590        0 <music21.stream.Part Soprano>
13591        '''
13592        # return self.getElementsByClass('Part')
13593        if 'parts' not in self._cache or self._cache['parts'] is None:
13594            partIterator = self.getElementsByClass('Part')
13595            partIterator.overrideDerivation = 'parts'
13596            self._cache['parts'] = partIterator
13597        return self._cache['parts']
13598
13599    def measures(self,
13600                 numberStart,
13601                 numberEnd,
13602                 collect=('Clef', 'TimeSignature', 'Instrument', 'KeySignature'),
13603                 gatherSpanners=GatherSpanners.ALL,
13604                 indicesNotNumbers=False):
13605        # noinspection PyShadowingNames
13606        '''
13607        This method overrides the :meth:`~music21.stream.Stream.measures`
13608        method on Stream. This creates a new Score stream that has the same measure
13609        range for all Parts.
13610
13611        The `collect` argument is a list of classes that will be collected; see
13612        Stream.measures()
13613
13614        >>> s = corpus.parse('bwv66.6')
13615        >>> post = s.measures(3, 5)  # range is inclusive, i.e., [3, 5]
13616        >>> len(post.parts)
13617        4
13618        >>> len(post.parts[0].getElementsByClass('Measure'))
13619        3
13620        >>> len(post.parts[1].getElementsByClass('Measure'))
13621        3
13622        '''
13623        post = self.__class__()
13624        # this calls on Music21Object, transfers groups, id if not id(self)
13625        post.mergeAttributes(self)
13626        # note that this will strip all objects that are not Parts
13627        for p in self.parts:
13628            # insert all at zero
13629            measuredPart = p.measures(numberStart,
13630                                      numberEnd,
13631                                      collect,
13632                                      gatherSpanners=gatherSpanners,
13633                                      indicesNotNumbers=indicesNotNumbers)
13634            post.insert(0, measuredPart)
13635        # must manually add any spanners; do not need to add .flatten(),
13636        # as Stream.measures will handle lower level
13637        if gatherSpanners:
13638            spStream = self.spanners
13639            for sp in spStream:
13640                post.insert(0, sp)
13641
13642        post.derivation.client = post
13643        post.derivation.origin = self
13644        post.derivation.method = 'measures'
13645        return post
13646
13647    def measure(self,
13648                measureNumber,
13649                collect=('Clef', 'TimeSignature', 'Instrument', 'KeySignature'),
13650                gatherSpanners=True,
13651                indicesNotNumbers=False):
13652        '''
13653        Given a measure number (or measure index, if indicesNotNumbers is True)
13654        return another Score object which contains multiple parts but each of which has only a
13655        single :class:`~music21.stream.Measure` object if the
13656        Measure number exists, otherwise returns a score with parts that are empty.
13657
13658        This method overrides the :meth:`~music21.stream.Stream.measure` method on Stream to
13659        allow for finding a single "measure slice" within parts:
13660
13661
13662        >>> bachIn = corpus.parse('bach/bwv324.xml')
13663        >>> excerpt = bachIn.measure(2)
13664        >>> excerpt
13665        <music21.stream.Score 0x10322b5f8>
13666        >>> len(excerpt.parts)
13667        4
13668        >>> excerpt.parts[0].show('text')
13669        {0.0} <music21.instrument.Instrument 'P1: Soprano: '>
13670        {0.0} <music21.clef.TrebleClef>
13671        {0.0} <music21.key.Key of e minor>
13672        {0.0} <music21.meter.TimeSignature 4/4>
13673        {0.0} <music21.stream.Measure 2 offset=0.0>
13674            {0.0} <music21.note.Note B>
13675            {1.0} <music21.note.Note B>
13676            {2.0} <music21.note.Note B>
13677            {3.0} <music21.note.Note B>
13678
13679        Note that the parts created have all the meta-information outside the measure
13680        unless this information appears in the measure itself at the beginning:
13681
13682        >>> bachIn.measure(1).parts[0].show('text')
13683        {0.0} <music21.instrument.Instrument 'P1: Soprano: '>
13684        {0.0} <music21.stream.Measure 1 offset=0.0>
13685            {0.0} <music21.clef.TrebleClef>
13686            {0.0} <music21.key.Key of e minor>
13687            {0.0} <music21.meter.TimeSignature 4/4>
13688            {0.0} <music21.layout.SystemLayout>
13689            {0.0} <music21.note.Note B>
13690            {2.0} <music21.note.Note D>
13691
13692        This way the original measure objects can be returned without being altered.
13693
13694        The final measure slice of the piece can be obtained with index -1.  Example:
13695        quickly get the last chord of the piece, without needing to run .chordify()
13696        on the whole piece:
13697
13698        >>> excerpt = bachIn.measure(-1)
13699        >>> excerptChords = excerpt.chordify()
13700        >>> excerptChords.show('text')
13701        {0.0} <music21.instrument.Instrument 'P1: Soprano: '>
13702        {0.0} <music21.clef.TrebleClef>
13703        {0.0} <music21.key.Key of e minor>
13704        {0.0} <music21.meter.TimeSignature 4/4>
13705        {0.0} <music21.stream.Measure 9 offset=0.0>
13706            {0.0} <music21.chord.Chord E2 G3 B3 E4>
13707            {4.0} <music21.bar.Barline type=final>
13708
13709        >>> lastChord = excerptChords.recurse().getElementsByClass('Chord').last()
13710        >>> lastChord
13711        <music21.chord.Chord E2 G3 B3 E4>
13712
13713        Note that we still do a .getElementsByClass('Chord') since many pieces end
13714        with nothing but a rest...
13715        '''
13716        if measureNumber < 0:
13717            indicesNotNumbers = True
13718
13719        startMeasureNumber = measureNumber
13720        endMeasureNumber = measureNumber
13721        if indicesNotNumbers:
13722            endMeasureNumber += 1
13723            if startMeasureNumber == -1:
13724                endMeasureNumber = None
13725
13726        post = self.__class__()
13727        # this calls on Music21Object, transfers id, groups
13728        post.mergeAttributes(self)
13729        # note that this will strip all objects that are not Parts
13730        for p in self.getElementsByClass('Part'):
13731            # insert all at zero
13732            mStream = p.measures(startMeasureNumber,
13733                                 endMeasureNumber,
13734                                 collect=collect,
13735                                 gatherSpanners=gatherSpanners,
13736                                 indicesNotNumbers=indicesNotNumbers)
13737            post.insert(0, mStream)
13738
13739        if gatherSpanners:
13740            spStream = self.spanners
13741            for sp in spStream:
13742                post.insert(0, sp)
13743
13744        post.derivation.client = post
13745        post.derivation.origin = self
13746        post.derivation.method = 'measure'
13747
13748        return post
13749
13750    def expandRepeats(self):
13751        '''
13752        Expand all repeats, as well as all repeat indications
13753        given by text expressions such as D.C. al Segno.
13754
13755        This method always returns a new Stream, with deepcopies
13756        of all contained elements at all level.
13757        '''
13758        post = self.cloneEmpty(derivationMethod='expandRepeats')
13759        # this calls on Music21Object, transfers id, groups
13760        post.mergeAttributes(self)
13761
13762        # get all things in the score that are not Parts
13763        for e in self.iter().getElementsNotOfClass('Part'):
13764            eNew = copy.deepcopy(e)  # assume that this is needed
13765            post.insert(self.elementOffset(e), eNew)
13766
13767        for p in self.getElementsByClass('Part'):
13768            # get spanners at highest level, not by Part
13769            post.insert(0, p.expandRepeats(copySpanners=False))
13770
13771        # spannerBundle = spanner.SpannerBundle(list(post.flatten().spanners))
13772        spannerBundle = post.spannerBundle  # use property
13773        # iterate over complete semi flat (need containers); find
13774        # all new/old pairs
13775        for e in post.recurse(includeSelf=False):
13776            # update based on last id, new object
13777            if e.sites.hasSpannerSite():
13778                origin = e.derivation.origin
13779                if origin is not None and e.derivation.method == '__deepcopy__':
13780                    spannerBundle.replaceSpannedElement(origin, e)
13781        return post
13782
13783    def measureOffsetMap(self, classFilterList=None):
13784        '''
13785        This Score method overrides the
13786        :meth:`~music21.stream.Stream.measureOffsetMap` method of Stream.
13787        This creates a map based on all contained Parts in this Score.
13788        Measures found in multiple Parts with the same offset will be
13789        appended to the same list.
13790
13791        If no parts are found in the score, then the normal
13792        :meth:`~music21.stream.Stream.measureOffsetMap` routine is called.
13793
13794        This method is smart and does not assume that all Parts
13795        have measures with identical offsets.
13796        '''
13797        parts = self.iter().parts
13798        if not parts:
13799            return Stream.measureOffsetMap(self, classFilterList)
13800        # else:
13801        offsetMap = {}
13802        for p in parts:
13803            mapPartial = p.measureOffsetMap(classFilterList)
13804            # environLocal.printDebug(['mapPartial', mapPartial])
13805            for k in mapPartial:
13806                if k not in offsetMap:
13807                    offsetMap[k] = []
13808                for m in mapPartial[k]:  # get measures from partial
13809                    if m not in offsetMap[k]:
13810                        offsetMap[k].append(m)
13811        orderedOffsetMap = collections.OrderedDict(sorted(offsetMap.items(), key=lambda o: o[0]))
13812        return orderedOffsetMap
13813
13814    def sliceByGreatestDivisor(self, *, addTies=True, inPlace=False):
13815        '''
13816        Slice all duration of all part by the minimum duration
13817        that can be summed to each concurrent duration.
13818
13819        Overrides method defined on Stream.
13820        '''
13821        if not inPlace:  # make a copy
13822            returnObj = self.coreCopyAsDerivation('sliceByGreatestDivisor')
13823        else:
13824
13825            returnObj = self
13826
13827        # find greatest divisor for each measure at a time
13828        # if no measures this will be zero
13829        mStream = returnObj.parts.first().getElementsByClass('Measure')
13830        mCount = len(mStream)
13831        if mCount == 0:
13832            mCount = 1  # treat as a single measure
13833        for i in range(mCount):  # may be 1
13834            uniqueQuarterLengths = []
13835            for p in returnObj.getElementsByClass('Part'):
13836                if p.hasMeasures():
13837                    m = p.getElementsByClass('Measure')[i]
13838                else:
13839                    m = p  # treat the entire part as one measure
13840
13841                # collect all unique quarter lengths
13842                for e in m.notesAndRests:
13843                    # environLocal.printDebug(['examining e', i, e, e.quarterLength])
13844                    if e.quarterLength not in uniqueQuarterLengths:
13845                        uniqueQuarterLengths.append(e.quarterLength)
13846
13847            # after ql for all parts, find divisor
13848            divisor = common.approximateGCD(uniqueQuarterLengths)
13849            # environLocal.printDebug(['Score.sliceByGreatestDivisor:
13850            # got divisor from unique ql:', divisor, uniqueQuarterLengths])
13851
13852            for p in returnObj.getElementsByClass('Part'):
13853                # in place: already have a copy if nec
13854                # must do on measure at a time
13855                if p.hasMeasures():
13856                    m = p.getElementsByClass('Measure')[i]
13857                else:
13858                    m = p  # treat the entire part as one measure
13859                m.sliceByQuarterLengths(quarterLengthList=[divisor],
13860                                        target=None,
13861                                        addTies=addTies,
13862                                        inPlace=True)
13863        del mStream  # cleanup Streams
13864        returnObj.coreElementsChanged()
13865        if not inPlace:
13866            return returnObj
13867
13868    def partsToVoices(self,
13869                      voiceAllocation: Union[int, List[Union[List, int]]] = 2,
13870                      permitOneVoicePerPart=False,
13871                      setStems=True):
13872        # noinspection PyShadowingNames
13873        '''
13874        Given a multi-part :class:`~music21.stream.Score`,
13875        return a new Score that combines parts into voices.
13876
13877        The `voiceAllocation` parameter sets the maximum number
13878        of voices per Part.
13879
13880        The `permitOneVoicePerPart` parameter, if True, will encode a
13881        single voice inside a single Part, rather than leaving it as
13882        a single Part alone, with no internal voices.
13883
13884
13885        >>> s = corpus.parse('bwv66.6')
13886        >>> len(s.flatten().notes)
13887        165
13888        >>> post = s.partsToVoices(voiceAllocation=4)
13889        >>> len(post.parts)
13890        1
13891        >>> len(post.parts.first().getElementsByClass('Measure').first().voices)
13892        4
13893        >>> len(post.flatten().notes)
13894        165
13895
13896        '''
13897        sub = []
13898        bundle = []
13899        if common.isNum(voiceAllocation):
13900            voicesPerPart = voiceAllocation
13901            for pIndex, p in enumerate(self.parts):
13902                if pIndex % voicesPerPart == 0:
13903                    sub = []
13904                    sub.append(p)
13905                else:
13906                    sub.append(p)
13907                if pIndex % voicesPerPart == voicesPerPart - 1:
13908                    bundle.append(sub)
13909                    sub = []
13910            if sub:  # get last
13911                bundle.append(sub)
13912        # else, assume it is a list of groupings
13913        elif common.isIterable(voiceAllocation):
13914            voiceAllocation: List[Union[List, int]]
13915            for group in voiceAllocation:
13916                sub = []
13917                # if a single entry
13918                if not common.isListLike(group):
13919                    # group is a single index
13920                    sub.append(self.parts[group])
13921                else:
13922                    for partId in group:
13923                        sub.append(self.parts[partId])
13924                bundle.append(sub)
13925        else:
13926            raise StreamException(f'incorrect voiceAllocation format: {voiceAllocation}')
13927
13928        # environLocal.printDebug(['partsToVoices() bundle:', bundle])
13929
13930        s = self.cloneEmpty(derivationMethod='partsToVoices')
13931        s.metadata = self.metadata
13932
13933        for sub in bundle:  # each sub contains parts
13934            if len(sub) == 1 and not permitOneVoicePerPart:
13935                # probably need to create a new part and measure
13936                s.insert(0, sub[0])
13937                continue
13938
13939            pActive = Part()
13940            # iterate through each part
13941            for pIndex, p in enumerate(sub):
13942                # only check for measures once per part
13943                if pActive.hasMeasures():
13944                    hasMeasures = True
13945                else:
13946                    hasMeasures = False
13947
13948                for mIndex, m in enumerate(p.getElementsByClass('Measure')):
13949                    # environLocal.printDebug(['pIndex, p', pIndex, p,
13950                    #     'mIndex, m', mIndex, m, 'hasMeasures', hasMeasures])
13951                    # only create measures if non already exist
13952                    if not hasMeasures:
13953                        # environLocal.printDebug(['creating measure'])
13954                        mActive = Measure()
13955                        # some attributes may be none
13956                        # note: not copying here; and first part read will provide
13957                        # attributes; possible other parts may have other attributes
13958                        mActive.mergeAttributes(m)
13959                        mActive.mergeElements(m, classFilterList=(
13960                            'Barline', 'TimeSignature', 'Clef', 'KeySignature'))
13961
13962                        # if m.timeSignature is not None:
13963                        #     mActive.timeSignature = m.timeSignature
13964                        # if m.keySignature is not None:
13965                        #     mActive.keySignature = m.keySignature
13966                        # if m.clef is not None:
13967                        #     mActive.clef = m.clef
13968                    else:
13969                        mActive = pActive.getElementsByClass('Measure')[mIndex]
13970
13971                    # transfer elements into a voice
13972                    v = Voice()
13973                    v.id = pIndex
13974                    # for now, just take notes, including rests
13975                    for e in m.notesAndRests:  # m.getElementsByClass():
13976                        if setStems and hasattr(e, 'stemDirection'):
13977                            e.stemDirection = 'up' if pIndex % 2 == 0 else 'down'
13978                        v.insert(e.getOffsetBySite(m), e)
13979                    # insert voice in new measure
13980                    # environLocal.printDebug(['inserting voice', v, v.id, 'into measure', mActive])
13981                    mActive.insert(0, v)
13982                    # mActive.show('t')
13983                    # only insert measure if new part does not already have measures
13984                    if not hasMeasures:
13985                        pActive.insert(m.getOffsetBySite(p), mActive)
13986
13987            s.insert(0, pActive)
13988            pActive = None
13989        return s
13990
13991    def implode(self):
13992        '''
13993        Reduce a polyphonic work into two staves.
13994
13995        Currently, this is just a synonym for `partsToVoices` with
13996        `voiceAllocation = 2`, and `permitOneVoicePerPart = False`,
13997        but someday this will have better methods for finding identical
13998        parts, etc.
13999        '''
14000        voiceAllocation = 2
14001        permitOneVoicePerPart = False
14002
14003        return self.partsToVoices(
14004            voiceAllocation=voiceAllocation,
14005            permitOneVoicePerPart=permitOneVoicePerPart
14006        )
14007
14008    def flattenParts(self, classFilterList=('Note', 'Chord')):
14009        # noinspection PyShadowingNames
14010        '''
14011        Given a Score, combine all Parts into a single Part
14012        with all elements found in each Measure of the Score.
14013
14014        The `classFilterList` can be used to specify which objects
14015        contained in Measures are transferred.
14016
14017        It also flattens all voices within a part.
14018
14019        Deprecated in v7.
14020
14021        >>> s = corpus.parse('bwv66.6')
14022        >>> len(s.parts)
14023        4
14024        >>> len(s.flatten().notes)
14025        165
14026        >>> post = s.flattenParts()
14027        >>> isinstance(post, stream.Part)
14028        True
14029        >>> len(post.flatten().notes)
14030        165
14031        '''
14032        post = self.parts.first().template(fillWithRests=False, retainVoices=False)
14033        for i, m in enumerate(post.getElementsByClass('Measure')):
14034            for p in self.parts:
14035                mNew = copy.deepcopy(p.getElementsByClass('Measure')[i]).flatten()
14036                for e in mNew:
14037                    match = False
14038                    for cf in classFilterList:
14039                        if cf in e.classes:
14040                            match = True
14041                            break
14042                    if match:
14043                        m.insert(e.getOffsetBySite(mNew), e)
14044        return post
14045
14046    def makeNotation(self,
14047                     meterStream=None,
14048                     refStreamOrTimeRange=None,
14049                     inPlace=False,
14050                     bestClef=False,
14051                     **subroutineKeywords):
14052        '''
14053        This method overrides the makeNotation method on Stream,
14054        such that a Score object with one or more Parts or Streams
14055        that may not contain well-formed notation may be transformed
14056        and replaced by well-formed notation.
14057
14058        If `inPlace` is True, this is done in-place;
14059        if `inPlace` is False, this returns a modified deep copy.
14060        '''
14061        if inPlace:
14062            returnStream = self
14063        else:
14064            returnStream = self.coreCopyAsDerivation('makeNotation')
14065        returnStream.coreGatherMissingSpanners()  # get spanners needed but not here!
14066
14067        # do not assume that we have parts here
14068        if self.hasPartLikeStreams():
14069            for s in returnStream.getElementsByClass('Stream'):
14070                # process all component Streams inPlace
14071                s.makeNotation(meterStream=meterStream,
14072                               refStreamOrTimeRange=refStreamOrTimeRange,
14073                               inPlace=True,
14074                               bestClef=bestClef,
14075                               **subroutineKeywords)
14076            # note: while the local-streams have updated their caches, the
14077            # containing score has an out-of-date cache of flat.
14078            # thus, must call elements changed
14079            # but... since all we have done in this method is call coreGatherMissingSpanners()
14080            # and makeNotation(), neither of which are supposed to leave the stream
14081            # unusable (with an out-of-date cache), the original issue was likely deeper
14082            # no matter, let's just be extra cautious and run this here (Feb 2021 - JTW)
14083            returnStream.coreElementsChanged()
14084        else:  # call the base method
14085            super(Score, returnStream).makeNotation(meterStream=meterStream,
14086                                                    refStreamOrTimeRange=refStreamOrTimeRange,
14087                                                    inPlace=True,
14088                                                    bestClef=bestClef,
14089                                                    **subroutineKeywords)
14090
14091        if inPlace:
14092            return None
14093        else:
14094            return returnStream
14095
14096
14097class Opus(Stream):
14098    '''
14099    A Stream subclass for handling multi-work music encodings.
14100    Many ABC files, for example, define multiple works or parts within a single file.
14101
14102    Opus objects can contain multiple Score objects, or even other Opus objects!
14103    '''
14104    recursionType = 'elementsOnly'
14105
14106    # TODO: get by title, possibly w/ regex
14107
14108    def getNumbers(self):
14109        '''
14110        Return a list of all numbers defined in this Opus.
14111
14112        >>> o = corpus.parse('josquin/oVenusBant')
14113        >>> o.getNumbers()
14114        ['1', '2', '3']
14115        '''
14116        post = []
14117        for s in self.getElementsByClass('Score'):
14118            post.append(s.metadata.number)
14119        return post
14120
14121    def getScoreByNumber(self, opusMatch):
14122        # noinspection PyShadowingNames
14123        '''
14124        Get Score objects from this Stream by number.
14125        Performs title search using the
14126        :meth:`~music21.metadata.Metadata.search` method,
14127        and returns the first result.
14128
14129        >>> o = corpus.parse('josquin/oVenusBant')
14130        >>> o.getNumbers()
14131        ['1', '2', '3']
14132        >>> s = o.getScoreByNumber(2)
14133        >>> s.metadata.title
14134        'O Venus bant'
14135        >>> s.metadata.alternativeTitle
14136        'Tenor'
14137        '''
14138        for s in self.getElementsByClass('Score'):
14139            match, unused_field = s.metadata.search(opusMatch, 'number')
14140            if match:
14141                return s
14142
14143    # noinspection SpellCheckingInspection
14144    def getScoreByTitle(self, titleMatch):
14145        # noinspection SpellCheckingInspection, PyShadowingNames
14146        '''
14147        Get Score objects from this Stream by a title.
14148        Performs title search using the :meth:`~music21.metadata.Metadata.search` method,
14149        and returns the first result.
14150
14151        >>> o = corpus.parse('essenFolksong/erk5')
14152        >>> s = o.getScoreByTitle('Vrienden, kommt alle gaere')
14153        >>> s.metadata.title
14154        'Vrienden, kommt alle gaere'
14155
14156        Regular expressions work fine
14157
14158        >>> s = o.getScoreByTitle('(.*)kommt(.*)')
14159        >>> s.metadata.title
14160        'Vrienden, kommt alle gaere'
14161        '''
14162        for s in self.getElementsByClass('Score'):
14163            match, unused_field = s.metadata.search(titleMatch, 'title')
14164            if match:
14165                return s
14166
14167    @property
14168    def scores(self):
14169        '''
14170        Return all :class:`~music21.stream.Score` objects
14171        in an iterator
14172        '''
14173        return self.getElementsByClass('Score')  # replacing with bare Score is not working.
14174
14175    def mergeScores(self):
14176        # noinspection PyShadowingNames
14177        '''
14178        Some Opus objects represent numerous scores
14179        that are individual parts of the same work.
14180        This method will treat each contained Score as a Part,
14181        merging and returning a single Score with merged Metadata.
14182
14183        >>> from music21 import corpus
14184        >>> o = corpus.parse('josquin/milleRegrets')
14185        >>> s = o.mergeScores()
14186        >>> s.metadata.title
14187        'Mille regrets'
14188        >>> len(s.parts)
14189        4
14190        '''
14191        sNew = Score()
14192        mdNew = metadata.Metadata()
14193
14194        for s in self.scores:
14195            p = s.parts.first().makeNotation()  # assuming only one part
14196            sNew.insert(0, p)
14197
14198            md = s.metadata
14199            # presently just getting the first of attributes encountered
14200            if md is not None:
14201                # environLocal.printDebug(['sub-score meta data', md,
14202                #   'md.composer', md.composer, 'md.title', md.title])
14203                if md.title is not None and mdNew.title is None:
14204                    mdNew.title = md.title
14205                if md.composer is not None and mdNew.composer is None:
14206                    mdNew.composer = md.composer
14207
14208        sNew.insert(0, mdNew)
14209        return sNew
14210
14211    # -------------------------------------------------------------------------
14212    def write(self, fmt=None, fp=None, **keywords):
14213        '''
14214        Displays an object in a format provided by the `fmt` argument or, if not
14215        provided, the format set in the user's Environment.
14216
14217        This method overrides the behavior specified in
14218        :class:`~music21.base.Music21Object` for all formats besides explicit
14219        lily.x calls.
14220
14221        Individual files are written for each score; returns the last file written.
14222
14223        >>> sc1 = stream.Score()
14224        >>> sc2 = stream.Score()
14225        >>> o = stream.Opus()
14226        >>> o.append([sc1, sc2])
14227
14228        #_DOCS_SHOW >>> o.write()
14229        #_DOCS_SHOW PosixPath('/some/temp/path-2.xml')
14230        '''
14231        if not self.scores:
14232            return None
14233
14234        if fmt is not None and 'lily' in fmt:
14235            return Stream.write(self, fmt, fp, **keywords)
14236        elif common.runningUnderIPython():
14237            return Stream.write(self, fmt, fp, **keywords)
14238
14239        delete = False
14240        if fp is None:
14241            if fmt is None:
14242                suffix = '.' + environLocal['writeFormat']
14243            else:
14244                unused_format, suffix = common.findFormat(fmt)
14245            fp = environLocal.getTempFile(suffix=suffix, returnPathlib=False)
14246            # Mark for deletion, because it won't actually be used
14247            delete = True
14248        if isinstance(fp, str):
14249            fp = pathlib.Path(fp)
14250
14251        fpParent = fp.parent
14252        fpStem = fp.stem
14253        fpSuffix = '.'.join(fp.suffixes)
14254
14255        post = []
14256        placesRequired = math.ceil(math.log10(len(self.scores)))
14257        for i, s in enumerate(self.scores):
14258            # if i = 9, num = 10, take log10(11) so the result is strictly greater than 1.0
14259            placesConsumed = math.ceil(math.log10(i + 2))
14260            zeroesNeeded = placesRequired - placesConsumed
14261            zeroes = '0' * zeroesNeeded
14262            scoreName = fpStem + '-' + zeroes + str(i + 1) + fpSuffix
14263            fpToUse = fpParent / scoreName
14264            fpReturned = s.write(fmt=fmt, fp=fpToUse, **keywords)
14265            environLocal.printDebug(f'Component {s} written to {fpReturned}')
14266            post.append(fpReturned)
14267
14268        if delete:
14269            os.remove(fp)
14270
14271        return post[-1] if post else None
14272
14273    def show(self, fmt=None, app=None, **keywords):
14274        '''
14275        Show an Opus file.
14276
14277        This method overrides the behavior specified in
14278        :class:`~music21.base.Music21Object` for all
14279        formats besides explicit lily.x calls. or when running under IPython notebook.
14280        '''
14281        if fmt is not None and 'lily' in fmt:
14282            return Stream.show(self, fmt, app, **keywords)
14283        elif common.runningUnderIPython():
14284            return Stream.show(self, fmt, app, **keywords)
14285        else:
14286            for s in self.scores:
14287                s.show(fmt=fmt, app=app, **keywords)
14288
14289
14290# -----------------------------------------------------------------------------
14291# Special stream sub-classes that are instantiated as hidden attributes within
14292# other Music21Objects (i.e., Spanner and Variant).
14293
14294
14295class SpannerStorage(Stream):
14296    '''
14297    For advanced use. This Stream subclass is only used
14298    inside of a Spanner object to provide object storage
14299    of connected elements (things the Spanner spans).
14300
14301    This subclass name can be used to search in an
14302    object's .sites and find any and all
14303    locations that are SpannerStorage objects.
14304
14305    A `spannerParent` keyword argument must be
14306    provided by the Spanner in creation.
14307
14308    TODO v7: rename spannerParent to client.
14309    '''
14310
14311    def __init__(self, *arguments, **keywords):
14312        # No longer need store as weakref since Py2.3 and better references
14313        self.spannerParent = None
14314        if 'spannerParent' in keywords:
14315            self.spannerParent = keywords['spannerParent']
14316            del keywords['spannerParent']
14317        super().__init__(*arguments, **keywords)
14318
14319        # must provide a keyword argument with a reference to the spanner
14320        # parent could name spannerContainer or other?
14321
14322        # environLocal.printDebug('keywords', keywords)
14323
14324    # NOTE: for serialization, this will need to properly tag
14325    # the spanner parent by updating the scaffolding code.
14326    def coreSelfActiveSite(self, el):
14327        '''
14328        Never set activeSite to spannerStorage
14329        '''
14330        pass
14331
14332    def coreStoreAtEnd(self, element, setActiveSite=True):  # pragma: no cover
14333        raise StreamException('SpannerStorage cannot store at end.')
14334
14335    def replace(self,
14336                target: base.Music21Object,
14337                replacement: base.Music21Object,
14338                *,
14339                recurse: bool = False,
14340                allDerived: bool = True) -> None:
14341        '''
14342        Overrides :meth:`~music21.stream.Stream.replace` in order to check first
14343        whether `replacement` already exists in `self`. If so, delete `target` from
14344        `self` and return; otherwise call the superclass method.
14345
14346        New in v7.
14347        '''
14348        # Does not perform a recursive search, but shouldn't need to
14349        if replacement in self:
14350            self.remove(target)
14351            return
14352        super().replace(target, replacement, recurse=recurse, allDerived=allDerived)
14353
14354
14355class VariantStorage(Stream):
14356    '''
14357    For advanced use. This Stream subclass is only
14358    used inside of a Variant object to provide object
14359    storage of connected elements (things the Variant
14360    defines).
14361
14362    This subclass name can be used to search in an
14363    object's .sites and find any and all
14364    locations that are VariantStorage objects.
14365
14366    A `variantParent` keyword argument must be provided
14367    by the Variant in creation.
14368
14369    # TODO v7: rename variantParent to client
14370    '''
14371
14372    def __init__(self, *arguments, **keywords):
14373        super().__init__(*arguments, **keywords)
14374        # must provide a keyword argument with a reference to the variant
14375        # parent
14376        self.variantParent = None
14377        if 'variantParent' in keywords:
14378            self.variantParent = keywords['variantParent']
14379
14380
14381# -----------------------------------------------------------------------------
14382
14383
14384# -----------------------------------------------------------------------------
14385
14386
14387class Test(unittest.TestCase):
14388    '''
14389    Note: most Stream tests are found in stream.tests
14390    '''
14391
14392    def testCopyAndDeepcopy(self):
14393        '''Test copying all objects defined in this module
14394        '''
14395        for part in sys.modules[self.__module__].__dict__:
14396            if part.startswith('_') or part.startswith('__'):
14397                continue
14398            elif part in ['Test', 'TestExternal']:
14399                continue
14400            elif callable(part):  # pragma: no cover
14401                # environLocal.printDebug(['testing copying on', part])
14402                # noinspection PyTypeChecker
14403                obj = getattr(self.__module__, part)()
14404                a = copy.copy(obj)
14405                b = copy.deepcopy(obj)
14406                self.assertNotEqual(a, obj)
14407                self.assertNotEqual(b, obj)
14408
14409
14410# -----------------------------------------------------------------------------
14411# define presented order in documentation
14412_DOC_ORDER = [Stream, Measure, Part, Score, Opus, Voice]
14413
14414
14415if __name__ == '__main__':
14416    import music21
14417    music21.mainTest(Test)
14418