Update to spice2x-25-04-25 (pre-apply)

> broken commit
This commit is contained in:
[ ]
2025-05-07 00:25:31 +09:00
parent 04dee88276
commit c94de456b5
543 changed files with 78491 additions and 91698 deletions
+276 -87
View File
@@ -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();
}