You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

269 lines
7.2KB

  1. /*
  2. * DISTRHO Cardinal Plugin
  3. * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 3 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the LICENSE file.
  16. */
  17. /**
  18. * This file is an edited version of VCVRack's engine/Port.hpp
  19. * Copyright (C) 2016-2021 VCV.
  20. *
  21. * This program is free software: you can redistribute it and/or
  22. * modify it under the terms of the GNU General Public License as
  23. * published by the Free Software Foundation; either version 3 of
  24. * the License, or (at your option) any later version.
  25. */
  26. #pragma once
  27. #include <common.hpp>
  28. #include <engine/Light.hpp>
  29. #include <list>
  30. namespace rack {
  31. namespace engine {
  32. /** This is inspired by the number of MIDI channels. */
  33. static const int PORT_MAX_CHANNELS = 16;
  34. struct Cable;
  35. struct Port {
  36. /** Voltage of the port. */
  37. /** NOTE Purposefully renamed in Cardinal as a way to catch plugins using it directly. */
  38. union {
  39. /** Unstable API. Use getVoltage() and setVoltage() instead. */
  40. float cvoltages[PORT_MAX_CHANNELS] = {};
  41. /** DEPRECATED. Unstable API. Use getVoltage() and setVoltage() instead. */
  42. float cvalue;
  43. };
  44. /** Special trickery for backwards compatibility with plugins using DEPRECATED APIs */
  45. struct BackwardsCompatPortValue {
  46. Port* const port;
  47. BackwardsCompatPortValue(Port* p) : port(p) {}
  48. void operator=(float value) { port->setVoltage(value); }
  49. void operator-=(float value) { port->setVoltage(port->cvalue - value); }
  50. void operator+=(float value) { port->setVoltage(port->cvalue + value); }
  51. void operator*=(float value) { port->setVoltage(port->cvalue * value); }
  52. void operator/=(float value) { port->setVoltage(port->cvalue / value); }
  53. operator float() const { return port->cvalue; }
  54. } value;
  55. Port() : value(this) {}
  56. union {
  57. /** Number of polyphonic channels.
  58. DEPRECATED. Unstable API. Use set/getChannels() instead.
  59. May be 0 to PORT_MAX_CHANNELS.
  60. 0 channels means disconnected.
  61. */
  62. uint8_t channels = 0;
  63. /** DEPRECATED. Unstable API. Use isConnected() instead. */
  64. uint8_t active;
  65. };
  66. /** For rendering plug lights on cables.
  67. Green for positive, red for negative, and blue for polyphonic.
  68. */
  69. Light plugLights[3];
  70. enum Type {
  71. INPUT,
  72. OUTPUT,
  73. };
  74. /** Cables connected to this output port. */
  75. std::list<Cable*> cables;
  76. /** Step-through the cables.
  77. Called whenever voltage changes, required for zero latency operation. */
  78. void stepCables();
  79. /** Sets the voltage of the given channel. */
  80. void setVoltage(float voltage, int channel = 0) {
  81. cvoltages[channel] = voltage;
  82. stepCables();
  83. }
  84. /** Returns the voltage of the given channel.
  85. Because of proper bookkeeping, all channels higher than the input port's number of channels should be 0V.
  86. */
  87. float getVoltage(int channel = 0) {
  88. return cvoltages[channel];
  89. }
  90. /** Returns the given channel's voltage if the port is polyphonic, otherwise returns the first voltage (channel 0). */
  91. float getPolyVoltage(int channel) {
  92. return isMonophonic() ? getVoltage(0) : getVoltage(channel);
  93. }
  94. /** Returns the voltage if a cable is connected, otherwise returns the given normal voltage. */
  95. float getNormalVoltage(float normalVoltage, int channel = 0) {
  96. return isConnected() ? getVoltage(channel) : normalVoltage;
  97. }
  98. float getNormalPolyVoltage(float normalVoltage, int channel) {
  99. return isConnected() ? getPolyVoltage(channel) : normalVoltage;
  100. }
  101. /** Returns a pointer to the array of voltages beginning with firstChannel.
  102. The pointer can be used for reading and writing.
  103. */
  104. float* getVoltages(int firstChannel = 0) {
  105. return &cvoltages[firstChannel];
  106. }
  107. /** Copies the port's voltages to an array of size at least `channels`. */
  108. void readVoltages(float* v) {
  109. for (int c = 0; c < channels; c++) {
  110. v[c] = cvoltages[c];
  111. }
  112. }
  113. /** Copies an array of size at least `channels` to the port's voltages.
  114. Remember to set the number of channels *before* calling this method.
  115. */
  116. void writeVoltages(const float* v) {
  117. for (int c = 0; c < channels; c++) {
  118. cvoltages[c] = v[c];
  119. }
  120. stepCables();
  121. }
  122. /** Sets all voltages to 0. */
  123. void clearVoltages() {
  124. for (int c = 0; c < channels; c++) {
  125. cvoltages[c] = 0.f;
  126. }
  127. stepCables();
  128. }
  129. /** Returns the sum of all voltages. */
  130. float getVoltageSum() {
  131. float sum = 0.f;
  132. for (int c = 0; c < channels; c++) {
  133. sum += cvoltages[c];
  134. }
  135. return sum;
  136. }
  137. /** Returns the root-mean-square of all voltages.
  138. Uses sqrt() which is slow, so use a custom approximation if calling frequently.
  139. */
  140. float getVoltageRMS() {
  141. if (channels == 0) {
  142. return 0.f;
  143. }
  144. else if (channels == 1) {
  145. return std::fabs(cvoltages[0]);
  146. }
  147. else {
  148. float sum = 0.f;
  149. for (int c = 0; c < channels; c++) {
  150. sum += std::pow(cvoltages[c], 2);
  151. }
  152. return std::sqrt(sum);
  153. }
  154. }
  155. template <typename T>
  156. T getVoltageSimd(int firstChannel) {
  157. return T::load(&cvoltages[firstChannel]);
  158. }
  159. template <typename T>
  160. T getPolyVoltageSimd(int firstChannel) {
  161. return isMonophonic() ? getVoltage(0) : getVoltageSimd<T>(firstChannel);
  162. }
  163. template <typename T>
  164. T getNormalVoltageSimd(T normalVoltage, int firstChannel) {
  165. return isConnected() ? getVoltageSimd<T>(firstChannel) : normalVoltage;
  166. }
  167. template <typename T>
  168. T getNormalPolyVoltageSimd(T normalVoltage, int firstChannel) {
  169. return isConnected() ? getPolyVoltageSimd<T>(firstChannel) : normalVoltage;
  170. }
  171. template <typename T>
  172. void setVoltageSimd(T voltage, int firstChannel) {
  173. voltage.store(&cvoltages[firstChannel]);
  174. stepCables();
  175. }
  176. /** Sets the number of polyphony channels.
  177. Also clears voltages of higher channels.
  178. If disconnected, this does nothing (`channels` remains 0).
  179. If 0 is given, `channels` is set to 1 but all voltages are cleared.
  180. */
  181. void setChannels(int channels) {
  182. // If disconnected, keep the number of channels at 0.
  183. if (this->channels == 0) {
  184. return;
  185. }
  186. // Set higher channel voltages to 0
  187. for (int c = channels; c < this->channels; c++) {
  188. cvoltages[c] = 0.f;
  189. }
  190. // Don't allow caller to set port as disconnected
  191. if (channels == 0) {
  192. channels = 1;
  193. }
  194. this->channels = channels;
  195. }
  196. /** Returns the number of channels.
  197. If the port is disconnected, it has 0 channels.
  198. */
  199. int getChannels() {
  200. return channels;
  201. }
  202. /** Returns whether a cable is connected to the Port.
  203. You can use this for skipping code that generates output voltages.
  204. */
  205. bool isConnected() {
  206. return channels > 0;
  207. }
  208. /** Returns whether the cable exists and has 1 channel. */
  209. bool isMonophonic() {
  210. return channels == 1;
  211. }
  212. /** Returns whether the cable exists and has more than 1 channel. */
  213. bool isPolyphonic() {
  214. return channels > 1;
  215. }
  216. /** Use getNormalVoltage() instead. */
  217. DEPRECATED float normalize(float normalVoltage) {
  218. return getNormalVoltage(normalVoltage);
  219. }
  220. };
  221. struct Output : Port {};
  222. struct Input : Port {};
  223. } // namespace engine
  224. } // namespace rack