
    rh              
         S r SSKJr  SSKJr  SSKJrJ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  \	R(                  (       a  SSKJr  \R,                  " S5      r " S S\R0                  5      r " S S5      r " S S\5      r " S S\5      r\r " S S\5      r " S S\5      r " S S\5      r  " S S\5      r!\\\\ \!/r" " S  S!\5      r# " S" S#\5      r$    S)S$ jr%S*S% jr& " S& S'\
RN                  5      r(\%\\#\$\\\\ \\!/
r)\*S(:X  a  SSKr\RV                  " \(5        gg)+a  
Modular analysis procedures for use alone or
applied with :class:`music21.analysis.windowed.WindowedAnalysis` class.

All procedures should inherit from
:class:`music21.analysis.discrete.DiscreteAnalysis`,
or provide a similar interface.

The :class:`music21.analysis.discrete.KrumhanslSchmuckler`
(for algorithmic key detection) and
:class:`music21.analysis.discrete.Ambitus` (for pitch range analysis) provide examples.
    )annotations)OrderedDict)IterableSequenceN)environment)exceptions21)harmony)interval)note)key)
percussion)pitchstreamzanalysis.discretec                      \ rS rSrSrg)DiscreteAnalysisException3    N)__name__
__module____qualname____firstlineno____static_attributes__r       S/home/james-whalen/.local/lib/python3.13/site-packages/music21/analysis/discrete.pyr   r   3   s    r   r   c                      \ rS rSr% SrSr/ rS\S'   SS jrSS jr	SS	 jr
S
 rS rS rS rSSS jjrS rS rS rS rSrg)DiscreteAnalysis7   a  
Parent class for analytical methods.

Each analytical method returns a discrete numerical (or other)
results as well as a color.  Colors can be used in mapping output.

Analytical methods may make use of a `referenceStream` to
configure the processor on initialization.
 z	list[str]identifiersNc                ,    Xl         / U l        / U l        g N)_referenceStreamsolutionsFoundalternativeSolutions)selfreferenceStreams     r   __init__DiscreteAnalysis.__init__E   s     / ! %'!r   c                ~    [        US   5      [        US   5      [        US   5      4nSUS   S US   S US   S 3$ )z
Utility conversion method

>>> da = analysis.discrete.DiscreteAnalysis()
>>> ffffff = (255, 255, 255)
>>> da._rgbToHex(ffffff)
'#ffffff'
r         #02x)round)r&   rgbs     r   	_rgbToHexDiscreteAnalysis._rgbToHexR   sO     CFmU3q6]E#a&M93q6#,s1vcl3q6#,77r   c           	        ^^ TR                  S5      m[        T5      m[        UU4S j[        STTS-  5       5       5      $ )z
Utility conversion method for six-digit hex values to RGB lists.

>>> da = analysis.discrete.DiscreteAnalysis()
>>> da._hexToRgb('#ffffff')
[255, 255, 255]
>>> da._hexToRgb('#ff8000')
[255, 128, 0]
>>> da._hexToRgb('#000000')
[0, 0, 0]
r-   c              3  L   >#    U  H  n[        TXTS -  -    S5      v   M     g7f)      N)int).0ilvvalues     r   	<genexpr>-DiscreteAnalysis._hexToRgb.<locals>.<genexpr>l   s*     Q;PaCaB!G,b11;Ps   !$r   r5   )lstriplenlistrange)r&   r;   r:   s    `@r   	_hexToRgbDiscreteAnalysis._hexToRgb^   s:     S!ZQ5Ba;PQQQr   c                *    US:  a  SnU$ US:  a  SnU$ )z
Utility conversion method -- limits all numbers to between 0 and 255.

>>> da = analysis.discrete.DiscreteAnalysis()
>>> da._rgbLimit(70)
70
>>> da._rgbLimit(300)
255
>>> da._rgbLimit(-30)
0
r      r   )r&   r;   s     r   	_rgbLimitDiscreteAnalysis._rgbLimitn   s*     19E  S[Er   c                    / U l         g)z
Clear all stored solutions
N)r$   r&   s    r   clearSolutionsFound$DiscreteAnalysis.clearSolutionsFound   s     !r   c                d    / nU R                    H  u  p#X1;  d  M  UR                  U5        M     U$ )z]
Based on solutions found so far with this processor,
return the colors that have been used.
r$   append)r&   postunused_solutioncolors       r   getColorsUsedDiscreteAnalysis.getColorsUsed   s5    
 &*&9&9"O E" ': r   c                d    / nU R                    H  u  p#X!;  d  M  UR                  U5        M     U$ )z`
Based on solutions found so far with this processor,
return the solutions that have been used.
rM   )r&   rO   solutionunused_colors       r   getSolutionsUsed!DiscreteAnalysis.getSolutionsUsed   s5    
 &*&9&9"H#H% ': r   c                    / $ )z
A list of pairs showing all discrete results and the assigned color.
Data should be organized to be passed to
:class:`music21.graph.GraphColorGridLegend`.

If `compress` is True, the legend will only show values for solutions
that have been encountered.
r   )r&   compresss     r   solutionLegendDiscreteAnalysis.solutionLegend   s	     	r   c                    g)K
Return a string describing the solution values. Used in Legend formation.
Nr   rI   s    r   solutionUnitString#DiscreteAnalysis.solutionUnitString   s     r   c                    g)z
Given an analysis specific result, return the appropriate color.
Must be able to handle None in the case that there is no result.
Nr   r&   rU   s     r   solutionToColor DiscreteAnalysis.solutionToColor       
 	r   c                    g)z
Given a Stream, apply the analysis to all components of this Stream.
Expected return is a solution (method-specific) and a color value.
Nr   )r&   sStreams     r   processDiscreteAnalysis.process   re   r   c                    g)zF
For a given Stream, apply the analysis and return the best solution.
Nr   )r&   	subStreams     r   getSolutionDiscreteAnalysis.getSolution   s     	r   )r#   r%   r$   r"   )r0   zSequence[float | int]returnstr)r;   ro   rn   z	list[int]FrZ   boolrn   z4list[list[str | list[tuple[int | str, str | None]]]])r   r   r   r   __doc__namer    __annotations__r(   r1   rB   rF   rJ   rR   rW   r[   r_   rc   rh   rl   r   r   r   r   r   r   7   sU     DK'
8R $!		r   r   c                     ^  \ rS rSr% SrSrSrSS/rSrS\	S	'   S
r
S\	S'   SU 4S jjrS rSS jrSS S jjrS rSS jrS!S jrS"S jrS#S$S jjrS rS rS rSS jrS#S jrS rS rSrU =r$ )%KeyWeightKeyAnalysis   z8
Base class for all key-weight key analysis subclasses.
FzKeyWeightKeyAnalysis Base Classzkey.baseClasszkeyscape.baseClass)CC#C-D-DE-EFF#G-GA-AB-Bztuple[str, ...]keysValidMajor)ry   rz   r}   D#r~   r   r   r   r   G#r   r   A#r   r   keysValidMinorc                   > [         TU ]  US9  Ub  U R                  U5      U l        OS U l        0 U l        0 U l        U R                  5         g N)r'   )superr(   _getSharpFlatCountsharpFlatCountmajorKeyColorsminorKeyColors_fillColorDictionariesr&   r'   	__class__s     r   r(   KeyWeightKeyAnalysis.__init__   sP    9&"&"9"9/"JD"&D  ##%r   c                *   SSSSSSSS.nU R                   U R                  4U R                  U R                  44 GHT  u  p#U GHG  n[        R
                  " U5      nUR                  nU R                  X   5      n[        [        U5      5       H  nU R                  Xg   S	-   5      Xg'   M     X0R                  :X  a3  [        [        U5      5       H  nU R                  Xg   S
-
  5      Xg'   M     [        UR                  5      S:  ac  SnUR                  S   S:X  a	  XSU-  S.n	O"UR                  S   S:X  a  SU-  SU-  US.n	O0 n	U	 H  nU R                  Xg   X   -   5      Xg'   M     U R                  U5      X$R                  '   GMJ     GMW     g)z
>>> p = analysis.discrete.KrumhanslSchmuckler()

This automatically calls _fillColorDictionaries

>>> len(p.majorKeyColors)
15
>>> p.majorKeyColors['C']
'#ff816b'
z#CD4F39z#DAA520z#BCEE68z#96CDCDz#6495EDz#8968CDz#FF83FA)ry   r}   r   r   r   r   r   2   d   r+      -)r   r+   r,   r-   N)r   r   r   r   r   PitchsteprB   rA   r?   rF   rt   r1   )
r&   stepLibdstvalidvalidKeyr   rgbStepr9   	magnitudeshiftLibs
             r   r   +KeyWeightKeyAnalysis._fillColorDictionaries   s   " 
 !//1D1DE //1D1DEGJC! ;;x0}}..7s7|,A!%
R!@GJ - ///"3w<0%)^^GJ4D%E
 1 x}}%) "I}}Q'3.'02	>#R!q)S0')I~"y.Y#W#%%%)^^GJ4L%M
 & &*^^G%<MM"9 "Gr   c                    SnSnUR                    HT  nUR                  c  M  UR                  R                  S:  a  US-  nM3  UR                  R                  S:  d  MO  US-  nMV     X24$ )z
Determine count of sharps and flats in a Stream

>>> s = corpus.parse('bach/bwv66.6')
>>> p = analysis.discrete.KrumhanslSchmuckler()
>>> p._getSharpFlatCount(s.flatten())
(87, 0)
r   r   r+   )pitches
accidentalalter)r&   rk   	flatCount
sharpCountps        r   r   'KeyWeightKeyAnalysis._getSharpFlatCount1  si     	
""A||'<<%%)OI\\''!+!OJ # $$r   c                f    UR                  5       nUS:X  a  / SQ$ US:X  a  / SQ$ [        SU 35      ez
Returns the key weights. To provide different key weights,
subclass and override this method. The defaults here are KrumhanslSchmuckler.

>>> a = analysis.discrete.KrumhanslSchmuckler()
>>> len(a.getWeights('major'))
12
>>> len(a.getWeights('minor'))
12
major)gffffff@gףp=
@gףp=
@gp=
ף@gQ@g\(\@g)\(@g(\@gQ@gHzG@gRQ@g
ףp=
@minor)gRQ@gq=
ףp@g)\(@gQ@g@g=
ףp=@gRQ@g      @gףp=
@gQ@gQ
@g\(\	@$Weights must be major or minor, not lowerr   r&   
weightTypes     r   
getWeightsKeyWeightKeyAnalysis.getWeightsF  sC      %%'
 [[7"[[+.RS]R^,_``r   c                    S/S-  nUR                   (       d  gUR                    H8  nUR                  nUR                   H  nX%R                  ==   U-  ss'   M     M:     U$ )aF  
Given a flat Stream, return a pitch class distribution.
The value of each pitch class is scaled by its duration in quarter lengths.

>>> a = analysis.discrete.KrumhanslSchmuckler()
>>> s = stream.Stream()
>>> n1 = note.Note('c')
>>> n1.quarterLength = 3
>>> n2 = note.Note('f#')
>>> n2.quarterLength = 2
>>> s.append(n1)
>>> s.append(n2)
>>> a._getPitchClassDistribution(s)
[3.0, 0, 0, 0, 0, 0, 2.0, 0, 0, 0, 0, 0]
>>> c1 = chord.Chord(['d', 'e', 'b-'])
>>> c1.quarterLength = 1.5
>>> s.append(c1)
>>> a._getPitchClassDistribution(s)
[3.0, 0, 1.5, 0, 1.5, 0, 2.0, 0, 0, 0, 1.5, 0]
r      N)notesquarterLengthr   
pitchClass)r&   	streamObjpcDistnlengthr   s         r   _getPitchClassDistribution/KeyWeightKeyAnalysis._getPitchClassDistributionY  sW    , rA__FYY||$.$  !
 r   c                    Uc  gS/S-  nU R                  U5      n[        [        U5      5       H7  n[        [        U5      5       H  nX5==   XFU-
  S-     X   -  -  ss'   M     M9     U$ )zTakes in a pitch class distribution as a list and convolutes it
over Sapp's given distribution for finding key, returning the result.
Nr   r   )r   rA   r?   )r&   pcDistributionr   rU   toneWeightsr9   js          r   _convoluteDistribution+KeyWeightKeyAnalysis._convoluteDistribution{  ss    
 !38ooj1s8}%A3~./UbL 9N<M MN 0 & r   c                    Uc  gS/S-  n[        S [        U5       5       5      nUR                  5         U H!  u  pV[        R                  " U5      X&   4X6'   M#     U$ )zTakes in a list of probable key results in points and returns a
list of keys in letters, sorted from most likely to least likely.
Nr   r   c              3  ,   #    U  H
  u  pX!4v   M     g 7fr"   r   )r8   pcresults      r   r<   6KeyWeightKeyAnalysis._getLikelyKeys.<locals>.<genexpr>  s     J4ILRF<4Is   )sorted	enumeratereverser   r   )r&   
keyResultsdifferences
likelyKeysaunused_correlationr   s          r   _getLikelyKeys#KeyWeightKeyAnalysis._getLikelyKeys  sd    
 #$#(
JIj4IJJ			 '("#kk"o{?JN '( r   c                   Uc  gS/S-  nS/S-  nS/S-  nS/S-  nU R                  U5      n[        U5      [        U5      -  n	[        U5      [        U5      -  n
[        [        U5      5       H  n[        [        U5      5       Hy  nX[   XU-
  S-     U	-
  X,   U
-
  -  -   X['   Xk   XU-
  S-     U	-
  S-  -   Xk'   X{   X,   U
-
  S-  -   X{'   Xk   S:X  d  X{   S:X  a  SXK'   M]  [	        X[   Xk   X{   -  S-  -  5      XK'   M{     M     U$ )zc
Takes in a list of numerical probable key results and returns the
difference of the top two keys.
Ng        r   r,   r         ?)r   sumr?   rA   float)r&   r   r   r   rU   topbottomRight
bottomLeftr   profileAveragehistogramAverager9   r   s                r   _getDifference#KeyWeightKeyAnalysis._getDifference  s\    !$
ebjebjURZ
ooj1[)C,<<~.^1DDs8}%A3{+,Q"->&),<<$> ? "-Q"->14E "F *"%(88Q2? !@
 >Q&*-1*<"%HK"';>JM3QVY2Y(Z"[HK - & r   c                   / SQn/ nU(       ac  U R                  5       nU R                  5       n/ nU H:  nU H1  nUS   c  M  XgS   R                  :X  d  M  UR                  U5          M8     M<     OUn/ nSn	S H  n
U
S:X  a  U R                  n	OU
S:X  a  U R
                  n	U
/n/ nU Vs/ s H  n[        R                  " U5      PM     sn Hy  n U R                  X/5      nSnU(       a  X;  a  S	nUR                  U	;  a  S	nU(       a  S
nSnO"UR                  nU
S:X  a  UR                  5       nUR                  UU45        M{     UR                  U5        UR                  U5        M     U$ s  snf ! [         a    Sn Nf = f)z
Returns a list of lists of possible results for the creation of a legend.

>>> p = analysis.discrete.KrumhanslSchmuckler()
>>> post = p.solutionLegend()
)r{   ry   rz   r|   r}   r   r~   r   r   r   r   r   r   r   r   r   r   r   r   Nr   )MajorMinorr   r   FT#ffffffr   )rR   rW   rt   rN   r   r   r   r   rc   KeyErrorr   )r&   rZ   _keySortOrder
colorsUsedsolutionsUsedkeySortOrderFilteredkeyElsoldatar   yLabelrowpairsr   keyPitchrQ   maskkeyStrs                     r   r[   #KeyWeightKeyAnalysis.solutionLegend  s   
 
++-J 113M $& &(C1v~ A+,33E: ) ' $1 !# )F ++7"++=CHC46E5IJ5IU[[^5IJ! 00(1CDE .#==-D%EF &]]F(!'fe_-/ K0 JJuKKE )F 5 K   ! E!s   * E2E77FFc                    g)r^   Keysr   rI   s    r   r_   'KeyWeightKeyAnalysis.solutionUnitString  s     r   c                    US   nUc  gUS   R                  5       nUS:X  a  U R                  UR                     $ U R                  UR                     $ )z
Given a two-element tuple of (tonicPitch, modality) return the proper color

>>> p = analysis.discrete.KrumhanslSchmuckler()
>>> solution = (pitch.Pitch('C'), 'major')
>>> p.solutionToColor(solution)
'#ff816b'
r   r   r+   r   )r   r   rt   r   )r&   rU   solutionKeymodalitys       r   rc   $KeyWeightKeyAnalysis.solutionToColor  s^     qkA;$$&w&&{'7'788&&{'7'788r   c                   U R                  U5      nU R                  US5      nU R                  UUS5      nU R                  X45      nU R                  US5      nU R                  UUS5      nU R                  Xg5      nXX4$ )Nr   r   )r   r   r   r   )	r&   rg   r   keyResultsMajordifferenceMajorlikelyKeysMajorkeyResultsMinordifferenceMinorlikelyKeysMinors	            r   _likelyKeys KeyWeightKeyAnalysis._likelyKeys'  s    88A 55ngN--o.<gG--oO55ngN--o.<gG--oO//r   c                    Uc  gSnUS:X  a  UR                   U R                  ;  a  SnO"US:X  a  UR                   U R                  ;  a  SnU(       a  UR                  SS9  U$ )a  

>>> ks = analysis.discrete.KrumhanslSchmuckler()
>>> s = converter.parse('tinynotation: 4/4 b-4 e- f g-')
>>> ks._bestKeyEnharmonic(pitch.Pitch('e#'), 'minor', s)
<music21.pitch.Pitch F>
>>> ks._bestKeyEnharmonic(pitch.Pitch('f-'), 'major', s)
<music21.pitch.Pitch E>

NFr   Tr   inPlace)rt   r   r   getEnharmonic)r&   pitchObjmoderg   flipEnharmonics        r   _bestKeyEnharmonic'KeyWeightKeyAnalysis._bestKeyEnharmonic7  sl        7?}}D$7$77!%W_}}D$7$77!%""4"0r   c                   UR                  5       R                  R                  [        R                  5      nU R                  U5      u  p4Ub  U VVs/ s H	  u  pVXeS4PM     nnnO/ nUb  UU VVs/ s H	  u  pVXeS4PM     snn-  nU(       d  [        S5      eUR                  5         UR                  5         US   u  penU R                  XXU5      nXXU4n	U R                  U	5      n
U(       aE  / U l        USS  H5  u  penU R                  XXU5      nU R                  R                  XXU45        M7     U R                  R                  X45        X4$ s  snnf s  snnf )a  
Takes in a Stream or sub-Stream and performs analysis
on all contents of the Stream. The
:class:`~music21.analysis.windowed.WindowedAnalysis`
windowing system can be used to get many results
by calling this method.

Returns two values, a solution data list and a color string.

The data list contains a key (as a string), a mode
(as a string), and a correlation value (degree of certainty)
Nr   r   z.failed to get likely keys for Stream componentr   r+   )flattennotesAndRestsgetElementsNotOfClassr   	Unpitchedr  r   sortr   r  rc   r%   rN   r$   )r&   rg   storeAlternativesr  r  r   coefficientsortListr  rU   rQ   s              r   rh   KeyWeightKeyAnalysis.processh  s    //#11GGW ,0+;+;G+D( &,;=,;( %1,;  =H H&-<>-<)! &'2-<> >H+,\]]  ({##AW5[)$$X. (*D%(0$++AW=))00!;1GH )5 	""H#45A=>s   E2Ec                P    [         R                  " US   US   S9nUS   Ul        U$ )zW
Convert a solution into an appropriate object representation, returning a Key object.
r   r+   )tonicr  r,   )r   KeycorrelationCoefficient)r&   rU   ks      r   _solutionToObject&KeyWeightKeyAnalysis._solutionToObject  s-     GG(1+HQK8#+A; r   c                
   U R                  UR                  5       SS9u  p#U R                  U5      nUR                  c  / Ul        U R                   H-  nUR                  R                  U R                  U5      5        M/     U$ )a8  
Return a music21 Key object defining the results of the analysis.
Do not call process before calling this method, as this method calls process.

Note that all alternative solutions are returned as Key objects and stored
on a list found at Key.alternateInterpretations.


>>> s = corpus.parse('bach/bwv66.6')
>>> p = analysis.discrete.KrumhanslSchmuckler()
>>> p.getSolution(s)  # this seems correct
<music21.key.Key of f# minor>

>>> s = corpus.parse('bach/bwv57.8')
>>> p = analysis.discrete.KrumhanslSchmuckler(s)
>>> p.getSolution(s)
<music21.key.Key of B- major>
T)r  )rh   r  r!  alternateInterpretationsr%   rN   )r&   rg   rU   rV   r   r   s         r   rl    KeyWeightKeyAnalysis.getSolution  s{    ( "&goo.?SW!X""8,%%-)+A&,,C&&--d.D.DS.IJ - r   )r%   r   r   r   r"   )rn   ztuple[int, int]r   )rn   zlist[float])rn   zlist[t.Any] | None)rn   zNone | list[float]rp   rq   )r   r   r   r   rs   _DOC_ALL_INHERITEDrt   r    r   ru   r   r(   r   r   r   r   r   r   r   r[   r_   rc   r  r  rh   r!  rl   r   __classcell__r   s   @r   rw   rw      s      -D"$89K'NO 'NO 	&8=t%*a&D$!FL\9(0 /bDL r   rw   c                  J   ^  \ rS rSrSrSrSr/ SQrS	U 4S jjrS
S jr	Sr
U =r$ )KrumhanslSchmuckleri  a  
Implementation of Krumhansl-Schmuckler/Kessler weightings for
Krumhansl-Schmuckler key determination algorithm.

Values from https://extras.humdrum.org/man/keycor/, which describes these
weightings as "Strong tendency to identify the dominant key as the tonic."

* Changed in v6.3: it used to be that these were different from the
  Kessler profiles, but that was likely a typo.  Thus, KrumhanslKessler and
  KrumhanslSchmuckler are synonyms of each other.
Fz)Krumhansl Schmuckler/Kessler Key Analysis)zkey.krumhanslzkey.schmucklerzkey.krumhansl-schmucklerzkey.krumhanslschmuckler	krumhansl
schmucklerzkrumhansl-schmucklerkrumhanslschmucklerzkey.kesslerzkey.krumhansl-kesslerzkey.krumhanslkesslerkesslerzkrumhansl-kesslerkrumhanslkesslerc                    > [         TU ]  US9  g r   r   r(   r   s     r   r(   KrumhanslSchmuckler.__init__      9r   c                f    UR                  5       nUS:X  a  / SQ$ US:X  a  / SQ$ [        SU 35      er   r   r   s     r   r   KrumhanslSchmuckler.getWeights  sG      %%'
 & &7"[[+.RS]R^,_``r   r   r"   r&  r   r   r   r   rs   r'  rt   r    r(   r   r   r(  r)  s   @r   r+  r+    s-    
 6DK:a ar   r+  c                  J   ^  \ rS rSrSrSrSr/ SQrS	U 4S jjrS
S jr	Sr
U =r$ )AardenEsseni	  as  
Implementation of Aarden-Essen weightings for Krumhansl-Schmuckler key determination algorithm.

Values from https://extras.humdrum.org/man/keycor/, which
describes these weightings as "Weak tendency to identify the subdominant key as the tonic."

(N.B. -- we are not sure exactly where the minor weightings come from, and recommend
only using these weights for major).
FzAarden Essen Key Analysis)
z
key.aardenz	key.essenzkey.aarden-essenzkey.aardenessenaardenessenzaarden-essenaardenessenr   keyscapec                    > [         TU ]  US9  g r   r2  r   s     r   r(   AardenEssen.__init__  r4  r   c                f    UR                  5       nUS:X  a  / SQ$ US:X  a  / SQ$ [        SU 35      e)z
Returns the key weights.

>>> a = analysis.discrete.AardenEssen()
>>> len(a.getWeights('major'))
12
>>> len(a.getWeights('minor'))
12
r   )g2w-!1@IΣ?g|?5^-@gv?g(3@g&&@gIΣ?gZd;6@rA  g:KTO @gF?gY4@r   )gC2@g,=)?g$(~,@g	h"0@[tz?g=U,@rB  gǺ2@g2C@g~5?g7T7@g䠄?r   r   r   s     r   r   AardenEssen.getWeights  sQ      %%'
 0 0 7"K K ,.RS]R^,_``r   r   r"   r&  r7  r)  s   @r   r9  r9  	  s-     &DK:a ar   r9  c                  J   ^  \ rS rSrSrSrSr/ SQrS	U 4S jjrS
S jr	Sr
U =r$ )SimpleWeightsi5  a(  
Implementation of simple weights by Craig Sapp for Krumhansl-Schmuckler
key determination algorithm.

Values from https://extras.humdrum.org/man/keycor/, which describes
these weightings as "Performs most consistently with large regions of music,
becomes noisier with smaller regions of music."
FzSimple Weight Key Analysis)z
key.simplez
key.weightzkey.simple-weightzkey.simpleweightsimpleweightzsimple-weightsimpleweightc                    > [         TU ]  US9  g r   r2  r   s     r   r(   SimpleWeights.__init__D  r4  r   c                f    UR                  5       nUS:X  a  / SQ$ US:X  a  / SQ$ [        SU 35      e)z
Returns the key weights.

>>> a = analysis.discrete.SimpleWeights()
>>> len(a.getWeights('major'))
12
>>> len(a.getWeights('minor'))
12
r   )r,   r   r+   r   r+   r+   r   r,   r   r+   r   r+   r   )r,   r   r+   r+   r   r+   r   r,   r+   r   r   r   r   r   r   s     r   r   SimpleWeights.getWeightsG  sC      %%'
 777";;+.RS]R^,_``r   r   r"   r&  r7  r)  s   @r   rE  rE  5  s-     'DK:a ar   rE  c                  J   ^  \ rS rSrSrSrSr/ SQrS	U 4S jjrS
S jr	Sr
U =r$ )BellmanBudgei[  z
Implementation of Bellman-Budge weightings for Krumhansl-Schmuckler key determination algorithm.

Values from https://extras.humdrum.org/man/keycor/, which describes these
weightings as "No particular tendencies for confusions with neighboring keys."
FzBellman Budge Key Analysis)zkey.bellmanz	key.budgezkey.bellman-budgezkey.bellmanbudgebellmanbudgezbellman-budgebellmanbudgec                    > [         TU ]  US9  g r   r2  r   s     r   r(   BellmanBudge.__init__h  r4  r   c                f    UR                  5       nUS:X  a  / SQ$ US:X  a  / SQ$ [        SU 35      e)z
Returns the key weights.

>>> a = analysis.discrete.BellmanBudge()
>>> len(a.getWeights('major'))
12
>>> len(a.getWeights('minor'))
12
>>> a.getWeights('major')
[16.8..., 0.8..., 12.9..., 1.4..., ...]

r   )g0@gQ?gfffff)@g(\?g{G*@g\('@g      ?gHzG4@g?gGz @gףp=
?gp=
#%@r   )g)\(2@gGz?g{G)@gGz*@gQ?gL&@gGz?gR5@g(\@g{Gz?gq=
ףp?gQk$@r   r   r   s     r   r   BellmanBudge.getWeightsk  sC      %%'
 aa7"aa+.RS]R^,_``r   r   r"   r&  r7  r)  s   @r   rN  rN  [  s-     'DK:a ar   rN  c                  J   ^  \ rS rSrSrSrSr/ SQrS	U 4S jjrS
S jr	Sr
U =r$ )TemperleyKostkaPaynei  a/  
Implementation of Temperley-Kostka-Payne weightings for Krumhansl-Schmuckler
key determination algorithm.

Values from https://extras.humdrum.org/man/keycor/, which describes
these weightings as "Strong tendency to identify the relative major as the tonic
in minor keys. Well-balanced for major keys."
Fz#Temperley Kostka Payne Key Analysis)
zkey.temperleyz
key.kostkaz	key.paynezkey.temperley-kostka-paynezkey.temperleykostkapayne	temperleykostkapayneztemperley-kostka-paynetemperleykostkapaynec                    > [         TU ]  US9  g r   r2  r   s     r   r(   TemperleyKostkaPayne.__init__  r4  r   c                f    UR                  5       nUS:X  a  / SQ$ US:X  a  / SQ$ [        SU 35      e)z
Returns the key weights.

>>> a = analysis.discrete.TemperleyKostkaPayne()
>>> len(a.getWeights('major'))
12
>>> len(a.getWeights('minor'))
12
r   )gV-?gQ?gZd;?gˡE?gq=
ףp?q=
ףp?g~jt?gzG?g9v?gCl?gv/?g?r   )gbX9?g/$?gtV?g-?gJ+?r_  gzG?gCl?gB`"?gx&?g/$?gQ?r   r   r   s     r   r   TemperleyKostkaPayne.getWeights  sM      %%'
 > >7"> > ,.RS]R^,_``r   r   r"   r&  r7  r)  s   @r   rW  rW    s-     0DK:a ar   rW  c                     ^  \ rS rSrSrSrSrSS/rSSU 4S jjjrSS jr	SS	 jr
SSS
 jjrS rSS jrS rS rSrU =r$ )Ambitusi  z
A basic analysis method for measuring register.

>>> ambitusAnalysis = analysis.discrete.Ambitus()
>>> ambitusAnalysis.identifiers[0]
'ambitus'
FzAmbitus Analysisambitusspanc                z   > [         TU ]  US9  S U l        S U l        [	        5       U l        U R                  5         g r   )r   r(   minPitchObjmaxPitchObjr   _pitchSpanColors_generateColorsr   s     r   r(   Ambitus.__init__  s;    9 .2-17B}r   c                   SnUcu  U R                   be  U R                  U R                   5      nUc  gUu  U l        U l        [	        U R                  R
                  U R                  R
                  -
  5      nOSnOUnXB-
  nUS:X  a  SnSnSn[        X$S-   5       HF  n[        SU-
  U-  U-  5      U-   n	U R                  U	S-  U	S-  U	45      U R                  U'   US-  nMH     g)	a&  
Provide uniformly distributed colors across the entire range.

>>> ambitusAnalysis = analysis.discrete.Ambitus()
>>> ambitusAnalysis._generateColors()
>>> for i, j in ambitusAnalysis._pitchSpanColors.items():
...     if i > 3: break
...     print(i, j)
0 #130f19
1 #14101b
2 #16111d
3 #16121e
r   N   r+      g     o@g      ?g333333?)
r#   getPitchSpanrf  rg  r7   psrA   r/   r1   rh  )
r&   	numColorsminPitchpitchSpanReturnmaxPitch
valueRanger   	antiBlackr9   vals
             r   ri  Ambitus._generateColors  s     $$0"&"3"3D4I4I"J"*5D2 $"2t//22T5E5E5H5HHI H(
?J	xA.A%)+z9TABYNC'+~~d
cCiRU6V'WD!!!$AID /r   c                   XR                   L a:  U R                  (       a)  U R                  (       a  U R                  U R                  4$ UR                  5       R                  nU(       d  g/ n/ nU H~  nSn[        U[        R                  5      (       a+  [        U[        R                  5      (       d  UR                  nX6 Vs/ s H  owR                  PM     sn-  nUR                  U5        M     U(       d  gUR                  [        U5      5      nUR                  [        U5      5      n	XH   n
XI   nXR                   L a  Xl        Xl        X4$ s  snf )a  
For a given subStream, return a tuple consisting of the two pitches
with the minimum and maximum pitch space value.

This public method may be used by other classes.  It ignores ChordSymbol objects.

Demonstration:

>>> s = corpus.parse('bach/bwv66.6')
>>> p = analysis.discrete.Ambitus()
>>> pitchMin, pitchMax = p.getPitchSpan(s.parts[0].getElementsByClass(stream.Measure)[3])
>>> pitchMin.ps, pitchMax.ps
(66.0, 71.0)
>>> p.getPitchSpan(s.parts[0].getElementsByClass(stream.Measure)[6])
(<music21.pitch.Pitch A4>, <music21.pitch.Pitch C#5>)

>>> s = stream.Stream()
>>> c = chord.Chord(['a2', 'b4', 'c8'])
>>> s.append(c)
>>> p.getPitchSpan(s)
(<music21.pitch.Pitch A2>, <music21.pitch.Pitch C8>)

Returns None if the stream contains no pitches.

>>> s = stream.Stream(note.Rest())
>>> p.getPitchSpan(s) is None
True

OMIT_FROM_DOCS

And with only ChordSymbols:

>>> s.insert(4, harmony.ChordSymbol('C6'))
>>> p.getPitchSpan(s) is None
True

Nr   )r#   rf  rg  recurser   
isinstancer   GeneralNoter	   ChordSymbolr   ro  extendindexminmax)r&   rk   	justNotespsFoundpitchesFoundr   r   r   minPitchIndexmaxPitchIndexrf  rg  s               r   rn  Ambitus.getPitchSpan  s&   L ---$2B2BtGWGW##T%5%555%%'--	  "*,A-/G!T--..z!WEXEX7Y7Y))g.gg..G(  c'l3c'l3"1"1---**''! /s   Ec                6   / nU(       a  U R                  5       n/ n0 n[        [        U R                  R	                  5       5      5       H0  nU(       a  U R                  U   U;  a  M  U R                  U   XE'   M2     [        UR	                  5       5      nUR                  5         US[        U5      S-   nU[        U5      S-  S nXx4 HI  n	S/n
/ nU	 H  nXE   nUR                  X\45        M     U
R                  U5        UR                  U
5        MK     U$ )a  
Return legend data.

>>> s = corpus.parse('bach/bwv66.6')
>>> soprano = s.parts[0]
>>> p = analysis.discrete.Ambitus(soprano)  # provide ref stream
>>> p.solutionLegend()
[['',
  [(0, '#130f19'), (1, '#211a2c'), (2, '#2f263f'),
   (3, '#3e3253'), (4, '#4c3d66'), (5, '#5b4979')]],
 ['',
  [(6, '#69548c'), (7, '#775f9f'), (8, '#866bb2'), (9, '#9476c5'),
   (10, '#a382d9'), (11, '#b18eec'), (12, '#bf99ff')]]]

>>> len(p.solutionLegend())
2
>>> [len(x) for x in p.solutionLegend()]
[2, 2]

>>> [len(y) for y in [x for x in p.solutionLegend()]]
[2, 2]

>>> s = corpus.parse('bach/bwv66.6')
>>> p = analysis.discrete.Ambitus()
>>> p.solutionLegend(compress=True)  # empty if nothing processed
[['', []], ['', []]]

>>> x = p.process(s.parts[0])
>>> [len(y) for y in [x for x in p.solutionLegend(compress=True)]]
[2, 2]

>>> x = p.process(s.parts[1])
>>> [len(y) for y in [x for x in p.solutionLegend(compress=True)]]
[2, 2]

Nr,   r   )rR   rA   r?   rh  keysr@   r  rN   )r&   rZ   r   r   colorsr9   r  
keysTopRowkeysBottomRowkeyGroupr   r   rQ   s                r   r[   Ambitus.solutionLegend>  s   N 
++-J!#s40055789A((+:=--a0FI	 : FKKM"		+CIN,
c$i1n./ $3H=?DC46E	aZ(  JJuKK 4 r   c                    g)r^   z
Half-Stepsr   rI   s    r   r_   Ambitus.solutionUnitString  s     r   c                H    Uc  U R                  S5      $ U R                  U   $ )z

>>> p = analysis.discrete.Ambitus()
>>> s = stream.Stream()
>>> c = chord.Chord(['a2', 'b4', 'c8'])
>>> s.append(c)
>>> minPitch, maxPitch = p.getPitchSpan(s)
>>> p.solutionToColor(maxPitch.ps - minPitch.ps).startswith('#')
True
)rE   rE   rE   )r1   rh  rb   s     r   rc   Ambitus.solutionToColor  s*     >>/22$$X..r   c                   U R                  U5      nUbJ  [        R                  " US   US   S9nU R                  US   R                  US   R                  -
  5      nOSnSnU R
                  R                  X445        X44$ )z
Given a Stream, return a solution (as an interval) and a color string.

>>> p = analysis.discrete.Ambitus()
>>> s = stream.Stream()
>>> c = chord.Chord(['a2', 'b4', 'c8'])
>>> s.append(c)
>>> p.process(s)
(<music21.interval.Interval m38>, '#665288')
Nr   r+   )	noteStartnoteEndr   )rn  r
   Intervalrc   ro  r$   rN   )r&   rg   rO   rU   rQ   s        r   rh   Ambitus.process  s       )((47DGLH((ad1gjj)@AEHE 	""H#45r   c                ,    U R                  U5      u  p#U$ )z
Procedure to only return an Interval object.

>>> s = corpus.parse('bach/bwv66.6')
>>> p = analysis.discrete.Ambitus()
>>> p.getSolution(s)
<music21.interval.Interval m21>
)rh   r&   rg   rU   rV   s       r   rl   Ambitus.getSolution  s     "&g!6r   )rh  rg  rf  r"   )r'   zstream.Stream | None)rn   z&tuple[pitch.Pitch, pitch.Pitch] | Nonerp   rq   )rU   z
int | Nonern   ro   )r   r   r   r   rs   r'  rt   r    r(   ri  rn  r[   r_   rc   rh   rl   r   r(  r)  s   @r   rb  rb    sV     Df%K 'VF(PEN/".
 
r   rb  c                  `   ^  \ rS rSrSrSrSrSS/rSU 4S jjrS r	SS	 jr
SS
 jrS rSrU =r$ )MelodicIntervalDiversityi  zN
An analysis method to determine the diversity of intervals used in a Stream.
FzInterval Diversityzinterval.diversity	intervalsc                    > [         TU ]  US9  g r   r2  r   s     r   r(   !MelodicIntervalDiversity.__init__  r4  r   c                    g)Nr   r   rb   s     r   rc   (MelodicIntervalDiversity.solutionToColor  s    r   c                   SSK Jn  Uc  0 nUR                  5       (       a%  [        UR	                  UR
                  5      5      nOU/nU GH&  nUR                  5       R                  SS9R	                  [        R                  5      R                  5       n[        U5       H  u  pU	[        U5      S-
  ::  a  XS-      nOSnUc  M&  [        R                  " X5      n	U(       a  U	R                  R                  S:X  a  M_  U(       a*  U	R                  R                  S:  a  U	R!                  5       n	U	R"                  U;  a  U	S/X)R"                  '   M  X)R"                     S==   S-  ss'   M     GM)     U$ )z
Find all unique melodic intervals in this Stream.

If `found` is provided as a dictionary, this dictionary will be used to store Intervals,
and counts of Intervals already found will be incremented.
r   r   NFr
  r,   r+   )music21r   hasPartLikeStreamsr@   getElementsByClassStreamr  	stripTiesr   Noter   r?   r
   r  	chromatic	semitonesr   directedName)r&   rg   foundignoreDirectionignoreUnisonr   procListr   
noteStreamr9   r   nNexts               r   countMelodicIntervals.MelodicIntervalDiversity.countMelodicIntervals  s?    	#=E %%''G66v}}EFHyHA ..u.=PPQUQZQZ[bbdJ!*-J!++&1u-E E$ ))!3A#;;00A5$&;;0014 !		A ~~U212Ann-nn-a0A50) . > r   c                n    U R                  X5      n[        U5      U R                  [        U5      5      4$ )z8
Find how many unique intervals are used in this Stream
)r  r?   rc   )r&   rg   r  uniqueIntervalss       r   rh    MelodicIntervalDiversity.process  s4     44WN?#T%9%9#o:N%OOOr   c                H    U R                  UR                  5       5      u  p#U$ )z-
Solution is the number of unique intervals.
)rh   r  r  s       r   rl   $MelodicIntervalDiversity.getSolution  s!     "&goo.?!@r   r   r"   )NTT)T)r   r   r   r   rs   r'  rt   r    r(   rc   r  rh   rl   r   r(  r)  s   @r   r  r    s?     D'5K:4lP r   r  c                z    US:X  a  Sn[        U5      nUb  U" 5       nUR                  U 5      $ [        SU 35      e)a  
Public interface to discrete analysis methods to be applied
to a Stream given as an argument. Methods return process-specific data format.
See subclasses for details.

Analysis methods can be specified as arguments or by use of a `method`
keyword argument. If `method` is the class name, that class is returned.
Otherwise, the :attr:`~music21.analysis.discrete.DiscreteAnalysis.identifiers`
list of all :class:`~music21.analysis.discrete.DiscreteAnalysis` subclass objects
will be searched for matches. The first match that is found is returned.

:class:`~music21.analysis.discrete.Ambitus`
:class:`~music21.analysis.discrete.KrumhanslSchmuckler`

>>> s = corpus.parse('bach/bwv66.6')
>>> analysis.discrete.analyzeStream(s, 'Krumhansl')
<music21.key.Key of f# minor>
>>> analysis.discrete.analyzeStream(s, 'ambitus')
<music21.interval.Interval m21>

>>> analysis.discrete.analyzeStream(s, 'key')
<music21.key.Key of f# minor>
>>> analysis.discrete.analyzeStream(s, 'span')
<music21.interval.Interval m21>


Note that the same results can be obtained by calling "analyze" directly on the stream object:
>>> s.analyze('key')
<music21.key.Key of f# minor>
>>> s.analyze('span')
<music21.interval.Interval m21>
rA   rd  zno such analysis method: )analysisClassFromMethodNamerl   r   )r   methodkeywordsanalysisClassNameobjs        r   analyzeStreamr    sR    J  5PQW5X$!y)) $&?x$H
IIr   c                   [         [        [        [        [        [
        /nSnU HQ  nU R                  5       UR                  R                  5       ;   d   U R                  5       UR                  ;   d  MO  Un  O   Uc&  U H   nUR                   H  nX:X  d  M
  Un  M     M"     Uc+  U H%  nUR                   H  nX;   d  M
  Un  O   Uc  M$    U$    U$ )a  
Returns an analysis class given a method name, or None if none can be found

Searches first the class name, then the .identifiers array for each class,
then a subset of any identifier.

>>> acfmn = analysis.discrete.analysisClassFromMethodName
>>> acfmn('aarden')
<class 'music21.analysis.discrete.AardenEssen'>
>>> acfmn('span')
<class 'music21.analysis.discrete.Ambitus'>

This one is fundamentally important:

>>> acfmn('key')
<class 'music21.analysis.discrete.AardenEssen'>

>>> print(repr(acfmn('unknown-format')))
None
N)
rb  r+  r9  rE  rN  rW  r   r   rt   r    )r  analysisClassesmatchanalysisClassidStrs        r   r  r  P  s    , 	5O *.E(LLNm44::<<<<>]%7%77!E ) },M&22?)E	 3 - },M&22?)E	 3
  L - Lr   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                2    SSK Jn  U" U [        5       5        g )Nr   )testCopyAll)music21.test.commonTestr  globals)r&   r  s     r   testCopyAndDeepcopyTest.testCopyAndDeepcopy  s    7D')$r   c                $   SSK Jn  [        5       nUR                  S5      nUR                  S5      nUR                  S5      nUR	                  UR                  5       5        UR                  UR                  5       5      u  pgUR                  5         UR                  5         Xg-   nUR	                  UR                  5       5        UR                  UR                  5       5      u  pU	R                  5         U
R                  5         X-   nUR                  UR                  5       5      u  pUR                  5         UR                  5         / n[        [        U5      5       H*  nX   u  nnX   u  nnUR                  UUU-   S-  45        M,     g )Nr   	converterz1tinynotation: 4/4 c4 d e f g a b c   c#4 d# e# f#z0tinynotation: 4/4 c#4 d# e# f#  f g a b- c d e fzQtinynotation: 4/4 c4 d e f g a b c   c#4 d# e# f#  c#4 d# e# f#  f g a b- c d e fg       @)r  r  r+  parserh   r  r  r  rA   r?   rN   )r&   r  r   s1s2s3likelyKeysMajor1likelyKeysMinor1allResults1likelyKeysMajor2likelyKeysMinor2allResults2likelyKeysMajor3likelyKeysMinor3avgr9   count1count2s                     r   testKeyAnalysisKrumhanslTest.testKeyAnalysisKrumhansl  sU   %!__PQ__OP__ @ A 	
		"**,-.]]2::<-H*&9 	
		"**,-.]]2::<-H*&9 ./]]2::<-H* s;'(A#IAv#IAvJJFVOs234 )r   c           	        SSK Jn  SSK Jn  UR                  " 5       nUR	                  [
        R                  " S5      5        UR	                  [
        R                  " S5      5        UR	                  [
        R                  " S5      5        [        5       nUR                  U5      nU R                  [        US   5      S5        U R                  [        US	   5      S
5        U R                  [        U5      S5        UR                  " 5       nUR	                  [
        R                  " S5      5        UR	                  [
        R                  " S5      5        UR	                  [
        R                  " S5      5        UR	                  [
        R                  " S5      5        [        5       nUR                  U5      nU R                  [        U5      S5        U R                  [        US   5      S5        UR                  USS9nU R                  [        U5      S5        U R                  [        US   5      S5        U R                  [        US   5      S5        [        5       nUR                  S5      nUR                  UR                  S   5      nU R                  [        U5      S5        U R                  [        US   5      S5        U R                  [        US   5      S5        U R                  [        US   5      S5        U R                  [        US   5      S5        UR                  U5      nU R                  [        U5      S5        U R                  [        [        [        U5      5      5      S 5        U R                  [        US!   5      S"5        U R                  [        US   5      S#5        U R                  [        US   5      S$5        U R                  [        US%   5      S&5        U R                  [        US   5      S'5        U R                  [        US   5      S(5        U R                  [        US	   5      S)5        g )*Nr   r   )corpuszg#3a3g4m7z#[<music21.interval.Interval m7>, 1]m2z#[<music21.interval.Interval m2>, 1]r,   c3d3r+   M2z#[<music21.interval.Interval M2>, 3]F)r  zM-2z$[<music21.interval.Interval M-2>, 1]z#[<music21.interval.Interval M2>, 2]zcorelli/opus3no1/1grave	   P5z#[<music21.interval.Interval P5>, 8]P4z#[<music21.interval.Interval P4>, 7]m3z#[<music21.interval.Interval m3>, 1]z$[<music21.interval.Interval M2>, 21]
   z=['M2', 'M3', 'M6', 'P15', 'P4', 'P5', 'P8', 'd5', 'm2', 'm3']P15z$[<music21.interval.Interval P15>, 1]z$[<music21.interval.Interval P5>, 16]z$[<music21.interval.Interval P4>, 29]M3z$[<music21.interval.Interval M3>, 16]z$[<music21.interval.Interval m3>, 12]z$[<music21.interval.Interval M2>, 79]z$[<music21.interval.Interval m2>, 43])r  r   r  r  rN   r   r  r  r  assertEqualro   r?   r  partsr   r@   )r&   r   r  smidmidDicts         r   testIntervalDiversityTest.testIntervalDiversity  sW   ""MMO	5!"	4!	4!&(++A.WT]+-RSWT]+-RSWq)MMO	4!	4!	4!	4!&(++A.Wq)WT]+-RS++Au+EWq)WU^,.TUWT]+-RS&(LL23 ++AGGAJ7Wq)WT]+-RSWT]+-RSWT]+-RSWT]+-ST++A.Wr*VDM23X	ZWU^,.TUWT]+-STWT]+-STWT]+-STWT]+-STWT]+-STWT]+-STr   c                    SSK Jn  S Hm  nUR                  " 5       nUR                  [        R
                  " U5      5        U R                  [        UR                  S5      R                  5      U5        Mo     g )Nr   r   )r   r   r   	Krumhansl)
r  r   r  rN   r   r  r  ro   analyzer  )r&   r   r   r  s       r   testKeyAnalysisSpellingTest.testKeyAnalysisSpelling  sR    ""AAHHTYYq\"S;!7!=!=>B #r   c                X   SSK Jn  SSKJn  UR	                  UR
                  5      n[        5       nUR                  U5      nUR                  UR                  UR                  /nU R                  [        US   5      S5        U R                  [        US   5      S5        U R                  [        US   5      SS S	5        [        5       nUR                  U5      nUR                  UR                  UR                  /nU R                  [        US   5      S5        U R                  [        US   5      S
5        [        5       nUR                  U5      nUR                  UR                  UR                  /nU R                  [        US   5      S5        U R                  [        US   5      S
5        [        5       nUR                  U5      nUR                  UR                  UR                  /nU R                  [        US   5      S5        U R                  [        US   5      S
5        [!        5       nUR                  U5      nUR                  UR                  UR                  /nU R                  [        US   5      S5        U R                  [        US   5      S
5        g )Nr   r  )	testFilesr   r+   r   r,      z0.81063r   )r  r  music21.musicxmlr  r  edgefield82br+  rl   r  r  r  r  ro   r9  rE  rN  rW  )r&   r  r  r  r   r   rO   s          r   testKeyAnalysisDiverseWeights"Test.testKeyAnalysisDiverseWeights  s   %.OOI223!MM!!9!9:T!Wt,T!Ww/T!Wa*I6MMM!!9!9:T!Wt,T!Ww/OMM!!9!9:T!Wt,T!Ww/NMM!!9!9:T!Wt,T!Ww/ "MM!!9!9:T!Wt,T!Ww/r   c                   SSK Jn  UR                  " 5       nUR                  [        R
                  " S5      S5        UR                  [        R
                  " S5      S5        UR                  [        R
                  " S5      S5        UR                  S	5      nU R                  [        U5      S
5        U R                  SR                  S UR                   5       5      S5        UR                  S5      nU R                  [        U5      S5        U R                  SR                  S UR                   5       5      S5        UR                  " 5       nUR                  [        R
                  " S5      S5        UR                  [        R
                  " S5      S5        UR                  S5      nU R                  [        UR                  5      S5        g )Nr   r   c   g   r   r,   r+  zC major c              3  8   #    U  H  oR                   v   M     g 7fr"   tonicPitchNameWithCaser8   kps     r   r<   1Test.testKeyAnalysisLikelyKeys.<locals>.<genexpr>)       !aF`";";F`   z7c G a F g e f E- A- B- d D A b b- c# f# C# E g# F# e- Br9  zF majorc              3  8   #    U  H  oR                   v   M     g 7fr"   r
  r  s     r   r<   r  .  r  r  z7C c g f a G d A- B- E- e b- D A f# C# b E c# e- F# B g#zc#r      )r  r   r  repeatAppendr   r  r  r  ro   joinr$  r?   )r&   r   r  r   r  s        r   testKeyAnalysisLikelyKeysTest.testKeyAnalysisLikelyKeys   sM   "MMO	tyy~q)	tyy~q)	tyy~q)II+,Q+!aaF`F`!aaR	T IIm$Q+!aaF`F`!aaR	T ]]_
		#*
		$+JJuQ778"=r   c           	        SSK Jn  UR                  " 5       nUR                  [        R
                  " 5       5        UR                  [        R                  " [        R
                  " 5       [        R                  " S5      [        R                  " S5      /5      5        UR                  S5      nU R                  UR                  S5        g )Nr   r   zE-4zB-4r   zE- major)r  r   r  rN   r   r  r   PercussionChordr  r  r  rt   )r&   r   r  r   s       r   testKeyAnalysisIgnoresUnpitched$Test.testKeyAnalysisIgnoresUnpitched<  s    "MMO	!"	++NNIIeIIe-
  	 IIe,r   r   N)r   r   r   r   r  r  r  r  r  r  r  r   r   r   r   r  r    s*    %&5R4UlC#0J>8-r   r  __main__)r   zstream.Streamr  ro   )r  ro   rn   ztype[DiscreteAnalysis] | None),rs   
__future__r   collectionsr   collections.abcr   r   typingtunittestr  r   r   r	   r
   r   r   r   r   TYPE_CHECKINGr   EnvironmentenvironLocalMusic21Exceptionr   r   rw   r+  KrumhanslKesslerr9  rE  rN  rW  keyWeightKeyAnalysisClassesrb  r  r  r  TestCaser  
_DOC_ORDERr   mainTestr   r   r   <module>r+     s   # # .           ??&&':;	 = = 	J JfI+ I\+a. +a\ ' )a& )aX#a( #aL$a' $aN&a/ &aT  3*M+-A! J J\S/ Sr2J2J2Jl:~x-8 x-x -w8P"M;!#79
 zT r   