libremidi update to v5.4.3 and added virtual midi out as default
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include <libremidi/backends/android/config.hpp>
|
||||
#include <libremidi/backends/android/midi_in.hpp>
|
||||
#include <libremidi/backends/android/midi_out.hpp>
|
||||
#include <libremidi/backends/android/observer.hpp>
|
||||
|
||||
NAMESPACE_LIBREMIDI
|
||||
{
|
||||
namespace android
|
||||
{
|
||||
struct backend
|
||||
{
|
||||
using midi_in = android::midi_in;
|
||||
using midi_out = android::midi_out;
|
||||
using midi_observer = android::observer;
|
||||
using midi_in_configuration = android::input_configuration;
|
||||
using midi_out_configuration = android::output_configuration;
|
||||
using midi_observer_configuration = android::observer_configuration;
|
||||
static const constexpr auto API = libremidi::API::ANDROID_AMIDI;
|
||||
static const constexpr std::string_view name = "android";
|
||||
static const constexpr std::string_view display_name = "Android MIDI API";
|
||||
|
||||
static bool available() noexcept
|
||||
{
|
||||
#if defined(__ANDROID__)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include <libremidi/config.hpp>
|
||||
|
||||
NAMESPACE_LIBREMIDI
|
||||
{
|
||||
namespace android
|
||||
{
|
||||
struct input_configuration
|
||||
{
|
||||
std::string_view client_name = "libremidi client";
|
||||
};
|
||||
|
||||
struct output_configuration
|
||||
{
|
||||
std::string_view client_name = "libremidi client";
|
||||
};
|
||||
|
||||
struct observer_configuration
|
||||
{
|
||||
std::string_view client_name = "libremidi client";
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
#include <libremidi/backends/android/helpers.hpp>
|
||||
#include <libremidi/backends/android/midi_in.hpp>
|
||||
#include <libremidi/backends/android/midi_out.hpp>
|
||||
|
||||
NAMESPACE_LIBREMIDI::android
|
||||
{
|
||||
// Main source of knowledge is RtMidi implementation
|
||||
// which was done by Yellow Labrador
|
||||
|
||||
JNIEnv* context::get_thread_env()
|
||||
{
|
||||
JNIEnv* env;
|
||||
JavaVM* jvm;
|
||||
jsize count;
|
||||
if (JNI_GetCreatedJavaVMs(&jvm, 1, &count) != JNI_OK || count < 1)
|
||||
{
|
||||
LOGE("No JVM found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (jvm->GetEnv((void**)&env, JNI_VERSION_1_6) == JNI_EDETACHED)
|
||||
{
|
||||
if (jvm->AttachCurrentThread(&env, nullptr) != JNI_OK)
|
||||
{
|
||||
LOGE("Failed to attach thread");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
jobject context::get_context(JNIEnv* env)
|
||||
{
|
||||
auto activity_thread = env->FindClass("android/app/ActivityThread");
|
||||
auto current_activity_thread = env->GetStaticMethodID(
|
||||
activity_thread, "currentActivityThread", "()Landroid/app/ActivityThread;");
|
||||
auto at = env->CallStaticObjectMethod(activity_thread, current_activity_thread);
|
||||
|
||||
if (!at)
|
||||
{
|
||||
LOGE("Failed to get ActivityThread");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto get_application
|
||||
= env->GetMethodID(activity_thread, "getApplication", "()Landroid/app/Application;");
|
||||
auto app = env->CallObjectMethod(at, get_application);
|
||||
|
||||
if (!app)
|
||||
{
|
||||
LOGE("Failed to get Application");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
jobject context::get_midi_manager(JNIEnv* env, jobject ctx)
|
||||
{
|
||||
auto context_class = env->FindClass("android/content/Context");
|
||||
auto get_system_service = env->GetMethodID(
|
||||
context_class, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
|
||||
auto midi_service_str = env->NewStringUTF("midi");
|
||||
auto service = env->CallObjectMethod(ctx, get_system_service, midi_service_str);
|
||||
env->DeleteLocalRef(midi_service_str);
|
||||
return service;
|
||||
}
|
||||
|
||||
void context::refresh_midi_devices(JNIEnv* env, jobject ctx, bool is_output)
|
||||
{
|
||||
cleanup_devices(env);
|
||||
|
||||
auto midi_service = get_midi_manager(env, ctx);
|
||||
if (!midi_service)
|
||||
return;
|
||||
|
||||
auto midi_mgr_class = env->FindClass("android/media/midi/MidiManager");
|
||||
auto get_devices_method
|
||||
= env->GetMethodID(midi_mgr_class, "getDevices", "()[Landroid/media/midi/MidiDeviceInfo;");
|
||||
auto device_array = (jobjectArray)env->CallObjectMethod(midi_service, get_devices_method);
|
||||
|
||||
auto device_info_class = env->FindClass("android/media/midi/MidiDeviceInfo");
|
||||
auto get_input_count = env->GetMethodID(device_info_class, "getInputPortCount", "()I");
|
||||
auto get_output_count = env->GetMethodID(device_info_class, "getOutputPortCount", "()I");
|
||||
|
||||
jsize count = env->GetArrayLength(device_array);
|
||||
for (jsize i = 0; i < count; ++i)
|
||||
{
|
||||
auto device_info = env->GetObjectArrayElement(device_array, i);
|
||||
int port_count = is_output ? env->CallIntMethod(device_info, get_input_count)
|
||||
: env->CallIntMethod(device_info, get_output_count);
|
||||
|
||||
if (port_count > 0)
|
||||
{
|
||||
midi_devices.push_back(env->NewGlobalRef(device_info));
|
||||
}
|
||||
env->DeleteLocalRef(device_info);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(device_array);
|
||||
env->DeleteLocalRef(midi_service);
|
||||
}
|
||||
|
||||
std::string context::port_name(JNIEnv* env, unsigned int port_number)
|
||||
{
|
||||
if (port_number >= midi_devices.size())
|
||||
{
|
||||
LOGE("Invalid port number");
|
||||
return "";
|
||||
}
|
||||
|
||||
auto device_info_class = env->FindClass("android/media/midi/MidiDeviceInfo");
|
||||
auto get_props_method
|
||||
= env->GetMethodID(device_info_class, "getProperties", "()Landroid/os/Bundle;");
|
||||
auto bundle = env->CallObjectMethod(midi_devices[port_number], get_props_method);
|
||||
|
||||
auto bundle_class = env->FindClass("android/os/Bundle");
|
||||
auto get_string_method
|
||||
= env->GetMethodID(bundle_class, "getString", "(Ljava/lang/String;)Ljava/lang/String;");
|
||||
auto key = env->NewStringUTF("name");
|
||||
auto name_str = (jstring)env->CallObjectMethod(bundle, get_string_method, key);
|
||||
|
||||
std::string result;
|
||||
if (name_str)
|
||||
{
|
||||
const char* name_chars = env->GetStringUTFChars(name_str, nullptr);
|
||||
result = name_chars;
|
||||
env->ReleaseStringUTFChars(name_str, name_chars);
|
||||
env->DeleteLocalRef(name_str);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(key);
|
||||
env->DeleteLocalRef(bundle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void context::cleanup_devices(JNIEnv* env)
|
||||
{
|
||||
for (jobject device : midi_devices)
|
||||
{
|
||||
env->DeleteGlobalRef(device);
|
||||
}
|
||||
midi_devices.clear();
|
||||
}
|
||||
|
||||
void context::open_device(jobject device_info, void* target, bool is_output)
|
||||
{
|
||||
auto env = get_thread_env();
|
||||
if (!env)
|
||||
return;
|
||||
|
||||
auto ctx = get_context(env);
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
auto midi_mgr = get_midi_manager(env, ctx);
|
||||
if (!midi_mgr)
|
||||
return;
|
||||
|
||||
// Check if we're on the main thread
|
||||
auto looper_class = env->FindClass("android/os/Looper");
|
||||
auto get_my_looper_method
|
||||
= env->GetStaticMethodID(looper_class, "myLooper", "()Landroid/os/Looper;");
|
||||
auto my_looper = env->CallStaticObjectMethod(looper_class, get_my_looper_method);
|
||||
|
||||
jobject handler = nullptr;
|
||||
if (!my_looper)
|
||||
{
|
||||
LOGI("Not on a Looper thread, using main looper");
|
||||
auto get_main_looper_method
|
||||
= env->GetStaticMethodID(looper_class, "getMainLooper", "()Landroid/os/Looper;");
|
||||
auto main_looper = env->CallStaticObjectMethod(looper_class, get_main_looper_method);
|
||||
|
||||
auto handler_class = env->FindClass("android/os/Handler");
|
||||
auto handler_ctor = env->GetMethodID(handler_class, "<init>", "(Landroid/os/Looper;)V");
|
||||
handler = env->NewObject(handler_class, handler_ctor, main_looper);
|
||||
|
||||
env->DeleteLocalRef(main_looper);
|
||||
}
|
||||
|
||||
// Create the callback listener
|
||||
auto callback_class = env->FindClass("dev/celtera/libremidi/MidiDeviceCallback");
|
||||
if (!callback_class)
|
||||
{
|
||||
LOGE("MidiDeviceCallback class not found - ensure the Java class is loaded");
|
||||
if (handler)
|
||||
env->DeleteLocalRef(handler);
|
||||
return;
|
||||
}
|
||||
|
||||
auto callback_ctor = env->GetMethodID(callback_class, "<init>", "(JZ)V");
|
||||
auto callback
|
||||
= env->NewObject(callback_class, callback_ctor, (jlong)target, (jboolean)is_output);
|
||||
|
||||
// Open the device
|
||||
auto midi_mgr_class = env->FindClass("android/media/midi/MidiManager");
|
||||
auto open_device_method = env->GetMethodID(
|
||||
midi_mgr_class, "openDevice",
|
||||
"(Landroid/media/midi/MidiDeviceInfo;Landroid/media/midi/"
|
||||
"MidiManager$OnDeviceOpenedListener;Landroid/os/Handler;)V");
|
||||
|
||||
env->CallVoidMethod(midi_mgr, open_device_method, device_info, callback, handler);
|
||||
|
||||
env->DeleteLocalRef(callback);
|
||||
if (handler)
|
||||
env->DeleteLocalRef(handler);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_dev_celtera_libremidi_MidiDeviceCallback_onDeviceOpened(
|
||||
JNIEnv* env, jobject /*thiz*/, jobject midi_device, jlong target_ptr, jboolean is_output)
|
||||
{
|
||||
if (!midi_device || target_ptr == 0)
|
||||
{
|
||||
LOGE("Invalid device or target pointer in callback");
|
||||
return;
|
||||
}
|
||||
|
||||
AMidiDevice* amidi_device = nullptr;
|
||||
AMidiDevice_fromJava(env, midi_device, &amidi_device);
|
||||
|
||||
if (!amidi_device)
|
||||
{
|
||||
LOGE("Failed to convert Java MIDI device to AMidiDevice");
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_output)
|
||||
{
|
||||
midi_out::open_callback(reinterpret_cast<midi_out*>(target_ptr), amidi_device);
|
||||
}
|
||||
else
|
||||
{
|
||||
midi_in::open_callback(reinterpret_cast<midi_in*>(target_ptr), amidi_device);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include <libremidi/error.hpp>
|
||||
|
||||
#include <amidi/AMidi.h>
|
||||
#include <android/log.h>
|
||||
|
||||
#include <jni.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define LOG_TAG "libremidi"
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
NAMESPACE_LIBREMIDI
|
||||
{
|
||||
namespace android
|
||||
{
|
||||
struct context
|
||||
{
|
||||
static inline std::string client_name;
|
||||
static inline std::vector<jobject> midi_devices;
|
||||
|
||||
static JNIEnv* get_thread_env();
|
||||
static jobject get_context(JNIEnv* env);
|
||||
static jobject get_midi_manager(JNIEnv* env, jobject context);
|
||||
static void refresh_midi_devices(JNIEnv* env, jobject context, bool is_output);
|
||||
static void open_device(jobject device_info, void* target, bool is_output);
|
||||
static std::string port_name(JNIEnv* env, unsigned int port_number);
|
||||
static void cleanup_devices(JNIEnv* env);
|
||||
};
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_dev_celtera_libremidi_MidiDeviceCallback_onDeviceOpened(
|
||||
JNIEnv* env, jobject /*thiz*/, jobject midi_device, jlong target_ptr, jboolean is_output);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
#pragma once
|
||||
#include <libremidi/backends/android/config.hpp>
|
||||
#include <libremidi/backends/android/helpers.hpp>
|
||||
#include <libremidi/detail/midi_in.hpp>
|
||||
#include <libremidi/detail/midi_stream_decoder.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
NAMESPACE_LIBREMIDI
|
||||
{
|
||||
namespace android
|
||||
{
|
||||
class midi_in
|
||||
: public midi1::in_api
|
||||
, public error_handler
|
||||
{
|
||||
public:
|
||||
explicit midi_in(
|
||||
libremidi::input_configuration&& conf, libremidi::android::input_configuration aconf)
|
||||
: configuration{std::move(conf)}
|
||||
{
|
||||
if (!context::client_name.empty() && context::client_name != aconf.client_name)
|
||||
{
|
||||
libremidi_handle_error(
|
||||
configuration, "Android backend only supports one client name per process");
|
||||
return;
|
||||
}
|
||||
context::client_name = std::string(aconf.client_name);
|
||||
client_open_ = stdx::error{};
|
||||
}
|
||||
|
||||
~midi_in() override { close_port(); }
|
||||
|
||||
libremidi::API get_current_api() const noexcept override
|
||||
{
|
||||
return libremidi::API::ANDROID_AMIDI;
|
||||
}
|
||||
|
||||
stdx::error open_port(const input_port& port, std::string_view /*name*/) override
|
||||
{
|
||||
if (is_port_open())
|
||||
{
|
||||
libremidi_handle_error(configuration, "midi_in::open_port: a port is already open");
|
||||
return std::errc{};
|
||||
}
|
||||
|
||||
const unsigned int port_number = port.port;
|
||||
auto env = context::get_thread_env();
|
||||
if (!env)
|
||||
{
|
||||
libremidi_handle_error(configuration, "midi_in::open_port: failed to get JNI environment");
|
||||
return std::errc{};
|
||||
}
|
||||
|
||||
auto ctx = context::get_context(env);
|
||||
if (!ctx)
|
||||
{
|
||||
libremidi_handle_error(configuration, "midi_in::open_port: failed to get Android context");
|
||||
return std::errc{};
|
||||
}
|
||||
|
||||
context::refresh_midi_devices(env, ctx, false);
|
||||
|
||||
if (port_number >= context::midi_devices.size())
|
||||
{
|
||||
libremidi_handle_error(configuration, "midi_in::open_port: invalid port number");
|
||||
return std::errc{};
|
||||
}
|
||||
|
||||
port_open = true;
|
||||
running = true;
|
||||
context::open_device(context::midi_devices[port_number], this, false);
|
||||
return stdx::error{};
|
||||
}
|
||||
|
||||
stdx::error close_port() override
|
||||
{
|
||||
if (!is_port_open())
|
||||
return std::errc{};
|
||||
|
||||
running = false;
|
||||
|
||||
if (poll_thread.joinable())
|
||||
{
|
||||
poll_thread.join();
|
||||
}
|
||||
|
||||
if (midi_output_port)
|
||||
{
|
||||
AMidiOutputPort_close(midi_output_port);
|
||||
midi_output_port = nullptr;
|
||||
}
|
||||
|
||||
if (receive_device)
|
||||
{
|
||||
AMidiDevice_release(receive_device);
|
||||
receive_device = nullptr;
|
||||
}
|
||||
|
||||
port_open = false;
|
||||
return stdx::error{};
|
||||
}
|
||||
|
||||
bool is_port_open() const noexcept { return port_open; }
|
||||
|
||||
void start_midi_thread() { poll_thread = std::thread(&midi_in::poll_midi, this); }
|
||||
|
||||
timestamp absolute_timestamp() const noexcept override { return system_ns(); }
|
||||
void poll_midi()
|
||||
{
|
||||
static constexpr timestamp_backend_info timestamp_info{
|
||||
.has_absolute_timestamps = false,
|
||||
.absolute_is_monotonic = false,
|
||||
.has_samples = false,
|
||||
};
|
||||
|
||||
uint8_t buffer[1024];
|
||||
int32_t op_code;
|
||||
int64_t timestamp;
|
||||
size_t num_bytes;
|
||||
|
||||
while (running)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
|
||||
ssize_t num_messages = AMidiOutputPort_receive(
|
||||
midi_output_port, &op_code, buffer, sizeof(buffer), &num_bytes, ×tamp);
|
||||
|
||||
if (num_messages < 0)
|
||||
{
|
||||
LOGE("Error receiving MIDI data: %d", (int)num_messages);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (num_messages > 0 && num_bytes > 0)
|
||||
{
|
||||
const auto to_ns = [=] { return timestamp; };
|
||||
|
||||
m_processing.on_bytes_multi(
|
||||
{buffer, buffer + num_bytes},
|
||||
m_processing.timestamp<timestamp_info>(to_ns, timestamp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void open_callback(midi_in* self, AMidiDevice* device)
|
||||
{
|
||||
self->receive_device = device;
|
||||
AMidiOutputPort_open(device, 0, &self->midi_output_port);
|
||||
self->start_midi_thread();
|
||||
}
|
||||
|
||||
private:
|
||||
libremidi::input_configuration configuration;
|
||||
AMidiDevice* receive_device = nullptr;
|
||||
AMidiOutputPort* midi_output_port = nullptr;
|
||||
std::thread poll_thread;
|
||||
std::atomic<bool> port_open{false};
|
||||
std::atomic<bool> running{false};
|
||||
midi1::input_state_machine m_processing{this->configuration};
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
#include <libremidi/backends/android/config.hpp>
|
||||
#include <libremidi/backends/android/helpers.hpp>
|
||||
#include <libremidi/detail/midi_out.hpp>
|
||||
|
||||
NAMESPACE_LIBREMIDI
|
||||
{
|
||||
namespace android
|
||||
{
|
||||
class midi_out final
|
||||
: public midi1::out_api
|
||||
, public error_handler
|
||||
{
|
||||
public:
|
||||
explicit midi_out(
|
||||
libremidi::output_configuration&& conf, libremidi::android::output_configuration aconf)
|
||||
: configuration{std::move(conf)}
|
||||
{
|
||||
if (!context::client_name.empty() && context::client_name != aconf.client_name)
|
||||
{
|
||||
libremidi_handle_error(
|
||||
configuration, "Android backend only supports one client name per process");
|
||||
return;
|
||||
}
|
||||
context::client_name = std::string(aconf.client_name);
|
||||
client_open_ = stdx::error{};
|
||||
}
|
||||
|
||||
~midi_out() override { close_port(); }
|
||||
|
||||
libremidi::API get_current_api() const noexcept override
|
||||
{
|
||||
return libremidi::API::ANDROID_AMIDI;
|
||||
}
|
||||
|
||||
stdx::error open_port(const output_port& port, std::string_view /*name*/) override
|
||||
{
|
||||
if (is_port_open())
|
||||
{
|
||||
libremidi_handle_error(configuration, "midi_out::open_port: a port is already open");
|
||||
return std::errc{};
|
||||
}
|
||||
|
||||
const unsigned int port_number = port.port;
|
||||
|
||||
auto env = context::get_thread_env();
|
||||
if (!env)
|
||||
{
|
||||
libremidi_handle_error(configuration, "midi_out::open_port: failed to get JNI environment");
|
||||
return std::errc{};
|
||||
}
|
||||
|
||||
auto ctx = context::get_context(env);
|
||||
if (!ctx)
|
||||
{
|
||||
libremidi_handle_error(configuration, "midi_out::open_port: failed to get Android context");
|
||||
return std::errc{};
|
||||
}
|
||||
|
||||
context::refresh_midi_devices(env, ctx, true);
|
||||
|
||||
if (port_number >= context::midi_devices.size())
|
||||
{
|
||||
libremidi_handle_error(configuration, "midi_out::open_port: invalid port number");
|
||||
return std::errc{};
|
||||
}
|
||||
|
||||
port_open = true;
|
||||
context::open_device(context::midi_devices[port_number], this, true);
|
||||
return stdx::error{};
|
||||
}
|
||||
|
||||
stdx::error close_port() override
|
||||
{
|
||||
if (!is_port_open())
|
||||
return std::errc{};
|
||||
|
||||
if (midi_input_port)
|
||||
{
|
||||
AMidiInputPort_close(midi_input_port);
|
||||
midi_input_port = nullptr;
|
||||
}
|
||||
|
||||
if (send_device)
|
||||
{
|
||||
AMidiDevice_release(send_device);
|
||||
send_device = nullptr;
|
||||
}
|
||||
|
||||
port_open = false;
|
||||
return stdx::error{};
|
||||
}
|
||||
|
||||
bool is_port_open() const noexcept { return port_open; }
|
||||
|
||||
stdx::error send_message(const unsigned char* message, size_t size) override
|
||||
{
|
||||
if (!is_port_open() || !midi_input_port)
|
||||
{
|
||||
libremidi_handle_error(configuration, "midi_out::send_message: port is not open");
|
||||
return std::errc{};
|
||||
}
|
||||
|
||||
ssize_t result = AMidiInputPort_send(midi_input_port, message, size);
|
||||
if (result < 0)
|
||||
{
|
||||
libremidi_handle_error(configuration, "midi_out::send_message: failed to send MIDI data");
|
||||
return std::errc{};
|
||||
}
|
||||
return stdx::error{};
|
||||
}
|
||||
|
||||
static void open_callback(midi_out* self, AMidiDevice* device)
|
||||
{
|
||||
self->send_device = device;
|
||||
AMidiInputPort_open(device, 0, &self->midi_input_port);
|
||||
}
|
||||
|
||||
private:
|
||||
libremidi::output_configuration configuration;
|
||||
AMidiDevice* send_device = nullptr;
|
||||
AMidiInputPort* midi_input_port = nullptr;
|
||||
bool port_open = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
#include <libremidi/backends/android/config.hpp>
|
||||
#include <libremidi/backends/android/helpers.hpp>
|
||||
#include <libremidi/detail/observer.hpp>
|
||||
|
||||
NAMESPACE_LIBREMIDI
|
||||
{
|
||||
namespace android
|
||||
{
|
||||
class observer final
|
||||
: public libremidi::observer_api
|
||||
, public error_handler
|
||||
{
|
||||
public:
|
||||
explicit observer(
|
||||
libremidi::observer_configuration&& conf, libremidi::android::observer_configuration aconf)
|
||||
: configuration{std::move(conf)}
|
||||
{
|
||||
if (!context::client_name.empty() && context::client_name != aconf.client_name)
|
||||
{
|
||||
LOGW("Android backend only supports one client name per process");
|
||||
}
|
||||
context::client_name = std::string(aconf.client_name);
|
||||
}
|
||||
|
||||
~observer()
|
||||
{
|
||||
auto env = context::get_thread_env();
|
||||
if (env)
|
||||
{
|
||||
context::cleanup_devices(env);
|
||||
}
|
||||
}
|
||||
|
||||
libremidi::API get_current_api() const noexcept override
|
||||
{
|
||||
return libremidi::API::ANDROID_AMIDI;
|
||||
}
|
||||
|
||||
std::vector<libremidi::input_port> get_input_ports() const noexcept override
|
||||
{
|
||||
auto env = context::get_thread_env();
|
||||
if (!env)
|
||||
return {};
|
||||
|
||||
auto ctx = context::get_context(env);
|
||||
if (!ctx)
|
||||
return {};
|
||||
|
||||
context::refresh_midi_devices(env, ctx, false);
|
||||
|
||||
std::vector<libremidi::input_port> ports;
|
||||
for (size_t i = 0; i < context::midi_devices.size(); ++i)
|
||||
{
|
||||
libremidi::input_port port;
|
||||
port.api = libremidi::API::ANDROID_AMIDI;
|
||||
port.port_name = context::port_name(env, i);
|
||||
port.port = i;
|
||||
ports.push_back(std::move(port));
|
||||
}
|
||||
|
||||
return ports;
|
||||
}
|
||||
|
||||
std::vector<libremidi::output_port> get_output_ports() const noexcept override
|
||||
{
|
||||
auto env = context::get_thread_env();
|
||||
if (!env)
|
||||
return {};
|
||||
|
||||
auto ctx = context::get_context(env);
|
||||
if (!ctx)
|
||||
return {};
|
||||
|
||||
context::refresh_midi_devices(env, ctx, true);
|
||||
|
||||
std::vector<libremidi::output_port> ports;
|
||||
for (size_t i = 0; i < context::midi_devices.size(); ++i)
|
||||
{
|
||||
libremidi::output_port port;
|
||||
port.port_name = context::port_name(env, i);
|
||||
port.port = i;
|
||||
ports.push_back(std::move(port));
|
||||
}
|
||||
|
||||
return ports;
|
||||
}
|
||||
|
||||
private:
|
||||
libremidi::observer_configuration configuration;
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user