major=[0,2,4,5,7,9,11,12]#this if sorted by pitch, lowest to highest. Patroneo works in reverse order to accomodate the row/column approach of a grid. We reverse in setScaleToKeyword
major=[0,2,4,5,7,9,11]#this is sorted by pitch, lowest to highest. Patroneo works in reverse order to accomodate the row/column approach of a grid. We reverse in setScaleToKeyword
schemesDict={
#this if sorted by pitch, lowest to highest. Patroneo works in reverse order to accomodate the row/column approach of a grid. We reverse in setScaleToKeyword
"Major":[0,0,0,0,0,0,0,0],
"Minor":[0,0,-1,0,0,-1,-1,0],
"Dorian":[0,0,-1,0,0,0,-1,0],
"Phrygian":[0,-1,-1,0,0,-1,-1,0],
"Lydian":[0,0,0,+1,0,0,0,0],
"Mixolydian":[0,0,0,0,0,0,-1,0],
"Locrian":[0,-1,-1,0,-1,-1,-1,0],
"Blues":[0,-2,-1,0,-1,-2,-1,0],
"Hollywood":[0,0,0,0,0,-1,-1,0],#The "Hollywood"-Scale. Stargate, Lord of the Rings etc.
"Chromatic":[0,-1,-2,-2,-3,-4,-5,-5],#not a complete octave, but that is how it goes.
#The lowest/first pitch is always 0 because it is just the given root note.
"Major":[0,0,0,0,0,0,0],
"Minor":[0,0,-1,0,0,-1,-1],
"Dorian":[0,0,-1,0,0,0,-1],
"Phrygian":[0,-1,-1,0,0,-1,-1],
"Lydian":[0,0,0,+1,0,0,0],
"Mixolydian":[0,0,0,0,0,0,-1],
"Locrian":[0,-1,-1,0,-1,-1,-1],
"Blues":[0,-2,-1,0,-1,-2,-1],#blues is a special case. It has less notes than we offer. Set a full scale and another script will mute/hide those extra steps.
"Blues":[0,+1,+1,+1,0,+1],
"Hollywood":[0,0,0,0,0,-1,-1],#The "Hollywood"-Scale. Stargate, Lord of the Rings etc.
rememberRootNote=track.pattern.scale[-1]#The last note has a special role by convention. No matter if this is the lowest midi-pitch or not. Most of the time it is the lowest though.
#Create a modified scalePattern for the tracks numberOfSteps.
#We technically only need to worry about creating additional steps. less steps is covered by zip(), see below
majorExt=[]
schemeExt=[]
mrev=list(reversed(major*16))#pad major to the maximum possible notes. We just need the basis to make it possible long schemes like chromatic fit
srev=list(reversed(schemesDict[keyword]*16))
foriinrange(track.pattern.numberOfSteps):
l=len(srev)
octaveOffset=i//l*12#starts with 0*12
majorExt.append(mrev[i%l]+octaveOffset)
schemeExt.append(srev[i%l])#this is always the same. it is only the difference to the major scale
majorExt=list(reversed(majorExt))
schemeExt=list(reversed(schemeExt))
scale=[x+yforx,yinzip(majorExt,schemeExt)]#zip just creates pairs until it reached the end of one of its arguments. This is reversed order.
#scale = [x + y for x, y in zip(major, schemesDict[keyword])] #zip just creates pairs until it reached the end of one of its arguments. This is reversed order.
difference=rememberRootNote-scale[-1]#isn't this the same as rootnote since scale[-1] is always 0? Well, we could have hypo-scales in the future.
result=[midipitch+differenceformidipitchinscale]#create actual midi pitches from the root note and the scale. This is reversed order because "scale" is.
#Here is a hack because chromatic didn't work with octave wrap-around. We want to make sure we don't fall back to a lower octave
NUMBER_OF_STEPS=8#for exchange with the GUI: one octave of range per pattern. This is unlikely to change and then will not work immediately, but better to keep it as a named "constant".
#self.scale = scale if scale else (72+ 12, 71+12, 69+12, 67+12, 65+12, 64+12, 62+12, 60+12, 71, 69, 67, 65, 64, 62, 60) #Scale needs to be set first because on init/load data already depends on it, at least the default scale. The scale is part of the track meta callback.
self.scale=scaleifscaleelse(72,71,69,67,65,64,62,60)#Scale needs to be set first because on init/load data already depends on it, at least the default scale. The scale is part of the track meta callback.
self.data=dataifdataelselist()#For content see docstring. this cannot be the default parameter because we would set the same list for all instances.
self.simpleNoteNames=simpleNoteNamesifsimpleNoteNameselseself.parentTrack.parentData.lastUsedNotenames[:]#This is mostly for the GUI or other kinds of representation instead midi notes
assertself.simpleNoteNames
#2.0 Pitch
#Extended Pitch
#Explanation: Why don't we just do 12 steps per ocatve and then leave steps blank to create a scale?
#We still want the: User set steps, not pitch idiom.
#We still want the "User set steps, not pitch" idiom.
#self.mutedSteps = set()
#self.stepsPerOctave = 7 # This is not supposed to go below 7! e.g. Pentatonic scales are done by leaving steps out with self.blankSteps.
#self.nrOfSteps = 8 # Needs to be >= stepsPerOctave. stepsPerOctave+1 will, by default, result in a full scale plus its octave. That can of course later be changed.
forpatternin(pforpinself.dataifp["index"]<self.parentTrack.parentData.howManyUnits*self.parentTrack.patternLengthMultiplicator):# < and not <= because index counts from 0 but howManyUnits counts from 1
@ -161,7 +163,7 @@ class MainWindow(TemplateMainWindow):
#Toolbar, which needs the widgets above already established
self._populateToolbar()
self.currentTrackId=None#this is purely a GUI construct. the engine does not know a current track. On startup there is no active track
self.start()#This shows the GUI, or not, depends on the NSM gui save setting. We need to call that after the menu, otherwise the about dialog will block and then we get new menu entries, which looks strange.
#There is always a track. Forcing that to be active is better than having to hide all the pattern widgets, or to disable them.
"""Index is a shared index, by convention, between our drop-down list and api.schemes"""
ifindex>0:
index-=1# the backend list obviously has no "Set Scale to" on index [0]
api.setScaleToKeyword(trackId=self.parentScene.parentView.parentMainWindow.currentTrackId,keyword=api.schemes[index])#this schemes must NOT be translated since it is the original key/symbol.
@ -886,7 +913,7 @@ class PitchWidget(QtWidgets.QGraphicsProxyWidget):