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.

190 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 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. 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. template <typename T>
  89. T getVoltageSimd(int firstChannel) {
  90. return T::load(&voltages[firstChannel]);
  91. }
  92. template <typename T>
  93. T getPolyVoltageSimd(int firstChannel) {
  94. return isMonophonic() ? getVoltage(0) : getVoltageSimd<T>(firstChannel);
  95. }
  96. template <typename T>
  97. T getNormalVoltageSimd(T normalVoltage, int firstChannel) {
  98. return isConnected() ? getVoltageSimd<T>(firstChannel) : normalVoltage;
  99. }
  100. template <typename T>
  101. T getNormalPolyVoltageSimd(T normalVoltage, int firstChannel) {
  102. return isConnected() ? getPolyVoltageSimd<T>(firstChannel) : normalVoltage;
  103. }
  104. template <typename T>
  105. void setVoltageSimd(T voltage, int firstChannel) {
  106. voltage.store(&voltages[firstChannel]);
  107. }
  108. /** Sets the number of polyphony channels.
  109. Also clears voltages of higher channels.
  110. If disconnected, this does nothing (`channels` remains 0).
  111. If 0 is given, `channels` is set to 1 but all voltages are cleared.
  112. */
  113. void setChannels(int channels) {
  114. // If disconnected, keep the number of channels at 0.
  115. if (this->channels == 0) {
  116. return;
  117. }
  118. // Set higher channel voltages to 0
  119. for (int c = channels; c < this->channels; c++) {
  120. voltages[c] = 0.f;
  121. }
  122. // Don't allow caller to set port as disconnected
  123. if (channels == 0) {
  124. channels = 1;
  125. }
  126. this->channels = channels;
  127. }
  128. /** Returns the number of channels.
  129. If the port is disconnected, it has 0 channels.
  130. */
  131. int getChannels() {
  132. return channels;
  133. }
  134. /** Returns whether a cable is connected to the Port.
  135. You can use this for skipping code that generates output voltages.
  136. */
  137. bool isConnected() {
  138. return channels > 0;
  139. }
  140. /** Returns whether the cable exists and has 1 channel. */
  141. bool isMonophonic() {
  142. return channels == 1;
  143. }
  144. /** Returns whether the cable exists and has more than 1 channel. */
  145. bool isPolyphonic() {
  146. return channels > 1;
  147. }
  148. /** Use getNormalVoltage() instead. */
  149. DEPRECATED float normalize(float normalVoltage) {
  150. return getNormalVoltage(normalVoltage);
  151. }
  152. };
  153. struct Output : Port {};
  154. struct Input : Port {};
  155. } // namespace engine
  156. } // namespace rack