#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2022 , Nils Hilbricht , Germany ( https : / / www . hilbricht . net )
This file is part of the Laborejo Software Suite ( https : / / www . laborejo . org ) ,
This application is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : / / www . gnu . org / licenses / > .
"""
"""
This file handles various pitches and their conversions .
"""
import logging ; logger = logging . getLogger ( __name__ ) ; logger . info ( " import " )
#Standard Library
from collections import defaultdict
from typing import Dict , Tuple , List , DefaultDict
#Third Party Modules
#Template Modules
#Our modules
class KeySignature ( object ) :
""" For tests and typechecking. The real one is in Laborejo/engine/items.py """
def __init__ ( self , root , deviationFromMajorScale ) :
self . root : int = root
self . deviationFromMajorScale : List = deviationFromMajorScale
self . keysigList : tuple = tuple ( )
#Constants
OCTAVE = 350
STEP = 50
MAX = 3140
MIN = 0
#Without a keysignature
def plain ( pitch : int ) - > int :
""" Extract the note from a note-number, without any octave but with the tailing zero.
This means we double - use the lowest octave as abstract version . """
#Dividing through the octave, 350, results in the number of the octave and the note as remainder.
return divmod ( pitch , 350 ) [ 1 ]
def octave ( pitch : int ) - > int :
""" Return the octave of given note. Lowest 0 is X,,, """
return divmod ( pitch , 350 ) [ 0 ]
def toOctave ( pitch : int , octave : int ) - > int :
""" Take a plain note and give the octave variant. Starts with 0 """
return pitch + octave * 350
def mirror ( pitch : int , axis : int ) - > int :
""" Calculate the distance between the pitch and the axis-pitch and
set the new pitch twice as far , which creates a mirror effect :
Half the distancen is object - > mirror and then mirror - > object on the
other side again . """
#1420 = c', 1520 = e', 1620 = g'
#1420 to 1520 is a third so the mirror would be a fifth.
#1420 + 2 * (1520 - 1420)
#1420 + 2 * 100
#1420 + 200 = 1620
return pitch + 2 * ( axis - pitch )
def diatonicIndex ( pitch : int ) - > int :
""" Return an int between 0 and 6, resembling the diatonic position
of the given pitch without octave , accidentals . 0 is c """
return divmod ( plain ( toWhite [ pitch ] ) , 50 ) [ 0 ]
def absoluteDiatonicIndex ( pitch : int ) - > int :
""" Like diatonicIndex but works from pitch 20 which gets index 0
middle c is 28
tuning a is 33
these two are indeed 5 steps apart
( not traditional interval steps , real step counting from 1 ) """
return divmod ( toWhite [ pitch ] , 50 ) [ 0 ]
def distanceInDiatonicSteps ( first : int , second : int ) - > int :
""" root is a pitch like 1720. Pitch as well
Returns not a signed int . If the first is lower than the second
you get a negative return value . """
return absoluteDiatonicIndex ( first ) - absoluteDiatonicIndex ( second )
def diatonicIndexToPitch ( index : int , octave : int ) - > int :
""" supports indices from - to +. """
while index < 0 :
index + = 7 #plus one octave. index -1 becomes b
octave - = 1
while index > 6 :
index - = 7 #minus one octave. index 7 becomes c again.
octave + = 1
return toOctave ( index * 50 + 20 , octave ) #0 is cesces, 20 is c
def upStepsFromRoot ( root : int , pitch : int ) - > int :
""" Like diatonicIndex but assumes a different root than C. So it is:
' stepcount upward in white keys from root to pitch ' .
It is always assumed it should go up .
Also it will never report anything over an octave since it
uses diatonicIndex ( ) """
assert root < = pitch
if root == pitch :
return 0
else :
r = diatonicIndex ( root )
p = diatonicIndex ( pitch )
if plain ( root ) > plain ( pitch ) : #we have an octave break g' to d''
p + = 7
return p - r
def fromMidi ( midipitch : int , keysig : KeySignature ) - > int :
""" Convert a midi pitch to internal pitch.
Nearest to pillar of fifth """
if ( midipitch , keysig ) in cache_fromMidi :
return cache_fromMidi [ ( midipitch , keysig ) ]
else :
midioctave , pitch = divmod ( midipitch , 12 )
table = [
20 , #c
60 , #des
70 , #d
110 , #ees
120 , #e
170 , #f
180 , #fis
220 , #g
260 , #aas
270 , #a
310 , #bes
320 , #b / h
]
#Sample note Gis/Aes in D Major should become Gis, which is the same as Fis in C Maj.
midiRoot = toMidi [ keysig . root ] - 12 #in D Major thats 2 (halftone steps away from C)
simpleConverted = toOctave ( table [ pitch ] , midioctave - 1 ) #in D Maj it is still Aes
fromC = halfToneDistanceFromC ( simpleConverted )
soundingDistanceToKeysigRoot = fromC - midiRoot #8 half tone steps - 2 from root = 6 (Tritonus)
#We now need to know how the 6 steps / tritonus look like in the current keysignature/root enviroment. This is told by the table above.
pitchInCMaj = table [ soundingDistanceToKeysigRoot ] #fis
#Interval between keysig root and c.
plainInterval , intervalOctaveOffset = interval ( 20 , keysig . root )
newInterval = ( plainInterval , midioctave - 1 ) #tuplets are immutable
#Transpose it by this interval
pitchInOriginalKey = intervalUp ( pitchInCMaj , newInterval , midiIn = True )
#Back to the correct Octave. This is what we wanted.
#bugfix/workaround. 320 b/h in keysigs <= Bes Major is "ces", but somehow one octave to low. Compensate here.
if pillarOfFifth . index ( keysig . root ) < = 13 and pitch == 11 :
returnValue = pitchInOriginalKey + 350
else :
returnValue = pitchInOriginalKey
cache_fromMidi [ ( midipitch , keysig ) ] = returnValue
return returnValue
def halfToneDistanceFromC ( pitch : int ) - > int :
""" Return the half-tone step distance from C. The " sounding " interval """
return {
#00 : 10, # ceses,,, -> bes
#10 : 11, # ces,,, -> b
00 : - 2 , # ceses,,, -> bes
10 : - 1 , # ces,,, -> b
20 : 0 , # c,,,
30 : 1 , # cis,,,
40 : 2 , # cisis,,, -> d ...
50 : 0 , # deses,,,
60 : 1 , # des,,,
70 : 2 , # d,,,
80 : 3 , # dis,,,
90 : 4 , # disis,,,
100 : 2 , # eeses,,,
110 : 3 , # ees,,,
120 : 4 , # e,,,
130 : 5 , # eis,,,
140 : 6 , # eisis,,,
150 : 3 , # feses,,,
160 : 4 , # fes,,,
170 : 5 , # f,,,
180 : 6 , # fis,,,
190 : 7 , # fisis,,,
200 : 5 , # geses,,,
210 : 6 , # ges,,,
220 : 7 , # g,,,
230 : 8 , # gis,,,
240 : 9 , # gisis,,,
250 : 7 , # aeses,,,
260 : 8 , # aes,,,
270 : 9 , # a,,,
280 : 10 , # ais,,,
290 : 11 , # aisis,,,
300 : 9 , # beses,,,
310 : 10 , # bes,,,
320 : 11 , # b,,,
330 : 12 , # bis,,,
340 : 13 , # bisis,,,
#330 : 0, # bis,,,
#340 : 1, # bisis,,,
} [ plain ( pitch ) ]
def sharpen ( pitch : int ) - > int :
""" Sharpen the pitch until double crossed """
sharper = pitch + 10
if toWhite [ sharper ] == toWhite [ pitch ] : #still the same base note?
return sharper
else :
return pitch #too sharp, do nothing.
def flatten ( pitch : int ) - > int :
""" Flatten the pitch until double flat """
flatter = pitch - 10
if toWhite [ flatter ] == toWhite [ pitch ] : #still the same base note?
return flatter
else :
return pitch #too flat, do nothing.
def interval ( pitch1 : int , pitch2 : int ) - > Tuple [ int , int ] :
""" Return the distance between two pitches as steps in the pillar of fifths.
Intervals are tuplets with two members x = 1 , 0 #fifth in the same octave
x [ 0 ] = interval . Steps in the pillar of fifths .
x [ 1 ] = octave in between . Octave is always > = 0 because an interval has no direction .
Just a base note and the other note , which is higher per definition """
if pitch1 > pitch2 : #bring the notes in right order. We want to calculate from higher to lower
return ( pillarOfFifth . index ( plain ( pitch1 ) ) - pillarOfFifth . index ( plain ( pitch2 ) ) , octave ( pitch1 - pitch2 ) )
else :
return ( pillarOfFifth . index ( plain ( pitch2 ) ) - pillarOfFifth . index ( plain ( pitch1 ) ) , octave ( pitch2 - pitch1 ) )
def intervalUp ( pitch : int , interval : Tuple [ int , int ] , midiIn : bool = False ) - > int :
""" Return a pitch which is _interval_ higher than the given pitch """
octv = octave ( pitch )
indexNumber = pillarOfFifth . index ( plain ( pitch ) )
targetPitch = pillarOfFifth [ indexNumber + interval [ 0 ] ] + octv * 350 #return to the old octave
if not midiIn and targetPitch < pitch : #the new note is lower than where we started. This is wrong. +1 octave!. Reason: it was the break between two octaves.
targetPitch + = 350
targetPitch + = interval [ 1 ] * 350
return targetPitch
def intervalDown ( pitch : int , interval : Tuple [ int , int ] ) - > int :
""" Return a pitch which is _interval_ lower than the given pitch
intervalUp ( 20 , ( - 12 , 1 ) ) #c,,, to deses,,"""
octv = octave ( pitch )
indexNumber = pillarOfFifth . index ( plain ( pitch ) )
targetPitch = pillarOfFifth [ indexNumber - interval [ 0 ] ] + octv * 350 #return to the old octave.
if targetPitch > pitch : #the new note is higher than where we started. This is wrong. -1 octave!. Reason: it was the break between two octaves.
targetPitch - = 350
targetPitch - = interval [ 1 ] * 350
return targetPitch
def intervalAutomatic ( originalPitch : int , rootPitch : int , targetPitch : int ) - > int :
""" Return the original pitch transposed by the interval
between rootPitch and targetPitch """
iv = interval ( rootPitch , targetPitch )
if rootPitch > = targetPitch :
return intervalDown ( originalPitch , iv )
else : #rootPitch < targetPitch
return intervalUp ( originalPitch , iv )
#With a Key Signature
def toScale ( pitch : int , keysig : KeySignature ) - > int :
""" Return a pitch which is the in-scale variant of the given one.
Needs a Key Signature as second parameter """
if ( pitch , keysig ) in cache_toScale :
return cache_toScale [ ( pitch , keysig ) ]
else :
workingcopy = list ( keysig . keysigList )
workingcopy . sort ( ) # sort first.
mod = workingcopy [ tonalDistanceFromC [ pitch ] ] # tonalDistanceFromC has the same syntax as the keysig step/list position. mod becomes the (step, value) tuplet
value = toWhite [ pitch ] + mod [ 1 ]
cache_toScale [ ( pitch , keysig ) ] = value
return value
def diffToKey ( pitch : int , keysig : KeySignature ) - > int :
""" Return if a note is natural, sharp, flat etc.
Same syntax as Key Signature :
- 20 double flat , 0 natural , + 20 d - sharp . """
return pitch - toScale ( pitch , keysig )
#Ordered pitches in fifths.
#To calculate real and correct intervals you need the pillar of fifth with 35 steps for each of the 31 realistic notenames (and 4 unrealistic ones)"""
pillarOfFifth = [
#pillarOfFifth.index(260) -> 11
150 , #feses 0
0 , #ceses 1
200 , #geses 2
50 , #deses 3
250 , #aeses 4
100 , #eeses 5
300 , #beses 6
160 , #fes 7
10 , #ces 8
210 , #ges 9
60 , #des 10
260 , #aes 11
110 , #ees 12
310 , #bes 13
170 , #f 14
20 , #c 15
220 , #g 16
70 , #d 17
270 , #a 18
120 , #e 19
320 , #b 20
180 , #fis 21
30 , #cis 22
230 , #gis 23
80 , #dis 24
280 , #ais 25
130 , #eis 26
330 , #bis 27
190 , #fisis 28
40 , #cisis 29
240 , #gisis 30
90 , #disis 31
290 , #aisis 32
140 , #eisis 33
340 , #bisis 34
]
def midiPitchLimiter ( pitch : int , transpose : int ) - > int :
if pitch + transpose < 0 :
logger . warning ( f " Tranpose lead to a note below midi value 0: { pitch } . Limiting to 0. Please fix manually " )
return 0
elif pitch + transpose > 127 :
logger . warning ( f " Tranpose lead to a note above midi value 127: { pitch } . Limiting to 127. Please fix manually " )
return 127
else :
return pitch + transpose
def midiChannelLimiter ( value : int ) - > int :
""" makes sure that a midi channel is in range 0-15 """
if value > 15 :
logger . warning ( " Midi Channel bigger 15 detected: {} . Limiting to 15. Please fix manually " . format ( value ) )
return 15
elif value < 0 :
logger . warning ( " Midi Channel smaller 0 detected: {} . Limiting to 0. Please fix manually " . format ( value ) )
return 0
else :
return value
#The table to convert internal pitches into lilypond and back.
#0 - A tone humans cannot hear anymore.
#1420 - "Middle" c'
#2130 - Soprano-Singers high C
#3150 - Goes beyond the range of a modern piano
#+inf.0 - A rest
#<10 is reserved for microtones, in the future.
#+10 One accidental up jumps over to the next note after cisis
#+50 One diatonic step, preserve accidentals
#+350 One Octave
ly2pitch = {
" ceses,,, " : 00 ,
" ces,,, " : 10 ,
" c,,, " : 20 ,
" cis,,, " : 30 ,
" cisis,,, " : 40 ,
" deses,,, " : 50 ,
" des,,, " : 60 ,
" d,,, " : 70 ,
" dis,,, " : 80 ,
" disis,,, " : 90 ,
" eeses,,, " : 100 ,
" ees,,, " : 110 ,
" e,,, " : 120 ,
" eis,,, " : 130 ,
" eisis,,, " : 140 ,
" feses,,, " : 150 ,
" fes,,, " : 160 ,
" f,,, " : 170 ,
" fis,,, " : 180 ,
" fisis,,, " : 190 ,
" geses,,, " : 200 ,
" ges,,, " : 210 ,
" g,,, " : 220 ,
" gis,,, " : 230 ,
" gisis,,, " : 240 ,
" aeses,,, " : 250 ,
" aes,,, " : 260 ,
" a,,, " : 270 ,
" ais,,, " : 280 ,
" aisis,,, " : 290 ,
" beses,,, " : 300 ,
" bes,,, " : 310 ,
" b,,, " : 320 ,
" bis,,, " : 330 ,
" bisis,,, " : 340 ,
" ceses,, " : 350 ,
" ces,, " : 360 ,
" c,, " : 370 ,
" cis,, " : 380 ,
" cisis,, " : 390 ,
" deses,, " : 400 ,
" des,, " : 410 ,
" d,, " : 420 ,
" dis,, " : 430 ,
" disis,, " : 440 ,
" eeses,, " : 450 ,
" ees,, " : 460 ,
" e,, " : 470 ,
" eis,, " : 480 ,
" eisis,, " : 490 ,
" feses,, " : 500 ,
" fes,, " : 510 ,
" f,, " : 520 ,
" fis,, " : 530 ,
" fisis,, " : 540 ,
" geses,, " : 550 ,
" ges,, " : 560 ,
" g,, " : 570 ,
" gis,, " : 580 ,
" gisis,, " : 590 ,
" aeses,, " : 600 ,
" aes,, " : 610 ,
" a,, " : 620 ,
" ais,, " : 630 ,
" aisis,, " : 640 ,
" beses,, " : 650 ,
" bes,, " : 660 ,
" b,, " : 670 ,
" bis,, " : 680 ,
" bisis,, " : 690 ,
" ceses, " : 700 ,
" ces, " : 710 ,
" c, " : 720 ,
" cis, " : 730 ,
" cisis, " : 740 ,
" deses, " : 750 ,
" des, " : 760 ,
" d, " : 770 ,
" dis, " : 780 ,
" disis, " : 790 ,
" eeses, " : 800 ,
" ees, " : 810 ,
" e, " : 820 ,
" eis, " : 830 ,
" eisis, " : 840 ,
" feses, " : 850 ,
" fes, " : 860 ,
" f, " : 870 ,
" fis, " : 880 ,
" fisis, " : 890 ,
" geses, " : 900 ,
" ges, " : 910 ,
" g, " : 920 ,
" gis, " : 930 ,
" gisis, " : 940 ,
" aeses, " : 950 ,
" aes, " : 960 ,
" a, " : 970 ,
" ais, " : 980 ,
" aisis, " : 990 ,
" beses, " : 1000 ,
" bes, " : 1010 ,
" b, " : 1020 ,
" bis, " : 1030 ,
" bisis, " : 1040 ,
" ceses " : 1050 ,
" ces " : 1060 ,
" c " : 1070 ,
" cis " : 1080 ,
" cisis " : 1090 ,
" deses " : 1100 ,
" des " : 1110 ,
" d " : 1120 ,
" dis " : 1130 ,
" disis " : 1140 ,
" eeses " : 1150 ,
" ees " : 1160 ,
" e " : 1170 ,
" eis " : 1180 ,
" eisis " : 1190 ,
" feses " : 1200 ,
" fes " : 1210 ,
" f " : 1220 ,
" fis " : 1230 ,
" fisis " : 1240 ,
" geses " : 1250 ,
" ges " : 1260 ,
" g " : 1270 ,
" gis " : 1280 ,
" gisis " : 1290 ,
" aeses " : 1300 ,
" aes " : 1310 ,
" a " : 1320 ,
" ais " : 1330 ,
" aisis " : 1340 ,
" beses " : 1350 ,
" bes " : 1360 ,
" b " : 1370 ,
" bis " : 1380 ,
" bisis " : 1390 ,
" ceses ' " : 1400 ,
" ces ' " : 1410 ,
" c ' " : 1420 ,
" cis ' " : 1430 ,
" cisis ' " : 1440 ,
" deses ' " : 1450 ,
" des ' " : 1460 ,
" d ' " : 1470 ,
" dis ' " : 1480 ,
" disis ' " : 1490 ,
" eeses ' " : 1500 ,
" ees ' " : 1510 ,
" e ' " : 1520 ,
" eis ' " : 1530 ,
" eisis ' " : 1540 ,
" feses ' " : 1550 ,
" fes ' " : 1560 ,
" f ' " : 1570 ,
" fis ' " : 1580 ,
" fisis ' " : 1590 ,
" geses ' " : 1600 ,
" ges ' " : 1610 ,
" g ' " : 1620 ,
" gis ' " : 1630 ,
" gisis ' " : 1640 ,
" aeses ' " : 1650 ,
" aes ' " : 1660 ,
" a ' " : 1670 ,
" ais ' " : 1680 ,
" aisis ' " : 1690 ,
" beses ' " : 1700 ,
" bes ' " : 1710 ,
" b ' " : 1720 ,
" bis ' " : 1730 ,
" bisis ' " : 1740 ,
" ceses ' ' " : 1750 ,
" ces ' ' " : 1760 ,
" c ' ' " : 1770 ,
" cis ' ' " : 1780 ,
" cisis ' ' " : 1790 ,
" deses ' ' " : 1800 ,
" des ' ' " : 1810 ,
" d ' ' " : 1820 ,
" dis ' ' " : 1830 ,
" disis ' ' " : 1840 ,
" eeses ' ' " : 1850 ,
" ees ' ' " : 1860 ,
" e ' ' " : 1870 ,
" eis ' ' " : 1880 ,
" eisis ' ' " : 1890 ,
" feses ' ' " : 1900 ,
" fes ' ' " : 1910 ,
" f ' ' " : 1920 ,
" fis ' ' " : 1930 ,
" fisis ' ' " : 1940 ,
" geses ' ' " : 1950 ,
" ges ' ' " : 1960 ,
" g ' ' " : 1970 ,
" gis ' ' " : 1980 ,
" gisis ' ' " : 1990 ,
" aeses ' ' " : 2000 ,
" aes ' ' " : 2010 ,
" a ' ' " : 2020 ,
" ais ' ' " : 2030 ,
" aisis ' ' " : 2040 ,
" beses ' ' " : 2050 ,
" bes ' ' " : 2060 ,
" b ' ' " : 2070 ,
" bis ' ' " : 2080 ,
" bisis ' ' " : 2090 ,
" ceses ' ' ' " : 2100 ,
" ces ' ' ' " : 2110 ,
" c ' ' ' " : 2120 ,
" cis ' ' ' " : 2130 ,
" cisis ' ' ' " : 2140 ,
" deses ' ' ' " : 2150 ,
" des ' ' ' " : 2160 ,
" d ' ' ' " : 2170 ,
" dis ' ' ' " : 2180 ,
" disis ' ' ' " : 2190 ,
" eeses ' ' ' " : 2200 ,
" ees ' ' ' " : 2210 ,
" e ' ' ' " : 2220 ,
" eis ' ' ' " : 2230 ,
" eisis ' ' ' " : 2240 ,
" feses ' ' ' " : 2250 ,
" fes ' ' ' " : 2260 ,
" f ' ' ' " : 2270 ,
" fis ' ' ' " : 2280 ,
" fisis ' ' ' " : 2290 ,
" geses ' ' ' " : 2300 ,
" ges ' ' ' " : 2310 ,
" g ' ' ' " : 2320 ,
" gis ' ' ' " : 2330 ,
" gisis ' ' ' " : 2340 ,
" aeses ' ' ' " : 2350 ,
" aes ' ' ' " : 2360 ,
" a ' ' ' " : 2370 ,
" ais ' ' ' " : 2380 ,
" aisis ' ' ' " : 2390 ,
" beses ' ' ' " : 2400 ,
" bes ' ' ' " : 2410 ,
" b ' ' ' " : 2420 ,
" bis ' ' ' " : 2430 ,
" bisis ' ' ' " : 2440 ,
" ceses ' ' ' ' " : 2450 ,
" ces ' ' ' ' " : 2460 ,
" c ' ' ' ' " : 2470 ,
" cis ' ' ' ' " : 2480 ,
" cisis ' ' ' ' " : 2490 ,
" deses ' ' ' ' " : 2500 ,
" des ' ' ' ' " : 2510 ,
" d ' ' ' ' " : 2520 ,
" dis ' ' ' ' " : 2530 ,
" disis ' ' ' ' " : 2540 ,
" eeses ' ' ' ' " : 2550 ,
" ees ' ' ' ' " : 2560 ,
" e ' ' ' ' " : 2570 ,
" eis ' ' ' ' " : 2580 ,
" eisis ' ' ' ' " : 2590 ,
" feses ' ' ' ' " : 2600 ,
" fes ' ' ' ' " : 2610 ,
" f ' ' ' ' " : 2620 ,
" fis ' ' ' ' " : 2630 ,
" fisis ' ' ' ' " : 2640 ,
" geses ' ' ' ' " : 2650 ,
" ges ' ' ' ' " : 2660 ,
" g ' ' ' ' " : 2670 ,
" gis ' ' ' ' " : 2680 ,
" gisis ' ' ' ' " : 2690 ,
" aeses ' ' ' ' " : 2700 ,
" aes ' ' ' ' " : 2710 ,
" a ' ' ' ' " : 2720 ,
" ais ' ' ' ' " : 2730 ,
" aisis ' ' ' ' " : 2740 ,
" beses ' ' ' ' " : 2750 ,
" bes ' ' ' ' " : 2760 ,
" b ' ' ' ' " : 2770 ,
" bis ' ' ' ' " : 2780 ,
" bisis ' ' ' ' " : 2790 ,
" ceses ' ' ' ' ' " : 2800 ,
" ces ' ' ' ' ' " : 2810 ,
" c ' ' ' ' ' " : 2820 ,
" cis ' ' ' ' ' " : 2830 ,
" cisis ' ' ' ' ' " : 2840 ,
" deses ' ' ' ' ' " : 2850 ,
" des ' ' ' ' ' " : 2860 ,
" d ' ' ' ' ' " : 2870 ,
" dis ' ' ' ' ' " : 2880 ,
" disis ' ' ' ' ' " : 2890 ,
" eeses ' ' ' ' ' " : 2900 ,
" ees ' ' ' ' ' " : 2910 ,
" e ' ' ' ' ' " : 2920 ,
" eis ' ' ' ' ' " : 2930 ,
" eisis ' ' ' ' ' " : 2940 ,
" feses ' ' ' ' ' " : 2950 ,
" fes ' ' ' ' ' " : 2960 ,
" f ' ' ' ' ' " : 2970 ,
" fis ' ' ' ' ' " : 2980 ,
" fisis ' ' ' ' ' " : 2990 ,
" geses ' ' ' ' ' " : 3000 ,
" ges ' ' ' ' ' " : 3010 ,
" g ' ' ' ' ' " : 3020 ,
" gis ' ' ' ' ' " : 3030 ,
" gisis ' ' ' ' ' " : 3040 ,
" aeses ' ' ' ' ' " : 3050 ,
" aes ' ' ' ' ' " : 3060 ,
" a ' ' ' ' ' " : 3070 ,
" ais ' ' ' ' ' " : 3080 ,
" aisis ' ' ' ' ' " : 3090 ,
" beses ' ' ' ' ' " : 3100 ,
" bes ' ' ' ' ' " : 3110 ,
" b ' ' ' ' ' " : 3120 ,
" bis ' ' ' ' ' " : 3130 ,
" bisis ' ' ' ' ' " : 3140 ,
#"r" : float('inf'), a rest is not a pitch
}
sortedNoteNameList = [
" ceses,,, " ,
" ces,,, " ,
" c,,, " ,
" cis,,, " ,
" cisis,,, " ,
" deses,,, " ,
" des,,, " ,
" d,,, " ,
" dis,,, " ,
" disis,,, " ,
" eeses,,, " ,
" ees,,, " ,
" e,,, " ,
" eis,,, " ,
" eisis,,, " ,
" feses,,, " ,
" fes,,, " ,
" f,,, " ,
" fis,,, " ,
" fisis,,, " ,
" geses,,, " ,
" ges,,, " ,
" g,,, " ,
" gis,,, " ,
" gisis,,, " ,
" aeses,,, " ,
" aes,,, " ,
" a,,, " ,
" ais,,, " ,
" aisis,,, " ,
" beses,,, " ,
" bes,,, " ,
" b,,, " ,
" bis,,, " ,
" bisis,,, " ,
" ceses,, " ,
" ces,, " ,
" c,, " ,
" cis,, " ,
" cisis,, " ,
" deses,, " ,
" des,, " ,
" d,, " ,
" dis,, " ,
" disis,, " ,
" eeses,, " ,
" ees,, " ,
" e,, " ,
" eis,, " ,
" eisis,, " ,
" feses,, " ,
" fes,, " ,
" f,, " ,
" fis,, " ,
" fisis,, " ,
" geses,, " ,
" ges,, " ,
" g,, " ,
" gis,, " ,
" gisis,, " ,
" aeses,, " ,
" aes,, " ,
" a,, " ,
" ais,, " ,
" aisis,, " ,
" beses,, " ,
" bes,, " ,
" b,, " ,
" bis,, " ,
" bisis,, " ,
" ceses, " ,
" ces, " ,
" c, " ,
" cis, " ,
" cisis, " ,
" deses, " ,
" des, " ,
" d, " ,
" dis, " ,
" disis, " ,
" eeses, " ,
" ees, " ,
" e, " ,
" eis, " ,
" eisis, " ,
" feses, " ,
" fes, " ,
" f, " ,
" fis, " ,
" fisis, " ,
" geses, " ,
" ges, " ,
" g, " ,
" gis, " ,
" gisis, " ,
" aeses, " ,
" aes, " ,
" a, " ,
" ais, " ,
" aisis, " ,
" beses, " ,
" bes, " ,
" b, " ,
" bis, " ,
" bisis, " ,
" ceses " ,
" ces " ,
" c " ,
" cis " ,
" cisis " ,
" deses " ,
" des " ,
" d " ,
" dis " ,
" disis " ,
" eeses " ,
" ees " ,
" e " ,
" eis " ,
" eisis " ,
" feses " ,
" fes " ,
" f " ,
" fis " ,
" fisis " ,
" geses " ,
" ges " ,
" g " ,
" gis " ,
" gisis " ,
" aeses " ,
" aes " ,
" a " ,
" ais " ,
" aisis " ,
" beses " ,
" bes " ,
" b " ,
" bis " ,
" bisis " ,
" ceses ' " ,
" ces ' " ,
" c ' " ,
" cis ' " ,
" cisis ' " ,
" deses ' " ,
" des ' " ,
" d ' " ,
" dis ' " ,
" disis ' " ,
" eeses ' " ,
" ees ' " ,
" e ' " ,
" eis ' " ,
" eisis ' " ,
" feses ' " ,
" fes ' " ,
" f ' " ,
" fis ' " ,
" fisis ' " ,
" geses ' " ,
" ges ' " ,
" g ' " ,
" gis ' " ,
" gisis ' " ,
" aeses ' " ,
" aes ' " ,
" a ' " ,
" ais ' " ,
" aisis ' " ,
" beses ' " ,
" bes ' " ,
" b ' " ,
" bis ' " ,
" bisis ' " ,
" ceses ' ' " ,
" ces ' ' " ,
" c ' ' " ,
" cis ' ' " ,
" cisis ' ' " ,
" deses ' ' " ,
" des ' ' " ,
" d ' ' " ,
" dis ' ' " ,
" disis ' ' " ,
" eeses ' ' " ,
" ees ' ' " ,
" e ' ' " ,
" eis ' ' " ,
" eisis ' ' " ,
" feses ' ' " ,
" fes ' ' " ,
" f ' ' " ,
" fis ' ' " ,
" fisis ' ' " ,
" geses ' ' " ,
" ges ' ' " ,
" g ' ' " ,
" gis ' ' " ,
" gisis ' ' " ,
" aeses ' ' " ,
" aes ' ' " ,
" a ' ' " ,
" ais ' ' " ,
" aisis ' ' " ,
" beses ' ' " ,
" bes ' ' " ,
" b ' ' " ,
" bis ' ' " ,
" bisis ' ' " ,
" ceses ' ' ' " ,
" ces ' ' ' " ,
" c ' ' ' " ,
" cis ' ' ' " ,
" cisis ' ' ' " ,
" deses ' ' ' " ,
" des ' ' ' " ,
" d ' ' ' " ,
" dis ' ' ' " ,
" disis ' ' ' " ,
" eeses ' ' ' " ,
" ees ' ' ' " ,
" e ' ' ' " ,
" eis ' ' ' " ,
" eisis ' ' ' " ,
" feses ' ' ' " ,
" fes ' ' ' " ,
" f ' ' ' " ,
" fis ' ' ' " ,
" fisis ' ' ' " ,
" geses ' ' ' " ,
" ges ' ' ' " ,
" g ' ' ' " ,
" gis ' ' ' " ,
" gisis ' ' ' " ,
" aeses ' ' ' " ,
" aes ' ' ' " ,
" a ' ' ' " ,
" ais ' ' ' " ,
" aisis ' ' ' " ,
" beses ' ' ' " ,
" bes ' ' ' " ,
" b ' ' ' " ,
" bis ' ' ' " ,
" bisis ' ' ' " ,
" ceses ' ' ' ' " ,
" ces ' ' ' ' " ,
" c ' ' ' ' " ,
" cis ' ' ' ' " ,
" cisis ' ' ' ' " ,
" deses ' ' ' ' " ,
" des ' ' ' ' " ,
" d ' ' ' ' " ,
" dis ' ' ' ' " ,
" disis ' ' ' ' " ,
" eeses ' ' ' ' " ,
" ees ' ' ' ' " ,
" e ' ' ' ' " ,
" eis ' ' ' ' " ,
" eisis ' ' ' ' " ,
" feses ' ' ' ' " ,
" fes ' ' ' ' " ,
" f ' ' ' ' " ,
" fis ' ' ' ' " ,
" fisis ' ' ' ' " ,
" geses ' ' ' ' " ,
" ges ' ' ' ' " ,
" g ' ' ' ' " ,
" gis ' ' ' ' " ,
" gisis ' ' ' ' " ,
" aeses ' ' ' ' " ,
" aes ' ' ' ' " ,
" a ' ' ' ' " ,
" ais ' ' ' ' " ,
" aisis ' ' ' ' " ,
" beses ' ' ' ' " ,
" bes ' ' ' ' " ,
" b ' ' ' ' " ,
" bis ' ' ' ' " ,
" bisis ' ' ' ' " ,
" ceses ' ' ' ' ' " ,
" ces ' ' ' ' ' " ,
" c ' ' ' ' ' " ,
" cis ' ' ' ' ' " ,
" cisis ' ' ' ' ' " ,
" deses ' ' ' ' ' " ,
" des ' ' ' ' ' " ,
" d ' ' ' ' ' " ,
" dis ' ' ' ' ' " ,
" disis ' ' ' ' ' " ,
" eeses ' ' ' ' ' " ,
" ees ' ' ' ' ' " ,
" e ' ' ' ' ' " ,
" eis ' ' ' ' ' " ,
" eisis ' ' ' ' ' " ,
" feses ' ' ' ' ' " ,
" fes ' ' ' ' ' " ,
" f ' ' ' ' ' " ,
" fis ' ' ' ' ' " ,
" fisis ' ' ' ' ' " ,
" geses ' ' ' ' ' " ,
" ges ' ' ' ' ' " ,
" g ' ' ' ' ' " ,
" gis ' ' ' ' ' " ,
" gisis ' ' ' ' ' " ,
" aeses ' ' ' ' ' " ,
" aes ' ' ' ' ' " ,
" a ' ' ' ' ' " ,
" ais ' ' ' ' ' " ,
" aisis ' ' ' ' ' " ,
" beses ' ' ' ' ' " ,
" bes ' ' ' ' ' " ,
" b ' ' ' ' ' " ,
" bis ' ' ' ' ' " ,
" bisis ' ' ' ' ' " ,
]
baseNotesToBaseLyNames = {
20 : " C " ,
70 : " D " ,
120 : " E " ,
170 : " F " ,
220 : " G " ,
270 : " A " ,
320 : " B/H " ,
20 - 10 : " Ces " ,
70 - 10 : " Des " ,
120 - 10 : " Ees " ,
170 - 10 : " Fes " ,
220 - 10 : " Ges " ,
270 - 10 : " Aes " ,
320 - 10 : " Bes " ,
20 - 20 : " Ceses " ,
70 - 20 : " Deses " ,
120 - 20 : " Eeses " ,
170 - 20 : " Feses " ,
220 - 20 : " Geses " ,
270 - 20 : " Aeses " ,
320 - 20 : " Beses " ,
20 + 10 : " Cis " ,
70 + 10 : " Dis " ,
120 + 10 : " Eis " ,
170 + 10 : " Fis " ,
220 + 10 : " Gis " ,
270 + 10 : " Ais " ,
320 + 10 : " Bis " ,
20 + 20 : " Cisis " ,
70 + 20 : " Disis " ,
120 + 20 : " Eisis " ,
170 + 20 : " Fisis " ,
220 + 20 : " Gisis " ,
270 + 20 : " Aisis " ,
320 + 20 : " Bisis " ,
}
baseNotesToAccidentalNames = {
20 : " C " ,
70 : " D " ,
120 : " E " ,
170 : " F " ,
220 : " G " ,
270 : " A " ,
320 : " B♮/H " ,
20 - 10 : " C♭ " ,
70 - 10 : " D♭ " ,
120 - 10 : " E♭ " ,
170 - 10 : " F♭ " ,
220 - 10 : " G♭ " ,
270 - 10 : " A♭ " ,
320 - 10 : " B♭/H♭ " ,
20 - 20 : " C𝄫 " ,
70 - 20 : " D𝄫 " ,
120 - 20 : " E𝄫 " ,
170 - 20 : " F𝄫 " ,
220 - 20 : " G𝄫 " ,
270 - 20 : " A𝄫 " ,
320 - 20 : " B𝄫/H𝄫 " ,
20 + 10 : " C♯ " ,
70 + 10 : " D♯ " ,
120 + 10 : " E♯ " ,
170 + 10 : " F♯ " ,
220 + 10 : " G♯ " ,
270 + 10 : " A♯ " ,
320 + 10 : " B♯/H♯ " ,
20 + 20 : " C𝄪 " ,
70 + 20 : " D𝄪 " ,
120 + 20 : " E𝄪 " ,
170 + 20 : " F𝄪 " ,
220 + 20 : " G𝄪 " ,
270 + 20 : " A𝄪 " ,
320 + 20 : " B𝄪/H𝄪 " ,
}
orderedBaseNotes = [ " C " , " D " , " E " , " F " , " G " , " A " , " H/B " ]
#Basic notes. For example to use as parameter for key-signatures in the API.
#P for Pitch
P_C = 20
P_D = 70
P_E = 120
P_F = 170
P_G = 220
P_B = P_H = 270
#This is a funny historical coincidence. The s was used as sharp-sign before. Which lead to people pronouncing Cs "Cis".
P_Cs = 20 + 10
P_Ds = 70 + 10
P_Es = 120 + 10
P_Fs = 170 + 10
P_Gs = 220 + 10
P_Bs = P_Hs = 270 + 10
P_Cb = 20 - 10
P_Db = 70 - 10
P_Eb = 120 - 10
P_Fb = 170 - 10
P_Gb = 220 - 10
P_Bb = P_Hb = 270 - 10
#Generate some tables and other cached dicts on startup
#These are mostly values that are called multiple thousand times per track for every callback
pitch2ly = dict ( ( ly2pitch [ k ] , k ) for k in ly2pitch )
cache_toScale : Dict [ Tuple [ int , KeySignature ] , int ] = { } #filled dynamically. int is a pitch, object is a keysignature. value is pitch
cache_fromMidi : Dict [ Tuple [ int , KeySignature ] , int ] = { } #filled dynamically midipitch:keysig . type is (midipitch, keysig) : internal pitch
toMidi = { } #filled for all pitches on startup, below
toWhite = { } #filled for all pitches on startup, below
tonalDistanceFromC = { } #filled for all pitches on startup, below
distanceInDiatonicStepsFrom1720 = { } #filled for all pitches on startup, below. Utilized by item.asDotOnLine
for pitch in ly2pitch . values ( ) :
toWhite [ pitch ] = divmod ( pitch , 50 ) [ 0 ] * 50 + 20
octOffset = ( octave ( pitch ) + 1 ) * 12 #twelve tones per midi octave
toMidi [ pitch ] = octOffset + halfToneDistanceFromC ( pitch )
tonalDistanceFromC [ pitch ] = divmod ( plain ( toWhite [ pitch ] ) , 50 ) [ 0 ]
#second round for dependencies on finished pitchmath
for pitch in ly2pitch . values ( ) :
distanceInDiatonicStepsFrom1720 [ pitch ] = distanceInDiatonicSteps ( 1720 , pitch ) #offset from the middle line in treble clef h', which is 0. c'' is -1, a' is +1
#Lists of Notenames. Maps midi to different note systems
#These are simple fixed lists without root notes or key signatures. For a real conversion use pitch.fromMidi(root, keysig)
midi_notenames_english = [ ]
for _midipitch in range ( 128 ) :
_octave , _pitch = divmod ( _midipitch , 12 )
midi_notenames_english . append ( " {} {} " . format ( " C Db D Eb E F F# G Ab A Bb B " . split ( ) [ _pitch ] , _octave - 1 ) )
midi_notenames_german = [ ]
for _midipitch in range ( 128 ) :
_octave , _pitch = divmod ( _midipitch , 12 )
midi_notenames_german . append ( " {} {} " . format ( " C Des D Es E F Fis G As A B H " . split ( ) [ _pitch ] , _octave - 1 ) )
#midi and sfz note names are normal midi notes again, but this time we need a dict because both Eb and D# are in there. c4 is middle.
_alt = {
" db " : " c# " ,
" eb " : " d# " ,
" f# " : " gb " ,
" ab " : " g# " ,
" bb " : " a# " ,
}
midiName2midiPitch = { }
for _midipitch in range ( 128 ) : #0-127 incl.
_octave , _pitch = divmod ( _midipitch , 12 )
_octave - = 1
notename = " c db d eb e f f# g ab a bb b " . split ( ) [ _pitch ]
sfz_name1 = " {} {} " . format ( notename , _octave )
midiName2midiPitch [ sfz_name1 ] = _midipitch
if notename in ( " db " , " eb " , " f# " , " ab " , " bb " ) : #Add the other variant
sfz_name2 = " {} {} " . format ( _alt [ notename ] , _octave )
midiName2midiPitch [ sfz_name2 ] = _midipitch
assert midiName2midiPitch [ " db4 " ] == 61 , midiName2midiPitch [ " db4 " ]
assert midiName2midiPitch [ " db4 " ] == midiName2midiPitch [ " c#4 " ] , midiName2midiPitch [ " c#4 " ]
""" midi_notenames_lilypond = []
for _midipitch in range ( 128 ) :
_octave , _pitch = divmod ( _midipitch , 12 )
midi_notenames_lilypond . append ( " {} {} " . format ( " C Des D Ees E F Fis G Aes A Bes B " . split ( ) [ _pitch ] , _octave - 1 ) )
"""
#60 is C'
t = """
C , , , , Des , , , , D , , , , Ees , , , , E , , , , F , , , , Fis , , , , G , , , , Aes , , , , A , , , , Bes , , , , B , , , ,
C , , , Des , , , D , , , Ees , , , E , , , F , , , Fis , , , G , , , Aes , , , A , , , Bes , , , B , , ,
C , , Des , , D , , Ees , , E , , F , , Fis , , G , , Aes , , A , , Bes , , B , ,
C , Des , D , Ees , E , F , Fis , G , Aes , A , Bes , B ,
C Des D Ees E F Fis G Aes A Bes B
C ' Des ' D ' Ees ' E ' F ' Fis ' G ' Aes ' A ' Bes ' B '
C ' ' Des ' ' D ' ' Ees ' ' E ' ' F ' ' Fis ' ' G ' ' Aes ' ' A ' ' Bes ' ' B ' '
C ''' Des ''' D ''' Ees ''' E ''' F ''' Fis ''' G ''' Aes ''' A ''' Bes ''' B '''
C ''' ' Des ''' ' D ' ''' Ees ''' ' E ' ''' F ''' ' Fis ' ''' G ''' ' Aes ' ''' A ''' ' Bes ' ''' B ''' '
C ''' ' ' Des ''' ' ' D ''' ' ' Ees ''' ' ' E ''' ' ' F ''' ' ' Fis ''' ' ' G ''' ' ' Aes ''' ' ' A ''' ' ' Bes ''' ' ' B ''' ' '
C ''' ''' Des ''' ''' D ''' ''' Ees ''' ''' E ''' ''' F ''' ''' Fis ''' ''' G ''' '''
"""
midi_notenames_lilypond = t . split ( )
midi_notenames_gm_drums = [ str ( i ) for i in range ( 0 , 35 ) ] + [
" Acoustic BD " ,
" Bass Drum 1 " ,
" Side Stick " ,
" Acoustic Snare " ,
" Hand Clap " ,
" Electric Snare " ,
" Low Floor Tom " ,
" Closed Hi Hat " ,
" High Floor Tom " ,
" Pedal Hi-Hat " ,
" Low Tom " ,
" Open Hi-Hat " ,
" Low-Mid Tom " ,
" Hi-Mid Tom " ,
" Crash Cymbal 1 " ,
" High Tom " ,
" Ride Cymbal 1 " ,
" Chinese Cymbal " ,
" Ride Bell " ,
" Tambourine " ,
" Splash Cymbal " ,
" Cowbell " ,
" Crash Cymbal 2 " ,
" Vibraslap " ,
" Ride Cymbal 2 " ,
" Hi Bongo " ,
" Low Bongo " ,
" Mute Hi Conga " ,
" Open Hi Conga " ,
" Low Conga " ,
" High Timbale " ,
" Low Timbale " ,
" High Agogo " ,
" Low Agogo " ,
" Cabasa " ,
" Maracas " ,
" Short Whistle " ,
" Long Whistle " ,
" Short Guiro " ,
" Long Guiro " ,
" Claves " ,
" Hi Wood Block " ,
" Low Wood Block " ,
" Mute Cuica " ,
" Open Cuica " ,
" Mute Triangle " ,
" Open Triangle " ,
] + [ str ( i ) for i in range ( 87 , 128 ) ]
def _defaultSimpleNoteNames ( ) - > list :
return midi_notenames_english
simpleNoteNames : DefaultDict [ str , list ] = defaultdict ( _defaultSimpleNoteNames )
simpleNoteNames [ " German " ] = midi_notenames_german
simpleNoteNames [ " English " ] = midi_notenames_english
simpleNoteNames [ " Lilypond " ] = midi_notenames_lilypond
simpleNoteNames [ " Drums GM " ] = midi_notenames_gm_drums