#pragma once #include #include #include #include #include namespace rack { /** Abstraction for all MIDI drivers in Rack */ namespace midi { struct Message { /** Initialized to 3 empty bytes. */ std::vector 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() : bytes(3) {} 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 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 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 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 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 getDeviceIds() = 0; virtual int getDefaultDeviceId() = 0; int getDeviceId(); virtual void setDeviceId(int deviceId) = 0; virtual std::string getDeviceName(int deviceId) = 0; virtual std::vector 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 getDeviceIds() override; int getDefaultDeviceId() override; void setDeviceId(int deviceId) override; std::string getDeviceName(int deviceId) override; std::vector 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 getDeviceIds() override; int getDefaultDeviceId() override; void setDeviceId(int deviceId) override; std::string getDeviceName(int deviceId) override; std::vector 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 getDriverIds(); Driver* getDriver(int driverId); } // namespace midi } // namespace rack