
    rh_              	         % 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
  \	R                  " S	5      rS
// SQ/ SQ/ SQ/ SQ/ SQ/ SQ/ SQS.rS&SS.S jjrS'SS.S jjrS(S jrS(S jr " S S\R$                  5      r " S S\R(                  5      r0 rS\S'    " S S 5      r " S! S"5      r " S# S$\R4                  5      r\/r\S%:X  a  SSKr\R<                  " \5        gg))zK
Utility routines for processing text in scores and other musical objects.
    )annotationsN)base)common)exceptions21)environment)styletextzal-)theaan)derdiedasdesdemdeneineineeinereinemeinen)dehetz'teen)ellaloslasununaunosunas)or   osasumumaunsumas)
ler   lesl'r   uner   duzde lar   )illor   r,   iglir*   zun'r   unor    deldellodelladeideglidelle)arenr   nlesptfrit wordSeparatorc               8   / n/ nU R                  5       R                  nU GHS  n UR                  US-
     nUR                  S:w  d  M)  UR
                  S;   a,  UR                  b  UR                  UR                  5        Mc  Me  UR
                  S;   aL  UR                  b  UR                  UR                  5        UR                  SR                  U5      5        / nM  UR
                  S:X  am  UR                  b  UR                  UR                  5        UR                  S   R
                  S;   a%  UR                  SR                  U5      5        / nGM;  GM>  [        SUR
                   35      e   UR                  U5      $ ! [         a     GMw  f = f)	a0  
Concatenate text from a stream. The Stream is automatically flattened.

The `lineNumber` parameter determines which line of text is assembled,
as a ONE-indexed identifier in the .lyrics array.
(To be changed in v8 to go with an identifier.).  This means that
`lineNumber=0` will retrieve the last line of text.

>>> s = stream.Stream()
>>> n1 = note.Note()
>>> n1.lyric = 'Hi'
>>> n2 = note.Note()
>>> n2.lyric = 'there'
>>> s.append(n1)
>>> s.append(n2)
>>> text.assembleLyrics(s)
'Hi there'

Composite lyrics can also be assembled.

>>> composite = note.Lyric()
>>> composite0 = note.Lyric(text="He'", syllabic='begin')
>>> composite1 = note.Lyric(text="ya", syllabic='end')
>>> composite1.elisionBefore = '_'
>>> composite.components = [composite0, composite1]
>>> n1.lyrics[0] = composite
>>> text.assembleLyrics(s)
"He'_ya there"

To get the lyrics from another line, set the lineNumber attribute.
(see also :func:`~music21.text.assembleAllLyrics` to get all
lyrics).

>>> n1.addLyric('Bye')
>>> n2.addLyric('Now')
>>> text.assembleLyrics(s, lineNumber=2)
'Bye Now'
   _)beginmiddle)endsingleN 	compositez no known Text syllabic setting: )
flattennotesAndRestslyrics
IndexErrorr	   syllabicappendjoin
components
ValueError)streamIn
lineNumberrC   wordwords
noteStreamnlyricObjs           F/home/james-whalen/.local/lib/python3.13/site-packages/music21/text.pyassembleLyricsr_   9   sf   N DE!!#11J	xx
Q/H ==C  $77==,KK. -""&====,KK.RWWT]+""k1==,KK.&&r*337NNLL/D O !#CHDUDUCV!WXX5 6 e$$1  		s   F


FFc               p    Sn[        SU5       H#  n[        XUS9nUS:w  d  M  US:  a  XB-  nXF-  nM%     U$ )a  
Concatenate all Lyrics text from a stream separated by lyricSeparation.
The Stream is automatically recursed.

uses assembleLyrics to do the heavy work.

maxLyrics just determines how many times we should parse through the score, since it is
not easy to determine what the maximum number of lyrics exist in the score.

Here is a demo with one note and five lyrics.

>>> f = corpus.parse('demos/multiple-verses.xml')
>>> text.assembleLyrics(f, 1)
'1. First'
>>> text.assembleLyrics(f, 2)
'2. Second'
>>> l = text.assembleAllLyrics(f)
>>> l
'1. First\n2. Second\n3. Third\n4. Fourth\n5. Fifth'

* Changed in v8: no lyric separator appears at the beginning.
rK   rE   rB   )ranger_   )rW   	maxLyricslyricSeparationrC   rP   r1   lyrs          r^   assembleAllLyricsre      sJ    . F1i XF"91u)MF ! M    c                H   SU ;  a  U $ Uc  / n[          H  nU[         U   -  nM     O	[         U   nU R                  S5      S   R                  5       nSnU H  nUR                  5       U:X  d  M  Un  O   Ub)  US-   SR	                  U R                  S5      SS 5      -   $ U $ )a  
Given a text string, if an article is found in a trailing position with a comma,
place the article in front and remove the comma.

>>> text.prependArticle('Ale is Dear, The')
'The Ale is Dear'
>>> text.prependArticle('Ale is Dear, The', 'en')
'The Ale is Dear'
>>> text.prependArticle('Ale is Dear, The', 'it')
'Ale is Dear, The'
>>> text.prependArticle('Combattimento di Tancredi e Clorinda, Il', 'it')
'Il Combattimento di Tancredi e Clorinda'
,NrM   rA   articleReferencesplitstriplowerrT   )srclanguagerefkeytrailingmatch	candidates          r^   prependArticleru      s     #~
#C#C((C $ x(yy~b!'')HE	>>y(E  s{SXXciinSb&9:::
rf   c                H   SU ;  a  U $ Uc  / n[          H  nU[         U   -  nM     O	[         U   nU R                  S5      S   R                  5       nSnU H  nUR                  5       U:X  d  M  Un  O   Ub)  SR	                  U R                  S5      SS 5      SU 3-   $ U $ )a  
Given a text string, if an article is found in a leading position,
place it at the end with a comma.


>>> text.postpendArticle('The Ale is Dear')
'Ale is Dear, The'
>>> text.postpendArticle('The Ale is Dear', 'en')
'Ale is Dear, The'
>>> text.postpendArticle('The Ale is Dear', 'it')
'The Ale is Dear'
>>> text.postpendArticle('Il Combattimento di Tancredi e Clorinda', 'it')
'Combattimento di Tancredi e Clorinda, Il'
rA   Nr   rE   z, ri   )rn   ro   rp   rq   leadingrs   rt   s          r^   postpendArticlerx      s      #~
#C#C((C $ x(iinQ%%'GE	==?i'E  xx		#qr*+5'l::
rf   c                      \ rS rSrSrg)TextException    N)__name__
__module____qualname____firstlineno____static_attributes__r|   rf   r^   rz   rz      s    rf   rz   c                     ^  \ rS rSrSr\R                  rSrSU 4S jjr	S r
\S 5       r\R                  S 5       r\S 5       r\R                  S	 5       rS
rU =r$ )TextBox   a<  
A TextBox is arbitrary text that might be positioned anywhere on a page,
independent of notes or staffs. A page attribute specifies what page this text is found on;
style.absoluteY and style.absoluteX position the text from the bottom left corner in
units of tenths.

This object is similar to the TextExpression object, but does not have as many position
parameters, enclosure attributes, and the ability to convert to
RepeatExpressions and TempoTexts.

>>> y = 1000  # set a fixed vertical distance
>>> s = stream.Stream()

Specify character, x position, y position

>>> tb = text.TextBox('m', 250, y)
>>> tb.style.fontSize = 40
>>> tb.style.alignVertical = 'bottom'
>>> s.append(tb)

>>> tb = text.TextBox('u', 300, y)
>>> tb.style.fontSize = 60
>>> tb.style.alignVertical = 'bottom'
>>> s.append(tb)

>>> tb = text.TextBox('s', 550, y)
>>> tb.style.fontSize = 120
>>> tb.style.alignVertical = 'bottom'
>>> s.append(tb)

>>> tb = text.TextBox('ic', 700, y)
>>> tb.style.alignVertical = 'bottom'
>>> tb.style.fontSize = 20
>>> tb.style.fontStyle = 'italic'
>>> s.append(tb)

>>> tb = text.TextBox('21', 850, y)
>>> tb.style.alignVertical = 'bottom'
>>> tb.style.fontSize = 80
>>> tb.style.fontWeight = 'bold'
>>> tb.style.fontStyle = 'italic'
>>> s.append(tb)

>>> #_DOCS_SHOW s.show()

.. image:: images/textBoxes-01.*
    :width: 600

ic                   > [         TU ]  " S0 UD6  S U l        Xl        SU l        X R
                  l        X0R
                  l        SU R
                  l        SU R
                  l	        g )NrE   topcenterr|   )
super__init___contentcontent_pager   	absoluteX	absoluteYalignVerticalalignHorizontal)selfr   xykeywords	__class__s        r^   r   TextBox.__init__-  sT    $8$ 
 

 

#(

 %-

"rf   c                    U R                   b4  [        U R                   5      S:  a  [        U R                   S S S-   5      $ U R                   b  [        U R                   5      $ g)N
   z...rK   )r   lenreprr   s    r^   _reprInternalTextBox._reprInternal<  sS    ==$T]]);b)@cr*U233]]&&&rf   c                    U R                   $ )z
Get or set the content.

>>> te = text.TextBox('Con fuoco')
>>> te.content
'Con fuoco'
>>> te.style.justify = 'center'
>>> te.style.justify
'center'
)r   r   s    r^   r   TextBox.contentE  s     }}rf   c                \    [        U[        5      (       d  [        U5      U l        g Xl        g N)
isinstancestrr   r   values     r^   r   r   S  s    %%%JDM!Mrf   c                    U R                   $ )z
Get or set the page number. The first page (page 1) is the default.

>>> te = text.TextBox('Great Score')
>>> te.content
'Great Score'
>>> te.page
1
>>> te.page = 2
>>> te.page
2
)r   r   s    r^   pageTextBox.pageZ  s     zzrf   c                ,    Ub  [        U5      U l        g g r   )intr   r   s     r^   r   r   j  s    UDJ rf   )r   r   r   )N  r   )r}   r~   r   r   __doc__r   	TextStyle_styleClassclassSortOrderr   r   propertyr   setterr   r   __classcell__)r   s   @r^   r   r      s{    0b //KN.   ^^" "   
[[$ $rf   r   zdict[str, Trigram]_stored_trigramsc                  b    \ rS rSrSr/ SQrSSSSSS	S
S.r\S 5       rSS jr	SS jr
SS jrSrg)LanguageDetectoriu  a;  
Attempts to detect language on the basis of trigrams.

>>> ld = text.LanguageDetector()
>>> ld.mostLikelyLanguage('Guten Morgen Frau Wieck. Ich bin Robert.')
'de'

Note that accuracy improves with longer texts, and that the trigrams are
case sensitive.  Putting "Morgen" in lowercase evaluates the text as Dutch.

Supported languages are currently:

>>> text.LanguageDetector.languageLong
{'en': 'English',
 'fr': 'French',
 'it': 'Italian',
 'de': 'German',
 'cn': 'Chinese',
 'la': 'Latin',
 'nl': 'Dutch'}

See also, documentation for :class:`~music21.text.Trigram`.
)r;   r?   r@   r   cnr   r<   EnglishFrenchItalianGermanChineseLatinDutchc                F   U R                    Hl  n[        R                  " 5       S-  S-  US-   -  nUR                  SS9 nUR	                  5       R                  5       n[        U5      [        U'   SSS5        Mn     [        R                  5       $ ! , (       d  f       M  = f)zb
Read the stored trigrams once and store them for later.

Called on first LanguageDectector read.
languageExcerptstrainingDataz.txtzutf-8)encodingN)	languageCodesr   getSourceFilePathopenreadrk   Trigramr   copy)clslanguageCodethisExcerptfexcerptWordss        r^   readExcerptsLanguageDetector.readExcerpts  s      --L!3358JJ,-0<v0EGK !!7!3q vvx~~/181F . 43	 .  $$&& 43s    1B
B 	Nc                x    Xl         [        R                  5       =(       d    [        R	                  5       U l        g r   )r	   r   r   r   r   trigrams)r   r	   s     r^   r   LanguageDetector.__init__  s&    	(--/R3C3P3P3Rrf   c                    U(       d  g[        U5      nSnSnU R                   H   nU R                  U   U-
  nXd:  d  M  UnUnM"     U$ )a  
Returns the code of the most likely language for a passage, works on
unicode or ascii. Current languages are:

>>> text.LanguageDetector.languageCodes
['en', 'fr', 'it', 'de', 'cn', 'la', 'nl']

or None if no language is detected.

>>> ld = text.LanguageDetector()
>>> ld.mostLikelyLanguage('Hello there, how are you doing today? '
...                       + "I haven't seen you in a while.")
'en'
>>> ld.mostLikelyLanguage('Ciao come stai? Sono molto lento oggi, ma non so perche.')
'it'
>>> ld.mostLikelyLanguage('Credo in unum deum. Patrem omnipotentem. Factorum celi')
'la'

>>> ld = text.LanguageDetector()
>>> ld.mostLikelyLanguage('') is None
True
NrK         ?)r   r   r   )r   excerpt
excTrigrammaxLangmaxDifferencelanglangDiffs          r^   mostLikelyLanguage#LanguageDetector.mostLikelyLanguage  sX    0 W%
&&D}}T*Z7H' (	 ' rf   c                    Ub  US:X  a  gU R                  U5      n[        [        U R                  5      5       H  nU R                  U   U:X  d  M  US-   s  $    [	        S5      e)aF  
returns a number representing the most likely language for a passage
or 0 if there is no text.

Useful for feature extraction.

The codes are the index of the language name in LanguageDetector.languageCodes + 1

>>> ld = text.LanguageDetector()
>>> for index in range(len(ld.languageCodes)):
...    print(str(index + 1) + ' ' +  ld.languageCodes[index])
1 en
2 fr
3 it
4 de
5 cn
6 la
7 nl
>>> numLang = ld.mostLikelyLanguageNumeric('Hello there, how are you doing today? '
...                + "I haven't seen you in a while.")
>>> numLang
1
>>> ld.languageCodes[numLang - 1]
'en'
rK   r   rE   z;got a language that was not in the codes; should not happen)r   ra   r   r   rz   )r   r   langCoder1   s       r^   mostLikelyLanguageNumeric*LanguageDetector.mostLikelyLanguageNumeric  sf    4 ?gm..w7H3t1123%%a(H4q5L 4   ]^^rf   )r	   r   r   )r   r   returnz
str | None)r}   r~   r   r   r   r   languageLongclassmethodr   r   r   r   r   r|   rf   r^   r   r   u  sP    8 ?ML ' ' S#L!_rf   r   c                  ^    \ rS rSrSrSS jr\S 5       rS rS r	SS jr
SS	 jrS
 rS rSrg)r   i  a  
See LanguageDetector above.
From https://code.activestate.com/recipes/326576-language-detection-using-character-trigrams/

The frequency of three character
sequences is calculated.  When treated as a vector, this information
can be compared to other trigrams, and the difference between them
seen as an angle.  The cosine of this angle varies between 1 for
complete similarity, and 0 for utter difference.  Since letter
combinations are characteristic to a language, this can be used to
determine the language of a body of text. For example:

>>> #_DOCS_SHOW reference_en = Trigram('/path/to/reference/text/english')
>>> #_DOCS_SHOW reference_de = Trigram('/path/to/reference/text/german')
>>> #_DOCS_SHOW unknown = Trigram('url://pointing/to/unknown/text')
>>> #_DOCS_SHOW unknown.similarity(reference_de)
#_DOCS_SHOW 0.4
>>> #_DOCS_SHOW unknown.similarity(reference_en)
#_DOCS_SHOW 0.95

would indicate the unknown text is almost certainly English.  As
syntax sugar, the minus sign is overloaded to return the difference
between texts, so the above objects would give you:

#_DOCS_SHOW >>> unknown - reference_de
#_DOCS_SHOW 0.6
#_DOCS_SHOW >>> reference_en - unknown    # order doesn't matter.
#_DOCS_SHOW 0.05

As it stands, the Trigram ignores character set information, which
means you can only accurately compare within a single encoding
(iso-8859-1 in the examples).  A more complete implementation might
convert to unicode first.

As an extra bonus, there is a method to make up nonsense words in the
style of the Trigram's text.

>>> #_DOCS_SHOW reference_en.makeWords(30)
My withillonquiver and ald, by now wittlectionsurper, may sequia,
tory, I ad my notter. Marriusbabilly She lady for rachalle spen hat knong al elf
Nc                J    0 U l         S U l        Ub  U R                  U5        g g r   )lut_lengthparseExcerpt)r   excerptLists     r^   r   Trigram.__init__)  s)    "k* #rf   c                T    U R                   c  U R                  5       $ U R                   $ r   )r   measurer   s    r^   lengthTrigram.length/  s"    <<<<>!<<rf   c                   Sn[        U[        5      (       a_  U HX  nUR                  5       S-    H>  nU R                  R	                  U0 5      nUR                  US5      S-   XT'   US   U-   nM@     MZ     ODU H>  nU R                  R	                  U0 5      nUR                  US5      S-   XT'   US   U-   nM@     U R                  5         g )N  rA   r   rE   )r   listrl   r   
setdefaultgetr   )r   r   pairlineletterds         r^   r   Trigram.parseExcerpt6  s    gt$$"jjlS0F++D"5A !fa 01 4AI7V+D 1   "HH''b1EE&!,q0	Aw' " 	rf   c           	         SnU R                   R                  5        H2  nU[        UR                  5        Vs/ s H  o3U-  PM	     sn5      -  nM4     US-  nX@l        gs  snf )zR
calculates the scalar length of the trigram vector and
stores it in self.length.
r         ?N)r   valuessumr   )r   totalr   r   
thisLengths        r^   r   Trigram.measureE  sW    
 "AS4Aa%455E #c\
! 5s   Ac                &   [        U[        5      (       d  [        S5      eU R                  nUR                  nSnU H.  nXS;   d  M
  X%   nX5   nU H  nX;   d  M
  XFU   Xx   -  -  nM     M0     [	        X@R
                  UR
                  -  -  5      $ )z
returns a number between 0 and 1 indicating similarity between
two trigrams.

1.0 means an identical ratio of trigrams;
0.0 means no trigrams in common.
z&can't compare Trigram with non-Trigramr   )r   r   	TypeErrorr   floatr   )	r   otherlut1lut2r   kr   br   s	            r^   
similarityTrigram.similarityP  s     %))DEExxyyAyGGAv1, 	  UkkELL89::rf   c                *    SU R                  U5      -
  $ )za
indicates difference between trigram sets; 1.0 is entirely
different, 0.0 is entirely the same.
r   )r  )r   r  s     r^   __sub__Trigram.__sub__j  s    
 T__U+++rf   c                    / nSnU(       a>  U R                  U5      nUR                  U5        US   U-   nUS;   a  US-  nU(       a  M>  SR                  U5      $ )z<
returns a string of made-up words based on the known text.
r   rE   z 	rK   )likelyrS   rT   )r   countr	   r  r\   s        r^   	makeWordsTrigram.makeWordsq  s]     AAKKN!qAEz
 e wwt}rf   c                    XR                   ;  a  g/ nU R                   U   R                  5        H  u  p4UR                  X4-  5        M     SR                  U5      n[        R
                  " U5      $ )zm
Returns a character likely to follow the given string
two character string, or a space if nothing is found.
rA   rK   )r   itemsrS   rT   randomchoice)r   r  lettersletKvs        r^   r  Trigram.likely  s^    
 HHxx{((*GDNN48$ +'''"}}W%%rf   )r   r   r   )r  r   r   r  )r}   r~   r   r   r   r   r   r   r   r   r  r
  r  r  r   r|   rf   r^   r   r     s?    (T+    	";4,&rf   r   c                  &    \ rS rSrS rS rS rSrg)Testi  c                ,   SSK Jn  SSK Jn  UR                  UR	                  S5      5      n[        U5      nU R                  US5        UR                  UR	                  S5      5      n[        U5      nU R                  UR                  S5      5        g )Nr   )	converter)corpuszhaydn/opus1no1/movement4.xmlrK   zluca/gloriaz)Et in terra pax hominibus bone voluntatis)	music21r  r  parsegetWorkr_   assertEqual
assertTrue
startswith)r   r  r  r   posts        r^   	testBasicTest.testBasic  sq    %"OOFNN+IJKa r"OOFNN=9:a (STUrf   c                H   SSK Jn  SSK Jn  UR                  5       nS H*  nUR	                  5       nXEl        UR                  U5        M,     [        U5      nU R                  US5        UR                  5       nS H*  nUR	                  5       nXEl        UR                  U5        M,     [        U5      nU R                  US5        UR                  5       nS H*  nUR	                  5       nXEl        UR                  U5        M,     [        US	S
9nU R                  US5        g )Nr   )stream)note)zhel-z-loa-z-gainzhello again)r*  z-ris-z-to-z-catsaregreatzaristocats are great)u   长u   亭u   外u   古u   道u   边rK   rB   u   长亭外古道边)	r  r(  r)  StreamNotelyricrS   r_   r!  )r   r(  r)  ssylr\   r$  s          r^   testAssembleLyricsATest.testAssembleLyricsA  s    " MMO1C		AGHHQK 2 a }-MMOCC		AGHHQK D a 56MMO=C		AGHHQK > ar234rf   c                &   [        5       nUR                  S   UR                  S   -
  nU R                  SUs=:  =(       a    S:  Os  5        U R                  SUR                  S   UR                  S   -
  s=:  =(       a    S:  Os  5        U R                  SUR                  S   UR                  S	   -
  s=:  =(       a    S
:  Os  5        U R                  SUR	                  S5      5        U R                  SUR	                  S5      5        g )Nr?   r@   r   g?gq=
ףp?r   gffffff?gGz?r   r   r;   zhhello friends, this is a test of the ability of language detector to tell what language I am writing in.ux   ciao amici! cosé trovo in quale lingua ho scritto questo passaggio. Spero che troverà che é stata scritta in italiano)r   r   r"  r!  r   )r   lddiffFrIts      r^   testLanguageDetectorTest.testLanguageDetector  s    ;;t$r{{4'88x..$./r{{402;;t3DDKKtKLr{{402;;t3DDJJsJK.. 0W X	Y 	r44: ; 	<rf   r|   N)r}   r~   r   r   r%  r2  r7  r   r|   rf   r^   r  r    s    
V5<<rf   r  __main__)rE   )r   
r   )r   
__future__r   r  unittestr  r   r   r   r   r   EnvironmentenvironLocalrj   r_   re   ru   rx   Music21Exceptionrz   Music21Objectr   r   __annotations__r   r   TestCaser  
_DOC_ORDERr}   mainTestr|   rf   r^   <module>rE     s   #        &&v. '

^
$
A
<
M=! ,F%C F%RUX F#L$P	L11 	
u$d   u$t (* $ )D_ D_PO& O&f:<8 :<L Y
 zT rf   