Browse Source

change argodejo to agordejo

master
Nils 4 years ago
parent
commit
8bbe547678
  1. 4
      template/documentation/build.py
  2. 6
      template/documentation/english.adoc.template
  3. 6
      template/documentation/german.adoc.template
  4. 12
      template/documentation/readme.template
  5. 9
      template/engine/api.py
  6. 228
      template/engine/input_midi.py
  7. 52
      template/engine/sampler_sf2.py
  8. 2
      template/qtgui/chooseSessionDirectory.py
  9. 90
      template/qtgui/eventloop.py
  10. 76
      template/qtgui/mainwindow.py
  11. 32
      template/qtgui/resources.py
  12. BIN
      template/qtgui/resources/translations/de.qm
  13. 6
      template/qtgui/resources/translations/de.ts
  14. 2
      template/start.py

4
template/documentation/build.py

@ -14,7 +14,7 @@ The correct out/ dir is already part of git.
""" """
#We still split between template and non-template, eventhough this is not the case in Argodejo. #We still split between template and non-template, eventhough this is not the case in Agordejo.
#However, this enables an easier diff across projects to keep track of changes that we need to do manually here #However, this enables an easier diff across projects to keep track of changes that we need to do manually here
#TODO: Unify in the future by creating calfbox and NSM client as documentation "modules" #TODO: Unify in the future by creating calfbox and NSM client as documentation "modules"
@ -110,7 +110,7 @@ https://www.laborejo.org/bugs
https://www.laborejo.org/ https://www.laborejo.org/
[examples] [examples]
Start {METADATA["shortName"]} through NSM, e.g. through Argodejo. This will take care of all Start {METADATA["shortName"]} through NSM, e.g. through Agordejo. This will take care of all
settings and save directories. settings and save directories.
Other modes of operations, mostly for testing, are: Other modes of operations, mostly for testing, are:

6
template/documentation/english.adoc.template

@ -36,7 +36,7 @@ For program version <version>
<name> is exclusive for Linux. The best way to install is to use your package manager. <name> is exclusive for Linux. The best way to install is to use your package manager.
If it is not there, or only in an outdated version, please ask your Linux distribution to provide a recent version. If it is not there, or only in an outdated version, please ask your Linux distribution to provide a recent version.
If available in the package repository, please continue reading directly at "Start <shortName> from Argodejo / New Session Manager". If available in the package repository, please continue reading directly at "Start <shortName> from Agordejo / New Session Manager".
If not, you can build <name> yourself. If not, you can build <name> yourself.
.Build and Install .Build and Install
@ -50,8 +50,8 @@ If not, you can build <name> yourself.
* `make` * `make`
* `sudo make install` * `sudo make install`
.Start <shortName> from Argodejo (New Session Manager, NSM) .Start <shortName> from Agordejo (New Session Manager, NSM)
* Run `argodejo` * Run `agordejo`
* Press the `New` button, and enter a name for your piece of music. * Press the `New` button, and enter a name for your piece of music.
* Use the launcher to add `<shortName>` to the session. * Use the launcher to add `<shortName>` to the session.
* Add any compatible programs, e.g. synthesizers. * Add any compatible programs, e.g. synthesizers.

6
template/documentation/german.adoc.template

@ -36,7 +36,7 @@ Für Programmversion <version>
Falls es dort nicht vorhanden ist, oder nur in einer veralteten Version, bitten sie ihre Falls es dort nicht vorhanden ist, oder nur in einer veralteten Version, bitten sie ihre
Linuxdistribution <name> bereitzustellen. Linuxdistribution <name> bereitzustellen.
Falls in den Paketquellen vorhanden bitte direkt bei "<shortName> in Argodejo (New Session Manager)" weiterlesen. Falls in den Paketquellen vorhanden bitte direkt bei "<shortName> in Agordejo (New Session Manager)" weiterlesen.
Falls nicht kann man <name> auch selbst "bauen". Falls nicht kann man <name> auch selbst "bauen".
.Abhängigkeiten* .Abhängigkeiten*
@ -50,8 +50,8 @@ Falls nicht kann man <name> auch selbst "bauen".
* `make` * `make`
* `sudo make install` * `sudo make install`
.<shortName> im Argodejo (New Session Manager, NSM) starten .<shortName> im Agordejo (New Session Manager, NSM) starten
* Starten Sie `argodejo` * Starten Sie `agordejo`
* Erstellen Sie eine neue Session, geben Sie einen Namen für das Musikstück ein. * Erstellen Sie eine neue Session, geben Sie einen Namen für das Musikstück ein.
* Benutzen Sie den Launcher um `<shortName>` hinzuzufügen. * Benutzen Sie den Launcher um `<shortName>` hinzuzufügen.
* Fügen Sie beliebige kompatible Programme hinzu, z.B. Synthesizer. * Fügen Sie beliebige kompatible Programme hinzu, z.B. Synthesizer.

12
template/documentation/readme.template

@ -42,7 +42,7 @@ It is possible to clone a git repository.
### Environment: ### Environment:
* Jack Audio Connection Kit must be running * Jack Audio Connection Kit must be running
* Argodejo / New Session Manager ("NSM") * Agordejo / New Session Manager ("NSM")
## Build ## Build
./configure --prefix=/usr/local ./configure --prefix=/usr/local
@ -57,15 +57,15 @@ system as you want.
We make no distinction if you installed <name> yourself or through the distributions package-manager. We make no distinction if you installed <name> yourself or through the distributions package-manager.
The differences are: With or without Argodejo, with or without sound, installed or from the source dir. The differences are: With or without Agordejo, with or without sound, installed or from the source dir.
### Installed , running through Argodejo (New Session Manager) (recommended) ### Installed , running through Agordejo (New Session Manager) (recommended)
Starting <name> through Argodejo after you installed <shortname> system-wide Starting <name> through Agordejo after you installed <shortname> system-wide
is the recommended and only supported way. Start argodejo and load or create a new is the recommended and only supported way. Start agordejo and load or create a new
session. Then use the program launcher to add `<shortname>`. session. Then use the program launcher to add `<shortname>`.
It should appear with an icon in the list and open its GUI. It should appear with an icon in the list and open its GUI.
### Installed without Argodejo ### Installed without Agordejo
If you start <shortname> directly it will present you with a dialog to choose your session directory. If you start <shortname> directly it will present you with a dialog to choose your session directory.
You can also start <shortname> from a terminal (or create a starter script). You can also start <shortname> from a terminal (or create a starter script).

9
template/engine/api.py

@ -263,7 +263,8 @@ class Callbacks(object):
def _channelChanged(self, channel): def _channelChanged(self, channel):
"""A single channel changed its parameters. The soundfont stays the same.""" """A single channel changed its parameters. The soundfont stays the same."""
exportDict = session.data.exportChannel(channel) exportDict = session.data.exportChannel(channel)
session.data.updateChannelJackMetadaPrettyname(channel) session.data.updateChannelAudioJackMetadaPrettyname(channel)
session.data.updateChannelMidiInJackMetadaPrettyname(channel)
for func in self.channelChanged: for func in self.channelChanged:
func(channel, exportDict) func(channel, exportDict)
@ -285,17 +286,13 @@ def startEngine(nsmClient):
It gets called by client applications before their own startEngine. It gets called by client applications before their own startEngine.
Stopping the engine is done via pythons atexit in the session. Stopping the engine is done via pythons atexit in the session.
session.eventLoop is overwritten by template.qtgui.mainWindow . It is the first action
it takes after imports.
""" """
logger.info("Starting template api engine") logger.info("Starting template api engine")
assert session assert session
assert callbacks assert callbacks
session.nsmClient = nsmClient session.nsmClient = nsmClient
session.eventLoop.directConnect(callbacks._checkPlaybackStatusAndSendSignal) session.eventLoop.fastConnect(callbacks._checkPlaybackStatusAndSendSignal)
session.eventLoop.fastConnect(callbacks._setPlaybackTicks) session.eventLoop.fastConnect(callbacks._setPlaybackTicks)
session.eventLoop.fastConnect(cbox.get_new_events) #global cbox.get_new_events does not eat dynamic midi port events. session.eventLoop.fastConnect(cbox.get_new_events) #global cbox.get_new_events does not eat dynamic midi port events.
session.eventLoop.slowConnect(callbacks._checkBBTAndSendSignal) session.eventLoop.slowConnect(callbacks._checkBBTAndSendSignal)

228
template/engine/input_midi.py

@ -33,215 +33,215 @@ from . import pitch
class MidiInput(object): class MidiInput(object):
"""MidiIn provides a port from calfboxes realtime midi in parsing to python data and functions. """MidiIn provides a port from calfboxes realtime midi in parsing to python data and functions.
Please note that it is not directly related to a track and is not responsible for actual midi Please note that it is not directly related to a track and is not responsible for actual midi
recording. This is the base class if you want to have midi control your program, like controlling recording. This is the base class if you want to have midi control your program, like controlling
a GUI, step entry like in Laborejo or visualizers with multiple tracks. a GUI, step entry like in Laborejo or visualizers with multiple tracks.
You can therefore choose how you interpret the incoming data. It is possible to only have one You can therefore choose how you interpret the incoming data. It is possible to only have one
midi in port for the whole program and decide where to route the incoming events based on e.g. midi in port for the whole program and decide where to route the incoming events based on e.g.
GUI status (which track is selected). GUI status (which track is selected).
Or you could mirror your internal tracks one by one with a midi input. Or you could mirror your internal tracks one by one with a midi input.
MidiIn is also the base class for instruments. However, these are subclasses. MidiIn is also the base class for instruments. However, these are subclasses.
It is recommended that you add a MidiIn object to your Python structures. It is recommended that you add a MidiIn object to your Python structures.
Don't subclass it because you might want to have MidiOut and audios as well, this Don't subclass it because you might want to have MidiOut and audios as well, this
will produce a messy multiple inheritance situation. will produce a messy multiple inheritance situation.
One usecase is Laborejo: A single global step midi input that calls api functions. One usecase is Laborejo: A single global step midi input that calls api functions.
In this case MidiInput is an alternative to the GUI. It does not belong in the engine but at In this case MidiInput is an alternative to the GUI. It does not belong in the engine but at
lowest in the api. lowest in the api.
""" """
def __init__(self, session, portName): def __init__(self, session, portName):
"""The processor adds itself to the event loop. """The processor adds itself to the event loop.
You need to call prepareDelete before removing a midi output again""" You need to call prepareDelete before removing a midi output again"""
self.session = session self.session = session
self.portName = portName self.portName = portName
self.scene = cbox.Document.get_engine().new_scene() self.scene = cbox.Document.get_engine().new_scene()
self.scene.clear() self.scene.clear()
self.scene.set_enable_default_song_input(False) self.scene.set_enable_default_song_input(False)
self.cboxMidiPortUid = cbox.JackIO.create_midi_input(portName) self.cboxMidiPortUid = cbox.JackIO.create_midi_input(portName)
self.realtimeMidiThroughLayer = self.scene.add_new_midi_layer(self.cboxMidiPortUid) #Create a midi layer for our input port. That layer support manipulation like transpose or channel routing. self.realtimeMidiThroughLayer = self.scene.add_new_midi_layer(self.cboxMidiPortUid) #Create a midi layer for our input port. That layer support manipulation like transpose or channel routing.
cbox.JackIO.set_appsink_for_midi_input(self.cboxMidiPortUid, True) #This sounds like a program wide sink, but it is needed for every port. cbox.JackIO.set_appsink_for_midi_input(self.cboxMidiPortUid, True) #This enables forwarding to Python for our midiProcessors get_new_events(self.parentInput.cboxMidiPortUid)
cbox.JackIO.route_midi_input(self.cboxMidiPortUid, self.scene.uuid) #Route midi input to the scene. cbox.JackIO.route_midi_input(self.cboxMidiPortUid, self.scene.uuid) #Route midi input to the scene. Without this we have no sound, but the python processor will still work.
self._currentMidiThruOutputMidiPortUid = None #RT midi thru self._currentMidiThruOutputMidiPortUid = None #RT midi thru
self.setMidiThruChannel(1) self.setMidiThruChannel(1)
self.midiProcessor = MidiProcessor(parentInput = self) self.midiProcessor = MidiProcessor(parentInput = self)
self.session.eventLoop.fastConnect(self.midiProcessor.processEvents) self.session.eventLoop.fastConnect(self.midiProcessor.processEvents)
self.readyToDelete = False self.readyToDelete = False
def prepareDelete(self): def prepareDelete(self):
self.session.eventLoop.fastDisconnect(self.midiProcessor.processEvents) self.session.eventLoop.fastDisconnect(self.midiProcessor.processEvents)
self.readyToDelete = True self.readyToDelete = True
def setMidiThru(self, cboxMidiOutUuid): def setMidiThru(self, cboxMidiOutUuid):
""" """
This is the output portion of the program. Everything in init is only for input. This is the output portion of the program. Everything in init is only for input.
Instruct the RT part to echo midi in directly to the connect output ports Instruct the RT part to echo midi in directly to the connect output ports
so we hear the current track with the tracks instrument. so we hear the current track with the tracks instrument.
e.g. if you have a single global midi input but multiple track based outputs you can use e.g. if you have a single global midi input but multiple track based outputs you can use
this to route the input RT to a track output while still getting the python data to process. this to route the input RT to a track output while still getting the python data to process.
If you use such a configuration for data entry the user will hear his inputs echoed by If you use such a configuration for data entry the user will hear his inputs echoed by
the actual target instrument and not e.g. a generic piano or sine wave sound. the actual target instrument and not e.g. a generic piano or sine wave sound.
""" """
if not self._currentMidiThruOutputMidiPortUid == cboxMidiOutUuid: #most of the time this stays the same e.g. cursor left/right. we only care about up and down if not self._currentMidiThruOutputMidiPortUid == cboxMidiOutUuid: #most of the time this stays the same e.g. cursor left/right. we only care about up and down
self._currentMidiThruOutputMidiPortUid = cboxMidiOutUuid self._currentMidiThruOutputMidiPortUid = cboxMidiOutUuid
self.realtimeMidiThroughLayer.set_external_output(cboxMidiOutUuid) self.realtimeMidiThroughLayer.set_external_output(cboxMidiOutUuid)
def setMidiThruChannel(self, channel): def setMidiThruChannel(self, channel):
if channel < 1 or channel > 16: if channel < 1 or channel > 16:
raise ValueError("Channels are from 1 to 16 (inclusive). You sent " + str(channel)) raise ValueError("Channels are from 1 to 16 (inclusive). You sent " + str(channel))
self.realtimeMidiThroughLayer.set_out_channel(channel) self.realtimeMidiThroughLayer.set_out_channel(channel)
class MidiProcessor(object):
""" class MidiProcessor(object):
"""
There are two principal modes: Step Entry and Live Recording. There are two principal modes: Step Entry and Live Recording.
Add your function to callbacks3 for notes and CC or callbacks2 for program change and Channel Pressure. Add your function to callbacks3 for notes and CC or callbacks2 for program change and Channel Pressure.
self.callbacks3[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.NOTE_ON)] = lambda: print(channel, note, velocity)""" self.callbacks3[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.NOTE_ON)] = lambda: print(channel, note, velocity)"""
SIMPLE_EVENT = "/io/midi/simple_event" SIMPLE_EVENT = "/io/midi/simple_event"
TRANSPORT_PLAY = "/io/midi/event_time_ppqn" TRANSPORT_PLAY = "/io/midi/event_time_ppqn"
TRANSPORT_STOPPED = "/io/midi/event_time_samples" TRANSPORT_STOPPED = "/io/midi/event_time_samples"
M_NOTE_ON = 0x90 M_NOTE_ON = 0x90
M_NOTE_OFF = 0x80 M_NOTE_OFF = 0x80
M_ACTIVE_SENSE = 0xFE #254 M_ACTIVE_SENSE = 0xFE #254
M_AFTERTOUCH = 0xA0 #160 M_AFTERTOUCH = 0xA0 #160
M_CONTROL_CHANGE = 0xB0 #176 M_CONTROL_CHANGE = 0xB0 #176
M_PROGRAMCHANGE = 0xC0 #192 M_PROGRAMCHANGE = 0xC0 #192
M_CHANNELPRESSURE = 0xD0 #208 M_CHANNELPRESSURE = 0xD0 #208
M_PITCHBEND = 0xE0 #224 M_PITCHBEND = 0xE0 #224
CC_BANKCHANGE_COARSE = 32 CC_BANKCHANGE_COARSE = 32
CC_BANKCHANGE_FINE = 0 CC_BANKCHANGE_FINE = 0
def __init__(self, parentInput): def __init__(self, parentInput):
self.parentInput = parentInput self.parentInput = parentInput
self.callbacks3 = {} #keys are tuples self.callbacks3 = {} #keys are tuples
self.callbacks2 = {} #keys are tuples self.callbacks2 = {} #keys are tuples
self.active = True self.active = True
self.ccState = {} self.ccState = {}
self.lastTimestamp = None #None for Transport not rolling, Value while rolling self.lastTimestamp = None #None for Transport not rolling, Value while rolling
#for cc in range(128): #0-127 inclusive #for cc in range(128): #0-127 inclusive
# self.ccState[cc] = None #start with undefined. # self.ccState[cc] = None #start with undefined.
def processEvents(self): def processEvents(self):
"""events come in packages. """events come in packages.
If you press just a key you'll get a list with length 2: timestamp, midi-message If you press just a key you'll get a list with length 2: timestamp, midi-message
For each event received in the same timeslot you'll get two events more. For each event received in the same timeslot you'll get two events more.
A chord of four keys, played staccato simultaniously will yield 16 events. A chord of four keys, played staccato simultaniously will yield 16 events.
2 for each key (=8), times two for note on and note off each.(=16) 2 for each key (=8), times two for note on and note off each.(=16)
We don't want the whole function too many indentation levels deep so we take a few shortcuts. We don't want the whole function too many indentation levels deep so we take a few shortcuts.
This function gets called very often. So every optimisation is good. This function gets called very often. So every optimisation is good.
""" """
events = cbox.JackIO.get_new_events(self.parentInput.cboxMidiPortUid) events = cbox.JackIO.get_new_events(self.parentInput.cboxMidiPortUid)
if not self.active: if not self.active:
return return
if not events: if not events:
return return
for message, stuff, dataList in events: for message, stuff, dataList in events:
l = len(dataList) l = len(dataList)
#Check recording mode. These message only get sent in front of another event, like a note. #Check recording mode. These message only get sent in front of another event, like a note.
#That means this is not a playback state detector because it only works after receiving a midi event #That means this is not a playback state detector because it only works after receiving a midi event
if message == MidiProcessor.TRANSPORT_PLAY: if message == MidiProcessor.TRANSPORT_PLAY:
#if self.lastTimestamp is None: #if self.lastTimestamp is None:
# print ("switching to live recording") # print ("switching to live recording")
self.lastTimestamp = dataList[0] self.lastTimestamp = dataList[0]
#print (self.lastTimestamp) #print (self.lastTimestamp)
elif message == MidiProcessor.TRANSPORT_STOPPED : elif message == MidiProcessor.TRANSPORT_STOPPED :
#if not self.lastTimestamp is None: #if not self.lastTimestamp is None:
# print ("switching to step recording") # print ("switching to step recording")
self.lastTimestamp = None self.lastTimestamp = None
#Process Data #Process Data
elif l == 3: #notes and CC elif l == 3: #notes and CC
if message == MidiProcessor.SIMPLE_EVENT: if message == MidiProcessor.SIMPLE_EVENT:
m_type, m_note, m_velocity = dataList #of course these can be different than a note, but this is easier to read then "byte 1", "byte 2" m_type, m_note, m_velocity = dataList #of course these can be different than a note, but this is easier to read then "byte 1", "byte 2"
m_channel = m_type & 0x0F m_channel = m_type & 0x0F
m_mode = m_type & 0xF0 #0x90 note on, 0x80 note off and so on. m_mode = m_type & 0xF0 #0x90 note on, 0x80 note off and so on.
key = (message, m_mode) key = (message, m_mode)
if m_mode == 0xB0: if m_mode == 0xB0:
self.ccState[m_note] = m_velocity #note is CCn , like 7=Volume, velocity is value. self.ccState[m_note] = m_velocity #note is CCn , like 7=Volume, velocity is value.
if key in self.callbacks3: if key in self.callbacks3:
self.callbacks3[key](self.lastTimestamp, m_channel, m_note, m_velocity) self.callbacks3[key](self.lastTimestamp, m_channel, m_note, m_velocity)
elif l == 2: #program change, aftertouch, elif l == 2: #program change, aftertouch,
if message == MidiProcessor.SIMPLE_EVENT: if message == MidiProcessor.SIMPLE_EVENT:
m_type, m_value = dataList m_type, m_value = dataList
m_channel = m_type & 0x0F m_channel = m_type & 0x0F
m_mode = m_type & 0xF0 m_mode = m_type & 0xF0
key = (message, m_mode) key = (message, m_mode)
if key in self.callbacks2: if key in self.callbacks2:
self.callbacks2[key](self.lastTimestamp, m_channel, m_value) self.callbacks2[key](self.lastTimestamp, m_channel, m_value)
#Active Sense (Keep Alive Signal) #Active Sense (Keep Alive Signal)
#TOOD: this can be commented out for performance reasons if we don't need to print out messages for debug reasons. #TOOD: this can be commented out for performance reasons if we don't need to print out messages for debug reasons.
#TODO: Sadly there is no compile time. Use the compiledPrefix instead? #TODO: Sadly there is no compile time. Use the compiledPrefix instead?
#elif l == 1 and message == MidiProcessor.SIMPLE_EVENT and dataList == [MidiProcessor.M_ACTIVE_SENSE]: #elif l == 1 and message == MidiProcessor.SIMPLE_EVENT and dataList == [MidiProcessor.M_ACTIVE_SENSE]:
# pass # pass
#else: #else:
# print (message, stuff, dataList) # print (message, stuff, dataList)
#Convenience Functions #Convenience Functions
def register_NoteOn(self, functionWithFourParametersTimestampChannelNoteVelocity): def register_NoteOn(self, functionWithFourParametersTimestampChannelNoteVelocity):
"""A printer would be: """A printer would be:
lambda timestamp, channel, note, velocity: print(timestamp, channel, note, velocity) lambda timestamp, channel, note, velocity: print(timestamp, channel, note, velocity)
""" """
self.callbacks3[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_NOTE_ON)] = functionWithFourParametersTimestampChannelNoteVelocity self.callbacks3[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_NOTE_ON)] = functionWithFourParametersTimestampChannelNoteVelocity
def register_NoteOff(self, functionWithFourParametersTimestampChannelNoteVelocity): def register_NoteOff(self, functionWithFourParametersTimestampChannelNoteVelocity):
"""A printer would be: """A printer would be:
lambda timestamp, channel, note, velocity: print(timestamp, channel, note, velocity) lambda timestamp, channel, note, velocity: print(timestamp, channel, note, velocity)
""" """
self.callbacks3[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_NOTE_OFF)] = functionWithFourParametersTimestampChannelNoteVelocity self.callbacks3[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_NOTE_OFF)] = functionWithFourParametersTimestampChannelNoteVelocity
def register_PolyphonicAftertouch(self, functionWithFourParametersTimestampChannelTypeValue): def register_PolyphonicAftertouch(self, functionWithFourParametersTimestampChannelTypeValue):
self.callbacks3[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_AFTERTOUCH)] = functionWithFourParametersTimestampChannelTypeValue self.callbacks3[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_AFTERTOUCH)] = functionWithFourParametersTimestampChannelTypeValue
def register_CC(self, functionWithFourParametersTimestampChannelTypeValue): def register_CC(self, functionWithFourParametersTimestampChannelTypeValue):
self.callbacks3[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_CONTROL_CHANGE)] = functionWithFourParametersTimestampChannelTypeValue self.callbacks3[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_CONTROL_CHANGE)] = functionWithFourParametersTimestampChannelTypeValue
def register_ProgramChange(self, functionWithThreeParametersTimestampChannelValue): def register_ProgramChange(self, functionWithThreeParametersTimestampChannelValue):
self.callbacks2[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_PROGRAMCHANGE)] = functionWithThreeParametersTimestampChannelValue self.callbacks2[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_PROGRAMCHANGE)] = functionWithThreeParametersTimestampChannelValue
def register_ChannelPressure(self, functionWithThreeParametersTimestampChannelValue): def register_ChannelPressure(self, functionWithThreeParametersTimestampChannelValue):
self.callbacks2[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_CHANNELPRESSURE)] = functionWithThreeParametersTimestampChannelValue self.callbacks2[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_CHANNELPRESSURE)] = functionWithThreeParametersTimestampChannelValue
def register_PitchBend(self, functionWithFourParametersTimestampChannelTypeValue): def register_PitchBend(self, functionWithFourParametersTimestampChannelTypeValue):
self.callbacks3[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_PITCHBEND)] = functionWithFourParametersTimestampChannelTypeValue self.callbacks3[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_PITCHBEND)] = functionWithFourParametersTimestampChannelTypeValue
#Hier weiter machen. After Touch, aber wie darstellen? Das ist ja per Taste. Wie CC Type? Oder unten im Velocity View? #Hier weiter machen. After Touch, aber wie darstellen? Das ist ja per Taste. Wie CC Type? Oder unten im Velocity View?
def notePrinter(self, state:bool): def notePrinter(self, state:bool):
if state: if state:
def _printer(timestamp, channel, note, velocity): def _printer(timestamp, channel, note, velocity):
print(f"[{timestamp}] Chan: {channel} Note: {pitch.midi_notenames_english[note]}: Vel: {velocity}") print(f"[{timestamp}] Chan: {channel} Note: {pitch.midi_notenames_english[note]}: Vel: {velocity}")
self.callbacks3[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_NOTE_ON)] = _printer self.callbacks3[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_NOTE_ON)] = _printer
else: else:
try: try:
del self.callbacks[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_NOTE_ON)] del self.callbacks[(MidiProcessor.SIMPLE_EVENT, MidiProcessor.M_NOTE_ON)]
except KeyError: except KeyError:
pass pass

52
template/engine/sampler_sf2.py

@ -53,13 +53,15 @@ class Sampler_sf2(Data):
self.patchlist = {} #bank:{program:name} self.patchlist = {} #bank:{program:name}
self.buffer_ignoreProgramChanges = ignoreProgramChanges self.buffer_ignoreProgramChanges = ignoreProgramChanges
self.midiInput = MidiInput(session=parentSession, portName="in") self.midiInput = MidiInput(session=parentSession, portName="all")
cbox.JackIO.Metadata.set_port_order(f"{cbox.JackIO.status().client_name}:all", 0) #first one. The other port order later start at 1 because we loop 1-based for channels.
#Set libfluidsynth! to 16 output pairs. We prepared 32 jack ports in the session start. "soundfont" is our given name, in the line below. This is a prepared config which will be looked up by add_new_instrument #Set libfluidsynth! to 16 output pairs. We prepared 32 jack ports in the session start. "soundfont" is our given name, in the line below. This is a prepared config which will be looked up by add_new_instrument
cbox.Config.set("instrument:soundfont", "engine", "fluidsynth") cbox.Config.set("instrument:soundfont", "engine", "fluidsynth")
cbox.Config.set("instrument:soundfont", "output_pairs", 16) #this is not the same as session.py cbox.Config.set("io", "outputs", METADATA["cboxOutputs"]). It is yet another layer and those two need connections cbox.Config.set("instrument:soundfont", "output_pairs", 16) #this is not the same as session.py cbox.Config.set("io", "outputs", METADATA["cboxOutputs"]). It is yet another layer and those two need connections
#Create the permanent instrument which loads changing sf2 #Create the permanent instrument which loads changing sf2
layer = self.midiInput.scene.add_instrument_layer("soundfont") layer = self.midiInput.scene.add_instrument_layer("soundfont")
self.instrumentLayer = layer
self.instrument = layer.get_instrument() self.instrument = layer.get_instrument()
self.loadSoundfont(filePath, defaultSoundfont) self.loadSoundfont(filePath, defaultSoundfont)
@ -98,6 +100,7 @@ class Sampler_sf2(Data):
cbox.JackIO.Metadata.set_port_order(portname, channelNumber) cbox.JackIO.Metadata.set_port_order(portname, channelNumber)
except Exception as e: #No Jack Meta Data except Exception as e: #No Jack Meta Data
logger.error(e) logger.error(e)
break
#Also sort the mixing channels #Also sort the mixing channels
try: try:
@ -108,7 +111,32 @@ class Sampler_sf2(Data):
except Exception as e: #No Jack Meta Data except Exception as e: #No Jack Meta Data
logger.error(e) logger.error(e)
#If demanded create 16 routing midi channels, basically a jack port -> midi channel merger.
if True:
for midiRouterPortNum in range(1,17):
portName = f"channel_{str(midiRouterPortNum).zfill(2)}"
router_cboxMidiPortUid = cbox.JackIO.create_midi_input(portName)
router_scene = cbox.Document.get_engine().new_scene()
router_scene.clear()
router_scene.set_enable_default_song_input(False)
#router_scene.add_instrument_layer("soundfont") #This creates multiple sf2 instances
router_midi_layer = router_scene.add_new_midi_layer(router_cboxMidiPortUid) #Create a midi layer for our input port. That layer supports manipulation like transpose or channel routing.
#cbox.JackIO.route_midi_input(router_cboxMidiPortUid, router_scene.uuid)
router_midi_layer.set_out_channel(midiRouterPortNum)
router_midi_layer.set_in_channel(midiRouterPortNum)
#router_midi_layer.set_enable(False)
#cbox.JackIO.route_midi_input(router_cboxMidiPortUid, self.midiInput.scene.uuid) #routes directly to the instrument and ignores the layer options like channel forcing
#cbox.JackIO.route_midi_input(router_cboxMidiPortUid, self.midiInput.cboxMidiPortUid) #runs, but does nothing
#cbox.JackIO.route_midi_input(router_midi_layer, self.midiInput.cboxMidiPortUid) #does not run
router_midi_layer.set_external_output(self.midiInput.cboxMidiPortUid)
#router_midi_layer.set_external_output(self.midiInput.scene.uuid)
#Port order
fullPortname = f"{cbox.JackIO.status().client_name}:out_{channelNumber}"
try:
cbox.JackIO.Metadata.set_port_order(fullPortname, midiRouterPortNum)
except Exception as e: #No Jack Meta Data
pass #don't break here.
@property @property
def _ignoreProgramChanges(self): def _ignoreProgramChanges(self):
@ -242,8 +270,8 @@ class Sampler_sf2(Data):
newValue = program | (bank<<7) newValue = program | (bank<<7)
self.instrument.engine.set_patch(channel, newValue) #call to cbox self.instrument.engine.set_patch(channel, newValue) #call to cbox
def updateChannelJackMetadaPrettyname(self, channel:int): def updateChannelAudioJackMetadaPrettyname(self, channel:int):
"""Channels 1-16""" """Audio output channel pairs 1-16"""
bank, program, name = self.activePatches()[channel] #activePatches returns a dict, not a list. channel is a 1-based key, not a 0-based index. bank, program, name = self.activePatches()[channel] #activePatches returns a dict, not a list. channel is a 1-based key, not a 0-based index.
chanL = channel*2-1 #channel is 1-based. [1]*2-1 = 1. chanL = channel*2-1 #channel is 1-based. [1]*2-1 = 1.
@ -259,6 +287,19 @@ class Sampler_sf2(Data):
except Exception as e: #No Jack Meta Data except Exception as e: #No Jack Meta Data
logger.error(e) logger.error(e)
def updateChannelMidiInJackMetadaPrettyname(self, channel:int):
"""Midi in single channels 1-16"""
bank, program, name = self.activePatches()[channel] #activePatches returns a dict, not a list. channel is a 1-based key, not a 0-based index.
portName = f"channel_{str(channel).zfill(2)}"
fullPortname = f"{cbox.JackIO.status().client_name}:{portName}"
#Use the instrument name as port name: 03-L:Violin
try:
cbox.JackIO.Metadata.set_pretty_name(fullPortname, f"{str(channel).zfill(2)} : {name}")
except Exception as e: #No Jack Meta Data
logger.error(e)
def updateAllChannelJackMetadaPrettyname(self): def updateAllChannelJackMetadaPrettyname(self):
"""Add this to a callback. It can't be run on program startup because sampler_sf2 is """Add this to a callback. It can't be run on program startup because sampler_sf2 is
@ -269,7 +310,8 @@ class Sampler_sf2(Data):
therefore the place to update is in the API, where the new state is also sent to the UI. therefore the place to update is in the API, where the new state is also sent to the UI.
""" """
for channel in range(1,17): for channel in range(1,17):
self.updateChannelJackMetadaPrettyname(channel) self.updateChannelAudioJackMetadaPrettyname(channel)
self.updateChannelMidiInJackMetadaPrettyname(channel)
""" """
for channel, (bank, program, name) in self.activePatches().items(): for channel, (bank, program, name) in self.activePatches().items():

2
template/qtgui/chooseSessionDirectory.py

@ -66,7 +66,7 @@ class ChooseSessionDirectory(QtWidgets.QDialog):
pixmap_scaled = aboutLogoPixmap.scaled(self.ui.goldenratioLabel.size(), QtCore.Qt.KeepAspectRatio) pixmap_scaled = aboutLogoPixmap.scaled(self.ui.goldenratioLabel.size(), QtCore.Qt.KeepAspectRatio)
self.ui.goldenratioLabel.setPixmap(pixmap_scaled) self.ui.goldenratioLabel.setPixmap(pixmap_scaled)
message = QtCore.QCoreApplication.translate("TemplateChooseSessionDirectory", "Please choose a directory for your session files. It is recommended to start through Argodejo/New Session Manager instead.") message = QtCore.QCoreApplication.translate("TemplateChooseSessionDirectory", "Please choose a directory for your session files. It is recommended to start through Agordejo/New Session Manager instead.")
self.ui.label.setText(message) self.ui.label.setText(message)
settings = QtCore.QSettings("LaborejoSoftwareSuite", METADATA["shortName"]) settings = QtCore.QSettings("LaborejoSoftwareSuite", METADATA["shortName"])

90
template/qtgui/eventloop.py

@ -0,0 +1,90 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2020, 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/>.
"""
import logging; logger = logging.getLogger(__name__); logger.info("import")
from PyQt5 import QtCore
class EventLoop(object):
def __init__(self):
"""The loop for all things GUI and controlling the GUI (e.g. by a control midi in port)
directConnect is 0ms (see below)
fastConnect 20ms
slowConnect 100ms
verySlowConnect 200ms
0 ms means "if there is time". 10ms-20ms is smooth. 100ms is still ok.
Influences everything. Control Midi In Latency, playback cursor scrolling smoothnes etc.
But not realtime. This is not the realtime loop. Converting midi into instrument sounds
or playing back sequenced midi data is not handled by this loop at all.
Creating a non-qt class for the loop is an abstraction layer that enables the engine to
work without modification for non-gui situations. In this case it will use its own loop,
like python async etc.
A qt event loop needs the qt-app started. Otherwise it will not run.
We init the event loop outside of main but call start from the mainWindow.
"""
self.fastLoop = QtCore.QTimer()
self.slowLoop = QtCore.QTimer()
self.verySlowLoop = QtCore.QTimer()
def fastConnect(self, function):
self.fastLoop.timeout.connect(function)
def slowConnect(self, function):
self.slowLoop.timeout.connect(function)
def verySlowConnect(self, function):
self.verySlowLoop.timeout.connect(function)
def fastDisconnect(self, function):
"""The function must be the exact instance that was registered"""
self.fastLoop.timeout.disconnect(function)
def slowDisconnect(self, function):
"""The function must be the exact instance that was registered"""
self.slowLoop.timeout.disconnect(function)
def verySlowDisconnect(self, function):
"""The function must be the exact instance that was registered"""
self.verySlowLoop.timeout.disconnect(function)
def start(self):
"""The event loop MUST be started after the Qt Application instance creation"""
logger.info("Starting fast qt event loop")
self.fastLoop.start(20)
logger.info("Starting slow qt event loop")
self.slowLoop.start(100)
logger.info("Starting very slow qt event loop")
self.verySlowLoop.start(200)
def stop(self):
logger.info("Stopping fast qt event loop")
self.fastLoop.stop()
logger.info("Stopping slow qt event loop")
self.slowLoop.stop()
logger.info("Stopping very slow qt event loop")
self.verySlowLoop.stop()

76
template/qtgui/mainwindow.py

@ -40,7 +40,9 @@ from .menu import Menu
from .resources import * from .resources import *
from .about import About from .about import About
from .helper import setPaletteAndFont from .helper import setPaletteAndFont
from .eventloop import EventLoop
from template.start import PATHS, qtApp from template.start import PATHS, qtApp
#Client modules #Client modules
from engine.config import * #imports METADATA from engine.config import * #imports METADATA
import engine.api as api #This loads the engine and starts a session. import engine.api as api #This loads the engine and starts a session.
@ -48,80 +50,6 @@ from qtgui.designer.mainwindow import Ui_MainWindow #The MainWindow designer fil
from qtgui.resources import * from qtgui.resources import *
from qtgui.constantsAndConfigs import constantsAndConfigs from qtgui.constantsAndConfigs import constantsAndConfigs
class EventLoop(object):
def __init__(self):
"""The loop for all things GUI and controlling the GUI (e.g. by a control midi in port)
directConnect is 0ms (see below)
fastConnect 20ms
slowConnect 100ms
verySlowConnect 200ms
0 ms means "if there is time". 10ms-20ms is smooth. 100ms is still ok.
Influences everything. Control Midi In Latency, playback cursor scrolling smoothnes etc.
But not realtime. This is not the realtime loop. Converting midi into instrument sounds
or playing back sequenced midi data is not handled by this loop at all.
Creating a non-qt class for the loop is an abstraction layer that enables the engine to
work without modification for non-gui situations. In this case it will use its own loop,
like python async etc.
A qt event loop needs the qt-app started. Otherwise it will not run.
We init the event loop outside of main but call start from the mainWindow.
"""
self.directLoop = QtCore.QTimer()
self.fastLoop = QtCore.QTimer()
self.slowLoop = QtCore.QTimer()
self.verySlowLoop = QtCore.QTimer()
def directConnect(self, function):
self.directLoop.timeout.connect(function)
def fastConnect(self, function):
self.fastLoop.timeout.connect(function)
def slowConnect(self, function):
self.slowLoop.timeout.connect(function)
def verySlowConnect(self, function):
self.verySlowLoop.timeout.connect(function)
def fastDisconnect(self, function):
"""The function must be the exact instance that was registered"""
self.fastLoop.timeout.disconnect(function)
def slowDisconnect(self, function):
"""The function must be the exact instance that was registered"""
self.slowLoop.timeout.disconnect(function)
def verySlowDisconnect(self, function):
"""The function must be the exact instance that was registered"""
self.verySlowLoop.timeout.disconnect(function)
def start(self):
"""The event loop MUST be started after the Qt Application instance creation"""
logger.info("Starting direct qt event loop")
self.directLoop.start(0)
logger.info("Starting fast qt event loop")
self.fastLoop.start(20)
logger.info("Starting slow qt event loop")
self.slowLoop.start(100)
logger.info("Starting very slow qt event loop")
self.verySlowLoop.start(200)
def stop(self):
logger.info("Stopping direct qt event loop")
self.directLoop.stop()
logger.info("Stopping fast qt event loop")
self.fastLoop.stop()
logger.info("Stopping slow qt event loop")
self.slowLoop.stop()
logger.info("Stopping very slow qt event loop")
self.verySlowLoop.stop()
api.session.eventLoop = EventLoop() api.session.eventLoop = EventLoop()
#QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_DontUseNativeMenuBar) #Force a real menu bar. Qt on wayland will not display it otherwise. #QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_DontUseNativeMenuBar) #Force a real menu bar. Qt on wayland will not display it otherwise.

32
template/qtgui/resources.py

@ -3320,9 +3320,9 @@ qt_resource_data = b"\
\xd0\x25\x00\x00\x08\xda\x00\x04\xd6\x8d\x00\x00\x0a\xe0\x00\x04\ \xd0\x25\x00\x00\x08\xda\x00\x04\xd6\x8d\x00\x00\x0a\xe0\x00\x04\
\xec\x30\x00\x00\x09\x0f\x00\x04\xf6\x35\x00\x00\x0b\x0b\x00\x05\ \xec\x30\x00\x00\x09\x0f\x00\x04\xf6\x35\x00\x00\x0b\x0b\x00\x05\
\x8b\xaf\x00\x00\x09\xaa\x00\x05\x98\xc5\x00\x00\x09\xeb\x00\x05\ \x8b\xaf\x00\x00\x09\xaa\x00\x05\x98\xc5\x00\x00\x09\xeb\x00\x05\
\xc4\xaf\x00\x00\x0a\x28\x00\x47\x96\xc4\x00\x00\x00\x00\x01\x7e\ \xc4\xaf\x00\x00\x0a\x28\x00\x47\x96\xc4\x00\x00\x00\x00\x00\x51\
\x97\x89\x00\x00\x04\x21\x03\x18\xa4\x29\x00\x00\x09\x44\x03\x19\ \x4a\x6e\x00\x00\x06\x95\x01\x7e\x97\x89\x00\x00\x04\x21\x03\x18\
\x9b\xde\x00\x00\x06\x95\x05\x1c\x51\x99\x00\x00\x06\x18\x05\x6c\ \xa4\x29\x00\x00\x09\x44\x05\x1c\x51\x99\x00\x00\x06\x18\x05\x6c\
\xc7\x3c\x00\x00\x0a\x67\x05\x6c\xc7\x3c\x00\x00\x0b\x3e\x07\x48\ \xc7\x3c\x00\x00\x0a\x67\x05\x6c\xc7\x3c\x00\x00\x0b\x3e\x07\x48\
\xba\x0e\x00\x00\x02\xff\x0c\x3f\xa2\x40\x00\x00\x05\xc5\x0d\x09\ \xba\x0e\x00\x00\x02\xff\x0c\x3f\xa2\x40\x00\x00\x05\xc5\x0d\x09\
\xd2\x63\x00\x00\x08\x44\x0d\x6c\xeb\x9f\x00\x00\x02\xa3\x0f\xc9\ \xd2\x63\x00\x00\x08\x44\x0d\x6c\xeb\x9f\x00\x00\x02\xa3\x0f\xc9\
@ -3443,7 +3443,7 @@ qt_resource_data = b"\
\x6d\x00\x70\x00\x66\x00\x6f\x00\x68\x00\x6c\x00\x65\x00\x6e\x00\ \x6d\x00\x70\x00\x66\x00\x6f\x00\x68\x00\x6c\x00\x65\x00\x6e\x00\
\x20\x00\x73\x00\x74\x00\x61\x00\x74\x00\x74\x00\x64\x00\x65\x00\ \x20\x00\x73\x00\x74\x00\x61\x00\x74\x00\x74\x00\x64\x00\x65\x00\
\x73\x00\x73\x00\x65\x00\x6e\x00\x20\x00\xfc\x00\x62\x00\x65\x00\ \x73\x00\x73\x00\x65\x00\x6e\x00\x20\x00\xfc\x00\x62\x00\x65\x00\
\x72\x00\x20\x00\x41\x00\x72\x00\x67\x00\x6f\x00\x64\x00\x65\x00\ \x72\x00\x20\x00\x41\x00\x67\x00\x6f\x00\x72\x00\x64\x00\x65\x00\
\x6a\x00\x6f\x00\x20\x00\x28\x00\x4e\x00\x65\x00\x77\x00\x20\x00\ \x6a\x00\x6f\x00\x20\x00\x28\x00\x4e\x00\x65\x00\x77\x00\x20\x00\
\x53\x00\x65\x00\x73\x00\x73\x00\x69\x00\x6f\x00\x6e\x00\x20\x00\ \x53\x00\x65\x00\x73\x00\x73\x00\x69\x00\x6f\x00\x6e\x00\x20\x00\
\x4d\x00\x61\x00\x6e\x00\x61\x00\x67\x00\x65\x00\x72\x00\x29\x00\ \x4d\x00\x61\x00\x6e\x00\x61\x00\x67\x00\x65\x00\x72\x00\x29\x00\
@ -3454,7 +3454,7 @@ qt_resource_data = b"\
\x6f\x75\x72\x20\x73\x65\x73\x73\x69\x6f\x6e\x20\x66\x69\x6c\x65\ \x6f\x75\x72\x20\x73\x65\x73\x73\x69\x6f\x6e\x20\x66\x69\x6c\x65\
\x73\x2e\x20\x49\x74\x20\x69\x73\x20\x72\x65\x63\x6f\x6d\x6d\x65\ \x73\x2e\x20\x49\x74\x20\x69\x73\x20\x72\x65\x63\x6f\x6d\x6d\x65\
\x6e\x64\x65\x64\x20\x74\x6f\x20\x73\x74\x61\x72\x74\x20\x74\x68\ \x6e\x64\x65\x64\x20\x74\x6f\x20\x73\x74\x61\x72\x74\x20\x74\x68\
\x72\x6f\x75\x67\x68\x20\x41\x72\x67\x6f\x64\x65\x6a\x6f\x2f\x4e\ \x72\x6f\x75\x67\x68\x20\x41\x67\x6f\x72\x64\x65\x6a\x6f\x2f\x4e\
\x65\x77\x20\x53\x65\x73\x73\x69\x6f\x6e\x20\x4d\x61\x6e\x61\x67\ \x65\x77\x20\x53\x65\x73\x73\x69\x6f\x6e\x20\x4d\x61\x6e\x61\x67\
\x65\x72\x20\x69\x6e\x73\x74\x65\x61\x64\x2e\x07\x00\x00\x00\x1e\ \x65\x72\x20\x69\x6e\x73\x74\x65\x61\x64\x2e\x07\x00\x00\x00\x1e\
\x54\x65\x6d\x70\x6c\x61\x74\x65\x43\x68\x6f\x6f\x73\x65\x53\x65\ \x54\x65\x6d\x70\x6c\x61\x74\x65\x43\x68\x6f\x6f\x73\x65\x53\x65\
@ -3523,14 +3523,14 @@ qt_resource_name = b"\
\x0d\xfc\x11\x13\ \x0d\xfc\x11\x13\
\x00\x74\ \x00\x74\
\x00\x72\x00\x61\x00\x6e\x00\x73\x00\x6c\x00\x61\x00\x74\x00\x69\x00\x6f\x00\x6e\x00\x73\ \x00\x72\x00\x61\x00\x6e\x00\x73\x00\x6c\x00\x61\x00\x74\x00\x69\x00\x6f\x00\x6e\x00\x73\
\x00\x0b\
\x09\xc1\xce\x26\
\x00\x65\
\x00\x75\x00\x74\x00\x65\x00\x72\x00\x70\x00\x65\x00\x2e\x00\x74\x00\x74\x00\x66\
\x00\x05\ \x00\x05\
\x00\x6f\xa6\x53\ \x00\x6f\xa6\x53\
\x00\x69\ \x00\x69\
\x00\x63\x00\x6f\x00\x6e\x00\x73\ \x00\x63\x00\x6f\x00\x6e\x00\x73\
\x00\x0b\
\x09\xc1\xce\x26\
\x00\x65\
\x00\x75\x00\x74\x00\x65\x00\x72\x00\x70\x00\x65\x00\x2e\x00\x74\x00\x74\x00\x66\
\x00\x0d\ \x00\x0d\
\x0d\x4c\x02\x07\ \x0d\x4c\x02\x07\
\x00\x77\ \x00\x77\
@ -3544,8 +3544,8 @@ qt_resource_name = b"\
qt_resource_struct_v1 = b"\ qt_resource_struct_v1 = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x02\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x02\
\x00\x00\x00\x50\x00\x02\x00\x00\x00\x01\x00\x00\x00\x06\ \x00\x00\x00\x34\x00\x02\x00\x00\x00\x01\x00\x00\x00\x06\
\x00\x00\x00\x34\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ \x00\x00\x00\x44\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x00\x16\x00\x02\x00\x00\x00\x01\x00\x00\x00\x05\ \x00\x00\x00\x16\x00\x02\x00\x00\x00\x01\x00\x00\x00\x05\
\x00\x00\x00\x80\x00\x00\x00\x00\x00\x01\x00\x00\xce\x32\ \x00\x00\x00\x80\x00\x00\x00\x00\x00\x01\x00\x00\xce\x32\
\x00\x00\x00\x60\x00\x00\x00\x00\x00\x01\x00\x00\xc6\xb4\ \x00\x00\x00\x60\x00\x00\x00\x00\x00\x01\x00\x00\xc6\xb4\
@ -3556,16 +3556,16 @@ qt_resource_struct_v2 = b"\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x02\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x02\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x50\x00\x02\x00\x00\x00\x01\x00\x00\x00\x06\ \x00\x00\x00\x34\x00\x02\x00\x00\x00\x01\x00\x00\x00\x06\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x34\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ \x00\x00\x00\x44\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x01\x72\xce\x17\xcb\xdb\ \x00\x00\x01\x73\x80\x8b\x3f\x01\
\x00\x00\x00\x16\x00\x02\x00\x00\x00\x01\x00\x00\x00\x05\ \x00\x00\x00\x16\x00\x02\x00\x00\x00\x01\x00\x00\x00\x05\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x80\x00\x00\x00\x00\x00\x01\x00\x00\xce\x32\ \x00\x00\x00\x80\x00\x00\x00\x00\x00\x01\x00\x00\xce\x32\
\x00\x00\x01\x72\xf1\xfa\xc2\xb1\ \x00\x00\x01\x73\x80\x8c\x2c\x95\
\x00\x00\x00\x60\x00\x00\x00\x00\x00\x01\x00\x00\xc6\xb4\ \x00\x00\x00\x60\x00\x00\x00\x00\x00\x01\x00\x00\xc6\xb4\
\x00\x00\x01\x72\xce\x17\xcb\xd8\ \x00\x00\x01\x73\x80\x8b\x3e\xfe\
" "
qt_version = [int(v) for v in QtCore.qVersion().split('.')] qt_version = [int(v) for v in QtCore.qVersion().split('.')]

BIN
template/qtgui/resources/translations/de.qm

Binary file not shown.

6
template/qtgui/resources/translations/de.ts

@ -37,14 +37,14 @@
<context> <context>
<name>TemplateChooseSessionDirectory</name> <name>TemplateChooseSessionDirectory</name>
<message> <message>
<location filename="../../chooseSessionDirectory.py" line="91"/> <location filename="../../chooseSessionDirectory.py" line="95"/>
<source>Choose Session Directory</source> <source>Choose Session Directory</source>
<translation>Sessionverzeichnis wählen</translation> <translation>Sessionverzeichnis wählen</translation>
</message> </message>
<message> <message>
<location filename="../../chooseSessionDirectory.py" line="69"/> <location filename="../../chooseSessionDirectory.py" line="69"/>
<source>Please choose a directory for your session files. It is recommended to start through Argodejo/New Session Manager instead.</source> <source>Please choose a directory for your session files. It is recommended to start through Agordejo/New Session Manager instead.</source>
<translation>Bitte wählen Sie ein Sessionverzeichnis. Darüberhinaus wird empfohlen stattdessen über Argodejo (New Session Manager) zu starten.</translation> <translation>Bitte wählen Sie ein Sessionverzeichnis. Darüberhinaus wird empfohlen stattdessen über Agordejo (New Session Manager) zu starten.</translation>
</message> </message>
</context> </context>
<context> <context>

2
template/start.py

@ -44,7 +44,7 @@ nothing gets loaded.
import argparse import argparse
parser = argparse.ArgumentParser(description=f"""{METADATA["name"]} - Version {METADATA["version"]} - Copyright {METADATA["year"]} by {METADATA["author"]} - {METADATA["url"]}""") parser = argparse.ArgumentParser(description=f"""{METADATA["name"]} - Version {METADATA["version"]} - Copyright {METADATA["year"]} by {METADATA["author"]} - {METADATA["url"]}""")
parser.add_argument("-v", "--version", action='version', version="{} {}".format(METADATA["name"], METADATA["version"])) parser.add_argument("-v", "--version", action='version', version="{} {}".format(METADATA["name"], METADATA["version"]))
parser.add_argument("-s", "--save", action='store', dest="directory", help="Use this directory to save. Will be created or loaded from if already present. Deactivates Argodejo/New-Session-Manager support.") parser.add_argument("-s", "--save", action='store', dest="directory", help="Use this directory to save. Will be created or loaded from if already present. Deactivates Agordejo/New-Session-Manager support.")
parser.add_argument("-p", "--profiler", action='store_true', help="(Development) Run the python profiler and produce a .cprof file at quit. The name will appear in your STDOUT.") parser.add_argument("-p", "--profiler", action='store_true', help="(Development) Run the python profiler and produce a .cprof file at quit. The name will appear in your STDOUT.")
parser.add_argument("-m", "--mute", action='store_true', help="(Development) Use a fake cbox module, effectively deactivating midi and audio.") parser.add_argument("-m", "--mute", action='store_true', help="(Development) Use a fake cbox module, effectively deactivating midi and audio.")
parser.add_argument("-V", "--verbose", action='store_true', help="(Development) Switch the logger to INFO and print out all kinds of information to get a high-level idea of what the program is doing.") parser.add_argument("-V", "--verbose", action='store_true', help="(Development) Switch the logger to INFO and print out all kinds of information to get a high-level idea of what the program is doing.")

Loading…
Cancel
Save