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.

207 lines
5.1KB

  1. #pragma once
  2. #include <common.hpp>
  3. #include <engine/Light.hpp>
  4. namespace rack {
  5. namespace engine {
  6. /** This is inspired by the number of MIDI channels. */
  7. static const int PORT_MAX_CHANNELS = 16;
  8. struct Port {
  9. /** Voltage of the port. */
  10. union {
  11. /** Unstable API. Use getVoltage() and setVoltage() instead. */
  12. float voltages[PORT_MAX_CHANNELS] = {};
  13. /** DEPRECATED. Unstable API. Use getVoltage() and setVoltage() instead. */
  14. float value;
  15. };
  16. union {
  17. /** Number of polyphonic channels
  18. DEPRECATED. Unstable API. Use set/getChannels() instead.
  19. May be 0 to PORT_MAX_CHANNELS.
  20. */
  21. uint8_t channels = 0;
  22. /** DEPRECATED. Unstable API. Use isConnected() instead. */
  23. uint8_t active;
  24. };
  25. /** For rendering plug lights on cables.
  26. Green for positive, red for negative, and blue for polyphonic.
  27. */
  28. Light plugLights[3];
  29. enum Type {
  30. INPUT,
  31. OUTPUT,
  32. };
  33. /** Sets the voltage of the given channel. */
  34. void setVoltage(float voltage, int channel = 0) {
  35. voltages[channel] = voltage;
  36. }
  37. /** Returns the voltage of the given channel.
  38. Because of proper bookkeeping, all channels higher than the input port's number of channels should be 0V.
  39. */
  40. float getVoltage(int channel = 0) {
  41. return voltages[channel];
  42. }
  43. /** Returns the given channel's voltage if the port is polyphonic, otherwise returns the first voltage (channel 0). */
  44. float getPolyVoltage(int channel) {
  45. return isMonophonic() ? getVoltage(0) : getVoltage(channel);
  46. }
  47. /** Returns the voltage if a cable is connected, otherwise returns the given normal voltage. */
  48. float getNormalVoltage(float normalVoltage, int channel = 0) {
  49. return isConnected() ? getVoltage(channel) : normalVoltage;
  50. }
  51. float getNormalPolyVoltage(float normalVoltage, int channel) {
  52. return isConnected() ? getPolyVoltage(channel) : normalVoltage;
  53. }
  54. /** Returns a pointer to the array of voltages beginning with firstChannel.
  55. The pointer can be used for reading and writing.
  56. */
  57. float* getVoltages(int firstChannel = 0) {
  58. return &voltages[firstChannel];
  59. }
  60. /** Copies the port's voltages to an array of size at least `channels`. */
  61. void readVoltages(float* v) {
  62. for (int c = 0; c < channels; c++) {
  63. v[c] = voltages[c];
  64. }
  65. }
  66. /** Copies an array of size at least `channels` to the port's voltages.
  67. Remember to set the number of channels *before* calling this method.
  68. */
  69. void writeVoltages(const float* v) {
  70. for (int c = 0; c < channels; c++) {
  71. voltages[c] = v[c];
  72. }
  73. }
  74. /** Sets all voltages to 0. */
  75. void clearVoltages() {
  76. for (int c = 0; c < channels; c++) {
  77. voltages[c] = 0.f;
  78. }
  79. }
  80. /** Returns the sum of all voltages. */
  81. float getVoltageSum() {
  82. float sum = 0.f;
  83. for (int c = 0; c < channels; c++) {
  84. sum += voltages[c];
  85. }
  86. return sum;
  87. }
  88. /** Returns the root-mean-square of all voltages. */
  89. float getVoltageRMS() {
  90. if (channels == 0) {
  91. return 0.f;
  92. }
  93. else if (channels == 1) {
  94. return std::fabs(voltages[0]);
  95. }
  96. else {
  97. float sum = 0.f;
  98. for (int c = 0; c < channels; c++) {
  99. sum += std::pow(voltages[c], 2);
  100. }
  101. return std::sqrt(sum);
  102. }
  103. }
  104. template <typename T>
  105. T getVoltageSimd(int firstChannel) {
  106. return T::load(&voltages[firstChannel]);
  107. }
  108. template <typename T>
  109. T getPolyVoltageSimd(int firstChannel) {
  110. return isMonophonic() ? getVoltage(0) : getVoltageSimd<T>(firstChannel);
  111. }
  112. template <typename T>
  113. T getNormalVoltageSimd(T normalVoltage, int firstChannel) {
  114. return isConnected() ? getVoltageSimd<T>(firstChannel) : normalVoltage;
  115. }
  116. template <typename T>
  117. T getNormalPolyVoltageSimd(T normalVoltage, int firstChannel) {
  118. return isConnected() ? getPolyVoltageSimd<T>(firstChannel) : normalVoltage;
  119. }
  120. template <typename T>
  121. void setVoltageSimd(T voltage, int firstChannel) {
  122. voltage.store(&voltages[firstChannel]);
  123. }
  124. /** Sets the number of polyphony channels.
  125. Also clears voltages of higher channels.
  126. If disconnected, this does nothing (`channels` remains 0).
  127. If 0 is given, `channels` is set to 1 but all voltages are cleared.
  128. */
  129. void setChannels(int channels) {
  130. // If disconnected, keep the number of channels at 0.
  131. if (this->channels == 0) {
  132. return;
  133. }
  134. // Set higher channel voltages to 0
  135. for (int c = channels; c < this->channels; c++) {
  136. voltages[c] = 0.f;
  137. }
  138. // Don't allow caller to set port as disconnected
  139. if (channels == 0) {
  140. channels = 1;
  141. }
  142. this->channels = channels;
  143. }
  144. /** Returns the number of channels.
  145. If the port is disconnected, it has 0 channels.
  146. */
  147. int getChannels() {
  148. return channels;
  149. }
  150. /** Returns whether a cable is connected to the Port.
  151. You can use this for skipping code that generates output voltages.
  152. */
  153. bool isConnected() {
  154. return channels > 0;
  155. }
  156. /** Returns whether the cable exists and has 1 channel. */
  157. bool isMonophonic() {
  158. return channels == 1;
  159. }
  160. /** Returns whether the cable exists and has more than 1 channel. */
  161. bool isPolyphonic() {
  162. return channels > 1;
  163. }
  164. /** Use getNormalVoltage() instead. */
  165. DEPRECATED float normalize(float normalVoltage) {
  166. return getNormalVoltage(normalVoltage);
  167. }
  168. };
  169. struct Output : Port {};
  170. struct Input : Port {};
  171. } // namespace engine
  172. } // namespace rack