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.

187 lines
4.7KB

  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 alignas(32) 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. 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. /** Sets the voltage of the given channel. */
  30. void setVoltage(float voltage, int channel = 0) {
  31. voltages[channel] = voltage;
  32. }
  33. /** Returns the voltage of the given channel.
  34. Because of proper bookkeeping, all channels higher than the input port's number of channels should be 0V.
  35. */
  36. float getVoltage(int channel = 0) {
  37. return voltages[channel];
  38. }
  39. /** Returns the given channel's voltage if the port is polyphonic, otherwise returns the first voltage (channel 0). */
  40. float getPolyVoltage(int channel) {
  41. return isMonophonic() ? getVoltage(0) : getVoltage(channel);
  42. }
  43. /** Returns the voltage if a cable is connected, otherwise returns the given normal voltage. */
  44. float getNormalVoltage(float normalVoltage, int channel = 0) {
  45. return isConnected() ? getVoltage(channel) : normalVoltage;
  46. }
  47. float getNormalPolyVoltage(float normalVoltage, int channel) {
  48. return isConnected() ? getPolyVoltage(channel) : normalVoltage;
  49. }
  50. /** Returns a pointer to the array of voltages beginning with firstChannel.
  51. The pointer can be used for reading and writing.
  52. */
  53. float* getVoltages(int firstChannel = 0) {
  54. return &voltages[firstChannel];
  55. }
  56. /** Copies the port's voltages to an array of size at least `channels`. */
  57. void readVoltages(float* v) {
  58. for (int c = 0; c < channels; c++) {
  59. v[c] = voltages[c];
  60. }
  61. }
  62. /** Copies an array of size at least `channels` to the port's voltages.
  63. Remember to set the number of channels *before* calling this method.
  64. */
  65. void writeVoltages(const float* v) {
  66. for (int c = 0; c < channels; c++) {
  67. voltages[c] = v[c];
  68. }
  69. }
  70. /** Sets all voltages to 0. */
  71. void clearVoltages() {
  72. for (int c = 0; c < channels; c++) {
  73. voltages[c] = 0.f;
  74. }
  75. }
  76. /** Returns the sum of all voltages. */
  77. float getVoltageSum() {
  78. float sum = 0.f;
  79. for (int c = 0; c < channels; c++) {
  80. sum += voltages[c];
  81. }
  82. return sum;
  83. }
  84. template <typename T>
  85. T getVoltageSimd(int firstChannel) {
  86. return T::load(&voltages[firstChannel]);
  87. }
  88. template <typename T>
  89. T getPolyVoltageSimd(int firstChannel) {
  90. return isMonophonic() ? getVoltage(0) : getVoltageSimd<T>(firstChannel);
  91. }
  92. template <typename T>
  93. T getNormalVoltageSimd(T normalVoltage, int firstChannel) {
  94. return isConnected() ? getVoltageSimd<T>(firstChannel) : normalVoltage;
  95. }
  96. template <typename T>
  97. T getNormalPolyVoltageSimd(T normalVoltage, int firstChannel) {
  98. return isConnected() ? getPolyVoltageSimd<T>(firstChannel) : normalVoltage;
  99. }
  100. template <typename T>
  101. void setVoltageSimd(T voltage, int firstChannel) {
  102. voltage.store(&voltages[firstChannel]);
  103. }
  104. /** Sets the number of polyphony channels.
  105. Also clears voltages of higher channels.
  106. If disconnected, this does nothing (`channels` remains 0).
  107. If 0 is given, `channels` is set to 1 but all voltages are cleared.
  108. */
  109. void setChannels(int channels) {
  110. // If disconnected, keep the number of channels at 0.
  111. if (this->channels == 0) {
  112. return;
  113. }
  114. // Set higher channel voltages to 0
  115. for (int c = channels; c < this->channels; c++) {
  116. voltages[c] = 0.f;
  117. }
  118. // Don't allow caller to set port as disconnected
  119. if (channels == 0) {
  120. channels = 1;
  121. }
  122. this->channels = channels;
  123. }
  124. /** Returns the number of channels.
  125. If the port is disconnected, it has 0 channels.
  126. */
  127. int getChannels() {
  128. return channels;
  129. }
  130. /** Returns whether a cable is connected to the Port.
  131. You can use this for skipping code that generates output voltages.
  132. */
  133. bool isConnected() {
  134. return channels > 0;
  135. }
  136. /** Returns whether the cable exists and has 1 channel. */
  137. bool isMonophonic() {
  138. return channels == 1;
  139. }
  140. /** Returns whether the cable exists and has more than 1 channel. */
  141. bool isPolyphonic() {
  142. return channels > 1;
  143. }
  144. void process(float deltaTime);
  145. /** Use getNormalVoltage() instead. */
  146. DEPRECATED float normalize(float normalVoltage) {
  147. return getNormalVoltage(normalVoltage);
  148. }
  149. };
  150. struct Output : Port {};
  151. struct Input : Port {};
  152. } // namespace engine
  153. } // namespace rack