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.

287 lines
9.9KB

  1. #ifndef TROWASOFT_UTILITIES_HPP
  2. #define TROWASOFT_UTILITIES_HPP
  3. #include "rack.hpp"
  4. using namespace rack;
  5. #include <string.h>
  6. #include <vector>
  7. #include <sstream> // std::istringstream
  8. #include "util/math.hpp"
  9. #define TROWA_DEBUG_LVL_HIGH 100
  10. #define TROWA_DEBUG_LVL_MED 50
  11. #define TROWA_DEBUG_LVL_LOW 1
  12. #define TROWA_DEBUG_LVL_OFF 0
  13. #define TROWA_DEBUG_MSGS TROWA_DEBUG_LVL_OFF
  14. #define TROWA_PULSE_WIDTH (1e-3)
  15. #define TROWA_HORIZ_MARGIN 13 // Margin for element layout in Module widget
  16. #define TROWA_VERT_MARGIN 13 // Margin for element layout
  17. #define TROWA_INDEX_UNDEFINED -1 // Value for undefined index.
  18. #define TROWA_DISP_MSG_SIZE 30 // For local buffers of strings
  19. #define TROWA_SEQ_NUM_PATTERNS 64 // Number of patterns for sequencers.
  20. #define TROWA_SEQ_PATTERN_MIN_V -10 // Min voltage input / output for controlling pattern index and BPM
  21. #define TROWA_SEQ_PATTERN_MAX_V 10 // Max voltage input / output for controlling pattern index and BPM
  22. #define TROWA_SEQ_NUM_NOTES 12 // Num notes per octave (1 V per octave)
  23. #define TROWA_SEQ_NOTES_MIN_V -5 // OK, so back to -5 to +5V (from -4 to +6V), we'll just have a -1 Octave since apparently -1 Octave is a thing (MIDI 0-11)?
  24. #define TROWA_SEQ_NOTES_MAX_V 5 // OK, so back to -5 to +5V (from -4 to +6V), we'll just have a -1 Octave since apparently -1 Octave is a thing (MIDI 0-11)?
  25. #define TROWA_SEQ_ZERO_OCTAVE 4 // Octave for voltage 0 -- Was 5, now 4
  26. #define TROWA_SEQ_NUM_OCTAVES 10 // Number of total octaves
  27. #define TROWA_MIDI_NOTE_MIDDLE_C 60 // MIDI note (middle C) - should correspond to C4 (Voltage 0)
  28. #define TROWA_NUM_GLOBAL_EFFECTS 11
  29. #define TROWA_ANGLE_STRAIGHT_UP_RADIANS (1.5*NVG_PI) // Angle for straight up (svg angles start from positive x and go clockwise)
  30. #define TROWA_ANGLE_STRAIGHT_DOWN_RADIANS (0.5*NVG_PI) // Angle for straiclamght down
  31. #define TROWA_BASE_FREQUENCY 261.626f // Base frequency for C4 (Voltage 0)
  32. // Fonts:
  33. #define TROWA_DIGITAL_FONT "res/Fonts/Digital dream Fat.ttf"
  34. #define TROWA_LABEL_FONT "res/Fonts/ZeroesThree-Regular.ttf"
  35. #define TROWA_MONOSPACE_FONT "res/Fonts/larabieb.ttf"
  36. #define TROWA_MATH_FONT "res/Fonts/Math Symbols Normal.ttf"
  37. extern const char * TROWA_NOTES[TROWA_SEQ_NUM_NOTES]; // Our note labels.
  38. // Given some input voltage, convert to our Pattern index [0-63].
  39. inline int VoltsToPattern(float voltsInput)
  40. {
  41. return (int)clamp((int)(roundf(rescale(voltsInput, (float)TROWA_SEQ_PATTERN_MIN_V, (float)TROWA_SEQ_PATTERN_MAX_V, 1.0f, (float)TROWA_SEQ_NUM_PATTERNS))), 1, TROWA_SEQ_NUM_PATTERNS);
  42. }
  43. // Pattern index [0-63] to output voltage.
  44. inline float PatternToVolts(int patternIx)
  45. {
  46. return rescale(patternIx + 1, 1, TROWA_SEQ_NUM_PATTERNS, TROWA_SEQ_PATTERN_MIN_V, TROWA_SEQ_PATTERN_MAX_V);
  47. }
  48. // Voltage [-5 to 5] to Octave -1 to 9
  49. inline int VoltsToOctave(float v)
  50. {
  51. return (int)(v + TROWA_SEQ_ZERO_OCTAVE);
  52. }
  53. // Note index 0 to 11 (to TROWA_NOTES array).
  54. inline int VoltsToNoteIx(float v)
  55. {
  56. // This doesn't work all the time.
  57. //(v - floorf(v))*TROWA_SEQ_NUM_NOTES
  58. // (-4.9 - -5) * 12 = 0.1*12 = int(1.2) = 1 [C#]
  59. // (-0.33 - -1) * 12 = 0.67*12 = int(8.04) = 8 [G#]
  60. return (int)(round((v + TROWA_SEQ_ZERO_OCTAVE)*TROWA_SEQ_NUM_NOTES)) % TROWA_SEQ_NUM_NOTES;
  61. }
  62. // Voltage to frequency (Hz).
  63. inline float VoltageToFrequency(float v)
  64. {
  65. return TROWA_BASE_FREQUENCY * powf(2.0f, v);
  66. }
  67. // Frequency (Hz) to Voltage.
  68. inline float Frequency2Voltage(float f)
  69. {
  70. return log2f(f / TROWA_BASE_FREQUENCY);
  71. }
  72. // Floating point hue [0-1.0] to color.
  73. NVGcolor inline HueToColor(float hue)
  74. {
  75. return nvgHSLA(hue, 1.0, 0.5, /*alpha 0-255*/ 0xff);
  76. }
  77. // Floating point hue [0-1.0] to color.
  78. NVGcolor inline HueToColor(float hue, float sat, float light)
  79. {
  80. return nvgHSLA(hue, sat, light, /*alpha 0-255*/ 0xff);
  81. }
  82. // Floating point hue [0-1.0] to color for our color gradient.
  83. NVGcolor inline HueToColorGradient(float hue)
  84. {
  85. return nvgHSLA(hue, 1.0, 0.5, /*alpha 0-255*/ 0xff);
  86. }
  87. NVGcolor inline ColorInvertToNegative(NVGcolor color)
  88. { // Keep alpha the same.
  89. return nvgRGBAf(1.0 - color.r, 1.0 - color.g, 1.0 - color.b, color.a);
  90. }
  91. // Split a string
  92. std::vector<std::string> str_split(const std::string& s, char delimiter);
  93. struct TSColorHSL {
  94. union {
  95. float hsl[3];
  96. struct {
  97. float h, s, lum;
  98. };
  99. };
  100. };
  101. typedef struct TSColorHSL TSColorHSL;
  102. namespace trowaSoft
  103. {
  104. void TSColorToHSL(NVGcolor color, TSColorHSL* hsv);
  105. }
  106. struct GlobalEffect {
  107. NVGcompositeOperation compositeOperation = NVG_SOURCE_OVER;
  108. const char* label;
  109. GlobalEffect(const char* label, NVGcompositeOperation compositeOperation)
  110. {
  111. this->label = label;
  112. this->compositeOperation = compositeOperation;
  113. return;
  114. }
  115. };
  116. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  117. // ValueSequencerMode
  118. // Information and methods for translating knob input voltages to output voltages
  119. // and for display strings.
  120. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  121. struct ValueSequencerMode
  122. {
  123. // Min value in voltage
  124. float voltageMin;
  125. // Max value in voltage
  126. float voltageMax;
  127. // Force whole / integer values
  128. bool wholeNumbersOnly;
  129. // The angle that represents 0 radians.
  130. float zeroPointAngle_radians;
  131. // Output voltage
  132. float outputVoltageMin;
  133. float outputVoltageMax;
  134. // Min value (what it means)
  135. float minDisplayValue;
  136. // Max value (what it means)
  137. float maxDisplayValue;
  138. bool needsTranslationDisplay;
  139. bool needsTranslationOutput;
  140. float roundNearestDisplay = 0;
  141. float roundNearestOutput = 0;
  142. // Format string for the display value
  143. const char * displayFormatString;
  144. // The display name.
  145. const char * displayName;
  146. float zeroValue;
  147. ValueSequencerMode()
  148. {
  149. return;
  150. }
  151. ValueSequencerMode(const char* displayName, float minDisplayValue, float maxDisplayValue, float min_V, float max_V,
  152. float outVoltageMin, float outVoltageMax,
  153. bool wholeNumbersOnly, float zeroPointAngle, const char * formatStr,
  154. float roundDisplay, float roundOutput, float zeroValue)
  155. {
  156. this->displayName = displayName;
  157. this->displayFormatString = formatStr;
  158. this->minDisplayValue = minDisplayValue; // I.e. 1
  159. this->maxDisplayValue = maxDisplayValue; // I.e. 64
  160. this->voltageMin = min_V; // I.e. -10 Volts
  161. this->voltageMax = max_V; // I.e. +10 Volts
  162. this->outputVoltageMin = outVoltageMin;
  163. this->outputVoltageMax = outVoltageMax;
  164. this->wholeNumbersOnly = wholeNumbersOnly; // Force whole numbers
  165. this->zeroPointAngle_radians = zeroPointAngle;
  166. this->roundNearestDisplay = roundDisplay;
  167. this->roundNearestOutput = roundOutput;
  168. this->zeroValue = zeroValue;
  169. needsTranslationDisplay = minDisplayValue != voltageMin || maxDisplayValue != voltageMax;
  170. needsTranslationOutput = outputVoltageMin != voltageMin || outputVoltageMax != voltageMax;
  171. return;
  172. }
  173. virtual void GetDisplayString(/*in*/ float val, /*out*/ char* buffer)
  174. {
  175. float dVal = val;
  176. if (needsTranslationDisplay)
  177. {
  178. dVal = rescale(val, voltageMin, voltageMax, minDisplayValue, maxDisplayValue);
  179. }
  180. if (roundNearestDisplay > 0)
  181. {
  182. dVal = static_cast<int>(dVal / roundNearestDisplay) * roundNearestDisplay;
  183. }
  184. sprintf(buffer, displayFormatString, dVal);
  185. return;
  186. }
  187. virtual float GetOutputValue(float val)
  188. {
  189. float oVal = val;
  190. if (needsTranslationOutput)
  191. {
  192. oVal = rescale(val, voltageMin, voltageMax, outputVoltageMin, outputVoltageMax);
  193. }
  194. if (roundNearestOutput > 0)
  195. { // Round this
  196. oVal = static_cast<int>(round(oVal / roundNearestOutput)) * roundNearestOutput;
  197. }
  198. return oVal;
  199. }
  200. };
  201. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  202. // NoteValueSequencerMode
  203. // Special sequencer mode for displaying human friendly Note labels instead of voltages.
  204. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  205. struct NoteValueSequencerMode : ValueSequencerMode
  206. {
  207. NoteValueSequencerMode(const char* displayName,
  208. float min_V, float max_V)
  209. {
  210. this->displayName = displayName;
  211. //this->minDisplayValue = -TROWA_SEQ_ZERO_OCTAVE; // -4
  212. //this->maxDisplayValue = TROWA_SEQ_NUM_OCTAVES - TROWA_SEQ_ZERO_OCTAVE; // 10-4 = 6
  213. this->minDisplayValue = TROWA_SEQ_NOTES_MIN_V; // Back to -5V
  214. this->maxDisplayValue = TROWA_SEQ_NOTES_MAX_V; // Back to +5V
  215. this->voltageMin = min_V; // I.e. -10 Volts
  216. this->voltageMax = max_V; // I.e. +10 Volts
  217. this->outputVoltageMin = TROWA_SEQ_NOTES_MIN_V; // Now back to -5V. Was -4V: -TROWA_SEQ_ZERO_OCTAVE;
  218. this->outputVoltageMax = TROWA_SEQ_NOTES_MAX_V; // Now back to +5V. Was +6V: TROWA_SEQ_NUM_OCTAVES - TROWA_SEQ_ZERO_OCTAVE; // 10-4 = 6
  219. this->wholeNumbersOnly = false; // Force whole numbers
  220. // Zero is no longer straight up now that we are going -4 to +6
  221. // Knob goes from 0.67*NVG_PI to 2.33*NVG_PI (1 and 2/3 Pi)
  222. //this->zeroPointAngle_radians = 0.67*NVG_PI + TROWA_SEQ_ZERO_OCTAVE *1.67*NVG_PI / TROWA_SEQ_NUM_OCTAVES;
  223. //this->zeroValue = rescale(0, this->minDisplayValue, this->maxDisplayValue, min_V, max_V);
  224. // C4 is now zero, but we we returning to -5 to +5V. (So starts at C-1 instead of C0 and goes to C9 instead of C10).
  225. this->zeroPointAngle_radians = TROWA_ANGLE_STRAIGHT_UP_RADIANS;// 1.5*NVG_PI; // Straight up
  226. this->zeroValue = rescale(0, this->minDisplayValue, this->maxDisplayValue, min_V, max_V);
  227. this->roundNearestDisplay = 1.0/TROWA_SEQ_NUM_NOTES;
  228. this->roundNearestOutput = 1.0/TROWA_SEQ_NUM_NOTES;
  229. needsTranslationDisplay = minDisplayValue != voltageMin || maxDisplayValue != voltageMax;
  230. needsTranslationOutput = outputVoltageMin != voltageMin || outputVoltageMax != voltageMax;
  231. return;
  232. }
  233. // Overriden display string to show notes instead of output voltage values.
  234. void GetDisplayString(/*in*/ float val, /*out*/ char* buffer) override
  235. {
  236. // Now octaves will go -1 to +9.
  237. int octave = VoltsToOctave(val);
  238. int noteIx = VoltsToNoteIx(val);
  239. if (noteIx > TROWA_SEQ_NUM_NOTES - 1)
  240. noteIx = TROWA_SEQ_NUM_NOTES - 1;
  241. else if (noteIx < 0)
  242. noteIx = 0;
  243. sprintf(buffer, "%s%d", TROWA_NOTES[noteIx], octave);
  244. return;
  245. }
  246. };
  247. #endif // end if not defined