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.

249 lines
6.4KB

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