|
@@ -0,0 +1,268 @@ |
|
|
|
|
|
/* |
|
|
|
|
|
* 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 engine/Port.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 <common.hpp> |
|
|
|
|
|
#include <engine/Light.hpp> |
|
|
|
|
|
|
|
|
|
|
|
#include <list> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace rack { |
|
|
|
|
|
namespace engine { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** This is inspired by the number of MIDI channels. */ |
|
|
|
|
|
static const int PORT_MAX_CHANNELS = 16; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct Cable; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct Port { |
|
|
|
|
|
/** Voltage of the port. */ |
|
|
|
|
|
/** NOTE Purposefully renamed in Cardinal as a way to catch plugins using it directly. */ |
|
|
|
|
|
union { |
|
|
|
|
|
/** Unstable API. Use getVoltage() and setVoltage() instead. */ |
|
|
|
|
|
float cvoltages[PORT_MAX_CHANNELS] = {}; |
|
|
|
|
|
/** DEPRECATED. Unstable API. Use getVoltage() and setVoltage() instead. */ |
|
|
|
|
|
float cvalue; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** Special trickery for backwards compatibility with plugins using DEPRECATED APIs */ |
|
|
|
|
|
struct BackwardsCompatPortValue { |
|
|
|
|
|
Port* const port; |
|
|
|
|
|
BackwardsCompatPortValue(Port* p) : port(p) {} |
|
|
|
|
|
void operator=(float value) { port->setVoltage(value); } |
|
|
|
|
|
void operator-=(float value) { port->setVoltage(port->cvalue - value); } |
|
|
|
|
|
void operator+=(float value) { port->setVoltage(port->cvalue + value); } |
|
|
|
|
|
void operator*=(float value) { port->setVoltage(port->cvalue * value); } |
|
|
|
|
|
void operator/=(float value) { port->setVoltage(port->cvalue / value); } |
|
|
|
|
|
operator float() const { return port->cvalue; } |
|
|
|
|
|
} value; |
|
|
|
|
|
Port() : value(this) {} |
|
|
|
|
|
|
|
|
|
|
|
union { |
|
|
|
|
|
/** Number of polyphonic channels. |
|
|
|
|
|
DEPRECATED. Unstable API. Use set/getChannels() instead. |
|
|
|
|
|
May be 0 to PORT_MAX_CHANNELS. |
|
|
|
|
|
0 channels means disconnected. |
|
|
|
|
|
*/ |
|
|
|
|
|
uint8_t channels = 0; |
|
|
|
|
|
/** DEPRECATED. Unstable API. Use isConnected() instead. */ |
|
|
|
|
|
uint8_t active; |
|
|
|
|
|
}; |
|
|
|
|
|
/** For rendering plug lights on cables. |
|
|
|
|
|
Green for positive, red for negative, and blue for polyphonic. |
|
|
|
|
|
*/ |
|
|
|
|
|
Light plugLights[3]; |
|
|
|
|
|
|
|
|
|
|
|
enum Type { |
|
|
|
|
|
INPUT, |
|
|
|
|
|
OUTPUT, |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** Cables connected to this output port. */ |
|
|
|
|
|
std::list<Cable*> cables; |
|
|
|
|
|
|
|
|
|
|
|
/** Step-through the cables. |
|
|
|
|
|
Called whenever voltage changes, required for zero latency operation. */ |
|
|
|
|
|
void stepCables(); |
|
|
|
|
|
|
|
|
|
|
|
/** Sets the voltage of the given channel. */ |
|
|
|
|
|
void setVoltage(float voltage, int channel = 0) { |
|
|
|
|
|
cvoltages[channel] = voltage; |
|
|
|
|
|
stepCables(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Returns the voltage of the given channel. |
|
|
|
|
|
Because of proper bookkeeping, all channels higher than the input port's number of channels should be 0V. |
|
|
|
|
|
*/ |
|
|
|
|
|
float getVoltage(int channel = 0) { |
|
|
|
|
|
return cvoltages[channel]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Returns the given channel's voltage if the port is polyphonic, otherwise returns the first voltage (channel 0). */ |
|
|
|
|
|
float getPolyVoltage(int channel) { |
|
|
|
|
|
return isMonophonic() ? getVoltage(0) : getVoltage(channel); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Returns the voltage if a cable is connected, otherwise returns the given normal voltage. */ |
|
|
|
|
|
float getNormalVoltage(float normalVoltage, int channel = 0) { |
|
|
|
|
|
return isConnected() ? getVoltage(channel) : normalVoltage; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
float getNormalPolyVoltage(float normalVoltage, int channel) { |
|
|
|
|
|
return isConnected() ? getPolyVoltage(channel) : normalVoltage; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Returns a pointer to the array of voltages beginning with firstChannel. |
|
|
|
|
|
The pointer can be used for reading and writing. |
|
|
|
|
|
*/ |
|
|
|
|
|
float* getVoltages(int firstChannel = 0) { |
|
|
|
|
|
return &cvoltages[firstChannel]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Copies the port's voltages to an array of size at least `channels`. */ |
|
|
|
|
|
void readVoltages(float* v) { |
|
|
|
|
|
for (int c = 0; c < channels; c++) { |
|
|
|
|
|
v[c] = cvoltages[c]; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Copies an array of size at least `channels` to the port's voltages. |
|
|
|
|
|
Remember to set the number of channels *before* calling this method. |
|
|
|
|
|
*/ |
|
|
|
|
|
void writeVoltages(const float* v) { |
|
|
|
|
|
for (int c = 0; c < channels; c++) { |
|
|
|
|
|
cvoltages[c] = v[c]; |
|
|
|
|
|
} |
|
|
|
|
|
stepCables(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Sets all voltages to 0. */ |
|
|
|
|
|
void clearVoltages() { |
|
|
|
|
|
for (int c = 0; c < channels; c++) { |
|
|
|
|
|
cvoltages[c] = 0.f; |
|
|
|
|
|
} |
|
|
|
|
|
stepCables(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Returns the sum of all voltages. */ |
|
|
|
|
|
float getVoltageSum() { |
|
|
|
|
|
float sum = 0.f; |
|
|
|
|
|
for (int c = 0; c < channels; c++) { |
|
|
|
|
|
sum += cvoltages[c]; |
|
|
|
|
|
} |
|
|
|
|
|
return sum; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Returns the root-mean-square of all voltages. |
|
|
|
|
|
Uses sqrt() which is slow, so use a custom approximation if calling frequently. |
|
|
|
|
|
*/ |
|
|
|
|
|
float getVoltageRMS() { |
|
|
|
|
|
if (channels == 0) { |
|
|
|
|
|
return 0.f; |
|
|
|
|
|
} |
|
|
|
|
|
else if (channels == 1) { |
|
|
|
|
|
return std::fabs(cvoltages[0]); |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
float sum = 0.f; |
|
|
|
|
|
for (int c = 0; c < channels; c++) { |
|
|
|
|
|
sum += std::pow(cvoltages[c], 2); |
|
|
|
|
|
} |
|
|
|
|
|
return std::sqrt(sum); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
T getVoltageSimd(int firstChannel) { |
|
|
|
|
|
return T::load(&cvoltages[firstChannel]); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
T getPolyVoltageSimd(int firstChannel) { |
|
|
|
|
|
return isMonophonic() ? getVoltage(0) : getVoltageSimd<T>(firstChannel); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
T getNormalVoltageSimd(T normalVoltage, int firstChannel) { |
|
|
|
|
|
return isConnected() ? getVoltageSimd<T>(firstChannel) : normalVoltage; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
T getNormalPolyVoltageSimd(T normalVoltage, int firstChannel) { |
|
|
|
|
|
return isConnected() ? getPolyVoltageSimd<T>(firstChannel) : normalVoltage; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
void setVoltageSimd(T voltage, int firstChannel) { |
|
|
|
|
|
voltage.store(&cvoltages[firstChannel]); |
|
|
|
|
|
stepCables(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Sets the number of polyphony channels. |
|
|
|
|
|
Also clears voltages of higher channels. |
|
|
|
|
|
If disconnected, this does nothing (`channels` remains 0). |
|
|
|
|
|
If 0 is given, `channels` is set to 1 but all voltages are cleared. |
|
|
|
|
|
*/ |
|
|
|
|
|
void setChannels(int channels) { |
|
|
|
|
|
// If disconnected, keep the number of channels at 0. |
|
|
|
|
|
if (this->channels == 0) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
// Set higher channel voltages to 0 |
|
|
|
|
|
for (int c = channels; c < this->channels; c++) { |
|
|
|
|
|
cvoltages[c] = 0.f; |
|
|
|
|
|
} |
|
|
|
|
|
// Don't allow caller to set port as disconnected |
|
|
|
|
|
if (channels == 0) { |
|
|
|
|
|
channels = 1; |
|
|
|
|
|
} |
|
|
|
|
|
this->channels = channels; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Returns the number of channels. |
|
|
|
|
|
If the port is disconnected, it has 0 channels. |
|
|
|
|
|
*/ |
|
|
|
|
|
int getChannels() { |
|
|
|
|
|
return channels; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Returns whether a cable is connected to the Port. |
|
|
|
|
|
You can use this for skipping code that generates output voltages. |
|
|
|
|
|
*/ |
|
|
|
|
|
bool isConnected() { |
|
|
|
|
|
return channels > 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Returns whether the cable exists and has 1 channel. */ |
|
|
|
|
|
bool isMonophonic() { |
|
|
|
|
|
return channels == 1; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Returns whether the cable exists and has more than 1 channel. */ |
|
|
|
|
|
bool isPolyphonic() { |
|
|
|
|
|
return channels > 1; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Use getNormalVoltage() instead. */ |
|
|
|
|
|
DEPRECATED float normalize(float normalVoltage) { |
|
|
|
|
|
return getNormalVoltage(normalVoltage); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct Output : Port {}; |
|
|
|
|
|
|
|
|
|
|
|
struct Input : Port {}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace engine |
|
|
|
|
|
} // namespace rack |