329 lines
8.0 KiB
C++
329 lines
8.0 KiB
C++
/*
|
|
* DISTRHO Cardinal Plugin
|
|
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 3 of
|
|
* the License, or any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* For a full copy of the GNU General Public License see the LICENSE file.
|
|
*/
|
|
|
|
/**
|
|
* This file is an edited version of VCVRack's midi.hpp
|
|
* Copyright (C) 2016-2021 VCV.
|
|
*
|
|
* This program is free software: you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 3 of
|
|
* the License, or (at your option) any later version.
|
|
*/
|
|
|
|
#pragma once
|
|
#include <vector>
|
|
#include <set>
|
|
|
|
#include <jansson.h>
|
|
|
|
#include <common.hpp>
|
|
#include <context.hpp>
|
|
|
|
#include "choc/choc_SmallVector.h"
|
|
|
|
namespace rack {
|
|
/** Abstraction for all MIDI drivers in Rack */
|
|
namespace midi {
|
|
|
|
|
|
struct Message {
|
|
/** Initialized to 3 empty bytes. */
|
|
choc::SmallVector<uint8_t, 3> bytes;
|
|
/** The Engine frame timestamp of the Message.
|
|
For output messages, the frame when the message was generated.
|
|
For input messages, the frame when it is intended to be processed.
|
|
-1 for undefined, to be sent or processed immediately.
|
|
*/
|
|
int64_t frame = -1;
|
|
|
|
Message() {}
|
|
|
|
int getSize() const {
|
|
return bytes.size();
|
|
}
|
|
void setSize(int size) {
|
|
bytes.resize(size);
|
|
}
|
|
|
|
uint8_t getChannel() const {
|
|
if (bytes.size() < 1)
|
|
return 0;
|
|
return bytes[0] & 0xf;
|
|
}
|
|
void setChannel(uint8_t channel) {
|
|
if (bytes.size() < 1)
|
|
return;
|
|
bytes[0] = (bytes[0] & 0xf0) | (channel & 0xf);
|
|
}
|
|
|
|
uint8_t getStatus() const {
|
|
if (bytes.size() < 1)
|
|
return 0;
|
|
return bytes[0] >> 4;
|
|
}
|
|
void setStatus(uint8_t status) {
|
|
if (bytes.size() < 1)
|
|
return;
|
|
bytes[0] = (bytes[0] & 0xf) | (status << 4);
|
|
}
|
|
|
|
uint8_t getNote() const {
|
|
if (bytes.size() < 2)
|
|
return 0;
|
|
return bytes[1];
|
|
}
|
|
void setNote(uint8_t note) {
|
|
if (bytes.size() < 2)
|
|
return;
|
|
bytes[1] = note & 0x7f;
|
|
}
|
|
|
|
uint8_t getValue() const {
|
|
if (bytes.size() < 3)
|
|
return 0;
|
|
return bytes[2];
|
|
}
|
|
void setValue(uint8_t value) {
|
|
if (bytes.size() < 3)
|
|
return;
|
|
bytes[2] = value & 0x7f;
|
|
}
|
|
|
|
std::string toString() const;
|
|
|
|
int64_t getFrame() const {
|
|
return frame;
|
|
}
|
|
|
|
void setFrame(int64_t frame) {
|
|
this->frame = frame;
|
|
}
|
|
};
|
|
|
|
////////////////////
|
|
// Driver
|
|
////////////////////
|
|
|
|
struct InputDevice;
|
|
struct Input;
|
|
struct OutputDevice;
|
|
struct Output;
|
|
|
|
/** Wraps a MIDI driver API containing any number of MIDI devices.
|
|
*/
|
|
struct Driver {
|
|
virtual ~Driver() {}
|
|
/** Returns the name of the driver. E.g. "ALSA". */
|
|
virtual std::string getName() {
|
|
return "";
|
|
}
|
|
/** Returns a list of all input device IDs that can be subscribed to. */
|
|
virtual std::vector<int> getInputDeviceIds() {
|
|
return {};
|
|
}
|
|
/** Returns the default device to use when the driver is selected, or -1 for none. */
|
|
virtual int getDefaultInputDeviceId() {
|
|
return -1;
|
|
}
|
|
/** Returns the name of an input device without obtaining it. */
|
|
virtual std::string getInputDeviceName(int deviceId) {
|
|
return "";
|
|
}
|
|
/** Adds the given port as a reference holder of a device and returns the it.
|
|
Creates the Device if no ports are subscribed before calling.
|
|
*/
|
|
virtual InputDevice* subscribeInput(int deviceId, Input* input) {
|
|
return NULL;
|
|
}
|
|
/** Removes the give port as a reference holder of a device.
|
|
Deletes the Device if no ports are subscribed after calling.
|
|
*/
|
|
virtual void unsubscribeInput(int deviceId, Input* input) {}
|
|
|
|
// The following behave identically as the above methods except for outputs.
|
|
|
|
virtual std::vector<int> getOutputDeviceIds() {
|
|
return {};
|
|
}
|
|
virtual int getDefaultOutputDeviceId() {
|
|
return -1;
|
|
}
|
|
virtual std::string getOutputDeviceName(int deviceId) {
|
|
return "";
|
|
}
|
|
virtual OutputDevice* subscribeOutput(int deviceId, Output* output) {
|
|
return NULL;
|
|
}
|
|
virtual void unsubscribeOutput(int deviceId, Output* output) {}
|
|
};
|
|
|
|
////////////////////
|
|
// Device
|
|
////////////////////
|
|
|
|
/** A single MIDI device of a driver API.
|
|
|
|
Modules and the UI should not interact with this API directly. Use Port instead.
|
|
|
|
Methods throw `rack::Exception` if the driver API has an exception.
|
|
*/
|
|
struct Device {
|
|
virtual ~Device() {}
|
|
virtual std::string getName() {
|
|
return "";
|
|
}
|
|
};
|
|
|
|
struct InputDevice : Device {
|
|
std::set<Input*> subscribed;
|
|
/** Not public. Use Driver::subscribeInput(). */
|
|
void subscribe(Input* input);
|
|
/** Not public. Use Driver::unsubscribeInput(). */
|
|
void unsubscribe(Input* input);
|
|
/** Called when a MIDI message is received from the device. */
|
|
void onMessage(const Message& message);
|
|
};
|
|
|
|
struct OutputDevice : Device {
|
|
std::set<Output*> subscribed;
|
|
/** Not public. Use Driver::subscribeOutput(). */
|
|
void subscribe(Output* output);
|
|
/** Not public. Use Driver::unsubscribeOutput(). */
|
|
void unsubscribe(Output* output);
|
|
/** Sends a MIDI message to the device. */
|
|
virtual void sendMessage(const Message& message) {}
|
|
};
|
|
|
|
////////////////////
|
|
// Port
|
|
////////////////////
|
|
|
|
/** A handle to a Device, typically owned by modules to have shared access to a single Device.
|
|
|
|
All Port methods safely wrap Drivers methods.
|
|
That is, if the active Device throws a `rack::Exception`, it is caught and logged inside all Port methods, so they do not throw exceptions.
|
|
|
|
Use Input or Output subclasses in your module, not Port directly.
|
|
*/
|
|
struct Port {
|
|
/** For MIDI output, the channel to automatically set outbound messages.
|
|
If -1, the channel is not overwritten and must be set by MIDI generator.
|
|
|
|
For MIDI input, messages will be filtered by the channel.
|
|
If -1, all MIDI channels pass through.
|
|
*/
|
|
int channel = -1;
|
|
|
|
// private
|
|
int driverId = -1;
|
|
int deviceId = -1;
|
|
/** Not owned */
|
|
Driver* driver = NULL;
|
|
Device* device = NULL;
|
|
Context* context;
|
|
|
|
Port();
|
|
virtual ~Port();
|
|
|
|
Driver* getDriver();
|
|
int getDriverId();
|
|
void setDriverId(int driverId);
|
|
|
|
Device* getDevice();
|
|
virtual std::vector<int> getDeviceIds() = 0;
|
|
virtual int getDefaultDeviceId() = 0;
|
|
int getDeviceId();
|
|
virtual void setDeviceId(int deviceId) = 0;
|
|
virtual std::string getDeviceName(int deviceId) = 0;
|
|
|
|
virtual std::vector<int> getChannels() = 0;
|
|
int getChannel();
|
|
void setChannel(int channel);
|
|
std::string getChannelName(int channel);
|
|
|
|
json_t* toJson();
|
|
void fromJson(json_t* rootJ);
|
|
};
|
|
|
|
|
|
struct Input : Port {
|
|
/** Not owned */
|
|
InputDevice* inputDevice = NULL;
|
|
|
|
Input();
|
|
~Input();
|
|
void reset();
|
|
|
|
std::vector<int> getDeviceIds() override;
|
|
int getDefaultDeviceId() override;
|
|
void setDeviceId(int deviceId) override;
|
|
std::string getDeviceName(int deviceId) override;
|
|
|
|
std::vector<int> getChannels() override;
|
|
|
|
virtual void onMessage(const Message& message) {}
|
|
};
|
|
|
|
|
|
/** An Input port that stores incoming MIDI messages and releases them when ready according to their frame timestamp.
|
|
*/
|
|
struct InputQueue : Input {
|
|
struct Internal;
|
|
Internal* internal;
|
|
|
|
InputQueue();
|
|
~InputQueue();
|
|
void onMessage(const Message& message) override;
|
|
/** Pops and returns the next message (by setting `messageOut`) if its frame timestamp is `maxFrame` or earlier.
|
|
Returns whether a message was returned.
|
|
*/
|
|
bool tryPop(Message* messageOut, int64_t maxFrame);
|
|
size_t size();
|
|
};
|
|
|
|
|
|
struct Output : Port {
|
|
/** Not owned */
|
|
OutputDevice* outputDevice = NULL;
|
|
|
|
Output();
|
|
~Output();
|
|
void reset();
|
|
|
|
std::vector<int> getDeviceIds() override;
|
|
int getDefaultDeviceId() override;
|
|
void setDeviceId(int deviceId) override;
|
|
std::string getDeviceName(int deviceId) override;
|
|
|
|
std::vector<int> getChannels() override;
|
|
|
|
void sendMessage(const Message& message);
|
|
};
|
|
|
|
|
|
PRIVATE void init();
|
|
PRIVATE void destroy();
|
|
/** Registers a new MIDI driver. Takes pointer ownership. */
|
|
void addDriver(int driverId, Driver* driver);
|
|
std::vector<int> getDriverIds();
|
|
Driver* getDriver(int driverId);
|
|
|
|
|
|
} // namespace midi
|
|
} // namespace rack
|