
    3j                       d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	Z
ddlZddlmZ ddlmZ ddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZ  ej.                         Z ej2                  i dededededededededededededededede      Z ej<                  d      Zd Z  ej2                  eee eeeeeeeeeeeed       Z!ee!d!Z"d"Z#d#Z$e#e$d!Z% G d$ d%      Z& G d& d'e&      Z' G d( d)e&      Z( G d* d+      Z) G d, d-      Z*d8d.Z+d9d/Z,d:d0Z-d;d1Z.d<d=d2Z/d>d?d3Z0d@d4Z1 G d5 d6ejd                        Z3e4d7k(  rddlZ ejj                  e3       yy)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                    	 t        |       S # t        $ rR t        j                  t        |       }t        |j                  d            t        |j                  d            z  cY S w xY w)Nnumerdenom)float
ValueErrorrematchMN_ONSET_REGEXgroup)valuems     K/DATA/.local/lib/python3.12/site-packages/music21/romanText/tsvConverter.py_float_or_fracr*   B   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                       e Zd ZdZd fdZed	d       Zej                  d
d       ZddZddZ		 	 	 	 	 	 	 	 ddZ
 xZS )TabChordBasez|
    Abstract base class for intermediate representation format for moving
    between tabular data and music21 chords.
    c                   t         |           d| _        d | _        d | _        i | _        d| _        d| _        d| _        d | _	        d | _
        d | _        d | _        d | _        d| _        d| _        d| _        d| _        y )N       ?r2   )super__init__r   r   representationTypeextradcml_versionr   r   r   r   r   r   r   r   r   r   r   self	__class__s    r)   rK   zTabChordBase.__init__   s    &*,0%'
 
#
"	!%!%#' !!	    c                    | j                   S )a  
        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   rP   s    r)   combinedChordzTabChordBase.combinedChord   s     zzrR   c                    || _         y Nr   )rP   r'   s     r)   rU   zTabChordBase.combinedChord   s	    
rR   c                *   | j                   dk(  r
d}d| _         n$| j                   dk(  r
d}d| _         nt        d      t        | j                  t	        | j
                        |      | _        |dk(  r| j                  | j                  j                  dd	      nd| _        | j                  d
k(  r| j                  j                  dd	      | _	        t        | j                        | _	        | j                  j                  dd      dk(  rE| j                  dk7  r6t        j                  dd| j                  t        j                         | _	        t	        | j                        r| j"                  rnt	        | j"                        r;t        | j"                  d|      | _        t        | j                  d|      | _        yt        | j"                  d|      | _        yt        | j                  d|      | _        y| j"                  rnt	        | j"                        r;t        | j"                  d|      | _        t        | j                  d|      | _        yt        | j"                  d|      | _        yt        | j                  d|      | _        y)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=   rG   Mm7Vz
                            (\d+)  # match one or more digits
                            (?![]\d])  # without a digit or a ']' to the right
                        zd\1)flagsTF)rL   r"   characterSwapsr   isMinorr   r   replacerN   r   handleAddedTonesrM   getr   r#   subVERBOSEr   )rP   r_   s     r)   _changeRepresentationz"TabChordBase._changeRepresentation   s:   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rR   c                t   | j                   dk(  r| j                          | j                  dv rt        j                         }n| j
                  dk(  r| j                  r| j                  }nWdj                  d | j                  | j                  | j                  fD              }| j                  r|d| j                  z   z  }| j                  Pt        j                  d| j                  t        j                        r!t        | j                  | j                         }n| j                  }t#        j$                  ||t"        j&                  j(                  t"        j&                  j(                  	      }t+        | t,              r| j.                  j1                  d
d      |j2                  _        | j.                  j1                  dd      |j2                  _        | j.                  j1                  dd      |j2                  _        | j:                  |j2                  _        d|j2                  _        d|_        |S )an  
        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   rG   c              3  &   K   | ]	  }|s|  y wrW    ).0attrs     r)   	<genexpr>z(TabChordBase.tabToM21.<locals>.<genexpr>S  s      #%LTPTD%Ls   /Nz.*(i*v|v?i+).*
sixthMinorseventhMinorr4   r5   r6           ) rL   rl   r   r   NoChordrN   r   joinr   r   r   r   r#   r$   
IGNORECASEgetLocalKeyr   r   RomanNumeralMinor67DefaultFLAT
isinstanceTabChordrM   ri   	editorialr4   r5   r6   r   r   quarterLength)rP   	thisEntrycombinedlocalKeyNonRomans       r)   tabToM21zTabChordBase.tabToM216  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) #&	rR   c           	         |j                         D ]+  \  }\  }}t        | |      st        | | |||                - |j                         D ci c]  \  }}||   s|||    c}}| _        y c c}}w rW   )itemshasattrsetattrrM   )rP   rowheadIndicesextraIndicescol_nameitype_to_coerce_tos          r)   populateFromRowzTabChordBase.populateFromRowx  s     1<0A0A0C,H,q+4*h(9#a&(AB	 1D 1=0B0B0D
0DHAHc!f0D

 
s   A2 A2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____doc__rK   propertyrU   setterrl   r   r   __classcell__rQ   s   @r)   rE   rE      su    
0     dCL@D

 1
 %	

 

rR   rE   c                  $     e Zd ZdZd fdZ xZS )r   zr
    An intermediate representation format for moving between tabular data in
    DCML v1 and music21 chords.
    c                Z    t         |           d | _        d | _        d | _        d| _        y )Nr2   )rJ   rK   r   r   r   rN   rO   s    r)   rK   zTabChord.__init__  s+    "&!%59!"rR   r   )r   r   r   r   rK   r   r   s   @r)   r   r     s    # #rR   r   c                      e Zd ZdZd fdZedd       Zej                  dd       Zedd       Zej                  dd       Zedd       Z	e	j                  dd       Z	edd	       Z
e
j                  dd
       Z
 xZS )
TabChordV2zr
    An intermediate representation format for moving between tabular data in
    DCML v2 and music21 chords.
    c                v    t         |           d| _        d| _        d| _        d| _        d| _        d| _        y )Nr   rx   rG   r3   )rJ   rK   r+   r,   r-   r.   r/   rN   rO   s    r)   rK   zTabChordV2.__init__  s9    "
 !"rR   c                &    | j                   dz  dz   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
              @rI   r,   rT   s    r)   r   zTabChordV2.beat  s    . }}s"S((rR   c                2    ||dz
  dz  | _         y d | _         y )NrI   r   r   )rP   r   s     r)   r   zTabChordV2.beat  s    .2.>s*DrR   c                ,    t        | j                        S )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+   rT   s    r)   r   zTabChordV2.measure  s     477|rR   c                8    |t        |      | _        y d | _        y rW   r   )rP   r   s     r)   r   zTabChordV2.measure  s    ")"5#g,4rR   c                    | j                   S )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/   rT   s    r)   r   zTabChordV2.local_key  s     }}rR   c                    || _         y rW   r   rP   ks     r)   r   zTabChordV2.local_key  s	    rR   c                    | j                   S )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.   rT   s    r)   r   zTabChordV2.global_key  s     ~~rR   c                    || _         y rW   r   r   s     r)   r   zTabChordV2.global_key  s	    rR   r   )r   r!   )r   r!   )r   r   )r   r   r   )r   r   )r   r   r   r   rK   r   r   r   r   r   r   r   r   s   @r)   r   r     s    # ) )0 
[[I I   ^^@ @        rR   r   c                  J    e Zd ZdZd
ddZddZddZddZddZddZ	ddZ
y	)
TsvHandlera  
    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                   |dk(  rt         d   | _        t        | _        n.|dk(  rt         d   | _        t        | _        nt        d| d      || _        g | _        d | _        i | _	        i | _
        || _        | j                         | _        y Nr2   r3   zdcml_version z is not in (1, 2))HEADERSheading_namesr   _tab_chord_clsr   r"   tsvFileName	chordList	m21stream_head_indices_extra_indicesrN   
_importTsvtsvData)rP   tsvFilerN   s      r)   rK   zTsvHandler.__init__  s    1!(D6>DQ!(D",D}\N:KLMM"-/,0@B.0((rR   c                    i | _         i | _        t        |      D ]C  \  }}|| j                  v r!| j                  |   }||f| j                   |<   5|| j                  |<   E y)a  
        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   )rP   
header_rowr   r   type_to_coerce_col_tos        r)   _get_heading_indiceszTsvHandler._get_heading_indices  sl       $Z0KAx4---(,(:(:8(D%013H/I""8,)1##A& 1rR   c                    | j                   }t        |dd      5 }t        j                  |dd      }| j	                  t        |             t        |      cddd       S # 1 sw Y   yxY w)z?
        Imports TSV file data for further processing.
        rutf-8)encoding	")	delimiter	quotecharN)r   opencsvreaderr   nextlist)rP   fileNamef	tsvreaders       r)   r   zTsvHandler._importTsv-  sT    
 ##(C'2a

1DI%%d9o6	?	 322s   =A""A+c                    | j                         }|j                  || j                  | j                         d|_        |S )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   rL   )rP   r   r   s      r)   _makeTabChordzTsvHandler._makeTabChord:  s>     '')	!!#t'9'94;N;NO'-	$rR   c                    | j                   }g | _        |D ]1  }| j                  |      }|| j                  j                  |       3 y)z
        Converts a list of lists (of the type imported by importTsv)
        into TabChords (i.e. a list of TabChords).
        N)r   r   r   append)rP   dataentryr   s       r)   tsvToChordszTsvHandler.tsvToChordsF  sI     ||E**51I %%i0 rR   c                   | j                   s| j                          | j                         }|j                  j	                         }||S | j                   D ]  }|j
                  dz
  }t        |t              rD|j                  r8|j                   t        j                  t        |j                        dz
      }n|j                  }|j                  |      }|t        d      |j                         }|j                  j!                  |j"                         |j%                  ||        |j'                         j)                  t*        j,                  d       |t*        j,                     j/                         }|5|j0                  |j2                  j4                  z
  |j4                  z
  |_        || _        |S )au  
        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   updaterM   insertflattenextendDurationr   Harmonylastr   
activeSiteoffsetr   )	rP   sp	thisChordoffsetInMeasuremeasureNumber
m21MeasurethisM21Chordlast_harmonys	            r)   toM21StreamzTsvHandler.toM21StreamW  ss    ~~OOGGMMO9HI'nnq0O)Z0Y__ (()&*@*@Y__AUXYAY*Z)[\  !* 1 1=1J! !@AA$--/L"")))//:o|<! ($ 	
		""7??D"A)..0#,"9"9"@"@@<CVCVV & rR   c                z	   t        j                         }t        j                         }d}| j                  dk(  rS|j	                  dt        j                                | j                  d   }g }d|j                  v rF|j                  d   |j
                  _	        |j                  d|j
                  j                  z          d|j                  v rF|j                  d   |j
                  _        |j                  d|j
                  j                  z          d|j                  v rF|j                  d   |j
                  _        |j                  d	|j
                  j                  z          |r d
j                  |      |j
                  _        t        | j                  d   j                         }t#        j$                  |      }t        | j                  d   j&                        }t)        j*                  |      }	|	j,                  j.                  }
d}| j                  d   j0                  dz
  }d}d}| j                  D ]  }t3        |t4              r]|j6                  |k7  rN|j6                  r3t9        j:                  |j6                        }|j	                  d|       nd}|j6                  }n|j0                  |k(  r|j0                  |dz   kD  rbt=        |dz   |j0                  dz         D ]A  }t        j>                  |      }||
z   |_         |j	                  |       |j@                  }|}C nt3        |t4              rD|j6                  r8|j0                   tB        jD                  tG        |j6                        dz
      }n|j0                  }t        j>                  |      }||
z   x}|_         |j	                  |       |j&                  |k7  rdt)        j*                  |j&                        }|j	                  |jH                  dz
  |       |j&                  xs d}|j,                  j.                  }
|j0                  }|||jK                  |       " |j                  |       |t         j>                     jM                         }|$|j	                  d|       |j	                  d|	       |S )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_rx   rG   )number)'r   ScorePartrN   r   r
   Metadatar   rM   
opusNumberr   r   movementNumberrz   titler   r   r	   Keyr   r   TimeSignaturebarDurationr   r   r   r   r-   r   RepeatBracketrangeMeasurer   r   r   r   r   addSpannedElementsr   )rP   r   r   r(   
firstEntryr  startingKeySigkscurrentTimeSigtscurrentMeasureLengthcurrentOffsetpreviousMeasurepreviousVoltarepeatBracketr   mNor   newTSfirst_measures                       r)   r   z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,/CCAHHHQK$%HHM&)O I eZ0U[[ ==/&*@*@U[[AQTUAU*V)WX " %*MMMNN-8 ,9;O+OO==N2!//>EHHUZZ!^U3%*]]%8bN+0+<+<+J+J("'--(Q]003c $f 	
&..)//1$  B'  B'rR   N)r2   )r   str | pathlib.PathrN   r   )r   r   r   r   r   zlist[list[str]])r   r   r   rE   r   )r   stream.Score)r   r   r   r   rK   r   r   r   r   r   r   rp   rR   r)   r   r     s,    :)"2"#
1",\crR   r   c                  :    e Zd ZdZdd	dZd
dZd
dZd
dZddZy)M21toTSVa@  
    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                    || _         || _        |dk(  rt        d   | _        n#|dk(  rt        d   | _        nt	        d| d      | j                         | _        y r   )version	m21StreamDCML_HEADERSdcml_headersr"   m21ToTsvr   )rP   r   rN   s      r)   rK   zM21toTSV.__init__  sZ    #"1 ,QDQ ,QD}\N:KLMM}}rR   c                `    | j                   dk(  r| j                         S | j                         S )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   )r  _m21ToTsv_v1_m21ToTsv_v2rT   s    r)   r#  zM21toTSV.m21ToTsv  s.    
 <<1$$&&  ""rR   c                   g }t        | j                  j                         j                  d            j                  j
                  }| j                  t        j                     D ]  }d }|j                  r|j                  j                  }d }|j                  r9|j                  j                  |j                  k(  r|j                  j                  }t               }|j                  |_        ||_        |j                  |j                  nd|_        t        |j                         |_        d |_        |j%                  t&        j(                        }|d|_        n|j,                  |_        | j                  j.                  j0                  xs d|j2                  d<   | j                  j.                  j4                  xs d|j2                  d<   | j                  j.                  j6                  xs d|j2                  d<   |j8                  |_        ||_        |j                  j
                  |_        d |_         |jB                  |_"        tG        |      |_$        tK        jL                  d|jN                        }||jQ                  d      |_)        nd|_)        d |_*        ||_+        d |_,        | jZ                  D 	cg c])  }	t]        ||	|j^                  ja                  |	d            + }
}	|jc                  |
        |S c c}	w )Nr}   r2   rG   r4   r5   r6   ^\D*(\d.*|))2r   r   recursegetElementsByClassr	   tonicPitchNameWithCaser   r}   secondaryRomanNumeralfigurer   rU   r   r   r   r!   r   r   getContextByClassr   r  r   ratioStringr
   r  rM   r   r  r   r   r   r   r   romanNumeralr   getFormr   r#   r$   figuresWrittenr&   r   r   r   r   r"  getattrr   ri   r   )rP   r   r   thisRNr   altChordr   r  figbassMatchnamethisInfos              r)   r%  zM21toTSV._m21ToTsv_v1  s   NN""$77G

#$$ 	 nnU%7%78FL++%;;BBH++//33vzzA%;;BBH 
I&,mmI#!)I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#-I "(**"C"CI"IO & 3 3I$V_IN88NF4I4IJL'$0$6$6q$9	!$&	! $I%1I""&I !---D 	4)9)9)=)=dB)GH-   NN8$] 9` s   .Lc                V   g }| j                   t        j                     j                         }||S |j                  }|j
                  }| j                   j                         j                  t        j                  t        j                  g      D ]  }t               }|j                  |_        |j                  dz  |_        |j                  t         j"                        }|d|_        n|j&                  |_        ||_        t+        |t        j                        rd|_        d|_        n#t0        j2                  rt+        |t        j                        sJ t5        |j                  |      }d }	|j6                  r-|j6                  j8                  }	t;        |	t=        |      d      }	|j8                  j?                  ddd      |_        d |_         |jB                  |_        tE        |      |_#        tI        jJ                  d|jL                        }
|
|
jO                  d      |_(        nd|_(        d |_)        |	|_*        d |_+        ||_,        | jZ                  D cg c])  }t]        |||j^                  ja                  |d            + }}|jc                  |        |S c c}w )	N   rG   rn   rZ   r_   dr2   r(  )2r   r   r}   r   r	   r+  r)  r*  r   ry   r   r   r+   r   r,   r.  r   r  r   r/  r   r   r   r   tTYPE_CHECKINGlocalKeyAsRnr,  r-  re   rf   rg   r   r0  r1  r   r#   r$   r2  r&   r   r   r   r   r   r"  r3  r   ri   r   )rP   r   first_rnglobal_key_objr   r4  r   r   r   r   
fig_bass_mr7  r8  s                r)   r&  zM21toTSV._m21ToTsv_v2J  sO   #% >>%"4"45;;=N!#::
nn,,.AA1
F #I!//IL "(!2I..u/B/BCG$&	!$+$7$7	!#-I &'//2$+	!")	??%fe.@.@AAA(^D	#//#)#?#?#F#FL#1$gi&8J$L #)--"7"7R"C	"&	$*$7$7	!!(	XXnf6K6KL
 )(2(8(8(;I%(*I%$(	!)5	&&*	#&/	# !---D 	4)9)9)=)=dB)GH-   NN8${
| s    .J&c                   t        |ddd      5 }t        j                  |ddt        j                        }|j	                  | j
                         | j                  D ]  }|j	                  |        	 ddd       y# 1 sw Y   yxY w)	zN
        Writes a list of lists (e.g. from m21ToTsv()) to a tsv file.
        arG   r   )newliner   r   r   )r   r   quotingN)r   r   writerQUOTE_MINIMALwriterowr"  r   )rP   filePathAndNamecsvFilecsvOutr   s        r)   writezM21toTSV.write  sn     /3WEZZ*.*-(+(9(9;F OOD--.!\\		* * FEEs   A%A??BN)r3   )r   r  rN   r   r  )rJ  r  )	r   r   r   r   rK   r#  r%  r&  rM  rp   rR   r)   r  r    s#     	'#7rGR+rR   r  c                    d| j                   v ryd| j                   v ryd| j                   v ryd| j                   v ryd| j                   v ryy)uC  
    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'))
    '%'
    ra   r`   oz+M+MrG   )r-  )rns    r)   r1  r1    sT    $ ryy
biiryy 
bii
biirR   c                $   t        j                  d|       }|s| S |j                  d      }|j                  d      }|j                  d      |j                  d      nd}|j                  d      }|dk(  r
|d	k(  rd
|z   S t        j                  d|t         j                        }g }g }|dv rd}	nd}	|D ]  \  }
}}}|
dk(  r|j                  d| d       #t        |      }|
dk(  s||	k\  rd}n$|dv rd}n|dv r|dk(  xs |dk(  }n|dk7  xr |dk7  }|3|r|j                  d|dz    d       n|j                  d|dz
   d       |dk(  rd}|j                  d| | d        |dj                  |      z   dj                  |      z   |z   S )aY  
    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   	secondaryNrG   r-  rc   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
        )rG   553z5/337      -z[no]rP  )r2         )r3   r:     #^bvr2   r   z[add)r#   r$   r&   findallrk   r   r   rz   )	dcmlChordr(   rT  rA   rU  r-  added_tone_tuples	additions	omissionsomission_thresholdadded_or_removedabove_or_below
alteration
factor_strfactorreplace_aboves                   r)   rh   rh     s   < 	]	A ggi G''-(K()(<(H$bIWWXF#~+-""9;	 	


	: II11DU@.*js"s:,a01Zs"f0B&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HrR   c                   | j                   }t        j                  |j                         rdnd|       }t        j                  t        j                  |j                        |      }| j                  dk(  rZ|j                  dk(  rK|j                  dv r=|j                  d   |j                  d   j                  z
  d	z  d
v rd|j                  z   S |j                  S )a  
    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majorr^   )VIVIIr      )	      rc  )r+  r   r}   islowerromanNumeralFromChordr   Chordpitchesmoder0  pitchClasses
pitchClass)r   r   letterrR  r   s        r)   r?  r?    s     --F			~~SY
B 	##EKK

$;JOA 	'!joo&@-/"Z%7%7%:%E%EEKwVQ^^##>>rR   c                (    | | j                         k(  S )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)   rf   rf   7  s     x~~'''rR   c                    |dk(  rddd}n|dk(  rddd}nt        d      |D ]  }| j                  |||         }  | S )	u4  
    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ø'
    rZ   r`   )z/ora   r\   ra   r[  )r`   M7z+Direction must be 'm21-DCML' or 'DCML-m21'.)r"   rg   )	preStringr^   r_   characterDictthisKeys        r)   re   re   D  sg     J"" 
j	 "" FGG %%g}W/EF	 ! rR   c                   |rt        | t        |d         d      } t        j                  | |t        j                  j
                  t        j                  j
                        }|j                         j                  }|j                         r|j                         }|S |j                         r|j                         }|S t        d      )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]   ru   z local key must be major or minor)re   rf   r   r}   r~   r   rB   r7  isMajorTriadupperisMinorTriadr  r"   )r   r   convertDCMLToM21asRomanrtnewKeys         r)   r|   r|   ^  s    0 "9GJqM4JV`a	  '',,))..	G 
		B M 
			 M ;<<rR   c                `    d| vr|}|S | j                  d      }| |dz   d }t        ||      }|S )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'
    rt   r2   N)indexr|   )rR  r   very_local_as_keypositionvery_local_as_romans        r)   getSecondaryKeyr    sM    " "}% 	 88C= A/'(;YGrR   c                  6    e Zd Zd Zd Zd Zd Zd Zd Zd Z	y)	Testc                r	   dd l }ddd}dD ]  }||   D ]  }t        j                         dz  |z  }d|vrt        ||      }t        |   }|j                  d      }| j                  |j                  d   |   |d	k(  rd
nd       | j                  |j                  d   |   d       |j                          |j                  d   }	|j                  d   }
| j                  |	t               | j                  |	j                  |d	k(  rd
nd       | j                  |	j                  d       | j                  |
j                  d       | j                  |
j                  d       | j                  |	j                  d       |	j                          | j                  |	j                  d       |
j                          | j                  |
j                  d       |	j!                         }|
j!                         }| j                  |j"                  |d	k(  rd
nd       | j                  |j"                  d       | j                  |j$                  j&                  d       | j                  |j$                  j&                  d       |j)                         }| j                  |j*                  d   j-                  d      t.        j0                     d   j"                  |d	k(  rd
nd       t3        j4                         }t        ||      }|j)                         }|j7                         }t9        ||      j;                  |       t        ||      }|j)                         }|j=                  |       | j                  t?        |jA                               t?        |jA                                      tC        tE        |tF        jH                     |tF        jH                                 D ](  \  }\  }}| j                  ||d| d| d| d|        * |tF        jH                     jK                         }|jL                  jN                  |jN                  z   }| j                  tQ        d |jA                         jS                  tF        jH                        D              |jT                  |z
           y )Nr   )ztsvEg_v1.tsv)ztsvEg_v2major.tsvztsvEg_v2minor.tsvr1   	romanTextr^   rN   r   r3   I6z.C.I6r2   z	#viio6/iirt  #viir[   zC majorzitem z
, version z: z != )msgc              3  4   K   | ]  }|j                     y wrW   )r   )rq   hs     r)   rs   z&Test.testTsvHandler.<locals>.<genexpr>  s      !VA !Vs   )+osr   getSourceFilePathr   r!  r  assertEqualr   r   r   assertIsInstancerE   rU   r   rL   rl   r   r-  r	   r7  r   r   r   r   r}   r   EnvironmentgetTempFiler  rM  removelenr)  r   zipr   r   r   r   r   sumr*  r   )rP   r  
test_filesr  r7  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)   testTsvHandlerzTest.testTsvHandler  s&    9

 G"7+ //1K?$F$&(G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 , rR   c                6   dd l }ddlm} |j                  d      }dD ]  }t	        ||      }|j
                  }t        |   j                  d      }| j                  |j                  d   j                  d      d   j                  d       | j                  |d   |   d       t        j                         }|j                         }	|j                  |	       t!        |	      }
| j                  |
j
                  d   |   d       |j#                  |	        y )	Nr   )corpusz-bach/choraleAnalyses/riemenschneider001.rntxtr1   r  r   r2   rt  )r  music21r  parser  r   r!  r  r  r   r   r-  r   r  r  rM  r   r  )rP   r  r  bachHarmonyr  initialr   	numeral_ir  tempFr  s              r)   testM21ToTsvz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 rR   c                l    | j                  t        d             | j                  t        d             y )Nr   F)
assertTruerf   assertFalserT   s    r)   testIsMinorzTest.testIsMinor  s$    %&rR   c           	        d}dj                  |D cg c]  }t        |d       c}      }| j                  |t               | j                  |t               | j	                  t        |      t        |             | j	                  |d       | j	                  |d       d}t        |dd      }| j	                  |d       | j	                  |d	       y c c}w )
Nzbefore%afterrG   r\   r;  u   beforeøafterzii%Fr]   u   iiø)rz   re   r  r   r  r  )rP   	startTextxnewText
testStr1intestStr1outs         r)   testOfCharacterz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                    t        dd      }| j                  |d       t        dd      }| j                  |d       t        dd      }| j                  |d	       t        d
dd      }| j                  |d       y )Nrc   GDiiCr<  r  rD  zg#viiT)r  g)r|   r  )rP   test1test2test3test4s        r)   testGetLocalKeyzTest.testGetLocalKey,  st    C%$D#&$FC(%E3>$rR   c                t    d}d}t        ||      }| j                  |t               | j                  |d       y )NzV/vir  re  )r  r  r   r  )rP   testRNtestLocalKeyveryLocalKeys       r)   testGetSecondaryKeyzTest.testGetSecondaryKey9  s8    &v|<lC0s+rR   c                    	 	 	 	 	 	 d fd}t        j                         dz  dz  }t        |d      }|j                         }|t        j
                     } j                  t        |      d       |\  }} ||dg        ||dg       y )	Nc                    | t         j                     D cg c]  }|j                          }}j                  ||       y c c}w rW   )r   r
  measureNumberWithSuffixr  )rbexpectedMeasuresr(   measure_nosrP   s       r)   _test_ending_contentsz/Test.testRepeats.<locals>._test_ending_contentsC  sF     AC6>>@RS@R11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  )rP   r  r  r  r  rb_iterfirst_endingsecond_endings   `       r)   testRepeatszTest.testRepeatsB  s    	<%	<9D	<	< '')K7:PP T2%%''//0Wq)&-#mlTF3mdV4rR   N)
r   r   r   r  r  r  r  r  r  r  rp   rR   r)   r  r    s'    Wr('.$%,5rR   r  __main__)rR  zroman.RomanNumeralr   r   )rh  r   r   r   )r   key.Keyr   r  r   r   )r  r   r   bool)TrZ   )r  r   r^   r  r_   r   r   r   )F)r   r   r   r   r  r  r   r   )rR  r   r   r   r   r   )6r   
__future__r   r   	fractionspathlibr#   r   typestypingr=  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_HEADERSr!  rE   r   r   r   r  r1  rh   r?  rf   re   r|   r  TestCaser  r   mainTestrp   rR   r)   <module>r	     sR   # 
   	              &{&&( $U## %S%% s% E	%
 s% s% e% #% % S% s% C% s% s% C%  !% 
& 8A $U##
% 
$ Z
(
2> #7l
 l
\#| #P Pj{ {~s+ s+nBSIl:
(4)X:q58 q5l zGT rR   