Browse Source

Fix channel change ly export. Better GUI text

master
Nils 5 years ago
parent
commit
e21076248a
  1. 9
      engine/items.py
  2. 34
      engine/track.py

9
engine/items.py

@ -2167,7 +2167,6 @@ class KeySignature(Item):
lyKeysignature = subtextRoot + "\\set Staff.keyAlterations = #`(( 0 . ,NATURAL))" #TODO: subtextRoot position is broken
return lyKeysignature
class Clef(Item):
"""A clef has no direct musical logical meaning. But it gives some hints:
Voice range. A typical musical voice in a polyphonic setup has between one and two octaves range
@ -2741,16 +2740,15 @@ class ChannelChange(Item):
"tickindex" : trackState.tickindex - duration, #we parse the tickindex after we stepped over the item.
"midiBytes" : [],
"text" : self.text,
"UIstring" : "ch{}".format(self.value), #this is for a UI, possibly a text UI, maybe for simple items of a GUI. Make it as short and unambigious as possible.
"UIstring" : f"{self.text}(ch{self.value})" if self.text else f"ch{self.value}", #this is for a UI, possibly a text UI, maybe for simple items of a GUI. Make it as short and unambigious as possible.
}
def _lilypond(self):
"""called by block.lilypond(), returns a string.
Don't create white-spaces yourself, this is done by the structures.
When in doubt prefer functionality and robustness over 'beautiful' lilypond syntax."""
if self.text:
raise NotImplementedError("Implement _lilypond() with correct instrument change syntax")
return self.text
if self.text:
return f'\\mark \\markup {{\\small "{self.text}"}}'
else:
return ""
@ -2846,6 +2844,7 @@ class LilypondText(Item):
Don't create white-spaces yourself, this is done by the structures.
When in doubt prefer functionality and robustness over 'beautiful' lilypond syntax."""
return self.text
@classmethod
def instanceFromSerializedData(cls, serializedObject, parentObject):

34
engine/track.py

@ -374,7 +374,7 @@ class Track(object):
def __init__(self, parentData, name=None):
self.parentData = parentData
self.sequencerInterface = template.engine.sequencer.SequencerInterface(parentTrack=self, name=name)
self.sequencerInterface = template.engine.sequencer.SequencerInterface(parentTrack=self, name=name)
self.ccGraphTracks = {} #cc number, graphTrackCC. Every CC is its own SequencerTrack that routes to this tracks jack midi out
self.ccChannels = tuple() #numbers from 0-15 which represent the midi channels all CCs are sent to. Only replaced by a new tuple by the user directly. From json this becomes a list, so don't test for type. If empty then CC uses the initial midi channel.
@ -986,8 +986,9 @@ class Track(object):
#TODO: However, it is even less elegant to put this call in all rhythm editing methods and functions. inserts, block duplicate, content links, augment, tuplets undo etc.
#Taken out an placed in tempo Export. #self.score.tempoTrack.expandLastBlockToScoreDuration() #we guarantee that the tempo track is always at least as long as the music tracks.
patternBlob = bytes() # Create a binary blob that contains the MIDI events
midiNotesBinaryCoxData = bytes() # Create a binary blob that contains the MIDI events
instrumentChangesBinaryCoxData = bytes() # same as above, but only for instrument changes.
originalPosition = self.state.position()
self.getPreliminaryData()
barlines = OrderedDict() #tick:metricalInstruction . We do not use selft.state.barlines, which is simply barlines for the live cursor. This is to send Barlines to a UI and also to generate the Metronome by associating a metricalInstruction with each barline.
@ -1005,9 +1006,9 @@ class Track(object):
assert self.initialMidiBankMsb == initialProgamChange.msb
assert self.initialMidiBankLsb == initialProgamChange.lsb
if initialProgamChange.program >= 0: #-1 is off.
patternBlob += cbox.Pattern.serialize_event(0, 0xC0 + self.initialMidiChannel, initialProgamChange.program, 0)
patternBlob += cbox.Pattern.serialize_event(0, 0xB0 + self.initialMidiChannel, 0, self.initialMidiBankMsb) #position, status byte+channel, controller number, controller value
patternBlob += cbox.Pattern.serialize_event(0, 0xB0 + self.initialMidiChannel, 32, self.initialMidiBankLsb) #position, status byte+channel, controller number, controller value
instrumentChangesBinaryCoxData += cbox.Pattern.serialize_event(0, 0xC0 + self.initialMidiChannel, initialProgamChange.program, 0)
instrumentChangesBinaryCoxData += cbox.Pattern.serialize_event(0, 0xB0 + self.initialMidiChannel, 0, self.initialMidiBankMsb) #position, status byte+channel, controller number, controller value
instrumentChangesBinaryCoxData += cbox.Pattern.serialize_event(0, 0xB0 + self.initialMidiChannel, 32, self.initialMidiBankLsb) #position, status byte+channel, controller number, controller value
localRight = self.right #performance
resultAppend = result.append #performance
@ -1024,11 +1025,15 @@ class Track(object):
expObj = previousItem.exportObject(self.state) #yes, it is correct that the state in the parameter is ahead by one position. Why is it that way? Because everything that matters, like new dynamics will only be parsed afterwards. The trackState is always correct except for the tickindex when exporting after parsing. Thats why exportObject sometimes substracts its own duration for finding its starting tick.
resultAppend(expObj)
dur = expObj["completeDuration"]
for blob in expObj["midiBytes"]: #a list of
patternBlob += blob
if expObj["type"] == "Chord" or expObj["type"] == "Rest": #save for later when we create Beams. No other use.
_allExportedChordsAppend(expObj)
if expObj["type"] != "InstrumentChange":
for blob in expObj["midiBytes"]: #a list of
midiNotesBinaryCoxData += blob
if expObj["type"] == "Chord" or expObj["type"] == "Rest": #save for later when we create Beams. No other use.
_allExportedChordsAppend(expObj)
else:
for blob in expObj["midiBytes"]: #a list of
instrumentChangesBinaryCoxData += blob
elif r == 2: #block end. Again, this is already the previous state. right now we are in the next block already.
lastBlock = self.blocks[self.state.blockindex-1] #why -1? see comment above
dur = lastBlock.staticExportEndMarkerDuration()
@ -1155,9 +1160,14 @@ class Track(object):
metaData["duration"] = self.state.tickindex #tickindex is now at the end, so this is the end duration. This includes Blocks minimumDuration as well since it is included in left/right
metaData["beams"] = resultBeamGroups
t = (patternBlob, 0, self.state.tickindex)
#Notes
t = (midiNotesBinaryCoxData, 0, self.state.tickindex)
self.sequencerInterface.setTrack([t]) #(bytes-blob, position, length) #tickindex is still on the last position, which means the second parameter is the length
#Instrument Changes
self.sequencerInterface.setSubtrack(key="instrumentChanges", blobs=[(instrumentChangesBinaryCoxData, 0, self.state.tickindex),]) #(bytes-blob, position, length)
self.toPosition(originalPosition, strict = False) #has head() in it
return result

Loading…
Cancel
Save