
    rh*                        S r SSKJr  SSKrSSKrSSKrS r " S S5      r " S S5      r	 " S	 S
\R                  5      r\S:X  a  SSKr\R                  " \5        gg)z
Harmonic annotations from humdrum to `music21`.

The `**harm` representation is described here: https://www.humdrum.org/rep/harm/
    )annotationsNc                   [        5       R                  U 5      nU(       d  gUS   S:X  a	  SnS/US'   O#US   S:X  a  SnOUS   S	:X  a	  S	nS/US'   OUS   nS
nUS   (       a  US   nS
nUS   (       a  US   nSnUS   (       a  SUS   ;   a  SnUS   (       a  / SQR                  US   5      nOSnS
nUS:X  a  U(       a  SOS
nO,US:X  a  U(       a  SOSnOUS:X  a  U(       a  SOSnOUS:X  a  Sn/ nUS   (       a?  UR                  S
5        US   n	U	(       a"  UR                  U	S   5        U	S   n	U	(       a  M"  S
n	U(       a  SR	                  U5      n	X2-   U-   U-   U	-   $ )a   
Converts a `**harm` string into a string that
can be used to instantiate a RomanNumeral object.

This is necessary because the two notations are not
identical. For example, a "V7b" in `**harm` turns into "V65".

Instantiate a HarmParser to process `**harm` strings

>>> convertHarmToRoman = humdrum.harmparser.convertHarmToRoman

Convert a few `**harm` strings to music21.roman.RomanNumeral figures

>>> diatonicTriads = ['I', 'Vc', 'Ib', 'iib', 'V', 'viiob', 'vib']
>>> [convertHarmToRoman(x) for x in diatonicTriads]
['I', 'V64', 'I6', 'ii6', 'V', 'viio6', 'vi6']

A few seventh-chord inversions
>>> diatonicSevenths = ['V7', 'viio7c', 'V7d', 'viio7b', 'V7c']
>>> [convertHarmToRoman(x) for x in diatonicSevenths]
['V7', 'viio43', 'V2', 'viio65', 'V43']

Inversion-wise, augmented sixth chords are a bit tricky
German and French are treated as seventh-chords (4 notes)
Italians are treated as triads

>>> italianSixths = ['Lt', 'Ltb', 'Ltc']
>>> [convertHarmToRoman(x) for x in italianSixths]
['It', 'It6', 'It64']

>>> frenchSixths = ['Fr', 'Frb', 'Frc', 'Frd']
>>> [convertHarmToRoman(x) for x in frenchSixths]
['Fr7', 'Fr65', 'Fr43', 'Fr2']

>>> germanSixths = ['Gn', 'Gnb', 'Gnc', 'Gnd']
>>> [convertHarmToRoman(x) for x in germanSixths]
['Ger7', 'Ger65', 'Ger43', 'Ger2']
NrootGnGer7	intervalsLtItFr 
accidental	attributeFT	inversion)abcdr      656   4364   2	secondary/)
HarmParserparseindexappendjoin)
harmStrharmdegree
alterationfifthsQuality	isSeventhinversionNumberr   secondaryFunctionsr   s
             T/home/james-whalen/.local/lib/python3.13/site-packages/music21/humdrum/harmparser.pyconvertHarmToRomanr-      s   R <g&DF|t E[	f		f	 E[fJL,'
MK[)IKSD$55	K.44T+5FGI!$C"		A	%D3		A	%D4		A		K!!"%%	%%i&78!+.I i IHH/0	.:YFF    c                  l    \ rS rSrSrSrSrSrSrSr	Sr
S	rS
rS\-   \-   \-   \-   \	-   \-   \-   S-   rSrg)HarmDefsw   z9
Regular expression definitions for the HarmParser class
zK
    (?P<accidental>          # Named group _accidental
    [#-]{0,2})
    z
    (?P<root>               # Named group _root_
     i|ii|iii|iv|v|vi|vii|  # Minor mode degrees
     I|II|III|IV|V|VI|VII|  # Major mode degrees
     N|Gn|Lt|Fr|Tr)         # Special chords
    zF
    (?P<attribute>          # Named group _attribute_
    [o+]?)
    aO  
    ((?P<intervals>         # Named group _intervals
    \d+|[mMPAD]\d+|         # Detect minor, Major, Augmented or Diminished intervals
    AA\d+|                  # Double-augmented intervals
    DD\d+)                  # Double-diminished intervals
    *)                      # Not a limit on how many intervals can be added
    z
    (?P<inversion>          # Named group _inversions_
    [b-d]?)                 # Only third inversions possible so far
    a  
    ^(                      # Match for entire string or fail
    \(                      # Open parenthesis
    (?P<implied_harmony>    # Group the expression
    ([^\(^\)])+             # At least one expression
    )                       # /Group the expression
    \)                      # Closing parenthesis
    )$                      # /Match for entire string or fail
    aQ  
    (\[                     # Open brackets
    (?P<alternative>        # Named group _alternative_
    ([^\[^\]])+)            # Match at least one time for any expression inside brackets
    \]                      # Close brackets
    )?                      # If no alternative expression, then no brackets should appear at all
    a(  
    (/                      # Slash implies a secondary function
    (?P<secondary>          # Named group _secondary_
    ([\s\S])+)              # Get all the expression after the slash symbol
    )?                      # If no secondary function, then the slash symbol should not appear
    z^(z)$ N)__name__
__module____qualname____firstlineno____doc__r   rootsr   r	   r   impliedalternativer   harmExpression__static_attributes__r2   r.   r,   r0   r0   w   s    
JEIIIGKI 

  	
    Nr.   r0   c                  6    \ rS rSrSr\" 5       rS rSS jrSr	g)r      z)
Parses an expression in `**harm` syntax
c                   [         R                  " [        R                  R                  [         R
                  5      U l        [         R                  " [        R                  R                  [         R
                  5      U l        g )N)	recompiler   defsr;   VERBOSE
harmRegExpr9   impliedRegExp)selfs    r,   __init__HarmParser.__init__   sC    **Z__%C%CRZZPZZ
(?(?Lr.   c                   U R                   R                  U5      nU(       a2  UR                  5       S   nU R                  U5      nU(       a  SUS'   U$ U R                  R                  U5      nU(       aW  UR                  5       nSUS'   US   b  US   nU R                  U5      nXtS'   US   b  US   nU R                  U5      n	XS'   U$ 0 $ )Nimplied_harmonyTr9   Fr:   r   )rE   match	groupdictr    rD   )
rF   r;   impliedMatchimpliedHarmonymmatchHarmRegExpalternativeExpressionr   secondaryExpressionss
             r,   r    HarmParser.parse   s     ))//?)3356GHN

>*A#)H #oo33NCO#--/$)]#/,-m,<)

#89A'(m$[>-*+K.'

#67A%&kNIr.   )rD   rE   N)returnzdict[str, t.Any])
r3   r4   r5   r6   r7   r0   rB   rG   r    r<   r2   r.   r,   r   r      s     :DMr.   r   c                  ,    \ rS rSrS rS rS rS rSrg)Test   c                    / SQnSnU Vs/ s H  n[        U5      PM     nnU R                  [        U5      U5        g s  snf )N)IIbIciiiibiiciiiiiibiiicIVIVbIVcVVbVcvivibvicviioviiobviioc)rZ   I6I64r]   ii6ii64r`   iii6iii64rc   IV6IV64rf   V6V64ri   vi6vi64rl   viio6viio64r-   assertEqualtuple)rF   
harmTriadsgroundTruthxromanTriadss        r,   
testTriadsTest.testTriads   sH    


 7AAj)!,jA{+[9 B   >c                    / SQnSnU Vs/ s H  n[        U5      PM     nnU R                  [        U5      U5        g s  snf )N)I7I7bI7cI7dii7ii7bii7cii7diii7iii7biii7ciii7dIV7IV7bIV7cIV7dV7V7bV7cV7dvi7vi7bvi7cvi7dviio7viio7bviio7cviio7d)r   I65I43I2r   ii65ii43ii2r   iii65iii43iii2r   IV65IV43IV2r   V65V43V2r   vi65vi43vi2r   viio65viio43viio2r}   )rF   harmSeventhsr   r   romanSeventhss        r,   testSeventhsTest.testSevenths  sH    

 9EE1+A.E}-{; Fr   c                    / SQnSnU Vs/ s H  n[        U5      PM     nnU R                  [        U5      U5        g s  snf )N)NNbNcr
   LtbLtcr   FrbFrcFrdr   GnbGncGnd)r   N6N64r   It6It64Fr7Fr65Fr43Fr2Ger7Ger65Ger43Ger2r}   )rF   harmSpecialChordsr   r   romanSpecialChordss        r,   testSpecialChordsTest.testSpecialChords!  sM    

 >OO=N03=NO12K@ Pr   c                    / SQnSnU Vs/ s H  n[        U5      PM     nnU R                  [        U5      U5        g s  snf )N)z-IIbz--IIc#IV7z##IV7b)z-II6z--II64r   z##IV65r}   )rF   harmAlterationsr   r   romanAlterationss        r,   testAlterationsTest.testAlterations1  sJ    

 <KK?a.q1?K/0+> Lr   r2   N)	r3   r4   r5   r6   r   r   r   r   r<   r2   r.   r,   rW   rW      s    :.<,A ?r.   rW   __main__)r7   
__future__r   r@   typingtunittestr-   r0   r   TestCaserW   r3   music21mainTestr2   r.   r,   <module>r      sm   
 # 	  ^GBM M`( (VG?8 G?T zT r.   