Browse Source

add option to change midi channel of tracks

master
Nils 3 years ago
parent
commit
5a0ac31d2b
  1. 3
      CHANGELOG
  2. 21
      engine/api.py
  3. 4
      engine/pattern.py
  4. 12
      engine/track.py
  5. 10
      qtgui/songeditor.py
  6. 1
      template/documentation/readme.template

3
CHANGELOG

@ -1,3 +1,6 @@
2021-04-15 Version 2.1.0
Add option to change the midi channel for a track in the tracks context menu.
2021-01-15 Version 2.0.0
Big new features increase the MAJOR version. Old save files can still be loaded.
The new features integrate seamlessly into the existing workflow:

21
engine/api.py

@ -440,6 +440,19 @@ def changeTrackColor(trackId, colorInHex):
track.color = colorInHex
callbacks._trackMetaDataChanged(track)
def changeTrackMidiChannel(trackId, newChannel:int):
"""newChannel is 1-16, we convert here to internal format 0-15.
Callbacks export data sends 1-16 again"""
if newChannel < 1 or newChannel > 16:
logger.warning(f"Midi Channel must be between 1-16 for this function, was: {newChannel}. Doing nothing.")
return
track = session.data.trackById(trackId)
track.midiChannel = newChannel-1
track.pattern.buildExportCache()
track.buildTrack()
updatePlayback()
callbacks._trackMetaDataChanged(track)
def addTrack(scale=None):
if scale:
assert type(scale) == tuple
@ -455,6 +468,7 @@ def createSiblingTrack(trackId):
newTrack = session.data.addTrack(name=track.sequencerInterface.name, scale=track.pattern.scale, color=track.color, simpleNoteNames=track.pattern.simpleNoteNames) #track name increments itself from "Track A" to "Track B" or "Track 1" -> "Track 2"
newTrack.pattern.averageVelocity = track.pattern.averageVelocity
newTrack.patternLengthMultiplicator = track.patternLengthMultiplicator
newTrack.midiChannel = track.midiChannel
jackConnections = cbox.JackIO.get_connected_ports(track.sequencerInterface.cboxPortName())
for port in jackConnections:
cbox.JackIO.port_connect(newTrack.sequencerInterface.cboxPortName(), port)
@ -498,6 +512,9 @@ def setTrackPatternLengthMultiplicator(trackId, newMultiplicator:int):
callbacks._patternLengthMultiplicatorChanged(track)
callbacks._patternChanged(track)
#Track Switches
def setSwitches(trackId, setOfPositions, newBool):
@ -956,11 +973,11 @@ def resizePatternWithoutScale(trackId, steps):
def noteOn(trackId, row):
track = session.data.trackById(trackId)
midipitch = track.pattern.scale[row]
cbox.send_midi_event(0x90, midipitch, track.pattern.averageVelocity, output=track.sequencerInterface.cboxMidiOutUuid)
cbox.send_midi_event(0x90+track.midiChannel, midipitch, track.pattern.averageVelocity, output=track.sequencerInterface.cboxMidiOutUuid)
def noteOff(trackId, row):
track = session.data.trackById(trackId)
midipitch = track.pattern.scale[row]
cbox.send_midi_event(0x80, midipitch, track.pattern.averageVelocity, output=track.sequencerInterface.cboxMidiOutUuid)
cbox.send_midi_event(0x80+track.midiChannel, midipitch, track.pattern.averageVelocity, output=track.sequencerInterface.cboxMidiOutUuid)

4
engine/pattern.py

@ -354,8 +354,8 @@ class Pattern(object):
velocity = noteDict["velocity"]
pitch = self._cachedTransposedScale[noteDict["pitch"] + scaleTransposition] + halftoneTransposition
exportPattern += cbox.Pattern.serialize_event(startTick, 0x90, pitch, velocity) # note on
exportPattern += cbox.Pattern.serialize_event(endTick-1, 0x80, pitch, velocity) # note off #-1 ticks to create a small logical gap. Does not affect next note on.
exportPattern += cbox.Pattern.serialize_event(startTick, 0x90 + self.parentTrack.midiChannel, pitch, velocity) # note on
exportPattern += cbox.Pattern.serialize_event(endTick-1, 0x80 + self.parentTrack.midiChannel, pitch, velocity) # note off #-1 ticks to create a small logical gap. Does not affect next note on.
pattern = cbox.Document.get_song().pattern_from_blob(exportPattern, oneMeasureInTicks)
self._builtPatternCache[cacheHash] = pattern

12
engine/track.py

@ -56,6 +56,7 @@ class Track(object): #injection at the bottom of this file!
#we take inspiration from the GUI that presents the Track on its own.
#The following setting is most likely to be found in the track sub-window:
self.patternLengthMultiplicator = 1 #int. >= 1 the multiplicator is added after all other calculations, like subdivions. We can't integrate this into howManyUnits because that is the global score value
self.midiChannel = 0 # 0-15 midi channel is always set.
self.pattern = Pattern(parentTrack=self, scale=scale, simpleNoteNames=simpleNoteNames)
self.structure = structure if structure else set() #see buildTrack(). This is the main track data structure besides the pattern. Just integers (starts at 0) as switches which are positions where to play the patterns. In between are automatic rests.
@ -109,6 +110,7 @@ class Track(object): #injection at the bottom of this file!
"whichPatternsAreScaleTransposed" : self.whichPatternsAreScaleTransposed,
"whichPatternsAreHalftoneTransposed" : self.whichPatternsAreHalftoneTransposed,
"patternLengthMultiplicator" : self.patternLengthMultiplicator,
"midiChannel" : self.midiChannel,
}
@classmethod
@ -116,11 +118,18 @@ class Track(object): #injection at the bottom of this file!
self = cls.__new__(cls)
self.parentData = parentData
self.sequencerInterface = template.engine.sequencer.SequencerInterface.instanceFromSerializedData(self, serializedData["sequencerInterface"])
if "patternLengthMultiplicator" in serializedData: #Version 1.8
#Version 2.0+ changes
if "patternLengthMultiplicator" in serializedData:
self.patternLengthMultiplicator = serializedData["patternLengthMultiplicator"]
else:
self.patternLengthMultiplicator = 1
if "midiChannel" in serializedData:
self.midiChannel = serializedData["midiChannel"]
else:
self.midiChannel = 0
self.color = serializedData["color"]
self.structure = set(serializedData["structure"])
self.whichPatternsAreHalftoneTransposed = {int(k):int(v) for k,v in serializedData["whichPatternsAreHalftoneTransposed"].items()} #json saves dict keys as strings
@ -144,6 +153,7 @@ class Track(object): #injection at the bottom of this file!
"numberOfMeasures": self.parentData.numberOfMeasures,
"whichPatternsAreScaleTransposed": self.whichPatternsAreScaleTransposed,
"whichPatternsAreHalftoneTransposed": self.whichPatternsAreHalftoneTransposed,
"midiChannel" : self.midiChannel+1, #1-16
}
#Dependency Injections.

10
qtgui/songeditor.py

@ -762,6 +762,16 @@ class TrackLabelEditor(QtWidgets.QGraphicsScene):
a.setEnabled(False)
a.triggered.connect(mergeCommand)
#Add a submenu to set the midi channel of this track. Highlight the current one
midiChannelMenu = menu.addMenu(QtCore.QCoreApplication.translate("TrackLabelContext", "Send on MIDI Channel"))
for mch in range(1, 17):
mchAction = QtWidgets.QAction(str(mch), midiChannelMenu)
midiChannelMenu.addAction(mchAction)
midiChannelCommand = lambda discard, chArg=mch: api.changeTrackMidiChannel(exportDict["id"], chArg) #discard parameter given by QAction
if exportDict["midiChannel"] == mch:
mchAction.setEnabled(False)
mchAction.triggered.connect(midiChannelCommand)
pos = QtGui.QCursor.pos()
pos.setY(pos.y() + 5)
self.parentView.parentMainWindow.setFocus()

1
template/documentation/readme.template

@ -4,6 +4,7 @@
#<name>
Program version <version>
![Screenshot](https://git.laborejo.org/lss/<name>/raw/branch/master/documentation/screenshot.png "Screenshot")

Loading…
Cancel
Save