You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1202 lines
30 KiB
1202 lines
30 KiB
#! /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'''''" ,
|
|
]
|
|
|
|
baseNotesToBaseNames = {
|
|
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/Bb",
|
|
|
|
20+10 : "Cis",
|
|
70+10 : "Dis",
|
|
120+10 : "Eis",
|
|
170+10 : "Fis",
|
|
220+10 : "Gis",
|
|
270+10 : "Ais",
|
|
320+10 : "Bis/His",
|
|
}
|
|
|
|
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
|
|
|