
    rh4                    4   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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  \R0                  " 5       r\	R4                  " 0 S\_S\_S\_S\_S\_S\_S\_S\_S\_S\_S\_S\_S\_S\_S\_S\_5      r\R>                  " S5      r S r!\	R4                  " \\\!\\\\\\\\\\\\S .5      r"\\"S!.r#S"r$S#r%\$\%S!.r& " S$ S%\RN                  5      r( " S& S'\(5      r) " S( S)\(5      r* " S* S+5      r+ " S, S-5      r,S8S. jr-S9S/ jr.S:S0 jr/S;S1 jr0S<S=S2 jjr1S>S?S3 jjr2S@S4 jr3 " S5 S6\Rh                  5      r5\6S7:X  a  SSKr\Rn                  " \55        gg)Az
Converter for parsing the tabular representations of harmonic analysis such as the
DCMLab's Annotated Beethoven Corpus (Neuwirth et al. 2018).
    )annotationsNchord)common)environment)harmony)key)metadata)meter)roman)spanner)streamr   altchordmeasurebeattotbeattimesiglength
global_key	local_keypedalnumeralformfigbasschangesrelativeroot	phraseendz1(?P<numer>\d+(?:\.\d+)?)/(?P<denom>\d+(?:\.\d+)?)c                     [        U 5      $ ! [         aS    [        R                  " [        U 5      n[        UR                  S5      5      [        UR                  S5      5      -  s $ f = f)Nnumerdenom)float
ValueErrorrematchMN_ONSET_REGEXgroup)valuems     X/home/james-whalen/.local/lib/python3.13/site-packages/music21/romanText/tsvConverter.py_float_or_fracr*   D   sZ    AU| AHH^U+QWWW%&qwww/?)@@@As   
 AA*)A*)r   mnmn_onsetr   volta	globalkeylocalkeyr   r   r   r   r   r   r   label      )r   r   r   r   r   r   opnomovr   r   r   r   r   r   r   r   r   r   )mcr+   mc_onsetr,   r   staffvoicer-   r0   r.   r/   r   r   specialr   r   r   r   r   cadencer   
chord_typeglobalkey_is_minorlocalkey_is_minorchord_tonesadded_tonesroot	bass_notec                     ^  \ rS rSrSrS
U 4S jjr\SS j5       r\R                  SS j5       rS
S jr	SS jr
        SS jrS	rU =r$ )TabChordBase   zp
Abstract base class for intermediate representation format for moving
between tabular data and music21 chords.
c                  > [         TU ]  5         SU l        S U l        S 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        S U l        SU l        SU l        SU l        SU l        g )N       ?r2   )super__init__r   r   representationTypeextradcml_versionr   r   r   r   r   r   r   r   r   r   r   self	__class__s    r)   rL   TabChordBase.__init__   s    &*,0%'
 
#
"	!%!%#' !!	    c                    U R                   $ )ak  
For easier interoperability with the DCML standards, we now use the
column name 'chord' from the DCML file. But to preserve backwards-
compatibility, we add this property, which is an alias for 'chord'.

>>> tabCd = romanText.tsvConverter.TabChord()
>>> tabCd.chord = 'viio7'
>>> tabCd.combinedChord
'viio7'
>>> tabCd.combinedChord = 'IV+'
>>> tabCd.chord
'IV+'
r   rQ   s    r)   combinedChordTabChordBase.combinedChord   s     zzrT   c                    Xl         g Nr   )rQ   r'   s     r)   rW   rX      s    
rT   c                0   U R                   S:X  a
  SnSU l         O%U R                   S:X  a
  SnSU l         O[        S5      e[        U R                  [	        U R
                  5      US9U l        US:X  a  U R                  b  U R                  R                  SS	5      OSU l        U R                  S
:X  a  U R                  R                  SS	5      U l	        [        U R                  5      U l	        U R                  R                  SS5      S:X  aD  U R                  S:w  a4  [        R                  " SSU R                  [        R                   S9U l	        [	        U R                  5      (       a  U R"                  (       aj  [	        U R"                  5      (       a5  [        U R"                  SUS9U l        [        U R                  SUS9U l        g[        U R"                  SUS9U l        g[        U R                  SUS9U l        gU R"                  (       aj  [	        U R"                  5      (       a5  [        U R"                  SUS9U l        [        U R                  SUS9U l        g[        U R"                  SUS9U l        g[        U R                  SUS9U l        g)u  
Converts the representationType of a TabChordBase subclass between the
music21 and DCML conventions.

To demonstrate, let's set up a dummy TabChordV2().

>>> tabCd = romanText.tsvConverter.TabChordV2()
>>> tabCd.global_key = 'F'
>>> tabCd.local_key = 'vi'
>>> tabCd.numeral = 'ii'
>>> tabCd.chord = 'ii%7(6)'
>>> tabCd.representationType = 'DCML'

>>> tabCd.representationType
'DCML'

>>> tabCd.chord
'ii%7(6)'

>>> tabCd._changeRepresentation()
>>> tabCd.representationType
'm21'

>>> tabCd.chord
'iiø7[no5][add6]'
m21m21-DCMLDCMLDCML-m21z@Data source must specify representation type as 'm21' or 'DCML'.minor	directionN%   ør3   r=   rH   Mm7Vz
                            (\d+)  # match one or more digits
                            (?![]\d])  # without a digit or a ']' to the right
                        zd\1)flagsTF)rM   r"   characterSwapsr   isMinorr   r   replacerO   r   handleAddedTonesrN   getr   r#   subVERBOSEr   )rQ   rb   s     r)   _changeRepresentation"TabChordBase._changeRepresentation   sC   8 ""e+"I&,D#$$."I&+D# _``'.5doo.F2;= 
"8<		8M		))#t4SWDI  A%!ZZ//T:
-djj9
 JJNN<4=+ "$ 

 jj"*DJ 4>>""  4,,--(6t7H7H=AAJ)LD% $2$,,8<<E$GDL )7t7H7H=BAJ)LD%  .dll488A C   4,,--(6t7H7H=BAJ)LD% $2$,,8<<E$GDL )7t7H7H=BAJ)LD%  .dll498A CrT   c                   U R                   S:X  a  U R                  5         U R                  S;   a  [        R                  " 5       nGOU R
                  S:X  a  U R                  (       a  U R                  nO\SR                  S U R                  U R                  U R                  4 5       5      nU R                  (       a  USU R                  -   -  nU R                  bV  [        R                  " SU R                  [        R                  5      (       a!  [        U R                  U R                   5      nOU R                  n["        R$                  " UU["        R&                  R(                  ["        R&                  R(                  S	9n[+        U [,        5      (       a  U R.                  R1                  S
S5      UR2                  l        U R.                  R1                  SS5      UR2                  l        U R.                  R1                  SS5      UR2                  l        U R:                  UR2                  l        SUR2                  l        SUl        U$ )a  
Creates and returns a music21.roman.RomanNumeral() object
from a TabChord with all shared attributes.
NB: call changeRepresentation() first if .representationType is not 'm21'
but you plan to process it with m21 (e.g. moving it into a stream).

>>> tabCd = romanText.tsvConverter.TabChord()
>>> tabCd.numeral = 'vii'
>>> tabCd.global_key = 'F'
>>> tabCd.local_key = 'V'
>>> tabCd.representationType = 'm21'
>>> m21Ch = tabCd.tabToM21()

Now we can check it's a music21 RomanNumeral():

>>> m21Ch.figure
'vii'
r^   )@noneNr3   rH   c              3  6   #    U  H  o(       d  M  Uv   M     g 7frZ    ).0attrs     r)   	<genexpr>(TabChordBase.tabToM21.<locals>.<genexpr>U  s      #%LTPTDD%Ls   
	/Nz.*(i*v|v?i+).*
sixthMinorseventhMinorr4   r5   r6           ) rM   ro   r   r   NoChordrO   r   joinr   r   r   r   r#   r$   
IGNORECASEgetLocalKeyr   r   RomanNumeralMinor67DefaultFLAT
isinstanceTabChordrN   rl   	editorialr4   r5   r6   r   r   quarterLength)rQ   	thisEntrycombinedlocalKeyNonRomans       r)   tabToM21TabChordBase.tabToM218  s   & ""f,&&(<<?*)0):I  A%$**:: 77 #&*llDIIt||%L#  $$d&7&7 77H~~)bhh!4>>2==/ /
 $/t~~t#O  $(>> **  //44"1166	I $)) *.b)A	##&)-b)A	##&*.**..*C	##'(,

I%,0I) #&	rT   c           	        UR                  5        H.  u  nu  pV[        X5      (       d  M  [        XU" X   5      5        M0     UR                  5        VVs0 s H  u  pTX   (       d  M  XAU   _M     snnU l        g s  snnf rZ   )itemshasattrsetattrrN   )rQ   rowheadIndicesextraIndicescol_nameitype_to_coerce_tos          r)   populateFromRowTabChordBase.populateFromRowz  s|     1<0A0A0C,H,q4**(9#&(AB	 1D 1=0B0B0D
0DH!f0D

 
s   A<)	A<)r   r   r   rO   rN   r   r   r   r   r   r   r   r   r   rM   r   returnNoner   str)r'   r   )r   zharmony.Harmony)r   	list[str]r   zdict[str, tuple[int, type]]r   zdict[int, str]r   r   )__name__
__module____qualname____firstlineno____doc__rL   propertyrW   setterro   r   r   __static_attributes____classcell__rR   s   @r)   rE   rE      sz    
0     dCL@D

 1
 %	

 

 
rT   rE   c                  0   ^  \ rS rSrSrSU 4S jjrSrU =r$ )r   i  zf
An intermediate representation format for moving between tabular data in
DCML v1 and music21 chords.
c                Z   > [         TU ]  5         S U l        S U l        S U l        SU l        g )Nr2   )rK   rL   r   r   r   rO   rP   s    r)   rL   TabChord.__init__  s+    "&!%59!"rT   )r   rO   r   r   r   )r   r   r   r   r   rL   r   r   r   s   @r)   r   r     s    # #rT   r   c                     ^  \ rS rSrSrSU 4S jjr\SS j5       r\R                  SS j5       r\SS j5       r	\	R                  SS j5       r	\SS j5       r
\
R                  SS	 j5       r
\SS
 j5       r\R                  SS j5       rSrU =r$ )
TabChordV2i  zf
An intermediate representation format for moving between tabular data in
DCML v2 and music21 chords.
c                v   > [         TU ]  5         SU l        SU l        SU l        SU l        SU l        SU l        g )Nr   r}   rH   r3   )rK   rL   r+   r,   r-   r.   r/   rO   rP   s    r)   rL   TabChordV2.__init__  s9    "
 !"rT   c                &    U R                   S-  S-   $ )a  
'beat' has been removed from DCML v2 in favor of 'mn_onset' and
'mc_onset'. 'mn_onset' is equivalent to 'beat', except that 'mn_onset'
is zero-indexed where 'beat' was 1-indexed, and 'mn_onset' is in
fractions of a whole-note rather than in quarter notes.

>>> tabCd = romanText.tsvConverter.TabChordV2()
>>> tabCd.mn_onset = 0.0
>>> tabCd.beat
1.0

>>> tabCd.mn_onset = 0.5
>>> tabCd.beat
3.0

>>> tabCd.beat = 1.5
>>> tabCd.beat
1.5
      @rJ   r,   rV   s    r)   r   TabChordV2.beat  s    . }}s"S((rT   c                4    Ub  US-
  S-  U l         g S U l         g )NrJ   r   r   )rQ   r   s     r)   r   r     s    .2.>s*DrT   c                ,    [        U R                  5      $ )z
'measure' has been removed from DCML v2 in favor of 'mn' and 'mc'. 'mn'
is equivalent to 'measure', so this property is provided as an alias.
intr+   rV   s    r)   r   TabChordV2.measure  s     477|rT   c                :    Ub  [        U5      U l        g S U l        g rZ   r   )rQ   r   s     r)   r   r     s    ")"5#g,4rT   c                    U R                   $ )z
'local_key' has been renamed 'localkey' in DCML v2. This property is
provided as an alias for 'localkey' so that TabChord and TabChordV2 can
be used in the same way.
r/   rV   s    r)   r   TabChordV2.local_key  s     }}rT   c                    Xl         g rZ   r   rQ   ks     r)   r   r     s    rT   c                    U R                   $ )z
'global_key' has been renamed 'globalkey' in DCML v2. This property is
provided as an alias for 'globalkey' so that TabChord and TabChordV2 can
be used in the same way.
r.   rV   s    r)   r   TabChordV2.global_key  s     ~~rT   c                    Xl         g rZ   r   r   s     r)   r   r     s    rT   )rO   r.   r/   r+   r,   r-   r   )r   r!   )r   r!   )r   r   )r   r   r   )r   r   )r   r   r   r   r   rL   r   r   r   r   r   r   r   r   r   s   @r)   r   r     s    # ) )0 
[[I I   ^^@ @        rT   r   c                  b    \ rS rSrSrSSS jjrSS jrSS jrSS jrSS jr	SS jr
SS	 jrS
rg)
TsvHandleri  a  
Conversion starting with a TSV file.

First we need to get a score. (Don't worry about this bit.)

>>> name = 'tsvEg_v1.tsv'
>>> path = common.getSourceFilePath() / 'romanText' / name
>>> handler = romanText.tsvConverter.TsvHandler(path)
>>> handler.tsvToChords()

These should be TabChords now.

>>> testTabChord1 = handler.chordList[0]
>>> testTabChord1.combinedChord
'.C.I6'

Good. We can make them into music21 Roman-numerals.

>>> m21Chord1 = testTabChord1.tabToM21()
>>> m21Chord1.figure
'I'

And for our last trick, we can put the whole collection in a music21 stream.

>>> out_stream = handler.toM21Stream()
>>> out_stream.parts[0].measure(1)[roman.RomanNumeral][0].figure
'I'

c                   US:X  a  [         S   U l        [        U l        O/US:X  a  [         S   U l        [        U l        O[        SU S35      eXl        / U l        S U l        0 U l	        0 U l
        X l        U R                  5       U l        g Nr2   r3   zdcml_version z is not in (1, 2))HEADERSheading_namesr   _tab_chord_clsr   r"   tsvFileName	chordList	m21stream_head_indices_extra_indicesrO   
_importTsvtsvData)rQ   tsvFilerO   s      r)   rL   TsvHandler.__init__  s    1!(D6>DQ!(D",D}\N:KLMM"-/,0@B.0((rT   c                    0 U l         0 U l        [        U5       HC  u  p#X0R                  ;   a!  U R                  U   nX$4U R                   U'   M5  X0R                  U'   ME     g)z
Private method to get column name/column index correspondences.

Expected column indices (those in HEADERS, which correspond to TabChord
attributes) are stored in self._head_indices. Others go in
self._extra_indices.
N)r   r   	enumerater   )rQ   
header_rowr   r   type_to_coerce_col_tos        r)   _get_heading_indicesTsvHandler._get_heading_indices  sc       $Z0KA---(,(:(:8(D%01/I""8,)1##A& 1rT   c                    U R                   n[        USSS9 n[        R                  " USSS9nU R	                  [        U5      5        [        U5      sSSS5        $ ! , (       d  f       g= f)z/
Imports TSV file data for further processing.
rutf-8)encoding	")	delimiter	quotecharN)r   opencsvreaderr   nextlist)rQ   fileNamef	tsvreaders       r)   r   TsvHandler._importTsv/  sT    
 ##(C'2a

1DI%%d9o6	?	 322s   ;A
A+c                    U R                  5       nUR                  XR                  U R                  5        SUl        U$ )z}
Makes a TabChord out of a list imported from TSV data
(a row of the original tabular format -- see TsvHandler.importTsv()).
r^   )r   r   r   r   rM   )rQ   r   r   s      r)   _makeTabChordTsvHandler._makeTabChord<  s<     '')	!!#'9'94;N;NO'-	$rT   c                    U R                   n/ U l        U H4  nU R                  U5      nUc  M  U R                  R                  U5        M6     g)zi
Converts a list of lists (of the type imported by importTsv)
into TabChords (i.e. a list of TabChords).
N)r   r   r   append)rQ   dataentryr   s       r)   tsvToChordsTsvHandler.tsvToChordsH  sI     ||E**51I %%i0 rT   c                   U R                   (       d  U R                  5         U R                  5       nUR                  R	                  5       nUc  U$ U R                    H  nUR
                  S-
  n[        U[        5      (       aI  UR                  (       a8  UR                   [        R                  [        UR                  5      S-
      3nOUR                  nUR                  U5      nUc  [        S5      eUR                  5       nUR                  R!                  UR"                  5        UR%                  XG5        M     UR'                  5       R)                  [*        R,                  SS9  U[*        R,                     R/                  5       nUb5  UR0                  UR2                  R4                  -
  UR4                  -
  Ul        Xl        U$ )aE  
Takes a list of TabChords (self.chordList, prepared by .tsvToChords()),
converts those TabChords in RomanNumerals
(converting to the music21 representation format as necessary),
creates a suitable music21 stream (by running .prepStream() using data from the TabChords),
and populates that stream with the new RomanNumerals.
r2   zm21Measure should not be NoneT)inPlace)r   r   
prepStreampartsfirstr   r   r   r-   r   stringascii_lowercaser   r"   r   r   updaterN   insertflattenextendDurationr   Harmonylastr   
activeSiteoffsetr   )	rQ   sp	thisChordoffsetInMeasuremeasureNumber
m21MeasurethisM21Chordlast_harmonys	            r)   toM21StreamTsvHandler.toM21StreamY  sr    ~~OOGGMMO9HI'nnq0O)Z00Y__ (()&*@*@Y__AUXYAY*Z)[\  !* 1 1=1J! !@AA$--/L"")))//:o<! ($ 	
		""7??D"A)..0#,"9"9"@"@@<CVCVV & rT   c                	   [         R                  " 5       n[         R                  " 5       nSnU R                  S:X  Ga_  UR	                  S[
        R                  " 5       5        U R                  S   n/ nSUR                  ;   aF  UR                  S   UR
                  l	        UR                  SUR
                  R                  -   5        SUR                  ;   aF  UR                  S   UR
                  l        UR                  SUR
                  R                  -   5        SUR                  ;   aF  UR                  S   UR
                  l        UR                  S	UR
                  R                  -   5        U(       a   S
R                  U5      UR
                  l        [        U R                  S   R                   5      n["        R$                  " U5      n[        U R                  S   R&                  5      n[(        R*                  " U5      n	U	R,                  R.                  n
SnU R                  S   R0                  S-
  nSnSnU R                   GH<  n[3        U[4        5      (       aa  UR6                  U:w  aQ  UR6                  (       a1  [8        R:                  " UR6                  S9nUR	                  SU5        OSnUR6                  nOUR0                  U:X  a  M  UR0                  US-   :  aa  [=        US-   UR0                  S-   5       H?  n[         R>                  " US9nX-   Ul         UR	                  U5        UR@                  nUnMA     GO[3        U[4        5      (       aI  UR6                  (       a8  UR0                   [B        RD                  [G        UR6                  5      S-
      3nOUR0                  n[         R>                  " US9nX-   =ol         UR	                  U5        UR&                  U:w  aj  [(        R*                  " UR&                  5      nUR	                  URH                  S-
  U5        UR&                  =(       d    SnUR,                  R.                  n
UR0                  nUc  GM%  Uc  GM+  URK                  U5        GM?     UR                  U5        U[         R>                     RM                  5       nUb$  UR	                  SU5        UR	                  SU	5        U$ )a,  
Prepares a music21 stream for the harmonic analysis to go into.
Specifically: creates the score, part, and measure streams,
as well as some (the available) metadata based on the original TSV data.
Works like the .template() method,
except that we don't have a score to base the template on as such.
Nr2   r   r4   Opr5   Nor6   Mov_r}   rH   )number)'r   ScorePartrO   r   r
   Metadatar   rN   
opusNumberr   r  movementNumberr   titler   r   r	   Keyr   r   TimeSignaturebarDurationr   r   r   r   r-   r   RepeatBracketrangeMeasurer  r   r   r   r   addSpannedElementsr   )rQ   r  r  r(   
firstEntryr  startingKeySigkscurrentTimeSigtscurrentMeasureLengthcurrentOffsetpreviousMeasurepreviousVoltarepeatBracketr   mNor  newTSfirst_measures                       r)   r   TsvHandler.prepStream  s    LLNKKM!%! HHQ))+,*JEz'''(2(8(8(>

%TAJJ$9$99:z'''$.$4$4T$:

!TAJJ$5$556
(((,6,<,<U,C

)UQZZ%>%>>?#&88E?

 T^^A.99:WW^$T^^A.667  0!~~;;25#~~a0881<;?^^E%,,1M;;
 %,$9$9$MM
 HHQ.$(M %/1 }}22 1!4emma6GHCc2A,CAHHHQK$%HHM&)O I eZ00U[[ ==/&*@*@U[[AQTUAU*V)WX " %*MMMNN-8 ,9+OO==N2!//>EHHUZZ!^U3%*]]%8bN+0+<+<+J+J("'--(Q]003c $f 	
&..)//1$  B'  B'rT   )	r   r   r   r   rO   r   r   r   r   N)r2   )r   str | pathlib.PathrO   r   )r   r   r   r   r   zlist[list[str]])r   r   r   rE   r   )r   stream.Score)r   r   r   r   r   rL   r   r   r   r   r  r   r   rt   rT   r)   r   r     s,    :)"2"#
1",\crT   r   c                  N    \ rS rSrSrS
SS jjrSS jrSS jrSS jrSS jr	Sr
g	)M21toTSVi  a  
Conversion starting with a music21 stream.
Exports to tabular data format and (optionally) writes the file.

>>> bachHarmony = corpus.parse('bach/choraleAnalyses/riemenschneider001.rntxt')
>>> bachHarmony.parts[0].measure(1)[0].figure
'I'

The initialization includes the preparation of a list of lists, so

>>> initial = romanText.tsvConverter.M21toTSV(bachHarmony, dcml_version=2)
>>> tsvData = initial.tsvData
>>> from music21.romanText.tsvConverter import DCML_V2_HEADERS
>>> tsvData[1][DCML_V2_HEADERS.index('chord')]
'I'
c                    X l         Xl        US:X  a  [        S   U l        O$US:X  a  [        S   U l        O[	        SU S35      eU R                  5       U l        g r   )version	m21StreamDCML_HEADERSdcml_headersr"   m21ToTsvr   )rQ   r6  rO   s      r)   rL   M21toTSV.__init__  sV    #"1 ,QDQ ,QD}\N:KLMM}}rT   c                b    U R                   S:X  a  U R                  5       $ U R                  5       $ )z
Converts a list of music21 chords to a list of lists
which can then be written to a tsv file with toTsv(), or processed another way.
r2   )r5  _m21ToTsv_v1_m21ToTsv_v2rV   s    r)   r9  M21toTSV.m21ToTsv
  s.    
 <<1$$&&  ""rT   c                X   / n[        U R                  R                  5       R                  S5      5      R                  R
                  nU R                  [        R                      GH  nS nUR                  (       a  UR                  R                  nS nUR                  (       a:  UR                  R                  UR                  :X  a  UR                  R                  n[        5       nUR                  Ul        XVl        UR                  b  UR                  OSUl        [        UR                   5      Ul        S Ul        UR%                  [&        R(                  5      nUc  SUl        OUR,                  Ul        U R                  R.                  R0                  =(       d    SUR2                  S'   U R                  R.                  R4                  =(       d    SUR2                  S'   U R                  R.                  R6                  =(       d    SUR2                  S'   UR8                  Ul        X&l        UR                  R
                  Ul        S Ul         URB                  Ul"        [G        U5      Ul$        [J        RL                  " SURN                  5      nUb  URQ                  S5      Ul)        OSUl)        S Ul*        XFl+        S Ul,        U RZ                   V	s/ s H)  n	[]        XiUR^                  Ra                  U	S5      5      PM+     n
n	URc                  U
5        GM     U$ s  sn	f )Nr   r2   rH   r4   r5   r6   ^\D*(\d.*|))2r   r6  recursegetElementsByClassr	   tonicPitchNameWithCaser   r   secondaryRomanNumeralfigurer   rW   r   r  r   r!   r   r   getContextByClassr   r  r   ratioStringr
   r  rN   r  r  r   r   r   r   r   romanNumeralr   getFormr   r#   r$   figuresWrittenr&   r   r   r   r   r8  getattrr   rl   r   )rQ   r   r   thisRNr   altChordr   r%  figbassMatchnamethisInfos              r)   r<  M21toTSV._m21ToTsv_v1  s   NN""$77G

#$$ 	 nnU%7%78FL++%;;BBH++//33vzzA%;;BBH 
I&,mmI#!)8>8L8L8X 4 4^_I"6;;/IN $I))%*=*=>Bz$&	!$&NN	!$(NN$;$;$F$F$L"IOOD!$(NN$;$;$B$B$HbIOOD!%)^^%<%<%K%K%QrIOOE"%33I#- "(**"C"CI"IO & 3 3I$V_IN88NF4I4IJL'$0$6$6q$9	!$&	! $I%1""&I !---D 	)9)9)=)=dB)GH-   NN8$] 9` s   0L'c                   / nU R                   [        R                     R                  5       nUc  U$ UR                  nUR
                  nU R                   R                  5       R                  [        R                  [        R                  /5       GH#  n[        5       nUR                  Ul        UR                  S-  Ul        UR                  [         R"                  5      nUc  SUl        OUR&                  Ul        XFl        [+        U[        R                  5      (       a  SUl        SUl        GO/[0        R2                  (       a!  [+        U[        R                  5      (       d   e[5        UR                  U5      nS n	UR6                  (       a*  UR6                  R8                  n	[;        U	[=        U5      SS9n	UR8                  R?                  SSS5      Ul        S Ul         URB                  Ul        [E        U5      Ul#        [H        RJ                  " SURL                  5      n
U
b  U
RO                  S5      Ul(        OSUl(        S Ul)        Xl*        S Ul+        Xl,        U RZ                   Vs/ s H)  n[]        XkUR^                  Ra                  US5      5      PM+     nnURc                  U5        GM&     U$ s  snf )	N   rH   rr   r]   rb   dr2   r@  )2r6  r   r   r   r	   rC  rA  rB  r   r~   r   r  r+   r  r,   rF  r   r  r   rG  r   r   r   r   tTYPE_CHECKINGlocalKeyAsRnrD  rE  rh   ri   rj   r   rH  rI  r   r#   r$   rJ  r&   r   r   r   r   r   r8  rK  r   rl   r   )rQ   r   first_rnglobal_key_objr   rL  r   r   r   r   figbassmrO  rP  s                r)   r=  M21toTSV._m21ToTsv_v2L  sK   #% >>%"4"45;;=N!#::
nn,,.AA1
F #I!//IL "(!2I..u/B/BCG$&	!$+$7$7	!#- &'//22$+	!")	??%fe.@.@AAAA(^D	#//#)#?#?#F#FL#1$gi&8J$L #)--"7"7R"C	"&	$*$7$7	!!(	88NF4I4IJ '(0q(9I%(*I%$(	!)5&&*	#&/# !---D 	)9)9)=)=dB)GH-   NN8${
| s   20J<c                   [        USSSS9 n[        R                  " USS[        R                  S9nUR	                  U R
                  5        U R                   H  nUR	                  U5        M     SSS5        g! , (       d  f       g= f)	z>
Writes a list of lists (e.g. from m21ToTsv()) to a tsv file.
arH   r   )newliner   r   r   )r   r   quotingN)r   r   writerQUOTE_MINIMALwriterowr8  r   )rQ   filePathAndNamecsvFilecsvOutr   s        r)   writeM21toTSV.write  sn     /3WEZZ*.*-(+(9(9;F OOD--.!\\		* * FEEs   A%A;;
B	)r8  r6  r   r5  N)r3   )r6  r1  rO   r   r0  )rd  r/  )r   r   r   r   r   rL   r9  r<  r=  rg  r   rt   rT   r)   r3  r3    s#     	'#7rGR+rT   r3  c                    SU R                   ;   a  gSU R                   ;   a  gSU R                   ;   a  gSU R                   ;   a  gSU R                   ;   a  gg)u  
Takes a music21.roman.RomanNumeral object and returns the string indicating
'form' expected by the DCML standard.

>>> romanText.tsvConverter.getForm(roman.RomanNumeral('V'))
''
>>> romanText.tsvConverter.getForm(roman.RomanNumeral('viio7'))
'o'
>>> romanText.tsvConverter.getForm(roman.RomanNumeral('IVM7'))
'M'
>>> romanText.tsvConverter.getForm(roman.RomanNumeral('III+'))
'+'
>>> romanText.tsvConverter.getForm(roman.RomanNumeral('IV+M7'))
'+M'
>>> romanText.tsvConverter.getForm(roman.RomanNumeral('viiø7'))
'%'
rd   rc   oz+M+MrH   )rE  )rns    r)   rI  rI    sT    $ ryy
biiryy 
bii
biirT   c                l   [         R                  " SU 5      nU(       d  U $ UR                  S5      nUR                  S5      nUR                  S5      b  UR                  S5      OSnUR                  S5      nUS:X  a  US	:X  a  S
U-   $ [         R                  " SU[         R                  5      n/ n/ nUS;   a  Sn	OSn	U H  u  ppU
S:X  a  UR                  SU S35        M#  [        U5      nU
S:X  d  X:  a  SnO2US;   a  SnO)US;   a  US:H  =(       d    US:H  nOUS:g  =(       a    US:g  nUb8  U(       a  UR                  SUS-    S35        OUR                  SUS-
   S35        US:X  a  SnUR                  SU U S35        M     USR                  U5      -   SR                  U5      -   U-   $ )a  
Converts DCML added-tone syntax to music21.

>>> romanText.tsvConverter.handleAddedTones('V(64)')
'Cad64'

>>> romanText.tsvConverter.handleAddedTones('i(4+2)')
'i[no3][add4][add2]'

>>> romanText.tsvConverter.handleAddedTones('Viio7(b4-5)/V')
'Viio7[no3][no5][addb4]/V'

When in root position, 7 does not replace 8:

>>> romanText.tsvConverter.handleAddedTones('vi(#74)')
'vi[no3][add#7][add4]'

When not in root position, 7 does replace 8:

>>> romanText.tsvConverter.handleAddedTones('ii6(11#7b6)')
'ii6[no8][no5][add11][add#7][addb6]'

'0' can be used to indicate root-replacement by 7 in a root-position chord.
We need to change '0' to '7' because music21 changes the 0 to 'o' (i.e.,
a diminished chord).

>>> romanText.tsvConverter.handleAddedTones('i(#0)')
'i[no1][add#7]'
zR(?P<primary>.*?(?P<figure>\d*(?:/\d+)*))\((?P<added_tones>.*)\)(?P<secondary>/.*)?primaryrA   	secondaryNrH   rE  rf   64Cad64z
            ([+\-])?  # indicates whether to add or remove chord factor
            ([\^v])?  # indicates whether tone replaces chord factor above/below
            (\#+|b+)?  # alteration
            (1\d|\d)  # figures 0-19, in practice 0-14
        )rH   553z5/337      -z[no]rk  )r2         )r3   rS     #^bvr2   r   z[add)r#   r$   r&   findallrn   r   r   r   )	dcmlChordr(   ro  rA   rp  rE  added_tone_tuples	additions	omissionsomission_thresholdadded_or_removedabove_or_below
alteration
factor_strfactorreplace_aboves                   r)   rk   rk     s   < 	]	A ggi G''-(K()(<(H$bIWWXF#~+-""9;	 	


	: II11DU@*s"s:,a01Zs"f&B My  My  '#-F31FM '#-G.C2GM$  3vzl!!45  3vzl!!45Q;F4
|F81561 EV2 RWWY''"'')*<<yHHrT   c                   U R                   n[        R                  " UR                  5       (       a  SOSU S9n[        R                  " [
        R                  " UR                  5      US9nU R                  S:X  a_  UR                  S:X  aO  UR                  S;   a?  UR                  S   UR                  S   R                  -
  S	-  S
;   a  SUR                  -   $ UR                  $ )aP  
Takes two music21.key.Key objects and returns the roman numeral for
`local_key` relative to `global_key`.

>>> k1 = key.Key('C')
>>> k2 = key.Key('e')
>>> romanText.tsvConverter.localKeyAsRn(k1, k2)
'VI'
>>> k3 = key.Key('C#')
>>> romanText.tsvConverter.localKeyAsRn(k3, k2)
'#VI'
>>> romanText.tsvConverter.localKeyAsRn(k2, k1)
'iii'
r   I)
keyOrScale)keyObjmajorra   )VIVIIr      )	      r~  )rC  r   r   islowerromanNumeralFromChordr   ChordpitchesmoderH  pitchClasses
pitchClass)r   r   letterrm  r   s        r)   rX  rX    s     --F			~~SY
B 	##EKK

$;JOA 	'!joo&@-/"Z%7%7%:%E%EEKwVQ^^##>>rT   c                &    X R                  5       :H  $ )z
Checks whether a key is minor or not simply by upper vs lower case.

>>> romanText.tsvConverter.isMinor('F')
False

>>> romanText.tsvConverter.isMinor('f')
True
)lower)test_keys    r)   ri   ri   9  s     ~~'''rT   c                    US:X  a  SSS.nOUS:X  a  SSS.nO[        S5      eU H  nU R                  XCU   5      n M     U $ )	u  
Character swap function to coordinate between the two notational versions, for instance
swapping between '%' and '/o' for the notation of half diminished (for example).

>>> testStr = 'ii%'
>>> romanText.tsvConverter.characterSwaps(testStr, minor=False, direction='DCML-m21')
'iiø'
r]   rc   )z/ord   r_   rd   rv  )rc   M7z+Direction must be 'm21-DCML' or 'DCML-m21'.)r"   rj   )	preStringra   rb   characterDictthisKeys        r)   rh   rh   F  se     J"" 
j	 "" FGG %%gW/EF	 ! rT   c                   U(       a  [        U [        US   5      SS9n [        R                  " U U[        R                  R
                  [        R                  R
                  S9nUR                  5       R                  nUR                  5       (       a  UR                  5       nU$ UR                  5       (       a  UR                  5       nU$ [        S5      e)a  
Re-casts comparative local key (e.g. 'V of G major') in its own terms ('D').

>>> romanText.tsvConverter.getLocalKey('V', 'G')
'D'

>>> romanText.tsvConverter.getLocalKey('ii', 'C')
'd'

>>> romanText.tsvConverter.getLocalKey('i', 'C')
'c'

By default, assumes an m21 input, and operates as such:

>>> romanText.tsvConverter.getLocalKey('#vii', 'a')
'g#'

Set convert=True to convert from DCML to m21 formats. Hence;

>>> romanText.tsvConverter.getLocalKey('vii', 'a', convertDCMLToM21=True)
'g'

r   r_   r`   rz   z local key must be major or minor)rh   ri   r   r   r   r   rB   rO  isMajorTriadupperisMinorTriadr  r"   )r   r   convertDCMLToM21asRomanrtnewKeys         r)   r   r   `  s    0 "9GJqM4JV`a	  '',,))..	G 
		B M 
				 M ;<<rT   c                `    SU ;  a  UnU$ U R                  S5      nXS-   S n[        XA5      nU$ )a  
Separates comparative Roman-numeral for tonicizations like 'V/vi' into the component parts of
a Roman-numeral (V) and
a (very) local key (vi)
and expresses that very local key in relation to the local key also called (DCML column 11).

While .getLocalKey() work on the figure and key pair:

>>> romanText.tsvConverter.getLocalKey('vi', 'C')
'a'

With .getSecondaryKey(), we're interested in the relative root of a secondaryRomanNumeral:

>>> romanText.tsvConverter.getSecondaryKey('V/vi', 'C')
'a'
ry   r2   N)indexr   )rm  r   very_local_as_keypositionvery_local_as_romans        r)   getSecondaryKeyr    sI    " "}% 	 88C= A/'(;GrT   c                  >    \ rS rSrS rS rS rS rS rS r	S r
S	rg
)Testi  c                j	   SS K nSSS.nS GH  nX#    GH  n[        R                  " 5       S-  U-  nSU;  Ga  [        XSS9n[        U   nUR                  S5      nU R                  UR                  S   U   US	:X  a  S
OS5        U R                  UR                  S   U   S5        UR                  5         UR                  S   n	UR                  S   n
U R                  U	[        5        U R                  U	R                  US	:X  a  S
OS5        U R                  U	R                  S5        U R                  U
R                  S5        U R                  U
R                  S5        U R                  U	R                  S5        U	R                  5         U R                  U	R                  S5        U
R                  5         U R                  U
R                  S5        U	R!                  5       nU
R!                  5       nU R                  UR"                  US	:X  a  S
OS5        U R                  UR"                  S5        U R                  UR$                  R&                  S5        U R                  UR$                  R&                  S5        UR)                  5       nU R                  UR*                  S   R-                  S5      [.        R0                     S   R"                  US	:X  a  S
OS5        [2        R4                  " 5       n[        XSS9nUR)                  5       nUR7                  5       n[9        UUS9R;                  U5        [        UUS9nUR)                  5       nUR=                  U5        U R                  [?        URA                  5       5      [?        URA                  5       5      5        [C        [E        U[F        RH                     U[F        RH                     5      5       H&  u  nu  nnU R                  UUSU SU SU SU 3S9  M(     U[F        RH                     RK                  5       nURL                  RN                  URN                  -   nU R                  [Q        S URA                  5       RS                  [F        RH                  5       5       5      URT                  U-
  5        GM     GM     g )Nr   )ztsvEg_v1.tsv)ztsvEg_v2major.tsvztsvEg_v2minor.tsvr1   	romanTextra   rO   r   r3   I6z.C.I6r2   z	#viio6/iir  #viir^   zC majorzitem z
, version z: z != )msgc              3  :   #    U  H  nUR                   v   M     g 7frZ   )r   )ru   hs     r)   rw   &Test.testTsvHandler.<locals>.<genexpr>  s      !VA !Vs   )+osr   getSourceFilePathr   r7  r  assertEqualr   r   r   assertIsInstancerE   rW   r   rM   ro   r   rE  r	   rO  r  r   r   r   r   r   EnvironmentgetTempFiler3  rg  removelenrA  r   zipr   r   r   r  r  sumrB  r   )rQ   r  
test_filesr5  rO  pathhandlerheaderschord_itestTabChord1testTabChord2	m21Chord1	m21Chord2
out_streamenvLocalforward1stream1	temp_tsv2forward2stream2r   item1item2first_harmonyfirst_offsets                            r)   testTsvHandlerTest.testTsvHandler  s     9

 G"+ //1K?$F$&(DG*73G%mmG4G $$W__Q%7%@'UV,$\cd$$W__Q%7%@+N '')$+$5$5a$8M$+$5$5a$8M))-F$$]%@%@'UV,$\cd$$]%:%:C@$$]%@%@+N$$]%:%:FC $$]%E%EvN!779$$]%:%:C@!779$$]%:%:FC !. 6 6 8I - 6 6 8I $$Y%5%5w!|tQTU$$Y%5%5{C$$Y]]%7%7C$$Y]]%7%7C ")!4!4!6J$$"((+33A6u7I7IJ1MTT '1# '224%dA"..0 %002	w7==iH &igF"..0		)$   W__%6!7W__=N9OP)23GOO,ggoo.F4 *%A~u $$uE!Jwir%PTUZT[*\ % * !( 8 > > @,77>>AUAUU   !(!2!E!Egoo!V  ))L8U , rT   c                4   SS K nSSKJn  UR                  S5      nS H  n[	        X4S9nUR
                  n[        U   R                  S5      nU R                  UR                  S   R                  S5      S   R                  S5        U R                  US   U   S5        [        R                  " 5       nUR                  5       n	UR                  U	5        [!        U	5      n
U R                  U
R
                  S   U   S5        UR#                  U	5        M     g )	Nr   )corpusz-bach/choraleAnalyses/riemenschneider001.rntxtr1   r  r   r2   r  )r  music21r  parser3  r   r7  r  r  r   r   rE  r   r  r  rg  r   r  )rQ   r  r  bachHarmonyr5  initialr   	numeral_ir  tempFr  s              r)   testM21ToTsvTest.testM21ToTsv  s    "ll#RSG{AGooG$W-33I>I[..q199!<Q?FFLWQZ	2C8 #..0H((*EMM%  'GW__Q/	:C@IIe rT   c                l    U R                  [        S5      5        U R                  [        S5      5        g )Nr   F)
assertTrueri   assertFalserV   s    r)   testIsMinorTest.testIsMinor  s$    %&rT   c           
        SnSR                  U Vs/ s H  n[        USS9PM     sn5      nU R                  U[        5        U R                  U[        5        U R	                  [        U5      [        U5      5        U R	                  US5        U R	                  US5        Sn[        USSS9nU R	                  US5        U R	                  US	5        g s  snf )
Nzbefore%afterrH   r_   rT  u   beforeøafterzii%Fr`   u   iiø)r   rh   r  r   r  r  )rQ   	startTextxnewText
testStr1intestStr1outs         r)   testOfCharacterTest.testOfCharacter  s    "	''IVIq>!zBIVWi-gs+YW6N3/2
$Zu
SU+f- Ws   Cc                    [        SS5      nU R                  US5        [        SS5      nU R                  US5        [        SS5      nU R                  US	5        [        S
SSS9nU R                  US5        g )Nrf   GDiiCrU  r  r^  zg#viiT)r  g)r   r  )rQ   test1test2test3test4s        r)   testGetLocalKeyTest.testGetLocalKey.  st    C%$D#&$FC(%E3>$rT   c                r    SnSn[        X5      nU R                  U[        5        U R                  US5        g )NzV/vir  r  )r  r  r   r  )rQ   testRNtestLocalKeyveryLocalKeys       r)   testGetSecondaryKeyTest.testGetSecondaryKey;  s6    &v<lC0s+rT   c                  ^        SU 4S jjn[         R                  " 5       S-  S-  n[        USS9nUR                  5       nU[        R
                     nT R                  [        U5      S5        Uu  pgU" US/5        U" US/5        g )	Nc                   > U [         R                      Vs/ s H  o"R                  5       PM     nnTR                  X15        g s  snf rZ   )r   r  measureNumberWithSuffixr  )rbexpectedMeasuresr(   measure_nosrQ   s       r)   _test_ending_contents/Test.testRepeats.<locals>._test_ending_contentsE  sA     AC6>>@RS@R1446@RKS[; Ts   Ar  ztsvEg_v2_repeats.tsvr3   r  2a2b)r  zspanner.RepeatBracketr  zt.List[str]r   r   )r   r  r   r  r   r  r  r  )rQ   r  r  r  r  rb_iterfirst_endingsecond_endings   `       r)   testRepeatsTest.testRepeatsD  s    	<%	<9D	<	< '')K7:PP T2%%''//0Wq)&-#lTF3mdV4rT   rt   N)r   r   r   r   r  r  r  r  r  r
  r  r   rt   rT   r)   r  r    s'    Wr('.$%,5rT   r  __main__)rm  zroman.RomanNumeralr   r   )r  r   r   r   )r   key.Keyr   r  r   r   )r  r   r   bool)Tr]   )r  r   ra   r  rb   r   r   r   )F)r   r   r   r   r  r  r   r   )rm  r   r   r   r   r   )8r   
__future__r   abcr   	fractionspathlibr#   r   typestypingrV  unittestr  r   r   r   r   r	   r
   r   r   r   r   r  environLocalMappingProxyTyper   r   r!   
V1_HEADERScompiler%   r*   
V2_HEADERSr   DCML_V1_HEADERSDCML_V2_HEADERSr7  ABCrE   r   r   r   r3  rI  rk   rX  ri   rh   r   r  TestCaser  r   mainTestrt   rT   r)   <module>r/     sQ   # 
 
   	              &&( ## %S%% s% E	%
 s% s% e% #% % S% s% C% s% s% C%  !% 
& 8A ##
% 
$ Z
(
2> #7l
377 l
\#| #P Pj{ {~s+ s+nBSIl:
(4)X:q58 q5l zT rT   