mute parameters
48
CQ-20B.org
Normal file
|
@ -0,0 +1,48 @@
|
|||
# -*- mode: org; coding: utf-8; -*-
|
||||
#+LaTeX_CLASS: zzkt-article
|
||||
#+LateX_Header: \setcounter{secnumdepth}{0}
|
||||
#+OPTIONS: toc:2 num:nil html-style:nil
|
||||
#+author:
|
||||
#+title: Allen & Heath CQ-20B
|
||||
|
||||
based on MIDI Protocol V1.2 Issue 2
|
||||
|
||||
[[file:img/CQ-20B.png]]
|
||||
|
||||
The CQ uses MIDI Channel 1 for all control messaging.
|
||||
|
||||
* Available Controls
|
||||
** Scene change
|
||||
** Soft Keys
|
||||
** Mutes
|
||||
** Levels
|
||||
** Panning/Balance
|
||||
** Getting values
|
||||
* Reference Tables
|
||||
|
||||
** Decimal, HEX, Note conversions
|
||||
|
||||
[[file:img/cq_ref_1.png]]
|
||||
|
||||
** Soft Key Note and Hex values
|
||||
|
||||
[[file:img/cq_ref_2.png]]
|
||||
|
||||
** Mutes Parameter Numbers
|
||||
|
||||
[[file:img/cq_ref_3.png]]
|
||||
|
||||
** Level Parameter Numbers — Inputs and FX to Outputs and FX
|
||||
|
||||
[[file:img/cq_ref_4.png]]
|
||||
|
||||
** Level Parameter Numbers — Outputs, FX unit input and DCAs
|
||||
|
||||
[[file:img/cq_ref_5.png]]
|
||||
|
||||
** Pan Balance Parameter Numbers — Inputs and FX to Main LR and Outputs
|
||||
|
||||
[[file:img/cq_ref_6.png]]
|
||||
|
||||
* Further
|
||||
- CQ MIDI Protocol
|
14
README.org
Normal file
|
@ -0,0 +1,14 @@
|
|||
# -*- mode: org; coding: utf-8; -*-
|
||||
#+title: OSC bridge(s) for A&H Equipment
|
||||
|
||||
* XONE:K2
|
||||
“Xone:K2 is a compact, slim line universal MIDI controller – incorporating a 4 channel soundcard, for use with any DJ software.”
|
||||
- MIDI controller
|
||||
- output OSC
|
||||
- https://www.allen-heath.com/hardware/xone-series/xonek2/
|
||||
|
||||
* CQ-20B
|
||||
“Ultra-Compact 20-in / 8-out Digital Mixer with Wi-Fi”
|
||||
- Digital mixer/stagebox
|
||||
- config, control & monitoring via OSC
|
||||
- https://www.allen-heath.com/hardware/cq/cq-20b/
|
260
XONE-K2.org
Normal file
|
@ -0,0 +1,260 @@
|
|||
# -*- mode: org; coding: utf-8; -*-
|
||||
#+LaTeX_CLASS: zzkt-article
|
||||
#+LateX_Header: \setcounter{secnumdepth}{0}
|
||||
#+OPTIONS: toc:2 num:nil html-style:nil
|
||||
#+title: XONE:K2
|
||||
|
||||
file:img/XONE-K2-squared.jpg
|
||||
|
||||
* install & setup
|
||||
|
||||
requirements
|
||||
#+BEGIN_SRC shell :dir :wrap SRC text :results raw
|
||||
python -m pip install oscpy mido python-rtmidi argparse
|
||||
#+END_SRC
|
||||
|
||||
make it go
|
||||
#+BEGIN_SRC shell :dir :wrap SRC text :results raw
|
||||
python k2-misc.py
|
||||
#+END_SRC
|
||||
|
||||
usage
|
||||
#+BEGIN_SRC text
|
||||
usage: k2-misc [-h] [--host OSC-host] [--port OSC-port] [--midi MIDI-port]
|
||||
[-v verbose] [-q quiet]
|
||||
|
||||
MIDI->OSC bridge for Allen & Heath XONE:K2 controller
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--host OSC-host hostname or address of OSC destination
|
||||
--port OSC-port port for OSC messages
|
||||
--midi MIDI-port port name of MIDI device
|
||||
-v verbose
|
||||
-q quiet
|
||||
#+END_SRC
|
||||
|
||||
defaults
|
||||
#+BEGIN_SRC shell :dir :wrap SRC text :results raw
|
||||
python k2-misc.py --host "127.0.0.1" --port 5111 --midi 'XONE:K2'
|
||||
#+END_SRC
|
||||
|
||||
* Overview
|
||||
|
||||
* Physical layout
|
||||
|
||||
XONE:K2 Publication AP8509
|
||||
|
||||
** Rotary Encoders
|
||||
|
||||
Turning an encoder produces MIDI CC (continuous controller) messages with a unique controller number in two’s compliment binary encoding These encoders feature a built in momentary push switch. Pressing down on the encoder knob activates the switch and sends a “Note On” MIDI message, releasing the switch sends a corresponding “Note Off” message. The window below the top row of encoders can be used to display the state of the encoder switch in the same manner as the other switches.
|
||||
|
||||
** Rotary Potentiometers
|
||||
|
||||
These controls are standard potentiometers with end stops. Turning a pot from left to right will send MIDI messages with a unique CC number and a control value from 0 to 127.
|
||||
|
||||
** Pot Switches
|
||||
|
||||
Each rotary potentiometer has a switch with tri-colour illumination below it.
|
||||
|
||||
** Linear Faders
|
||||
|
||||
Moving a linear fader will send a MIDI message with a unique CC number and a control value from 0 (bottom) to 127 (top).
|
||||
|
||||
** Switch Matrix
|
||||
|
||||
The switch matrix consists of 16 back-lit tri-colour switches.
|
||||
|
||||
** Layer Button
|
||||
|
||||
The Layer button is completely user assignable but can also function as an embedded layer button.
|
||||
|
||||
* OSC layout & mapping
|
||||
|
||||
Physical controls are numbered L->R and top->down
|
||||
|
||||
[[file:img/K2-layout.png]]
|
||||
** Rotary potentiometers
|
||||
|
||||
Rotary potentiometers are numbered 1-12 (soft sync/pickup?) and send value 0-127 when turned, and =pressed= / =released= messages are sent when knobs are pressed (and released).
|
||||
|
||||
#+BEGIN_SRC text
|
||||
/xone/k2/rp/<n> <int>
|
||||
/xone/k2/rp/<n>/pressed
|
||||
/xone/k2/rp/<n>/released
|
||||
#+END_SRC
|
||||
|
||||
** Rotary encoders
|
||||
|
||||
Rotary encoders on the top row are numbered 1-4 and the bottom row are numbered 5 and 6. Encoders send =inc= messages when turned right (clockwise) or =dec= when lurned left (widdershins). While turned & pressed, the encoder sends =inc-fine= and =dec-fine= messages.
|
||||
|
||||
#+BEGIN_SRC text
|
||||
/xone/k2/re/<n>/inc
|
||||
/xone/k2/re/<n>/dec
|
||||
/xone/k2/re/<n>/inc-fine
|
||||
/xone/k2/re/<n>/dec-fine
|
||||
#+END_SRC
|
||||
|
||||
** Linear faders
|
||||
|
||||
Linear faders are numbered 1-4 and send values from 0 (fully down) up to 127 (fully up).
|
||||
#+BEGIN_SRC text
|
||||
/xone/k2/fader/<n>/value <int>
|
||||
#+END_SRC
|
||||
|
||||
** Buttons
|
||||
|
||||
The upper block of buttons (above the faders) are numbered from 1-12 and the lower block (grid below the faders) are named =A-P=, =LAYER=, and =SHIFT= as labled)
|
||||
|
||||
#+BEGIN_SRC text
|
||||
/xone/k2/button/<name>/pressed
|
||||
/xone/k2/button/<name>/released
|
||||
#+END_SRC
|
||||
|
||||
set button colour (not yet implemented)
|
||||
#+BEGIN_SRC text
|
||||
/xone/k2/button/<name>/set <colour>
|
||||
<colour> = red, orange, green, off (string)
|
||||
#+END_SRC
|
||||
|
||||
* MIDI layout (MIDI IMPLEMENTATION SEND / RETURN)
|
||||
|
||||
By default the MIDI Channel number is set to 15 (14) to prevent control interaction with Xone DB series mixers which default to channel 16 (15).
|
||||
|
||||
[[file:img/XONE-K2-layers.jpg]]
|
||||
|
||||
* MIDI NOTE IMPLEMENTATION TABLE
|
||||
|
||||
| DEC | HEX | NOTE |
|
||||
| 0 | 00 | C-1 |
|
||||
| 1 | 01 | C#-1 |
|
||||
| 2 | 02 | D-1 |
|
||||
| 3 | 03 | D#-1 |
|
||||
| 4 | 04 | E-1 |
|
||||
| 5 | 05 | F-1 |
|
||||
| 6 | 06 | F#-1 |
|
||||
| 7 | 07 | G-1 |
|
||||
| 8 | 08 | G#-1 |
|
||||
| 9 | 09 | A-1 |
|
||||
| 10 | 0A | A#-1 |
|
||||
| 11 | 0B | B-1 |
|
||||
| 12 | 0C | C0 |
|
||||
| 13 | 0D | C#0 |
|
||||
| 14 | 0E | D0 |
|
||||
| 15 | 0F | D#0 |
|
||||
| 16 | 10 | E0 |
|
||||
| 17 | 11 | F0 |
|
||||
| 18 | 12 | F#0 |
|
||||
| 19 | 13 | G0 |
|
||||
| 20 | 14 | G#0 |
|
||||
| 21 | 15 | A0 |
|
||||
| 22 | 16 | A#0 |
|
||||
| 23 | 17 | B0 |
|
||||
| 24 | 18 | C1 |
|
||||
| 25 | 19 | C#1 |
|
||||
| 26 | 1A | D1 |
|
||||
| 27 | 1B | D#1 |
|
||||
| 28 | 1C | E1 |
|
||||
| 29 | 1D | F1 |
|
||||
| 30 | 1E | F#1 |
|
||||
| 31 | 1F | G1 |
|
||||
| 32 | 20 | G#1 |
|
||||
| 33 | 21 | A1 |
|
||||
| 34 | 22 | A#1 |
|
||||
| 35 | 23 | B1 |
|
||||
| 36 | 24 | C2 |
|
||||
| 37 | 25 | C#2 |
|
||||
| 38 | 26 | D2 |
|
||||
| 39 | 27 | D#2 |
|
||||
| 40 | 28 | E2 |
|
||||
| 41 | 29 | F2 |
|
||||
| 42 | 2A | F#2 |
|
||||
| 43 | 2B | G2 |
|
||||
| 44 | 2C | G#2 |
|
||||
| 45 | 2D | A2 |
|
||||
| 46 | 2E | A#2 |
|
||||
| 47 | 2F | B2 |
|
||||
| 48 | 30 | C3 |
|
||||
| 49 | 31 | C#3 |
|
||||
| 50 | 32 | D3 |
|
||||
| 51 | 33 | D#3 |
|
||||
| 52 | 34 | E3 |
|
||||
| 53 | 35 | F3 |
|
||||
| 54 | 36 | F#3 |
|
||||
| 55 | 37 | G3 |
|
||||
| 56 | 38 | G#3 |
|
||||
| 57 | 39 | A3 |
|
||||
| 58 | 3A | A#3 |
|
||||
| 59 | 3B | B3 |
|
||||
| 60 | 3C | C4 |
|
||||
| 61 | 3D | C#4 |
|
||||
| 62 | 3E | D4 |
|
||||
| 63 | 3F | D#4 |
|
||||
| 64 | 40 | E4 |
|
||||
| 65 | 41 | F4 |
|
||||
| 66 | 42 | F#4 |
|
||||
| 67 | 43 | G4 |
|
||||
| 68 | 44 | G#4 |
|
||||
| 69 | 45 | A4 |
|
||||
| 70 | 46 | A#4 |
|
||||
| 71 | 47 | B4 |
|
||||
| 72 | 48 | C5 |
|
||||
| 73 | 49 | C#5 |
|
||||
| 74 | 4A | D5 |
|
||||
| 75 | 4B | D#5 |
|
||||
| 76 | 4C | E5 |
|
||||
| 77 | 4D | F5 |
|
||||
| 78 | 4E | F#5 |
|
||||
| 79 | 4F | G5 |
|
||||
| 80 | 50 | G#5 |
|
||||
| 81 | 51 | A5 |
|
||||
| 82 | 52 | A#5 |
|
||||
| 83 | 53 | B5 |
|
||||
| 84 | 54 | C6 |
|
||||
| 85 | 55 | C#6 |
|
||||
| 86 | 56 | D6 |
|
||||
| 87 | 57 | D#6 |
|
||||
| 88 | 58 | E6 |
|
||||
| 89 | 59 | F6 |
|
||||
| 90 | 5A | F#6 |
|
||||
| 91 | 5B | G6 |
|
||||
| 92 | 5C | G#6 |
|
||||
| 93 | 5D | A6 |
|
||||
| 94 | 5E | A#6 |
|
||||
| 95 | 5F | B6 |
|
||||
| 96 | 60 | C7 |
|
||||
| 97 | 61 | C#7 |
|
||||
| 98 | 62 | D7 |
|
||||
| 99 | 63 | D#7 |
|
||||
| 100 | 64 | E7 |
|
||||
| 101 | 65 | F7 |
|
||||
| 102 | 66 | F#7 |
|
||||
| 103 | 67 | G7 |
|
||||
| 104 | 68 | G#7 |
|
||||
| 105 | 69 | A7 |
|
||||
| 106 | 6A | A#7 |
|
||||
| 107 | 6B | B7 |
|
||||
| 108 | 6C | C8 |
|
||||
| 109 | 6D | C#8 |
|
||||
| 110 | 6E | D8 |
|
||||
| 111 | 6F | D#8 |
|
||||
| 112 | 70 | E8 |
|
||||
| 113 | 71 | F8 |
|
||||
| 114 | 72 | F#8 |
|
||||
| 115 | 73 | G8 |
|
||||
| 116 | 74 | G#8 |
|
||||
| 117 | 75 | A8 |
|
||||
| 118 | 76 | A#8 |
|
||||
| 119 | 77 | B8 |
|
||||
| 120 | 78 | C9 |
|
||||
| 121 | 79 | C#9 |
|
||||
| 122 | 7A | D9 |
|
||||
| 123 | 7B | D#9 |
|
||||
| 124 | 7C | E9 |
|
||||
| 125 | 7D | F9 |
|
||||
| 126 | 7E | F#9 |
|
||||
| 127 | 7F | G9 |
|
||||
|
||||
* various
|
||||
- Allen & Heath [[https://www.allen-heath.com/hardware/xone-series/xonek2/][hardware notes]] & MIDI docs
|
||||
- see also https://github.com/taw10/x1k2-midi-osc-alsa
|
BIN
img/CQ-20B.png
Normal file
After Width: | Height: | Size: 481 KiB |
BIN
img/K2-layout.png
Normal file
After Width: | Height: | Size: 101 KiB |
1155
img/K2-layout.svg
Normal file
After Width: | Height: | Size: 104 KiB |
BIN
img/XONE-K2-layers.jpg
Normal file
After Width: | Height: | Size: 447 KiB |
BIN
img/XONE-K2-squared.jpg
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
img/cq_ref_1.png
Normal file
After Width: | Height: | Size: 417 KiB |
BIN
img/cq_ref_2.png
Normal file
After Width: | Height: | Size: 301 KiB |
BIN
img/cq_ref_3.png
Normal file
After Width: | Height: | Size: 233 KiB |
BIN
img/cq_ref_4.png
Normal file
After Width: | Height: | Size: 328 KiB |
BIN
img/cq_ref_5.png
Normal file
After Width: | Height: | Size: 111 KiB |
BIN
img/cq_ref_6.png
Normal file
After Width: | Height: | Size: 228 KiB |
166
k2-misc.py
Normal file
|
@ -0,0 +1,166 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# File name: K2-misc.py
|
||||
# Description: MIDI->OSC bridge for Allen & Heath XONE:K2 controller
|
||||
# Author: nik gaffney <nik@fo.am>
|
||||
# Created: 2024-09-10
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import argparse
|
||||
import mido
|
||||
import mido.backends.rtmidi
|
||||
from oscpy.client import OSCClient
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='k2osc',
|
||||
description='MIDI->OSC bridge for Allen & Heath XONE:K2 controller')
|
||||
parser.add_argument("--host", metavar='OSC-host', required=False,
|
||||
help='hostname or address of OSC destination',
|
||||
dest='osc_host', default='127.0.0.1')
|
||||
parser.add_argument("--port", metavar='OSC-port', required=False,
|
||||
help='port for OSC messages',
|
||||
dest='osc_port', default=5111)
|
||||
parser.add_argument("--midi", metavar='MIDI-port',
|
||||
required=False, help='port name of MIDI device',
|
||||
dest='midi_port', default='XONE:K2')
|
||||
parser.add_argument("-v", metavar='verbose',
|
||||
required=False, dest='verbose')
|
||||
parser.add_argument("-q", metavar='quiet',
|
||||
required=False, dest='quiet')
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
# XONE:K2 controller mapping
|
||||
|
||||
# valid types of controllers
|
||||
# rp - rotary pots from 0-127
|
||||
# re - rotary encoders (inc/dec)
|
||||
# fader - linear faders 0-127
|
||||
# button - pressed/released (MIDI notes)
|
||||
CONTROLLERS = ["rp", "re", "fader", "button"]
|
||||
|
||||
# controller types and corresponding MIDI control_id
|
||||
rotary_encoder_channels = [0, 1, 2, 3, 20, 21]
|
||||
rotary_potentiometer_channels = [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
|
||||
fader_channels = [16, 17, 18, 19]
|
||||
|
||||
|
||||
# map MIDI control_id to controller number
|
||||
def normalise_control_id(id):
|
||||
id_map = [1, 2, 3, 4, 1,
|
||||
2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11,
|
||||
12, 1, 2, 3, 4,
|
||||
5, 6, 0, 0, 0,]
|
||||
return id_map[id]
|
||||
|
||||
|
||||
# map MIDI note to button number (NOTE: no latching layers or LEDs yet)
|
||||
def normalise_button_id(id):
|
||||
id_map = {
|
||||
# upper block
|
||||
48: 1, 49: 2, 50: 3, 51: 4,
|
||||
44: 5, 45: 6, 46: 7, 47: 8,
|
||||
40: 9, 41: 10, 42: 11, 43: 12,
|
||||
# lower block
|
||||
36: "A", 37: "B", 38: "C", 39: "D",
|
||||
32: "E", 33: "F", 34: "G", 35: "H",
|
||||
28: "I", 29: "J", 30: "K", 31: "L",
|
||||
24: "M", 25: "N", 26: "O", 27: "P",
|
||||
# square buttons
|
||||
12: "LAYER", 15: "EXIT"}
|
||||
return id_map[id]
|
||||
|
||||
|
||||
# predicates for mapped controllers
|
||||
def is_rp(id):
|
||||
return id in rotary_potentiometer_channels
|
||||
|
||||
|
||||
def is_re(id):
|
||||
return id in rotary_encoder_channels
|
||||
|
||||
|
||||
def is_fader(id):
|
||||
return id in fader_channels
|
||||
|
||||
|
||||
def is_button(note):
|
||||
return True
|
||||
|
||||
|
||||
# MIDI in OSC out
|
||||
def parse_midi_message(msg):
|
||||
msg_type = msg.type
|
||||
print(f"\nrecv message of type '{msg_type}': {msg}")
|
||||
if msg_type == 'control_change':
|
||||
control_id, value = msg.control, msg.value
|
||||
id = normalise_control_id(control_id)
|
||||
if is_rp(control_id):
|
||||
mutaliate("rp", id, value)
|
||||
elif is_re(control_id):
|
||||
mutaliate("re", id, "inc" if (value == 1) else "dec")
|
||||
elif is_fader(control_id):
|
||||
mutaliate("fader", id, value)
|
||||
if msg_type in ['note_on', 'note_off']:
|
||||
note = msg.note
|
||||
if is_button(note):
|
||||
id = normalise_button_id(note)
|
||||
mutaliate("button", id,
|
||||
"pressed" if (msg_type == 'note_on') else "released")
|
||||
elif is_re(note):
|
||||
print("increase resolution of encoder when pressed...")
|
||||
# print(f"note: {note}")
|
||||
print(f"unrecognised: {msg}")
|
||||
|
||||
|
||||
# OSC interslonk
|
||||
# see also -> https://github.com/kivy/oscpy
|
||||
|
||||
def osc_setup(address="127.0.0.1", port=5111):
|
||||
osc = OSCClient(address, port)
|
||||
print(f"OSC client active. Sending to {address} on port {port}")
|
||||
return osc
|
||||
|
||||
|
||||
# send some OSC messages etc+
|
||||
def mutaliate(control, control_id, value=""):
|
||||
global osc
|
||||
# print(f"mutaliate: {control}, {control_id}, {arg}, {value}")
|
||||
if control in CONTROLLERS:
|
||||
path = f"/xone/k2/{control}/{control_id}"
|
||||
print(f"osc: {path} {value}")
|
||||
osc.send_message(path, value)
|
||||
return True
|
||||
|
||||
|
||||
def looper():
|
||||
loop = 0
|
||||
while True:
|
||||
loop += 1
|
||||
return True
|
||||
|
||||
|
||||
# setup MIDI ports
|
||||
# send_port = mido.open_output('send-to-K2')
|
||||
|
||||
def midi_setup(label):
|
||||
mido.open_input(label, callback=parse_midi_message)
|
||||
port = mido.open_output(label)
|
||||
return port
|
||||
|
||||
|
||||
def main():
|
||||
global osc, k2_send_port
|
||||
args = parse_arguments()
|
||||
osc = osc_setup(args.osc_host, args.osc_port)
|
||||
# midi = midi_setup('XONE:K2')
|
||||
# event loop
|
||||
looper()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|