1
Fork 0

Override Rack's Message class for prealloc is on stack, not on heap

Signed-off-by: falkTX <falktx@falktx.com>
This commit is contained in:
falkTX 2022-01-14 14:47:10 +00:00
parent a8009a172d
commit c74923eb2d
No known key found for this signature in database
GPG Key ID: CDBAA37ABC74FBA0
3 changed files with 983 additions and 0 deletions

View File

@ -0,0 +1,528 @@
//
// ██████ ██  ██  ██████  ██████
// ██      ██  ██ ██    ██ ██       ** Clean Header-Only Classes **
// ██  ███████ ██  ██ ██
// ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc
//  ██████ ██  ██  ██████   ██████
//
// CHOC is (C)2021 Tracktion Corporation, and is offered under the terms of the ISC license:
//
// Permission to use, copy, modify, and/or distribute this software for any purpose with or
// without fee is hereby granted, provided that the above copyright notice and this permission
// notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#ifndef CHOC_SMALLVECTOR_HEADER_INCLUDED
#define CHOC_SMALLVECTOR_HEADER_INCLUDED
#include <algorithm>
#include "choc_Span.h"
namespace choc
{
/**
A std::vector-style container class, which uses some pre-allocated storage
to avoid heap allocation when the number of elements is small.
Inspired by LLVM's SmallVector, I've found this to be handy in many situations
where you know there's only likely to be a small or fixed number of elements,
and where performance is important.
It retains most of the same basic methods as std::vector, but without some of
the more exotic tricks that the std library uses, just to avoid things getting
too complicated.
*/
template <typename ElementType, size_t numPreallocatedElements>
struct SmallVector
{
using value_type = ElementType;
using reference = ElementType&;
using const_reference = const ElementType&;
using iterator = ElementType*;
using const_iterator = const ElementType*;
using size_type = size_t;
SmallVector() noexcept;
~SmallVector() noexcept;
SmallVector (SmallVector&&) noexcept;
SmallVector (const SmallVector&);
SmallVector& operator= (SmallVector&&) noexcept;
SmallVector& operator= (const SmallVector&);
/// Creates a SmallVector as a copy of some kind of iterable container.
template <typename VectorType>
SmallVector (const VectorType& initialContent);
/// Replaces the contents of this vector with a copy of some kind of iterable container.
template <typename VectorType>
SmallVector& operator= (const VectorType&);
reference operator[] (size_type index);
const_reference operator[] (size_type index) const;
value_type* data() const noexcept;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
iterator begin() noexcept;
iterator end() noexcept;
const_reference front() const;
reference front();
const_reference back() const;
reference back();
bool empty() const noexcept;
size_type size() const noexcept;
size_type length() const noexcept;
size_type capacity() const noexcept;
bool contains (const ElementType&) const;
void clear() noexcept;
void resize (size_type newSize);
void reserve (size_type requiredNumElements);
void push_back (const value_type&);
void push_back (value_type&&);
/// Handy method to add multiple elements with a single push_back call.
template <typename... Others>
void push_back (const value_type& first, Others&&... others);
template <typename... ConstructorArgs>
void emplace_back (ConstructorArgs&&... args);
void pop_back();
void insert (iterator insertPosition, const value_type& valueToInsert);
void insert (iterator insertPosition, value_type&& valueToInsert);
void erase (iterator startPosition);
void erase (iterator startPosition, iterator endPosition);
bool operator== (span<value_type>) const;
bool operator!= (span<value_type>) const;
private:
value_type* elements;
size_type numElements = 0, numAllocated = numPreallocatedElements;
uint64_t internalStorage[(numPreallocatedElements * sizeof (value_type) + sizeof (uint64_t) - 1) / sizeof (uint64_t)];
void shrink (size_type);
value_type* getInternalStorage() noexcept { return reinterpret_cast<value_type*> (internalStorage); }
bool isUsingInternalStorage() const noexcept { return numAllocated <= numPreallocatedElements; }
void resetToInternalStorage() noexcept;
void freeHeapAndResetToInternalStorage() noexcept;
static inline ElementType& _nullValue() noexcept { static ElementType e = {}; return e; }
};
//==============================================================================
// _ _ _ _
// __| | ___ | |_ __ _ (_)| | ___
// / _` | / _ \| __| / _` || || |/ __|
// | (_| || __/| |_ | (_| || || |\__ \ _ _ _
// \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_)
//
// Code beyond this point is implementation detail...
//
//==============================================================================
template <typename ElementType, size_t preSize>
SmallVector<ElementType, preSize>::SmallVector() noexcept : elements (getInternalStorage())
{
}
template <typename ElementType, size_t preSize>
SmallVector<ElementType, preSize>::~SmallVector() noexcept
{
clear();
}
template <typename ElementType, size_t preSize>
SmallVector<ElementType, preSize>::SmallVector (const SmallVector& other) : SmallVector()
{
operator= (other);
}
template <typename ElementType, size_t preSize>
template <typename VectorType>
SmallVector<ElementType, preSize>::SmallVector (const VectorType& initialContent) : SmallVector()
{
reserve (initialContent.size());
for (auto& i : initialContent)
emplace_back (i);
}
template <typename ElementType, size_t preSize>
SmallVector<ElementType, preSize>::SmallVector (SmallVector&& other) noexcept
{
if (other.isUsingInternalStorage())
{
elements = getInternalStorage();
numElements = other.numElements;
for (size_type i = 0; i < numElements; ++i)
new (elements + i) value_type (std::move (other.elements[i]));
}
else
{
elements = other.elements;
numElements = other.numElements;
numAllocated = other.numAllocated;
other.resetToInternalStorage();
other.numElements = 0;
}
}
template <typename ElementType, size_t preSize>
SmallVector<ElementType, preSize>& SmallVector<ElementType, preSize>::operator= (SmallVector&& other) noexcept
{
clear();
if (other.isUsingInternalStorage())
{
numElements = other.numElements;
for (size_type i = 0; i < numElements; ++i)
new (elements + i) value_type (std::move (other.elements[i]));
}
else
{
elements = other.elements;
numElements = other.numElements;
numAllocated = other.numAllocated;
other.resetToInternalStorage();
other.numElements = 0;
}
return *this;
}
template <typename ElementType, size_t preSize>
SmallVector<ElementType, preSize>& SmallVector<ElementType, preSize>::operator= (const SmallVector& other)
{
if (other.size() > numElements)
{
reserve (other.size());
for (size_type i = 0; i < numElements; ++i)
elements[i] = other.elements[i];
for (size_type i = numElements; i < other.size(); ++i)
new (elements + i) value_type (other.elements[i]);
numElements = other.size();
}
else
{
shrink (other.size());
for (size_type i = 0; i < numElements; ++i)
elements[i] = other.elements[i];
}
return *this;
}
template <typename ElementType, size_t preSize>
template <typename VectorType>
SmallVector<ElementType, preSize>& SmallVector<ElementType, preSize>::operator= (const VectorType& other)
{
if (other.size() > numElements)
{
reserve (other.size());
for (size_type i = 0; i < numElements; ++i)
elements[i] = other[i];
for (size_type i = numElements; i < other.size(); ++i)
new (elements + i) value_type (other[i]);
numElements = other.size();
}
else
{
shrink (other.size());
for (size_type i = 0; i < numElements; ++i)
elements[i] = other[i];
}
return *this;
}
template <typename ElementType, size_t preSize>
void SmallVector<ElementType, preSize>::resetToInternalStorage() noexcept
{
elements = getInternalStorage();
numAllocated = preSize;
}
template <typename ElementType, size_t preSize>
void SmallVector<ElementType, preSize>::freeHeapAndResetToInternalStorage() noexcept
{
if (! isUsingInternalStorage())
{
delete[] reinterpret_cast<char*> (elements);
resetToInternalStorage();
}
}
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::reference SmallVector<ElementType, preSize>::operator[] (size_type index)
{
DISTRHO_SAFE_ASSERT_RETURN (index < numElements, _nullValue());
return elements[index];
}
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::const_reference SmallVector<ElementType, preSize>::operator[] (size_type index) const
{
DISTRHO_SAFE_ASSERT_RETURN (index < numElements, _nullValue());
return elements[index];
}
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::value_type* SmallVector<ElementType, preSize>::data() const noexcept { return elements; }
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::const_iterator SmallVector<ElementType, preSize>::begin() const noexcept { return elements; }
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::const_iterator SmallVector<ElementType, preSize>::end() const noexcept { return elements + numElements; }
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::const_iterator SmallVector<ElementType, preSize>::cbegin() const noexcept { return elements; }
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::const_iterator SmallVector<ElementType, preSize>::cend() const noexcept { return elements + numElements; }
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::iterator SmallVector<ElementType, preSize>::begin() noexcept { return elements; }
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::iterator SmallVector<ElementType, preSize>::end() noexcept { return elements + numElements; }
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::reference SmallVector<ElementType, preSize>::front()
{
DISTRHO_SAFE_ASSERT_RETURN (! empty(), _nullValue());
return elements[0];
}
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::const_reference SmallVector<ElementType, preSize>::front() const
{
DISTRHO_SAFE_ASSERT_RETURN (! empty(), _nullValue());
return elements[0];
}
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::reference SmallVector<ElementType, preSize>::back()
{
DISTRHO_SAFE_ASSERT_RETURN (! empty(), _nullValue());
return elements[numElements - 1];
}
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::const_reference SmallVector<ElementType, preSize>::back() const
{
DISTRHO_SAFE_ASSERT_RETURN (! empty(), _nullValue());
return elements[numElements - 1];
}
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::size_type SmallVector<ElementType, preSize>::size() const noexcept { return numElements; }
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::size_type SmallVector<ElementType, preSize>::length() const noexcept { return numElements; }
template <typename ElementType, size_t preSize>
typename SmallVector<ElementType, preSize>::size_type SmallVector<ElementType, preSize>::capacity() const noexcept { return numAllocated; }
template <typename ElementType, size_t preSize>
bool SmallVector<ElementType, preSize>::empty() const noexcept { return numElements == 0; }
template <typename ElementType, size_t preSize>
bool SmallVector<ElementType, preSize>::contains (const ElementType& target) const
{
for (size_t i = 0; i < numElements; ++i)
if (elements[i] == target)
return true;
return false;
}
template <typename ElementType, size_t preSize>
bool SmallVector<ElementType, preSize>::operator== (span<value_type> other) const { return span<value_type> (*this) == other; }
template <typename ElementType, size_t preSize>
bool SmallVector<ElementType, preSize>::operator!= (span<value_type> other) const { return span<value_type> (*this) != other; }
template <typename ElementType, size_t preSize>
void SmallVector<ElementType, preSize>::push_back (const value_type& item)
{
reserve (numElements + 1);
new (elements + numElements) value_type (item);
++numElements;
}
template <typename ElementType, size_t preSize>
void SmallVector<ElementType, preSize>::push_back (value_type&& item)
{
reserve (numElements + 1);
new (elements + numElements) value_type (std::move (item));
++numElements;
}
template <typename ElementType, size_t preSize>
template <typename... Others>
void SmallVector<ElementType, preSize>::push_back (const value_type& first, Others&&... others)
{
reserve (numElements + 1 + sizeof... (others));
push_back (first);
push_back (std::forward<Others> (others)...);
}
template <typename ElementType, size_t preSize>
template <typename... ConstructorArgs>
void SmallVector<ElementType, preSize>::emplace_back (ConstructorArgs&&... args)
{
reserve (numElements + 1);
new (elements + numElements) value_type (std::forward<ConstructorArgs> (args)...);
++numElements;
}
template <typename ElementType, size_t preSize>
void SmallVector<ElementType, preSize>::insert (iterator insertPos, const value_type& item)
{
DISTRHO_SAFE_ASSERT_RETURN (insertPos != nullptr && insertPos >= begin() && insertPos <= end(),);
auto index = insertPos - begin();
push_back (item);
std::rotate (begin() + index, end() - 1, end());
}
template <typename ElementType, size_t preSize>
void SmallVector<ElementType, preSize>::insert (iterator insertPos, value_type&& item)
{
DISTRHO_SAFE_ASSERT_RETURN (insertPos != nullptr && insertPos >= begin() && insertPos <= end(),);
auto index = insertPos - begin();
push_back (std::move (item));
std::rotate (begin() + index, end() - 1, end());
}
template <typename ElementType, size_t preSize>
void SmallVector<ElementType, preSize>::pop_back()
{
if (numElements == 1)
{
clear();
}
else
{
DISTRHO_SAFE_ASSERT_RETURN (numElements > 0,);
elements[--numElements].~value_type();
}
}
template <typename ElementType, size_t preSize>
void SmallVector<ElementType, preSize>::clear() noexcept
{
for (size_type i = 0; i < numElements; ++i)
elements[i].~value_type();
numElements = 0;
freeHeapAndResetToInternalStorage();
}
template <typename ElementType, size_t preSize>
void SmallVector<ElementType, preSize>::resize (size_type newSize)
{
if (newSize > numElements)
{
reserve (newSize);
while (numElements < newSize)
new (elements + numElements++) value_type (value_type());
}
else
{
shrink (newSize);
}
}
template <typename ElementType, size_t preSize>
void SmallVector<ElementType, preSize>::shrink (size_type newSize)
{
if (newSize == 0)
return clear();
DISTRHO_SAFE_ASSERT_RETURN (newSize <= numElements,);
while (newSize < numElements && numElements > 0)
elements[--numElements].~value_type();
}
template <typename ElementType, size_t preSize>
void SmallVector<ElementType, preSize>::reserve (size_type requiredNumElements)
{
if (requiredNumElements > numAllocated)
{
requiredNumElements = static_cast<size_type> ((requiredNumElements + 15u) & ~(size_type) 15u);
if (requiredNumElements > preSize)
{
auto* newBuffer = reinterpret_cast<value_type*> (new char[requiredNumElements * sizeof (value_type)]);
for (size_type i = 0; i < numElements; ++i)
{
new (newBuffer + i) value_type (std::move (elements[i]));
elements[i].~value_type();
}
freeHeapAndResetToInternalStorage();
elements = newBuffer;
}
numAllocated = requiredNumElements;
}
}
template <typename ElementType, size_t preSize>
void SmallVector<ElementType, preSize>::erase (iterator startElement)
{
erase (startElement, startElement + 1);
}
template <typename ElementType, size_t preSize>
void SmallVector<ElementType, preSize>::erase (iterator startElement, iterator endElement)
{
DISTRHO_SAFE_ASSERT_RETURN (startElement != nullptr && startElement >= begin() && startElement <= end(),);
DISTRHO_SAFE_ASSERT_RETURN (endElement != nullptr && endElement >= begin() && endElement <= end(),);
if (startElement != endElement)
{
DISTRHO_SAFE_ASSERT_RETURN (startElement < endElement,);
if (endElement == end())
return shrink (static_cast<size_type> (startElement - begin()));
auto dest = startElement;
for (auto src = endElement; src < end(); ++dest, ++src)
*dest = std::move (*src);
shrink (size() - static_cast<size_type> (endElement - startElement));
}
}
}
#endif // CHOC_SMALLVECTOR_HEADER_INCLUDED

127
include/choc/choc_Span.h Normal file
View File

@ -0,0 +1,127 @@
//
// ██████ ██  ██  ██████  ██████
// ██      ██  ██ ██    ██ ██       ** Clean Header-Only Classes **
// ██  ███████ ██  ██ ██
// ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc
//  ██████ ██  ██  ██████   ██████
//
// CHOC is (C)2021 Tracktion Corporation, and is offered under the terms of the ISC license:
//
// Permission to use, copy, modify, and/or distribute this software for any purpose with or
// without fee is hereby granted, provided that the above copyright notice and this permission
// notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#ifndef CHOC_SPAN_HEADER_INCLUDED
#define CHOC_SPAN_HEADER_INCLUDED
#include <vector>
#include "DistrhoUtils.hpp"
namespace choc
{
//==============================================================================
/** This is a temporary stunt-double for std::span, with the intention of it being
deprecated when there's more widespread compiler support for the real std::span.
This class has fewer bells-and-whistles than a real std::span, but it does have
the advantage of calling CHOC_ASSERT when mistakes are made like out-of-range
accesses, which can be useful for getting clean error handling rather than UB.
*/
template <typename Item>
struct span
{
span() = default;
span (const span&) = default;
span (span&&) = default;
span& operator= (span&&) = default;
span& operator= (const span&) = default;
/// Construct from some raw start and end pointers. For an empty span, these
/// can both be nullptr, but if one is a real pointer then the caller must ensure
/// that start <= end.
span (Item* start, Item* end) noexcept : s (start), e (end) {}
/// Constructs a span from a pointer and length.
/// The pointer must not be nullptr unless the length is 0.
span (const Item* start, size_t length) noexcept : span (const_cast<Item*> (start), const_cast<Item*> (start) + length) {}
/// Constructor taking a raw C++ array.
template <size_t length>
span (Item (&array)[length]) : span (array, length) {}
/// Constructor which takes some kind of class like std::vector or std::array.
/// Any class that provides data() and size() methods can be passed in.
template <typename VectorOrArray>
span (const VectorOrArray& v) : span (v.data(), v.size()) {}
/// Returns true if the span is empty.
bool empty() const { return s == e; }
/// Returns the number of elements.
/// The length() and size() methods are equivalent.
size_t size() const { return static_cast<size_t> (e - s); }
/// Returns the number of elements.
/// The length() and size() methods are equivalent.
size_t length() const { return static_cast<size_t> (e - s); }
/// Returns a raw pointer to the start of the data.
Item* data() const noexcept { return s; }
const Item& front() const { DISTRHO_SAFE_ASSERT_RETURN (! empty(), _nullValue()); return *s; }
const Item& back() const { DISTRHO_SAFE_ASSERT_RETURN (! empty(), _nullValue()); return *(e - 1); }
Item& front() { DISTRHO_SAFE_ASSERT_RETURN (! empty(), _nullValue()); return *s; }
Item& back() { DISTRHO_SAFE_ASSERT_RETURN (! empty(), _nullValue()); return *(e - 1); }
const Item& operator[] (size_t index) const { DISTRHO_SAFE_ASSERT_RETURN (index < length(), _nullValue()); return s[index]; }
Item& operator[] (size_t index) { DISTRHO_SAFE_ASSERT_RETURN (index < length(), _nullValue()); return s[index]; }
/// A handy bonus function for getting a (non-empty) span's tail elements
span tail() const { DISTRHO_SAFE_ASSERT_RETURN (! empty(), _nullValue()); return { s + 1, e }; }
const Item* begin() const noexcept { return s; }
const Item* end() const noexcept { return e; }
Item* begin() noexcept { return s; }
Item* end() noexcept { return e; }
/// Helper function to return a std::vector copy of the span's elements.
std::vector<typename std::remove_const<Item>::type> createVector() const
{
return std::vector<typename std::remove_const<Item>::type> (s, e);
}
/// Two spans are considered identical if their elements are all comparable
template <typename OtherSpan>
bool operator== (const OtherSpan& other) const
{
auto sz = size();
if (sz != other.size())
return false;
for (decltype (sz) i = 0; i < sz; ++i)
if (s[i] != other.s[i])
return false;
return true;
}
template <typename OtherSpan>
bool operator!= (const OtherSpan& other) const { return ! operator== (other); }
private:
Item* s = {};
Item* e = {};
static inline Item& _nullValue() noexcept { static Item e = {}; return e; }
};
} // namespace choc
#endif // CHOC_SPAN_HEADER_INCLUDED

328
include/midi.hpp Normal file
View File

@ -0,0 +1,328 @@
/*
* 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