1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 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)
|