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.

180 lines
4.4KB

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