#! /usr/bin/env python3 # -*- coding: utf-8 -*- """ Copyright 2019, Nils Hilbricht, Germany ( https://www.hilbricht.net ) This file is part of the Laborejo Software Suite ( https://www.laborejo.org ), more specifically its template base application. The Template Base 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 . """ import logging; logging.info("import {}".format(__file__)) #Python Standard Library #Third Party Modules #Template Modules from template.engine.input_midi import MidiInput #Our Modules import engine.api as api class StepMidiInput(MidiInput): """We initialize with everything switched off. This makes it easier for a complex system of engine, GUI and midiinput to start up and get the order of operations right For chord entry we use the first note only as indicator. The indicator was a set() at one time, which means that as long as one of the chord notes would still be down the chord was still "on". That is not as robust and convenient as using the starting note, which is counter intuitive, therefore documented here. """ def __init__(self): self.firstActiveNote = None #for chord entry. self._currentlyActiveNotes = set() def start(self): """Call this manually after the engine and an event loop have started. For example from the GUI. It is currently started by mainwindow.py start() But it could be started from a simple command line interface as well.""" assert api.laborejoEngineStarted super().__init__(session=api.session, portName="in") self.midiProcessor.active = False #specific to Laborejo #Connect the template midi input with Laborejo api calls. #self.midiProcessor.notePrinter(True) self.midiProcessor.register_NoteOn(self._insertMusicItemFromMidi) self.midiProcessor.register_NoteOff(self._pop) api.callbacks.setCursor.append(self._setMidiThru) #When the track changes re-route cbox RT midi thru @property def midiInIsActive(self): try: return self.midiProcessor.active except AttributeError: #during startupt return False def _insertMusicItemFromMidi(self, channel, midipitch, velocity): if self._currentlyActiveNotes: #Chord api.left() keysig = api.session.data.currentTrack().state.keySignature() pitchToInsert = api.pitchmath.fromMidi(midipitch, keysig) api.addNoteToChord(pitchToInsert) api.right() else: #Single note baseDuration = api.session.data.cursor.prevailingBaseDuration keysig = api.session.data.currentTrack().state.keySignature() pitchToInsert = api.pitchmath.fromMidi(midipitch, keysig) api.insertChord(baseDuration, pitchToInsert) self._currentlyActiveNotes.add(midipitch) def _pop(self, channel, midipitch, velocity): self._currentlyActiveNotes.remove(midipitch) def setMidiInputActive(self, state:bool): self.midiProcessor.active = state api.callbacks._prevailingBaseDurationChanged(api.session.data.cursor.prevailingBaseDuration) def toggleMidiIn(self): self.setMidiInputActive(not self.midiInIsActive) def _setMidiThru(self, cursorExport): """We don't need to react to deleted tracks because that does reset the cursor. The template midi in does _not_ check if the routed output ports still exist. however, that is a low risk state that needs changes in the program""" self.setMidiThru(cursorExport["cboxMidiOutUuid"]) stepMidiInput = StepMidiInput() #global to use in other parts of Laborejo