Update to spice2x-25-04-25 (pre-apply)
> broken commit
This commit is contained in:
+36
-1
@@ -14,6 +14,8 @@ extern "C" {
|
||||
|
||||
#include "util/unique_plain_ptr.h"
|
||||
|
||||
#include "smxdedicab.h"
|
||||
#include "smxstage.h"
|
||||
#include "sextet.h"
|
||||
|
||||
namespace rawinput {
|
||||
@@ -27,6 +29,8 @@ namespace rawinput {
|
||||
MIDI,
|
||||
SEXTET_OUTPUT,
|
||||
PIUIO_DEVICE,
|
||||
SMX_STAGE,
|
||||
SMX_DEDICAB
|
||||
};
|
||||
|
||||
enum MouseKeys {
|
||||
@@ -128,26 +132,55 @@ namespace rawinput {
|
||||
};
|
||||
|
||||
struct DeviceMIDIInfo {
|
||||
// notes -legacy logic
|
||||
std::vector<bool> states;
|
||||
std::vector<uint8_t> states_events;
|
||||
std::vector<bool> bind_states;
|
||||
|
||||
// notes - v2 logic
|
||||
std::vector<double> v2_last_off_time;
|
||||
std::vector<double> v2_last_on_time;
|
||||
std::vector<uint8_t> v2_velocity_threshold;
|
||||
std::vector<bool> v2_velocity_threshold_set_on_device;
|
||||
|
||||
// buttons with velocity - 16 channels, each channel has notes [0-127]
|
||||
std::vector<uint8_t> velocity; // 16*128 x 7 bit resolution
|
||||
|
||||
bool freeze;
|
||||
|
||||
// precision controls (double width / 14 bit MSB+LSB controls)
|
||||
// 16 channels, each channel has controls [0, 1F] (total 32)
|
||||
std::vector<uint16_t> controls_precision; // 16*32 14 bit resolution
|
||||
std::vector<uint16_t> controls_precision_bind;
|
||||
std::vector<bool> controls_precision_msb;
|
||||
std::vector<bool> controls_precision_lsb;
|
||||
std::vector<bool> controls_precision_set;
|
||||
|
||||
// single controls (single width / 8 bit)
|
||||
// 16 channels, each channel has:
|
||||
// single byte controls [0x46, 0x5F] (total 26)
|
||||
// undefined single byte controls [0x66, 0x77] (total 18)
|
||||
// (total 44)
|
||||
std::vector<uint8_t> controls_single; // 16*44 7 bit resolution
|
||||
std::vector<uint8_t> controls_single_bind;
|
||||
std::vector<bool> controls_single_set;
|
||||
|
||||
// on-off controls
|
||||
// 16 channels, each channel has controls [0x40, 0x45] (total 6)
|
||||
std::vector<bool> controls_onoff; // 16*6 1 bit resolution :)
|
||||
std::vector<bool> controls_onoff_bind;
|
||||
std::vector<bool> controls_onoff_set;
|
||||
uint16_t pitch_bend; // 14 bit resolution
|
||||
std::vector<double> v2_controls_onoff_last_on_time;
|
||||
std::vector<double> v2_controls_onoff_last_off_time;
|
||||
|
||||
// pitch bend
|
||||
std::vector<int16_t> pitch_bend; // 16*14 bit resolution
|
||||
std::vector<bool> pitch_bend_set;
|
||||
};
|
||||
|
||||
class PIUIO;
|
||||
class SmxStageDevice;
|
||||
class SmxDedicabDevice;
|
||||
|
||||
struct Device {
|
||||
size_t id;
|
||||
@@ -167,6 +200,8 @@ namespace rawinput {
|
||||
DeviceMIDIInfo *midiInfo = nullptr;
|
||||
SextetDevice *sextetInfo = nullptr;
|
||||
PIUIO* piuioDev = nullptr;
|
||||
SmxStageDevice *smxstageInfo = nullptr;
|
||||
SmxDedicabDevice* smxdedicabInfo = nullptr;
|
||||
double input_time = 0.0;
|
||||
double input_hz = 0.f;
|
||||
double input_hz_max = 0.f;
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace rawinput {
|
||||
std::string path = to_string(drive) + ":\\card" + to_string(player) + ".txt";
|
||||
if (fileutils::file_exists(path)) {
|
||||
uint8_t card_data[8];
|
||||
if (eamuse_get_card(path, card_data)) {
|
||||
if (eamuse_get_card_from_file(path, card_data, player)) {
|
||||
eamuse_card_insert(player, card_data);
|
||||
}
|
||||
}
|
||||
|
||||
+276
-87
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <cstdarg>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <objbase.h>
|
||||
#include <setupapi.h>
|
||||
@@ -22,6 +23,42 @@ namespace rawinput {
|
||||
// settings
|
||||
bool NOLEGACY = false;
|
||||
uint8_t HID_LIGHT_BRIGHTNESS = 100; // 100%
|
||||
bool ENABLE_SMX_STAGE = false;
|
||||
bool ENABLE_SMX_DEDICAB = false;
|
||||
int TOUCHSCREEN_RANGE_X = 0;
|
||||
int TOUCHSCREEN_RANGE_Y = 0;
|
||||
bool DUMP_HID_DEVICES_TO_LOG = false;
|
||||
|
||||
// set this to something slightly longer than 16.67ms (60Hz) so that I/O can pick it up
|
||||
// this may need to be adjusted for each game in the future if there is a game that polls less
|
||||
// often than 60Hz
|
||||
uint32_t MIDI_NOTE_SUSTAIN = 20;
|
||||
|
||||
static MidiNoteAlgorithm MIDI_NOTE_ALGORITHM = MidiNoteAlgorithm::V2;
|
||||
}
|
||||
|
||||
rawinput::MidiNoteAlgorithm rawinput::get_midi_algorithm() {
|
||||
return rawinput::MIDI_NOTE_ALGORITHM;
|
||||
}
|
||||
|
||||
void rawinput::set_midi_algorithm(rawinput::MidiNoteAlgorithm new_algo) {
|
||||
rawinput::MIDI_NOTE_ALGORITHM = new_algo;
|
||||
std::string s = "Unknown";
|
||||
switch (new_algo) {
|
||||
case rawinput::MidiNoteAlgorithm::LEGACY:
|
||||
s = "legacy";
|
||||
break;
|
||||
case rawinput::MidiNoteAlgorithm::V2:
|
||||
s = "v2";
|
||||
break;
|
||||
case rawinput::MidiNoteAlgorithm::V2_DRUM:
|
||||
s = "v2_drum";
|
||||
break;
|
||||
default:
|
||||
log_info("rawinput", "assert failed: invalid midi algorithm");
|
||||
break;
|
||||
}
|
||||
log_info("rawinput", "using MIDI algorithm: {}", s);
|
||||
}
|
||||
|
||||
rawinput::RawInputManager::RawInputManager() {
|
||||
@@ -139,6 +176,14 @@ void rawinput::RawInputManager::devices_reload() {
|
||||
this->devices_scan_midi();
|
||||
this->devices_scan_piuio();
|
||||
|
||||
if (ENABLE_SMX_STAGE) {
|
||||
this->devices_scan_smxstage();
|
||||
}
|
||||
|
||||
if (ENABLE_SMX_DEDICAB) {
|
||||
this->devices_scan_smxdedicab();
|
||||
}
|
||||
|
||||
// check for LIT Board
|
||||
sextet_register("COM54", "LIT Board", false);
|
||||
|
||||
@@ -180,6 +225,9 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
||||
|
||||
// get device name
|
||||
std::string device_name = rawinput_get_device_name(device->hDevice);
|
||||
if (device_name.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// extract information out of name
|
||||
auto device_info = rawinput::RawInputManager::get_device_info(device_name);
|
||||
@@ -269,7 +317,9 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
||||
if ((caps.UsagePage >> 8) == 0xFF
|
||||
&& !(hid_vid == 0xBEEF && hid_pid == 0x5730)) // allow Minimaid
|
||||
{
|
||||
log_misc("rawinput", "skipping vendor-specific device");
|
||||
if (DUMP_HID_DEVICES_TO_LOG) {
|
||||
log_misc("rawinput", "skipping vendor-specific device, vid/pid 0x{:04x}:0x{:04x}", hid_vid, hid_pid);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -444,7 +494,7 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
||||
// get string index
|
||||
ULONG string_index = 0;
|
||||
if (button_caps.IsStringRange && button_caps.Range.StringMin != 0) {
|
||||
string_index = button_caps.Range.StringMin + static_cast<ULONG>(button_output_caps_list.size()) - 1;
|
||||
string_index = button_caps.Range.StringMin + static_cast<ULONG>(button_output_caps_names.size());
|
||||
}
|
||||
else if (!button_caps.IsStringRange && button_caps.NotRange.StringIndex != 0) {
|
||||
string_index = button_caps.NotRange.StringIndex;
|
||||
@@ -787,6 +837,10 @@ void rawinput::RawInputManager::devices_scan_midi() {
|
||||
midi_device_midi_info->states = std::vector<bool>(16 * 128);
|
||||
midi_device_midi_info->states_events = std::vector<uint8_t>(16 * 128);
|
||||
midi_device_midi_info->bind_states = std::vector<bool>(16 * 128);
|
||||
midi_device_midi_info->v2_last_on_time = std::vector<double>(16 * 128);
|
||||
midi_device_midi_info->v2_last_off_time = std::vector<double>(16 * 128);
|
||||
midi_device_midi_info->v2_velocity_threshold = std::vector<uint8_t>(16 * 128);
|
||||
midi_device_midi_info->v2_velocity_threshold_set_on_device = std::vector<bool>(16 * 128);
|
||||
midi_device_midi_info->velocity = std::vector<uint8_t>(16 * 128);
|
||||
midi_device_midi_info->freeze = false;
|
||||
midi_device_midi_info->controls_precision = std::vector<uint16_t>(16 * 32);
|
||||
@@ -800,9 +854,14 @@ void rawinput::RawInputManager::devices_scan_midi() {
|
||||
midi_device_midi_info->controls_onoff = std::vector<bool>(16 * 6);
|
||||
midi_device_midi_info->controls_onoff_bind = std::vector<bool>(16 * 6);
|
||||
midi_device_midi_info->controls_onoff_set = std::vector<bool>(16 * 6);
|
||||
midi_device_midi_info->pitch_bend = 0x2000;
|
||||
midi_device_midi_info->v2_controls_onoff_last_on_time = std::vector<double>(16 * 6);
|
||||
midi_device_midi_info->v2_controls_onoff_last_off_time = std::vector<double>(16 * 6);
|
||||
midi_device_midi_info->pitch_bend = std::vector<int16_t>(16 * 6);
|
||||
midi_device_midi_info->pitch_bend_set = std::vector<bool>(16 * 6);
|
||||
|
||||
// build identifier
|
||||
// build identifier for MIDI
|
||||
// ;MIDI; format is now set in stone (in other parts of the code base and in the config xml file)
|
||||
// so it should never be changed
|
||||
std::ostringstream midi_identifier;
|
||||
midi_identifier << ";" << "MIDI";
|
||||
midi_identifier << ";" << midi_device_id;
|
||||
@@ -882,6 +941,54 @@ void rawinput::RawInputManager::devices_scan_piuio() {
|
||||
}
|
||||
}
|
||||
|
||||
void rawinput::RawInputManager::devices_scan_smxstage() {
|
||||
auto *new_smxstage_device = new Device();
|
||||
new_smxstage_device->type = SMX_STAGE;
|
||||
new_smxstage_device->name = "smxstage";
|
||||
new_smxstage_device->desc = "SMX Stage";
|
||||
new_smxstage_device->smxstageInfo = nullptr;
|
||||
new_smxstage_device->mutex = new std::mutex();
|
||||
new_smxstage_device->mutex_out = new std::mutex();
|
||||
|
||||
auto &device = this->devices.emplace_back(*new_smxstage_device);
|
||||
auto smxstageInfo = new SmxStageDevice();
|
||||
if (smxstageInfo->Initialize()) {
|
||||
device.smxstageInfo = smxstageInfo;
|
||||
|
||||
// notify add
|
||||
for (auto &cb : this->callback_add) {
|
||||
cb.f(cb.data, &device);
|
||||
}
|
||||
} else {
|
||||
// remove device since connection failed
|
||||
this->devices.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void rawinput::RawInputManager::devices_scan_smxdedicab() {
|
||||
auto *new_smxdedicab_device = new Device();
|
||||
new_smxdedicab_device->type = SMX_DEDICAB;
|
||||
new_smxdedicab_device->name = "smxdedicab";
|
||||
new_smxdedicab_device->desc = "SMX Dedicated Cabinet";
|
||||
new_smxdedicab_device->smxdedicabInfo = nullptr;
|
||||
new_smxdedicab_device->mutex = new std::mutex();
|
||||
new_smxdedicab_device->mutex_out = new std::mutex();
|
||||
|
||||
auto &device = this->devices.emplace_back(*new_smxdedicab_device);
|
||||
auto smxdedicabInfo = new SmxDedicabDevice();
|
||||
if (smxdedicabInfo->Initialize()) {
|
||||
device.smxdedicabInfo = smxdedicabInfo;
|
||||
|
||||
// notify add
|
||||
for (auto &cb : this->callback_add) {
|
||||
cb.f(cb.data, &device);
|
||||
}
|
||||
} else {
|
||||
// remove device since connection failed
|
||||
this->devices.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void rawinput::RawInputManager::flush_start() {
|
||||
|
||||
// start flush thread
|
||||
@@ -1332,6 +1439,10 @@ void rawinput::RawInputManager::devices_destruct(Device *device, bool log) {
|
||||
device->midiInfo = nullptr;
|
||||
delete device->sextetInfo;
|
||||
device->sextetInfo = nullptr;
|
||||
delete device->smxstageInfo;
|
||||
device->smxstageInfo = nullptr;
|
||||
delete device->smxdedicabInfo;
|
||||
device->smxdedicabInfo = nullptr;
|
||||
// TODO: check if mutex can be deleted
|
||||
}
|
||||
|
||||
@@ -1608,7 +1719,7 @@ LRESULT CALLBACK rawinput::RawInputManager::input_wnd_proc(
|
||||
}
|
||||
|
||||
// update buttons
|
||||
bool new_states[button_count] {};
|
||||
std::vector<bool> new_states(button_count);
|
||||
for (ULONG usage_num = 0; usage_num < usages_length; usage_num++) {
|
||||
USAGE usage = usages[usage_num] - button_caps.Range.UsageMin;
|
||||
|
||||
@@ -1827,21 +1938,31 @@ void CALLBACK rawinput::RawInputManager::input_midi_proc(HMIDIIN hMidiIn, UINT w
|
||||
case 0x8: { // NOTE OFF
|
||||
|
||||
// param mapping
|
||||
auto midi_note = midi_byte1 & 127u;
|
||||
const auto midi_note = midi_byte1 & 127u;
|
||||
|
||||
// log_misc("midi", "[{}] OFF", midi_note);
|
||||
|
||||
// get index
|
||||
auto midi_index = midi_status_channel * 128 + midi_note;
|
||||
const auto midi_index = midi_status_channel * 128 + midi_note;
|
||||
if (midi_index < 16 * 128) {
|
||||
|
||||
// update velocity
|
||||
device.midiInfo->velocity[midi_index] = 0;
|
||||
|
||||
// disable note
|
||||
if (device.midiInfo->states_events[midi_index])
|
||||
device.midiInfo->states[midi_index] = false;
|
||||
|
||||
// mark device as updated
|
||||
device.updated = true;
|
||||
if (MIDI_NOTE_ALGORITHM == MidiNoteAlgorithm::LEGACY) {
|
||||
// update velocity
|
||||
device.midiInfo->velocity[midi_index] = 0;
|
||||
// disable note
|
||||
if (device.midiInfo->states_events[midi_index]) {
|
||||
device.midiInfo->states[midi_index] = false;
|
||||
}
|
||||
device.updated = true;
|
||||
} else {
|
||||
// v2 logic
|
||||
// exactly the same as NOTE ON with 0 velocity
|
||||
// velocity is kept; api will ignore it if button is not pressed
|
||||
if (MIDI_NOTE_ALGORITHM == MidiNoteAlgorithm::V2) {
|
||||
device.midiInfo->v2_last_off_time[midi_index] = get_performance_milliseconds();
|
||||
device.updated = true;
|
||||
}
|
||||
// for v2_drum, NOTE ON is ignored
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1849,36 +1970,69 @@ void CALLBACK rawinput::RawInputManager::input_midi_proc(HMIDIIN hMidiIn, UINT w
|
||||
case 0x9: { // NOTE ON
|
||||
|
||||
// param mapping
|
||||
auto midi_note = midi_byte1 & 127u;
|
||||
auto midi_velocity = midi_byte2 & 127u;
|
||||
const auto midi_note = midi_byte1 & 127u;
|
||||
|
||||
// per MIDI spec, if NOTE ON is sent with 0 velocity, it's the same thing as NOTE OFF.
|
||||
const auto midi_velocity = midi_byte2 & 127u;
|
||||
|
||||
// log_misc("midi", "[{}] ON v={}", midi_note, midi_velocity);
|
||||
|
||||
// get index
|
||||
auto midi_index = midi_status_channel * 128 + midi_note;
|
||||
const auto midi_index = midi_status_channel * 128 + midi_note;
|
||||
if (midi_index < 16 * 128) {
|
||||
if (MIDI_NOTE_ALGORITHM == MidiNoteAlgorithm::LEGACY) {
|
||||
// update velocity
|
||||
device.midiInfo->velocity[midi_index] = (uint8_t) midi_velocity;
|
||||
|
||||
// update velocity
|
||||
device.midiInfo->velocity[midi_index] = (uint8_t) midi_velocity;
|
||||
if (midi_velocity) {
|
||||
// update events (for legacy logic)
|
||||
// how does this work? see the comment in api.cpp around the check for
|
||||
// get_midi_algorithm() for an explanation
|
||||
|
||||
// update events
|
||||
if (midi_velocity) {
|
||||
// so currently it's meant to be turned on
|
||||
device.midiInfo->states[midi_index] = true;
|
||||
|
||||
// so currently it's meant to be turned on
|
||||
device.midiInfo->states[midi_index] = true;
|
||||
// if its already on just increase it by one to turn it off
|
||||
if (device.midiInfo->states_events[midi_index] % 2)
|
||||
device.midiInfo->states_events[midi_index]++;
|
||||
else
|
||||
device.midiInfo->states_events[midi_index] += 2;
|
||||
|
||||
// if its already on just increase it by one to turn it off
|
||||
if (device.midiInfo->states_events[midi_index] % 2)
|
||||
device.midiInfo->states_events[midi_index]++;
|
||||
else
|
||||
device.midiInfo->states_events[midi_index] += 2;
|
||||
} else if (!device.midiInfo->freeze) {
|
||||
// velocity 0 means turn it off
|
||||
device.midiInfo->states[midi_index] = false;
|
||||
}
|
||||
device.updated = true;
|
||||
|
||||
} else if (!device.midiInfo->freeze) {
|
||||
} else {
|
||||
// v2 logic
|
||||
const auto now = get_performance_milliseconds();
|
||||
auto threshold = device.midiInfo->v2_velocity_threshold[midi_index];
|
||||
// when device is frozen (binding is happening) ignore the velocity threshold
|
||||
// this allows users to bind keys even if the midi note is set to high threshold at
|
||||
// rawinput layer, either from a previous binding that was cleared, or existing binding
|
||||
// for another button
|
||||
if (device.midiInfo->freeze) {
|
||||
threshold = 0;
|
||||
}
|
||||
if (threshold < midi_velocity) {
|
||||
device.midiInfo->velocity[midi_index] = (uint8_t)midi_velocity;
|
||||
device.midiInfo->v2_last_on_time[midi_index] = now;
|
||||
|
||||
// velocity 0 means turn it off
|
||||
device.midiInfo->states[midi_index] = false;
|
||||
// disable holds and release all notes immediately
|
||||
if (MIDI_NOTE_ALGORITHM == MidiNoteAlgorithm::V2_DRUM) {
|
||||
device.midiInfo->v2_last_off_time[midi_index] = now;
|
||||
}
|
||||
device.updated = true;
|
||||
} else {
|
||||
if (MIDI_NOTE_ALGORITHM == MidiNoteAlgorithm::V2) {
|
||||
// insufficient velocity ON == exactly the same as NOTE OFF
|
||||
device.midiInfo->v2_last_off_time[midi_index] = now;
|
||||
device.updated = true;
|
||||
}
|
||||
// for v2_drum, NOTE ON with insufficient velocity is ignored
|
||||
}
|
||||
}
|
||||
|
||||
// mark device as updated
|
||||
device.updated = true;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1957,15 +2111,33 @@ void CALLBACK rawinput::RawInputManager::input_midi_proc(HMIDIIN hMidiIn, UINT w
|
||||
|
||||
// update index
|
||||
midi_index = midi_status_channel * 6 + midi_control - 0x40;
|
||||
device.midiInfo->controls_precision_set[midi_index] = true;
|
||||
device.midiInfo->controls_onoff_set[midi_index] = true;
|
||||
|
||||
// get on/off state
|
||||
auto onoff_state = midi_value >= 64;
|
||||
const auto onoff_state = midi_value >= 64;
|
||||
|
||||
// update device
|
||||
if (device.midiInfo->controls_onoff[midi_index] != onoff_state) {
|
||||
if (MIDI_NOTE_ALGORITHM == MidiNoteAlgorithm::LEGACY) {
|
||||
if (device.midiInfo->controls_onoff[midi_index] != onoff_state) {
|
||||
device.midiInfo->controls_onoff[midi_index] = onoff_state;
|
||||
device.updated = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
// v2 and v2_drum:
|
||||
// unlike notes (drum pads), controls can send continuous ON signal
|
||||
// therefore, check for rising and falling edges
|
||||
const auto now = get_performance_milliseconds();
|
||||
const auto previous_value = device.midiInfo->controls_onoff[midi_index];
|
||||
if (!previous_value && onoff_state) {
|
||||
device.midiInfo->v2_controls_onoff_last_on_time[midi_index] = now;
|
||||
device.updated = true;
|
||||
} else if (previous_value && !onoff_state) {
|
||||
device.midiInfo->v2_controls_onoff_last_off_time[midi_index] = now;
|
||||
device.updated = true;
|
||||
}
|
||||
|
||||
device.midiInfo->controls_onoff[midi_index] = onoff_state;
|
||||
device.updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1973,8 +2145,8 @@ void CALLBACK rawinput::RawInputManager::input_midi_proc(HMIDIIN hMidiIn, UINT w
|
||||
else if (midi_control >= 0x46 && midi_control <= 0x5F) {
|
||||
|
||||
// update index
|
||||
midi_index = midi_status_channel * 32 + midi_control - 0x46;
|
||||
device.midiInfo->controls_precision_set[midi_index] = true;
|
||||
midi_index = midi_status_channel * 44 + midi_control - 0x46;
|
||||
device.midiInfo->controls_single_set[midi_index] = true;
|
||||
|
||||
// update device
|
||||
if (device.midiInfo->controls_single[midi_index] != midi_value) {
|
||||
@@ -1993,8 +2165,8 @@ void CALLBACK rawinput::RawInputManager::input_midi_proc(HMIDIIN hMidiIn, UINT w
|
||||
|
||||
// update index
|
||||
auto sbc_count = 0x5F - 0x46 + 1;
|
||||
midi_index = midi_status_channel * 32 + midi_control - 0x66 + sbc_count;
|
||||
device.midiInfo->controls_precision_set[midi_index] = true;
|
||||
midi_index = midi_status_channel * 44 + midi_control - 0x66 + sbc_count;
|
||||
device.midiInfo->controls_single_set[midi_index] = true;
|
||||
|
||||
// update device
|
||||
if (device.midiInfo->controls_single[midi_index] != midi_value) {
|
||||
@@ -2013,56 +2185,34 @@ void CALLBACK rawinput::RawInputManager::input_midi_proc(HMIDIIN hMidiIn, UINT w
|
||||
device.midiInfo->controls_precision[midi_status_channel * 32 + i] = 0;
|
||||
for (int i = 0; i < 44; i++)
|
||||
device.midiInfo->controls_single[midi_status_channel * 44 + i] = 0;
|
||||
for (int i = 0; i < 6; i++)
|
||||
device.midiInfo->controls_onoff[midi_status_channel * 6 + i] = false;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
const auto index = midi_status_channel * 6 + i;
|
||||
device.midiInfo->controls_onoff[index] = false;
|
||||
device.midiInfo->v2_controls_onoff_last_on_time[index] = 0;
|
||||
device.midiInfo->v2_controls_onoff_last_off_time[index] = 0;
|
||||
}
|
||||
device.updated = true;
|
||||
break;
|
||||
}
|
||||
case 0x7A: // local control on/off
|
||||
break;
|
||||
case 0x7B: { // all notes off
|
||||
for (int i = 0; i < 128; i++) {
|
||||
device.midiInfo->states[channel_offset + i] = false;
|
||||
device.midiInfo->states_events[channel_offset + i] = 0;
|
||||
device.midiInfo->bind_states[channel_offset + i] = false;
|
||||
device.midiInfo->velocity[channel_offset + i] = 0;
|
||||
}
|
||||
device.updated = true;
|
||||
break;
|
||||
}
|
||||
case 0x7B: // all notes off
|
||||
case 0x7C: // omni mode off + all notes off
|
||||
for (int i = 0; i < 128; i++) {
|
||||
device.midiInfo->states[channel_offset + i] = false;
|
||||
device.midiInfo->states_events[channel_offset + i] = 0;
|
||||
device.midiInfo->bind_states[channel_offset + i] = false;
|
||||
device.midiInfo->velocity[channel_offset + i] = 0;
|
||||
}
|
||||
device.updated = true;
|
||||
break;
|
||||
case 0x7D: // omni mode on + all notes off
|
||||
for (int i = 0; i < 128; i++) {
|
||||
device.midiInfo->states[channel_offset + i] = false;
|
||||
device.midiInfo->states_events[channel_offset + i] = 0;
|
||||
device.midiInfo->bind_states[channel_offset + i] = false;
|
||||
device.midiInfo->velocity[channel_offset + i] = 0;
|
||||
}
|
||||
device.updated = true;
|
||||
break;
|
||||
case 0x7E: // mono mode on + poly off + all notes off
|
||||
for (int i = 0; i < 128; i++) {
|
||||
device.midiInfo->states[channel_offset + i] = false;
|
||||
device.midiInfo->states_events[channel_offset + i] = 0;
|
||||
device.midiInfo->bind_states[channel_offset + i] = false;
|
||||
device.midiInfo->velocity[channel_offset + i] = 0;
|
||||
}
|
||||
device.updated = true;
|
||||
break;
|
||||
case 0x7F: // poly mode on + mono off + all notes off
|
||||
for (int i = 0; i < 128; i++) {
|
||||
// common
|
||||
device.midiInfo->velocity[channel_offset + i] = 0;
|
||||
device.midiInfo->bind_states[channel_offset + i] = false;
|
||||
|
||||
// legacy
|
||||
device.midiInfo->states[channel_offset + i] = false;
|
||||
device.midiInfo->states_events[channel_offset + i] = 0;
|
||||
device.midiInfo->bind_states[channel_offset + i] = false;
|
||||
device.midiInfo->velocity[channel_offset + i] = 0;
|
||||
|
||||
// v2
|
||||
device.midiInfo->v2_last_off_time[channel_offset + i] = 0.0;
|
||||
device.midiInfo->v2_last_on_time[channel_offset + i] = 0.0;
|
||||
}
|
||||
device.updated = true;
|
||||
break;
|
||||
@@ -2080,12 +2230,14 @@ void CALLBACK rawinput::RawInputManager::input_midi_proc(HMIDIIN hMidiIn, UINT w
|
||||
break; // skipped above (!)
|
||||
case 0xE: { // PITCH BENDING
|
||||
|
||||
// build value
|
||||
uint16_t value = midi_byte1 | midi_byte2 << 7u;
|
||||
// raw values range from [0, 0x3FFF] (16383)
|
||||
// build value, centered around zero [-8192, 8191]
|
||||
int16_t value = ((midi_byte1) | (midi_byte2 << 7u)) - 0x2000;
|
||||
|
||||
// update device
|
||||
if (device.midiInfo->pitch_bend != value) {
|
||||
device.midiInfo->pitch_bend = value;
|
||||
if (device.midiInfo->pitch_bend[midi_status_channel] != value) {
|
||||
device.midiInfo->pitch_bend[midi_status_channel] = value;
|
||||
device.midiInfo->pitch_bend_set[midi_status_channel] = true;
|
||||
device.updated = true;
|
||||
}
|
||||
break;
|
||||
@@ -2331,6 +2483,14 @@ void rawinput::RawInputManager::device_write_output(Device *device, bool only_up
|
||||
device->sextetInfo->push_light_state();
|
||||
break;
|
||||
}
|
||||
case SMX_STAGE: {
|
||||
device->smxstageInfo->Update();
|
||||
break;
|
||||
};
|
||||
case SMX_DEDICAB: {
|
||||
device->smxdedicabInfo->Update();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -2360,15 +2520,24 @@ void rawinput::RawInputManager::devices_flush_output(bool optimized) {
|
||||
|
||||
void rawinput::RawInputManager::devices_print() {
|
||||
|
||||
if (!DUMP_HID_DEVICES_TO_LOG) {
|
||||
log_info("rawinput", "verbose dump of HID devices is disabled by default; see -sysdump option");
|
||||
return;
|
||||
}
|
||||
|
||||
bool touchscreen_found = false;
|
||||
|
||||
// iterate devices
|
||||
log_info("rawinput", "printing list of detected devices");
|
||||
log_info("rawinput", "detected device count: {}", devices.size());
|
||||
for (auto &device : devices) {
|
||||
bool is_touchscreen = false;
|
||||
|
||||
// lock it
|
||||
device.mutex->lock();
|
||||
|
||||
// general information
|
||||
log_misc("rawinput", "--------begin device @{}", device.handle);
|
||||
log_misc("rawinput", "device name: {}", device.name);
|
||||
log_misc("rawinput", "device desc: {}", device.desc);
|
||||
log_misc("rawinput", "device handle: {}", device.handle);
|
||||
@@ -2389,6 +2558,10 @@ void rawinput::RawInputManager::devices_print() {
|
||||
// check touchscreen
|
||||
if (device.hidInfo->touch.valid) {
|
||||
log_info("rawinput", "device is marked as touchscreen");
|
||||
if (!touchscreen_found) {
|
||||
touchscreen_found = true;
|
||||
is_touchscreen = true;
|
||||
}
|
||||
}
|
||||
|
||||
// button caps
|
||||
@@ -2437,6 +2610,12 @@ void rawinput::RawInputManager::devices_print() {
|
||||
LONG max = value_caps.LogicalMax;
|
||||
log_misc("rawinput", "device value caps detected: {} ({} to {}, {}-bit)",
|
||||
name, min, max, value_caps.BitSize);
|
||||
|
||||
if (name.compare("X") == 0 && is_touchscreen) {
|
||||
TOUCHSCREEN_RANGE_X = max;
|
||||
} else if (name.compare("Y") == 0 && is_touchscreen) {
|
||||
TOUCHSCREEN_RANGE_Y = max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2471,12 +2650,22 @@ void rawinput::RawInputManager::devices_print() {
|
||||
log_misc("rawinput", "device type: PIUIO");
|
||||
break;
|
||||
}
|
||||
case SMX_STAGE: {
|
||||
log_misc("rawinput", "device type: SMX_STAGE");
|
||||
break;
|
||||
}
|
||||
case SMX_DEDICAB: {
|
||||
log_misc("rawinput", "device type: SMX_DEDICAB");
|
||||
break;
|
||||
}
|
||||
case UNKNOWN:
|
||||
default:
|
||||
log_warning("rawinput", "device type: UNKNOWN");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
log_misc("rawinput", "----------end device @{}", device.handle);
|
||||
// unlock device
|
||||
device.mutex->unlock();
|
||||
}
|
||||
|
||||
+22
-1
@@ -18,6 +18,16 @@ namespace rawinput {
|
||||
|
||||
extern uint8_t HID_LIGHT_BRIGHTNESS;
|
||||
|
||||
extern bool ENABLE_SMX_STAGE;
|
||||
extern bool ENABLE_SMX_DEDICAB;
|
||||
|
||||
extern int TOUCHSCREEN_RANGE_X;
|
||||
extern int TOUCHSCREEN_RANGE_Y;
|
||||
|
||||
extern bool DUMP_HID_DEVICES_TO_LOG;
|
||||
|
||||
extern uint32_t MIDI_NOTE_SUSTAIN;
|
||||
|
||||
struct DeviceCallback {
|
||||
void *data;
|
||||
std::function<void(void*, Device*)> f;
|
||||
@@ -27,6 +37,15 @@ namespace rawinput {
|
||||
std::function<void(void*, Device*, uint8_t cmd, uint8_t ch, uint8_t b1, uint8_t b2)> f;
|
||||
};
|
||||
|
||||
enum class MidiNoteAlgorithm {
|
||||
LEGACY,
|
||||
V2,
|
||||
V2_DRUM
|
||||
};
|
||||
|
||||
rawinput::MidiNoteAlgorithm get_midi_algorithm();
|
||||
void set_midi_algorithm(rawinput::MidiNoteAlgorithm new_algo);
|
||||
|
||||
class RawInputManager {
|
||||
private:
|
||||
|
||||
@@ -51,6 +70,8 @@ namespace rawinput {
|
||||
void devices_reload();
|
||||
void devices_scan_rawinput(RAWINPUTDEVICELIST *device, bool log = true);
|
||||
void devices_scan_piuio();
|
||||
void devices_scan_smxstage();
|
||||
void devices_scan_smxdedicab();
|
||||
void devices_destruct();
|
||||
void devices_destruct(Device *device, bool log = true);
|
||||
void flush_start();
|
||||
@@ -114,7 +135,7 @@ namespace rawinput {
|
||||
if (device.type == MIDI) {
|
||||
device.midiInfo->freeze = freeze;
|
||||
if (!freeze) {
|
||||
for (unsigned short index = 0; index < 16 * 128; index++) {
|
||||
for (unsigned short index = 0; index < device.midiInfo->states.size(); index++) {
|
||||
device.midiInfo->states[index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#include "smx.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static HINSTANCE LIBSMX_INSTANCE = nullptr;
|
||||
static string LIBSMX_NAME = "SMX.dll";
|
||||
|
||||
// SMX_Start API
|
||||
static string SMX_StartStr = "SMX_Start";
|
||||
typedef VOID(__stdcall* SMX_Start_t)(
|
||||
SMXUpdateCallback UpdateCallback, void *pUser
|
||||
);
|
||||
static SMX_Start_t pSMX_Start = nullptr;
|
||||
|
||||
// SMX_SetLights2 API
|
||||
static string SMX_SetLights2Str = "SMX_SetLights2";
|
||||
typedef VOID(__stdcall* SMX_SetLights2_t)(
|
||||
const char *lightData, int lightDataSize
|
||||
);
|
||||
static SMX_SetLights2_t pSMX_SetLights2 = nullptr;
|
||||
|
||||
// SMX_SetDedicatedCabinetLights API
|
||||
static string SMX_SetDedicatedCabinetLightsStr = "SMX_SetDedicatedCabinetLights";
|
||||
typedef VOID(__stdcall* SMX_SetDedicatedCabinetLights_t)(
|
||||
SMXDedicatedCabinetLights lightDevice, const char* lightData, int lightDataSize
|
||||
);
|
||||
static SMX_SetDedicatedCabinetLights_t pSMX_SetDedicatedCabinetLights = nullptr;
|
||||
|
||||
SMXWrapper::SMXWrapper() {
|
||||
static bool functions_loaded = false;
|
||||
|
||||
if (functions_loaded)
|
||||
return;
|
||||
|
||||
// See if we even have the SMX.dll available.
|
||||
LIBSMX_INSTANCE = libutils::try_library(LIBSMX_NAME);
|
||||
|
||||
if (LIBSMX_INSTANCE != nullptr) {
|
||||
// Load functions
|
||||
pSMX_Start =
|
||||
(SMX_Start_t) libutils::get_proc(LIBSMX_INSTANCE, SMX_StartStr.c_str());
|
||||
pSMX_SetLights2 =
|
||||
(SMX_SetLights2_t) libutils::get_proc(LIBSMX_INSTANCE, SMX_SetLights2Str.c_str());
|
||||
pSMX_SetDedicatedCabinetLights =
|
||||
(SMX_SetDedicatedCabinetLights_t) libutils::get_proc(LIBSMX_INSTANCE, SMX_SetDedicatedCabinetLightsStr.c_str());
|
||||
|
||||
// Make sure they actually loaded. We won't block on pSMX_SetDedicatedCabinetLights being null,
|
||||
// since that functionality doesn't exist in the mainline SDK.
|
||||
if (pSMX_Start == nullptr || pSMX_SetLights2 == nullptr) {
|
||||
log_warning("smx", "Unable to load all external functions successfully");
|
||||
} else {
|
||||
functions_loaded = true;
|
||||
}
|
||||
|
||||
// This only exists in a fork of the SMX SDK right now, don't consider the functions not
|
||||
// to be loaded if this is null
|
||||
if (pSMX_SetDedicatedCabinetLights == nullptr) {
|
||||
log_warning("smx", "Unable to load cabinet lights functionality");
|
||||
}
|
||||
} else {
|
||||
log_warning("smx", "Could not load \'{}\'", LIBSMX_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
SMXWrapper::~SMXWrapper() {}
|
||||
|
||||
void SMXWrapper::SMX_Start(SMXUpdateCallback UpdateCallback, void *pUser) {
|
||||
if (pSMX_Start != nullptr) {
|
||||
pSMX_Start(UpdateCallback, pUser);
|
||||
}
|
||||
}
|
||||
|
||||
void SMXWrapper::SMX_SetLights2(const char *lightData, int lightDataSize) {
|
||||
if (pSMX_SetLights2 != nullptr) {
|
||||
pSMX_SetLights2(lightData, lightDataSize);
|
||||
}
|
||||
}
|
||||
|
||||
void SMXWrapper::SMX_SetDedicatedCabinetLights(SMXDedicatedCabinetLights lightDevice, const char *lightData, int lightDataSize) {
|
||||
if (pSMX_SetDedicatedCabinetLights != nullptr) {
|
||||
pSMX_SetDedicatedCabinetLights(lightDevice, lightData, lightDataSize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
#include <string>
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "device.h"
|
||||
|
||||
enum SMXUpdateCallbackReason {
|
||||
// This is called when a generic state change happens: connection or disconnection, inputs changed,
|
||||
// test data updated, etc. It doesn't specify what's changed. We simply check the whole state.
|
||||
SMXUpdateCallback_Updated,
|
||||
|
||||
// This is called when SMX_FactoryReset completes, indicating that SMX_GetConfig will now return
|
||||
// the reset configuration.
|
||||
SMXUpdateCallback_FactoryResetCommandComplete
|
||||
};
|
||||
|
||||
enum SMXDedicatedCabinetLights {
|
||||
MARQUEE = 0,
|
||||
LEFT_STRIP = 1,
|
||||
LEFT_SPOTLIGHTS = 2,
|
||||
RIGHT_STRIP = 3,
|
||||
RIGHT_SPOTLIGHTS = 4
|
||||
};
|
||||
|
||||
|
||||
typedef void SMXUpdateCallback(int pad, SMXUpdateCallbackReason reason, void *pUser);
|
||||
|
||||
/**
|
||||
* Wrapper for the StepManiaX SDK DLL, so we can use its functionality without having to import the source
|
||||
* of the SDK wholesale.
|
||||
*/
|
||||
class SMXWrapper {
|
||||
public:
|
||||
static SMXWrapper& getInstance() {
|
||||
static SMXWrapper instance;
|
||||
return instance;
|
||||
}
|
||||
~SMXWrapper();
|
||||
void SMX_Start(SMXUpdateCallback UpdateCallback, void *pUser);
|
||||
void SMX_SetLights2(const char *lightData, int lightDataSize);
|
||||
void SMX_SetDedicatedCabinetLights(SMXDedicatedCabinetLights lightDevice, const char* lightData, int lightDataSize);
|
||||
private:
|
||||
SMXWrapper();
|
||||
};
|
||||
@@ -0,0 +1,97 @@
|
||||
#include "smxdedicab.h"
|
||||
|
||||
#include "smx.h"
|
||||
#include "util/logging.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace {
|
||||
static const std::string DEVICE_NAMES[] = {
|
||||
"Marquee",
|
||||
"Left Vertical Strip",
|
||||
"Left Spotlights",
|
||||
"Right Vertical Strip",
|
||||
"Right Spotlights"
|
||||
};
|
||||
|
||||
static const size_t DEVICE_LED_COUNTS[] = {
|
||||
24, 28, 8, 28, 8
|
||||
};
|
||||
|
||||
void NullSmxCallback(int pad, SMXUpdateCallbackReason reason, void *pUser) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace rawinput {
|
||||
std::string SmxDedicabDevice::GetLightNameByIndex(size_t index) {
|
||||
const size_t subpixel = index % 3;
|
||||
const size_t device = (index / 3);
|
||||
|
||||
switch (subpixel) {
|
||||
case 0: return fmt::format("{} Red", DEVICE_NAMES[device]);
|
||||
case 1: return fmt::format("{} Green", DEVICE_NAMES[device]);
|
||||
case 2: return fmt::format("{} Blue", DEVICE_NAMES[device]);
|
||||
}
|
||||
|
||||
return "StepManiaX Dedicated Cabinet - Invalid Index";
|
||||
}
|
||||
|
||||
SmxDedicabDevice::SmxDedicabDevice() :
|
||||
m_lightData {
|
||||
new uint8_t[DEVICE_LED_COUNTS[0] * 3],
|
||||
new uint8_t[DEVICE_LED_COUNTS[1] * 3],
|
||||
new uint8_t[DEVICE_LED_COUNTS[2] * 3],
|
||||
new uint8_t[DEVICE_LED_COUNTS[3] * 3],
|
||||
new uint8_t[DEVICE_LED_COUNTS[4] * 3]
|
||||
},
|
||||
m_lightDataMutex()
|
||||
{
|
||||
}
|
||||
|
||||
bool SmxDedicabDevice::Initialize() {
|
||||
SMXWrapper::getInstance().SMX_Start(&NullSmxCallback, nullptr);
|
||||
|
||||
// After initializing the connection, we need to momentarily pause in order for the
|
||||
// IO device to not miss our first packets. Then we need to send 0x00 to flush out each
|
||||
// channel on initialization. If we don't do this, the lights have garbage data for each
|
||||
// channel until they're fully flushed.
|
||||
Sleep(1000);
|
||||
for (size_t device = 0; device < DEVICE_COUNT; device++) {
|
||||
const size_t numLeds = DEVICE_LED_COUNTS[device];
|
||||
for (size_t j = 0; j < numLeds * 3; j++) {
|
||||
m_lightData[device][j] = 0x00;
|
||||
}
|
||||
|
||||
SMXWrapper::getInstance().SMX_SetDedicatedCabinetLights(
|
||||
static_cast<SMXDedicatedCabinetLights>(device),
|
||||
reinterpret_cast<const char*>(&(m_lightData[device][0])),
|
||||
numLeds * 3
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SmxDedicabDevice::SetLightByIndex(size_t index, uint8_t value) {
|
||||
const size_t subpixel = index % 3;
|
||||
const size_t device = (index / 3);
|
||||
const size_t numLeds = DEVICE_LED_COUNTS[device];
|
||||
|
||||
m_lightDataMutex.lock();
|
||||
for (size_t i = 0; i < numLeds; i++) {
|
||||
m_lightData[device][(i * 3) + subpixel] = value;
|
||||
}
|
||||
m_lightDataMutex.unlock();
|
||||
}
|
||||
|
||||
void SmxDedicabDevice::Update() {
|
||||
m_lightDataMutex.lock();
|
||||
for (int i = 0; i < DEVICE_COUNT; i++) {
|
||||
const size_t numLeds = DEVICE_LED_COUNTS[i];
|
||||
SMXWrapper::getInstance().SMX_SetDedicatedCabinetLights(
|
||||
static_cast<SMXDedicatedCabinetLights>(i),
|
||||
reinterpret_cast<const char*>(&(m_lightData[i][0])),
|
||||
numLeds * 3
|
||||
);
|
||||
}
|
||||
m_lightDataMutex.unlock();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
namespace rawinput {
|
||||
class SmxDedicabDevice {
|
||||
public:
|
||||
static constexpr int DEVICE_COUNT = 5;
|
||||
static constexpr int LIGHTS_COUNT = DEVICE_COUNT * 3;
|
||||
static std::string GetLightNameByIndex(size_t index);
|
||||
|
||||
SmxDedicabDevice();
|
||||
bool Initialize();
|
||||
void SetLightByIndex(size_t index, uint8_t value);
|
||||
void Update();
|
||||
private:
|
||||
uint8_t* m_lightData[DEVICE_COUNT];
|
||||
std::mutex m_lightDataMutex;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "smxstage.h"
|
||||
|
||||
#include "smx.h"
|
||||
#include "util/logging.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace {
|
||||
static const std::string PANEL_NAMES[] = {
|
||||
"Top-Left", "Top-Center", "Top-Right",
|
||||
"Middle-Left", "Middle-Center", "Middle-Right",
|
||||
"Bottom-Left", "Bottom-Center", "Bottom-Right"
|
||||
};
|
||||
|
||||
void NullSmxCallback(int pad, SMXUpdateCallbackReason reason, void *pUser) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace rawinput {
|
||||
std::string SmxStageDevice::GetLightNameByIndex(size_t index) {
|
||||
const size_t subpixel = index%3;
|
||||
const size_t panel = (index/3)%PANEL_COUNT;
|
||||
const size_t pad = (index/3)/PANEL_COUNT;
|
||||
|
||||
switch (subpixel) {
|
||||
case 0: return fmt::format("Pad {} Panel {} Red", pad+1, PANEL_NAMES[panel]);
|
||||
case 1: return fmt::format("Pad {} Panel {} Green", pad+1, PANEL_NAMES[panel]);
|
||||
case 2: return fmt::format("Pad {} Panel {} Blue", pad+1, PANEL_NAMES[panel]);
|
||||
}
|
||||
|
||||
return "StepmaniaX invalid index";
|
||||
}
|
||||
|
||||
SmxStageDevice::SmxStageDevice() :
|
||||
m_lightData(TOTAL_LIGHT_COUNT*LEDS_PER_PAD),
|
||||
m_lightDataMutex()
|
||||
{
|
||||
}
|
||||
|
||||
bool SmxStageDevice::Initialize() {
|
||||
SMXWrapper::getInstance().SMX_Start(&NullSmxCallback, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SmxStageDevice::SetLightByIndex(size_t index, uint8_t value) {
|
||||
const size_t subpixel = index%3;
|
||||
const size_t panel = (index/3)%PANEL_COUNT;
|
||||
const size_t pad = (index/3)/PANEL_COUNT;
|
||||
|
||||
m_lightDataMutex.lock();
|
||||
for(size_t i = 0; i < LEDS_PER_PAD; ++i) {
|
||||
m_lightData[(pad*PANEL_COUNT*LEDS_PER_PAD*3)+(panel*LEDS_PER_PAD*3)+(i*3)+subpixel] = value;
|
||||
}
|
||||
m_lightDataMutex.unlock();
|
||||
}
|
||||
|
||||
void SmxStageDevice::Update() {
|
||||
m_lightDataMutex.lock();
|
||||
SMXWrapper::getInstance().SMX_SetLights2(reinterpret_cast<const char*>(&m_lightData[0]), m_lightData.size());
|
||||
m_lightDataMutex.unlock();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "smx.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
namespace rawinput {
|
||||
class SmxStageDevice {
|
||||
public:
|
||||
static constexpr int PAD_COUNT = 2;
|
||||
static constexpr int PANEL_COUNT = 9;
|
||||
static constexpr int LEDS_PER_PAD = 25;
|
||||
static constexpr int TOTAL_LIGHT_COUNT = PAD_COUNT*PANEL_COUNT*3;
|
||||
|
||||
static inline size_t GetPanelIndexR(size_t pad, size_t panel) {return ((pad*PANEL_COUNT)+panel)*3 + 0;};
|
||||
static inline size_t GetPanelIndexG(size_t pad, size_t panel) {return ((pad*PANEL_COUNT)+panel)*3 + 1;};
|
||||
static inline size_t GetPanelIndexB(size_t pad, size_t panel) {return ((pad*PANEL_COUNT)+panel)*3 + 2;};
|
||||
|
||||
static std::string GetLightNameByIndex(size_t index);
|
||||
|
||||
SmxStageDevice();
|
||||
bool Initialize();
|
||||
void SetLightByIndex(size_t index, uint8_t value);
|
||||
void Update();
|
||||
private:
|
||||
std::vector<uint8_t> m_lightData;
|
||||
std::mutex m_lightDataMutex;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user