You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

125 lines
5.2 KiB

6 years ago
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2022, Nils Hilbricht, Germany ( https://www.hilbricht.net )
6 years ago
This file is part of the Laborejo Software Suite ( https://www.laborejo.org ),
more specifically its template base application.
This application is free software: you can redistribute it and/or modify
6 years ago
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; 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
"""
This file is used directly. It is a global part of the engine (see bottom of the file)
that uses the api directly, which in turn triggers the GUI.
"""
6 years ago
class StepMidiInput(MidiInput):
6 years ago
"""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
6 years ago
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.
Up until 2022 we used the midi in active toggle just on/off. So the template bypass.
But now we never switch midi processing off, we just reroute input vs. setting the pitch cursor
6 years ago
"""
def __init__(self):
#No super init in here! This is delayed until self.start
self.firstActiveNote = None #for chord entry.
self._currentlyActiveNotes = set()
self.ready = False #Program start
self.inputitemTrueCursorpitchFalse = False #start with just
6 years ago
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."""
6 years ago
assert api.laborejoEngineStarted
super().__init__(session=api.session, portName="in")
self.midiProcessor.active = True #never off.
self.ready = True #Program start
#Connect the template midi input with Laborejo api calls.
6 years ago
#self.midiProcessor.notePrinter(True)
self.midiProcessor.register_NoteOn(self._insertMusicItemFromMidi)
self.midiProcessor.register_NoteOff(self._pop)
6 years ago
api.callbacks.setCursor.append(self._setMidiThru) #When the track changes re-route cbox RT midi thru
api.callbacks._setCursor(destroySelection = False) #Force once to trigger a cursor export which calls our midi thru setter
6 years ago
@property
def midiInIsActive(self):
"""
6 years ago
try:
return self.midiProcessor.active
except AttributeError: #during startup
6 years ago
return False
"""
return self.inputitemTrueCursorpitchFalse
def _insertMusicItemFromMidi(self, timeStamp, channel, midipitch, velocity):
keysig = api.session.data.currentTrack().state.keySignature()
pitchToInsert = api.pitchmath.fromMidi(midipitch, keysig)
if self.inputitemTrueCursorpitchFalse:
if self._currentlyActiveNotes: #Chord
api.left()
api.addNoteToChord(pitchToInsert)
api.right()
else: #Single note
baseDuration = api.session.data.cursor.prevailingBaseDuration
api.insertChord(baseDuration, pitchToInsert)
else:
api.session.data.cursor.cursorWasMovedAfterChoosingPitchViaMidi = False
api.toPitch(pitchToInsert)
self._currentlyActiveNotes.add(midipitch) #This needs to be at the end of the function for chord-detection to work
def _pop(self, timeStamp, channel, midipitch, velocity):
self._currentlyActiveNotes.remove(midipitch)
6 years ago
def setMidiInputActive(self, state:bool):
#self.midiProcessor.active = state
self.inputitemTrueCursorpitchFalse = state
6 years ago
api.callbacks._prevailingBaseDurationChanged(api.session.data.cursor.prevailingBaseDuration)
def toggleMidiIn(self):
#self.setMidiInputActive(not self.midiInIsActive)
self.setMidiInputActive(not self.inputitemTrueCursorpitchFalse)
6 years ago
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"])
self.setMidiThruChannel(cursorExport["midiChannel"]+1) #cursor midi channel is 0 based
6 years ago
stepMidiInput = StepMidiInput() #global to use in other parts of Laborejo