|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- #pragma once
- #include <common.hpp>
- #include <engine/Light.hpp>
-
-
- namespace rack {
- namespace engine {
-
-
- /** This is inspired by the number of MIDI channels. */
- static const int PORT_MAX_CHANNELS = 16;
-
-
- struct Port {
- /** Voltage of the port. */
- union {
- /** Unstable API. Use getVoltage() and setVoltage() instead. */
- float voltages[PORT_MAX_CHANNELS] = {};
- /** DEPRECATED. Unstable API. Use getVoltage() and setVoltage() instead. */
- float value;
- };
- 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,
- };
-
- /** Sets the voltage of the given channel. */
- void setVoltage(float voltage, uint8_t channel = 0) {
- voltages[channel] = voltage;
- }
-
- /** 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(uint8_t channel = 0) {
- return voltages[channel];
- }
-
- /** Returns the given channel's voltage if the port is polyphonic, otherwise returns the first voltage (channel 0). */
- float getPolyVoltage(uint8_t 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, uint8_t channel = 0) {
- return isConnected() ? getVoltage(channel) : normalVoltage;
- }
-
- float getNormalPolyVoltage(float normalVoltage, uint8_t 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(uint8_t firstChannel = 0) {
- return &voltages[firstChannel];
- }
-
- /** Copies the port's voltages to an array of size at least `channels`. */
- void readVoltages(float* v) {
- for (uint8_t c = 0; c < channels; c++) {
- v[c] = voltages[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 (uint8_t c = 0; c < channels; c++) {
- voltages[c] = v[c];
- }
- }
-
- /** Sets all voltages to 0. */
- void clearVoltages() {
- for (uint8_t c = 0; c < channels; c++) {
- voltages[c] = 0.f;
- }
- }
-
- /** Returns the sum of all voltages. */
- float getVoltageSum() {
- float sum = 0.f;
- for (uint8_t c = 0; c < channels; c++) {
- sum += voltages[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(voltages[0]);
- }
- else {
- float sum = 0.f;
- for (uint8_t c = 0; c < channels; c++) {
- sum += std::pow(voltages[c], 2);
- }
- return std::sqrt(sum);
- }
- }
-
- template <typename T>
- T getVoltageSimd(uint8_t firstChannel) {
- return T::load(&voltages[firstChannel]);
- }
-
- template <typename T>
- T getPolyVoltageSimd(uint8_t firstChannel) {
- return isMonophonic() ? getVoltage(0) : getVoltageSimd<T>(firstChannel);
- }
-
- template <typename T>
- T getNormalVoltageSimd(T normalVoltage, uint8_t firstChannel) {
- return isConnected() ? getVoltageSimd<T>(firstChannel) : normalVoltage;
- }
-
- template <typename T>
- T getNormalPolyVoltageSimd(T normalVoltage, uint8_t firstChannel) {
- return isConnected() ? getPolyVoltageSimd<T>(firstChannel) : normalVoltage;
- }
-
- template <typename T>
- void setVoltageSimd(T voltage, uint8_t firstChannel) {
- voltage.store(&voltages[firstChannel]);
- }
-
- /** 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(uint8_t channels) {
- // If disconnected, keep the number of channels at 0.
- if (this->channels == 0) {
- return;
- }
- // Set higher channel voltages to 0
- for (uint8_t c = channels; c < this->channels; c++) {
- voltages[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
|