§
    „Øjc5  ã                  óR  — U d Z ddlmZ ddlmZ ddl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mZ ddlmZ ddlmZ dd	lmZ  ej        d
¦  «        Zdddddœd„Zd„ Zdd„Zd„ Zdddœd„Zd„ Zd d„Zd„ Z	 	 	 d!d„Z	 	 	 	 d"d„Zg Zded<   edk    rddlZ ej         ¦   «          dS dS )#az  
tools for segmenting -- that is, dividing up a score into small, possibly overlapping
sections -- for searching across pieces for similarity.

Speed notes:

   this module is definitely a case where running PyPy rather than cPython will
   give you a 3-5x speedup.

   If you really want to do lots of comparisons, the `scoreSimilarity` method will
   use python-Levenshtein if it is installed, unless forceDifflib is set to True.
   python-Levenshtein can be installed via **pip install python-Levenshtein**.
   The ratios are very slightly different, but the speedup is between 10 and 100x!
   (But then PyPy probably won't work.)
é    )Úannotations)ÚOrderedDictN)Úpartial)Úcommon)Ú	converter)Úcorpus)Úenvironmentzsearch.segmenté   é   )ÚsegmentLengthsÚoverlapÚ	algorithmÚjitterc               óˆ  ‡‡— ddl m} |€|j        }|                      ¦   «         j                             ¦   «         } ||d¬¦  «        \  }}t          |¦  «        }	t          t          j	        |	‰‰z
  z  ¦  «        ¦  «        }
ˆˆfd„t          |
¦  «        D ¦   «         }g }g }|D ]š}|t          j        d|z  |¦  «        z  }t          d|¦  «        }t          ||	dz
  ¦  «        }t          |‰z   |	¦  «        }|||…         }||         ||dz
           f}|                     |¦  «         |                     |¦  «         Œ›||fS )	aË  
    Translates a monophonic part with measures to a set of segments of length
    `segmentLengths` (measured in number of notes) with an overlap of `overlap` notes
    using a conversion algorithm of `algorithm` (default: search.translateStreamToStringNoRhythm).
    Returns two lists, a list of segments, and a list of tuples of measure start and end
    numbers that match the segments.

    If algorithm is None then a default algorithm of music21.search.translateStreamToStringNoRhythm
    is used

    >>> luca = corpus.parse('luca/gloria')
    >>> lucaCantus = luca.parts[0]
    >>> segments, measureLists = search.segment.translateMonophonicPartToSegments(lucaCantus)
    >>> segments[0:2]
    ['HJHEAAEHHCE@JHGECA@A>@A><A@AAE', '@A>@A><A@AAEEECGHJHGH@CAE@FECA']

    Segment zero begins at measure 1 and ends in m. 12.  Segment 1 spans m.7 - m.18:

    >>> measureLists[0:2]
    [(1, 12), (7, 18)]

    >>> segments, measureLists = search.segment.translateMonophonicPartToSegments(
    ...     lucaCantus,
    ...     algorithm=search.translateDiatonicStreamToString)
    >>> segments[0:2]
    ['CRJOMTHCQNALRQPAGFEFDLFDCFEMOO', 'EFDLFDCFEMOOONPJDCBJSNTHLBOGFE']

    >>> measureLists[0:2]
    [(1, 12), (7, 18)]

    r   )ÚsearchNT)ÚreturnMeasuresc                ó    •— g | ]
}|‰‰z
  z  ‘ŒS © r   )Ú.0Úir   r   s     €€ú‹/Users/tedpeters/Documents/Projects/Claude Projects/Misc Tools/notename-sheets/.venv/lib/python3.11/site-packages/music21/search/segment.pyú
<listcomp>z5translateMonophonicPartToSegments.<locals>.<listcomp>]   s#   ø€ ÐUÐUÐU¸�Q˜.¨7Ñ2Ñ3ÐUÐUÐUó    éÿÿÿÿé   )Úmusic21r   ÚtranslateStreamToStringNoRhythmÚrecurseÚnotesÚstreamÚlenÚintÚmathÚceilÚrangeÚrandomÚrandintÚmaxÚminÚappend)ÚinputStreamr   r   r   r   r   ÚnStreamÚ	outputStrÚmeasuresÚtotalLengthÚnumberOfSegmentsÚsegmentStartsÚsegmentListÚmeasureListÚsegmentStartÚ
segmentEndÚcurrentSegmentÚmeasureTuples    ``               r   Ú!translateMonophonicPartToSegmentsr8   -   sz  øø€ ðN ÐÐÐÐÐØÐØÔ:ˆ	à×!Ò!Ñ#Ô#Ô)×0Ò0Ñ2Ô2€GØ#˜) G¸DÐAÑAÔAÑ€IˆxÝ�i‘.”.€Kå�4œ9 [°NÀWÑ4LÑ%MÑNÔNÑOÔOÐØUÐUÐUÐUÐU½UÐCSÑ=TÔ=TÐUÑUÔU€Mð €KØ€Kà%ð 
)ð 
)ˆØ�œ r¨F¡{°FÑ;Ô;Ñ;ˆÝ˜1˜lÑ+Ô+ˆÝ˜<¨°q©Ñ9Ô9ˆå˜¨Ñ6¸ÑDÔDˆ
Ø" <°
Ð#:Ô;ˆØ  Ô.°¸Àa¹Ô0HÐIˆà×Ò˜>Ñ*Ô*Ð*Ø×Ò˜<Ñ(Ô(Ð(Ð(Ø˜Ð%Ð%r   c                ór   — | j         }g }|D ]*}t          |fi |¤Ž\  }}|                     ||dœ¦  «         Œ+|S )au  
    Creates segment and measure lists for each part of a score
    Returns list of dictionaries of segment and measure lists

    >>> bach = corpus.parse('bwv66.6')
    >>> scoreList = search.segment.indexScoreParts(bach)
    >>> scoreList[1]['segmentList'][0]
    '@B@@@@ED@DBDA=BB@?==B@@EBBDBBA'
    >>> scoreList[1]['measureList'][0:3]
    [(0, 7), (4, 9), (8, 9)]
    )r2   r3   )Úpartsr8   r*   )Ú	scoreFileÚkeywordsÚscoreFilePartsÚindexedListÚpartr2   r3   s          r   ÚindexScorePartsr@   r   sx   € ð ”_€NØ€KØð ð ˆÝ#DØð$ð $Øð$ð $Ñ ˆ�[à×ÒØ&Ø&ð
ð 
ñ 	ô 	ð 	ð 	ð Ðr   Fc                óú   — t          | t          j        ¦  «        st          j        | ¦  «        } | j        }	 t	          | fi |¤Ž}n3# t
          $ r&}|st          d| › d|› �¦  «         d}n|‚Y d}~nd}~ww xY w||| fS )z5
    Index one path in the context of multicore.
    zFailed on parse/index for, z: Ú N)Ú
isinstanceÚpathlibÚPathÚnameÚindexOnePathÚ	ExceptionÚprint)ÚfilePathÚfailFastr<   ÚshortFpÚindexOutputÚes         r   Ú_indexSingleMulticorerO   Š   s¸   € õ �h¥¤Ñ-Ô-ð *Ý”< Ñ)Ô)ˆàŒm€GðÝ" 8Ð8Ð8¨xÐ8Ð8ˆˆøÝð ð ð Øð 	ÝÐ?°Ð?Ð?¸AÐ?Ð?Ñ@Ô@Ð@ØˆKˆKàˆGð ˆKˆKˆKˆKøøøøðøøøð �[ (Ð+Ð+s   ·A Á
A5ÁA0Á0A5c           	     óD   — t          d|d         › d| › d|› d�¦  «         d S )NzIndexed r   ú (ú/ú))rI   )ÚnumRunÚtotalRunÚlatestOutputs      r   Ú_giveUpdatesMulticorerW   ž   s6   € Ý	Ð
<�\ !”_Ð
<Ð
<¨Ð
<Ð
<°Ð
<Ð
<Ð
<Ñ=Ô=Ð=Ð=Ð=r   T)ÚgiveUpdatesÚrunMulticorec               óæ  — |du rt           }nd}t          t          fi |¤Ž}t          t	          | ¦  «        ¦  «        D ]?}t          | |         t          j        ¦  «        st          j        | |         ¦  «        | |<   Œ@|rt          j	        | ||¬¦  «        }nt          j
        | ||¬¦  «        }i }|D ]\  }	}
}|	|
f||<   Œg }| D ]}|                     ||         ¦  «         Œt          |¦  «        }|S )a±  
    Returns a dictionary of the lists from indexScoreParts for each score in
    scoreFilePaths

    >>> #_DOCS_SHOW searchResults = corpus.search('bwv190')
    >>> searchResults = corpus.corpora.CoreCorpus().search('bwv190') #_DOCS_HIDE
    >>> fpsNamesOnly = sorted([searchResult.sourcePath for searchResult in searchResults])
    >>> len(fpsNamesOnly)
    2

    >>> scoreDict = search.segment.indexScoreFilePaths(fpsNamesOnly)
    >>> len(scoreDict['bwv190.7.mxl'])
    4

    >>> scoreDict['bwv190.7.mxl'][0]['measureList']
    [(0, 9), (6, 15), (11, 20), (17, 25), (22, 31), (27, 32)]

    >>> scoreDict['bwv190.7.mxl'][0]['segmentList'][0]
    'NNJLNOLLLJJIJLLLLNJJJIJLLJNNJL'
    TN)ÚupdateFunction)rW   r   rO   r%   r!   rC   rD   rE   r   ÚrunParallelÚrunNonParallelr*   r   )ÚscoreFilePathsrX   rY   r<   r[   Ú	indexFuncr   ÚrpListUnOrderedÚrpDictÚoutShortNameÚoutDataÚoriginalPathlibÚrpListÚpÚ	scoreDicts                  r   ÚindexScoreFilePathsrh   £   sA  € ð4 �dÐÐÝ.ˆˆàˆåÕ-Ð:Ð:°Ð:Ð:€Iå•3�~Ñ&Ô&Ñ'Ô'ð @ð @ˆÝ˜.¨Ô+­W¬\Ñ:Ô:ð 	@Ý '¤¨^¸AÔ->Ñ ?Ô ?ˆN˜1Ñøàð 	+Ý Ô,ØØØ)ð+ñ +ô +ˆˆõ
 !Ô/ØØØ)ð+ñ +ô +ˆð €FØ2Að :ð :Ñ.ˆ�g˜Ø#/°Ð"9ˆˆÑÐà€FØð !ð !ˆØ�Š�f˜Q”iÑ Ô Ð Ð å˜FÑ#Ô#€IàÐr   c                óö   — t          | t          j        ¦  «        st          j        | ¦  «        } |                      ¦   «         st	          j        | ¦  «        }nt          j        | ¦  «        }t          |fi |¤Ž}|S )z8
    Index a single path.  Returns a scoreDictEntry
    )rC   rD   rE   Úis_absoluter   Úparser   r@   )rJ   r<   ÚscoreObjÚscoreDictEntrys       r   rG   rG   á   sv   € õ �h¥¤Ñ-Ô-ð *Ý”< Ñ)Ô)ˆà×ÒÑ!Ô!ð -Ý”< Ñ)Ô)ˆˆå”? 8Ñ,Ô,ˆå$ XÐ:Ð:°Ð:Ð:€NØÐr   c                ó$  — |€t                                d¦  «        }n0t          |t          t          f¦  «        rt          j        |¦  «        }|                     d¦  «        5 }t          j	        | |¦  «         ddd¦  «         n# 1 swxY w Y   |S )zÆ
    Save the score dict from indexScoreFilePaths as a .json file for quickly
    reloading

    Returns the filepath (assumes you'll probably be using a temporary file)
    as a pathlib.Path()
    Nz.jsonÚwb)
ÚenvironLocalÚgetTempFilerC   ÚstrÚbytesrD   rE   ÚopenÚjsonÚdump)rg   rJ   Úfs      r   ÚsaveScoreDictrx   ñ   sÁ   € ð ÐÝ×+Ò+¨GÑ4Ô4ˆˆÝ	�H�s¥E˜lÑ	+Ô	+ð *Ý”< Ñ)Ô)ˆà	�Š�tÑ	Ô	ð   ÝŒ	�)˜QÑÔÐð ð  ð  ñ  ô  ð  ð  ð  ð  ð  ð  øøøð  ð  ð  ð  ð €Os   Á#BÂB	ÂB	c                óä   — t          | t          j        ¦  «        st          j        | ¦  «        } |                      d¦  «        5 }t	          j        |¦  «        }ddd¦  «         n# 1 swxY w Y   |S )z1
    Load the scoreDictionary from filePath.
    ÚrbN)rC   rD   rE   rt   ru   Úload)rJ   rw   rg   s      r   ÚloadScoreDictr|     sž   € õ �h¥¤Ñ-Ô-ð *Ý”< Ñ)Ô)ˆà	�Š�tÑ	Ô	ð ! Ý”I˜a‘L”Lˆ	ð!ð !ð !ñ !ô !ð !ð !ð !ð !ð !ð !øøøð !ð !ð !ð !àÐs   ÁA%Á%A)Á,A)c                óÆ   — |du rt          j        |d| ¦  «        }nE	 ddlm} |                     |d| ¦  «        }n&# t          $ r t          j        |d| ¦  «        }Y nw xY w|S )zÜ
    Returns either a difflib.SequenceMatcher or pyLevenshtein
    StringMatcher.StringMatcher object depending on what is installed.

    If forceDifflib is True then use difflib even if pyLevenshtein is installed:
    TrB   r   )ÚStringMatcher)ÚdifflibÚSequenceMatcherÚLevenshteinr~   ÚImportError)Úseq2ÚjunkÚforceDifflibÚsmObjectÚpyLevenshteins        r   ÚgetDifflibOrPyLevrˆ     s�   € ð �tÐÐÝÔ*¨4°°TÑ:Ô:ˆˆð	?àBÐBÐBÐBÐBÐBØ$×2Ò2°4¸¸TÑBÔBˆHˆHøÝð 	?ð 	?ð 	?ÝÔ.¨t°R¸Ñ>Ô>ˆHˆHˆHð	?øøøà€Os   �; » AÁAé   c                ó  ‡ ‡‡‡‡	‡
‡‡‡‡‡‡— g ŠdŠt          ‰ ¦  «        Št          ‰                      ¦   «         ¦  «        Š
dŠ	dŠˆˆˆˆ	ˆ ˆ
ˆˆˆˆˆˆfd„}t          ‰¦  «        D ]§}‰
|         Š‰ ‰         }‰dz  Š|du rt	          d‰› d‰› d‰› d	�¦  «         t          t          |¦  «        ¦  «        D ]VŠ	t          |‰	         d
         ¦  «        D ]8\  Š}t          |¦  «        ‰k     rŒ|‰	         d         ‰         Š ||¦  «         Œ9ŒWŒ¨‰S )a  
    Find the level of similarity between each pair of segments in a scoreDict.

    This takes twice as long as it should because it does not cache the
    pairwise similarity.

    >>> filePaths = []
    >>> for p in ('bwv197.5.mxl', 'bwv190.7.mxl', 'bwv197.10.mxl'):
    ...     #_DOCS_SHOW source = corpus.search(p)[0].sourcePath
    ...     source = corpus.corpora.CoreCorpus().search(p)[0].sourcePath #_DOCS_HIDE
    ...     filePaths.append(source)
    >>> scoreDict = search.segment.indexScoreFilePaths(filePaths)
    >>> scoreSim = search.segment.scoreSimilarity(scoreDict, forceDifflib=True) #_DOCS_HIDE
    >>> #_DOCS_SHOW scoreSim = search.segment.scoreSimilarity(scoreDict)
    >>> len(scoreSim)
    496

    Returns a list of tuples of first score name, first score voice number, first score
    measure number, second score name, second score voice number, second score
    measure number, and similarity score (0 to 1).

    >>> for result in scoreSim[133:137]:
    ...     result
    ('bwv197.5.mxl', 1, 1, (4, 10), 'bwv190.7.mxl', 3, 4, (22, 30), 0.13...)
    ('bwv197.5.mxl', 1, 1, (4, 10), 'bwv197.10.mxl', 0, 0, (0, 8), 0.2)
    ('bwv197.5.mxl', 1, 1, (4, 10), 'bwv197.10.mxl', 1, 0, (0, 7), 0.266...)
    ('bwv197.5.mxl', 1, 1, (4, 10), 'bwv197.10.mxl', 1, 1, (4, 9), 0.307...)
    r   Nc                ó  •— t          | ‰¬¦  «        }t          ‰‰¦  «        D ]æ}‰|         }‰|         }t          t          |¦  «        ¦  «        D ]·}t          ||         d         ¦  «        D ]™\  }}t          |¦  «        ‰k     rŒ|                     |¦  «         |                     ¦   «         }||         d         |         }	‰‰‰‰||||	|f	}
‰                     |
¦  «         ‰sŒy||||	‰‰‰‰|f	}‰                     |¦  «         ŒšŒ¸Œçd S )N)r…   r2   r3   )rˆ   r%   r!   Ú	enumerateÚset_seq1Úratior*   )ÚthisSegmentÚdlÚthatScoreNumberÚthatScoreKeyÚ	thatScoreÚpNum2ÚthatSegmentNumberÚthatSegmentrŽ   ÚthatMeasureNumberÚsimilarityTupleÚsimilarityTupleReversedr…   ÚincludeReverseÚminimumLengthÚpNumrg   ÚscoreDictKeysÚ
scoreIndexÚsegmentNumberÚsimilarityScoresÚthisMeasureNumberÚthisScoreKeyÚtotalScoress               €€€€€€€€€€€€r   ÚdoOneSegmentz%scoreSimilarity.<locals>.doOneSegmentR  su  ø€ Ý˜{¸ÐFÑFÔFˆå$ Z°Ñ=Ô=ð $	Eð $	EˆOØ(¨Ô9ˆLØ! ,Ô/ˆIÝ�s 9™~œ~Ñ.Ô.ð !Eð !E�Ý6?Ø! %Ô(¨Ô7ñ79ô 79ð  Eð  EÑ2Ð% {å˜;Ñ'Ô'¨-Ò7Ð7Ø Ø—K’K Ñ,Ô,Ð,ØŸHšH™JœJ�EØ(1°%Ô(8¸Ô(GÐHYÔ(ZÐ%à$ØØ%Ø)Ø$ØØ)Ø)Øð
'�Oð %×+Ò+¨OÑ<Ô<Ð<Ø)ð !Ø à$ØØ)Ø)Ø$ØØ%Ø)Øð
/Ð+ð %×+Ò+Ð,CÑDÔDÐDÐDðA Eð!Eð$	Eð $	Er   r   Tz
Comparing rQ   rR   rS   r2   r3   )r!   ÚlistÚkeysr%   rI   rŒ   )rg   r›   rX   rš   r…   r¤   ÚthisScoreNumberÚ	thisScoreÚthisSegmentOuterrœ   r�   rž   rŸ   r    r¡   r¢   r£   s   `` ``    @@@@@@@@r   ÚscoreSimilarityrª   '  s¨  øøøøøøøøøøøø€ ðH ÐØ€JÝ�i‘.”.€KÝ˜ŸšÑ)Ô)Ñ*Ô*€MØ€DØ€Mð'Eð 'Eð 'Eð 'Eð 'Eð 'Eð 'Eð 'Eð 'Eð 'Eð 'Eð 'Eð 'Eð 'Eð 'Eð 'EõR ! Ñ-Ô-ð /ð /ˆØ$ _Ô5ˆØ˜lÔ+ˆ	Ø�a‰ˆ
Ø˜$ÐÐÝÐJ˜|ÐJÐJ¨zÐJÐJ¸KÐJÐJÐJÑKÔKÐKÝ�#˜i™.œ.Ñ)Ô)ð 	/ð 	/ˆDÝ3<¸YÀt¼_È]Ô=[Ñ3\Ô3\ð /ð /Ñ/�Ð/ÝÐ'Ñ(Ô(¨=Ò8Ð8ØØ$-¨d¤O°MÔ$BÀ=Ô$QÐ!Ø�Ð-Ñ.Ô.Ð.Ð.ð	/ð	/ð Ðr   z
list[type]Ú
_DOC_ORDERÚ__main__)F)N)NNF)r‰   FFF)!Ú__doc__Ú
__future__r   Úcollectionsr   r   Ú	functoolsr   ru   r#   rD   r&   r   r   r   r   r	   ÚEnvironmentrp   r8   r@   rO   rW   rh   rG   rx   r|   rˆ   rª   r«   Ú__annotations__Ú__name__ÚmainTestr   r   r   ú<module>rµ      s  ððð ð ð  #Ð "Ð "Ð "Ð "Ð "à #Ð #Ð #Ð #Ð #Ð #Ø €€€Ø Ð Ð Ð Ð Ð Ø €€€Ø €€€Ø €€€Ø €€€à Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð à&ˆ{Ô&Ð'7Ñ8Ô8€ð ØØØðA&ð A&ð A&ð A&ð A&ðJð ð ð0,ð ,ð ,ð ,ð(>ð >ð >ð %*Ø%)ð;ð ;ð ;ð ;ð ;ð|ð ð ð ð ð ð ð&	ð 	ð 	ð 
Ø	Øðð ð ð ð2 ØØØðcð cð cð cðP €
Ð Ð Ð Ñ ð ˆzÒÐØ€N€N€NØ€GÔÑÔÐÐÐð Ðr   