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.

819 lines
29KB

  1. #ifndef TROWASOFT_MODULE_TSSEQUENCERBASE_HPP
  2. #define TROWASOFT_MODULE_TSSEQUENCERBASE_HPP
  3. #include "rack.hpp"
  4. using namespace rack;
  5. #include <thread> // std::thread
  6. #include <mutex>
  7. #include <queue>
  8. #include <vector>
  9. #include <string.h>
  10. #include <stdio.h>
  11. //#include "trowaSoft.hpp"
  12. #include "dsp/digital.hpp"
  13. #include "trowaSoftComponents.hpp"
  14. #include "trowaSoftUtilities.hpp"
  15. #include <chrono>
  16. #include "TSTempoBPM.hpp"
  17. #include "TSExternalControlMessage.hpp"
  18. #include "TSOSCCommon.hpp"
  19. #include "TSOSCSequencerListener.hpp"
  20. #include "TSOSCCommunicator.hpp"
  21. #include "TSOSCSequencerOutputMessages.hpp"
  22. #include "TSSequencerWidgetBase.hpp"
  23. #include "../lib/oscpack/osc/OscOutboundPacketStream.h"
  24. #include "../lib/oscpack/ip/UdpSocket.h"
  25. #include "../lib/oscpack/osc/OscReceivedElements.h"
  26. #include "../lib/oscpack/osc/OscPacketListener.h"
  27. #define TROWA_SEQ_NUM_CHNLS 16 // Num of channels/triggers/voices
  28. #define TROWA_SEQ_NUM_STEPS 16 // Num of steps per gate/voice
  29. #define TROWA_SEQ_MAX_NUM_STEPS 64 // Maximum number of steps
  30. #define N64_NUM_STEPS 64
  31. #define N64_NUM_ROWS 8
  32. #define N64_NUM_COLS (N64_NUM_STEPS/N64_NUM_ROWS)
  33. // Default OSC outgoing address (Tx). 127.0.0.1.
  34. #define OSC_ADDRESS_DEF "127.0.0.1"
  35. // Default OSC outgoing port (Tx). 7000.
  36. #define OSC_OUTPORT_DEF 7000
  37. // Default OSC incoming port (Rx). 7001.
  38. #define OSC_INPORT_DEF 7001
  39. // Default namespace for OSC
  40. #define OSC_DEFAULT_NS "/tsseq"
  41. #define OSC_OUTPUT_BUFFER_SIZE (1024*TROWA_SEQ_MAX_NUM_STEPS)
  42. #define OSC_ADDRESS_BUFFER_SIZE 50
  43. // If we should update the current step pointer to OSC (turn off prev step, highlight current step).
  44. // This gets slow though during testing.
  45. #define OSC_UPDATE_CURRENT_STEP_LED 1
  46. // We only show 4x4 grid of steps at time.
  47. #define TROWA_SEQ_STEP_NUM_ROWS 4 // Num of rows for display of the Steps (single Gate displayed at a time)
  48. #define TROWA_SEQ_STEP_NUM_COLS (TROWA_SEQ_NUM_STEPS/TROWA_SEQ_STEP_NUM_ROWS)
  49. #define TROWA_SEQ_NUM_MODES 3
  50. #define TROWA_SEQ_STEPS_MIN_V TROWA_SEQ_PATTERN_MIN_V // Min voltage input / output for controlling # steps
  51. #define TROWA_SEQ_STEPS_MAX_V TROWA_SEQ_PATTERN_MAX_V // Max voltage input / output for controlling # steps
  52. #define TROWA_SEQ_BPM_KNOB_MIN -2
  53. #define TROWA_SEQ_BPM_KNOB_MAX 6
  54. #define TROWA_SEQ_SWING_ADJ_MIN -0.5
  55. #define TROWA_SEQ_SWING_ADJ_MAX 0.5
  56. #define TROWA_SEQ_SWING_STEPS 4
  57. // 0 WILL BE NO SWING
  58. // To copy all gates/triggers in the selected target Pattern
  59. #define TROWA_SEQ_COPY_CHANNELIX_ALL TROWA_INDEX_UNDEFINED
  60. #define TROWA_SEQ_NUM_RANDOM_PATTERNS 27
  61. #define TROWA_SEQ_BOOLEAN_NUM_RANDOM_PATTERNS 7 // Num of patterns that actually apply to a boolean sequencer. List should always be sorted by #Unique Values. Only #Unique vals 1-2 should be chosen.
  62. // Random Structure
  63. // From feature request: https ://github.com/j4s0n-c/trowaSoft-VCV/issues/10
  64. struct RandStructure {
  65. uint8_t numDiffVals;
  66. std::vector<uint8_t> pattern;
  67. };
  68. //===============================================================================
  69. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  70. // TSSequencerModuleBase
  71. // Sequencer Base Class
  72. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  73. //===============================================================================
  74. struct TSSequencerModuleBase : Module {
  75. enum ParamIds {
  76. // BPM Knob
  77. BPM_PARAM,
  78. // Run toggle
  79. RUN_PARAM,
  80. // Reset Trigger (Momentary)
  81. RESET_PARAM,
  82. // Step length
  83. STEPS_PARAM,
  84. SELECTED_PATTERN_PLAY_PARAM, // What pattern we are playing
  85. SELECTED_PATTERN_EDIT_PARAM, // What pattern we are editing
  86. SELECTED_CHANNEL_PARAM, // Which gate is selected for editing
  87. SELECTED_OUTPUT_VALUE_MODE_PARAM, // Which value mode we are doing
  88. SWING_ADJ_PARAM, // Amount of swing adjustment (-0.1 to 0.1)
  89. COPY_PATTERN_PARAM, // Copy the current editing Pattern
  90. COPY_CHANNEL_PARAM, // Copy the current Channel/gate/trigger in the current Pattern only.
  91. PASTE_PARAM, // Paste what is on our clip board to the now current editing.
  92. SELECTED_BPM_MULT_IX_PARAM, // Selected index into our BPM calculation multipliers (for 1/4, 1/8, 1/8T, 1/16 note calcs)
  93. OSC_SAVE_CONF_PARAM, // ENABLE and Save the configuration for OSC
  94. OSC_AUTO_RECONNECT_PARAM, // Auto-reconnect OSC on load from save file.
  95. OSC_SHOW_CONF_PARAM, // Configure OSC toggle
  96. CHANNEL_PARAM, // Edit Channel/Step Buttons/Knobs
  97. NUM_PARAMS = CHANNEL_PARAM // Add the number of steps separately...
  98. };
  99. enum InputIds {
  100. // BPM Input
  101. BPM_INPUT,
  102. EXT_CLOCK_INPUT,
  103. RESET_INPUT,
  104. STEPS_INPUT,
  105. SELECTED_PATTERN_PLAY_INPUT, // What pattern we are playing
  106. SELECTED_PATTERN_EDIT_INPUT, // What pattern we are editing
  107. UNUSED_INPUT,
  108. NUM_INPUTS
  109. };
  110. // Each of the 16 voices need a gate output
  111. enum OutputIds {
  112. CHANNELS_OUTPUT, // Output Channel ports
  113. NUM_OUTPUTS = CHANNELS_OUTPUT + TROWA_SEQ_NUM_CHNLS
  114. };
  115. enum LightIds {
  116. RUNNING_LIGHT,
  117. RESET_LIGHT,
  118. COPY_PATTERN_LIGHT, // Copy pattern
  119. COPY_CHANNEL_LIGHT, // Copy channel
  120. PASTE_LIGHT, // Paste light
  121. SELECTED_BPM_MULT_IX_LIGHT, // BPM multiplier/note index
  122. OSC_CONFIGURE_LIGHT, // The light for configuring OSC.
  123. OSC_ENABLED_LIGHT, // Light for OSC enabled and currently running/active.
  124. CHANNEL_LIGHTS, // Channel output lights.
  125. PAD_LIGHTS = CHANNEL_LIGHTS + TROWA_SEQ_NUM_CHNLS, // Lights for the steps/pads for the currently editing Channel
  126. // Not the number of lights yet, add the # of steps (maxSteps)
  127. NUM_LIGHTS = PAD_LIGHTS // Add the number of steps separately...
  128. };
  129. // The random structures/patterns
  130. static RandStructure RandomPatterns[TROWA_SEQ_NUM_RANDOM_PATTERNS];
  131. // If the module has been fully initialized or not.
  132. bool initialized = false;
  133. // If reset is pressed while paused, when we play, we should fire step 0.
  134. bool resetPaused = false;
  135. // [03/30/2015] A reset has been queued for the next step. (https://github.com/j4s0n-c/trowaSoft-VCV/issues/11)
  136. // So now reset is not immediate, but will wait for the next step.
  137. bool resetQueued = false;
  138. // If this module is running.
  139. bool running = true;
  140. SchmittTrigger clockTrigger; // for external clock
  141. SchmittTrigger runningTrigger; // Detect running btn press
  142. SchmittTrigger resetTrigger; // Detect reset btn press
  143. float realPhase = 0.0;
  144. // Index into the sequence (step)
  145. int index = 0;
  146. // Last index we played (for OSC)
  147. int prevIndex = TROWA_INDEX_UNDEFINED;
  148. // Next index in to jump to in the sequence (step) if any. (for external controls)
  149. int nextIndex = TROWA_INDEX_UNDEFINED;
  150. // Flag if values are being changed outside of step().
  151. bool valuesChanging = false;
  152. enum GateMode : short {
  153. TRIGGER = 0,
  154. RETRIGGER = 1,
  155. CONTINUOUS = 2,
  156. };
  157. GateMode gateMode = TRIGGER;
  158. PulseGenerator gatePulse;
  159. enum ValueMode : short {
  160. VALUE_TRIGGER = 0,
  161. VALUE_RETRIGGER = 1,
  162. VALUE_CONTINUOUS = 2,
  163. VALUE_VOLT = 0,
  164. VALUE_MIDINOTE = 1,
  165. VALUE_PATTERN = 2,
  166. MIN_VALUE_MODE = 0,
  167. MAX_VALUE_MODE = 2,
  168. NUM_VALUE_MODES = MAX_VALUE_MODE + 1
  169. };
  170. // Selected output value mode.
  171. ValueMode selectedOutputValueMode = VALUE_TRIGGER;
  172. ValueMode lastOutputValueMode = VALUE_TRIGGER;
  173. // Maximum number of steps for this sequencer.
  174. int maxSteps = 16;
  175. // The number of rows for steps (for layout).
  176. int numRows = 4;
  177. // The number of columns for steps (for layout).
  178. int numCols = 4;
  179. // Step data for each pattern and channel.
  180. float * triggerState[TROWA_SEQ_NUM_PATTERNS][TROWA_SEQ_NUM_CHNLS];
  181. SchmittTrigger* gateTriggers;
  182. // Knob indices for top control knobs.
  183. enum KnobIx {
  184. // Playing pattern knob ix
  185. PlayPatternKnob = 0,
  186. // Playing BPM knob ix
  187. BPMKnob,
  188. // Playing Step Length ix
  189. StepLengthKnob,
  190. // Output mode knob ix
  191. OutputModeKnob,
  192. // Edit pattern knob ix
  193. EditPatternKnob,
  194. // Edic channel knob ix
  195. EditChannelKnob,
  196. // Number of Control Knobs
  197. NumKnobs
  198. };
  199. // References to input knobs (top row of knobs)
  200. SVGKnob* controlKnobs[NumKnobs];
  201. // Another flag to reload the matrix.
  202. bool reloadEditMatrix = false;
  203. // Keep track of the pattern that was playing last step (for OSC)
  204. int lastPatternPlayingIx = -1;
  205. // Index of which pattern we are playing
  206. int currentPatternEditingIx = 0;
  207. // Index of which pattern we are editing
  208. int currentPatternPlayingIx = 0;
  209. // Index of which channel (trigger/gate/voice) is currently displayed/edited.
  210. int currentChannelEditingIx = 0;
  211. /// TODO: Perhaps change this to setting for each pattern or each pattern-channel.
  212. // The current number of steps to play
  213. int currentNumberSteps = TROWA_SEQ_NUM_STEPS;
  214. // Calculated current BPM
  215. float currentBPM = 0.0f;
  216. // If the last step was the external clock
  217. bool lastStepWasExternalClock = false;
  218. // Currently stored pattern (for external control like OSC clients that can not store values themselves, the controls can set a 'stored' value
  219. // and then have some button click fire off the SetPlayPattern message with -1 as argument and we'll use this.
  220. int storedPatternPlayingIx = 0;
  221. // Currently stored length (for external control like OSC clients that can not store values themselves, the controls can set a 'stored' value
  222. // and then have some button click fire off the SetPlayLength message with -1 as argument and we'll use this.
  223. int storedNumberSteps = TROWA_SEQ_NUM_STEPS;
  224. // Currently stored BPM (for external control like OSC clients that can not store values themselves, the controls can set a 'stored' value
  225. // and then have some button click fire off the SetPlayBPM message with -1 as argument and we'll use this.
  226. int storedBPM = 120;
  227. //// Last time of the external step
  228. //std::chrono::high_resolution_clock::time_point lastExternalStepTime;
  229. // Pad/Knob lights - Step On
  230. float** stepLights; /// TODO: Just make linear
  231. float** gateLights; /// TODO: Just make linear
  232. // Default values for our pads/knobs:
  233. float defaultStateValue = 0.0;
  234. // References to our pad lights
  235. ColorValueLight*** padLightPtrs; /// TODO: Just make linear
  236. // Output lights (for triggers/gate jacks)
  237. float gateLightsOut[TROWA_SEQ_NUM_CHNLS];
  238. // Colors for each channel
  239. NVGcolor voiceColors[TROWA_SEQ_NUM_CHNLS] = {
  240. COLOR_TS_RED, COLOR_DARK_ORANGE, COLOR_YELLOW, COLOR_TS_GREEN,
  241. COLOR_CYAN, COLOR_TS_BLUE, COLOR_PURPLE, COLOR_PINK,
  242. COLOR_TS_RED, COLOR_DARK_ORANGE, COLOR_YELLOW, COLOR_TS_GREEN,
  243. COLOR_CYAN, COLOR_TS_BLUE, COLOR_PURPLE, COLOR_PINK
  244. };
  245. // Swing ////////////////////////////////
  246. float swingAdjustment = 0.0; // Amount of swing adjustment (i.e. -0.1 to 0.1)
  247. const int swingResetSteps = TROWA_SEQ_SWING_STEPS; // These many steps need to be adjusted.
  248. float swingAdjustedPhase = 0.0;
  249. int swingRealSteps = 0;
  250. // Copy & Paste /////////////////////////
  251. // Source pattern to copy
  252. int copySourcePatternIx = -1;
  253. // Source channel to copy (or TROWA_SEQ_COPY_CHANNELIX_ALL for all).
  254. int copySourceChannelIx = TROWA_SEQ_COPY_CHANNELIX_ALL;
  255. // Copy buffer
  256. float* copyBuffer[TROWA_SEQ_NUM_CHNLS];
  257. SchmittTrigger copyPatternTrigger;
  258. SchmittTrigger copyGateTrigger;
  259. SchmittTrigger pasteTrigger;
  260. /// TODO: Maybe eventually separate UI controls from module (UI changes in Widget not Module).
  261. // Light for paste button
  262. TS_LightString* pasteLight;
  263. // Light for copy pattern button
  264. ColorValueLight* copyPatternLight;
  265. // Light for copy channel button
  266. ColorValueLight* copyGateLight;
  267. // BPM Calculation //////////////
  268. // Index into the array BPMOptions
  269. int selectedBPMNoteIx = 1; // 1/8th
  270. SchmittTrigger selectedBPMNoteTrigger;
  271. // External Messages ///////////////////////////////////////////////
  272. // Message queue for external (to Rack) control messages
  273. std::queue<TSExternalControlMessage> ctlMsgQueue;
  274. enum ExternalControllerMode {
  275. // Edit Mode : Send to control what we are editing.
  276. EditMode = 0,
  277. // Performance/Play mode : Send to controller what we are playing
  278. // SetStepValue messages should be interupted as SetPlayingStep (Jump)
  279. PerformanceMode = 1
  280. };
  281. // The current control mode (i.e. Edit Mode or Play / Performance Mode)
  282. ExternalControllerMode currentCtlMode = ExternalControllerMode::EditMode;
  283. // OSC Messaging ////////////////
  284. // If we allow osc or not.
  285. bool allowOSC = true;
  286. // Flag if we should use OSC or not.
  287. bool useOSC = true;
  288. // An OSC id.
  289. int oscId = 0;
  290. // Mutex for osc messaging.
  291. std::mutex oscMutex;
  292. // Current OSC IP address and port settings.
  293. TSOSCConnectionInfo currentOSCSettings = { OSC_ADDRESS_DEF, OSC_OUTPORT_DEF , OSC_INPORT_DEF };
  294. // OSC Configure trigger
  295. SchmittTrigger oscConfigTrigger;
  296. SchmittTrigger oscConnectTrigger;
  297. SchmittTrigger oscDisconnectTrigger;
  298. // Show the OSC configuration screen or not.
  299. bool oscShowConfigurationScreen = false;
  300. // Flag to reconnect at load. IFF true and oscInitialized is also true.
  301. bool oscReconnectAtLoad = false;
  302. // Flag if OSC objects have been initialized
  303. bool oscInitialized = false;
  304. // If there is an osc error.
  305. bool oscError = false;
  306. // OSC output buffer.
  307. char* oscBuffer = NULL;
  308. // OSC namespace to use
  309. std::string oscNamespace = OSC_DEFAULT_NS;
  310. // Sending OSC socket
  311. UdpTransmitSocket* oscTxSocket = NULL;
  312. // OSC message listener
  313. TSOSCSequencerListener* oscListener = NULL;
  314. // Receiving OSC socket
  315. UdpListeningReceiveSocket* oscRxSocket = NULL;
  316. // The OSC listener thread
  317. std::thread oscListenerThread;
  318. // Osc address buffer.
  319. char oscAddrBuffer[SeqOSCOutputMsg::NUM_OSC_OUTPUT_MSGS][OSC_ADDRESS_BUFFER_SIZE];
  320. // Prev step that was last turned off (when going to a new step).
  321. int oscLastPrevStepUpdated = TROWA_INDEX_UNDEFINED;
  322. // Settings for new OSC.
  323. TSOSCInfo oscNewSettings = { OSC_ADDRESS_DEF, OSC_OUTPORT_DEF , OSC_INPORT_DEF };
  324. // OSC Mode action (i.e. Enable, Disable)
  325. enum OSCAction {
  326. None,
  327. Disable,
  328. Enable
  329. };
  330. // Flag for our module to either enable or disable osc.
  331. OSCAction oscCurrentAction = OSCAction::None;
  332. // The current osc client. Clients such as touchOSC and Lemur are limited and need special treatment.
  333. OSCClient oscCurrentClient = OSCClient::GenericClient;
  334. // Mode /////////////////////////
  335. // The mode string.
  336. const char* modeString;
  337. // Mode strings
  338. const char* modeStrings[3];
  339. // If it is the first load this session
  340. bool firstLoad = true;
  341. // If this was loaded from a save, what version
  342. int saveVersion = -1;
  343. const float lightLambda = 0.05;
  344. // The number of structured random patterns to actually use. Should be <= TROWA_SEQ_NUM_RANDOM_PATTERNS.
  345. int numStructuredRandomPatterns = TROWA_SEQ_BOOLEAN_NUM_RANDOM_PATTERNS;
  346. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  347. // TSSequencerModuleBase()
  348. // Instantiate the abstract base class.
  349. // @numSteps: (IN) Maximum number of steps
  350. // @numRows: (IN) The number of rows (for layout).
  351. // @numCols: (IN) The number of columns (for layout).
  352. // @numRows * @numCols = @numSteps
  353. // @defStateVal : (IN) The default state value (i.e. 0/false for a boolean step sequencer or whatever float value you want).
  354. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  355. TSSequencerModuleBase(/*in*/ int numSteps, /*in*/ int numRows, /*in*/ int numCols, /*in*/ float defStateVal);
  356. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  357. // Delete our goodies.
  358. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  359. ~TSSequencerModuleBase();
  360. // Get the inputs for this step.
  361. void getStepInputs(bool* pulse, bool* reloadMatrix, bool* valueModeChanged);
  362. // Paste the clipboard pattern and/or specific gate to current selected pattern and/or gate.
  363. bool paste();
  364. // Copy the contents:
  365. void copy(int patternIx, int channelIx);
  366. // Set a single step value
  367. virtual void setStepValue(int step, float val, int channel, int pattern);
  368. // Get the toggle step value
  369. virtual float getToggleStepValue(int step, float val, int channel, int pattern) = 0;
  370. // Calculate a representation of all channels for this step
  371. virtual float getPlayingStepValue(int step, int pattern) = 0;
  372. // Initialize OSC on the given ip and ports.
  373. void initOSC(const char* ipAddress, int outputPort, int inputPort);
  374. // Clean up OSC.
  375. void cleanupOSC();
  376. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  377. // Set the OSC namespace.
  378. // @oscNs: (IN) The namespace for OSC.
  379. // Sets the command address strings too.
  380. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  381. void setOSCNamespace(const char* oscNs);
  382. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  383. // reset(void)
  384. // Reset ALL step values to default.
  385. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  386. void reset() override;
  387. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  388. // randomize(void)
  389. // Only randomize the current gate/trigger steps.
  390. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  391. void randomize() override
  392. {
  393. randomize(currentPatternEditingIx, currentChannelEditingIx, false);
  394. return;
  395. }
  396. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  397. // randomize()
  398. // @patternIx : (IN) The index into our pattern matrix (0-63). Or TROWA_INDEX_UNDEFINED for all patterns.
  399. // @channelIx : (IN) The index of the channel (gate/trigger/voice) if any (0-15, or TROWA_SEQ_COPY_CHANNELIX_ALL/TROWA_INDEX_UNDEFINED for all).
  400. // @useStructured: (IN) Create a random sequence/pattern of random values.
  401. // Random all from : https://github.com/j4s0n-c/trowaSoft-VCV/issues/8
  402. // Structured from : https://github.com/j4s0n-c/trowaSoft-VCV/issues/10
  403. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  404. virtual void randomize(int patternIx, int channelIx, bool useStructured);
  405. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  406. // getRandomValue()
  407. // Get a random value for a step in this sequencer.
  408. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  409. virtual float getRandomValue() {
  410. // Default are boolean sequencers
  411. return randomUniform() > 0.5;
  412. }
  413. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  414. // onShownStepChange()
  415. // If we changed a step that is shown on the matrix, then do something.
  416. // For voltSeq to adjust the knobs so we dont' read the old knob values again.
  417. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  418. virtual void onShownStepChange(int step, float val) {
  419. // DO nothing
  420. return;
  421. }
  422. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  423. // clearClipboard(void)
  424. // Shallow clear of clipboard and reset the Copy/Paste lights
  425. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  426. void clearClipboard()
  427. {
  428. copySourcePatternIx = -1;
  429. copySourceChannelIx = TROWA_SEQ_COPY_CHANNELIX_ALL; // Which trigger we are copying, -1 for all
  430. lights[COPY_CHANNEL_LIGHT].value = 0;
  431. pasteLight->setColor(COLOR_WHITE); // Return the paste light to white
  432. lights[COPY_PATTERN_LIGHT].value = 0;
  433. lights[PASTE_LIGHT].value = 0;
  434. return;
  435. }
  436. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  437. // toJson(void)
  438. // Save our junk to json.
  439. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  440. json_t *toJson() override;
  441. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  442. // fromJson(void)
  443. // Read in our junk from json.
  444. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  445. virtual void fromJson(json_t *rootJ) override;
  446. }; // end struct TSSequencerModuleBase
  447. //===============================================================================
  448. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  449. // TSSeqDisplay
  450. // A top digital display for trowaSoft sequencers.
  451. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  452. //===============================================================================
  453. struct TSSeqDisplay : TransparentWidget {
  454. TSSequencerModuleBase *module;
  455. std::shared_ptr<Font> font;
  456. std::shared_ptr<Font> labelFont;
  457. int fontSize;
  458. char messageStr[TROWA_DISP_MSG_SIZE]; // tmp buffer for our strings.
  459. bool showDisplay = true;
  460. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  461. // TSSeqDisplay(void)
  462. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  463. TSSeqDisplay() {
  464. font = Font::load(assetPlugin(plugin, TROWA_DIGITAL_FONT));
  465. labelFont = Font::load(assetPlugin(plugin, TROWA_LABEL_FONT));
  466. fontSize = 12;
  467. for (int i = 0; i < TROWA_DISP_MSG_SIZE; i++)
  468. messageStr[i] = '\0';
  469. showDisplay = true;
  470. return;
  471. }
  472. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  473. // draw()
  474. // @vg : (IN) NVGcontext to draw on
  475. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  476. void draw(/*in*/ NVGcontext *vg) override {
  477. bool isPreview = module == NULL; // May get a NULL module for preview
  478. // Background Colors:
  479. NVGcolor backgroundColor = nvgRGB(0x20, 0x20, 0x20);
  480. NVGcolor borderColor = nvgRGB(0x10, 0x10, 0x10);
  481. // Screen:
  482. nvgBeginPath(vg);
  483. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 5.0);
  484. nvgFillColor(vg, backgroundColor);
  485. nvgFill(vg);
  486. nvgStrokeWidth(vg, 1.0);
  487. nvgStrokeColor(vg, borderColor);
  488. nvgStroke(vg);
  489. if (!showDisplay)
  490. return;
  491. int currPlayPattern = 1;
  492. int currEditPattern = 1;
  493. int currentGate = 1;
  494. int currentNSteps = 16;
  495. float currentBPM = 120;
  496. NVGcolor currColor = COLOR_RED;
  497. if (!isPreview)
  498. {
  499. currColor = module->voiceColors[module->currentChannelEditingIx];
  500. currPlayPattern = module->currentPatternPlayingIx + 1;
  501. currEditPattern = module->currentPatternEditingIx + 1;
  502. currentGate = module->currentChannelEditingIx + 1;
  503. currentNSteps = module->currentNumberSteps;
  504. currentBPM = module->currentBPM;
  505. }
  506. // Default Font:
  507. nvgFontSize(vg, fontSize);
  508. nvgFontFaceId(vg, font->handle);
  509. nvgTextLetterSpacing(vg, 2.5);
  510. NVGcolor textColor = nvgRGB(0xee, 0xee, 0xee);
  511. int y1 = 42;
  512. int y2 = 27;
  513. int dx = 0;
  514. int x = 0;
  515. int spacing = 61;
  516. nvgTextAlign(vg, NVG_ALIGN_CENTER);
  517. // Current Playing Pattern
  518. nvgFillColor(vg, textColor);
  519. x = 5 + 21;
  520. nvgFontSize(vg, fontSize); // Small font
  521. nvgFontFaceId(vg, labelFont->handle);
  522. nvgText(vg, x, y1, "PATT", NULL);
  523. sprintf(messageStr, "%02d", currPlayPattern);
  524. nvgFontSize(vg, fontSize * 1.5); // Large font
  525. nvgFontFaceId(vg, font->handle);
  526. nvgText(vg, x + dx, y2, messageStr, NULL);
  527. // Current Playing Speed
  528. nvgFillColor(vg, textColor);
  529. x += spacing;
  530. nvgFontSize(vg, fontSize); // Small font
  531. nvgFontFaceId(vg, labelFont->handle);
  532. if (isPreview)
  533. sprintf(messageStr, "BPM/%s", BPMOptions[1]->label);
  534. else
  535. sprintf(messageStr, "BPM/%s", BPMOptions[module->selectedBPMNoteIx]->label);
  536. nvgText(vg, x, y1, messageStr, NULL);
  537. if (module->lastStepWasExternalClock)
  538. {
  539. sprintf(messageStr, "%s", "CLK");
  540. }
  541. else
  542. {
  543. sprintf(messageStr, "%03.0f", currentBPM);
  544. }
  545. nvgFontFaceId(vg, font->handle);
  546. nvgFontSize(vg, fontSize * 1.5); // Large font
  547. nvgText(vg, x + dx, y2, messageStr, NULL);
  548. // Current Playing # Steps
  549. nvgFillColor(vg, textColor);
  550. x += spacing;
  551. nvgFontSize(vg, fontSize); // Small font
  552. nvgFontFaceId(vg, labelFont->handle);
  553. nvgText(vg, x, y1, "LENG", NULL);
  554. sprintf(messageStr, "%02d", currentNSteps);
  555. nvgFontSize(vg, fontSize * 1.5); // Large font
  556. nvgFontFaceId(vg, font->handle);
  557. nvgText(vg, x + dx, y2, messageStr, NULL);
  558. // Current Mode:
  559. nvgFillColor(vg, nvgRGB(0xda, 0xda, 0xda));
  560. x += spacing + 5;
  561. nvgFontSize(vg, fontSize); // Small font
  562. nvgFontFaceId(vg, labelFont->handle);
  563. nvgText(vg, x, y1, "MODE", NULL);
  564. nvgFontSize(vg, fontSize); // Small font
  565. if (module->modeString != NULL)
  566. {
  567. nvgFontFaceId(vg, font->handle);
  568. //nvgText(vg, x + dx -6, y2, module->modeString, NULL);
  569. nvgText(vg, x + dx, y2, module->modeString, NULL);
  570. }
  571. nvgTextAlign(vg, NVG_ALIGN_CENTER);
  572. // Current Edit Pattern
  573. nvgFillColor(vg, textColor);
  574. x += spacing;
  575. nvgFontSize(vg, fontSize); // Small font
  576. nvgFontFaceId(vg, labelFont->handle);
  577. nvgText(vg, x, y1, "PATT", NULL);
  578. sprintf(messageStr, "%02d", currEditPattern);
  579. nvgFontSize(vg, fontSize * 1.5); // Large font
  580. nvgFontFaceId(vg, font->handle);
  581. nvgText(vg, x + dx, y2, messageStr, NULL);
  582. // Current Edit Gate/Trigger
  583. nvgFillColor(vg, currColor); // Match the Gate/Trigger color
  584. x += spacing;
  585. nvgFontSize(vg, fontSize); // Small font
  586. nvgFontFaceId(vg, labelFont->handle);
  587. nvgText(vg, x, y1, "CHNL", NULL);
  588. sprintf(messageStr, "%02d", currentGate);
  589. nvgFontSize(vg, fontSize * 1.5); // Large font
  590. nvgFontFaceId(vg, font->handle);
  591. nvgText(vg, x + dx, y2, messageStr, NULL);
  592. // [[[[[[[[[[[[[[[[ EDIT Box Group ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
  593. nvgTextAlign(vg, NVG_ALIGN_LEFT);
  594. NVGcolor groupColor = nvgRGB(0xDD, 0xDD, 0xDD);
  595. nvgFillColor(vg, groupColor);
  596. int labelX = 297;
  597. x = labelX; // 289
  598. nvgFontSize(vg, fontSize - 5); // Small font
  599. nvgFontFaceId(vg, labelFont->handle);
  600. nvgText(vg, x, 8, "EDIT", NULL);
  601. // Edit Label Line ---------------------------------------------------------------
  602. nvgBeginPath(vg);
  603. // Start top to the left of the text "Edit"
  604. int y = 5;
  605. nvgMoveTo(vg, /*start x*/ x - 3, /*start y*/ y);// Starts new sub-path with specified point as first point.s
  606. x = 256;// x - 35;//xOffset + 3 * spacing - 3 + 60;
  607. nvgLineTo(vg, /*x*/ x, /*y*/ y); // Go to Left (Line Start)
  608. x = labelX + 22;
  609. y = 5;
  610. nvgMoveTo(vg, /*x*/ x, /*y*/ y); // Right of "Edit"
  611. x = box.size.x - 6;
  612. nvgLineTo(vg, /*x*/ x, /*y*/ y); // RHS of box
  613. nvgStrokeWidth(vg, 1.0);
  614. nvgStrokeColor(vg, groupColor);
  615. nvgStroke(vg);
  616. // [[[[[[[[[[[[[[[[ PLAYBACK Box Group ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
  617. groupColor = nvgRGB(0xEE, 0xEE, 0xEE);
  618. nvgFillColor(vg, groupColor);
  619. labelX = 64;
  620. x = labelX;
  621. nvgFontSize(vg, fontSize - 5); // Small font
  622. nvgText(vg, x, 8, "PLAYBACK", NULL);
  623. // Play Back Label Line ---------------------------------------------------------------
  624. nvgBeginPath(vg);
  625. // Start top to the left of the text "Play"
  626. y = 5;
  627. nvgMoveTo(vg, /*start x*/ x - 3, /*start y*/ y);// Starts new sub-path with specified point as first point.s
  628. x = 6;
  629. nvgLineTo(vg, /*x*/ x, /*y*/ y); // Go to the left
  630. x = labelX + 52;
  631. y = 5;
  632. nvgMoveTo(vg, /*x*/ x, /*y*/ y); // To the Right of "Playback"
  633. x = 165; //x + 62 ;
  634. nvgLineTo(vg, /*x*/ x, /*y*/ y); // Go Right
  635. nvgStrokeWidth(vg, 1.0);
  636. nvgStrokeColor(vg, groupColor);
  637. nvgStroke(vg);
  638. return;
  639. } // end draw()
  640. }; // end struct TSSeqDisplay
  641. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  642. // TSSeqLabelArea
  643. // Draw labels on our sequencer.
  644. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  645. struct TSSeqLabelArea : TransparentWidget {
  646. TSSequencerModuleBase *module;
  647. std::shared_ptr<Font> font;
  648. int fontSize;
  649. bool drawGridLines = false;
  650. char messageStr[TROWA_DISP_MSG_SIZE];
  651. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  652. // TSSeqLabelArea()
  653. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  654. TSSeqLabelArea() {
  655. font = Font::load(assetPlugin(plugin, TROWA_LABEL_FONT));
  656. fontSize = 13;
  657. for (int i = 0; i < TROWA_DISP_MSG_SIZE; i++)
  658. messageStr[i] = '\0';
  659. }
  660. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  661. // draw()
  662. // @vg : (IN) NVGcontext to draw on
  663. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  664. void draw(NVGcontext *vg) override {
  665. // Default Font:
  666. nvgFontSize(vg, fontSize);
  667. nvgFontFaceId(vg, font->handle);
  668. nvgTextLetterSpacing(vg, 1);
  669. NVGcolor textColor = nvgRGB(0xee, 0xee, 0xee);
  670. nvgFillColor(vg, textColor);
  671. nvgFontSize(vg, fontSize);
  672. /// MAKE LABELS HERE
  673. int x = 45;
  674. int y = 163;
  675. int dy = 28;
  676. // Selected Pattern Playback:
  677. nvgText(vg, x, y, "PAT", NULL);
  678. // Clock
  679. y += dy;
  680. nvgText(vg, x, y, "BPM ", NULL);
  681. // Steps
  682. y += dy;
  683. nvgText(vg, x, y, "LNG", NULL);
  684. // Ext Clock
  685. y += dy;
  686. nvgText(vg, x, y, "CLK", NULL);
  687. // Reset
  688. y += dy;
  689. nvgText(vg, x, y, "RST", NULL);
  690. // Outputs
  691. nvgFontSize(vg, fontSize * 0.95);
  692. x = 320;
  693. y = 350;
  694. nvgText(vg, x, y, "OUTPUTS", NULL);
  695. // TINY btn labels
  696. nvgFontSize(vg, fontSize * 0.6);
  697. // OSC Labels
  698. y = 103;
  699. if (module->allowOSC)
  700. {
  701. x = 240;
  702. nvgText(vg, x, y, "OSC", NULL);
  703. }
  704. // Copy button labels:
  705. x = 302;
  706. nvgText(vg, x, y, "CPY", NULL);
  707. x = 362;
  708. nvgText(vg, x, y, "CPY", NULL);
  709. // BPM divisor/note label:
  710. x = 118;
  711. nvgText(vg, x, y, "DIV", NULL);
  712. if (drawGridLines)
  713. {
  714. NVGcolor gridColor = nvgRGB(0x44, 0x44, 0x44);
  715. nvgBeginPath(vg);
  716. x = 80;
  717. y = 228;
  718. nvgMoveTo(vg, /*start x*/ x, /*start y*/ y);// Starts new sub-path with specified point as first point
  719. x += 225;
  720. nvgLineTo(vg, /*x*/ x, /*y*/ y); // Go to the left
  721. nvgStrokeWidth(vg, 1.0);
  722. nvgStrokeColor(vg, gridColor);
  723. nvgStroke(vg);
  724. // Vertical
  725. nvgBeginPath(vg);
  726. x = 192;
  727. y = 116;
  728. nvgMoveTo(vg, /*start x*/ x, /*start y*/ y);// Starts new sub-path with specified point as first point
  729. y += 225;
  730. nvgLineTo(vg, /*x*/ x, /*y*/ y); // Go to the left
  731. nvgStrokeWidth(vg, 1.0);
  732. nvgStrokeColor(vg, gridColor);
  733. nvgStroke(vg);
  734. }
  735. return;
  736. } // end draw()
  737. }; // end struct TSSeqLabelArea
  738. #endif