very, very simple WP with midi CC out
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <libremidi/config.hpp>
|
||||
|
||||
namespace libremidi
|
||||
{
|
||||
|
||||
struct emscripten_input_configuration
|
||||
{
|
||||
};
|
||||
struct emscripten_output_configuration
|
||||
{
|
||||
};
|
||||
struct emscripten_observer_configuration
|
||||
{
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <libremidi/config.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace libremidi
|
||||
{
|
||||
namespace webmidi_helpers
|
||||
{
|
||||
struct device_information
|
||||
{
|
||||
std::string id;
|
||||
std::string name;
|
||||
bool connected{};
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <libremidi/backends/emscripten/midi_access.hpp>
|
||||
|
||||
extern "C" {
|
||||
LIBREMIDI_EXPORT
|
||||
LIBREMIDI_INLINE
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void libremidi_devices_poll()
|
||||
{
|
||||
libremidi::webmidi_helpers::midi_access_emscripten::instance().devices_poll();
|
||||
}
|
||||
|
||||
LIBREMIDI_EXPORT
|
||||
LIBREMIDI_INLINE
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void libremidi_devices_input(int port, double timestamp, int len, char* bytes)
|
||||
{
|
||||
libremidi::webmidi_helpers::midi_access_emscripten::instance().devices_input(
|
||||
port, timestamp, len, bytes);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,303 @@
|
||||
#pragma once
|
||||
#include <libremidi/backends/emscripten/midi_in.hpp>
|
||||
#include <libremidi/backends/emscripten/midi_out.hpp>
|
||||
#include <libremidi/backends/emscripten/observer.hpp>
|
||||
#include <libremidi/libremidi.hpp>
|
||||
|
||||
#include <emscripten.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
extern "C" {
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void libremidi_devices_poll();
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void libremidi_devices_input(int port, double timestamp, int len, char* bytes);
|
||||
}
|
||||
|
||||
namespace libremidi
|
||||
{
|
||||
namespace webmidi_helpers
|
||||
{
|
||||
|
||||
class midi_access_emscripten
|
||||
{
|
||||
public:
|
||||
static midi_access_emscripten& instance() noexcept
|
||||
{
|
||||
static midi_access_emscripten inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
bool available() const noexcept
|
||||
{
|
||||
return EM_ASM_INT(return typeof globalThis.__libreMidi_access !== undefined;);
|
||||
}
|
||||
|
||||
int input_count() const noexcept
|
||||
{
|
||||
return EM_ASM_INT(return globalThis.__libreMidi_currentInputs.length;);
|
||||
}
|
||||
|
||||
int output_count() const noexcept
|
||||
{
|
||||
return EM_ASM_INT(return globalThis.__libreMidi_currentOutputs.length;);
|
||||
}
|
||||
|
||||
void load_current_infos() noexcept
|
||||
{
|
||||
#define get_js_string(variable_to_read, ...) \
|
||||
(char*)EM_ASM_INT( \
|
||||
{ \
|
||||
var jsstr = variable_to_read; \
|
||||
var bytes = lengthBytesUTF8(jsstr) + 1; \
|
||||
var str = _malloc(bytes); \
|
||||
stringToUTF8(jsstr, str, bytes); \
|
||||
return str; \
|
||||
}, \
|
||||
__VA_ARGS__);
|
||||
|
||||
EM_ASM_INT({
|
||||
let inputs = [];
|
||||
let outputs = [];
|
||||
for (let inpt of globalThis.__libreMidi_access.inputs.values())
|
||||
{
|
||||
inputs.push(inpt);
|
||||
}
|
||||
for (let outpt of globalThis.__libreMidi_access.outputs.values())
|
||||
{
|
||||
outputs.push(outpt);
|
||||
}
|
||||
globalThis.__libreMidi_currentInputs = inputs;
|
||||
globalThis.__libreMidi_currentOutputs = outputs;
|
||||
});
|
||||
const int inputs = input_count();
|
||||
const int outputs = output_count();
|
||||
|
||||
m_current_inputs.resize(inputs);
|
||||
m_current_outputs.resize(outputs);
|
||||
|
||||
for (int i = 0; i < inputs; i++)
|
||||
{
|
||||
int device_index = -1;
|
||||
char* midi_id = get_js_string(globalThis.__libreMidi_currentInputs[$0].id, i);
|
||||
auto it = m_input_indices.find(midi_id); // TODO transparent comparator, string_view...
|
||||
if (it == m_input_indices.end())
|
||||
{
|
||||
device_index = m_input_indices.size();
|
||||
m_input_indices[midi_id] = device_index;
|
||||
}
|
||||
else
|
||||
{
|
||||
device_index = it->second;
|
||||
}
|
||||
|
||||
char* midi_name = get_js_string(globalThis.__libreMidi_currentInputs[$0].name, i);
|
||||
|
||||
const bool connected
|
||||
= EM_ASM_INT(return globalThis.__libreMidi_currentInputs[$0].state === "connected", i);
|
||||
|
||||
m_current_inputs[device_index].id = midi_id;
|
||||
m_current_inputs[device_index].name = midi_name;
|
||||
m_current_inputs[device_index].connected = connected;
|
||||
|
||||
free(midi_id);
|
||||
free(midi_name);
|
||||
}
|
||||
|
||||
for (int i = 0; i < outputs; i++)
|
||||
{
|
||||
int device_index = -1;
|
||||
char* midi_id = get_js_string(globalThis.__libreMidi_currentOutputs[$0].id, i);
|
||||
|
||||
auto it = m_output_indices.find(midi_id); // TODO transparent comparator, string_view...
|
||||
if (it == m_output_indices.end())
|
||||
{
|
||||
device_index = m_output_indices.size();
|
||||
m_output_indices[midi_id] = device_index;
|
||||
}
|
||||
else
|
||||
{
|
||||
device_index = it->second;
|
||||
}
|
||||
|
||||
char* midi_name = get_js_string(globalThis.__libreMidi_currentOutputs[$0].name, i);
|
||||
|
||||
const bool connected
|
||||
= EM_ASM_INT(return globalThis.__libreMidi_currentOutputs[$0].state === "connected", i);
|
||||
|
||||
m_current_outputs[device_index].id = midi_id;
|
||||
m_current_outputs[device_index].name = midi_name;
|
||||
m_current_outputs[device_index].connected = connected;
|
||||
|
||||
free(midi_id);
|
||||
free(midi_name);
|
||||
}
|
||||
|
||||
#undef get_js_string
|
||||
}
|
||||
|
||||
void register_observer(observer_emscripten& obs)
|
||||
{
|
||||
m_observers.push_back(&obs);
|
||||
if (m_observers.size() == 1)
|
||||
{
|
||||
start_observing();
|
||||
}
|
||||
}
|
||||
|
||||
void unregister_observer(observer_emscripten& obs)
|
||||
{
|
||||
if (m_observers.size() == 1)
|
||||
{
|
||||
stop_observing();
|
||||
}
|
||||
auto it = std::find(m_observers.begin(), m_observers.end(), &obs);
|
||||
if (it != m_observers.end())
|
||||
{
|
||||
m_observers.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void devices_poll()
|
||||
{
|
||||
load_current_infos();
|
||||
|
||||
for (auto& obs : m_observers)
|
||||
{
|
||||
obs->update(m_current_inputs, m_current_outputs);
|
||||
}
|
||||
}
|
||||
|
||||
void open_input(int port_index, midi_in_emscripten& input)
|
||||
{
|
||||
auto& vec = m_opened_inputs[port_index];
|
||||
vec.push_back(&input);
|
||||
if (vec.size() != 1)
|
||||
return;
|
||||
|
||||
start_stream(port_index);
|
||||
}
|
||||
|
||||
void close_input(int port_index, midi_in_emscripten& input)
|
||||
{
|
||||
auto& vec = m_opened_inputs[port_index];
|
||||
auto it = std::find(vec.begin(), vec.end(), &input);
|
||||
if (it != vec.end())
|
||||
{
|
||||
vec.erase(it);
|
||||
}
|
||||
|
||||
if (vec.empty())
|
||||
{
|
||||
stop_stream(port_index);
|
||||
}
|
||||
}
|
||||
|
||||
void devices_input(int port, double timestamp, int len, char* data)
|
||||
{
|
||||
unsigned char* bytes = reinterpret_cast<unsigned char*>(data);
|
||||
for (auto input : m_opened_inputs[port])
|
||||
{
|
||||
input->on_input(timestamp, bytes, bytes + len);
|
||||
}
|
||||
}
|
||||
|
||||
void send_message(int port_index, const char* bytes, int len)
|
||||
{
|
||||
const auto& id = m_current_outputs[port_index].id;
|
||||
EM_ASM(
|
||||
{
|
||||
let data = HEAPU8.subarray($0, $0 + $1);
|
||||
const id = UTF8ToString($2);
|
||||
let output = globalThis.__libreMidi_access.outputs.get(id);
|
||||
let bytes = HEAPU8.subarray($0, $0 + $1);
|
||||
output.send(Array.from(bytes));
|
||||
},
|
||||
bytes, len, id.c_str());
|
||||
}
|
||||
|
||||
const std::vector<device_information>& inputs() const noexcept { return m_current_inputs; }
|
||||
const std::vector<device_information>& outputs() const noexcept { return m_current_outputs; }
|
||||
|
||||
private:
|
||||
midi_access_emscripten() noexcept
|
||||
{
|
||||
EM_ASM(
|
||||
if (navigator.requestMIDIAccess) {
|
||||
navigator.requestMIDIAccess().then(
|
||||
(midiAccess) => { globalThis.__libreMidi_access = midiAccess; },
|
||||
() => console.log('MIDI support rejected, MIDI will not be available;'));
|
||||
} else { console.log('WebMIDI is not supported in this browser.'); });
|
||||
}
|
||||
|
||||
~midi_access_emscripten() { stop_observing(); }
|
||||
|
||||
void start_observing()
|
||||
{
|
||||
EM_ASM(
|
||||
let id = setInterval(Module._libremidi_devices_poll, 100);
|
||||
globalThis.__libreMidi_timer = id;);
|
||||
}
|
||||
|
||||
void stop_observing()
|
||||
{
|
||||
EM_ASM(clearInterval(globalThis.__libreMidi_timer); globalThis.__libreMidi_timer = undefined;);
|
||||
}
|
||||
|
||||
void start_stream(int port_index)
|
||||
{
|
||||
// Isn't life great...
|
||||
// https://github.com/Planeshifter/emscripten-examples/tree/master/01_PassingArrays
|
||||
const auto& id = m_current_inputs[port_index].id;
|
||||
EM_ASM(
|
||||
const port_index = $0;
|
||||
const id = UTF8ToString($1);
|
||||
|
||||
let input = globalThis.__libreMidi_access.inputs.get(id);
|
||||
|
||||
function _arrayToHeap(typedArray){
|
||||
const numBytes = typedArray.length * typedArray.BYTES_PER_ELEMENT;
|
||||
const ptr = Module._malloc(numBytes);
|
||||
const heapBytes = new Uint8Array(Module.HEAPU8.buffer, ptr, numBytes);
|
||||
heapBytes.set(new Uint8Array(typedArray.buffer));
|
||||
return heapBytes;
|
||||
}
|
||||
|
||||
function _freeArray(heapBytes){
|
||||
Module._free(heapBytes.byteOffset);
|
||||
}
|
||||
|
||||
input.onmidimessage = (message) => {
|
||||
let bytes = message.data;
|
||||
var heapBytes = _arrayToHeap(bytes);
|
||||
Module._libremidi_devices_input(port_index, message.timeStamp, bytes.length, heapBytes.byteOffset);
|
||||
_freeArray(heapBytes);
|
||||
};
|
||||
, port_index
|
||||
, id.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
void stop_stream(int port_index)
|
||||
{
|
||||
const auto& id = m_current_inputs[port_index].id;
|
||||
EM_ASM(const id = UTF8ToString($1);
|
||||
|
||||
let input = globalThis.__libreMidi_access.inputs.get(id);
|
||||
input.onmidimessage = undefined;, id.c_str());
|
||||
}
|
||||
|
||||
std::vector<observer_emscripten*> m_observers;
|
||||
std::vector<device_information> m_current_inputs;
|
||||
std::vector<device_information> m_current_outputs;
|
||||
|
||||
std::map<int, std::vector<midi_in_emscripten*>> m_opened_inputs;
|
||||
|
||||
std::map<std::string, int> m_input_indices;
|
||||
std::map<std::string, int> m_output_indices;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <libremidi/backends/emscripten/midi_access.hpp>
|
||||
#include <libremidi/backends/emscripten/midi_in.hpp>
|
||||
#include <libremidi/detail/midi_stream_decoder.hpp>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace libremidi
|
||||
{
|
||||
LIBREMIDI_INLINE midi_in_emscripten::midi_in_emscripten(
|
||||
input_configuration&& conf, emscripten_input_configuration&& apiconf)
|
||||
: configuration{std::move(conf), std::move(apiconf)}
|
||||
{
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE midi_in_emscripten::~midi_in_emscripten()
|
||||
{
|
||||
// Close a connection if it exists.
|
||||
midi_in_emscripten::close_port();
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE libremidi::API midi_in_emscripten::get_current_api() const noexcept
|
||||
{
|
||||
return libremidi::API::WEBMIDI;
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE bool midi_in_emscripten::open_port(int portNumber, std::string_view)
|
||||
{
|
||||
auto& midi = webmidi_helpers::midi_access_emscripten::instance();
|
||||
|
||||
if (portNumber < 0 || portNumber >= midi.input_count())
|
||||
{
|
||||
error<no_devices_found_error>(
|
||||
this->configuration, "midi_in_emscripten::open_port: no MIDI output sources found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
midi.open_input(portNumber, *this);
|
||||
portNumber_ = portNumber;
|
||||
return true;
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE bool
|
||||
midi_in_emscripten::open_port(const libremidi::input_port& p, std::string_view nm)
|
||||
{
|
||||
return open_port(p.port, nm);
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE bool midi_in_emscripten::open_virtual_port(std::string_view)
|
||||
{
|
||||
warning(configuration, "midi_in_emscripten::open_virtual_port: unsupported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE void midi_in_emscripten::close_port()
|
||||
{
|
||||
auto& midi = webmidi_helpers::midi_access_emscripten::instance();
|
||||
|
||||
midi.close_input(portNumber_, *this);
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE void midi_in_emscripten::set_client_name(std::string_view)
|
||||
{
|
||||
warning(configuration, "midi_in_emscripten::set_client_name: unsupported.");
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE void midi_in_emscripten::set_port_name(std::string_view)
|
||||
{
|
||||
warning(configuration, "midi_in_emscripten::set_port_name: unsupported.");
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE int64_t midi_in_emscripten::absolute_timestamp() const noexcept
|
||||
{
|
||||
return system_ns();
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE void
|
||||
midi_in_emscripten::on_input(double ts, unsigned char* begin, unsigned char* end)
|
||||
{
|
||||
static constexpr timestamp_backend_info timestamp_info{
|
||||
.has_absolute_timestamps = true,
|
||||
.absolute_is_monotonic = true,
|
||||
.has_samples = false,
|
||||
};
|
||||
const auto to_ns = [=] { return 1e6 * ts; };
|
||||
|
||||
m_processing.on_bytes({begin, end}, m_processing.timestamp<timestamp_info>(to_ns, 0));
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
#include <libremidi/backends/emscripten/config.hpp>
|
||||
#include <libremidi/backends/emscripten/helpers.hpp>
|
||||
#include <libremidi/detail/midi_in.hpp>
|
||||
#include <libremidi/detail/midi_stream_decoder.hpp>
|
||||
|
||||
namespace libremidi
|
||||
{
|
||||
class midi_in_emscripten final
|
||||
: public midi1::in_api
|
||||
, public error_handler
|
||||
{
|
||||
public:
|
||||
struct
|
||||
: input_configuration
|
||||
, emscripten_input_configuration
|
||||
{
|
||||
} configuration;
|
||||
|
||||
midi_in_emscripten(input_configuration&& conf, emscripten_input_configuration&& apiconf);
|
||||
~midi_in_emscripten() override;
|
||||
|
||||
libremidi::API get_current_api() const noexcept override;
|
||||
|
||||
bool open_port(int portNumber, std::string_view);
|
||||
bool open_port(const input_port& p, std::string_view) override;
|
||||
bool open_virtual_port(std::string_view) override;
|
||||
void close_port() override;
|
||||
|
||||
void set_client_name(std::string_view clientName) override;
|
||||
void set_port_name(std::string_view portName) override;
|
||||
timestamp absolute_timestamp() const noexcept override;
|
||||
|
||||
void on_input(double ts, unsigned char* begin, unsigned char* end);
|
||||
|
||||
private:
|
||||
int portNumber_{};
|
||||
|
||||
midi1::input_state_machine m_processing{this->configuration};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <libremidi/backends/emscripten/midi_access.hpp>
|
||||
#include <libremidi/backends/emscripten/midi_out.hpp>
|
||||
|
||||
namespace libremidi
|
||||
{
|
||||
LIBREMIDI_INLINE midi_out_emscripten::midi_out_emscripten(
|
||||
output_configuration&& conf, emscripten_output_configuration&& apiconf)
|
||||
: configuration{std::move(conf), std::move(apiconf)}
|
||||
{
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE midi_out_emscripten::~midi_out_emscripten()
|
||||
{
|
||||
// Close a connection if it exists.
|
||||
midi_out_emscripten::close_port();
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE libremidi::API midi_out_emscripten::get_current_api() const noexcept
|
||||
{
|
||||
return libremidi::API::WEBMIDI;
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE bool midi_out_emscripten::open_port(unsigned int portNumber, std::string_view)
|
||||
{
|
||||
auto& midi = webmidi_helpers::midi_access_emscripten::instance();
|
||||
|
||||
if (portNumber >= midi.output_count())
|
||||
{
|
||||
error<no_devices_found_error>(
|
||||
this->configuration, "midi_out_emscripten::open_port: no MIDI output sources found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
portNumber_ = portNumber;
|
||||
return true;
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE bool midi_out_emscripten::open_port(const output_port& p, std::string_view nm)
|
||||
{
|
||||
return open_port(p.port, nm);
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE void midi_out_emscripten::close_port() { }
|
||||
|
||||
LIBREMIDI_INLINE void midi_out_emscripten::set_client_name(std::string_view clientName)
|
||||
{
|
||||
warning(configuration, "midi_out_emscripten::set_client_name: unsupported.");
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE void midi_out_emscripten::set_port_name(std::string_view portName)
|
||||
{
|
||||
warning(configuration, "midi_out_emscripten::set_port_name: unsupported.");
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE bool midi_out_emscripten::open_virtual_port(std::string_view)
|
||||
{
|
||||
warning(configuration, "midi_in_emscripten::open_virtual_port: unsupported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE void midi_out_emscripten::send_message(const unsigned char* message, size_t size)
|
||||
{
|
||||
if (portNumber_ < 0)
|
||||
error<invalid_use_error>(
|
||||
this->configuration,
|
||||
"midi_out_emscripten::send_message: trying to send a message without an open "
|
||||
"port.");
|
||||
|
||||
webmidi_helpers::midi_access_emscripten::instance().send_message(
|
||||
portNumber_, reinterpret_cast<const char*>(message), size);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include <libremidi/backends/emscripten/config.hpp>
|
||||
#include <libremidi/backends/emscripten/helpers.hpp>
|
||||
#include <libremidi/detail/midi_out.hpp>
|
||||
|
||||
namespace libremidi
|
||||
{
|
||||
class midi_out_emscripten final
|
||||
: public midi1::out_api
|
||||
, public error_handler
|
||||
{
|
||||
public:
|
||||
struct
|
||||
: output_configuration
|
||||
, emscripten_output_configuration
|
||||
{
|
||||
} configuration;
|
||||
|
||||
midi_out_emscripten(output_configuration&& conf, emscripten_output_configuration&& apiconf);
|
||||
~midi_out_emscripten() override;
|
||||
|
||||
libremidi::API get_current_api() const noexcept override;
|
||||
|
||||
bool open_port(unsigned int portNumber, std::string_view);
|
||||
bool open_port(const output_port& p, std::string_view) override;
|
||||
bool open_virtual_port(std::string_view) override;
|
||||
void close_port() override;
|
||||
|
||||
void set_client_name(std::string_view clientName) override;
|
||||
void set_port_name(std::string_view portName) override;
|
||||
|
||||
void send_message(const unsigned char* message, size_t size) override;
|
||||
|
||||
private:
|
||||
int portNumber_{-1};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <libremidi/backends/emscripten/midi_access.hpp>
|
||||
#include <libremidi/backends/emscripten/observer.hpp>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace libremidi
|
||||
{
|
||||
LIBREMIDI_INLINE observer_emscripten::observer_emscripten(
|
||||
observer_configuration&& conf, emscripten_observer_configuration&& apiconf)
|
||||
: configuration{std::move(conf), std::move(apiconf)}
|
||||
{
|
||||
if (!configuration.has_callbacks())
|
||||
return;
|
||||
|
||||
auto& webmidi = webmidi_helpers::midi_access_emscripten::instance();
|
||||
webmidi.register_observer(*this);
|
||||
|
||||
// Trigger an initial notification
|
||||
webmidi.load_current_infos();
|
||||
|
||||
if (configuration.notify_in_constructor)
|
||||
update(webmidi.inputs(), webmidi.outputs());
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE observer_emscripten::~observer_emscripten()
|
||||
{
|
||||
if (!configuration.has_callbacks())
|
||||
return;
|
||||
|
||||
webmidi_helpers::midi_access_emscripten::instance().unregister_observer(*this);
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE
|
||||
libremidi::API observer_emscripten::get_current_api() const noexcept
|
||||
{
|
||||
return libremidi::API::WEBMIDI;
|
||||
}
|
||||
|
||||
template <bool Input>
|
||||
static auto to_port_info(int index, const webmidi_helpers::device_information& dev)
|
||||
-> std::conditional_t<Input, input_port, output_port>
|
||||
{
|
||||
return {
|
||||
{.client = 0,
|
||||
.port = (uint64_t)index,
|
||||
.manufacturer = "",
|
||||
.device_name = "",
|
||||
.port_name = dev.name,
|
||||
.display_name = dev.name}};
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE
|
||||
std::vector<libremidi::input_port> observer_emscripten::get_input_ports() const noexcept
|
||||
{
|
||||
std::vector<libremidi::input_port> ret;
|
||||
auto& webmidi = webmidi_helpers::midi_access_emscripten::instance();
|
||||
webmidi.load_current_infos();
|
||||
|
||||
for (std::size_t i = 0, n = webmidi.input_count(); i < n; i++)
|
||||
{
|
||||
ret.push_back(to_port_info<true>(i, webmidi.inputs()[i]));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE
|
||||
std::vector<libremidi::output_port> observer_emscripten::get_output_ports() const noexcept
|
||||
{
|
||||
std::vector<libremidi::output_port> ret;
|
||||
auto& webmidi = webmidi_helpers::midi_access_emscripten::instance();
|
||||
webmidi.load_current_infos();
|
||||
|
||||
for (std::size_t i = 0, n = webmidi.output_count(); i < n; i++)
|
||||
{
|
||||
ret.push_back(to_port_info<false>(i, webmidi.outputs()[i]));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
LIBREMIDI_INLINE void observer_emscripten::update(
|
||||
const std::vector<observer_emscripten::device>& current_inputs,
|
||||
const std::vector<observer_emscripten::device>& current_outputs)
|
||||
{
|
||||
// WebMIDI never remove inputs, it just marks them as disconnected.
|
||||
// At least in known browsers...
|
||||
assert(current_inputs.size() >= m_known_inputs.size());
|
||||
assert(current_outputs.size() >= m_known_outputs.size());
|
||||
|
||||
for (std::size_t i = m_known_inputs.size(); i < current_inputs.size(); i++)
|
||||
{
|
||||
m_known_inputs.push_back(current_inputs[i]);
|
||||
configuration.input_added(to_port_info<true>(i, m_known_inputs[i]));
|
||||
}
|
||||
|
||||
for (std::size_t i = m_known_outputs.size(); i < current_outputs.size(); i++)
|
||||
{
|
||||
m_known_outputs.push_back(current_outputs[i]);
|
||||
configuration.output_added(to_port_info<false>(i, m_known_outputs[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include <libremidi/backends/emscripten/config.hpp>
|
||||
#include <libremidi/backends/emscripten/helpers.hpp>
|
||||
#include <libremidi/detail/observer.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace libremidi
|
||||
{
|
||||
class observer_emscripten final : public observer_api
|
||||
{
|
||||
using device = webmidi_helpers::device_information;
|
||||
|
||||
public:
|
||||
struct
|
||||
: observer_configuration
|
||||
, emscripten_observer_configuration
|
||||
{
|
||||
} configuration;
|
||||
|
||||
explicit observer_emscripten(
|
||||
observer_configuration&& conf, emscripten_observer_configuration&& apiconf);
|
||||
~observer_emscripten();
|
||||
|
||||
void
|
||||
update(const std::vector<device>& current_inputs, const std::vector<device>& current_outputs);
|
||||
|
||||
libremidi::API get_current_api() const noexcept override;
|
||||
|
||||
std::vector<libremidi::input_port> get_input_ports() const noexcept override;
|
||||
std::vector<libremidi::output_port> get_output_ports() const noexcept override;
|
||||
|
||||
private:
|
||||
std::vector<device> m_known_inputs;
|
||||
std::vector<device> m_known_outputs;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user