Compare commits
No commits in common. "b17b26a326d0c93759faffb58fa6201103a9f125" and "f298edbb8195e3b2148432965edcc2d96a0ae923" have entirely different histories.
b17b26a326
...
f298edbb81
1 changed files with 37 additions and 62 deletions
99
mixer.py
99
mixer.py
|
@ -5,46 +5,16 @@ import socket
|
||||||
import mido
|
import mido
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
# For Allen & Heath Qu MIDI protocol documentation see:
|
# For Allen & Heath Qu MIDI protocol documentation see:
|
||||||
# https://www.allen-heath.com/content/uploads/2023/06/Qu_MIDI_Protocol_V1.9.pdf
|
# https://www.allen-heath.com/content/uploads/2023/06/Qu_MIDI_Protocol_V1.9.pdf
|
||||||
|
|
||||||
|
|
||||||
class Mixer:
|
class Mixer:
|
||||||
@dataclass
|
|
||||||
class SystemState:
|
|
||||||
class QuModel(Enum):
|
|
||||||
QU16 = 1
|
|
||||||
QU24 = 2
|
|
||||||
QU32 = 3
|
|
||||||
QUPAC = 4
|
|
||||||
QUSB = 5
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
labels = {
|
|
||||||
1: "Qu-16",
|
|
||||||
2: "Qu-24",
|
|
||||||
3: "Qu-32",
|
|
||||||
4: "Qu-Pac",
|
|
||||||
5: "Qu-SB",
|
|
||||||
}
|
|
||||||
|
|
||||||
return labels[self.value]
|
|
||||||
|
|
||||||
sys_ex_header : list
|
|
||||||
midi_channel : int
|
|
||||||
id : int
|
|
||||||
model : QuModel
|
|
||||||
major_ver : int
|
|
||||||
minor_ver : int
|
|
||||||
|
|
||||||
def __init__(self, ip):
|
def __init__(self, ip):
|
||||||
self.MIXER_PORT = 51325
|
self.MIXER_PORT = 51325
|
||||||
self.sock = socket.create_connection((ip, self.MIXER_PORT))
|
self.sock = socket.create_connection((ip, self.MIXER_PORT))
|
||||||
self.mido_parser = mido.Parser()
|
self.mido_parser = mido.Parser()
|
||||||
self.qu_midi_channel = self.get_system_state().midi_channel
|
|
||||||
self.daw_midi_channel = self.qu_midi_channel + 1
|
|
||||||
|
|
||||||
ALLEN_HEATH_ID = [0x00, 0x00, 0x1A]
|
ALLEN_HEATH_ID = [0x00, 0x00, 0x1A]
|
||||||
QU_MIXER = [0x50, 0x11]
|
QU_MIXER = [0x50, 0x11]
|
||||||
|
@ -91,7 +61,7 @@ class Mixer:
|
||||||
|
|
||||||
def get_system_state(self):
|
def get_system_state(self):
|
||||||
msg_id = self.SysExMessageId.GET_SYSTEM_STATE_REQUEST.value
|
msg_id = self.SysExMessageId.GET_SYSTEM_STATE_REQUEST.value
|
||||||
i_pad_flag = 0x00
|
i_pad_flag = 0x01
|
||||||
|
|
||||||
data = self.SYSEX_ALL_CALL + [msg_id, i_pad_flag]
|
data = self.SYSEX_ALL_CALL + [msg_id, i_pad_flag]
|
||||||
|
|
||||||
|
@ -103,18 +73,37 @@ class Mixer:
|
||||||
|
|
||||||
response = self.recv_sys_ex(self.SysExMessageId.GET_SYSTEM_STATE_RESPONSE)
|
response = self.recv_sys_ex(self.SysExMessageId.GET_SYSTEM_STATE_RESPONSE)
|
||||||
|
|
||||||
|
class QuModel(Enum):
|
||||||
|
QU16 = 1
|
||||||
|
QU24 = 2
|
||||||
|
QU32 = 3
|
||||||
|
QUPAC = 4
|
||||||
|
QUSB = 5
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
labels = {
|
||||||
|
1: "Qu-16",
|
||||||
|
2: "Qu-24",
|
||||||
|
3: "Qu-32",
|
||||||
|
4: "Qu-Pac",
|
||||||
|
5: "Qu-SB",
|
||||||
|
}
|
||||||
|
|
||||||
|
return labels[self.value]
|
||||||
|
|
||||||
|
sysex_header = response.data[:8]
|
||||||
|
|
||||||
|
midi_channel = int(response.data[7])
|
||||||
|
id = int(response.data[8])
|
||||||
i_pad_flag = int(response.data[9])
|
i_pad_flag = int(response.data[9])
|
||||||
|
major_ver = int(response.data[10])
|
||||||
|
minor_ver = int(response.data[11])
|
||||||
|
|
||||||
state = self.SystemState(
|
print(f"sysex_header: {sysex_header}")
|
||||||
sys_ex_header=response.data[:8],
|
print(f"MIDI channel: {midi_channel}")
|
||||||
midi_channel=int(response.data[7]),
|
print(f"ID: 0x{id:02X}")
|
||||||
id=int(response.data[8]),
|
print(f"Model: {QuModel(i_pad_flag)}")
|
||||||
model=self.SystemState.QuModel(i_pad_flag),
|
print(f"Firmware Version: {major_ver}.{minor_ver}")
|
||||||
major_ver=int(response.data[10]),
|
|
||||||
minor_ver = int(response.data[11])
|
|
||||||
)
|
|
||||||
|
|
||||||
return state
|
|
||||||
|
|
||||||
def get_name_from_qu(self, channel_no, name_to_set):
|
def get_name_from_qu(self, channel_no, name_to_set):
|
||||||
msg_id = self.SysExMessageId.GET_NAME_FROM_QU_REQUEST.value
|
msg_id = self.SysExMessageId.GET_NAME_FROM_QU_REQUEST.value
|
||||||
|
@ -157,17 +146,11 @@ class Mixer:
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
self.nrpn_parameter_control(midi_ch=0, mixer_ch=0, id=0x5F, va=0x00, vx=0x00)
|
self.nrpn_parameter_control(midi_ch=0, mixer_ch=0, id=0x5F, va=0x00, vx=0x00)
|
||||||
|
|
||||||
def set_bank(self, bank_no):
|
def set_bank_1(self):
|
||||||
if bank_no < 1 or bank_no > 16384:
|
msb = mido.Message("control_change", channel=0, control=0x00, value=0x00)
|
||||||
raise ValueError(f"bank_no param must be between 0 and 16384, got {bank_no}")
|
lsb = mido.Message("control_change", channel=0, control=0x20, value=0x00)
|
||||||
|
|
||||||
msb_val = ((bank_no - 1) >> 7) & 0xFF
|
msg_bytes = bytes(msb.bytes() + lsb.bytes())
|
||||||
lsb_val = (bank_no - 1) & 0x7F
|
|
||||||
|
|
||||||
control_change_msb = mido.Message("control_change", channel=self.qu_midi_channel, control=0x00, value=msb_val)
|
|
||||||
control_change_lsb = mido.Message("control_change", channel=self.qu_midi_channel, control=0x20, value=lsb_val)
|
|
||||||
|
|
||||||
msg_bytes = bytes(control_change_msb.bytes() + control_change_lsb.bytes())
|
|
||||||
|
|
||||||
print(" ".join(f"{b:02X}" for b in msg_bytes))
|
print(" ".join(f"{b:02X}" for b in msg_bytes))
|
||||||
self.sock.sendall(msg_bytes)
|
self.sock.sendall(msg_bytes)
|
||||||
|
@ -175,8 +158,8 @@ class Mixer:
|
||||||
def scene_recall(self, scene_id):
|
def scene_recall(self, scene_id):
|
||||||
print(f"scene_recall: scene_id={scene_id}")
|
print(f"scene_recall: scene_id={scene_id}")
|
||||||
|
|
||||||
self.set_bank(1)
|
self.set_bank_1()
|
||||||
msb = mido.Message("program_change", channel=self.qu_midi_channel, program=scene_id)
|
msb = mido.Message("program_change", channel=0, program=scene_id)
|
||||||
|
|
||||||
msg_bytes = bytes(msb.bytes())
|
msg_bytes = bytes(msb.bytes())
|
||||||
|
|
||||||
|
@ -223,15 +206,7 @@ def main():
|
||||||
|
|
||||||
match args.command:
|
match args.command:
|
||||||
case "get_name_from_qu": mixer.get_name_from_qu(args.channel_id, args.name)
|
case "get_name_from_qu": mixer.get_name_from_qu(args.channel_id, args.name)
|
||||||
case "get_system_state":
|
case "get_system_state": mixer.get_system_state()
|
||||||
state = mixer.get_system_state()
|
|
||||||
|
|
||||||
print(f"sysex_header: {state.sys_ex_header}")
|
|
||||||
print(f"MIDI channel: {state.midi_channel}")
|
|
||||||
print(f"ID: 0x{state.id:02X}")
|
|
||||||
print(f"Model: {state.model}")
|
|
||||||
print(f"Firmware Version: {state.major_ver}.{state.minor_ver}")
|
|
||||||
|
|
||||||
case "shutdown": mixer.shutdown()
|
case "shutdown": mixer.shutdown()
|
||||||
case "scene_recall": mixer.scene_recall(args.scene_number)
|
case "scene_recall": mixer.scene_recall(args.scene_number)
|
||||||
case "scene_recall_default": mixer.scene_recall(0)
|
case "scene_recall_default": mixer.scene_recall(0)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue