
    rhe                    2   S r SSKJr  SSKrSSKrSSKrSSKJrJ	r	J
r
  SSKJr  SSKJr  SSKJr  SSKJr  SS	KJr  SS
KJr  SSKJr  SSKJrJr        SS jr " S S5      r " S S\R<                  5      r\ S:X  a  SSKr\RB                  " \5        gg)z
A mixin to ScoreExporter that includes the capabilities for producing a single
MusicXML `<part>` from multiple music21 `PartStaff` objects.
    )annotationsN)Element
SubElementComment)flattenList)
M21ObjType)KeySignature)
StaffGroup)TimeSignature)stream)helpers)MusicXMLExportExceptionMusicXMLWarningc                l   U R                   S:w  a  [        S5      eU H  nU R                  U5       H{  nUR                  S5      b4  [        S5      nU R	                  S5      =n(       a  Xel        UeSUl        Ue[        S5      n[        U5      Ul        [        R                  " XG/ SQS	9  M}     M     g)
a  
For a <measure> tag `measure`, add a <staff> grandchild to any instance of
a child tag of a type in `tagList`.

>>> from xml.etree.ElementTree import fromstring as El
>>> from music21.musicxml.partStaffExporter import addStaffTags
>>> from music21.musicxml.helpers import dump
>>> elem = El("""
...     <measure number="1">
...        <note>
...          <rest measure="yes" />
...          <duration>8</duration>
...        </note>
...      </measure>"""
...     )
>>> addStaffTags(elem, 2, tagList=['note', 'forward', 'direction', 'harmony'])
>>> dump(elem)
<measure number="1">
  <note>
    <rest measure="yes" />
    <duration>8</duration>
    <staff>2</staff>
  </note>
</measure>

Raise if a <staff> grandchild is already present:

>>> addStaffTags(elem, 2, tagList=['note', 'forward', 'direction'])
Traceback (most recent call last):
music21.musicxml.xmlObjects.MusicXMLExportException:
    In part (), measure (1): Attempted to create a second <staff> tag

The function doesn't accept elements other than <measure>:

>>> addStaffTags(elem.find('note'), 2, tagList=['direction'])
Traceback (most recent call last):
music21.musicxml.xmlObjects.MusicXMLExportException:
    addStaffTags() only accepts <measure> tags
measurez*addStaffTags() only accepts <measure> tagsstaffNz(Attempted to create a second <staff> tagnumber )beam	notationslyricplaysoundtagList)tagr   findallfindgetmeasureNumberr   strtextr   insertBeforeElements)r   staffNumberr   tagNamer   emmxStaffs           \/home/james-whalen/.local/lib/python3.13/site-packages/music21/musicxml/partStaffExporter.pyaddStaffTagsr*   "   s    X {{i%&RSS??7+Cxx ,+,VW-4[[-B(B(B! JLg&G{+GL((:CD ,     c                      \ rS rSrS rSS jrSS jrSS jr        SS jrSS jr	SS jr
\SS	 j5       rSS
 jrSrg)PartStaffExporterMixin]   c                    U R                   (       d  U R                  5       U l         U R                    HG  nU R                  U5        U R                  U5        U R	                  U5        U R                  U5        MI     g)a  
Collect <part> elements exported from
:class:`~music21.stream.PartStaff` objects under a single
<part> element using <staff> and <voice> subelements.

Here we load in a simple 2-staff piano piece.  Note that they
are both elements of the :class:`~music21.stream.PartStaff`
Stream subclass.

>>> from music21.musicxml import testPrimitive
>>> s = converter.parse(testPrimitive.pianoStaff43a)
>>> s.show('text')
{0.0} <music21.metadata.Metadata object at 0x107d6a100>
{0.0} <music21.stream.PartStaff P1-Staff1>
    {0.0} <music21.instrument.Instrument 'P1: MusicXML Part: '>
    {0.0} <music21.stream.Measure 1 offset=0.0>
        {0.0} <music21.clef.TrebleClef>
        {0.0} <music21.key.KeySignature of no sharps or flats>
        {0.0} <music21.meter.TimeSignature 4/4>
        {0.0} <music21.note.Note F>
{0.0} <music21.stream.PartStaff P1-Staff2>
    {0.0} <music21.instrument.Instrument 'P1: MusicXML Part: '>
    {0.0} <music21.stream.Measure 1 offset=0.0>
        {0.0} <music21.clef.BassClef>
        {0.0} <music21.key.KeySignature of no sharps or flats>
        {0.0} <music21.meter.TimeSignature 4/4>
        {0.0} <music21.note.Note B>
{0.0} <music21.layout.StaffGroup
         <music21.stream.PartStaff P1-Staff1><music21.stream.PartStaff P1-Staff2>>

Now these get joined into a single part in the `parse()` process:

>>> SX = musicxml.m21ToXml.ScoreExporter(s)
>>> root = SX.parse()
>>> parts = root.findall('part')
>>> len(parts)
1

>>> clefs = root.findall('.//clef')
>>> len(clefs)
2

Note that there are exactly two notes (an F and B) in the original score,
so there are exactly two staff tags in the output.

>>> staffTags = root.findall('part/measure/note/staff')
>>> len(staffTags)
2
N)groupsToJoinjoinableGroupsaddStaffTagsMultiStaffPartsmovePartStaffMeasureContents&setEarliestAttributesAndClefsPartStaffcleanUpSubsequentPartStaffs)selfgroups     r)   joinPartStaffs%PartStaffExporterMixin.joinPartStaffs^   si    h    !% 3 3 5D&&E,,U3--e477>,,U3	 'r+   c                   [         R                  (       a  SSKJn  [	        X5      (       d   eU R
                  =n(       a  [        UR                  [        5      5      O/ n/ nU Hr  n[        U5      S::  a  M  [        S U 5       5      (       d  M-  [        S U 5       5      (       d  MF   U H  nU R                  U5        M     UR                  U5        Mt     [        5       n/ nU H5  n	[        U	5      n
X;  a  UR                  U	5        UR!                  U
5        M7     [#        U5      n[        [        U5      5      [        U5      :w  a!  [$        R&                  " [)        S5      5        / $ U H;  nSnU H0  nU R*                   H  nUR
                  ULa  M  Xl        Un  M.     M2     M=     U$ ! [         a     GM[  f = f)a	  
Returns a list of :class:`~music21.layout.StaffGroup` objects that
represent :class:`~music21.stream.base.PartStaff` objects that can be
joined into a single MusicXML `<part>`, so long as there exists a
`PartExporter` for it in `ScoreExporter.partExporterList`.

Sets :attr:`~music21.musicxml.m21ToXml.PartExporter.previousPartStaffInGroup`.

>>> s = stream.Score()

Group 1: three staves.

>>> p1a = stream.PartStaff(id='p1a')
>>> p1a.insert(0, stream.Measure())
>>> p1b = stream.PartStaff(id='p1b')
>>> p1b.insert(0, stream.Measure())
>>> p1c = stream.PartStaff(id='p1c')
>>> p1c.insert(0, stream.Measure())
>>> sg1 = layout.StaffGroup([p1a, p1b, p1c])

Group 2: two staves.

>>> p2a = stream.PartStaff(id='p2a')
>>> p2a.insert(0, stream.Measure())
>>> p2b = stream.PartStaff(id='p2b')
>>> p2b.insert(0, stream.Measure())
>>> sg2 = layout.StaffGroup([p2a, p2b])

Group 3: one staff -- will not be merged.

>>> p3a = stream.PartStaff(id='p3a')
>>> p3a.insert(0, stream.Measure())
>>> sg3 = layout.StaffGroup([p3a])

Group 4: two staves, but no measures, will not be merged:

>>> p4a = stream.PartStaff(id='p4a')
>>> p4b = stream.PartStaff(id='p4b')
>>> sg4 = layout.StaffGroup([p4a, p4b])

Group 5: two staves, but no staff group

>>> p5a = stream.PartStaff(id='p5a')
>>> p5a.insert(0, stream.Measure())
>>> p5b = stream.PartStaff(id='p5b')
>>> p5b.insert(0, stream.Measure())

Group 6: same as Group 2, just to show that valid groups can come later

>>> p6a = stream.PartStaff(id='p6a')
>>> p6a.insert(0, stream.Measure())
>>> p6b = stream.PartStaff(id='p6b')
>>> p6b.insert(0, stream.Measure())
>>> sg6 = layout.StaffGroup([p6a, p6b])

Group 7: same as Group 6, but with Parts instead of PartStaffs

>>> p7a = stream.Part(id='p7a')
>>> p7a.insert(0, stream.Measure())
>>> p7b = stream.Part(id='p7b')
>>> p7b.insert(0, stream.Measure())
>>> sg7 = layout.StaffGroup([p7a, p7b])

Group 8: encloses same objects as Group 6, just to show it's gracefully ignored

>>> sg8 = layout.StaffGroup([p6a, p6b])

>>> for el in (p1a, p1b, p1c, sg1, p2a, p2b, sg2, p3a, sg3,
...            p4a, p4b, sg4, p5a, p5b, p6a, p6b, sg6, p7a, p7b, sg7, sg8):
...     s.insert(0, el)

>>> SX = musicxml.m21ToXml.ScoreExporter(s)
>>> SX.scorePreliminaries()
>>> SX.parsePartlikeScore()  # populate .partExporterList
>>> SX.joinableGroups()
[<music21.layout.StaffGroup <... p1a><... p1b><... p1c>>,
 <music21.layout.StaffGroup <... p2a><... p2b>>,
 <music21.layout.StaffGroup <... p6a><... p6b>>]
r   )XMLExporterBase   c              3  \   #    U  H"  n[         R                  UR                  ;   v   M$     g 7fN)r   	PartStaffclassSet.0ps     r)   	<genexpr>8PartStaffExporterMixin.joinableGroups.<locals>.<genexpr>   s     Br!v''1::5rs   *,c              3  ^   #    U  H#  oR                  [        R                  5      v   M%     g 7fr>   )getElementsByClassr   MeasurerA   s     r)   rD   rE      s      HR++FNN;;Rs   +-z7Got overlapping StaffGroups; will not merge ANY groups.N)tTYPE_CHECKINGmusic21.musicxml.m21ToXmlr;   
isinstancer   listrG   r
   lenallgetRootForPartStaffr   appendsettupleaddr   warningswarnr   partExporterListpreviousPartStaffInGroup)r6   r;   sstaffGroupsr1   sgrC   permutationsdeduplicatedGroupsjgcontainedPartsjoinable_components_listr7   prior_part_staff
part_staffpart_exporters                   r)   r1   %PartStaffExporterMixin.joinableGroups   s   b ??Ad4444FJkkAQAQd1//
;<XZ+- B2w!|BrBBBHRHHHA,,Q/  !!"%  u/1 B"2YN1"))"-^,	 ! $//A#B s+,-5M1NNMM YZ\I (E##
%)%:%:M$++:= =M:'1$ &; $ ( "!? + s   ,F22
G Gc                N   Sn[        U5       HH  u  p4US-   nU R                  U5      nUR                  S5       H  n [        UU/ SQS9  M     Ub  MF  UnMJ     g! [         a?  nUR
                  Ul        UR                  S5      =n	(       a  Xl        UeSUl        UeSnAff = f)aG  
Create child <staff> tags under each <note>, <direction>, and <forward> element
in the <part>s being joined.

Called by :meth:`joinPartStaffs`.

>>> from music21.musicxml import testPrimitive
>>> s = converter.parse(testPrimitive.pianoStaff43a)
>>> SX = musicxml.m21ToXml.ScoreExporter(s)
>>> root = SX.parse()
>>> m1 = root.find('part/measure')
>>> SX.dump(m1)
<measure implicit="no" number="1">
...
  <note>
    <pitch>
      <step>F</step>
      <octave>4</octave>
    </pitch>
    <duration>40320</duration>
    <voice>1</voice>
    <type>whole</type>
    <staff>1</staff>
  </note>
  <backup>
    <duration>40320</duration>
  </backup>
  <note>
    <pitch>
      <step>B</step>
      <octave>2</octave>
    </pitch>
    <duration>40320</duration>
    <voice>2</voice>
    <type>whole</type>
    <staff>2</staff>
  </note>
</measure>
Nr<   r   )note	directionforwardharmonyr   r   r   )	enumeraterP   r   r*   r   partNamer   r    )
r6   r7   initialPartStaffRootipsr$   thisPartStaffRoot	mxMeasurer&   m_nums
             r)   r2   2PartStaffExporterMixin.addStaffTagsMultiStaffParts  s    P .2u%EA 1uK)-)A)A")E /66yA		 !# K B $+'8$' & / !#AJ9Bx9P0P0PeOG XZAOGs   A
B$%:BB$c                .   U R                  US   5      n[        U5       Hr  u  p4US:X  a  M  US-   nU R                  U5      nU R                  X&U5      nSnUR                  5        H'  u  pU
 H  nUR	                  X-   U5        US-  nM     M)     Mt     g)z
For every <part> after the first, find the corresponding measure in the initial
<part> and merge the contents by inserting all contained elements.

Called by :meth:`joinPartStaffs`

StaffGroup must be a valid one from
:meth:`joinableGroups`.
r   r<   N)rP   rj   processSubsequentPartStaffitemsinsert)r6   r7   targetrm   rn   r$   source
insertionsinsertionCounteroriginalIdxelementselements               r)   r3   3PartStaffExporterMixin.movePartStaffMeasureContents]  s     ))%(3u%EAAv 1uK--b1F88UJ$%)3)9)9);%'GMM+"@'J$)$  ( *< &r+   c                R  ^^ SmSmS
UU4S jjn[        UR                  S5      5      nSn0 n[        U5       GH.  u  pU	R                  S:w  a  M  Uc   [	        U5      nU	R                  S5      n
UR                  S5      nX:X  a  U R                  XiU5        SnMd  Ub   U
b  [        R                  " X5      (       a  M  Ub  U
b  [        R                  " X5      (       ax  X;  a  / Xx'   [        R                  (       a  [        U[        5      (       d   eXx==   U" U5      U/-  ss'    [	        U5      nUb   U
b  [        R                  " X5      (       a  Mx  [        SU
 SU 35      e   [        U5      nUb  UR!                  S	U5        U Hm  nUR                  S5      nUc  M  [#        U5      nX;  a  / X~'   [        R                  (       a  [        U[        5      (       d   eX~==   U" U5      U/-  ss'   Mo     U$ ! [
         a    Us s  $ f = f! [
         a    Us s  $ f = f)a  
Move elements from subsequent PartStaff's measures into `target`: the <part>
element representing the initial PartStaff that will soon represent the merged whole.

Called by
:meth:`movePartStaffMeasureContents`,
which is in turn called by
:meth:`joinPartStaffs`.
zB========================= Measure [NNN] ==========================z[NNN]c                L   > [        TR                  T[        U 5      5      5      $ r>   )r   replacer!   )inner_sourceNumberDIVIDER_COMMENTPLACEHOLDERs    r)   makeDividerFPartStaffExporterMixin.processSubsequentPartStaff.<locals>.makeDivider  s!    ?22;DV@WXYYr+   r   Nr   z2joinPartStaffs() was unable to order the measures z, r   )r   z	int | strreturnzElement[t.Any])iterr   rj   r   nextStopIterationr   moveMeasureContentsr   measureNumberComesBeforerI   rJ   rL   r!   r   rM   rv   rN   )r6   rw   rx   staffNumr   sourceMeasuressourceMeasurery   rm   targetMeasuretargetNumbersourceNumberremainingMeasures	remainingidxr   r   s                  @@r)   rt   1PartStaffExporterMixin.processSubsequentPartStaffw  sG    _	Z 	Z fnnY78/1
 !*& 1A  I-$&$($8M ),,X6L(,,X6L +((xP $ (#/77SS  +#/77SS&$&JM??%lC8888+l";]!KK&$($8M  +#/77SS *D.<.23 3O !2X !0$$$Q6*I$==2L#f+C$"$
!,4444OL 99EEO + k % &%%&> % &%%&s$   H+HHHH&%H&c                .  ^  S   SU4S jjjnU" [         5      nU" [        SS9nSnSn[        T5       GH  u  pxUS-   n	Uc  U R                  U5      nUR	                  S5      =nb&  UR	                  S5      =n
b  U
R                  SS	5        [        S
5      n[        [        T5      5      Ul	        [        R                  " UU/ SQS9  U(       a)  Ub&  UR	                  S5      nUb  UR                  SS	5        U(       a/  Ub*  UR	                  S5      nUb  UR                  SS	5        M  M  M  M  U R                  U5      nUR	                  S5      nUGb  UGb  UR                  S5      n[        U5      U	:  a  [        S5      nUR                  Ul        Ue[        S5      nUR                  S[        U	5      5        [        US5      nUR	                  S5      =nb  UR                  OSUl	        [        US5      nUR	                  S5      =nb  UR                  OSUl	        UR	                  S5      nUb  [        US5      nUR                  Ul	        [        R                  " UU/ SQS9  U(       aF  UR	                  S5      =nb2  UR                  S[        U	5      5        [        R                  " UUS
/S9  U(       d  GM  UR	                  S5      =nc  GM  UR                  S[        U	5      5        [        R                  " UUSS
/S9  GM     g)ac  
Set the <staff>, <key>, <time>, and <clef> information on the earliest
measure <attributes> tag in the <part> representing the joined PartStaffs.

Need the earliest <attributes> tag, which may not exist in the merged <part>
until moved there by movePartStaffMeasureContents() --
e.g. RH of piano doesn't appear until m. 40, and earlier music for LH needs
to be merged first in order to find earliest <attributes>.

Called by :meth:`joinPartStaffs`

Multiple keys:

>>> from music21.musicxml import testPrimitive
>>> xmlDir = common.getSourceFilePath() / 'musicxml' / 'lilypondTestSuite'
>>> s = converter.parse(xmlDir / '43b-MultiStaff-DifferentKeys.xml')
>>> SX = musicxml.m21ToXml.ScoreExporter(s)
>>> root = SX.parse()
>>> m1 = root.find('part/measure')
>>> SX.dump(m1)
<measure implicit="no" number="1">
  <attributes>
    <divisions>10080</divisions>
    <key number="1">
      <fifths>0</fifths>
    </key>
    <key number="2">
      <fifths>2</fifths>
    </key>
    <time>
      <beats>4</beats>
      <beat-type>4</beat-type>
    </time>
    <staves>2</staves>
    <clef number="1">
      <sign>G</sign>
      <line>2</line>
    </clef>
    <clef number="2">
      <sign>F</sign>
      <line>4</line>
    </clef>
  </attributes>
...
</measure>

Multiple meters (not very well supported by MusicXML readers):

>>> from music21.musicxml import testPrimitive
>>> s = converter.parse(testPrimitive.pianoStaffPolymeterWithClefOctaveChange)
>>> SX = musicxml.m21ToXml.ScoreExporter(s)
>>> root = SX.parse()
>>> m1 = root.find('part/measure')
>>> SX.dump(m1)
<measure implicit="no" number="1">
    <attributes>
    <divisions>10080</divisions>
    <key>
        <fifths>0</fifths>
    </key>
    <time number="1">
        <beats>4</beats>
        <beat-type>4</beat-type>
    </time>
    <time number="2">
        <beats>2</beats>
        <beat-type>2</beat-type>
    </time>
    <staves>2</staves>
    <clef number="1">
        <sign>G</sign>
        <line>2</line>
    </clef>
    <clef number="2">
        <sign>G</sign>
        <line>2</line>
        <clef-octave-change>-1</clef-octave-change>
    </clef>
    </attributes>
...
</measure>
c                  > SnT H  nUc/  UR                  5       R                  U 5      R                  5       nM5  UR                  5       R                  U 5      R                  5       nUc  Mg  [        XA5      nU" U5      (       a  M    g   g)z
Return True if any first instance of m21Class in any subsequent staff
in this StaffGroup does not compare to the first instance of that class
in the earliest staff where found (not necessarily the first) using `comparison`.
NTF)recurserG   firstgetattr)m21Class
comparisoninitialM21Instancern   firstInstanceSubsequentStaffcomparisonWrapperr7   s         r)   isMultiAttributeWPartStaffExporterMixin.setEarliestAttributesAndClefsPartStaff.<locals>.isMultiAttribute!  s     37%-)+)H)H)R)X)X)Z&35::<3R3RS[3\3b3b3d03?,34P,])01CDD#'  r+   
ratioEqual)r   Nr<   zmeasure/attributesclefr   1staves)zpart-symbolinstrumentsr   staff-details	transpose	directivemeasure-styler   keytimezmeasure/attributes/clefz'Attempted to add more clefs than staffssignliner   zclef-octave-change)r   r   r   r   zmeasure/attributes/timezmeasure/attributes/key)__eq__)r   ztype[M21ObjType]r   r!   r   bool)r	   r   rj   rP   r   rR   r   r!   rN   r"   r   r#   r   r   rk   r   )r6   r7   r   multiKey
multiMeterrl   mxAttributesrm   rn   r$   clef1mxStaveskey1meter1ro   oldClefclefsInMxAttributesAlreadyr&   newClefnewSignoldClefSignnewLine	foundLinefoundOctave	newOctaveoldMeteroldKeys    `                         r)   r4   =PartStaffExporterMixin.setEarliestAttributesAndClefsPartStaff  s   j 08	),	<@	 	, *,7+MlS
-1%)u%EA 1uK $+'+'?'?'C$$8$=$=>R$SSL`!-!2!26!::G		(C0"8, #CJ,, K  8',,U3D'3/,":)..v6F)

8S1 * #;: .2-E-Eb-I!(9(>(>?X(Y&<+C1=1E1Ef1M.56+E34]^%'[[
 &foGKK#k*:;(&9G7>||F7K(K'X %0$4$4)- L )&9G5<\\&5I(I	'V %.NN)+ L #*,,/C"DK".$.w8L$M	)4)9)9	00$ \ $5$:$:;T$UUb Xs;/?@44($%-J
 8"3"8"89Q"RR_

8S-=>44("%+X$6O &r+   c                    USS  HG  nU R                  U5      nU R                   Vs/ s H  oDR                  U:w  d  M  UPM     snU l        MI     gs  snf )a  
Now that the contents of all PartStaffs in `group` have been represented
by a single :class:`~music21.musicxml.m21ToXml.PartExporter`, remove any
obsolete `PartExporter` from `self.partExporterList`.

Called by :meth:`joinPartStaffs`

>>> from music21.musicxml import testPrimitive
>>> s = converter.parse(testPrimitive.pianoStaff43a)
>>> SX = musicxml.m21ToXml.ScoreExporter(s)
>>> SX.scorePreliminaries()
>>> SX.parsePartlikeScore()
>>> len(SX.partExporterList)
2
>>> SX.postPartProcess()
>>> len(SX.partExporterList)
1
r<   N)rP   rW   xmlRoot)r6   r7   rn   partStaffRootpexs        r)   r5   2PartStaffExporterMixin.cleanUpSubsequentPartStaffs  sZ    & )B%)%=%=b%AM  $44S4}8T4SD!	 Ss
   AAc                   U R                   S:w  d  UR                   S:w  a  [        SU  SU S35      eSnSnUR                  S5       H)  nUR                  nUc  M  [	        U[        U5      5      nM+     US:X  aF  S	nUR                  S
5       H-  n[        S5      nSUl        [        R                  " UU/ SQS9  M/     SnSn	UR                  S
5       HL  n
U
R                  S5      b  M  U
R                  S5      nUc  M-  UR                  nUc  M>  U	[        U5      -  n	MN     UR                  S5       H"  nUR                  nUc  M  U	[        U5      -  n	M$     UR                  S5       H"  nUR                  nUc  M  U	[        U5      -  n	M$     U	(       a8  [        S5      n[        US5      n[        U	5      Ul        UR                  U5        U R                  S5       GH}  nUR                   S:X  a  M  UR                   S:X  aK  UR                  S5      (       a  M>  UR                  S5       H  nUR                  S[        U5      5        M      UR                   S:X  aQ  UR                  S5      nUR                  S5       H+  nUR                  S5      U:X  d  M  UR                  U5        M-     UR                   S
:X  a  UR                  S5      nUb@  U(       a8  UR                  (       a'  [        [        UR                  5      S-   5      Ul        O7O6[        S5      n[        US-   5      Ul        [        R                  " UU/ SQS9  UR                  U5        GM     g)aw  
Move the child elements of `measure` into `otherMeasure`;
create voice numbers if needed;
bump voice numbers if they conflict;
account for <backup> and <forward> tags;
skip <print> tags;
set "number" on midmeasure clef changes;
replace existing <barline> tags.

>>> from xml.etree.ElementTree import fromstring as El
>>> measure = El('<measure><note /></measure>')
>>> otherMeasure = El('<measure><note /></measure>')
>>> SX = musicxml.m21ToXml.ScoreExporter
>>> SX.moveMeasureContents(measure, otherMeasure, 2)
>>> SX().dump(otherMeasure)
<measure>
  <note>
    <voice>1</voice>
  </note>
  <note>
    <voice>2</voice>
  </note>
</measure>

>>> SX.moveMeasureContents(El('<junk />'), otherMeasure, 2)
Traceback (most recent call last):
music21.musicxml.xmlObjects.MusicXMLExportException:
    moveMeasureContents() called on <Element 'junk'...

Only one <barline> should be exported per merged measure:

>>> from music21.musicxml import testPrimitive
>>> s = converter.parse(testPrimitive.mixedVoices1a)
>>> SX = musicxml.m21ToXml.ScoreExporter(s)
>>> root = SX.parse()
>>> root.findall('part/measure/barline')
[<Element 'barline' at 0x...]
r   z moveMeasureContents() called on z and z (not measures).r   Fz*/voiceNTrf   voicer   )typedot
accidentalztime-modificationstemnoteheadznotehead-textr   r   r<   chorddurationzforward/durationzbackup/durationbackup*print
attributes	divisionsr   r   barlinelocation)r   r   r   r"   maxintr   r   r#   r   r   r!   rQ   rR   r   remove)r   otherMeasurer$   	maxVoicesotherMeasureLackedVoiceother_voiceotherVoiceTextelem	new_voiceamountToBackuprf   durbackupDurTextforwardDurText	backupDurmxBackup
mxDurationmidMeasureClefthisBarlineLocationexistingBarliner   s                        r)   r   *PartStaffExporterMixin.moveMeasureContents  s#   R ;;)#|'7'79'D)27)5N^_a a	(-'//	:K(--N)	3~+>?	 ;
 >&*#$,,V4#G,	!$	,, 5 I   ((0Dyy!-))J'C{HHM(#m"44 1  ''(:;C XXN)#n"55 < &--.?@I%NNM(#m"44 A x(H#Hj9J!.1JO) OOC(Dxx7"xx<'<<,,&*ll6&:N"&&x[1AB ';xx9$&*hhz&:#'3';';I'FO&**:6:MM$++O< (G xx6!		'*$.5::%(UZZ1)<%=
#G,E!$Y]!3EJ00! %I )r+   c                   U R                    H.  nXR                  L d  M  UR                  c  M"  UR                  s  $    U R                    HQ  nUR                  R                  R	                  5        H&  nX1L d  M	  UR                  c  M  UR                  s  s  $    MS     U R                    HD  nUR
                  UR                  R
                  :X  d  M)  UR                  c  M8  UR                  s  $    U R                    Hg  nUR                  R                  R	                  5        H<  nUR
                  UR
                  :X  d  M  UR                  c  M.  UR                  s  s  $    Mi     [        U S35      e)aW  
Look up the <part> Element being used to represent the music21 `partStaff`.

>>> from music21.musicxml import testPrimitive
>>> s = converter.parse(testPrimitive.pianoStaff43a)
>>> SX = musicxml.m21ToXml.ScoreExporter(s)
>>> SX.scorePreliminaries()
>>> SX.parsePartlikeScore()
>>> SX.getRootForPartStaff(s.parts[0])
<Element 'part' at 0x...

>>> other = stream.PartStaff()
>>> other.id = 'unrelated'
>>> SX.getRootForPartStaff(other)
Traceback (most recent call last):
music21.musicxml.xmlObjects.MusicXMLExportException:
    <music21.stream.PartStaff unrelated> not found in self.partExporterList
z# not found in self.partExporterList)rW   r   r   
derivationchainidr   )r6   	partStaffr   deriveds       r)   rP   *PartStaffExporterMixin.getRootForPartStaff'  s   & ((CJJ&3;;+B{{" )
 ((C::00668'CKK,C;;& 9 ) ((C||szz}},1H{{" ) ((C::00668<<7::-#++2I;;& 9 )
 &k<=? 	?r+   )r0   rW   N)r   zlist[StaffGroup])r7   r
   )rw   r   rx   r   r   r   r   zdict[int, list[Element]])r   r   r   r   r$   r   )r   zstream.PartStaffr   r   )__name__
__module____qualname____firstlineno__r8   r1   r2   r3   rt   r4   r5   staticmethodr   rP   __static_attributes__ r+   r)   r-   r-   ]   sr    <4|A"F<|*4T+2T+2T .1T )A	Tlzx6 @& @&D(?r+   r-   c                  n    \ rS rSrS rS rS rS rS rS r	S r
S	 rS
 rS rS rS rS rS rS rSrg)TestiR  c                d    SSK Jn  U" 5       nUR                  U5      nUR                  S5      nU$ )Nr   )GeneralObjectExporterzutf-8)rK   r   parsedecode)r6   objr   gexbytesOutbytesOutUnicodes         r)   getXmlTest.getXmlT  s.    C#%99S>"//'2r+   c                n    SSK Jn  U" U5      nUR                  5       n[        R                  " U5        U$ )Nr   ScoreExporter)rK   r  r  r   indent)r6   r  r  SXmxScores        r)   getET
Test.getET\  s+    ;3((*wr+   c                   SSK Jn  UR                  SS5      nU R                  U5      nUR	                  S5      nUR                  S5      nU R                  [        U5      S5        U R                  US   R                  S5      S	5        U R                  US
   R                  S5      S5        U R                  US   R                  S5      S5        U R                  US   R	                  S5      R                  S5        g)z;
Measure 1, staff 2 contains mid-measure treble clef in LH
r   corpusschoenberg/opus19   part/measurezattributes/clef   r   r   r<   2r   GN)
music21r  r  r  r   r   assertEqualrN   r   r"   )r6   r  schrootm1clefss         r)   testJoinPartStaffsATest.testJoinPartStaffsAd  s     	#ll.2zz# YY~&

,-UQ'qh/5qh/5qh/5qv.33S9r+   c                T   SSK Jn  SSK Jn  [        R                  " 5       n[        R
                  " 5       nUR                  SUR                  5       5        UR                  SUR                  5       5        [        R
                  " 5       nUR                  SUR                  5       5        UR                  U5        UR                  U5        UR                  SUR                  XE/5      5        U R                  U5      nUR                  S5      nU HG  nU R                  UR                  S5      R                  UR                  S5      R                  5        MI     UR                  S5      n	UR                  S	5      n
[        US   R                  S
5      R                  5      [        U	R                  S
5      R                  5      -   [        US   R                  S
5      R                  5      -   nU R                  [        U
R                  S
5      R                  5      U5        g)zE
Gapful first PartStaff, ensure <backup> in second PartStaff correct
r   layoutrf   r  .//noter   r   z
.//forwardz	.//backupr   r<   N)r  r$  rf   r   Scorer?   rv   NoterQ   r
   r  r   r  r   r"   r   )r6   r$  rf   rY   ps1ps2r  notesmxNoterh   r   r   s               r)   testJoinPartStaffsBTest.testJoinPartStaffsBu  s    	# LLN 

1diik"

1diik" 

1diik"			F%%sj12zz!}Y' FV[[166G8L8Q8QR  ))L);'aj)../',,z*//01%(--
+0012 	
 	V[[499:NKr+   c                   SSK Jn  SSK Jn  [        R                  " 5       n[        R
                  " 5       nUR                  UR                  5       S5        UR                  SS9  UR                  SU5        [        R
                  " 5       nUR                  UR                  5       S5        UR                  SS9  UR                  SU5        UR                  SUR                  XE/5      5        U R                  U5      nUR                  S5      nUR                  S	5      nU R                  [        U5      S
5        U R                  [        U5      S5        g)z$
First PartStaff longer than second
r   r#  r%     TinPlace   
.//measurer&  r     Nr  r$  rf   r   r'  r?   repeatAppendr(  makeNotationrv   r
   r  r   r  rN   	r6   r$  rf   rY   r)  r*  r  measuresr+  s	            r)   testJoinPartStaffsCTest.testJoinPartStaffsC  s    	# LLN a(&	C a(&	C	F%%sj12zz!}<<-Y'X*UR(r+   c                   SSK Jn  SSK Jn  [        R                  " 5       n[        R
                  " 5       nUR                  UR                  5       S5        UR                  SS9  [        R
                  " 5       nUR                  UR                  5       S5        UR                  SS9  UR                  SU5        UR                  SU5        UR                  SUR                  XT/5      5        U R                  U5      nUR                  S5      nUR                  S	5      nU R                  [        U5      S
5        U R                  [        U5      S5        g)z^
Same example as testJoinPartStaffsC but switch the hands:
second PartStaff longer than first
r   r#  r%  r0  Tr1  r3  r4  r&  r  r5  Nr6  r9  s	            r)   testJoinPartStaffsDTest.testJoinPartStaffsD  s	   
 	# LLN a(& a(&	C	C	F%%sj12zz!}<<-Y' 	X*UR(r+   c                
   SSK Jn  SSK Jn  [        R                  " 5       n[        R
                  " 5       n[        R                  " 5       nUR                  SU5        [        R                  " 5       n[        R                  " 5       nUR                  SU5        UR                  SU5        UR                  UR                  S5      S5        UR                  UR                  S5      S5        UR                  SS9  [        R
                  " 5       n[        R                  " 5       n	UR                  SU	5        [        R                  " 5       n
[        R                  " 5       nU	R                  SU
5        U	R                  SU5        U
R                  UR                  S	5      S5        UR                  UR                  S
5      S5        UR                  SS9  UR                  SU5        UR                  SU5        UR                  SUR                  XH/5      5        U R                  U5      nUR                  S5      nUR                  S5      nU R                  [!        U5      S5        U R                  [!        U5      S5        U GHs  nUR#                  S5      nUR#                  S5      R$                  S:X  aw  UR#                  S5      R$                  S:X  aX  U R                  UR#                  S5      R$                  S5        U R                  UR#                  S5      R$                  S5        M  UR#                  S5      R$                  S:X  ax  UR#                  S5      R$                  S:X  aY  U R                  UR#                  S5      R$                  S5        U R                  UR#                  S5      R$                  S5        GMB  UR#                  S5      R$                  S:X  ax  UR#                  S5      R$                  S:X  aY  U R                  UR#                  S5      R$                  S5        U R                  UR#                  S5      R$                  S5        GM  UR#                  S5      R$                  S:X  d  GM  UR#                  S5      R$                  S:X  d  GM  U R                  UR#                  S5      R$                  S5        U R                  UR#                  S5      R$                  S5        GMv     g)zS
Add measures and voices and check for unique voice numbers across the StaffGroup.
r   r#  r%  C4r3  E4Tr1  C3G3r4  r&  r<      pitchstepCoctave4r   r   r   Er  3r  N)r  r$  rf   r   r'  r?   rH   rv   Voicer7  r(  r8  r
   r  r   r  rN   r   r"   )r6   r$  rf   rY   r)  r  v1v2r*  m2v3v4r  r:  r+  r,  mxPitchs                    r)   testJoinPartStaffsD2Test.testJoinPartStaffsD2  s    	# LLN ^^

1b\\^\\^
		!R
		!R
		$+
		$+& ^^

1b\\^\\^
		!R
		!R
		$+
		$+&	C	C	F%%sj12zz!}<<-Y' 	X*UR( Fkk'*G||F#((C/GLL4J4O4OSV4V  W!5!:!:C@  W!5!:!:C@f%**c1gll86L6Q6QUX6X  W!5!:!:C@  W!5!:!:C@f%**c1gll86L6Q6QUX6X  W!5!:!:C@  W!5!:!:C@f%**c1gll86L6Q6QUX6X  W!5!:!:C@  W!5!:!:C@ r+   c                F   SSK Jn  SSK Jn  UR                  SS5      n[        R
                  " 5       n[        R                  " 5       n[        R                  " 5       nUR                  U5        UR                  U5        UR                  SUR                  XV/5      5        UR                  S   R                  S5      nUR                  S   R                  S5      nUR                  S   R                  S5      n	UR                  U5        UR                  U	5        UR                  UR                  U5        U R                  U5      n
U
R                  S5      u  pnU R                  UR                  S	5       Vs1 s H  oR                   iM     snS
15        U R                  UR                  S	5       Vs1 s H  oR                   iM     snS15        U R                  UR                  S	5       Vs1 s H  oR                   iM     snS
15        gs  snf s  snf s  snf )zN
Measure numbers existing only in certain PartStaffs: don't collapse together
r   r  r#  r  r  r<   r  r  z
note/staffr   r  N)r  r  r$  r  r   r'  r?   rQ   rv   r
   partsr   offsetr  r   r  r"   )r6   r  r$  r  rY   r)  r*  r  rP  m3r  m1tagm2tagm3tagr   s                  r)   testJoinPartStaffsETest.testJoinPartStaffsE  s    	#"ll.2LLN  			F%%sj12YYq\!!!$YYq\!!!$YYq\!!!$

2

2

299b!zz!}"ll>:e%--2MN2M**2MNQTPUV%--2MN2M**2MNQTPUV%--2MN2M**2MNQTPUV ONNs   =H9H5Hc                   SSK Jn  SSK Jn  UR                  SS5      nUR                  R                  5       nU R                  [        S5         UR                  UR                  5       5      nSSS5        UR                  R                  W5      nUR                  5         UR                  5         UR                  5         g! , (       d  f       NZ= f)zY
Flattening the score will leave StaffGroup spanners with parts no longer in the stream.
r   r  musicxmlr  r  znot well-formedN)r  r  ra  r  m21ToXmlr   assertWarnsRegexWarningfromGeneralObjectflattenr  scorePreliminariesparseFlatScorer8   )r6   r  ra  r  r  r  r  s          r)   testJoinPartStaffsFTest.testJoinPartStaffsF  s     	#$ll.2 557""7,=>''6C ? ,,S1


 ?>s    C
Cc                `   SSK Jn  SSK Jn  UR                  S5      nUR	                  S5      nU R                  SUR                  5        UR                  R                  U5      nUR                  5         UR                  5         U R                  [        UR                  5       5      S5        g)z4
A derived score should still have joinable groups.
r   r  r`  zdemos/two-partsr<   r'  N)r  r  ra  r  r   assertInclassesrb  r  rg  parsePartlikeScorer  rN   r1   )r6   r  ra  rY   r  r  s         r)   testJoinPartStaffsGTest.testJoinPartStaffsG+  s     	#$LL*+YYq\grzz*,,R0

R..0115r+   c                |   SSK Jn  [        R                  " [        R                  " 5       5      n[        R                  " [        R                  " 5       5      n[        R                  " [        R                  " 5       5      n[        X#/5      n[        X$/5      n[        R                  " X#XEU/5      nUR                  R                  U5      nUR                  5         U R                  [        5         UR                  5         U R                  UR                  5       / 5        SSS5        g! , (       d  f       g= f)z
Overlapping PartStaffs cannot be guaranteed to export correctly,
so they fall back to the old export paradigm (no joinable groups).
r   r`  N)r  ra  r   r?   rH   r
   r'  rb  r  rg  assertWarnsr   rn  r  r1   )	r6   ra  r)  r*  ps3sg1sg2rY   r  s	            r)   testJoinPartStaffsHTest.testJoinPartStaffsH:  s    
 	%v~~/0v~~/0v~~/0#$#$LL#Cc23,,Q/
o.!!#R..0"5 /..s   31D--
D;c                    SSK Jn  SSKJn  UR	                  S5      nU" U5      nUR	                  5         UR	                  5         g)zt
Regression test for side effects on the stream passed to ScoreExporter
preventing it from being written out again.
r   r  r
  cpebachN)r  r  rK   r  r  )r6   r  r  br  s        r)   testJoinPartStaffsAgainTest.testJoinPartStaffsAgainN  s4    
 	#;LL#1



r+   c                "   SSK Jn  SSK Jn  SSK Jn  [        R
                  " 5       n[        R
                  " 5       nUR                  XE/5      n[        R                  " XEU/5      nXE4 H  nUR                  SUR                  S5      5        UR                  UR                  SS9S5        UR                  S	S
9  U[        R                     S   R                  UR                  S5      5        M     U R                  U5      n	U R                  [!        U	R#                  S5      5      S5        SU[        R                     R%                  5       l        U R                  U5      n	U R                  [!        U	R#                  S5      5      S5        g )Nr   r#  )meterr%  z3/1wholer      Tr1  r<   z4/1zpart/measure/attributes/timer  r  )r  r$  r~  rf   r   r?   r
   r'  rv   r   r7  r(  r8  rH   r  r  rN   r   lastr   )
r6   r$  r~  rf   r)  r*  r[   rY   rn   r  s
             r)   testMeterChangesTest.testMeterChangesZ  s4   "!   z*LL#B((BIIa,,U34OODII7I3Q7OODO)v~~q!(()<)<U)CD	  zz!}T\\*HIJAN -.FNN  ")zz!}T\\*HIJANr+   c                   SSK Jn  SSK Jn  SSK Jn  [        R
                  " UR                  S5      5      n[        R
                  " UR                  S5      5      nUR                  XE/5      n[        R                  " XdU/5      nU R                  U5      nU R                  UR                  S5      S   R                  [        UR                  5      5        g)	zE
Regression test for chord members causing too-large backup amounts.
r   )r   )defaultsr#  zC E GzD F Azpart/measure/backup/durationN)r  r   r  r$  r   r?   Chordr
   r'  r  r  r   r"   r!   divisionsPerQuarter)	r6   r   r  r$  r)  r*  r[   rY   r  s	            r)   testBackupAmountTest.testBackupAmountr  s     	"$"u{{734u{{734z*LL"3(zz!}LL78;@@,,-	
r+   c                   SSK Jn  SSK Jn  SSK Jn  [        R
                  " UR                  SS9UR                  SS9/5      nUR                  UR                  S	S95        [        R
                  " UR                  SS9UR                  SS9/5      nUR                  UR                  S	S95        [        R                  " U5      n[        R                  " U5      nUR                  Xg/5      n[        R                  " XU/5      n	U R                  U	5      n
U R                  U
R                  S
5       Vs/ s H  oR                  S5      PM     snSS/5        gs  snf )z2
Regression test for losing forward repeat marks.
r   )barr#  r%  start)rg   r  r  endz	.//repeatrg   rh   backwardN)r  r  r$  rf   r   rH   Repeatr(  
storeAtEndr?   r
   r'  r  r  r   r   )r6   r  r$  rf   	measureRH	measureLHr)  r*  r[   rY   r  rs               r)   testForwardRepeatMarksTest.testForwardRepeatMarks  s'    	 " NNZZ'Z*DII7I,CDF	SZZ%Z89NNZZ'Z*DII7I,CDF	SZZ%Z89y)y)z*LL"3(zz!})-k)BC)BAUU;)BC
#	
Cs   8Er   N)r   r   r   r   r  r  r   r-  r;  r>  rT  r]  ri  ro  rv  r{  r  r  r  r   r   r+   r)   r   r   R  sS    :"LB),)26ApW4(66(
O0
&
r+   r   __main__)r   r   r$   r   r   z	list[str])"__doc__
__future__r   typingrI   unittestrU   xml.etree.ElementTreer   r   r   music21.common.miscr   music21.common.typesr   music21.keyr	   music21.layoutr
   music21.meterr   r  r   music21.musicxmlr   music21.musicxml.xmlObjectsr   r   r*   r-   TestCaser   r   mainTestr   r+   r)   <module>r     s    #    > > + + $ % '  $ P8D8D8D 8Dvr? r?jK
8 K
\
 zT r+   