
    rhB                    L   S r SSK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  \R                  " S
5      r " S S\R                  5      r " S S5      r " S S5      r " S S\R&                  5      r\/r\S:X  a  SSKr\R.                  " \5        gg)an  
This module describes classes for performing windowed and overlapping windowed analysis.
The :class:`music21.analysis.windowed.WindowedAnalysis` provides a reusable framework for
systematic overlapping window analysis at the starting at the level of the quarter note
and moving to the size of an entire :class:`music21.stream.Stream`.

Modular analysis procedures inherit from :class:`music21.analysis.discrete.DiscreteAnalysis`.
The :class:`music21.analysis.discrete.KrumhanslSchmuckler` (for algorithmic key detection)
and :class:`music21.analysis.discrete.Ambitus` (for pitch range analysis) classes provide examples.
    )annotationsN)exceptions21)common)environment)meter)stream)DiscreteAnalysisExceptionzanalysis.windowedc                      \ rS rSrSrg)WindowedAnalysisException(    N)__name__
__module____qualname____firstlineno____static_attributes__r       S/home/james-whalen/.local/lib/python3.13/site-packages/music21/analysis/windowed.pyr   r   (   s    r   r   c                  T    \ rS rSrSrS rS	S jrS
S jr     S     SS jjrSr	g)WindowedAnalysis.   z
Create a WindowedAnalysis object.

The provided `analysisProcessor` must provide a `process()` method that,
when given a windowed Stream (a Measure) returns two element tuple containing
(a) a data value (implementation dependent) and (b) a color code.
c                    X l         [        U[        R                  5      (       d  [	        S5      eUR                  5       (       a  UR                  5       nXl        U R                  5       U l	        g )Nznon-stream provided as argument)
	processor
isinstancer   Streamr   hasPartLikeStreamsflatten
_srcStreamgetMinimumWindowStream_windowedStream)self	streamObjanalysisProcessors      r   __init__WindowedAnalysis.__init__7   sW    *)V]]33+,MNN''))!))+I##::<r   c                   [         R                  " 5       nUR                  S[        R                  " U5      5        U R
                  R                  US9nUR                  S5        U(       d  [        S5      eUR                  SS9  U$ )a  
Take the loaded stream and restructure it into measures of 1 quarter note duration.

>>> s = corpus.parse('bach/bwv324')
>>> p = analysis.discrete.Ambitus()

Placing one part into analysis:

>>> wa = analysis.windowed.WindowedAnalysis(s.parts[0], p)

>>> post = wa.getMinimumWindowStream()
>>> len(post.getElementsByClass(stream.Measure))
42
>>> post.getElementsByClass(stream.Measure).first()
<music21.stream.Measure 1 offset=0.0>

Time signature set to 1/4 time signature

>>> post.getElementsByClass(stream.Measure).first().timeSignature
<music21.meter.TimeSignature 1/4>

leaves one note in this measure

>>> len(post.getElementsByClass(stream.Measure)[1].notes)
1

Placing a score with parts into analysis will automatically flatten
the stream.  So these two calls are equivalent:

>>> wa1 = analysis.windowed.WindowedAnalysis(s, p)
>>> wa2 = analysis.windowed.WindowedAnalysis(s.flatten(), p)

r   )meterStreamMeasurezMaking measures failedT)inPlace)
r   r   insertr   TimeSignaturer   makeMeasuresremoveByNotOfClassr   makeTies)r!   timeSignaturer'   measureds       r   r   'WindowedAnalysis.getMinimumWindowStreamB   sy    H mmo1e11-@A
 ??//K/H 	##I.+,DEE$'r   c                "   [        U R                  5      nUS:X  a  X1-
  S-   nOVUS:X  a.  X1-  S-   n[        U5      nXT:w  a  [        R                  " S5        O"US:X  a  UnO[
        R                  " SU 35      eS/U-  nS/U-  n[        U5      nUS:X  aw  U Hn  n	[        R                  " 5       n
[        XU-   5       H!  nU
R                  U R                  U   5        M#      U R                  R                  U
5      u  Xi'   Xy'   Mp     Xg4$ US:X  a  SnX-   n[        U5       H  n	[        U[        U R                  5      5      n[        R                  " 5       n
[        X5       H!  nU
R                  U R                  U   5        M#      U R                  R                  U
5      u  Xi'   Xy'   UnX-   nM     Xg4$ US:X  a  / n[        X1-
  S-   5       Hp  n	[        R                  " 5       n
/ n[        XU-   5       H2  nU
R                  U R                  U   5        UR                  U5        M4     UR                  X/5        Mr     [        U5       Hh  n	[        R                  " 5       n
U H'  u  nnX;   d  M  U H  nU
R                  U5        M     M)      U R                  R                  U
5      u  Xi'   Xy'   Mj     Xg4$ ! [         a    Su  Xi'   Xy'    GM@  f = f! [         a    Su  Xi'   Xy'    GNDf = f! [         a    Su  Xi'   Xy'    M  f = f)	a  
Calls, for a given window size, an analysis method across all windows in the source Stream.

If windowType is "overlap", windows above size 1 are always overlapped, so if a window
of size 2 is used, windows 1-2, then 2-3, then 3-4 are compared. If a window of size 3
is used, windows 1-3, then 2-4, then 3-5 are compared.

Windows are assumed to be partitioned by :class:`music21.stream.Measure` objects.

Returns two lists for results, each equal in size to the length of minimum windows
minus the window size plus one. If we have 20 1/4 windows, then the results lists
will be of length 20 for window size 1, 19 for window size 2, 18 for window size 3, etc.


>>> s = corpus.parse('bach/bwv66.6')
>>> p = analysis.discrete.Ambitus()
>>> wa = analysis.windowed.WindowedAnalysis(s.flatten(), p)
>>> len(wa._windowedStream)
36
>>> a, b = wa.analyze(1)
>>> len(a), len(b)
(36, 36)

>>> a, b = wa.analyze(4)
>>> len(a), len(b)
(33, 33)

>>> a, b = wa.analyze(1, windowType='noOverlap')
>>> len(a), len(b)
(37, 37)

>>> a, b = wa.analyze(4, windowType='noOverlap')
>>> len(a), len(b)
(10, 10)

>>> a, b = wa.analyze(1, windowType='adjacentAverage')
>>> len(a), len(b)
(36, 36)

overlap   	noOverlapzJmaxWindowCount is not divisible by windowSize, possibly undefined behavioradjacentAveragezUnknown windowType: r   ))NNr   z#ffffff)lenr    intwarningswarnr   Music21Exceptionranger   r   appendr   processr	   min)r!   
windowSize
windowTypemaxWindowCountwindowCountwindowCountFloatdatacolorwindowCountIndicesicurrentjstartend
overlappedparticipants
dataStreamms                     r   analyzeWindowedAnalysis.analyzeu   s(   R T112 "(59K;&-:Q>./K.` ,,(K//2Fzl0STTs[ k!";/"' --/qj.1ANN4#7#7#:; 2C(,(>(>w(G%DGUX (r {[ ;&E$C;'#s4#7#789 --/u*ANN4#7#7#:; +C(,(>(>w(G%DGUX
 ( (T {5 ,,J>6:; --/!qj.1ANN4#7#7#:; ''* 2 !!7"9: < >* --/0:,J(!+A#NN1- ", 1;C(,(>(>w(G%DGUX + {c 1 C(B%DGUXC  1 C(B%DGUXC: 1 C(B%DGUXCs6   %"K"K"K7KKK43K47LLc                   Uc  [        U R                  5      nOUnUc  [        U R                  5      nOUnUc  SnODUR                  5       S;   a  SnO-UR                  5       S;   a  SnOUR                  5       S;   a  Sn/ n/ n	/ n
[        U[        5      (       a  [        [        XvS-   U5      5      nOP[        R                  " U5      u  p/ nUn UR                  U5        U[        [	        U5      5      -  nXS-  :  a  OM3  U(       a+  [        U R                  5      nX;  a  UR                  U5        U HM  nU R                  UUS	9u  nnUR                  U5        U	R                  U5        S
U0nU
R                  U5        MO     XU
4$ )a9  
Main method for windowed analysis across one or more window sizes.

Calls :meth:`~music21.analysis.WindowedAnalysis.analyze` for
the number of different window sizes to be analyzed.

The `minWindow` and `maxWindow` set the range of window sizes in quarter lengths.
The `windowStepSize` parameter determines the increment between these window sizes,
in quarter lengths.

If `minWindow` or `maxWindow` is None, the largest window size available will be set.

If `includeTotalWindow` is True, the largest window size will always be added.


>>> s = corpus.parse('bach/bwv324')
>>> ksAnalyzer = analysis.discrete.KrumhanslSchmuckler()

placing one part into analysis

>>> sopr = s.parts[0]
>>> wa = analysis.windowed.WindowedAnalysis(sopr, ksAnalyzer)
>>> solutions, colors, meta = wa.process(1, 1, includeTotalWindow=False)
>>> len(solutions)  # we only have one series of windows
1

>>> solutions, colors, meta = wa.process(1, 2, includeTotalWindow=False)
>>> len(solutions)  # we have two series of windows
2

>>> solutions[1]
[(<music21.pitch.Pitch B>, 'major', 0.6844...),
 (<music21.pitch.Pitch B>, 'minor', 0.8308...),
 (<music21.pitch.Pitch D>, 'major', 0.6844...),
 (<music21.pitch.Pitch B>, 'minor', 0.8308...),...]

>>> colors[1]
['#ffb5ff', '#9b519b', '#ffd752', '#9b519b', ...]

>>> meta
[{'windowSize': 1}, {'windowSize': 2}]
r3   r3   )	nooverlapnonoverlappingr5   )adjacentaverager6   r4   g      ?)rA   r@   )r7   r    lowerr   r8   listr<   r   getNumFromStrr=   roundrQ   )r!   	minWindow	maxWindowwindowStepSizerA   includeTotalWindow	maxLength	minLengthsolutionMatrixcolorMatrix
metaMatrixwindowSizesnumjunkxtotalWindowrH   solution	colorNamemetas                       r   r>   WindowedAnalysis.process   s   b D001I!ID001I!I"J;."J#BB$J#66*J 
nc**uYA~NOK,,^<ICKA""1%c#h'D()	  d223K-"";/A #',,qZ,"HHi!!(+y) !$Dd#  J66r   )r   r    r   N)z1/4rT   )r4   r4   r4   r3   T)r\   
int | Noner]   rn   r^   z	int | str)
r   r   r   r   __doc__r$   r   rQ   r>   r   r   r   r   r   r   .   sS    	=1fyz '(&'*+$#'f7#f7#f7 !(f7 f7r   r   c                      \ rS rSrS rSrg)TestMockProcessori\  c                L    [        UR                  5       R                  5      S4$ )z(
Simply count the number of notes found
N)r7   r   notesAndRests)r!   	subStreams     r   r>   TestMockProcessor.process^  s#     9$$&445t;;r   r   N)r   r   r   r   r>   r   r   r   r   rq   rq   \  s    <r   rq   c                  ,    \ rS rSrS rS rS rS rSrg)Testie  c                2    SSK Jn  U" U [        5       5        g )Nr   )testCopyAll)music21.test.commonTestry   globals)r!   ry   s     r   testCopyAndDeepcopyTest.testCopyAndDeepcopyf  s    7D')$r   c                &   SSK Jn  SSKJn  UR	                  S5      nUR
                  UR                  4 HX  nU" 5       n[        UR                  5       U5      n[        [        SS5      5      S /-    H  nUR                  Xw5      u  pn
M     MZ     g )Nr   corpusdiscretezbach/bwv324r4      )music21r   music21.analysisr   parseKrumhanslSchmucklerAmbitusr   r   rY   r<   r>   )r!   r   r   spClasspwarH   unused_xunused_yunused_zs              r   	testBasicTest.testBasicj  s~    "- LL'33X5E5EFFA "!))+q1B%1+&$//1zz!/?,H 0 Gr   c                   [        5       nSSKJn  [        R                  " 5       nUR                  UR                  S5      5        UR                  UR                  S5      5        [        R                  " 5       nUR                  UR                  S5      5        UR                  UR                  S5      5        UR                  UR                  S5      5        UR                  UR                  S5      5        UR                  UR                  S5      5        UR                  UR                  S5      5        UR                  UR                  S	5      5        UR                  UR                  S5      5        [        X15      n[        XA5      nU R                  [        UR                  5      S
5        U R                  [        UR                  5      S5        UR                  SSSSS9u  pxn	U R                  [        US   5      S
5        U R                  US   S   S5        U R                  US   S   S5        UR                  S
S
SSS9u  pxn	U R                  [        US   5      S5        U R                  US   S   S
5        UR                  SSSSS9u  pxn	U R                  [        US   5      S5        U R                  US   S   S5        U R                  US   S   S5        UR                  S
S
SSS9u  pxn	U R                  [        US   5      S5        UR                  SSSSS9u  pxn	U R                  [        US   5      S
5        UR                  SSSSS9u  pxn	U R                  [        US   5      S5        g)z:
Test that windows are doing what they are supposed to do
r   )noteCDEFGAB      r4   F)r_      N)rq   r   r   r   r   r=   Noter   assertEqualr7   r    r>   )
r!   r   r   s1s2wa1wa2aunused_bunused_cs
             r   testWindowingTest.testWindowing{  s     ]]_
		$))C.!
		$))C.!]]_
		$))C.!
		$))C.!
		$))C.!
		$))C.!
		$))C.!
		$))C.!
		$))C.!
		$))C.!r%r% 	S00115S00115 !$Aq! NXQqTA&1a!$1a!$ !$Aq! NXQqTA&1a!$ !$Aq! NXQqTA&1a!$1a!$ !$Aq! NXQqTA& !$Aq! NXQqTA& !$Aq! NXQqTA&r   c                   SSK Jn  SSKJn  SSKJn  UR                  5       nUR                  S5      n[        UR                  5       U5      nUR                  R                  UR                  5       S SSS9nUR                  5         g )	Nr   r   r   )graphzbach/bwv66.6r   r3   )
doneAction
windowSteprA   )r   r   r   r   r   r   r   r   r   plotWindowedKeyrun)r!   r   r   r   r   r   	unused_war   s           r   testVariableWindowingTest.testVariableWindowing  sk    -"!((*LL($QYY[!4	zz%%aiikd12y & J
r   r   N)	r   r   r   r   r|   r   r   r   r   r   r   r   rw   rw   e  s    %@"8'vr   rw   __main__)ro   
__future__r   unittestr9   r   r   r   r   r   r   music21.analysis.discreter	   EnvironmentenvironLocalr;   r   r   rq   TestCaserw   
_DOC_ORDERr   mainTestr   r   r   <module>r      s   	 #         ?&&':;	 = = 	i7 i7\	< <]8 ]F 
zT r   