Quisk version 4.2.28

This commit is contained in:
James C. Ahlstrom
2023-12-26 15:39:37 -05:00
commit ffa047c2ef
143 changed files with 52434 additions and 0 deletions
+161
View File
@@ -0,0 +1,161 @@
# This module implements Midi processing in Quisk. The OnReadMIDI() method is called for any Midi bytes
# received. Do not change this file. If you want to replace it with your own Midi handler, create a
# configuration file, copy this file into it and make any changes there. Quisk will use your configuration
# file MidiHandler instead of this one.
# Midi messages are generally three bytes long. The first byte is the status and has the most significant bit set.
# Subsequent bytes have the most significant bit zero. The status byte of a channel message is a 1 bit, three bits of message
# type and 4 bits of channel number. This is followed by two bytes of data.
# For Note On (status 0x9?) and Note Off (status 0x8?) the data bytes are the note number and velocity. Velocity
# indicates how hard the key was pressed. If the velocity of a Note On message is zero it is treated the same as Note Off.
# For a control change (status = 0xB?) the data bytes are the controller number and the controller value. For some controllers
# it only matters if the value is less than 64 (down) or 64 or greater (up). For other controllers the value 0 to 127
# is the actual control setting.
import traceback
class MidiHandler: # Quisk calls this to make the Midi handler instance.
tune_speed = {0:10, 1:20, 2:50, 3:100, 4:200, 5:500, 6:1000, 7:2000, 8:5000, 9:10000}
slider_speed = {0:1, 1:2, 2:3, 3:5, 4:7, 5:9, 6:12, 7:15, 8:18, 9:22}
def __init__(self, app, conf):
self.app = app # The application object
self.conf = conf # The configuration settings
self.midi_message = [] # Save Midi bytes until a whole message is received.
def OnReadMIDI(self, byts): # Quisk calls this for any Midi bytes received.
for byt in byts:
if byt & 0x80: # this is a status byte and the start of a new message
self.midi_message = [byt]
else:
self.midi_message.append(byt)
if len(self.midi_message) == 3:
#print ("0x%2X%02X %d" % tuple(self.midi_message))
status = self.midi_message[0]
status = status & 0xF0 # Ignore channel
if status == 0x90: # Note On
if self.midi_message[2] == 0: # Note On with zero velocity is the same as Note Off
self.NoteOff()
else:
self.NoteOn()
elif status == 0x80: # Note Off
self.NoteOff()
elif status == 0xB0: # Control Change
try:
name = self.app.local_conf.MidiNoteDict["0x%02X%02X" % (self.midi_message[0], self.midi_message[1])]
except:
pass
#traceback.print_exc()
else:
if len(name) > 3 and name[-3] == " " and name[-2] in "+-" and name[-1] in "0123456789":
self.JogWheel(name)
else:
self.ControlKnob(name)
def NoteOn(self):
try:
name = self.app.local_conf.MidiNoteDict["0x%02X%02X" % (self.midi_message[0], self.midi_message[1])]
btn = self.app.idName2Button[name]
except:
return
if btn.idName == 'PTT' and not self.conf.midi_ptt_toggle:
btn.SetValue(True, True)
else:
btn.Shortcut(None, name)
def NoteOff(self):
try: # Look up the Note On name
name = self.app.local_conf.MidiNoteDict["0x9%X%02X" % (self.midi_message[0] & 0xF, self.midi_message[1])]
btn = self.app.idName2Button[name]
except:
return
if hasattr(btn, "repeat_state"): # This is a QuiskRepeatbutton
btn.Shortcut(None, "_end_")
elif btn.idName == 'PTT' and not self.conf.midi_ptt_toggle:
btn.SetValue(False, True)
def ControlKnob(self, name):
if self.midi_message[2] == 64: # Mid control
dec_value = 0.5
else:
dec_value = self.midi_message[2] / 127.0
if name == "Tune":
tune = self.app.sample_rate * (dec_value - 0.5) * 0.98
tune = int(tune)
self.app.ChangeHwFrequency(tune, self.app.VFO, 'FreqEntry')
elif name == "Rit": # Offset values by the CW tone frequency
ctrl, func = self.app.midiControls[name]
value = self.midi_message[2] - 64 # Center value
if self.app.mode == 'CWU':
offset = - self.conf.cwTone
value = value * 1000 // 63 + offset
elif self.app.mode == 'CWL':
offset = self.conf.cwTone
value = value * 1000 // 63 + offset
else:
offset = 0
value = value * 2000 // 63 + offset
if value < ctrl.themin:
value = ctrl.themin
elif value > ctrl.themax:
value = ctrl.themax
ctrl.SetValue(value)
if self.app.remote_control_head:
self.app.Hardware.RemoteCtlSend(f'{ctrl.idName};{ctrl.GetValue()}\n')
func()
elif name in self.app.midiControls:
ctrl, func = self.app.midiControls[name]
if ctrl:
ctrl.SetDecValue(dec_value, False)
if self.app.remote_control_head:
self.app.Hardware.RemoteCtlSend(f'{ctrl.idName};{ctrl.GetValue()}\n')
func()
else: # Try to treat as Note On/Off
try:
btn = self.app.idName2Button[name]
except:
return
if self.midi_message[2] == 0: # Note On with zero velocity is the same as Note Off
if hasattr(btn, "repeat_state"): # This is a QuiskRepeatbutton
btn.Shortcut(None, "_end_")
else: # Note On
btn.Shortcut(None, name)
def JogWheel(self, name):
speed = int(name[-1])
if name[-2] == '+':
direction = +1
else:
direction = -1
name = name[0:-3]
if name == "Tune":
freq = self.app.txFreq + self.app.VFO
delta = self.tune_speed[speed]
if self.midi_message[2] < 64:
freq += direction * delta
else:
freq -= direction * delta
freq = ((freq + delta // 2) // delta) * delta
tune = freq - self.app.VFO
d = self.app.sample_rate * 45 // 100
if -d <= tune <= d: # Frequency is on-screen
vfo = self.app.VFO
else: # Change the VFO
vfo = (freq // 5000) * 5000 - 5000
tune = freq - vfo
self.app.ChangeHwFrequency(tune, vfo, 'FreqEntry')
elif name in self.app.midiControls:
ctrl, func = self.app.midiControls[name]
self.AdjSlider(ctrl, direction, speed)
if self.app.remote_control_head:
self.app.Hardware.RemoteCtlSend(f'{ctrl.idName};{ctrl.GetValue()}\n')
func()
else:
pass #print ("Unknown jog name", name)
def AdjSlider(self, ctrl, direction, speed):
value = ctrl.GetValue()
if self.midi_message[2] < 64:
value += direction * self.slider_speed[speed]
else:
value -= direction * self.slider_speed[speed]
if value < ctrl.themin:
value = ctrl.themin
elif value > ctrl.themax:
value = ctrl.themax
ctrl.SetValue(value)