Implemented Midi devices changed notification for CoreMidi

This commit is contained in:
Casper Jeukendrup 2021-06-04 22:06:46 +02:00
parent e285f6ef87
commit 784bed3d97
7 changed files with 76 additions and 68 deletions

View file

@ -31,7 +31,7 @@
namespace mu::midi {
class IMidiOutPort : MODULE_EXPORT_INTERFACE
{
INTERFACE_ID(IMidiPort)
INTERFACE_ID(IMidiOutPort)
public:
virtual ~IMidiOutPort() = default;

View file

@ -26,7 +26,7 @@
using namespace mu::midi;
std::vector<MidiDevice> DummyMidiOutPort::devices() const
MidiDeviceList DummyMidiOutPort::devices() const
{
MidiDevice d;
d.id = "dummy";
@ -34,7 +34,12 @@ std::vector<MidiDevice> DummyMidiOutPort::devices() const
return { d };
}
mu::Ret DummyMidiOutPort::connect(const std::string& deviceID)
mu::async::Notification DummyMidiOutPort::devicesChanged() const
{
return {};
}
mu::Ret DummyMidiOutPort::connect(const MidiDeviceID& deviceID)
{
LOGI() << "deviceID: " << deviceID;
m_connectedDeviceID = deviceID;
@ -52,7 +57,7 @@ bool DummyMidiOutPort::isConnected() const
return !m_connectedDeviceID.empty();
}
std::string DummyMidiOutPort::deviceID() const
MidiDeviceID DummyMidiOutPort::deviceID() const
{
return m_connectedDeviceID;
}

View file

@ -28,19 +28,18 @@ namespace mu::midi {
class DummyMidiOutPort : public IMidiOutPort
{
public:
MidiDeviceList devices() const override;
async::Notification devicesChanged() const override;
std::vector<MidiDevice> devices() const override;
Ret connect(const std::string& deviceID) override;
Ret connect(const MidiDeviceID& deviceID) override;
void disconnect() override;
bool isConnected() const override;
std::string deviceID() const override;
MidiDeviceID deviceID() const override;
Ret sendEvent(const Event& e) override;
private:
std::string m_connectedDeviceID;
MidiDeviceID m_connectedDeviceID;
};
}

View file

@ -42,25 +42,6 @@ CoreMidiInPort::CoreMidiInPort()
{
m_core = std::unique_ptr<Core>(new Core());
initCore();
m_devicesListener.startWithCallback([this]() {
return devices();
});
m_devicesListener.devicesChanged().onNotify(this, [this]() {
bool connectedDeviceRemoved = true;
for (const MidiDevice& device: devices()) {
if (m_deviceID == device.id) {
connectedDeviceRemoved = false;
}
}
if (connectedDeviceRemoved) {
disconnect();
}
m_devicesChanged.notify();
});
}
CoreMidiInPort::~CoreMidiInPort()
@ -84,7 +65,6 @@ CoreMidiInPort::~CoreMidiInPort()
MidiDeviceList CoreMidiInPort::devices() const
{
std::lock_guard lock(m_devicesMutex);
MidiDeviceList ret;
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
@ -130,8 +110,7 @@ static void proccess(const MIDIPacketList* list, void* readProc, void* srcConn)
uint32_t message(0);
memcpy(&message, packet->data, std::min(sizeof(message), sizeof(char) * packet->length));
self->doProcess(message, packet->timeStamp);
}
if (packet->length > 4) {
} else if (packet->length > 4) {
LOGW() << "unsupported midi message size " << packet->length << " bytes";
}
@ -143,14 +122,42 @@ void CoreMidiInPort::initCore()
{
OSStatus result;
static auto onCoreMidiNotificationReceived = [](const MIDINotification* notification, void* refCon) {
auto self = static_cast<CoreMidiInPort*>(refCon);
IF_ASSERT_FAILED(self) {
return;
}
switch (notification->messageID) {
case kMIDIMsgObjectAdded:
case kMIDIMsgObjectRemoved:
self->devicesChanged().notify();
break;
// General message that should be ignored because we handle specific ones
case kMIDIMsgSetupChanged:
// Questionable whether we should send a notification for this ones
// Possibly we should send a notification when the changed property is the device name
case kMIDIMsgPropertyChanged:
case kMIDIMsgThruConnectionsChanged:
case kMIDIMsgSerialPortOwnerChanged:
case kMIDIMsgIOError:
break;
}
};
QString name = "MuseScore";
result = MIDIClientCreate(name.toCFString(), nullptr, nullptr, &m_core->client);
result = MIDIClientCreate(name.toCFString(), onCoreMidiNotificationReceived, this, &m_core->client);
IF_ASSERT_FAILED(result == noErr) {
LOGE() << "failed create midi input client";
return;
}
QString portName = "MuseScore MIDI input port";
// TODO: MIDIInputPortCreate is deprecated according to the documentation.
// Need to use MIDIInputPortCreateWithProtocol instead.
result = MIDIInputPortCreate(m_core->client, portName.toCFString(), proccess, this, &m_core->inputPort);
IF_ASSERT_FAILED(result == noErr) {
LOGE() << "failed create midi input port";

View file

@ -24,12 +24,10 @@
#include <memory>
#include "async/asyncable.h"
#include "imidiinport.h"
#include "internal/midideviceslistener.h"
namespace mu::midi {
class CoreMidiInPort : public IMidiInPort, public async::Asyncable
class CoreMidiInPort : public IMidiInPort
{
public:
CoreMidiInPort();
@ -57,13 +55,10 @@ private:
struct Core;
std::unique_ptr<Core> m_core;
MidiDeviceID m_deviceID;
async::Notification m_devicesChanged;
bool m_running = false;
async::Channel<tick_t, Event> m_eventReceived;
async::Notification m_devicesChanged;
MidiDevicesListener m_devicesListener;
mutable std::mutex m_devicesMutex;
};
}

View file

@ -43,25 +43,6 @@ CoreMidiOutPort::CoreMidiOutPort()
{
m_core = std::unique_ptr<Core>(new Core());
initCore();
m_devicesListener.startWithCallback([this]() {
return devices();
});
m_devicesListener.devicesChanged().onNotify(this, [this]() {
bool connectedDeviceRemoved = true;
for (const MidiDevice& device: devices()) {
if (m_deviceID == device.id) {
connectedDeviceRemoved = false;
}
}
if (connectedDeviceRemoved) {
disconnect();
}
m_devicesChanged.notify();
});
}
CoreMidiOutPort::~CoreMidiOutPort()
@ -81,8 +62,35 @@ CoreMidiOutPort::~CoreMidiOutPort()
void CoreMidiOutPort::initCore()
{
OSStatus result;
static auto onCoreMidiNotificationReceived = [](const MIDINotification* notification, void* refCon) {
auto self = static_cast<CoreMidiOutPort*>(refCon);
IF_ASSERT_FAILED(self) {
return;
}
switch (notification->messageID) {
case kMIDIMsgObjectAdded:
case kMIDIMsgObjectRemoved:
self->devicesChanged().notify();
break;
// General message that should be ignored because we handle specific ones
case kMIDIMsgSetupChanged:
// Questionable whether we should send a notification for this ones
// Possibly we should send a notification when the changed property is the device name
case kMIDIMsgPropertyChanged:
case kMIDIMsgThruConnectionsChanged:
case kMIDIMsgSerialPortOwnerChanged:
case kMIDIMsgIOError:
break;
}
};
QString name = "MuseScore";
result = MIDIClientCreate(name.toCFString(), nullptr, nullptr, &m_core->client);
result = MIDIClientCreate(name.toCFString(), onCoreMidiNotificationReceived, this, &m_core->client);
IF_ASSERT_FAILED(result == noErr) {
LOGE() << "failed create midi output client";
return;
@ -97,7 +105,6 @@ void CoreMidiOutPort::initCore()
MidiDeviceList CoreMidiOutPort::devices() const
{
std::lock_guard lock(m_devicesMutex);
MidiDeviceList ret;
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);

View file

@ -24,12 +24,10 @@
#include <memory>
#include "async/asyncable.h"
#include "midi/imidioutport.h"
#include "internal/midideviceslistener.h"
namespace mu::midi {
class CoreMidiOutPort : public IMidiOutPort, public async::Asyncable
class CoreMidiOutPort : public IMidiOutPort
{
public:
CoreMidiOutPort();
@ -53,9 +51,6 @@ private:
MidiDeviceID m_deviceID;
async::Notification m_devicesChanged;
MidiDevicesListener m_devicesListener;
mutable std::mutex m_devicesMutex;
};
}