
    rh                      S r SSKJr  SSKrSSKrSSKrSSKrSSK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  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  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"  \RF                  (       a  SSK$r$\RJ                  " S5      r&SS0r'/ SQr( " S  S!\RR                  5      r* " S" S#\RV                  5      r, " S$ S%\,5      r- " S& S'5      r. " S( S)\.5      r/ " S* S+\.5      r0 " S, S-\.5      r1 " S. S/\RV                  5      r2 " S0 S1\RV                  5      r3 " S2 S3\35      r4 " S4 S5\35      r5 " S6 S7\35      r6 " S8 S9\RV                  5      r7 " S: S;\RV                  5      r8 " S< S=5      r9S> r:SNS? jr;S@ r< " SA SB\Rz                  5      r> " SC SD\Rz                  5      r? " SE SF\Rz                  5      r@ " SG SH\Rz                  5      rA " SI SJ\R                  5      rC " SK SL\R                  5      rD\ESM:X  a  SSK	r	\	R                  " \C5        gg)Oa  
music21.humdrum.spineParser is a collection of utilities for changing
native humdrum code into music21 streams.  Most music21 users will
simply want to call:

>>> #_DOCS_SHOW myFile = converter.parse('myFile.krn')
>>> #_DOCS_SHOW myFile.show()

The methods in here are documented for developers wishing to expand
music21's ability to parse humdrum.

SpineParsing consists of several steps.

* The data file is read in and all events are sliced horizontally (EventCollections)
    and vertically (Protospines)
* Protospines are parsed into HumdrumSpines by following Spine Path Indicators
    (:samp:`*^` and :samp:`*v` especially)
    Protospines that separate become new Protospines with their parentSpine indicated.  Protospines
    that merge again then followed by the same Protospine as before.  This will cause problems if
    a voice re-merges with another staff, but in practice I have not
    seen a .krn file that does this and
    should be avoided in any case.
* HumdrumSpines are reclassed according to their exclusive definition.
    :samp:`**kern` becomes KernSpines, etc.
* All reclassed HumdrumSpines are filled with music21 objects in their .stream property.
    Measures are put into the spine but are empty containers.  The resulting
    HumdrumSpine.stream objects
    look like Stream.semiFlat versions in many ways.
* For HumdrumSpines with parent spines their .stream contents are then
    inserted into their parent spines with
    voice tagged as a music21 Group property.
* Lyrics and Dynamics are placed into their corresponding HumdrumSpine.stream objects
* Stream elements are moved into their measures within a Stream
* Measures are searched for elements with voice groups and Voice objects are created
    )annotationsN)articulations)bar)base)chord)clef)common)dynamics)duration)environment)exceptions21)expressions)
instrument)key)note)meter)metadata)roman)prebase)stream)tempo)tie)	testFiles)
harmparser)instrumentszhumdrum.spineParserJRPF)z*+*-*^*v*x*c                      \ rS rSrSrSrg)HumdrumExceptionZ   z:
General Exception class for problems relating to Humdrum
 N)__name__
__module____qualname____firstlineno____doc____static_attributes__r%       U/home/james-whalen/.local/lib/python3.13/site-packages/music21/humdrum/spineParser.pyr#   r#   Z   s     	r,   r#   c                  h    \ rS rSrSrSS jrS rS rSS jrS r	SS	 jr
S
 rSS jrS rSS jrSrg)HumdrumDataCollectiona   a  
A HumdrumDataCollection takes in a mandatory list where each element
is a line of humdrum data.  Together this list represents a collection
of spines.  Essentially it's the contents of a humdrum file.


Usually you will probably want to use HumdrumFile which can read
in a file directly.  This class exists in case you have your Humdrum
data in another format (database, from the web, etc.) and already
have it as a string.


You are probably better off running humdrum.parseFile("filename")
which returns a humdrum.SpineCollection directly, or even better,
converter.parse('file.krn') which will just give you a stream.Score
instead.

LIMITATIONS:
(1) Spines cannot change definition (\*\*exclusive interpretations) mid-spine.

    So if you start off with \*\*kern, the rest of the spine needs to be
    \*\*kern (actually, the first exclusive interpretation for a spine is
    used throughout)

    Note that, even though changing exclusive interpretations mid-spine
    seems to be legal by the humdrum definition, it looks like none of the
    conventional humdrum parsing tools allow for changing
    definitions mid-spine, so I don't think this limitation is a problem.
    (Craig Stuart Sapp confirmed this to me)

    The Aarden/Miller Palestrina dataset uses `\*-` followed by `\*\*kern`
    at the changes of sections thus some parsing of multiple exclusive
    interpretations in a protospine may be necessary.

(2) Split spines are assumed to be voices in a single spine staff.
Nc                   S U l         S U l        S U l        S U l        S U l        S U l        S U l        Ub  U(       d  [        S[        U5      -   5      eUc  / nO%[        U[        5      (       a  UR                  5       nXl        S U l        g )NzAdataStream is not optional, specify some lines: 
dataStream was: )	eventList	maxSpinesparsePositionInStream
fileLengthprotoSpineseventCollectionsspineCollectionr#   repr
isinstancestr
splitlines
dataStreamr   )selfr=   s     r-   __init__HumdrumDataCollection.__init__   s    %)" $#!*" $8:>z:J$K L LJ
C((#..0J$r,   c                    U R                   nUc  [        S5      eU R                  U5      u  p#USL a  U R                  U5      $ U R	                  U5      $ )z
Parse a list (dataStream) of lines into a HumdrumSpineCollection
(which contains HumdrumSpines)
and set it in self.spineCollection


if dataStream is None, look for it in self.dataStream.  If that's None too,
return an exception.

+Need a list of lines (dataStream) to parse!T)r=   r#   determineIfDataStreamIsOpusparseOpusDataCollectionsparseNonOpus)r>   r=   hasOpusdataCollectionss       r-   parseHumdrumDataCollection.parse   s[     __
"#PQQ#'#C#CJ#O d?00AA$$Z00r,   c                   [         R                  " 5       U l         SU l        SU l        U R	                  U5         U R                  U R
                  :X  d   e U R                  5         U R                  5       U l
        U R                  R                  5         U R                  5         U R                   H*  nS[        UR                  5      -   UR                   l        M,     U R                   HI  nUR                  b  M  UR                   S:X  d  M$  U R                   R#                  UR                   5        MK     U R%                  5         g! [         a    [        S5      ef = f)z8
The main parse function for non-opus data collections.
r   z<getEventListFromDataStream failed: did not parse entire filespine_Nkern)r   Scorer3   r4   parseEventListFromDataStreamr5   AssertionErrorr#   #parseProtoSpinesAndEventCollectionscreateHumdrumSpinesr8   createMusic21StreamsinsertGlobalEventsr;   idparentSpine	spineTypeinsertparseMetadata)r>   r=   	thisSpines      r-   rE   "HumdrumDataCollection.parseNonOpus   s%    lln &'"))*5	c--@@@ 	002#779113!--I"*S->">I .--I$$,1D1D1N""9#3#34 . 	  	c"#abb	cs   E E!c                    Uc  U R                   n/ n/ n[        U5       Hs  u  pEUR                  5       n[        R                  " SU5      (       a  UR                  U5        MD  [        R                  " SU5      (       d  Mb  UR                  U5        Mu     [        U5      S:  a  gUS   US   :  a  OUR                  [        U5      5        / n[        [        U5      5       H{  nUS:X  a  X4   nUR                  USUS-    5        M&  U[        U5      S-
  :X  a  X4S-
     S-   nUR                  XS 5        MW  X4S-
     S-   nX4   S-   nUR                  XU 5        M}     [        U5      S:  a  S	U4$ [        S
5      e)a  
Some Humdrum files contain multiple pieces in one file
which are better represented as :class:`~music21.stream.Opus`
file containing multiple scores.

This method examines that dataStream (or self.dataStream) and
if it only has a single piece then it returns (False, None).

If it has multiple pieces, it returns True and a list of dataStreams.

>>> from pprint import pprint as pp

>>> mps = humdrum.testFiles.multipartSanctus
>>> hdc = humdrum.spineParser.HumdrumDataCollection(mps)
>>> (hasOpus, dataCollections) = hdc.determineIfDataStreamIsOpus()
>>> hasOpus
True
>>> pp(dataCollections)
[['!!!COM: Palestrina, Giovanni Perluigi da',
  '**kern\t**kern\t**kern\t**kern',
  '*Ibass\t*Itenor\t*Icalto\t*Icant',
  '!Bassus\t!Tenor\t!Altus\t!Cantus',
  '*clefF4\t*clefGv2\t*clefG2\t*clefG2',
  '*M4/2\t*M4/2\t*M4/2\t*M4/2',
  '=1\t=1\t=1\t=1',
  '0r\t0r\t1g\t1r',
  '.\t.\t1a\t1cc',
  '=2\t=2\t=2\t=2',
  '0r\t0r\t1g\t1dd',
  '.\t.\t1r\t1cc',
  '*-\t*-\t*-\t*-'],
 ['!! Pleni',
  '**kern\t**kern\t**kern',
  '*Ibass\t*Itenor\t*Icalto',
  '*clefF4\t*clefGv2\t*clefG2',
  '*M4/2\t*M4/2\t*M4/2',
  '=3\t=3\t=3',
  '1G\t1r\t0r',
  '1A\t1c\t.',
  '=4\t=4\t=4',
  '1B\t1d\t1r',
  '1c\t1e\t1g',
  '*-\t*-\t*-'],
 ['!! Hosanna',
  '**kern\t**kern\t**kern\t**kern',
  '*Ibass\t*Itenor\t*Icalto\t*Icant',
  '*clefF4\t*clefGv2\t*clefG2\t*clefG2',
  '*M3/2\t*M3/2\t*M3/2\t*M3/2',
  '=5\t=5\t=5\t=5',
  '1r\t1r\t1g\t1r',
  '2r\t2r\t[2a\t[2cc',
  '=5\t=5\t=5\t=5',
  '1r\t1r\t2a]\t2cc]',
  '.\t.\t2f\t1dd',
  '2r\t2r\t2g\t.',
  '*-\t*-\t*-\t*-']]
Nz^(\*-\t)*\*-$z^(\*\*\w+\t)*\*\*\w+$   )FNr      TzMalformed humdrum data: possibly multiple **tags without closing information. Or a *tandem tag accidentally encoded as a **spine tag.)	r=   	enumeraterstripresearchappendlenranger#   )	r>   r=   startPositionsendPositionsilinerG   endPosstartPoss	            r-   rC   1HumdrumDataCollection.determineIfDataStreamIsOpus   s   v J ,GA;;=Dyy)400##A&3T::%%a( - ~" nR00 J0s<()AAv%&&z+6A:'>?c,'!++'A.2&&z)'<= (A.2%1,&&z6'BC * !#/**";< <r,   c                   [         R                  " 5       n[        U5       Hg  u  p4[        U5      nUR	                  5         UR                   nS[        US-   5      -   Ul        US-   UR                  l        UR                  U5        Mi     U(       aC  [        R                  " UR                  S   R                  5      Ul        SUR                  l        X l         U$ )a  
Take a dataCollection from `determineIfDataStreamIsOpus`
and set self.stream to be an Opus instead.

>>> mps = humdrum.testFiles.multipartSanctus
>>> hdc = humdrum.spineParser.HumdrumDataCollection(mps)
>>> (hasOpus, dataCollections) = hdc.determineIfDataStreamIsOpus()
>>> if hasOpus is True:
...     op = hdc.parseOpusDataCollections(dataCollections)
...     print(len(op.scores))
...     for sc in op.scores:
...        print(sc)
3
<music21.stream.Score section_1>
<music21.stream.Score section_2>
<music21.stream.Score section_3>
section_r^   r   )r   Opusr_   r/   rH   r;   rT   r   numberrc   copydeepcopyscores)r>   rG   opusrh   dchdcscs          r-   rD   .HumdrumDataCollection.parseOpusDataCollections4  s    & {{}/EA'+CIIKBQU+BE!"QBKKKKO 0  MM$++a.*A*ABDM#$DMM r,   c                   Uc  U R                   nUc  [        S5      eSU l        / U l        U GH  nUR	                  5       nUS:X  a  M  [
        R                  " SU5      (       a0  U R                  R                  [        U R                  U5      5        O[
        R                  " SU5      (       a0  U R                  R                  [        U R                  U5      5        OV[        U R                  U5      n[        U R                  UR                  5      U l        U R                  R                  U5        U =R                  S-  sl        GM"     U R                  U l        U R                  $ )a  
Sets self.eventList from a dataStream (that is, a
list of lines).  It sets self.maxSpines to the
largest number of spine events found on any line
in the file.

It sets self.fileLength to the number of lines (excluding
totally blank lines) in the file.

The difference between the dataStream and self.eventList
are the following:

    * Blank lines are skipped.
    * !!! lines become :class:`~music21.humdrum.spineParser.GlobalReferenceLine` objects
    * !! lines become :class:`~music21.humdrum.spineParser.GlobalCommentLine` objects
    * all other lines become :class:`~music21.humdrum.spineParser.SpineLine` objects

Returns eventList in addition to setting it as self.eventList.


>>> eventString = ('!!! COM: Beethoven, Ludwig van\n' +
...                '!! Not really a piece by Beethoven\n' +
...                '**kern\t**dynam\n' +
...                'C4\tpp\n' +
...                'D8\t.\n')
>>> hdc = humdrum.spineParser.HumdrumDataCollection(eventString)
>>> hdc.maxSpines = 2
>>> eList = hdc.parseEventListFromDataStream()
>>> eList is hdc.eventList
True
>>> for e in eList:
...     print(e)
<music21.humdrum.spineParser.GlobalReferenceLine object at 0x...>
<music21.humdrum.spineParser.GlobalCommentLine object at 0x...>
<music21.humdrum.spineParser.SpineLine object at 0x...>
<music21.humdrum.spineParser.SpineLine object at 0x...>
<music21.humdrum.spineParser.SpineLine object at 0x...>
>>> print(eList[0].value)
Beethoven, Ludwig van
>>> print(eList[3].spineData)
['C4', 'pp']
rB   r    z!!!!!r^   )r=   r#   r4   r2   r`   ra   matchrc   GlobalReferenceLineGlobalCommentLine	SpineLinemaxr3   	numSpinesr5   )r>   r=   ri   thisLines       r-   rN   2HumdrumDataCollection.parseEventListFromDataStreamW  s!   V J!&'TUU%&"D;;=Drz&$''%%&9$:T:TVZ&[\%&&%%&78R8RTX&YZ$T%?%?F!$T^^X5G5G!H%%h/&&!+&&  44~~r,   c                  ^ ^^ T R                   (       d  T R                  5         / n/ m/ mUUU 4S jn[        T R                  5       Hm  n/ m[        T R                  5       H5  nUS:X  a$  TR                  [        T R                  5      5        U" XC5        M7     UR                  [        T5      5        Mo     UT l        TT l	        UT4$ )a	  
Run after
:meth:`~music21.humdrum.spineParser.HumdrumDataCollection.parseEventListFromDataStream()`
to take self.eventList and slice it horizontally
to get self.eventCollections, which is a list of
EventCollection objects, or things that happen simultaneously.

And, more importantly, this method slices self.eventList
vertically to get self.protoSpines which is a list
of ProtoSpines, that is a vertical slice of everything that
happens in a column, regardless of spine-path indicators.

EventCollection objects store global events at the position.
ProtoSpines do not.

So self.eventCollections and self.protoSpines can each be
thought of as a two-dimensional sheet of cells, but where
the first index of the former is the vertical position in
the dataStream and the first index of the latter is the
horizontal position in the dataStream.  The contents of
each cell is a SpineEvent object or None (if there's no
data at that point).  Even '.' (continuation events) get
translated into SpineEvent objects.

Calls :meth:`~music21.humdrum.spineParser.parseEventListFromDataStream`
if it hasn't already been called.

returns a tuple of protoSpines and eventCollections in addition to
setting it in the calling object.


>>> eventString = ('!!!COM: Beethoven, Ludwig van\n' +
...                '!! Not really a piece by Beethoven\n' +
...                '**kern\t**dynam\n' +
...                'C4\tpp\n' +
...                'D8\t.\n')
>>> hdc = humdrum.spineParser.HumdrumDataCollection(eventString)
>>> hdc.maxSpines = 2
>>> hdc.fileLength = 5
>>> protoSpines, eventCollections = hdc.parseProtoSpinesAndEventCollections()
>>> protoSpines is hdc.protoSpines
True
>>> eventCollections is hdc.eventCollections
True

Looking at individual slices is unlikely to tell you much.

>>> for thisSlice in eventCollections:
...    print(thisSlice)
<music21.humdrum.spineParser.EventCollection object at 0x...>
<music21.humdrum.spineParser.EventCollection object at 0x...>
<music21.humdrum.spineParser.EventCollection object at 0x...>
<music21.humdrum.spineParser.EventCollection object at 0x...>
<music21.humdrum.spineParser.EventCollection object at 0x...>

>>> for thisSlice in protoSpines:
...    print(thisSlice)
<music21.humdrum.spineParser.ProtoSpine object at 0x...>
<music21.humdrum.spineParser.ProtoSpine object at 0x...>

But looking at the individual slices is revealing:

>>> eventCollections[4].getAllOccurring()
[<music21.humdrum.spineParser.SpineEvent D8>, <music21.humdrum.spineParser.SpineEvent pp>]
c                R  > TU    nTR                   U    R                  SL Ga'  [        TR                   U    R                  5      U:  a  [	        TR                   U    R                  U   5      nXl        Xl        UR                  [        ;   a  SUl	        TR                  U5        UR                  X5        UR                  S:X  aH  U S:  aA  TU S-
     R                  U   nUb(  UR                  UTU S-
     R                  U5      5        g g g g [	        S 5      nXl        Xl        UR                  X5        TR                  S 5        g US:X  a  UR                  TR                   U    5        [	        S 5      nXl        Xl        UR                  X5        TR                  S 5        g )NT.r   r^   )r2   isSpineLinerd   	spineData
SpineEventpositionprotoSpineIdcontentsspinePathIndicatorsspinePathDatarc   addSpineEventeventsaddLastSpineEventgetSpineOccurringaddGlobalEvent)rh   jthisEventCollection	thisEvent	lastEventprotoSpineEventListreturnEventCollectionsr>   s        r-   	doOneCellLHumdrumDataCollection.parseProtoSpinesAndEventCollections.<locals>.doOneCell  s   "8"; ~~a ,,4t~~a(223a7 !+4>>!+<+F+Fq+I JI)*&-.* ))-@@<@+9'..y9'55aC ))S0QU$:1q5$A$H$H$K	$0/AA ! 6q1u = O OPQ R 1 6;0 !+4 0I)*&-.*'55aC'..t46'66t~~a7HI&t,	%&")*&#11!?#**40r,   r   )
r2   rN   re   r3   r5   rc   EventCollection
ProtoSpiner6   r7   )r>   returnProtoSpinesr   r   rh   r   r   s   `    @@r-   rP   9HumdrumDataCollection.parseProtoSpinesAndEventCollections  s    D ~~--/ !# '	1T t~~&A"$4??+6*11/$..2QR!	 , $$Z0C%DE ' - 6!#9::r,   c           	        Ub  Uc  U R                   nU R                  n[        U5      n[        R                  " S 5      n[        5       n[        U R                  5       GH  nX&   n[        U5       H\  nX   R                  U   n	U	c  M  XH   n
U
c  UR                  5       n
Xjl
        XU'   U
R                  U	5        U
R                  U	l        M^     UR                  SL a  M  [        R                  " S 5      nSnSn[        U5       GH=  nX   R                  U   n	XH   n
U	c  U
b  UR                  U
5        M2  U	c  M7  U	R                  S:X  a  Xjl        MO  U	R                  S:X  a  UR                  ["        R$                  S9nUS-   Ul
        Xl        SUl        UR                  ["        R$                  S9nUS-   Ul
        Xl        Xjl        U
R*                  R                  U5        U
R*                  R                  U5        X4U
R,                  U'   UR                  U5        UR                  U5        GM1  U	R                  S	:X  a  USL a%  U
R&                  b  U
R&                  nOSnXjl        GMk  Xjl        U
R&                  b  UR                  U
R&                  5        OLUSLa  UR                  U5        O5UR                  ["        R.                  S9nUUl
        UR                  U5        SnGM  U	R                  S
:X  a1  USL a  U
nGM  UR                  U
5        UR                  U5        SnGM,  UR                  U
5        GM@     USLa  [1        SSU SUR2                   S3-   5      eUnGM     U$ )a  
Takes the data from the object's protoSpines and eventCollections
and returns a :class:`~music21.humdrum.spineParser.SpineCollection`
object that contains HumdrumSpine() objects.

A HumdrumSpine object differs from a ProtoSpine in that it follows
spinePathData -- a ProtoSpine records all data in a given tab
position, and thus might consist of data from several
spines that move around.  HumdrumSpines are smart enough not to
have this limitation.

When we check for spinePathData we look for the following spine
path indicators (from HumdrumDoc)::

    *+    add a new spine (to the right of the current spine)
    *-    terminate a current spine
    *^    split a spine (into two)
    *v    join (two or more) spines into one
    *x    exchange the position of two spines
    *     do nothing
c                     g Nr%   r%   r,   r-   <lambda>;HumdrumDataCollection.createHumdrumSpines.<locals>.<lambda>=      dr,   Fc                     g r   r%   r%   r,   r-   r   r   ^  r   r,   r   r   streamClassr^   Tr   r    z4ProtoSpine found with unpaired exchange instruction zat line z [])r6   r7   rd   r	   defaultlistSpineCollectionre   r5   r2   addSpineinsertPointrc   rT   r   r   r   endingPositionr   VoicerU   isFirstVoicechildSpineschildSpineInsertPointsPartr#   r   )r>   r6   r7   r3   currentSpineListr8   rh   r   r   r   currentSpinenewSpineListmergerActiveexchangeActive	newSpine1	newSpine2ss                    r-   rQ   )HumdrumDataCollection.createHumdrumSpines   ss   , "2":**K#44$	 "--l;)+ t'A"2"59%'N44Q7	$/2' $3#;#;#=L/0,*6Q'##I.)5	&! && #00E9 "--l;L L"N9%'N44Q7	/2$)A ''5&''4/23/''4/ / 8 8V\\ 8 RI,-EI),8)-1I* / 8 8V\\ 8 RI,-EI),8)23/ ,,33I> ,,33I>>G=SL77: ''	2 ''	2''4/ $u,'33?+7+C+CL+/L673 783'33?(//0H0HI)5(//= !0 8 8V[[ 8 QA,-AM(//2',''4/%.)5 %++L9$++N;). ''5I &L U*&']+3A3b9L9S9S8TTU)V(W X X+S (V r,   c                R   U R                   R                  5       nU R                  n[        U5      nSn/ n/ n[	        U5       H  u  pxUR
                  SL a  US-  nSn	Sn
[        US-   U5       H+  nX;   d  M
  X   S   n	X   S   S   R                  S-
  U-   n
  O   UR                  SL a!  [        UR                  UR                  5      nO[        UR                  5      nXl        U	c  UR                  U5        M  X4nUR                  U5        M  SnM     U H   u  pU R                  R                  X5        M"     U(       a  U R                  R!                  5         U H  nU R                  R#                  U5        M      U(       a  U R                  R!                  5         gg)a  
Insert the Global Events (GlobalReferenceLines and GlobalCommentLines) into an appropriate
place in the outer Stream.

Run after self.spineCollection.createMusic21Streams().
Is run automatically by self.parse().
uses self.spineCollection.getOffsetsAndPrioritiesByPosition()
r   Fr^   N(   T)r8   !getOffsetsAndPrioritiesByPositionr2   rd   r_   r   re   priorityisReferenceGlobalReferencecodevalueGlobalCommentrc   r   
coreInsertcoreElementsChanged
coreAppend)r>   positionDictr2   maxEventListnumberOfGlobalEventsInARow
insertList
appendListrh   eventinsertOffsetinsertPriorityr   elinsertTupleoffsets                  r-   rS   (HumdrumDataCollection.insertGlobalEvents  s    ++MMONN	9~%&"

!),HA  E)*a/*#!"q1ul3A('3q'9*6/!*<Q*?*H*H,.+/,F+G  4 $$,(U[[AB&u{{3B,'%%b)#/"4K%%k2 ./*5 -8 %JFKK""6. % KK++-BKK""2&  KK++- r,   c                    Uc  U R                   n[        R                  " 5       nX!l        / nU[            H%  nUR	                  U5        UR                  U5        M'     U(       a  UR                  USS9  gg)z(
Create a metadata object for the file.
NT)recurse)r   r   Metadatar   updateMetadatarc   remove)r>   r   md
grToRemovegrs        r-   rX   #HumdrumDataCollection.parseMetadata   so     9A 

O$Bb!b! % HHZH. r,   )	r=   r7   r2   r5   r3   r4   r6   r8   r   r   )NN)r&   r'   r(   r)   r*   r?   rH   rE   rC   rD   rN   rP   rQ   rS   rX   r+   r%   r,   r-   r/   r/   a   sD    #J*1*:d<L!FAFD;LL\6.d/r,   r/   c                  H   ^  \ rS rSrSrSSU 4S jjjrSSS jjrS rSrU =r	$ )	HumdrumFilei  zp
A HumdrumFile is a HumdrumDataCollection which takes
as a mandatory argument a filename to be opened and read.
c                .   > [         TU ]  5         Xl        g r   )superr?   filename)r>   r   	__class__s     r-   r?   HumdrumFile.__init__  s     r,   c                    Uc  U R                   nUc  [        S5      e[        USS9 nU R                  U5      U l        S S S 5        g ! , (       d  f       g = f)Nz-Cannot parse humdrum file without a filename!zlatin-1)encoding)r   r#   openparseFileHandler2   )r>   r   humFHs      r-   parseFilenameHumdrumFile.parseFilename  sM    }}H"#RSS(Y/5!11%8DN 0//s   A
Ac                f    / nU H  nUR                  U5        M     X l        U R                  5       $ )zG
takes a fileHandle and returns a HumdrumCollection by calling parse()
)rc   r=   rH   )r>   
fileHandlespineDataCollectionri   s       r-   r   HumdrumFile.parseFileHandle&  s3     !D&&t, -zz|r,   )r=   r2   r   r   )r   zstr | pathlib.Path | None)
r&   r'   r(   r)   r*   r?   r   r   r+   __classcell__r   s   @r-   r   r     s!    
! !9 r,   r   c                  (    \ rS rSrSrSrSrSrSrSr	g)HumdrumLinei1  a  
HumdrumLine is a dummy class for subclassing
:class:`~music21.humdrum.spineParser.SpineLine`,
:class:`~music21.humdrum.spineParser.GlobalCommentLine`, and
:class:`~music21.humdrum.spineParser.GlobalReferenceLine` classes
all of which represent one horizontal line of
text in a :class:`~music21.humdrum.spineParser.HumdrumDataCollection`
that is aware of its
position in the file.

See the documentation for the specific classes mentioned above
for more details.
Fr   r%   N)
r&   r'   r(   r)   r*   r   isGlobal	isCommentr   r+   r%   r,   r-   r   r   1  s     KHIHr,   r   c                  6    \ rS rSrSrSrSrSrSrS	S
S jjr	Sr
g)r   iE  a  
A SpineLine is any horizontal line of a Humdrum file that
contains one or more spine elements (separated by tabs)
and not Global elements.

Takes in a position (line number in file, excluding blank lines)
and a string of contents.


>>> hsl = humdrum.spineParser.SpineLine(
...         position = 7, contents='C4\t!local comment\t*M[4/4]\t.\n')
>>> hsl.position
7
>>> hsl.numSpines
4
>>> hsl.spineData
['C4', '!local comment', '*M[4/4]', '.']
r   rz   Tc                    Xl         UR                  5       n[        R                  " SU5      n[	        U5      U l        X l        X0l        g )Nz	+)r   r`   ra   splitrd   r   r   r   )r>   r   r   
returnLists       r-   r?   SpineLine.__init__]  s8     ??$XXeX.
Z #r,   )r   r   r   r   Nr   rz   r   intr   r;   )r&   r'   r(   r)   r*   r   r   r   r   r?   r+   r%   r,   r-   r   r   E  s'    $ HHKI$ $r,   r   c                  B    \ rS rSrSrSrSrSrSrSr	Sr
SrS
SS jjrSrg	)r}   if  a  
A GlobalReferenceLine is a type of HumdrumLine that contains
information about the humdrum document.

In humdrum it is represented by three exclamation points
followed by non-whitespace followed by a colon.  Examples::

    !!!COM: Stravinsky, Igor Fyodorovich
    !!!CDT: 1882/6/17/-1971/4/6
    !!!ODT: 1911//-1913//; 1947//
    !!!OPT@@RUS: Vesna svyashchennaya
    !!!OPT@FRE: Le sacre du printemps

The GlobalReferenceLine object takes two inputs::

    position   line number in the humdrum file
    contents   string of contents

And stores them as three attributes::

    position: as above
    code:     non-whitespace code (usually three letters)
    value:    its value


>>> gr = humdrum.spineParser.GlobalReferenceLine(
...        position = 20, contents = '!!!COM: Stravinsky, Igor Fyodorovich\n')
>>> gr.position
20
>>> gr.code
'COM'
>>> gr.value
'Stravinsky, Igor Fyodorovich'

TODO: add parsing of three-digit Kern comment codes into fuller metadata
r   rz   TFc                   Xl         [        R                  " SSU5      n UR                  SS5      u  pEUR	                  5       nUc  [        SSU S3-   5      e X l        X@l        XPl	        g ! [         a    [        SS	U S3-   5      ef = f)
N^!!!+rz   :r^   z/GlobalReferenceLine (!!!) found without a code z$listed; this is probably a problem!  z7GlobalReferenceLine (!!!) found without a code listed; zthis is probably a problem! )
r   ra   subr   stripr#   
IndexErrorr   r   r   )r>   r   r   	noExclaimr   r   s         r-   r?   GlobalReferenceLine.__init__  s     FF8R2		Q%OOC3MTKKME|&'X+OPXzYZ)[(\ ] ]  !	
  	Q"#\'CH:Q%O$P Q Q	Qs   9A- -B
)r   r   r   r   N)r   z!!! NUL: Noner   )r&   r'   r(   r)   r*   r   r   r   r   r   r   r   r?   r+   r%   r,   r-   r}   r}   f  s7    #H HHHKIKI r,   r}   c                  F    \ rS rSrSrSrSrSrSrSr	Sr
SrSrS
SS jjrSrg	)r~   i  a  
A GlobalCommentLine is a humdrum comment that pertains to all spines
In humdrum it is represented by two exclamation points (and usually one space)

The GlobalComment object takes two inputs and stores them as attributes::

    position (line number in the humdrum file)
    contents (string of contents)
    value    (contents minus !!)

The constructor can be passed (position, contents)
if contents begins with bangs, they are removed along with up to one space directly afterwards


>>> com1 = humdrum.spineParser.GlobalCommentLine(
...          position = 4, contents='!! this comment is global')
>>> com1.position
4
>>> com1.contents
'!! this comment is global'
>>> com1.value
'this comment is global'
r   rz   TFc                X    Xl         [        R                  " SSU5      nX l        X0l        g )N^!!+\s?rz   )r   ra   r  r   r   )r>   r   r   r   s       r-   r?   GlobalCommentLine.__init__  s#     z2x0 
r,   )r   r   r   Nr   r   )r&   r'   r(   r)   r*   r   r   r   r   r   r   r   r   r?   r+   r%   r,   r-   r~   r~     s;    . HHEHKIKI r,   r~   c                  "    \ rS rSrSrSS jrSrg)r   i  a  
A ProtoSpine is a collection of events arranged vertically.
It differs from a HumdrumSpine in that spine paths are not followed.
So ProtoSpine(1) would be everything in the 2nd column
of a Humdrum file regardless of whether the 2nd column
was at some point an independent Spine
or if it later became a split from the first spine.

See :meth:`~music21.humdrum.spineParser.parseProtoSpinesAndEventCollections`
for more details on how ProtoSpine objects are created.
Nc                    Uc  / nXl         g r   r2   )r>   r2   s     r-   r?   ProtoSpine.__init__  s    I"r,   r  r   )r&   r'   r(   r)   r*   r?   r+   r%   r,   r-   r   r     s    
#r,   r   c                      \ rS rSrSrSS\R                  4SS jjrS rS r	S r
S	 rS
 rSS jr\" \\5      rS rS rS rSS jr\" \\5      rS rS rSrg)HumdrumSpinei  ar  
A HumdrumSpine is a representation of a generic HumdrumSpine
regardless of \*\*definition after spine path indicators have
been simplified.

A HumdrumSpine is a collection of events arranged vertically that have a
connection to each other.
Each HumdrumSpine MUST have an id (numeric or string) attached to it.


>>> SE = humdrum.spineParser.SpineEvent
>>> spineEvents = [SE('**kern'), SE('c,4'), SE('d#8')]
>>> spine1Id = 5
>>> spine1 = humdrum.spineParser.HumdrumSpine(spine1Id, spineEvents)
>>> spine1.insertPoint = 5
>>> spine1.endingPosition = 6
>>> spine1.parentSpine = 3  # spine 3 is the previous spine leading to this one
>>> spine1.childSpines = [7, 8]  # the spine ends by being split into spines 7 and 8

we keep weak references to the spineCollection so that we
don't have circular references

>>> spineCollection1 = humdrum.spineParser.SpineCollection()
>>> spine1.spineCollection = spineCollection1

The spineType property searches the EventList or parentSpine to
figure out the spineType

>>> spine1.spineType
'kern'

Spines can be iterated through:

>>> for e in spine1:
...    print(e)
**kern
c,4
d#8

If you'd eventually like this spine to be converted to a class
other than :class:`~music21.stream.Stream`, pass its classname in
as the streamClass argument:

>>> spine2 = humdrum.spineParser.HumdrumSpine(streamClass=stream.Part)
>>> spine2.stream
<music21.stream.Part ...>
r   Nc                    Xl         Uc  / nU H	  nXl        M     X l        U" 5       U l        SU l        SU l        S U l        S U l        SU l        / U l	        0 U l
        SU l        SU l        SU l        S U l        S U l        S U l        S U l        g Nr   F)rT   spineIdr2   r   r   r   rU   newSpineisLastSpiner   r   parsedmeasuresMovedinsertionsDone_spineCollection
_spineTyper   	iterIndex)r>   r  r2   r   r   s        r-   r?   HumdrumSpine.__init__  s    IE#M  #!m /1KM#"# $'+r,   c                :   S[        U R                  5      -   nU R                  (       a(  US[        U R                  R                  5      -   S-   -  nU R                  (       a8  US-  nU R                   H  nU[        UR                  5      S-   -  nM      US-  nU$ )N: z [child of: r   z [parent of: r  z ])r;   rT   rU   r   )r>   representationr   s      r-   _reprInternalHumdrumSpine._reprInternal/  s    DGG,ns43C3C3F3F/GG#MMNo-N%%#add)c/1 &d"Nr,   c                :    U R                   R                  U5        g)z
add an item to this Spine
N)r2   rc   )r>   r   s     r-   rc   HumdrumSpine.append:  s     	e$r,   c                    SU l         U $ )z6
Resets the counter to 0 so that iteration is correct
r   )r  r>   s    r-   __iter__HumdrumSpine.__iter__@  s     r,   c                    U R                   [        U R                  5      :X  a  [        eU R                  U R                      nU =R                   S-  sl         U$ )z?
Returns the current event and increments the iteration index.
r^   )r  rd   r2   StopIteration)r>   r   s     r-   __next__HumdrumSpine.__next__G  sE     >>S00NN4>>2	!r,   c                B    [         R                  " U R                  5      $ r   )r	   unwrapWeakrefr  r&  s    r-   _getSpineCollection HumdrumSpine._getSpineCollectionQ  s    ##D$9$9::r,   c                :    [         R                  " U5      U l        g r   )r	   wrapWeakrefr  )r>   rw   s     r-   _setSpineCollection HumdrumSpine._setSpineCollectionT  s     & 2 22 6r,   c                    U R                   b  U R                   $ U R                   HO  n[        R                  " SUR                  5      nU(       d  M-  UR                  S5      U l         U R                   s  $    g )Nz\*\*(.*)r^   )r  r2   ra   r|   r   group)r>   r   m1s      r-   _getLocalSpineTypeHumdrumSpine._getLocalSpineTypeY  s]    ??&??"!^^	XXk9+=+=>2&(hhqkDO??*	 ,
 r,   c                F    U R                   nUb  UR                  nUb  U$ g g r   )rU   rV   )r>   rU   psSpineTypes      r-   _getParentSpineType HumdrumSpine._getParentSpineTyped  s0    &&"%//K&""r,   c                    U R                   b  U R                   $ U R                  5       nUb  Xl         U$ U R                  5       nUb  Xl         U$ [        S[	        U R
                  5      -   5      e)z8
searches the current and parent spineType for a search
z0Could not determine spineType for spine with id )r  r8  r<  r#   r;   rT   )r>   sts     r-   _getSpineTypeHumdrumSpine._getSpineTypen  sx     ??&??"((*B~"$	--/>&(OI* ,BDGL,Q R Rr,   c                    Xl         g r   )r  )r>   newSpineTypes     r-   _setSpineTypeHumdrumSpine._setSpineType  s    &r,   c                   UR                  5       n[        R                  " 5       nSUl        SnSnSnU H  n[	        U[        R
                  5      (       aT  US:w  d  U(       a!  UR                  5         UR                  U5        UnUR                  nUR                  nUS:X  a  SnMt  Mv  US:w  d  UR                  R                  S:w  a!  UR                  UR                  U-
  U5        M  UR                  U5        M     UR                  5         UR                  5         U(       a  UR                  U5        UR                  [        R                  5      nU(       a  US   n	U(       d  SU	l        UR                  S5      n
U
 HY  n[	        U[        R
                  5      (       a  M$  SUR                  ;   a  M6  U	R!                  SU5        UR#                  U5        M[     U	R$                  nUb`  U	R                  R                  UR&                  R                  :  a2  UR&                  R                  U	R                  R                  -
  U	l        U$ )a  
Takes a parsed stream and moves the elements inside the
measures.  Works with pickup measures, etc. Does not
automatically create ties, etc.

Why not just use Stream.makeMeasures()? because
humdrum measures contain extra information about barlines
etc. and pickups are explicitly defined.

>>> s1 = stream.Stream()
>>> s1.append(meter.TimeSignature('2/4'))
>>> m1 = stream.Measure()
>>> m1.number = 1
>>> s1.append(m1)
>>> s1.append(note.Note('C4', type='quarter'))
>>> m2 = stream.Measure()
>>> m2.number = 2
>>> s1.append(m2)
>>> s1.append(note.Note('D4', type='half'))
>>> s1.show('text')
{0.0} <music21.stream.Measure 1 offset=0.0>
<BLANKLINE>
{0.0} <music21.meter.TimeSignature 2/4>
{0.0} <music21.note.Note C>
{1.0} <music21.stream.Measure 2 offset=1.0>
<BLANKLINE>
{1.0} <music21.note.Note D>

>>> hds = humdrum.spineParser.HumdrumSpine()
>>> s2 = hds.moveElementsIntoMeasures(s1)
>>> s2.show('text')
{0.0} <music21.stream.Measure 1 offset=0.0>
    {0.0} <music21.meter.TimeSignature 2/4>
    {0.0} <music21.note.Note C>
{1.0} <music21.stream.Measure 2 offset=1.0>
    {0.0} <music21.note.Note D>

The first measure is correctly identified as a pickup!

>>> s2.measure(1).paddingLeft
1.0
r   Fr^   T
MiscTandem)r   r   Measurerp   r:   Streamr   r   r   r   quarterLengthr   rc   getElementsByClassgetElementsByOffsetclassesrW   r   timeSignaturebarDurationpaddingLeft)r>   streamIn	streamOutcurrentMeasurecurrentMeasureNumbercurrentMeasureOffsethasMeasureOner   measureElementsr7  beginningStuffm1TimeSignatures               r-   moveElementsIntoMeasures%HumdrumSpine.moveElementsIntoMeasures  s   X &&(	) !  B"fmm,,'1,"668((8!#')yy$')yy$'1,$(M - (1,0I0IQ0N"--bii:N.NPRS ((,# & 	**,%%'^, $66v~~F #B 	&::1=N$b&--00!RZZ/IIa$$$R( % !..O*;;,,/J/J/X/XX&5&A&A&O&O(*(A(A'BBN r,   c                "   [        S5      nU R                   H  n[        UR                  5      nSnUS:X  a  OUR	                  S5      (       a  U[
        ;  a  [        U5      nOmUR	                  S5      (       a  [        X15      nUnOIUR	                  S5      (       a  [        U5      nO'[        R                  " U5      nUR                  Ul        Uc  M  U R                  R                  U5        M     U R                  R                  5         g)z
Dummy method that pushes all these objects to HumdrumSpine.stream
as ElementWrappers.  Should be overridden in
specific Spine subclasses.
=0Nr   r!   =!)hdStringToMeasurer2   r;   r   
startswithr   rG  SpineCommentr   ElementWrapperr   humdrumPositionr   r   r   )r>   lastContainerr   eventC
thisObjects        r-   rH   HumdrumSpine.parse  s     *$/^^E(FJ}""3''!44!+F!3J""3'' 1& H*
""3'')&1
!007
-2^^
*%&&z2' $( 	'')r,   )r  r  r   r   r   r2   rT   r   r  r   r  r  r  r  rU   r  r   r  r   r   )r&   r'   r(   r)   r*   r   rI  r?   r!  rc   r'  r+  r/  r3  propertyr8   r8  r<  r@  rD  rV   rZ  rH   r+   r%   r,   r-   r  r    sw    .` '(4V]] 6	%;7 24GHO	R(' 6I_B*r,   r  c                  l   ^  \ rS rSrSrSS\R                  4SU 4S jjjrS rS r	S r
S	 rS
 rSrU =r$ )	KernSpinei  z
A KernSpine is a type of humdrum spine with the \*\*kern
attribute set and thus events are processed as if they
are kern notes
r   Nc                z   > [         TU ]  XU5        S U l        S U l        S U l        SU l        SU l        SU l        g )Nr           )r   r?   re  inTupletlastNotecurrentBeamNumberscurrentTupletDurationdesiredTupletDuration)r>   r  r2   r   r   s       r-   r?   KernSpine.__init__  s@    [9!"#%("%("r,   c                   [        S5      U l        SU l        S U l        SU l        SU l        SU l        U R                   GH  n UR                  nS nUS:X  a  OUR                  S5      (       a  [        U5      nUb  UnOUR                  S5      (       a'  [        X R                  5      U l        U R                  nO]UR                  S5      (       a  [        U5      nUR                  S	:X  a  S nO)S
U;   a  U R                  U5      nOU R                  U5      nUb@  UR                  Ul        UR                  Ul        U R$                  R'                  U5        GM  GM!     U R$                  R7                  5         g ! [(         ay  nSS Kn[,        R/                  SUR                  < SUR                  < SUR0                   SU 35        UR3                  5       n[,        R5                  SU 35         S nAGM  S nAff = f)Nr]  Fr   rn  r   r!   r^  r_  rz   r  zError in parsing event (z) at position z for spine r  zTraceback for the exception: 
)r`  re  ro  rp  rq  rr  rs  r2   r   ra  kernTandemToObjectrb  commentprocessChordEventprocessNoteEventr   rd  r   r   r   	Exception	tracebackenvironLocalwarnr  
format_exc
printDebugr   )r>   r   rf  rg  
tempObjecter{  tbs           r-   rH   KernSpine.parse  s   .t4"#%("%("^^E"P!
S=&&s++!3F!;J!-%/
&&s++):6CUCU)VD&!%!3!3J&&s++!-f!5J!))R/%)
F]!%!7!7!?J!%!6!6v!>J)16J.*/..J'KK**:6 *1 $N 	'')  P !!.u~~.@ A##(>>"4 5!!&r!6
 ))+''*I"(NOOPs   DE>>
HA-G<<Hc                l    [        U5      nU R                  U5        U R                  U5        X l        U$ )z
similar to hdStringToNote, this method processes a string representing a single
note, but stores information about current beam and tuplet state.
)hdStringToNotesetBeamsForNotesetTupletTypeForNoterp  )r>   rf  	eventNotes      r-   ry  KernSpine.processNoteEventI  s5    
 #6*	Y'!!),!r,   c                .   UR                  5       n/ nU H  n[        U5      nUR                  U5        M!     [        R                  " X3S   R
                  S9nUS   R                  Ul        U R                  U5        U R                  U5        X`l	        U$ )zn
Process a single chord event

Like processNoteEvent, stores information about current beam and tuplet state.
r]   )beamsr   )
r   r  rc   r   Chordr  r   r  r  rp  )r>   rf  notesToProcess
chordNotesnoteToProcessthisNote
eventChords          r-   rx  KernSpine.processChordEventV  s      
+M%m4Hh' , [[b>3G3GH
(m44
Z(!!*-"r,   c                   [        US5      (       d  gU R                  S:w  aS  UR                  R                  (       d8  [	        U R                  5       H  nUR                  R                  S5        M      gUR                  R                  (       at  UR                  R                  S   R                  S:X  a  SU l        gUR                  R                   H*  nUR                  S:w  d  M  U =R                  S-  sl        M,     gg)z
sets the beams for a Note (or Chord) given self.currentBeamNumbers
and updates self.currentBeamNumbers based on stop beams.

Safe enough to use on elements such as rests that don't have beam info.
r  Nr   continuestopr^   )hasattrrq  r  	beamsListre   rc   type)r>   nunused_counterthisBeams       r-   r  KernSpine.setBeamsForNotek  s     q'""""a'0A0A"'(?(?"@z* #AWWww  #((F2*+' ! 1 1H}}.//14/ !2	 r,   c                   UR                   nUR                   R                  nU R                  SL aV  U(       aO  SU l        US   R                  5       U l        UR
                  U l        SUR                   R                  S   l        g U R                  SL aE  U(       d>  SU l        SU l        SU l        SU R                  R                   R                  S   l        g U R                  SL a  U =R                  UR
                  -  sl        U R                  U R                  :X  d=  U R                  U R                  -  [        U R                  U R                  -  5      :X  a   SUS   l        SU l        SU l        SU l        g g g )NFTr   startrn  r  )
r   tupletsro  totalTupletLengthrs  rJ  rr  r  rp  r   )r>   r  nDurnTupletss       r-   r  KernSpine.setTupletTypeForNote  sJ   zz::%%==E!h DM)1!)F)F)HD&)-););D&)0AJJq!&]]d"8!DM),D&),D&5;DMM""**1-2]]d"&&$*<*<<&**d.H.HH 22T5O5OOTW22T5O5OOUQ Q#)  %-0*-0*Q #r,   )rq  rr  rs  ro  re  rp  ri  )r&   r'   r(   r)   r*   r   rI  r?   rH   ry  rx  r  r  r+   r   r   s   @r-   rl  rl    s>     '(4V]] ) )/*d*5*1 1r,   rl  c                      \ rS rSrSrS rSrg)
DynamSpinei  z
A DynamSpine is a type of humdrum spine with the \*\*dynam
attribute set and thus events are processed as if they
are dynamics.
c                r   S nU R                    GH  n[        UR                  5      nS nUS:X  a  GOUR                  S5      (       a  U[        ;  a  [        U5      nOUR                  S5      (       a:  Ub+  UR                  5         U R                  R                  U5        [        U5      nOUR                  S5      (       a  [        U5      nUR                  S:X  a  S nOnUR                  S5      (       a  [        R                  " 5       nOBUR                  S5      (       a  [        R                  " 5       nO[        R                  " U5      nUc  GMG  UR                   Ul        Uc  U R                  R                  U5        GMy  UR                  U5        GM     U R                  R                  5         g )Nr   r!   r^  r_  rz   <>)r2   r;   r   ra  r   rG  r   r   r   r`  rb  rw  r
   
Diminuendo	CrescendoDynamicr   rd  )r>   thisContainerr   rf  rg  s        r-   rH   DynamSpine.parse  s^   ^^E(FJ}""3''!44!+F!3J""3'' ,!557KK**=9 1& 9""3'')&1
%%+!%J""3''%002
""3''%//1
%--f5
%-2^^
* (KK**:6!,,Z8? $B 	'')r,   r%   Nr&   r'   r(   r)   r*   rH   r+   r%   r,   r-   r  r    s    
#*r,   r  c                      \ rS rSrSrS rSrg)	HarmSpinei  a  
A HarmSpine is a type of humdrum spine with the \*\*harm
attribute set and thus events are processed as if they
are harmonic analysis annotations in the "harm" syntax.

The harm roman numeral annotations are parsed using
a superset of the original \*\*harm Humdrum representation, written
for python and extending the syntax based on other projects like the
RomanText and the MuseScore roman numeral notations.
c                x   [        S5      n[        R                  " S5      nU R                   GHn  nUR                  nS nUS:X  a  GOUR                  S5      (       aL  U[        ;   a  M<  UR                  S5      (       a  USS n[        R                  " U5      nUnO[        U5      nOUR                  S5      (       a  [        XA5      nUnOUR                  S	5      (       a  [        U5      nOiUR                  n[        R                  " U5      n[        R                  " UU[        R                  R                  [        R                  R                   S
9nUc  GM1  UR"                  Ul        UR"                  Ul        U R(                  R+                  U5        GMq     U R(                  R-                  5         g )Nr]  Cr   r!   r  r^   r]   r^  r_  )
sixthMinorseventhMinor)r`  r   Keyr2   r   ra  r   endswithrG  rb  r   convertHarmToRomanr   RomanNumeralMinor67DefaultFLATSHARPr   rd  r   r   r   r   )	r>   re  
currentKeyr   rf  rg  	keyStringharmStrromanStrs	            r-   rH   HarmSpine.parse  sc   )$/WWS\
^^E^^FJ}""3''00??3'' &qI!$!3J!+J ",F!3J""3'' 1& H*
""3'')&1
..%88A"//$3388!&!5!5!;!;	
 %-2^^
*&+nn
#&&z2G $H 	'')r,   r%   Nr  r%   r,   r-   r  r    s    	'*r,   r  c                  <    \ rS rSrSrS	S
S jjrS rS rSS jrSr	g)r   i  a  
A SpineEvent is an event in a HumdrumSpine or ProtoSpine.

It's .contents property contains the contents of the spine, or
it could be '.', in which case it means that a
particular event appears after the last event in a different spine.
It could also be "None" indicating that there is no event at all
at this moment in the humdrum file.  Happens if no ProtoSpine exists
at this point in the file in this tab position.

Should be initialized with its contents and position in file.

These attributes are optional but likely to be very helpful::

    position -- event position in the file
    protoSpineId -- ProtoSpine id (0 to N)
    spineId -- id of HumdrumSpine actually attached to (after SpinePaths are parsed)

The `toNote()` method converts the contents into a music21 note as
if it's kern -- useful to have in all spine types.


>>> se1 = humdrum.spineParser.SpineEvent('EEE-8')
>>> se1.position = 40
>>> se1.contents
'EEE-8'
>>> se1
<music21.humdrum.spineParser.SpineEvent EEE-8>
>>> n = se1.toNote()
>>> n
<music21.note.Note E->
c                8    Xl         X l        SU l        S U l        g )Nr   r   r   r   r  )r>   r   r   s      r-   r?   SpineEvent.__init__   s    %%!"!%r,   c                    U R                   $ r   r   r&  s    r-   r!  SpineEvent._reprInternal&      }}r,   c                    U R                   $ r   r  r&  s    r-   __str__SpineEvent.__str__)  r  r,   Nc                H    Uc  [        U R                  5      $ [        U5      $ )z
parse the object as a \*\*kern note and return a
:class:`~music21.note.Note` object (or Rest, or Chord)

>>> se = humdrum.spineParser.SpineEvent('DD#4')
>>> n = se.toNote()
>>> n
<music21.note.Note D#>
>>> n.octave
2
>>> n.duration.type
'quarter'
)r  r   )r>   convertStrings     r-   toNoteSpineEvent.toNote,  s$      !$--00!-00r,   r  )rz   r   )r   r;   r   r   r   )
r&   r'   r(   r)   r*   r?   r!  r  r  r+   r%   r,   r-   r   r     s    @&1r,   r   c                      \ rS rSrSrS rS rS r\R                  4SS jjr
S rS rS	 rS
 rS rS rS rS rS rS rS rSS jrS rSrg)r   iB  a  
A SpineCollection is a set of HumdrumSpines with relationships to each
other and where their position attributes indicate
simultaneous onsets.

When you iterate over a SpineCollection, it goes from right to
left, since that's the order that humdrum expects.
c                J    / U l         SU l        SU l        S U l        S U l        g r  )spines
nextFreeIdspineReclassDoner  r  r&  s    r-   r?   SpineCollection.__init__L  s&     %r,   c                @    [        U R                  5      S-
  U l        U $ )zE
Resets the counter to len(self.spines) so that iteration is correct
r^   )rd   r  r  r&  s    r-   r'  SpineCollection.__iter__S  s     T[[)A-r,   c                    U R                   S:  a  [        eU R                  U R                      nU =R                   S-  sl         U$ )z?
Returns the current spine and decrements the iteration index.
r   r^   )r  r*  r  r>   rY   s     r-   r+  SpineCollection.__next__Z  s<     >>AKK/	!r,   c                   [        U R                  US9U l        X R                  l        U R                  R                  U R                  5        U =R                  S-  sl        SU R                  l        U R                  $ )a@  
creates a new spine in the collection and returns it.

By default, the underlying music21 class of the spine is
:class:`~music21.stream.Part`.  This can be overridden
by passing in a different streamClass.

Automatically sets the id of the Spine.


>>> hsc = humdrum.spineParser.SpineCollection()
>>> newSpine = hsc.addSpine()
>>> newSpine.id
0
>>> newSpine.stream
<music21.stream.Part ...>
>>> newSpine2 = hsc.addSpine(streamClass=stream.Stream)
>>> newSpine2.id
1
>>> newSpine2
<music21.humdrum.spineParser.HumdrumSpine: 1>
>>> newSpine2.stream
<music21.stream.Stream ...>
r   r^   F)r  r  r  r8   r  rc   r   )r>   r   s     r-   r   SpineCollection.addSpined  sZ    2 %T__+N(,%4==)1%*"}}r,   c                :    U R                   R                  U5        g)zf
appendSpine(spine) -- appends an already existing HumdrumSpine
to the SpineCollection.  Returns void
N)r  rc   )r>   spines     r-   appendSpineSpineCollection.appendSpine  s    
 	5!r,   c                f    U R                    H  nUR                  U:X  d  M  Us  $    [        S5      e)zr
returns the HumdrumSpine with the given id.

raises a HumdrumException if the spine with a given id is not found
z#Could not find a Spine with that ID)r  rT   r#   )r>   r  rY   s      r-   getSpineByIdSpineCollection.getSpineById  s2     I||w&   % DEEr,   c                    U R                    H0  nUR                  U:X  d  M  U R                   R                  U5          g   [        S[         35      e)a  
deletes a spine from the SpineCollection (after inserting, integrating, etc.)


>>> hsc = humdrum.spineParser.SpineCollection()
>>> newSpine = hsc.addSpine()
>>> newSpine.id
0
>>> newSpine2 = hsc.addSpine()
>>> newSpine2.id
1
>>> hsc.spines
[<music21.humdrum.spineParser.HumdrumSpine: 0>,
 <music21.humdrum.spineParser.HumdrumSpine: 1>]
>>> hsc.removeSpineById(newSpine.id)
>>> hsc.spines
[<music21.humdrum.spineParser.HumdrumSpine: 1>]

raises a HumdrumException if the spine with a given id is not found
Nz$Could not find a Spine with that ID )r  rT   r   r#   )r>   r  r   s      r-   removeSpineByIdSpineCollection.removeSpineById  sG    * Attw""1%  !EbTJKKr,   c                    U R                  5         U R                  5         U R                  5         U R                  5         U R	                  5         U R                  5         U R                  5         g)a  
Create Music21 Stream objects from spineCollections by running:

    self.reclassSpines()
    self.parseMusic21()
    self.performInsertions()
    self.moveObjectsToMeasures()
    self.moveDynamicsAndLyricsToStreams()
    self.makeVoices()
    self.assignIds()
N)reclassSpinesparseMusic21performInsertionsmoveObjectsToMeasuresmoveDynamicsAndLyricsToStreams
makeVoices	assignIdsr&  s    r-   rR   $SpineCollection.createMusic21Streams  sT     	 ""$++-r,   c                   SS0n0 nU R                    H  nUR                  nUR                  [        R                  5      nU(       d  SnOUS   R
                  nXa;  a  XBU'   SX'   Xdl        M\  Uc"  US==   S-  ss'   [        US   5      Ul        M  X   S:X  a  X&   nUR                  S-   Ul        X==   S-  ss'   US-   [        X   5      -   Ul        M     g)a5  
assign an ID based on the instrument or just a string of a number if none


>>> hsc = humdrum.spineParser.SpineCollection()
>>> newSpineDefault = hsc.addSpine()
>>> newSpineDefault2 = hsc.addSpine()
>>> newSpineOboe1 = hsc.addSpine()
>>> newSpineOboe2 = hsc.addSpine()
>>> newSpineTrumpet = hsc.addSpine()
>>> newSpineOboe1.stream.append(instrument.Oboe())
>>> newSpineOboe2.stream.append(instrument.Oboe())
>>> newSpineTrumpet.stream.append(instrument.Trumpet())
>>> hsc.assignIds()
>>> for sp in hsc.spines:
...     print(sp.stream.id)
1
2
Oboe-1
Oboe-2
Trumpet
Nr   r^   z-1-)r  r   rK  r   
InstrumentinstrumentNamerT   r;   )r>   usedIdsfirstStreamForEachInstrumentrY   spineStreaminstrumentsStreamspineInstrumentmyFirstStreams           r-   r  SpineCollection.assignIds  s    . )')$I#**K + > >z?T?T U$"&"3A"6"E"E-@K_=+,(!0 ("!$WT]!3+q0$@$QM'4'7'7$'>M$(A-(!03!6W=U9V!V) %r,   c                B   U R                    H  nUR                  b  M  UR                  (       d  M%  UR                  R	                  5       n[        UR                  5      nSnUR                  R                   H]  n UR                  nU H2  nXG:  d  Xg:  a  M  U R                  XU5        UR                  U5        M4     UnU?UR                  U5        M_     UR                  5         X!l        U H  nU R                  XU5        M     GM     g! [         a     NXf = f)zl
take a parsed spineCollection as music21 objects and take
sub-spines and put them in their proper location
Nr]   )r  rU   r   r   r   sortedr   	_elementsrd  performSpineInsertionr   AttributeErrorr   r   )r>   rY   	newStreaminsertPointslastHumdrumPositionr   rd  rh   s           r-   r  !SpineCollection.performInsertions  s   
 I$$0(( "((224I!)"B"BCL"$&&00&(&8&8O).2o6I$229K$++A. * +:'* $$R( 1 ))+(
 "**9C "C %. & s   =AD
DDc                   UR                  5         UR                  nUR                  U   nSnU H  nUS-  nS[        U5      -   nUR                  R
                   Hl  n	UR                  SL a!  [        U	[        R                  5      (       a  M3  U	R                  R                  U5        UR                  XIR                  -   U	5        Mn     M     UR                  5         g)z`
Insert all the spines into newStream that should be
inserted into thisSpine at insertionPoint.
r   r^   voiceFN)r   highestTimer   r;   r   r  r   r:   rH  groupsrc   r   r   )
r>   rY   r   insertionPoint
startPointchildrenToInsertvoiceNumberinsertSpinevoiceStrinsertEls
             r-   r  %SpineCollection.performSpineInsertion!  s    
 	%%'**
$;;NK+K1K[!11H'..88++u4Hfnn9]9]OO**84((oo)ExP 9	 , 	%%'r,   c                    U R                    HZ  nUR                  S:X  a  [        Ul        M   UR                  S:X  a  [        Ul        M=  UR                  S:X  d  MO  [
        Ul        M\     SU l        g)z
changes the classes of HumdrumSpines to more specific types
(KernSpine, DynamicSpine)
according to their spineType (e.g., \*\*kern, \*\*dynam)
rL   dynamharmTN)r  rV   rl  r   r  r  r  r  s     r-   r  SpineCollection.reclassSpines7  s^     I""f,&/	#$$/&0	#$$.&/	# % !%r,   c                `   0 nU R                    H  nUR                  b  M  UR                  R                  5       nU Hi  n[	        US5      (       d  M  UR
                  U;  a!  U/nUR                  U4nXaUR
                  '   MG  XR
                     S   nUR                  U5        Mk     M     U$ )a'  
iterates through the spines by location
and records the offset and priority for each

Returns a dictionary where each index is humdrumPosition and the value is a
two-element tuple where the first element is the music21 offset and the
second element is a list of Music21Objects at that position
rd  r^   )r  rU   r   flattenr  rd  r   rc   )r>   r   rY   sfr   elementList
valueTuples          r-   r   1SpineCollection.getOffsetsAndPrioritiesByPositionF  s     I$$,%%--/Br#455--\A+-$K*,))[)AJ?I););<*67I7I*J1*MK'..r2  % r,   c                   [         R                  " 5       nU R                   H  nUR                  b  M  UR	                  UR
                  5      Ul        UR
                  R                  [
        R                  5       H=  nUR                  U5        UR                  SS9nU H  nUR                  U5        M     M?     SUl        M     g)z
run moveElementsIntoMeasures for each HumdrumSpine
that is not a sub-spine.

Also fixes up the tuplets using duration.TupletFixer
NT)incorporateGroupings)r   TupletFixerr  rU   rZ  r   rK  rH  	setStreamfindTupletGroupsfixBrokenTupletDurationr  )r>   tfrY   mtupletGroupstgs         r-   r  %SpineCollection.moveObjectsToMeasures_  s     !!#I$$,#,#E#EiFVFV#W	  #))<<V^^LALLO#%#6#6D#6#QL*2226 + M +/	' %r,   c                   0 nU R                    H  nUR                  b  M  UR                  S:w  a  M$  UR                  R	                  [
        5       HL  nUR                  R                  S5      (       d  M%  [        UR                  SS 5      nUR                  X'     M     M     U R                    GH  nUR                  b  M  UR                  S:X  a  M%  / n0 nUR                  [
            H^  nUR                  R                  S5      (       d  M%  UR                  SS nUR                  S5       Vs/ s H  n[        U5      PM     nn  O   UR                  S:X  a  UR                  R                  5        H2  n[        U[        R                  5      (       d  M$  XUR                  '   M4     U Hc  n	X   n
U
R                  5        HH  nUR                   U;  a  M   UR"                  R%                  UR&                  XkR                      5        MJ     Me     GMj  UR                  S:X  a  UR                  R                  5        H2  n[        U[,        R.                  5      (       d  M$  XUR                  '   M4     U Hc  n	X   n
U
R                  5        HH  nUR                   U;  a  M   UR"                  R%                  UR&                  XkR                      5        MJ     Me     GM6  UR                  S;   d  GMI  UR                  R                  5        H-  nS	UR0                  ;   d  M  UR2                  XmR                  '   M/     U HN  n	X   n
U
R                  5        H3  nUR                   U;   d  M  XkR                      R4                  nXl        M5     MP     GM     gs  snf ! [(        R*                   a     GM  f = f! [(        R*                   a     GMD  f = f)
z
move :samp:`**dynam` and :samp:`**lyrics/**text` information to the appropriate staff.

Assumes that :samp:`*staff` is consistent through the spine.
NrL   z*staff   /r  r  )lyricstextrc  )r  rU   rV   r   rK  rG  tandemra  r   r   r  r:   r
   r  rd  r   r   
activeSiterW   r   r   StreamExceptionr   r  rM  objr   lyric)r>   kernStreamsrY   r*  	staffInfostavesAppliedToprioritiesToSearchxdynamic
applyStaffapplyStreamr   r  r)  r.  s                  r-   r  .SpineCollection.moveDynamicsAndLyricsToStreamsu  sL    I$$0""f,#**==jI}}//99ab 12	)2)9)9& J % I$$0""f, O!##**:6==++H55 &ab 1I7@s7K&L7K!s1v7KO&L	 7
 ""g-(//779G!'8+;+;<<FM7+B+BC  : #2J"-"9K)113;;.@@$!MM001CKK1PR	 4 #2 $$.%,,446D!$(:(:;;CG4+?+?@ 7 #2J"-"9K)113;;.@@$!MM001CKK1PR	 4 #2 $$(::%,,446D'4<<7CG88*+?+?@ 7 #2J"-"9K)113;;*<<$6{{$C$L$LE',H 4 #2g % 'M  ,;; ! !"  ,;; ! !s*   5N 2N%+2O%N>	=N>	O	O	c                   U R                    GH  nUR                  S:w  d  UR                  b  M#  UR                  nUR	                  [        R
                  5       GHp  nSnSnU H#  nSUR                  ;   d  M  SnUR                  n  O   U(       d  M:  [        S5       Vs/ s H  nSPM     nnUR                  n	U	 H  nUR                  n
U
(       a  U
S   R                  S5      (       d  M1  U
S   n[        US	   5      nX   nUc4  [        R                  " 5       nXU'   UR                  R                  U5        [        R                  (       a  Uc   eUR                  nUR!                  U5        UR#                  X-
  U5        M     U H)  nUc  M  UR%                  5         UR#                  X]5        M+     UR%                  5         GMs     GM     gs  snf )
z
make voices for each kernSpine -- why not just run
stream.makeVoices() ? because we have more information
here that lets us make voices more intelligently

Should be done after measures have been made.
rL   NFr   voice1T
   r     )r  rV   rU   r   rK  rH  r  r   re   elementsra  r   r   rc   tTYPE_CHECKINGr   r   r   )r>   rY   
thisStreamr   	hasVoiceslowestVoiceOffsetmElrh   voicesrW  	mElGroups	voiceNamer  	voicePart	mElOffsets                  r-   r  SpineCollection.makeVoices  s    I""f,	0E0E0Q"))J 33FNNC!	$%!C3::-$(	,/JJ)	 
 !AFr2KA42K"$++*C #

I$IaL,C,CG,L,L  )!I"%il"3K & 3I ($*LLN	.7{+!((//	:(444 #

IIIcN(()FL% +( "(I ,!557&7C	 "(
 &&(M D	 % 3Ls   "Gc                J    U R                    H  nUR                  5         M     g)zT
runs spine.parse() for each Spine.
thus populating the spine.stream for each Spine
N)r  rH   r  s     r-   r  SpineCollection.parseMusic21  s    
 IOO %r,   )r  r  r  r  r  N)r   ztype[stream.Stream])returnNone)r&   r'   r(   r)   r*   r?   r'  r+  r   r   r   r  r  r  rR   r  r  r  r  r   r  r  r  r  r+   r%   r,   r-   r   r   B  sp     ;A++ B"	FL6(-W^'DX(,%2/,K-Z2)jr,   r   c                  F    \ rS rSrSrSS jrS rS rS rS r	S r
S	 rS
rg)r   i  a  
An EventCollection is a time slice of all events that have
an onset of a certain time.  If an event does not occur at
a certain time, then you should check EventCollection[n].lastEvents
to get the previous event.  (If it is a note, it is the note
still sounding, etc.).  The happeningEvent method gets either
currentEvent or lastEvent.

These objects normally get created by
:meth:`~music21.humdrum.spineParser.HumdrumDataCollection.parseProtoSpinesAndEventCollections`
so you won't need to do all the setup.

Assume that ec1 is
     C4     pp

and ec2 is:
     D4     .


>>> SE = humdrum.spineParser.SpineEvent
>>> eventList1 = [SE('C4'),SE('pp')]
>>> eventList2 = [SE('D4'),SE('.')]
>>> ec1 = humdrum.spineParser.EventCollection(maxSpines=2)
>>> ec1.events = eventList1
>>> ec2 = humdrum.spineParser.EventCollection(maxSpines=2)
>>> ec2.events = eventList2
>>> ec2.lastEvents[1] = eventList1[1]
>>> ec2.maxSpines
2
>>> ec2.getAllOccurring()
[<music21.humdrum.spineParser.SpineEvent D4>, <music21.humdrum.spineParser.SpineEvent pp>]
c                    [         R                  " S 5      U l        [         R                  " S 5      U l        Xl        SU l        g )Nc                     g r   r%   r%   r,   r-   r   *EventCollection.__init__.<locals>.<lambda>%  s    r,   c                     g r   r%   r%   r,   r-   r   rP  &  s    Tr,   F)r	   r   r   
lastEventsr3   r   )r>   r3   s     r-   r?   EventCollection.__init__$  s3    ((6 ,,\:""r,   c                     X R                   U'   g r   r   r>   spineNum
spineEvents      r-   r   EventCollection.addSpineEvent+  s     *Hr,   c                     X R                   U'   g r   )rR  rV  s      r-   r   !EventCollection.addLastSpineEvent.  s    $.!r,   c                X    [        U R                  5       H  nXR                  U'   M     g r   )re   r3   r   )r>   globalEventrh   s      r-   r   EventCollection.addGlobalEvent2  s     t~~&A(KKN 'r,   c                     U R                   U   $ r   rU  r>   rW  s     r-   getSpineEventEventCollection.getSpineEvent6  s    {{8$$r,   c                ^    U R                   U   b  U R                   U   $ U R                  U   $ r   )rR  r   r`  s     r-   r   !EventCollection.getSpineOccurring9  s1     ??8$0??8,,;;x((r,   c                    / n[        U R                  5       H#  nUR                  U R                  U5      5        M%     U$ r   )re   r3   rc   r   )r>   	retEventsrh   s      r-   getAllOccurringEventCollection.getAllOccurringB  s9    	t~~&AT33A67 'r,   )r   rR  r3   r   N)r   )r&   r'   r(   r)   r*   r?   r   r   r   ra  r   rg  r+   r%   r,   r-   r   r     s+    B#+/)%)r,   r   c                   [         R                  " SU 5      nSnSU ;   a  [        R                  " 5       nOzU(       ad  UR	                  S5      nUS   R                  5       nXCS   :X  a  S[        U5      -   nOS[        U5      -
  n[        R                  " US9nXBl        O[        S	U  S
35      e[         R                  " SU 5      n[         R                  " SU 5      nU(       a!  UR	                  S5      UR                  l        O?U(       a!  UR	                  S5      UR                  l        OSU ;   a  SUR                  l        [        U R                  S5      5       H  nM     [        U R                  S5      5       H  nM     [        U R                  S5      5       H  nM     [        U R                  S5      5       H  nM     SU ;   a  [        R                  " S5      Ul        OCSU ;   a  [        R                  " S5      Ul        O!SU ;   a  [        R                  " S5      Ul        SU ;   a/  UR                   R#                  [         R$                  " 5       5        O4SU ;   a.  UR                   R#                  [         R&                  " 5       5        SU ;   a/  UR                   R#                  [         R(                  " 5       5        OSU ;   a/  UR                   R#                  [         R*                  " 5       5        OiSU ;   a/  UR                   R#                  [         R,                  " 5       5        O4SU ;   a.  UR                   R#                  [         R.                  " 5       5        SU ;   a/  UR                   R#                  [         R0                  " 5       5        OrSU ;   a/  UR                   R#                  [         R2                  " 5       5        O=S U ;   a7  [         R0                  " 5       n	S!U	l        UR                   R#                  U	5        S"U ;   a   S#U ;   a.  UR                   R#                  [         R6                  " 5       5        S$U ;   a.  UR8                  R#                  [8        R:                  " 5       5        S%U ;   a.  UR8                  R#                  [8        R<                  " 5       5        S&U ;   a.  UR8                  R#                  [8        R>                  " 5       5        S'U ;   a.  UR8                  R#                  [8        R@                  " 5       5        S(U ;   a.  UR8                  R#                  [8        RB                  " 5       5        S)U ;   a.  UR                   R#                  [         RD                  " 5       5        S*U ;   a/  UR8                  R#                  [8        RF                  " 5       5        O4S+U ;   a.  UR8                  R#                  [8        RH                  " 5       5        S,U ;   a  S-Ul%        OS.U ;   a  S/Ul%        [         R                  " S0U 5      n
[         R                  " S1U 5      nU
(       as  [M        U
R	                  S5      5      n[O        U
R	                  S25      5      nSU-  U-  URP                  l)        S3U ;   a   U R                  S35      URP                  l*        GOU(       Ga  [M        UR	                  S5      5      nUS:X  az  UR	                  S5      nUS4:X  a  S5URP                  l+        O)US6:X  a  S7URP                  l+        OS8URP                  l+        S3U ;   a   U R                  S35      URP                  l*        GO/U[P        RX                  ;   aJ  [P        RX                  U   URP                  l+        S3U ;   a   U R                  S35      URP                  l*        GO[M        U5      S9-   n[Z        R\                  " [Z        R^                  " U5      5      u  nnS2U-  n[P        RX                  [M        U5         URP                  l+        [P        R`                  " 5       n[P        Rb                  " URP                  RV                  S5      Ul2        [P        Rb                  " URP                  RV                  S5      Ul3        [Z        Rh                  " [M        U5      [M        U5      5      n[M        UU-  5      Ul5        [M        [O        U5      U-  5      Ul6        [n        S:   nUS;L aE  S3U ;   a?  [P        Rb                  " URf                  RV                  U R                  S35      5      Ul3        URP                  Rq                  U5        US!L a&  S3U ;   a   U R                  S35      URP                  l*        S<U ;   a"  URs                  5       nS=URP                  l+        OVS>U ;   a3  URs                  5       nS;URP                  l:        S=URP                  l+        OS?U ;   a  URs                  S!S@9nOSAU ;   a   [        U R                  SB5      5       H  nURv                  R#                  S5        M      [        U R                  SC5      5       H  nURv                  R#                  S5        M      [        U R                  SD5      5       H  nURv                  R#                  SESF5        M!     [        U R                  SG5      5       H  nURv                  R#                  SESF5        M!     U$ )Ha  
returns a :class:`~music21.note.Note` (or Rest or Unpitched, etc.)
matching the current SpineEvent.
Does not check to see that it is sane or part of a :samp:`**kern` spine, etc.


New rhythmic extensions formerly defined in
`wiki.humdrum.org/index.php/Rational_rhythms`
and now at https://extras.humdrum.org/man/rscale/
are fully implemented:


>>> n = humdrum.spineParser.hdStringToNote('CC3%2')
>>> n.duration.quarterLength
Fraction(8, 3)
>>> n.duration.fullName
'Whole Triplet (2 2/3 QL)'

>>> n = humdrum.spineParser.hdStringToNote('e-00.')
>>> n.pitch
<music21.pitch.Pitch E-4>
>>> n.duration.quarterLength
24.0
>>> n.duration.fullName
'Perfect Longa'

>>> n = humdrum.spineParser.hdStringToNote('F#000')
>>> n.duration.quarterLength
32.0
>>> n.duration.fullName
'Imperfect Maxima'


Note that the following example is interpreted as one note in the time of a
double-dotted quarter not a double-dotted quarter-note triplet.

I believe that the latter definition, though used in
https://kern.humdrum.org/cgi-bin/ksdata?l=musedata/mozart/quartet&file=k421-01.krn&f=kern
and the Josquin Research Project [JRP] is incorrect, seeing as it
contradicts the specification in
https://web.archive.org/web/20100203144730/http://www.music-cog.ohio-state.edu/Humdrum/representations/kern.html#N-Tuplets

>>> storedFlavors = humdrum.spineParser.flavors['JRP']  #_DOCS_HIDE

This is the default:

>>> humdrum.spineParser.flavors['JRP'] = False

>>> n = humdrum.spineParser.hdStringToNote('6..fff')
>>> n.duration.quarterLength
Fraction(7, 6)

>>> n.duration.dots
0

>>> n.duration.tuplets[0].durationNormal.dots
2

If you want the JRP definition, set humdrum.spineParser.flavors['JRP'] = True
before calling converter.parse() or anything like that:

>>> humdrum.spineParser.flavors['JRP'] = True
>>> n = humdrum.spineParser.hdStringToNote('6..fff')
>>> n.duration.quarterLength
Fraction(7, 6)
>>> n.duration.dots
2
>>> n.duration.tuplets[0].durationNormal.dots
0

>>> n = humdrum.spineParser.hdStringToNote('gg#q/LL')
>>> n.duration
<music21.duration.GraceDuration unlinked type:eighth quarterLength:0.0>
>>> n.duration.isGrace
True

>>> humdrum.spineParser.flavors['JRP'] = storedFlavors  #_DOCS_HIDE

z([a-gA-G]+)Nrr^   r         )octavezCould not parse z for note informationz(#+)z(-+)r  {}()[r  r   r  _r  r=  TwWr!  MS$RTr  O'"`~^;vur'  up\downz(\d+)%(\d+)z(\d+)r\   r   000maxima00longabrevern  r   FqeighthQP)appoggiaturapLJkpartialrightK)<ra   rb   r   Restr6  lowerrd   Notestepr#   pitch
accidentalre   countr   Tier   rc   HalfStepTrillWholeStepTrillHalfStepInvertedMordentWholeStepInvertedMordentHalfStepMordentWholeStepMordentTurnInvertedTurnconnectedToPreviousOrnamentr   Staccato	PizzicatoStaccatissimoTenutoAccentFermataUpBowDownBowstemDirectionr   floatr   rJ  dotsr  typeFromNumDictmathmodflog2TupletdurationTupleFromTypeDotsdurationActualdurationNormalgcdnumberNotesActualnumberNotesNormalflavorsappendTupletgetGraceslashr  )r   matchedNoterg  kernNoteNamer  rm  matchedSharpmatchedFlatrh   t1foundRationalfoundNumberdurationFirstdurationSeconddurationTypedurationStringdTunused_remainder	exponents	baseValuenewTupr  r   s                          r-   r  r  I  s|   j ))M84KJ hYY[
	"((+A$$&?"\**F\**FYYf-
 !1(;PQRR99Wh/L))GX.K&2&8&8&;
#	&1&7&7&:
#	&)
# 8>>#&' (8>>#&' (8>>#&' (8>>#&' (
h)
	
	,
 h%%k&?&?&AB	%%k&@&@&BC
h%%k&I&I&KL	%%k&J&J&LM	%%k&A&A&CD	%%k&B&B&DE
h%%k&6&6&89	%%k&>&>&@A	!%%%b)
h 	
h%%k&:&:&<= h  ''(>(>(@A
h  ''(?(?(AB
h 	  ''(C(C(EF
h  ''(<(<(>?
h  ''(<(<(>?
h%%k&9&9&;< h  ''(;(;(=>	  ''(=(=(?@ h#'
 		#)
  IInh7M))Hh/KM//23}22156,-,>,N
)(?'/~~c':J$	;,,Q/01(..q1N&+3
##(4'+2
##(+2
##(h+3>>#+>
##(X555'/'?'?'MJ$h+3>>#+>
##(\"S(B,0IIdiim,D)yYI'/'?'?I'OJ$__&F$,$F$FzGZGZG_G_ab$cF!$,$F$FzGZGZG_G_ab$cF!((3r7C	N3C'*28}F$'*5+;c+A'BF$
 %.Ce|x(0(J(J))..s0C)E% ,,V4d{sh+3>>#+>
##( h((*
#+
 	((*
$)
!#+
 	((d(;
	 8>>#&'( (8>>#&'' (8>>#&'	73 (8>>#&'	73 ( r,   c                $   [         R                  " 5       n[        R                  " SU 5      nU(       aK  [	        UR                  S5      5      Ul        UR                  S5      (       a  UR                  S5      Ul        [        R                  " 5       nSU ;   a	  SUl
        GOSU ;   a	  SUl
        GOSU ;   a	  S	Ul
        GOS
U ;   aC  SUl
        U R                  S5      S:  a	  SUl        GOsSU ;   a	  SUl        GOdSU ;   a  SUl        GOUSU ;   aC  SUl
        U R                  S5      S:  a	  SUl        GO*SU ;   a	  SUl        GOSU ;   a  SUl        GOSU ;   a@  SUl
        U R                  S5      S:  a  SUl        OSU ;   a  SUl        OSU ;   a  SUl        OSU ;   a@  SUl
        U R                  S5      S:  a  SUl        OSU ;   a  SUl        OSU ;   a  SUl        OSU ;   a@  SUl
        U R                  S5      S:  a  SUl        OVSU ;   a  SUl        OHSU ;   a  SUl        O:SU ;   a4  SUl
        U R                  S5      S:  a  SUl        U S:X  a  [        S5      eSU ;   a  [        R                  " 5       Ul        Ub  XAl        U$ XBl        U$ ) aG  
kern uses an equals sign followed by processing instructions to
create new measures.

N.B. -- much of the code for describing how repeat
signs are encoded is among the oldest code for music21
and was not updated when musicxml parsing was added.
It does not yet use the bar.RepeatMark() class
or any other post 2009 additions.
z(\d+)([a-z]?)r^   r\   r  noner|  shortr~  tickz||doubler  bothz:|leftz|:r  r{   zheavy-heavyz:!z!:z|!finalz!|zheavy-light|regularz==z==|zmCannot import a double bar visually rendered as a single bar -- not sure exactly what that would mean anyhow.r  )r   rH  ra   rb   r   r6  rp   numberSuffixr   Barliner  r  
repeatDotsr#   r   r  pauserightBarlineleftBarline)r   previousMeasurer7  	rematchMNbarlines        r-   r`  r`  `	  sv    
	B		*H5I	*+	??1'ooa0BOkkmG
h				>>#"!'GX!'GX!(G		$>>#"!'GX!'GX!(G		>>#"!'GX!'GX!(G		$>>#"!'GX!'GX!(G	 >>#"!'GX!'GX!(G		>>#"!'Gu"BC C h#++-"'.$ I !Ir,   c                X   U [         ;   a  gU R                  S5      (       a  U SS nUS:X  a  [        R                  " 5       $ US:X  a  [        R                  " 5       $ US:X  a  [        R
                  " 5       $ US:X  a  [        R
                  " 5       $ US:X  a  [        R                  " 5       $ US	:X  a  [        R                  " 5       $ US
:X  a  [        R                  " 5       $ US:X  a  [        R                  " 5       $  [        R                  " U5      nU$ U R                  S5      (       a'  U SS n [        U5      n[        R                  " US9nU$ U R                  S5      (       a  U SS n[        R"                  " SU5      nU(       a  [%        UR'                  S5      5      nUR'                  S5      n	U	S;  a  [(        R*                  " U5      $ U	S:X  a  US-  nSn	OU	S:X  a  US-  nSn	OU	S:X  a  US-  nSn	[(        R*                  " U SU	 35      $ [        S U  S35      eU R                  S!5      (       a  U SS n
 [,        R.                  " U
5      nU$ U R                  S"5      (       a  [3        U 5      $ U R                  S#5      (       a  [3        U 5      $ U R                  S$5      (       a  U SS n [,        R4                  " U5      nU$ U R                  S%5      (       aA  U R7                  S&5      nUS':X  a  S(U R7                  S5      -  n[8        R:                  " U5      $ U R=                  S)5      (       a  U SS( n[8        R>                  " U5      $ [3        U 5      $ ! [        R                   a    [        SU  S35      ef = f! [         aI    [        R                   " SSU5      n[        R                   " SSU5      n[        R                  " US9nUs $ f = f! [,        R0                   a    [3        U
5      s $ f = f! [,        R0                   a    [3        U5      s $ f = f)*a  
Kern uses symbols such as :samp:`*M5/4` and :samp:`*clefG2`, etc., to control processing

This method converts them to music21 objects.


>>> m = humdrum.spineParser.kernTandemToObject('*M3/1')
>>> m
<music21.meter.TimeSignature 3/1>

Unknown objects are converted to MiscTandem objects:

>>> m2 = humdrum.spineParser.kernTandemToObject('*TandyUnk')
>>> m2
<music21.humdrum.spineParser.MiscTandem *TandyUnk>
Nz*clefr;  r  XGv2GvzG^2zG^Fv4FvzUnknown clef type z foundz*MMrk  )rp   z^\[rz   z]\s*$r)  z*Mr\   z(\d+)/(\d+)r^   )0r  r  r  r  rl  r     r'  zIncorrect meter: z*ICz*IGz*ITrz*Iz*k#r   r]   r  ) r   ra  r   NoClefPercussionClefTreble8vbClefTreble8vaClefBass8vbClefclefFromStringClefExceptionr#   r  r   MetronomeMark
ValueErrorra   r  r|   r   r6  r   TimeSignaturer   fromHumdrumClassHumdrumInstrumentExceptionrG  fromHumdrumInstrumentr  r   KeySignaturer  r  )r*  clefTyper  metronomeMarkMMMS	meterTypetsTemp	numeratordenominatorinstrumentClassiObjinstrumentStr	numSharpsthisKeys                  r-   rv  rv  	  s   $ $$			7	#	#!":s?;;= _&&((%%''%%''%%''%%''##%%##%%L!%!4!4X!>%% 
		5	!	!qr
		!-0M$$M:BI 
		4	 	 12J	.)4FLLO,I ,,q/K"44**955#%NI"#K D(NI"#K E)NI"#K**i[++GHH"%6vhf#EFF			5	!	! *	///@DK 
		5	!	!&!!			6	"	"&!!			4	 	 qr
	-44]CDK 
		4	 	 LL%	>fll3/0I	**			2,www&!!G %% L&);F86'JKKL  	FF62}=MFF8R?M$$-8BI	B 55 	/o..	/ 55 	-m,,	-sC    M% 4 N O" :P %$N	AOO"PPP)(P)c                  2   ^  \ rS rSrSU 4S jjrS rSrU =r$ )rG  i6
  c                2   > [         TU ]  " S0 UD6  Xl        g )Nr%   )r   r?   r*  )r>   r*  keywordsr   s      r-   r?   MiscTandem.__init__7
  s    $8$r,   c                    U R                    $ r   r*  r&  s    r-   r!  MiscTandem._reprInternal;
  s    ++r,   r  rz   )r&   r'   r(   r)   r?   r!  r+   r   r   s   @r-   rG  rG  6
  s       r,   rG  c                  6   ^  \ rS rSrSrSU 4S jjrS rSrU =r$ )rb  i?
  z
A Music21Object that represents a comment in a single spine.


>>> sc = humdrum.spineParser.SpineComment('! this is a spine comment')
>>> sc
<music21.humdrum.spineParser.SpineComment 'this is a spine comment'>
>>> sc.comment
'this is a spine comment'
c                b   > [         TU ]  " S0 UD6  [        R                  " SSU5      nX0l        g )Nz^!+\s?rz   r%   )r   r?   ra   r  rw  r>   rw  r  commentPartr   s       r-   r?   SpineComment.__init__J
  s*    $8$ffYG4"r,   c                ,    [        U R                  5      $ r   r9   rw  r&  s    r-   r!  SpineComment._reprInternalO
      DLL!!r,   rw  r  	r&   r'   r(   r)   r*   r?   r!  r+   r   r   s   @r-   rb  rb  ?
  s    	#
" "r,   rb  c                  6   ^  \ rS rSrSrSU 4S jjrS rSrU =r$ )r   iS
  a  
A Music21Object that represents a comment for the whole score

>>> sc = humdrum.spineParser.GlobalComment('!! this is a global comment')
>>> sc
<music21.humdrum.spineParser.GlobalComment 'this is a global comment'>
>>> sc.comment
'this is a global comment'
c                   > [         TU ]  " S0 UD6  [        R                  " SSU5      nUR	                  5       nX0l        g )Nr
  rz   r%   )r   r?   ra   r  r  rw  r  s       r-   r?   GlobalComment.__init__]
  s8    $8$ffZW5!'')"r,   c                ,    [        U R                  5      $ r   r  r&  s    r-   r!  GlobalComment._reprInternalc
  r  r,   r  r  r  r   s   @r-   r   r   S
  s    #" "r,   r   c                  z  ^  \ rS rSr% SrSU 4S jjr0 SS_SS_S	S
_SS_SS_SS_SS_SS_SS_SS_SS_SS_SS_SS_SS_SS _S!S_0 S"S#_S$S_S%S&_S'S_S(S)_S*S_S+S_S,S_S-S_S.S_S/S_S0S_S1S2_S3S_S4S_S5S6_S7S8_E0 S9S:_S;S<_S=S>_S?S@_SASB_SCSD_SESF_SGSH_SISJ_SKSL_SMSN_SOSP_SQS_SRSS_STSU_SVSW_SXSY_E0 SZS[_S\S]_S^S_S_S_S`Sa_SbSc_SdSe_SfSg_ShSi_SjSk_SlSm_SnSo_SpSq_SrSs_StSu_SvSw_SxSy_E0 SzS_S{S_S|S_S}S~_SS_SS_SS_SS_SS_SS_SS_SS_SS_SS_SS_SS_SS_ESSSSSSSSSSSS.ErS\S'   SS jrS r	Sr
U =r$ )r   ig
  a  
A Music21Object that represents a reference in the score, called a "reference record"
in Humdrum.  See Humdrum User's Guide Chapter 2.

>>> sc = humdrum.spineParser.GlobalReference('!!!REF:this is a global reference')
>>> sc
<music21.humdrum.spineParser.GlobalReference REF 'this is a global reference'>
>>> sc.code
'REF'
>>> sc.value
'this is a global reference'

Alternate form

>>> sc = humdrum.spineParser.GlobalReference('REF', 'this is a global reference')
>>> sc
<music21.humdrum.spineParser.GlobalReference REF 'this is a global reference'>
>>> sc.code
'REF'
>>> sc.value
'this is a global reference'

Language codes are parsed:

>>> sc = humdrum.spineParser.GlobalReference('!!!OPT@@RUS: Vesna svyashchennaya')
>>> sc.code
'OPT'
>>> sc.language
'RUS'
>>> sc.isPrimary
True

>>> sc = humdrum.spineParser.GlobalReference('!!!OPT@FRE: Le sacre du printemps')
>>> sc.code
'OPT'
>>> sc.language
'FRE'
>>> sc.isPrimary
False
rz   c                  > [         TU ]  " S
0 UD6  [        R                  " SSU5      nUR	                  5       nUc6  SU;   a0  [        R                  " SSU5      n[        R                  " SSU5      nXl        X l        S U l        SU l        SU R
                  ;   a(  SU l        U R
                  R                  SS	5      U l        S	U R
                  ;   a)  U R
                  R                  S	5      u  U l        U l        g g )Nr   rz   r  z^.*?:z:.*$Fz@@T@r%   )r   r?   ra   r  r  r   r   language	isPrimaryreplacer   )r>   	codeOrAllvalueOrNoner  r   s       r-   r?   GlobalReference.__init__
  s    $8$FF8R3	OO%	3)#3&&2y9KwI6I	 
499!DN		))$4DI$))'+yys';$DIt} r,   COMcomposerCOAattributedComposerCOSsuspectedComposerCOLcomposerAliasCOCcomposerCorporateCDTCBLCDLCNTLYRlyricistLIB
librettistLARarrangerLORorchestratorTXOtextOriginalLanguageTXLtextLanguageTRN
translatorRTLRMMmanufacturerzRC#RRD
dateIssuedRLCRNPproducerRDTzRT#MGNMPNMPSMRDMLCMCN	conductorMPDMDTOTLtitleOTPpopularTitleOTAalternativeTitleOPRparentTitleOAC	actNumberOSCsceneNumberOMVmovementNumberOMDmovementNameOPS
opusNumberONMrp   OVMvolumeNumberODEdedicatedToOCO
commissionOCLtranscriberONBODTdateCreatedOCYcountryOfCompositionOPClocaleOfCompositionGTL
groupTitleGAWassociatedWorkGCOcollectionDesignationPUBPEDPPRfirstPublisherPDTdateFirstPublishedPTLpublicationTitlePPPplaceFirstPublishedzPC#publishersCatalogNumberSCTscholarlyCatalogAbbreviationSCAscholarlyCatalogNameSMSmanuscriptSourceNameSMLmanuscriptLocationSMAmanuscriptAccessAcknowledgementYEPelectronicPublisherYEC	copyrightYERelectronicReleaseDateYEMYENYORYOOoriginalDocumentOwnerYOYYOEoriginalEditorEEDelectronicEditorENCelectronicEncoderENDEMDEEVEFLESTVTSACOAFRAGN)ASTAMDAMTAINAREARLHAOHTXRLNRNBRWBdicthumdrumKeyToUniqueNamec                
   U R                   nU R                  nU R                  R                  US5      nU(       a  UR	                  XC5        gX R                  ;   a  UR                  SU-   U5        gUR                  X#5        g)zy
update a metadata object according to information in this GlobalReference

See humdrum guide Appendix I for information
rz   zhumdrum:N)r   r   r  getadd	addCustom)r>   r   cr  
uniqueNames        r-   r   GlobalReference.updateMetadata  si     IIJJ5599!R@
FF:!---LLa+ LLr,   c                :    U R                    SU R                  < 3$ )Nr  )r   r   r&  s    r-   r!  GlobalReference._reprInternal#  s    ))Adjj^,,r,   )r   r(  r'  r   )rz   N)r   zmetadata.Metadata)r&   r'   r(   r)   r*   r?   r  __annotations__r   r!  r+   r   r   s   @r-   r   r   g
  s;   'P<"l$ 	zl$ 	#	l$
 	"l$ 	l$ 	"l$ 	rl$ 	rl$ 	rl$ 	rl$ 	zl$ 	|l$ 	zl$ 	~l$  	%!l$" 	~#l$( 	|)l$* 	r+l$, 	~-l$. 	r/l$0 	|1l$2 	r3l$4 	z5l$6 	r7l$8 	r9l$< 	r=l$> 	r?l$@ 	rAl$B 	rCl$D 	rEl$F 	{Gl$H 	rIl$J 	rKl$N 	wOl$P 	~Ql$R 	!Sl$T 	}Ul$V 	{Wl$X 	}Yl$Z 	[l$\ 	~]l$^ 	|_l$` 	xal$b 	~cl$d 	}el$f 	|gl$h 	}il$j 	rkl$l 	}ml$n 	%ol$p 	$ql$t 	|ul$v 	wl$x 	&yl$| 	r}l$~ 	rl$@ 	Al$B 	#Cl$D 	!El$F 	$Gl$H 	(Il$J 	-Kl$L 	%Ml$N 	%Ol$P 	#Ql$R 	0Sl$T 	$Ul$V 	{Wl$X 	&Yl$Z 	r[l$\ 	r]l$^ 	r_l$` 	&al$b 	rcl$d 	el$f 	!gl$h 	"il$j 	rkl$l 	rml$n 	rol$p 	rql$r 	rsl$t 	rul$x 	ryl$z 	r{l$| 	r}l$~ Wl$D l\&- -r,   r   c                      \ 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S rS rS rSrg)Testi'  c                2    SSK Jn  U" U [        5       5        g )Nr   )testCopyAll)music21.test.commonTestr  globals)r>   r  s     r-   testCopyAndDeepcopyTest.testCopyAndDeepcopy(  s    7D')$r,   c                &   [        [        R                  5      nUR                  5         UR                  R                  S5      nU R                  UR                  S5        U R                  UR                  R                  S5        UR                  R                  S5      nUR                   Vs/ s H  oDR                  PM     nnU R                  U/ SQ5        U R                  UR                  S5        U R                  U[        5        g s  snf )Nr;  r^   )r;  r&  	   r:                           #   $   '   r   rL   )r/   r   mazurka6rH   r8   r  assertEqualrT   rU   r   rV   assertIsInstancerl  )r>   hf1spine5spine1csspine1Childrens         r-   testLoadMazurkaTest.testLoadMazurka,  s     $I$6$67		$ $$11!4A&++..2$$11!4*0*<*<=*<B%%*<=V	X 	))62fi0 >s   0Dc                `   [        S5      nUR                  5       nU R                  UR                  R                  R
                  S5        U R                  UR                  R                  S5        U R                  UR                  R                  S   R                  R                  S5        g )Nz40..ccccc##_wtLLK~v/zdouble-sharpr   r\   )
r   r  r  r  r  namer   r  r  r  )r>   abs      r-   testSingleNoteTest.testSingleNoteN  s{    -.HHJ++00.A!,++A.==BBAFr,   c                    [         R                  " 5       n[        SU5      nU R                  UR                  S5        U R                  UR
                  S5        U R                  UR                  R                  S5        U R                  UR                  R                  S5        U R                  UR                  R                  5        U R                  UR                  R                  [        R                  5        g )Nz=29a;:|:   r  r  r  )r   rH  r`  r  rp   r  r  r  r  assertIsNotNoner  r  r   r  )r>   m0r7  s      r-   testMeasureBoundariesTest.testMeasureBoundariesV  s    ^^z2.B'#.--y933V<R__223boo33[5H5HIr,   c                4   [        S5      nU R                  [        U5      S5        [        S5      nU R                  [        U5      S5        [        S5      nU R                  [        U5      S5        [        S5      nU R                  [        U5      S5        g )	Nz*M3/1z!<music21.meter.TimeSignature 3/1>z*M3/0z!<music21.meter.TimeSignature 6/1>z*M3/00z"<music21.meter.TimeSignature 12/1>z*M3/000z"<music21.meter.TimeSignature 24/1>)rv  r  r;   )r>   r!  s     r-   testMeterBreveTest.testMeterBreve`  s~    w'Q!DEw'Q!DEx(Q!EFy)Q!EFr,   c                    [        [        R                  5      nUR                  5         UR                  nUR                  5         g)zD
test loading a fake piece with spine paths, lyrics, dynamics, etc.
N)r/   r   fakeTestrH   r   show)r>   rv   mss      r-   x_testFakePieceTest.x_testFakePiecej  s/     $I$6$67		ZZ
	r,   c                   [        [        R                  5      nUR                  5         UR                  nSnUR                  5        H  n[        US5      (       a3  UR                  R                  S:X  a  UR                  S:X  a  US-  nME  MG  [        US5      (       d  MZ  UR                   H,  nUR                  S:X  d  M  UR                  S:X  d  M'  US-  nM.     M     U R                  US5        g )Nr   r  zG#r\   r^   pitchesV   )r/   r   r  rH   r   r   r  r  r  beatr  r  )r>   r  masterStreamgSharpCountr  r  s         r-   testSpineMazurkaTest.testSpineMazurkas  s    #I$6$67
 			zz %%'Aq'""qww||t';66Q;1$K I&&Avv~!&&A+#q( # ( 	b)r,   c                l    SSK Jn  [        R                  " 5       S-  nUS-  nUR	                  U5      ng )Nr   )	converterhumdrumzMissa_Sine_nomine-Kyrie.krn)music21r  r	   getSourceFilePathrH   )r>   r  
parserPathsineNominePathunused_myScores        r-   testParseSineNomineTest.testParseSineNomine  s1    %--/);
#&CC"8r,   c                Z   [        [        R                  5      nUR                  5         UR                  nU R                  UR                  S   R                  S5      R                  5       5        U R                  UR                  S   R                  S5      R                  5       5        g )Nr   rl  r;  )
r/   r   splitSpines2rH   r   
assertTruepartsmeasurer@  assertFalse)r>   r  r  s      r-   testSplitSpinesTest.testSplitSpines  s|    #I$:$:;		zz**1-55a8BBDE++A.66q9CCEFr,   c                n    [        [        R                  5      nUR                  5         UR                  ng r   )r/   r   r  rH   r   )r>   r  unused_ss      r-   x_testMoveDynamicsTest.x_testMoveDynamics  s$    #I$6$67		::r,   c                    SSK Jn  [        [        R                  5      nUR                  5         UR                  nUR                  U5      nU R                  US5        g )Nr   r  zMagijago ickewyan)	r	  r)  r/   r   r  rH   r   assembleLyricsr  )r>   r)  r  r   r(  s        r-   testLyricsInSpineTest.testLyricsInSpine  sH     #I$6$67		JJ$$Q'!45r,   c                n    [        [        R                  5      nUR                  5         UR                  ng)z
Currently this does not work since a second split on a stream that
already resulted from a split does not parse properly.  Shows up also
in strangeWTCOpening, below.
N)r/   r   	splitLotsrH   r   r>   r  unused_masterStreams      r-   testSplitSpines2Test.testSplitSpines2  s'     $I$7$78		!jjr,   c                N    [        [        R                  5      nUR                  ng r   )r/   r   strangeWTCOpeningr   r"  s      r-   testParseStrangeSplitTest.testParseStrangeSplit  s    #I$?$?@!jjr,   c                   [        [        R                  5      nUR                  5         UR                  nUR
                  S   n/ nU[            H  nUR                  UR                  5        M      U R                  SU;   5        g )Nr\   zspine comment)
r/   r   r  rH   r   r  rb  rc   rw  r  )r>   r  r   r  commentsr  s         r-   testSpineCommentsTest.testSpineComments  se    #I$6$67		JJGGAJ<AOOAII& !834r,   c                   [        [        R                  5      nUR                  5         UR                  n0 SS/ SQSSSS4_SS/ S	QS
S
SS4_SS/ SQSSSS4_SS/ SQSSSS4_SS/ SQSSSS4_SS/ SQSSSS4_SS/ SQSSSS4_SS / S!QSS"S#S4_S$S%/ S&QS"S
S'S(4_S)S/ SQSSSS4_S*S/ SQSSSS4_S+S,/ S-QSSSS4_S.S// S0QS
S
SS4_S1S2/ S3QS4S4SS4_S5S6/ S7QS8SS#S4_S9S:/ S;QSSSS4_S<S=/ SQSSSS4_S>/ S?QS@SAS#S4S=/ SQSSSS4SB/ SCQS
SASDS(4S=/ SQSSSS4SE/ SFQS@SAS'S(4SG/ SHQSSSS4S=/ SQSSSS4S=/ SQSSSS4SI/ SQSSSS4SJ.	EnUR                  5       R                  [        R                  5       H  nUR                  nUR                  nUR                  5       R                  nUR                  5       R                  nUR                  5       n	UR                  5       n
UUUUU	U
4nU R!                  XUR"                     5        M     g )KNrn  I in C majorr   rl     r  5   F      ?zii in C major)r\   r;  r  D       @ziii in C major)rl  r1     E      @zIV in C major)r;  r  r   F      @zI64 in C major)r1  r   rl  G@         @zV in C major)r1  r6  r\         @zvi in C majorr  r   rl  A      @zV6 in C major)r6  r\   r1  Br&         @zviio65/i in C majorr\   r;  r  r6  A   T      "@      &@      (@zi in c minor)r   rk  r1        *@ziio in c minor)r\   r;  r        ,@zIII in c minor)rk  r1  r:  zE-      .@zN6 in c minor)r;  r  r^   zD-      0@zi64 in c minor)r1  r   rk        1@zV in c minorzIt6 in c minor)r  r   r&  zF#zA-zFr43 in c minor)r  r   r\   r&  +   zGer65 in c minor)r  r   rk  r&  ziv in c minor)r;  r  r   zI in c minor	      2@g      4@g      5@g      7@      8@      ;@      >@g      @@g     @@)r/   r   harmScaleDegreesrH   r   r  rK  r   r  figureAndKeypitchClassesrootr  bassinversionName	isSeventhr  r   r>   r  r   groundTruthr  rU  rV  rW  rX  rY  rZ  assertTuples               r-   testHarmSpineDegreesTest.testHarmSpineDegrees  s   #I$>$>?		JJ
.)S#r5A
/9c3EB
 "JS"eD
 /9c3EB	

 "IsCUC
 .*c3EB
 /9c3EB
 /:sCEB
 'S"dK
 .)S#r5A
 >9c3EB
 >9c3EB
 #YS"eD
 #ZtRG
 ?ItS!UC
  #YS"eD!
" >:sCUC#
$ $YdAuE!:sCUC$lCr4H!:sCUC%|T4TJ"IsCUC!:sCUC!:sCUC!9c3EB5
8 IIK2253E3EFD,,L,,L99;##D99;##D ..0M(IK [dkk*BC Gr,   c                   [        [        R                  5      nUR                  5         UR                  n0 SS/ SQSSSS4_SS/ S	QS
S
SS4_SS/ SQSSSS4_SS/ SQSSSS4_SS/ SQSSSS4_SS/ SQSSSS4_SS/ SQSSSS4_SS / S!QSSS"S#4_S$S%/ SQSSSS4_S&S'/ SQSSSS4_S(S)/ SQSSSS4_S*S+/ SQSSSS4_S,S-/ S	QS
S
SS4_S.S// SQSSSS4_S0S1/ S2QSSSS4_S3S4/ S5QSSS"S#4_S6S7/ S8QSSS9S4_S:/ S;QS
SS<S4S=/ S>QSSS9S4S?/ S@QSSS<S4SA/ SBQSSSCS4S/ SQSSSS4SD/ SEQSSSCS4S / S!QSSS"S#4SF/ SGQSSS9S4SH/ SIQSSS<S4SJ/ SKQSSS9S4SL/ SMQSSS<S4SN/ SOQS
SSCS4S// SQSSSS4SP/ SQQSSSCS4S4/ S5QSSS"S#4SR.EnUR                  5       R                  [        R                  5       H  nUR                  nUR                  nUR                  5       R                  nUR                  5       R                  nUR                  5       n	UR                  5       n
UUUUU	U
4nU R!                  XUR"                     5        M     g )SNrn  zI7 in C major)r   rl  r1  r6  r  r1  Tr3  zIV7 in C major)r;  r  r   rl  r9  r5  zviio7 in C major)r6  r\   r;  r  rB  r8  ziii7 in C major)rl  r1  r6  r\   r7  r:  zvi7 in C major)r  r   rl  r1  r@  r=  zii7 in C major)r\   r;  r  r   r4  r>  zV7 in C major)r1  r6  r\   r;  r;  rA  r/  r0  r2  FrH  zi7 in a minorrI  ziv7 in a minorrJ  z-VII7 in a minorrK  zIII7 in a minorrL  zVI7 in a minorrM  ziio7 in a minorrP  zV7 in a minor)rl  r  r6  r\   g      3@zi in a minorr?  rQ  zI65 in C major)rl  r1  r6  r   rE  zIV2 in C major)rl  r;  r  r   *   zviio65 in C majorrD  ziii2 in C major)r\   rl  r1  r6  zvi43 in C major)rl  r1  r  r   rN  zV43 in C major)r\   r;  r1  r6  zi65 in a minor)r   rl  r1  r  ziv2 in a minor)r   r\   r;  r  z-VII65 in a minor)r6  r\   r;  r1  zIII2 in a minor)r6  r   rl  r1  zVI43 in a minor)r   rl  r;  r  zV43 in a minor)r6  r\   rl  r  )g      9@g      :@rR  g      <@g      =@rS  g      ?@g      B@g     B@g      C@g     C@g      D@g     D@g      E@g     E@)r/   r   harmSeventhsrH   r   r  rK  r   r  rU  rV  rW  r  rX  rY  rZ  r  r   r[  s               r-   testHarmSpineSeventhsTest.testHarmSpineSevenths  s|   #I$:$:;		JJ!
/=#sAtD!
"L#sAtD!
 $mS#q$G!
 #]CaF	!

 "L#sAtD!
 "L#sAtD!
 /=#sAtD!
 .)S#r5A!
 ?L#sAtD!
 #\3QE!
 %}c34H!
 $mS#q$G!
 #\3QE!
 $mS#q$G!
 ?M3QE!
  >9c3EB!!
" #]Cb$G#!
$ $\3RF&sCTJ$mS#r4H$lCb$G#\3QE#]Cb$G!9c3EB#\3RF#\3RF&sCTJ$mS#r4H$lCb$G$mS#q$G#]Cb$G!9c3EBA!
D IIK2253E3EFD,,L,,L99;##D99;##D ..0M(IK [dkk*BC Gr,   c                    [        [        R                  5      nUR                  5         UR                  n0 SS_SS_SS_SS_SS_SS_SS_S	S_S
S_SS_SS_SS_SS_SS_SS_SS_SS_SSSSSSSSSS.	EnUR                  5       R                  [        R                  5       Hg  nUR                  5       nUR                  5       nUR                  5       nUR                  5       nUUUU4n	U R                  XUR                     5        Mi     g )Nrn  )FFFFr3  r5  r8  r:  r=  r>  rA  rC  rF  rG  rH  rI  rJ  rK  rL  rM  )TTFF)TFTF)TFFTrO  )r/   r   rT  rH   r   r  rK  r   r  isAugmentedSixthisItalianAugmentedSixthisFrenchAugmentedSixthisGermanAugmentedSixthr  r   )
r>   r  r   r\  r  rf  rg  rh  ri  r]  s
             r-   testHarmSpineAugmentedSixths!Test.testHarmSpineAugmentedSixths5  s   #I$>$>?		JJ
-
 -
 -	

 -
 -
 -
 -
 -
 -
 -
 .
 .
 .
 .
  .!
" .#
$ .%
& -.,.,....7
: IIK2253E3EFD#446&*&B&B&D#%)%@%@%B"%)%@%@%B" '&&	K [dkk*BC Gr,   c                    SSK Jn  UR                  S5      nUR                  nU R	                  UR
                  5        U R                  SUR
                  5        g )Nr   )corpuszpalestrina/agnus_0
Palestrina)r	  rm  rH   r   r  r.  assertIn)r>   rm  r  r   s       r-   testMetadataRetrievedTest.testMetadataRetrievedc  sA    "LL-.ZZR[[)lBKK0r,   c                   [         S   nS[         S'   [        [        R                  5      nUR	                  5         UR
                  nS[         S'   [        [        R                  5      nUR	                  5         UR
                  nU[         S'   UR                  S   R                  S5      R                  S   nUR                  S   R                  S5      R                  S   nU R                  UR                  R                  S5        U R                  UR                  R                  S5        U R                  [        UR                  R                  S   R                  5      S5        U R                  UR                  R                  S   R                  R                  S5        U R                  UR                  R                  S5        U R                  UR                  R                  S5        U R                  [        UR                  R                  S   R                  5      S	5        U R                  UR                  R                  S   R                  R                  S5        g )
Nr   FTr   r^   zEighth Triplet (1/2 QL)z8DurationTuple(type='eighth', dots=1, quarterLength=0.75)zDotted Eighth Triplet (1/2 QL)z7DurationTuple(type='eighth', dots=0, quarterLength=0.5))r  r/   r   dottedTupletrH   r   r  r  notesr  r   fullNamer  r9   r  r  )r>   
prevFlavorrv   r  hdc2dcndns           r-   testFlavorsTest.testFlavorsj  s   U^
#I$:$:;		JJ$Y%;%;<

KK#WWQZ"((+WWQZ"((+--/HI))1-bkk11!4CCDS	U,,Q/>>CCQG--/OP))1-bkk11!4CCDR	T,,Q/>>CCQGr,   r%   N)r&   r'   r(   r)   r  r  r  r  r  r  r  r  r  r  r  r$  r(  r,  r^  rc  rj  rp  r{  r+   r%   r,   r-   r  r  '  sn    % 1DGJG*D9G6))5/Db5Dn,D\1Hr,   r  c                      \ rS rSrSrS rSrg)TestExternali  Tc                    [        [        R                  5      nUR                  5         U R                  (       a  UR
                  R	                  5         g g r   )r/   r   
sousaStarsrH   r  r   )r>   r  s     r-   testShowSousaTestExternal.testShowSousa  s5    #I$8$89		99JJOO r,   r%   N)r&   r'   r(   r)   r  r  r+   r%   r,   r-   r~  r~    s    Dr,   r~  __main__r   )Gr*   
__future__r   rq   r  ra   typingr=  unittestr	  r   r   r   r   r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   r   r   r   music21.humdrumr   r   r   r>  pathlibEnvironmentr|  r  r   Music21Exceptionr#   ProtoM21Objectr/   r   r   r   r}   r~   r   r  rl  r  r  r   r   r   r  r`  rv  Music21ObjectrG  rb  r   r   TestCaser  r~  r&   mainTestr%   r,   r-   <module>r     s$  "F #   	   !                      % & '??&&'<=%.9 	|44 	n
/G22 n
/b' > ($ $B=+ =@% %P#'' #*a*7)) a*H	R1 R1j)* )*X2* 2*n>1'' >1F{g,, {@D DNTnWtm"x ##  "4%% "("D&& "(}-d(( }-@YH8 YHx
8$$  zT r,   