Panel Events and Input Scanning Specification
All human-machine interface (HMI) interactions, including capacitive touch events, mechanical button presses, rotary encoder rotations, and analog controller movements, are digitized by the coprocessor and sent to the Host via the Interrupt IN endpoint (EP1) as structured 4-byte frames.
Event Dispatch Pipeline
When an Interrupt IN packet is received, the Host's ReceiveEventBuffer routine (located at .text+0x2CB0) dispatches individual 4-byte event payloads based on the value of the type byte (byte[3]):
flowchart TD
RX[Receive NKS4Command via INT IN] --> CHK{byte3 == 0x87?}
CHK -->|Yes| END[End of transfer]
CHK -->|No| TYPE{byte3 type?}
TYPE -->|0x00| T0[Touch and Button Handler]
TYPE -->|0x01| T1{byte2 & 0xF0?}
TYPE -->|0x03| T3[Analog Controller Dispatch]
TYPE -->|0x07| T7[SPDIF Clock Monitor]
TYPE -->|0x61| T61[SPDIF Sync Init]
TYPE -->|0x62| T62[SPDIF Sync Stream]
TYPE -->|0xE1| TE1[NV2AC Read Completion]
T1 -->|0x50| ENC[Rotary Encoder Decode]
T1 -->|0x71| RESP[SendNKS4EventToLinuxReader]
T1 -->|other| BTN[Button/Switch Matrix scan]
T0 --> SUB{byte2 & 0xF0?}
SUB -->|0x10| TOUCH[Touch Coordinate Decode]
SUB -->|0x30| TOUCH
SUB -->|0x40| TOUCH
SUB -->|0x66| RESP
SUB -->|0x70| RESP
SUB -->|0x80+| SCAN[System State Scan]
Event Payload Formats
Each active input category is formatted into a unique 4-byte payload. The general command layout is:
Protocol Event Classification
Type (byte[3]) |
Sub-type (byte[2]) |
Target Input / Event | Host Processing Routine |
|---|---|---|---|
0x00 |
0x1x |
Capacitive Touch Press | HandleTouchPanel |
0x00 |
0x30 |
Mechanical Switch Press | HandleSwitchEvent(code, true) |
0x00 |
0x40 |
Mechanical Switch Release | HandleSwitchEvent(code, false) |
0x00 |
0x66 |
CommunicationCheck ACK |
SendNKS4EventToLinuxReader |
0x00 |
0x70 |
GetVersion Response |
SendNKS4EventToLinuxReader |
0x01 |
0x71 |
ReadPortConfiguration Response |
SendNKS4EventToLinuxReader |
0x01 |
0x5x |
Rotary Encoder Delta | HandleRotary(value) |
0x03 |
0xNN |
Analog Controller ADC (0-1023) | HandleAnalogController(id, value) |
0x07 |
N/A | SPDIF Hardware Clock Status | Updates clock synchronization flags |
0xE1 |
N/A | Multi-byte Atmel/Register Response | SignalAtmelReadComplete |
SPDIF Clock Synchronization Frames (0x61 / 0x62)
The system manages S/PDIF digital audio clock synchronization using two real-time frame types:
Type 0x61: [value_hi, value_lo, source_id | flags, 0x61] (Sync Init)
Type 0x62: [value_hi, value_lo, source_id | flags, 0x62] (Sync Stream)
source_id: Extracted frombyte[2] & 0x3F.value: Computed as a 16-bit big-endian value:(byte[0] << 8) | byte[1].
Dual Keybed Scanning Architecture
The Host orchestrates keyboard scanning using two completely independent hardware paths:
Path 1: Velocity-Sensitive Scan Link (COM Port UART)
To avoid USB scheduling latency, the primary 88-key velocity matrix is scanned by a dedicated coprocessor microcontroller, which routes real-time note events directly to the Host's CSTGKeybedInterface (within OA.ko) over a dedicated serial UART link:
Keyboard Keybed Matrix
│
▼
[ Coprocessor ] (Sub-millisecond dual-contact scan & velocity calc)
│
▼ (Physical UART Serial Interconnect)
[ Host COM Port ] (I/O mapped registers at 0x240 / 0x3F8)
│
▼
[ CSTGKeybedInterface ] (Real-time RTAI interrupt handler in OA.ko)
- Access Mode: Direct x86
in/outport registers, bypasses standard kernel USB stacks. - External MIDI Accessory Routing Separation: The USB MIDI interface (Interface 2, managed by
USBMidiAccessory.ko) is strictly dedicated to external accessories connected to the physical MIDI IN/OUT DIN jacks. It is not involved in processing internal keybed events.
Path 2: USB Scanning Control Interface (ConfigureScanning)
The Host's synthesis engine controls coprocessor scanning by writing an 8-bit mask to the coprocessor via the USB Bulk OUT control path. The coprocessor applies this mask to its scanning hardware:
| Bit Position | Hexadecimal Mask | Target Hardware Operation |
|---|---|---|
| 7 | 0x80 |
Globally enable mechanical keybed matrix scanning |
| 6 | 0x40 |
Enable physical keybed aftertouch (pressure) scanning |
| 4 | 0x10 |
Enable analog expression pedal input scanning |
| 3 | 0x08 |
Reserved configuration parameter |
| 2 | 0x04 |
Reserved configuration parameter |
| 1 | 0x02 |
Reserved configuration parameter |
| 0 | 0x01 |
Reserved configuration parameter |
Input Event Conversion Specifications
Capacitive Touch Coordinates Decoding Math
When the user touches the panel, the coprocessor generates a 4-byte touch coordinate frame:
WhereP represents the active logical panel ID (byte[2] & 0x0F), ranging from 0 to 15. The Host's HandleTouchPanel routine converts the raw coordinates into standard MIDI control values using linear scaling:
- Column Index: Mapped to a standard 7-bit MIDI control value (0-127).
- Row Index: Selects one of the 8 physical capacitive touch strips on the panel. The row index is added to
28to yield the finaleSTGButtonCodebutton press event sent to the synthesis engine.
Mechanical Button Events (Press / Release)
Mechanical button actions are sent using press and release frames:
[0x00, button_code, 0x30, 0x00] // State: Pressed
[0x00, button_code, 0x40, 0x00] // State: Released
button_code: Unique 7-bit identifier (byte[1] & 0x7F), ranging from 0 to 78 (0x4E). These map directly to physical panel controls (e.g., switches, navigation pads, transport controls).
Rotary Encoder Delta Decoding
Rotary encoder movements are sent as signed 16-bit relative deltas:
* Relative Rotation: Decoded as a 16-bit signed integer:(int16_t)((delta_hi << 8) | delta_lo). Positive values represent clockwise rotation, while negative values represent counter-clockwise rotation.
Analog Controller Value Reconstruction Math
High-resolution analog inputs (e.g., sliders, pitch bend joysticks, expression pedals) are digitized by a 10-bit analog-to-digital converter (ADC). The 10-bit value is split across two bytes to fit the NKS4Command frame:
The Host reconstructs the original 10-bit ADC value (\(V \in [0, 1023]\)) using the following bitwise operations:
To match standard MIDI control conventions, the Host mirrors the reconstructed value around its center point (512) and downsamples it to a 7-bit value:
Analog Controller ID Assignments (eSTGAnalogDeviceCode)
The controller_id byte (byte[2] & 0x3F) maps directly to specific physical hardware controls:
| Controller ID | Physical Hardware Control | Operational Notes |
|---|---|---|
0 - 7 |
Realtime Control Knobs 1 - 8 | Continuous rotary pots |
8 - 15 |
Mixer Drawbars / Sliders 1 - 8 | Linear volume sliders |
16 |
Joystick X-Axis | Pitch bend control (centered at 512) |
17 |
Joystick Y-Axis | Modulation control |
18 |
Ribbon Controller | Linear touch-sensitive ribbon |
19 - 23 |
Pedals 1 - 5 | Continuous expression and damper inputs |
24 |
Mode Selector | Dispatches control behavior based on active system state |
Coprocessor Event Packing Implementation
To transmit a digitized 10-bit ADC value to the Host, the coprocessor's firmware must pack the bits into the standard NKS4Command structure:
void transmit_analog_event(uint8_t controller_id, uint16_t adc_10bit) {
// Clamps the input to 10-bit resolution (0 to 1023)
uint16_t val = adc_10bit & 0x3FF;
// Distribute bits across payload bytes
uint8_t byte0 = (val >> 2) & 0xFE; // Upper 7 bits mapped to bits 7:1
if (val & 0x04) {
byte0 |= 0x01; // Map bit 2 to bit 0
}
uint8_t byte1 = (val << 6) & 0xC0; // Lower 2 bits mapped to bits 7:6
// Assemble and send the 4-byte NKS4Command
uint8_t event_frame[4] = { byte0, byte1, controller_id, 0x03 };
usb_interrupt_in_transmit(event_frame, 4);
}