
    rh                        S r SSKJr  SSKrSSKJr  SSKJr  SSKJr   " S S\5      r	 " S	 S
5      r
S r\S:X  a  SSKr\R                  " 5         gg)z
The floatingKey analyzer will give an approximation of the key at any point in
a score down to the measure level using a fixed window.  It helps smooth out
measures emphasizing non-chord tones, etc.
    )annotationsNkey)stream)AnalysisExceptionc                      \ rS rSrSrg)FloatingKeyException    N)__name__
__module____qualname____firstlineno____static_attributes__r       V/home/james-whalen/.local/lib/python3.13/site-packages/music21/analysis/floatingKey.pyr	   r	      s    r   r	   c                  :    \ rS rSrSrS
S jrS rS rS rS r	S	r
g)KeyAnalyzer   aS
  
KeyAnalyzer is the main object to use for floating analysis.

The `windowSize` attribute (default 4) determines how many measures to look at in making
the decision.  Make it larger for pieces (like Mozart sonatas) that you expect fewer key
changes.  Make it smaller for pieces (like Bach chorales) that you expect more key changes.
Or set it to an integer based on the number of the measures in the piece.

The `weightAlgorithm` attribute determines how to scale the weight of measures according to
their distance.  Currently only one algorithm is supported: floatingKey.divide.

TODO: Needs more work to work with second endings, partial measures, etc.

>>> b = corpus.parse('bwv66.6')
>>> ka = analysis.floatingKey.KeyAnalyzer(b)
>>> ka.windowSize = 2  # chorale uses quick key changes
>>> ka.run()  # first measure is the pickup
[<music21.key.Key of A major>, <music21.key.Key of A major>, <music21.key.Key of A major>,
 <music21.key.Key of f# minor>, <music21.key.Key of f# minor>, <music21.key.Key of f# minor>,
 <music21.key.Key of f# minor>, <music21.key.Key of f# minor>,
 <music21.key.Key of f# minor>, <music21.key.Key of f# minor>]

Raw analysis (no smoothing):

>>> ka.getRawKeyByMeasure()
[<music21.key.Key of A major>, <music21.key.Key of E major>, <music21.key.Key of A major>,
 <music21.key.Key of f# minor>, <music21.key.Key of E major>, <music21.key.Key of A major>,
 <music21.key.Key of b minor>, <music21.key.Key of C# major>,
 <music21.key.Key of F# major>, <music21.key.Key of b minor>]

Major smoothing:

>>> ka.windowSize = ka.numMeasures // 2
>>> ka.run()  # only the pickup seems to be in A major by this approach
[<music21.key.Key of A major>, <music21.key.Key of f# minor>, <music21.key.Key of f# minor>,
 <music21.key.Key of f# minor>, <music21.key.Key of f# minor>, <music21.key.Key of f# minor>,
 <music21.key.Key of f# minor>, <music21.key.Key of f# minor>,
 <music21.key.Key of f# minor>, <music21.key.Key of f# minor>]

>>> tiny = converter.parse('tinyNotation: c1 e1 g1 c1 d-4 d-4 d-4 d-4')
>>> ka = analysis.floatingKey.KeyAnalyzer(tiny)
>>> ka.windowSize = 1
>>> ka.run()  # This previously only gave four elements: am, CM, CM, CM
[<music21.key.Key of a minor>, <music21.key.Key of C major>, <music21.key.Key of C major>,
 <music21.key.Key of C major>, <music21.key.Key of b- minor>]

No measures will fail.

>>> s = stream.Part([note.Note()])
>>> ka = analysis.floatingKey.KeyAnalyzer(s)
Traceback (most recent call last):
music21.analysis.floatingKey.FloatingKeyException: Stream must have Measures inside it

* Changed in v7: analysis now incorporates final measures in pieces without pickup measures:
Nc                |   Uc  [        S5      eXl        SU l        / U l        0 U l        [
        U l        UR                  5       (       a)  UR                  5       R                  R                  5       nOUn[        UR                  [        R                  5      5      U l        U R                  S:X  a  [        S5      eg )NzNeed a Stream to initialize   r   z#Stream must have Measures inside it)r	   r   
windowSizerawKeyByMeasure_interpretationMeasureDictdivideweightAlgorithmhasPartLikeStreamsiterpartsfirstlengetElementsByClassMeasurenumMeasures)selfsps      r   __init__KeyAnalyzer.__init__S   s    9&'DEE!*,'%!!$$&AAq33FNNCDq &'LMM !r   c                B    U R                  5         U R                  5       $ N)getRawKeyByMeasuresmoothInterpretationByMeasure)r%   s    r   runKeyAnalyzer.rune   s    !1133r   c                   / n[        U R                  5       Hd  nU R                  R                  USS9nUb  UR	                  5       R
                  (       d  S nOUR                  S5      nUR                  U5        Mf     Xl        U$ )NT)indicesNotNumbersr   )	ranger$   r   measurerecursenotesanalyzeappendr   )r%   keyByMeasureimks        r   r,   KeyAnalyzer.getRawKeyByMeasurei   sw    t''(A##A#>Ay		 1 1IIe$" )  ,r   c                x   XR                   ;   a  U R                   U   $ U R                  (       d  U R                  5         U R                  U   nUc  g0 nUR                  X2R                  '   UR
                   H  nUR                  X4R                  '   M     X0R                   U'   [        R                  " U5      $ )zQ
Returns a dictionary of interpretations for the measure.
`mIndex` is 0-indexed.
N)r   r   r,   correlationCoefficienttonicPitchNameWithCasealternateInterpretationscopy)r%   mIndexmkinterpretationsotherKeys        r   getInterpretationByMeasure&KeyAnalyzer.getInterpretationByMeasurev   s    
 444226::####%!!&):575N5N11233H?G?^?^O;;< 42A''/yy))r   c                   / nU R                   n[        U R                  5       H  nU R                  U5      nUc  M  [        SU R                  -  U R                  S-   5       HY  nX5-   nUS:  d  X`R                  :  d  Xc:X  a  M#  U R                  U5      nUc  M9  U H  nU" Xx   U5      n	XH==   U	-  ss'   M     M[     [        XDR                  S9n
UR                  [        R                  " U
5      5        M     U$ )N   r   r   )
r   r2   r$   rF   r   maxgetr7   r   Key)r%   smoothedKeysByMeasure	algorithmr9   baseInterpretationsjmNumnewInterpretationsr;   coefficientbestNames              r   r-   )KeyAnalyzer.smoothInterpretationByMeasure   s     "((	t''(A"&"A"A!"D"*2/11DEu!8t'7'7749%)%D%DT%J"%10&/0B0Eq&I+.+=. 1 F .4K4KLH!(():; )  %$r   )r   r$   r   r   r   r   r+   )r   r   r   r   __doc__r(   r.   r,   rF   r-   r   r   r   r   r   r      s"    6nN$4*(%r   r   c                $    U [        U5      S-   -  $ )zp
Divide the coefficient by the absolute value of the distance + 1

>>> analysis.floatingKey.divide(4.0, -1)
2.0
rJ   )abs)rT   distances     r   r   r      s     #h-!+,,r   __main__)rW   
__future__r   rA   music21r   r   music21.exceptions21r   r	   r   r   r   mainTestr   r   r   <module>r`      sX   
 #    2	, 	C% C%J- z r   