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.

252 lines
6.5KB

  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. /** NOTE alignas is required in some systems in order to allow SSE usage. */
  31. #define SIMD_ALIGN alignas(16)
  32. namespace rack {
  33. namespace engine {
  34. /** This is inspired by the number of MIDI channels. */
  35. static constexpr const int PORT_MAX_CHANNELS = 16;
  36. struct Cable;
  37. struct Port {
  38. /** Voltage of the port. */
  39. /** NOTE alignas is required in order to allow SSE usage.
  40. Consecutive data (like in a vector) would otherwise pack Ports in a way that breaks SSE. */
  41. union SIMD_ALIGN {
  42. /** Unstable API. Use getVoltage() and setVoltage() instead. */
  43. float voltages[PORT_MAX_CHANNELS] = {};
  44. /** DEPRECATED. Unstable API. Use getVoltage() and setVoltage() instead. */
  45. float value;
  46. };
  47. union {
  48. /** Number of polyphonic channels.
  49. DEPRECATED. Unstable API. Use set/getChannels() instead.
  50. May be 0 to PORT_MAX_CHANNELS.
  51. 0 channels means disconnected.
  52. */
  53. uint8_t channels = 0;
  54. /** DEPRECATED. Unstable API. Use isConnected() instead. */
  55. uint8_t active;
  56. };
  57. /** For rendering plug lights on cables.
  58. Green for positive, red for negative, and blue for polyphonic.
  59. */
  60. Light plugLights[3];
  61. enum Type {
  62. INPUT,
  63. OUTPUT,
  64. };
  65. /** Sets the voltage of the given channel. */
  66. void setVoltage(float voltage, int channel = 0) {
  67. voltages[channel] = voltage;
  68. }
  69. /** Returns the voltage of the given channel.
  70. Because of proper bookkeeping, all channels higher than the input port's number of channels should be 0V.
  71. */
  72. float getVoltage(int channel = 0) {
  73. return voltages[channel];
  74. }
  75. /** Returns the given channel's voltage if the port is polyphonic, otherwise returns the first voltage (channel 0). */
  76. float getPolyVoltage(int channel) {
  77. return isMonophonic() ? getVoltage(0) : getVoltage(channel);
  78. }
  79. /** Returns the voltage if a cable is connected, otherwise returns the given normal voltage. */
  80. float getNormalVoltage(float normalVoltage, int channel = 0) {
  81. return isConnected() ? getVoltage(channel) : normalVoltage;
  82. }
  83. float getNormalPolyVoltage(float normalVoltage, int channel) {
  84. return isConnected() ? getPolyVoltage(channel) : normalVoltage;
  85. }
  86. /** Returns a pointer to the array of voltages beginning with firstChannel.
  87. The pointer can be used for reading and writing.
  88. */
  89. float* getVoltages(int firstChannel = 0) {
  90. return &voltages[firstChannel];
  91. }
  92. /** Copies the port's voltages to an array of size at least `channels`. */
  93. void readVoltages(float* v) {
  94. for (int c = 0; c < channels; c++) {
  95. v[c] = voltages[c];
  96. }
  97. }
  98. /** Copies an array of size at least `channels` to the port's voltages.
  99. Remember to set the number of channels *before* calling this method.
  100. */
  101. void writeVoltages(const float* v) {
  102. for (int c = 0; c < channels; c++) {
  103. voltages[c] = v[c];
  104. }
  105. }
  106. /** Sets all voltages to 0. */
  107. void clearVoltages() {
  108. for (int c = 0; c < channels; c++) {
  109. voltages[c] = 0.f;
  110. }
  111. }
  112. /** Returns the sum of all voltages. */
  113. float getVoltageSum() {
  114. float sum = 0.f;
  115. for (int c = 0; c < channels; c++) {
  116. sum += voltages[c];
  117. }
  118. return sum;
  119. }
  120. /** Returns the root-mean-square of all voltages.
  121. Uses sqrt() which is slow, so use a custom approximation if calling frequently.
  122. */
  123. float getVoltageRMS() {
  124. if (channels == 0) {
  125. return 0.f;
  126. }
  127. else if (channels == 1) {
  128. return std::fabs(voltages[0]);
  129. }
  130. else {
  131. float sum = 0.f;
  132. for (int c = 0; c < channels; c++) {
  133. sum += std::pow(voltages[c], 2);
  134. }
  135. return std::sqrt(sum);
  136. }
  137. }
  138. template <typename T>
  139. T getVoltageSimd(int firstChannel) {
  140. return T::load(&voltages[firstChannel]);
  141. }
  142. template <typename T>
  143. T getPolyVoltageSimd(int firstChannel) {
  144. return isMonophonic() ? getVoltage(0) : getVoltageSimd<T>(firstChannel);
  145. }
  146. template <typename T>
  147. T getNormalVoltageSimd(T normalVoltage, int firstChannel) {
  148. return isConnected() ? getVoltageSimd<T>(firstChannel) : normalVoltage;
  149. }
  150. template <typename T>
  151. T getNormalPolyVoltageSimd(T normalVoltage, int firstChannel) {
  152. return isConnected() ? getPolyVoltageSimd<T>(firstChannel) : normalVoltage;
  153. }
  154. template <typename T>
  155. void setVoltageSimd(T voltage, int firstChannel) {
  156. voltage.store(&voltages[firstChannel]);
  157. }
  158. /** Sets the number of polyphony channels.
  159. Also clears voltages of higher channels.
  160. If disconnected, this does nothing (`channels` remains 0).
  161. If 0 is given, `channels` is set to 1 but all voltages are cleared.
  162. */
  163. void setChannels(int channels) {
  164. // If disconnected, keep the number of channels at 0.
  165. if (this->channels == 0) {
  166. return;
  167. }
  168. // Set higher channel voltages to 0
  169. for (int c = channels; c < this->channels; c++) {
  170. voltages[c] = 0.f;
  171. }
  172. // Don't allow caller to set port as disconnected
  173. if (channels == 0) {
  174. channels = 1;
  175. }
  176. this->channels = channels;
  177. }
  178. /** Returns the number of channels.
  179. If the port is disconnected, it has 0 channels.
  180. */
  181. int getChannels() {
  182. return channels;
  183. }
  184. /** Returns whether a cable is connected to the Port.
  185. You can use this for skipping code that generates output voltages.
  186. */
  187. bool isConnected() {
  188. return channels > 0;
  189. }
  190. /** Returns whether the cable exists and has 1 channel. */
  191. bool isMonophonic() {
  192. return channels == 1;
  193. }
  194. /** Returns whether the cable exists and has more than 1 channel. */
  195. bool isPolyphonic() {
  196. return channels > 1;
  197. }
  198. /** Use getNormalVoltage() instead. */
  199. DEPRECATED float normalize(float normalVoltage) {
  200. return getNormalVoltage(normalVoltage);
  201. }
  202. };
  203. struct Output : Port {
  204. /** List of cables connected to this port. */
  205. std::list<Cable*> cables;
  206. };
  207. struct Input : Port {};
  208. } // namespace engine
  209. } // namespace rack