Skip to content

Commit

Permalink
midi: update with libremidi changes
Browse files Browse the repository at this point in the history
implement support for keyboard input on macos
  • Loading branch information
jcelerier committed Jan 9, 2025
1 parent 093f648 commit 1b378f5
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 46 deletions.
3 changes: 3 additions & 0 deletions src/app/Info.plist.in
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>

<key>NSAppleEventsUsageDescription</key>
<string>ossia score needs assistive access for MIDI keyboard input and minimap movement</string>

<key>NSMicrophoneUsageDescription</key>
<string>ossia score needs microphone permission for audio input.</string>

Expand Down
49 changes: 8 additions & 41 deletions src/plugins/score-plugin-protocols/Protocols/MIDI/MIDIDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#include <Explorer/DocumentPlugin/DeviceDocumentPlugin.hpp>

#include <Protocols/MIDI/MIDIKeyboardEventFilter.linux.hpp>
#include <Protocols/MIDI/MIDIKeyboardEventFilter.macos.hpp>
#include <Protocols/MIDI/MIDISpecificSettings.hpp>

#include <score/application/GUIApplicationContext.hpp>
Expand All @@ -29,48 +31,11 @@
#include <libremidi/configurations.hpp>
// clang-format on

#include <memory>
#include <score_plugin_protocols_export.h>

#include <memory>
namespace Protocols
{
class MidiKeyboardEventFilter : public QObject
{
public:
libremidi::kbd_input_configuration::scancode_callback press, release;
MidiKeyboardEventFilter(
libremidi::kbd_input_configuration::scancode_callback press,
libremidi::kbd_input_configuration::scancode_callback release)
: press{std::move(press)}
, release{std::move(release)}
, target{score::GUIAppContext().mainWindow}
{
// X11 quirk
if(qApp->platformName() == "xcb")
scanCodeOffset = -8;
}

bool eventFilter(QObject* object, QEvent* event)
{
if(object == target)
{
if(event->type() == QEvent::KeyPress)
{
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if(!keyEvent->isAutoRepeat())
press(keyEvent->nativeScanCode() + scanCodeOffset);
}
else if(event->type() == QEvent::KeyRelease)
{
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if(!keyEvent->isAutoRepeat())
release(keyEvent->nativeScanCode() + scanCodeOffset);
}
}
return false;
}
QObject* target{};
int scanCodeOffset{0};
};

MIDIDevice::MIDIDevice(
const Device::DeviceSettings& settings, const ossia::net::network_context_ptr& ctx)
Expand Down Expand Up @@ -119,7 +84,7 @@ bool MIDIDevice::reconnect()
case libremidi::API::ALSA_SEQ: {
conf.timestamps = libremidi::timestamp_mode::AudioFrame;

auto ptr = std::any_cast<libremidi::alsa_seq::input_configuration>(&api_conf);
auto ptr = std::get_if<libremidi::alsa_seq::input_configuration>(&api_conf);
SCORE_ASSERT(ptr);
ptr->client_name = "ossia score";
break;
Expand All @@ -133,11 +98,13 @@ bool MIDIDevice::reconnect()
break;
}
case libremidi::API::KEYBOARD: {
auto ptr = std::any_cast<libremidi::kbd_input_configuration>(&api_conf);
auto ptr = std::get_if<libremidi::kbd_input_configuration>(&api_conf);
SCORE_ASSERT(ptr);
ptr->set_input_scancode_callbacks = [this](auto keypress, auto keyrelease) {
m_kbdfilter = new MidiKeyboardEventFilter{keypress, keyrelease};
#if !defined(__APPLE__)
qApp->installEventFilter(m_kbdfilter);
#endif
};
break;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#pragma once
#if !defined(__APPLE__)
#include <score/application/GUIApplicationContext.hpp>

#include <QGuiApplication>
#include <QKeyEvent>
#include <QMainWindow>
#include <QObject>

#include <libremidi/backends/keyboard/config.hpp>

namespace Protocols
{
class MidiKeyboardEventFilter : public QObject
{
public:
libremidi::kbd_input_configuration::scancode_callback press, release;
MidiKeyboardEventFilter(
libremidi::kbd_input_configuration::scancode_callback press,
libremidi::kbd_input_configuration::scancode_callback release)
: press{std::move(press)}
, release{std::move(release)}
, target{score::GUIAppContext().mainWindow}
{
}

bool eventFilter(QObject* object, QEvent* event)
{
if(object == target)
{
if(event->type() == QEvent::KeyPress)
{
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if(!keyEvent->isAutoRepeat())
press(key(*keyEvent));
}
else if(event->type() == QEvent::KeyRelease)
{
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if(!keyEvent->isAutoRepeat())
release(key(*keyEvent));
}
}
return false;
}

inline uint32_t key(QKeyEvent& e)
{
#if defined(__linux__)
// X11 quirk
static const int scanCodeOffset = (qApp->platformName() == "xcb") ? -8 : 0;
return e.nativeScanCode() - scanCodeOffset;
#elif defined(__APPLE__)
return e.nativeVirtualKey();
#else
return e.nativeScanCode();
#endif
}
QObject* target{};
};
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#pragma once
#if defined(__APPLE__)
#include <QDebug>
#include <QObject>

#include <CoreGraphics/CoreGraphics.h>
#include <libremidi/backends/keyboard/config.hpp>

namespace Protocols
{
class MidiKeyboardEventFilter : public QObject
{
public:
libremidi::kbd_input_configuration::scancode_callback press, release;
MidiKeyboardEventFilter(
libremidi::kbd_input_configuration::scancode_callback press,
libremidi::kbd_input_configuration::scancode_callback release)
: press{std::move(press)}
, release{std::move(release)}
{
CGEventMask mask = CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp)
| CGEventMaskBit(kCGEventScrollWheel)
| CGEventMaskBit(kCGEventLeftMouseDown)
| CGEventMaskBit(kCGEventRightMouseDown);
auto eventTap = CGEventTapCreate(
kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, mask,
[](CGEventTapProxy, CGEventType type, CGEventRef event, void* ctx) {
auto& self = *decltype(this)(ctx);
switch(type)
{
case kCGEventKeyDown:
self.press(CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode));
break;
case kCGEventKeyUp:
self.release(CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode));
break;
}
return event;
}, this);
if(eventTap == nullptr)
return;

CFRunLoopAddSource(
CFRunLoopGetCurrent(),
CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0),
kCFRunLoopCommonModes);
// Enable the event tap.
CGEventTapEnable(eventTap, true);
}
};
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
#include <QTimer>
#include <QUrl>

#include <libremidi/backends.hpp>
// clang-format off
#include <libremidi/libremidi.hpp>
#include <libremidi/backends.hpp>
// clang-format on
namespace Device
{
class DeviceInterface;
Expand Down Expand Up @@ -71,7 +73,7 @@ template <ossia::net::midi::midi_info::Type Type>
class MidiEnumerator : public Device::DeviceEnumerator
{
libremidi::API m_api = getCurrentAPI();
std::any m_observer_conf = [this] {
libremidi::observer_api_configuration m_observer_conf = [this] {
auto api_conf = libremidi::observer_configuration_for(m_api);

libremidi::midi_any::for_observer_configuration([&](auto& conf) {
Expand Down
3 changes: 1 addition & 2 deletions src/plugins/score-plugin-protocols/Protocols/MIDIUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

#include <score/application/GUIApplicationContext.hpp>

#include <libremidi/backends.hpp>
#include <libremidi/libremidi.hpp>
#include <libremidi/api.hpp>
namespace Protocols
{
inline libremidi::API getCurrentAPI()
Expand Down

0 comments on commit 1b378f5

Please sign in to comment.