
    rhC                       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KJr  SSKJ	r	  0 S	S
_SS_SS_SS_SS_SS_SS_SS_SS_SS_SS_SS_SS _S!S"_S#S$_S%S&_S'S(_0 S)S*_S+S,_S-S._S/S0_S1S2_S3S_S4S5_S6S7_S8S9_S:S;_S<S=_S>S_S?S@_SASB_SCSD_SESF_SGSH_ESSISJSKSLSMSN.Er
0 S
S	_SS_SS_SS_SS_SS_SS_SS_SS_SS_S S_S"S!_S$S#_S&S%_S(S'_S*S)_S,S+_0 S.S-_S0S/_S2S1_S5S4_S7S6_S9S8_S;S:_S=S<_S@S?_SBSA_SDSC_SFSE_SHSG_SISO_SJSP_SKSQ_SLSR_ESMSS0Er0 SST_S	SU_SSV_SSW_SSX_SSY_SSZ_SS[_SS\_SS]_S%S^_S'S__S)S`_S1Sa_S3Sb_S4Sc_S:Sd_SeSfSgShSiSjSkSlSmSn.	ErSo rSp rSq rSr rSs rSSt jrSSu jrSSv jrSSw jr " Sx Sy\R,                  5      r " Sz S{5      r " S| S}\R2                  5      r\/r\S~:X  a  SSKr\R:                  " \5        gg)zx
Implementation of Walter Hewlett's base40 system for musical intervals.
(See Hewlett and Ann K. Blombach 1989 article)
    )annotationsN)common)exceptions21)interval)note)pitch   zC--   zC-   C   zC#   zC##      zD--   zD-	   D
   zD#   zD##      zE--   zE-   E   zE#   zE##   zF--   zF-   F   zF#   zF##      zG--   zG-   G   zG#   zG##      zA--   zA-    A!   zA#"   zA##zB--zB-BzB#zB##)#   $   %   &   '   (   r3   r4   r5   r6   r7   P1A1d2m2M2A2d3m3M3A3d4P4A4d5P5A5d6m6M6A6d7m7M7A7d8P8)	r)   r*   r+   r/   r0   r2   r3   r6   r7   c                   SnU S:  a  Sn[        U 5      S-  n [        U   n[        R                  " U5      n[        U 5      S-  nUR                  n[        R                  " XR                  SU-  -   -  5      nUR                  n[        UR                  5      [        UR                  5      -   n	[        R                  " U	5      $ ! [         a    [        S[        U5      -   5      ef = f)a  
Returns a music21 Interval between two Base40 pitch numbers
given the delta (difference) between them.

Raises a Base40 Exception if the interval is not handled by Base40.
Base40 can only handle major, minor, perfect, augmented,
and diminished intervals. Although not for certain, it seems
that the engineers that designed this system assumed that
other intervals (doubly augmented intervals, for instance)
would be of a very rare occurrence, and extreme intervals
which would trigger an incorrect answer (C-- to C##, for
instance, would return a diminished second, even though it's
a quadruplely augmented unison) just would not occur.


>>> musedata.base40.base40DeltaToInterval(4)
<music21.interval.Interval d2>
>>> musedata.base40.base40DeltaToInterval(11)
<music21.interval.Interval m3>
>>> musedata.base40.base40DeltaToInterval(23)
<music21.interval.Interval P5>
>>> musedata.base40.base40DeltaToInterval(-23)
<music21.interval.Interval P-5>
>>> musedata.base40.base40DeltaToInterval(52)
<music21.interval.Interval M10>
>>> musedata.base40.base40DeltaToInterval(-52)
<music21.interval.Interval M-10>
>>> musedata.base40.base40DeltaToInterval(77)
Traceback (most recent call last):
music21.musedata.base40.Base40Exception: Interval not handled by Base40 37
r	   r   r7   zInterval not handled by Base40 r   )absbase40IntervalTabler   IntervalKeyErrorBase40ExceptionstrgenericGenericIntervalvaluediatonic	specifier)
delta	directionsimpleDeltasimpleIntervalNamesimpleInterval
numOctavessgicgisdinewIntervals
             Q/home/james-whalen/.local/lib/python3.13/site-packages/music21/musedata/base40.pybase40DeltaToIntervalrj      s    B Iqy	e*r/KT0=!**+=> Ur!J

 
 C

"
"9		A
N0J#K
LC

!
!Ccmm$s399~5K[))  T?#kBRRSSTs   C "C*c                    [         R                  " 5       nU S-
  S-  S-   Ul        U SUR                  S-
  -  -
  n[        U   nUb  X1l        U$ [        S[        U 5      -   5      e)a  
Converts a Base40 pitch number into a music21 Pitch.
The Base40 number is octave specific.

Raises a Base40 Exception if the Base40 pitch number given doesn't
have an associated pitch name. There is one unassigned number
each time the interval between two letters is a whole step.


>>> musedata.base40.base40ToPitch(1)
<music21.pitch.Pitch C--1>
>>> musedata.base40.base40ToPitch(40)
<music21.pitch.Pitch B##1>
>>> musedata.base40.base40ToPitch(23)
Traceback (most recent call last):
music21.musedata.base40.Base40Exception: Pitch name not assigned to this Base40 number 23
>>> musedata.base40.base40ToPitch(186)
<music21.pitch.Pitch G5>
r	   r7   .Pitch name not assigned to this Base40 number )r   Pitchoctavebase40EquivalentnamerX   rY   )	base40NumptableNum	pitchNames       ri   base40ToPitchru      sk    ( 	AQ"$)AH2A..H *I
JSQZ^[
\\    c                   [        U [        5      (       a  [        R                  " U 5      n U R                  [
        ;   a*  [
        U R                     nSU R                  S-
  -  U-   nU$ [        SU R                  -   5      e)a*  
Converts a pitch string or a music21 Pitch into a Base40
pitch number. The Base40 number is octave specific.

Raises a Base40 Exception if the pitch to convert is outside the set
of pitches that Base40 can handle; for example, half flats
and half sharps or triple flats and triple sharps.


>>> musedata.base40.pitchToBase40(pitch.Pitch('C--5'))
161
>>> musedata.base40.pitchToBase40('F##4')
142
>>> musedata.base40.pitchToBase40('F###4')
Traceback (most recent call last):
music21.musedata.base40.Base40Exception: Base40 cannot handle this pitch F###4
r7   r	   z Base40 cannot handle this pitch )	
isinstancerY   r   rm   rp   base40Representationrn   rX   nameWithOctave)pitchToConvertrs   rq   s      ri   pitchToBase40r|      sw    $ .#&&^422'(;(;<>00145A	 <~?\?\\
]]rv   c                   [         U S-
  S-  S-      n[         US-
  S-  S-      nX-
  nUc,  Uc)  [        S[        U 5      -   S-   [        U5      -   S-   5      eUc  [        S[        U 5      -   S-   5      eUc  [        S[        U5      -   S-   5      eUS:  a5  US   US   :X  a)  [        S	[        U 5      -   S-   [        U5      -   S
-   5      e[        U5      $ )a  
Returns a music21 Interval between two base40 pitch
numbers, using their delta (difference) as defined
in Base40. The interval provided is without direction.

Raises a Base40 Exception if the delta doesn't correspond
to an interval in Base40, or if either base40 pitch
number doesn't correspond to a pitch name.


>>> musedata.base40.base40Interval(163, 191)
<music21.interval.Interval m6>

>>> musedata.base40.base40Interval(186, 174)      # Descending M3
<music21.interval.Interval M-3>


Base40 has limitations for intervals smaller than diminished or bigger than augmented.

>>> musedata.base40.base40Interval(1, 5)
Traceback (most recent call last):
music21.musedata.base40.Base40Exception: Base40 cannot compute interval between 1 and 5.

>>> musedata.base40.base40Interval(1, 3)
Traceback (most recent call last):
music21.musedata.base40.Base40Exception: Interval not handled by Base40 2

>>> musedata.base40.base40Interval(2, 6)
Traceback (most recent call last):
music21.musedata.base40.Base40Exception: Pitch name not assigned to this Base40 number 6
    Interval does not exist

>>> musedata.base40.base40Interval(12, 6)
Traceback (most recent call last):
music21.musedata.base40.Base40Exception: Pitch name not assigned to these Base40 numbers
    12 and 6 Interval does not exist
r	   r7   z0Pitch name not assigned to these Base40 numbers z and z Interval does not existrl   r   r   z'Base40 cannot compute interval between .)ro   rX   rY   rj   )
base40NumA
base40NumBpitchApitchBr_   s        ri   base40Intervalr     s:   L zA~3a78FzA~3a78F#E~&.P #J029: #J02LM N 	N ~N #J02LM N 	N~N #J02LM N 	NqyVAY&)+G #J029:<?
OLNQR S 	S !''rv   c                    [        U 5      n[        U5      n[        R                  " 5       nX$l        [        R                  " 5       nX5l         [        R
                  " XE5      $ ! [         a    [        S5      ef = f)a  
Calculates a music21 Interval between two Base40 pitch
numbers, as calculated using the music21.interval module.

Raises a Base40 Exception if (a) Either of the Base40 pitch
numbers does not correspond to a pitch name or (b) If
an unusual interval is encountered that can't be handled
by music21.


>>> musedata.base40.base40ActualInterval(163, 191)
<music21.interval.Interval m6>
>>> musedata.base40.base40ActualInterval(186, 174)  # Descending M3
<music21.interval.Interval M-3>
>>> musedata.base40.base40ActualInterval(1, 5)
<music21.interval.Interval AAAA1>
>>> musedata.base40.base40ActualInterval(1, 3)
<music21.interval.Interval AA1>
>>> musedata.base40.base40ActualInterval(2, 6)
Traceback (most recent call last):
music21.musedata.base40.Base40Exception: Pitch name not assigned to this Base40 number 6

OMIT_FROM_DOCS
>>> musedata.base40.base40ActualInterval(12, 6)
Traceback (most recent call last):
music21.musedata.base40.Base40Exception: Pitch name not assigned to this Base40 number 12
z0Unusual interval- Limitation of music21.interval)ru   r   Noter   r   rV   
IndexErrorrX   )r   r   r   r   noteAnoteBs         ri   base40ActualIntervalr   J  sl    8 :&F:&FIIKEKIIKEKR  .. RPQQRs   A$ $A:c                   US:X  a  SnOUS:X  a  SnO[        SU 35      e/ n[        R                  " U 5      (       a  U n[        R	                  US5      n U c  SnO[
        R	                  U S5      nUb  XS-   S-  nUS:X  a  Sn[        R	                  US5      nUS	L a  Ub  [        U5      S
:  a  SnUc  SnO?US   U S   :X  a  SnSnU H  nUS   US   :X  d  M  SnSnM     Ub  UR                  U5        Ub  M  U$ )zP
Helper function for quickHigherEnharmonicString and quickLowerEnharmonicString
upr   downzNot a valid direction, Nr7   r   Fr
   )rX   r   isNumro   getry   lenappend)nameStrr`   allowDoubleAccidentalsaddNumenharmonics	base40num	base40stres           ri   _quickEnharmonicStringr   t  s7    D	f	 7	{CDDK||G	"&&y$7?I(,,Wd;	

'2-	>I$((D9	!U*y/DYZ[I[II|wqz) 	 	 Q<1Q4' $I $I ! $""9-) 
, rv   c                    [        U SUS9$ )a  
Takes a name of a string and returns a list of the quick higher enharmonics, limited
to double sharps and double flats (or to single sharps and single flats if
allowDoubleAccidentals is False)

>>> musedata.base40.quickHigherEnharmonicString('F#')
['G-']
>>> musedata.base40.quickHigherEnharmonicString('C##')
['D', 'E--']
>>> musedata.base40.quickHigherEnharmonicString('C##', allowDoubleAccidentals=False)
['D']
>>> musedata.base40.quickHigherEnharmonicString('B#')
['C', 'D--']
r   r`   r   r   r   r   s     ri   quickHigherEnharmonicStringr     s     "',09OQ Qrv   c                    [        U SUS9$ )a  
Takes a name of a string and returns a list of the quick lower enharmonics, limited
to double sharps and double flats (or to single sharps and single flats if
allowDoubleAccidentals is False)

>>> musedata.base40.quickLowerEnharmonicString('B-')
['A#']
>>> musedata.base40.quickLowerEnharmonicString('G-')
['F#', 'E##']
>>> musedata.base40.quickLowerEnharmonicString('G-', allowDoubleAccidentals=False)
['F#']
>>> musedata.base40.quickLowerEnharmonicString('C-')
['B', 'A##']
r   r   r   r   s     ri   quickLowerEnharmonicStringr     s     "',29OQ Qrv   c                P    [        X5      nUR                  [        X5      5        U$ )a  
Takes a name of a string and returns a list of the quick lower and higher enharmonics, limited
to double sharps and double flats (or to single sharps and single flats if
allowDoubleAccidentals is False)

>>> musedata.base40.quickEnharmonicString('C')
['B#', 'D--']
>>> musedata.base40.quickEnharmonicString('C', allowDoubleAccidentals=False)
['B#']
>>> musedata.base40.quickEnharmonicString('G')
['F##', 'A--']
)r   extendr   )r   r   harmonicStrings      ri   quickEnharmonicStringr     s(     0PN5gVWrv   c                      \ rS rSrSrg)rX   i   N__name__
__module____qualname____firstlineno____static_attributes__r   rv   ri   rX   rX         rv   rX   c                  $    \ rS rSrSS jrS rSrg)BaseNi  c                    Xl         g )Norder)selfr   s     ri   __init__BaseN.__init__  s    
rv   c                F   / nS H  n[        U R                  SS5       H  nUR                  USU-  -   5        M     UR                  U5        [        U R                  5       H  nUR                  USUS-   -  -   5        M     US;  d  M  UR                  S 5        M     U$ )N)r   r   r   r    r'   r.   r1   r   rS   -#r	   )r   r1   )ranger   r   )r   
outLettersletteris       ri   generateLettersBaseN.generateLetters  s    
9F4::q"-!!&37"23 .f%4::&!!&3!a%="89 'Z'!!$' : rv   r   N)r
   )r   r   r   r   r   r   r   r   rv   ri   r   r     s    
rv   r   c                      \ rS rSrSrg)Testi  r   Nr   r   rv   ri   r   r     r   rv   r   __main__)r   T)T)__doc__
__future__r   unittestmusic21r   r   r   r   r   ro   ry   rU   rj   ru   r|   r   r   r   r   r   r   Music21ExceptionrX   r   TestCaser   
_DOC_ORDERr   mainTestr   rv   ri   <module>r      s   #       (Au (t(s( t( u	(
 t( u( t( s( ( ( ( ( ( ( (  !(" #($ %(& '(( )(* +(, -(. /(0 1(2 3(4 5(6 7(8 9(: ;(< =(> ?(@ A(B C(D O( Z(q (a(Q( a( q	( q( a( Q( b( r( r( b( R( b(  r!(" r#($ b%(& R'(( b)(* r+(. r/(0 b1(2 R3(4 b5(6 r7(: r;(< b=(> R?(@ bA(B rC(F rG(H bI(J RK(L bM(N rO( Z!q$ !4! 4! 4	!
 4! 4! D! D! D! D! D! D! D!" D#!$ D%!& D'!* D+!, !        A! H6*r]>^:9(x'RT*ZQ(Q($	l33 	 &	8 	
 ##
 z T	 rv   