Skip to content

Commit 70dad8e

Browse files
committed
Handle MIDI device disconnect and reconnect
1 parent 497b3b3 commit 70dad8e

File tree

2 files changed

+101
-17
lines changed

2 files changed

+101
-17
lines changed

plugins/midi/midi-helpers.cpp

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,55 @@ namespace advss {
1717
std::map<std::pair<MidiDeviceType, std::string>, MidiDeviceInstance *>
1818
MidiDeviceInstance::devices = {};
1919

20+
static bool setupDeviceObservers()
21+
{
22+
static std::vector<libremidi::observer> observers;
23+
for (auto api : libremidi::available_apis()) {
24+
libremidi::observer_configuration cbs;
25+
cbs.input_added = [=](const libremidi::input_port &p) {
26+
auto dev = MidiDeviceInstance::GetDevice(p);
27+
if (!dev) {
28+
return;
29+
}
30+
blog(LOG_INFO, "MIDI input connected: %s",
31+
p.port_name.c_str());
32+
dev->ClosePort();
33+
dev->OpenPort();
34+
};
35+
cbs.input_removed = [=](const libremidi::input_port &p) {
36+
auto dev = MidiDeviceInstance::GetDevice(p);
37+
if (!dev) {
38+
return;
39+
}
40+
blog(LOG_INFO, "MIDI input removed: %s",
41+
p.port_name.c_str());
42+
};
43+
cbs.output_added = [=](const libremidi::output_port &p) {
44+
auto dev = MidiDeviceInstance::GetDevice(p);
45+
if (!dev) {
46+
return;
47+
}
48+
blog(LOG_INFO, "MIDI output connected: %s",
49+
p.port_name.c_str());
50+
dev->ClosePort();
51+
dev->OpenPort();
52+
};
53+
cbs.output_removed = [=](const libremidi::output_port &p) {
54+
auto dev = MidiDeviceInstance::GetDevice(p);
55+
if (!dev) {
56+
return;
57+
}
58+
blog(LOG_INFO, "MIDI output removed: %s",
59+
p.port_name.c_str());
60+
};
61+
observers.emplace_back(
62+
cbs, libremidi::observer_configuration_for(api));
63+
}
64+
return true;
65+
}
66+
67+
static bool deviceObserversAreSetup = setupDeviceObservers();
68+
2069
void MidiDeviceInstance::ResetAllDevices()
2170
{
2271
for (auto const &[_, device] : MidiDeviceInstance::devices) {
@@ -212,8 +261,9 @@ bool MidiMessage::Matches(const MidiMessage &m) const
212261
return channelMatch && noteMatch && valueMatch && typeMatch;
213262
}
214263

215-
MidiDeviceInstance *MidiDeviceInstance::GetDevice(MidiDeviceType type,
216-
const std::string &name)
264+
MidiDeviceInstance *
265+
MidiDeviceInstance::GetDeviceAndOpen(MidiDeviceType type,
266+
const std::string &name)
217267
{
218268
if (name.empty()) {
219269
return nullptr;
@@ -252,6 +302,30 @@ getNameFromPortInformation(const libremidi::port_information &info)
252302
return "[" + name;
253303
}
254304

305+
MidiDeviceInstance *
306+
advss::MidiDeviceInstance::GetDevice(const libremidi::input_port &p)
307+
{
308+
auto key = std::make_pair(MidiDeviceType::INPUT,
309+
getNameFromPortInformation(p));
310+
auto it = MidiDeviceInstance::devices.find(key);
311+
if (it == devices.end()) {
312+
return nullptr;
313+
}
314+
return it->second;
315+
}
316+
317+
MidiDeviceInstance *
318+
advss::MidiDeviceInstance::GetDevice(const libremidi::output_port &p)
319+
{
320+
auto key = std::make_pair(MidiDeviceType::OUTPUT,
321+
getNameFromPortInformation(p));
322+
auto it = MidiDeviceInstance::devices.find(key);
323+
if (it == devices.end()) {
324+
return nullptr;
325+
}
326+
return it->second;
327+
}
328+
255329
static inline QStringList getInputDeviceNames()
256330
{
257331
QStringList devices;
@@ -305,10 +379,11 @@ static std::string getPortNameFromNumber(MidiDeviceType type, int port)
305379
return devices.at(port).toStdString();
306380
}
307381

308-
MidiDeviceInstance *MidiDeviceInstance::GetDevice(MidiDeviceType type, int port)
382+
MidiDeviceInstance *MidiDeviceInstance::GetDeviceAndOpen(MidiDeviceType type,
383+
int port)
309384
{
310385
std::string name = getPortNameFromNumber(type, port);
311-
return GetDevice(type, name);
386+
return GetDeviceAndOpen(type, name);
312387
}
313388

314389
void MidiDevice::Save(obs_data_t *obj) const
@@ -327,13 +402,13 @@ void MidiDevice::Load(obs_data_t *obj)
327402
// TODO: Remove this fallback at some point
328403
if (obs_data_has_user_value(data, "port")) {
329404
auto port = obs_data_get_int(data, "port");
330-
_dev = MidiDeviceInstance::GetDevice(_type, port);
405+
_dev = MidiDeviceInstance::GetDeviceAndOpen(_type, port);
331406
if (_dev) {
332407
_name = _dev->_name;
333408
}
334409
} else {
335410
_name = obs_data_get_string(data, "portName");
336-
_dev = MidiDeviceInstance::GetDevice(_type, _name);
411+
_dev = MidiDeviceInstance::GetDeviceAndOpen(_type, _name);
337412
}
338413
}
339414

@@ -372,8 +447,7 @@ getInPortFromName(const std::string &name)
372447

373448
bool MidiDeviceInstance::OpenPort()
374449
{
375-
if ((_type == MidiDeviceType::INPUT && _in.is_port_open()) ||
376-
(_type == MidiDeviceType::OUTPUT && _out.is_port_open())) {
450+
if (IsOpened()) {
377451
return true;
378452
}
379453

@@ -437,10 +511,15 @@ bool MidiDeviceInstance::OpenPort()
437511
return false;
438512
}
439513

514+
bool MidiDeviceInstance::IsOpened() const
515+
{
516+
return (_type == MidiDeviceType::INPUT && _in.is_port_open()) ||
517+
(_type == MidiDeviceType::OUTPUT && _out.is_port_open());
518+
}
519+
440520
void MidiDeviceInstance::ClosePort()
441521
{
442-
if ((_type == MidiDeviceType::INPUT && !_in.is_port_open()) ||
443-
(_type == MidiDeviceType::OUTPUT && !_out.is_port_open())) {
522+
if (!IsOpened()) {
444523
return;
445524
}
446525

@@ -542,7 +621,6 @@ MidiMessageBuffer MidiDeviceInstance::RegisterForMidiMessages()
542621

543622
void MidiDeviceInstance::ReceiveMidiMessage(libremidi::message &&msg)
544623
{
545-
auto lock = LockContext();
546624
_dispatcher.DispatchMessage(msg);
547625
vblog(LOG_INFO, "received midi: %s",
548626
MidiMessage::ToString(msg).c_str());
@@ -590,7 +668,7 @@ void MidiDeviceSelection::IdxChangedHelper(int idx)
590668
}
591669

592670
auto name = currentText().toStdString();
593-
auto devInstance = MidiDeviceInstance::GetDevice(_type, name);
671+
auto devInstance = MidiDeviceInstance::GetDeviceAndOpen(_type, name);
594672
if (!devInstance) {
595673
DisplayMessage(obs_module_text(
596674
"AdvSceneSwitcher.midi.deviceOpenFail"));

plugins/midi/midi-helpers.hpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,22 @@ enum class MidiDeviceType {
6464

6565
class MidiDeviceInstance {
6666
public:
67-
static MidiDeviceInstance *GetDevice(MidiDeviceType type,
68-
const std::string &);
69-
static MidiDeviceInstance *GetDevice(MidiDeviceType type, int port);
67+
static MidiDeviceInstance *GetDeviceAndOpen(MidiDeviceType type,
68+
const std::string &);
69+
static MidiDeviceInstance *GetDeviceAndOpen(MidiDeviceType type,
70+
int port);
71+
static MidiDeviceInstance *GetDevice(const libremidi::input_port &p);
72+
static MidiDeviceInstance *GetDevice(const libremidi::output_port &p);
73+
7074
static void ResetAllDevices();
7175

76+
bool OpenPort();
77+
void ClosePort();
78+
7279
private:
7380
MidiDeviceInstance() = default;
7481
~MidiDeviceInstance() = default;
75-
bool OpenPort();
76-
void ClosePort();
82+
bool IsOpened() const;
7783
bool SendMessge(const MidiMessage &);
7884
[[nodiscard]] MidiMessageBuffer RegisterForMidiMessages();
7985
void ReceiveMidiMessage(libremidi::message &&);

0 commit comments

Comments
 (0)