The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

380 lines
12KB

  1. #ifndef MAINCOMPONENT_H_INCLUDED
  2. #define MAINCOMPONENT_H_INCLUDED
  3. #include "../JuceLibraryCode/JuceHeader.h"
  4. /**
  5. A struct that handles the setup and layout of the DrumPadGridProgram
  6. */
  7. struct ColourGrid
  8. {
  9. ColourGrid (int cols, int rows)
  10. : numColumns (cols),
  11. numRows (rows)
  12. {
  13. constructGridFillArray();
  14. }
  15. /** Creates a GridFill object for each pad in the grid and sets its colour
  16. and fill before adding it to an array of GridFill objects
  17. */
  18. void constructGridFillArray()
  19. {
  20. gridFillArray.clear();
  21. int counter = 0;
  22. for (int i = 0; i < numColumns; ++i)
  23. {
  24. for (int j = 0; j < numRows; ++j)
  25. {
  26. DrumPadGridProgram::GridFill fill;
  27. Colour colourToUse = colourArray.getUnchecked (counter);
  28. fill.colour = colourToUse.withBrightness (colourToUse == currentColour ? 1.0 : 0.1);
  29. if (colourToUse == Colours::black)
  30. fill.fillType = DrumPadGridProgram::GridFill::FillType::hollow;
  31. else
  32. fill.fillType = DrumPadGridProgram::GridFill::FillType::filled;
  33. gridFillArray.add (fill);
  34. if (++counter == colourArray.size())
  35. counter = 0;
  36. }
  37. }
  38. }
  39. /** Sets which colour should be active for a given touch co-ordinate. Returns
  40. true if the colour has changed
  41. */
  42. bool setActiveColourForTouch (int x, int y)
  43. {
  44. bool colourHasChanged = false;
  45. int xindex = x / 5;
  46. int yindex = y / 5;
  47. Colour newColour = colourArray.getUnchecked ((yindex * 3) + xindex);
  48. if (currentColour != newColour)
  49. {
  50. currentColour = newColour;
  51. constructGridFillArray();
  52. colourHasChanged = true;
  53. }
  54. return colourHasChanged;
  55. }
  56. //==============================================================================
  57. int numColumns, numRows;
  58. float width, height;
  59. Array<DrumPadGridProgram::GridFill> gridFillArray;
  60. Array<Colour> colourArray = { Colours::white, Colours::red, Colours::green, Colours::blue, Colours::hotpink,
  61. Colours::orange, Colours::magenta, Colours::cyan, Colours::black };
  62. Colour currentColour = Colours::hotpink;
  63. //==============================================================================
  64. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourGrid)
  65. };
  66. /**
  67. The main component
  68. */
  69. class MainComponent : public Component,
  70. public TopologySource::Listener,
  71. private TouchSurface::Listener,
  72. private ControlButton::Listener,
  73. private Timer
  74. {
  75. public:
  76. MainComponent() : layout (3, 3)
  77. {
  78. setSize (600, 400);
  79. activeLeds.clear();
  80. // Register MainContentComponent as a listener to the PhysicalTopologySource object
  81. topologySource.addListener (this);
  82. }
  83. ~MainComponent()
  84. {
  85. if (activeBlock != nullptr)
  86. detachActiveBlock();
  87. }
  88. void paint (Graphics& g) override
  89. {
  90. g.fillAll (Colours::lightgrey);
  91. }
  92. void resized() override {}
  93. /** Overridden from TopologySource::Listener. Called when the topology changes */
  94. void topologyChanged() override
  95. {
  96. // Reset the activeBlock object
  97. if (activeBlock != nullptr)
  98. detachActiveBlock();
  99. // Get the array of currently connected Block objects from the PhysicalTopologySource
  100. Block::Array blocks = topologySource.getCurrentTopology().blocks;
  101. // Iterate over the array of Block objects
  102. for (auto b : blocks)
  103. {
  104. // Find the first Lightpad
  105. if (b->getType() == Block::Type::lightPadBlock)
  106. {
  107. activeBlock = b;
  108. // Register MainContentComponent as a listener to the touch surface
  109. if (auto surface = activeBlock->getTouchSurface())
  110. surface->addListener (this);
  111. // Register MainContentComponent as a listener to any buttons
  112. for (auto button : activeBlock->getButtons())
  113. button->addListener (this);
  114. // Get the LEDGrid object from the Lightpad and set its program to the program for the current mode
  115. if (auto grid = activeBlock->getLEDGrid())
  116. {
  117. // Work out scale factors to translate X and Y touches to LED indexes
  118. scaleX = (float) (grid->getNumColumns() - 1) / activeBlock->getWidth();
  119. scaleY = (float) (grid->getNumRows() - 1) / activeBlock->getHeight();
  120. setLEDProgram (grid);
  121. }
  122. break;
  123. }
  124. }
  125. }
  126. private:
  127. /** Overridden from TouchSurface::Listener. Called when a Touch is received on the Lightpad */
  128. void touchChanged (TouchSurface&, const TouchSurface::Touch& touch) override
  129. {
  130. // Translate X and Y touch events to LED indexes
  131. int xLed = roundToInt (touch.x * scaleX);
  132. int yLed = roundToInt (touch.y * scaleY);
  133. if (currentMode == colourPalette)
  134. {
  135. if (layout.setActiveColourForTouch (xLed, yLed))
  136. colourPaletteProgram->setGridFills (layout.numColumns, layout.numRows, layout.gridFillArray);
  137. }
  138. else if (currentMode == canvas)
  139. {
  140. drawLEDs ((uint32) xLed, (uint32) yLed, touch.z, layout.currentColour);
  141. }
  142. }
  143. /** Overridden from ControlButton::Listener. Called when a button on the Lightpad is pressed */
  144. void buttonPressed (ControlButton&, Block::Timestamp) override {};
  145. /** Overridden from ControlButton::Listener. Called when a button on the Lightpad is released */
  146. void buttonReleased (ControlButton&, Block::Timestamp) override
  147. {
  148. if (currentMode == canvas)
  149. {
  150. // Wait 500ms to see if there is a second press
  151. if (! isTimerRunning())
  152. startTimer (500);
  153. else
  154. doublePress = true;
  155. }
  156. else if (currentMode == colourPalette)
  157. {
  158. // Switch to canvas mode and set the LEDGrid program
  159. currentMode = canvas;
  160. setLEDProgram (activeBlock->getLEDGrid());
  161. }
  162. }
  163. void timerCallback() override
  164. {
  165. if (doublePress)
  166. {
  167. // Clear the LED grid
  168. for (uint32 x = 0; x < 15; ++x)
  169. for (uint32 y = 0; y < 15; ++ y)
  170. canvasProgram->setLED (x, y, Colours::black);
  171. // Clear the ActiveLED array
  172. activeLeds.clear();
  173. // Reset the doublePress flag
  174. doublePress = false;
  175. }
  176. else
  177. {
  178. // Switch to colour palette mode and set the LEDGrid program
  179. currentMode = colourPalette;
  180. setLEDProgram (activeBlock->getLEDGrid());
  181. }
  182. stopTimer();
  183. }
  184. /** Removes TouchSurface and ControlButton listeners and sets activeBlock to nullptr */
  185. void detachActiveBlock()
  186. {
  187. if (auto surface = activeBlock->getTouchSurface())
  188. surface->removeListener (this);
  189. for (auto button : activeBlock->getButtons())
  190. button->removeListener (this);
  191. activeBlock = nullptr;
  192. }
  193. /** Sets the LEDGrid Program for the selected mode */
  194. void setLEDProgram (LEDGrid* grid)
  195. {
  196. if (currentMode == canvas)
  197. {
  198. // Create a new BitmapLEDProgram for the LEDGrid
  199. canvasProgram = new BitmapLEDProgram (*grid);
  200. // Set the LEDGrid program
  201. grid->setProgram (canvasProgram);
  202. // Redraw any previously drawn LEDs
  203. redrawLEDs();
  204. }
  205. else if (currentMode == colourPalette)
  206. {
  207. // Create a new DrumPadGridProgram for the LEDGrid
  208. colourPaletteProgram = new DrumPadGridProgram (*grid);
  209. // Set the LEDGrid program
  210. grid->setProgram (colourPaletteProgram);
  211. // Setup the grid layout
  212. colourPaletteProgram->setGridFills (layout.numColumns, layout.numRows, layout.gridFillArray);
  213. }
  214. }
  215. /** Sets an LED on the Lightpad for a given touch co-ordinate and pressure */
  216. void drawLEDs (uint32 x0, uint32 y0, float z, Colour drawColour)
  217. {
  218. // Check if the activeLeds array already contains an ActiveLED object for this LED
  219. int index = -1;
  220. for (int i = 0; i < activeLeds.size(); ++i)
  221. {
  222. if (activeLeds.getReference(i).occupies (x0, y0))
  223. {
  224. index = i;
  225. break;
  226. }
  227. }
  228. // If the colour is black then just set the LED to black and return
  229. if (drawColour == Colours::black)
  230. {
  231. if (index != -1)
  232. {
  233. canvasProgram->setLED (x0, y0, Colours::black);
  234. activeLeds.remove (index);
  235. }
  236. return;
  237. }
  238. // If there is no ActiveLED obejct for this LED then create one,
  239. // add it to the array, set the LED on the Block and return
  240. if (index == -1)
  241. {
  242. ActiveLED led;
  243. led.x = x0;
  244. led.y = y0;
  245. led.colour = drawColour;
  246. led.brightness = z;
  247. activeLeds.add (led);
  248. canvasProgram->setLED (led.x, led.y, led.colour.withBrightness (led.brightness));
  249. return;
  250. }
  251. // Get the ActiveLED object for this LED
  252. ActiveLED currentLed = activeLeds.getReference (index);
  253. // If the LED colour is the same as the draw colour, add the brightnesses together.
  254. // If it is different, blend the colours
  255. if (currentLed.colour == drawColour)
  256. currentLed.brightness = jmin (currentLed.brightness + z, 1.0f);
  257. else
  258. currentLed.colour = currentLed.colour.interpolatedWith (drawColour, z);
  259. // Set the LED on the Block and change the ActiveLED object in the activeLeds array
  260. canvasProgram->setLED (currentLed.x, currentLed.y, currentLed.colour.withBrightness (currentLed.brightness));
  261. activeLeds.set (index, currentLed);
  262. }
  263. /** Redraws the LEDs on the Lightpad from the activeLeds array */
  264. void redrawLEDs()
  265. {
  266. // Iterate over the activeLeds array and set the LEDs on the Block
  267. for (auto led : activeLeds)
  268. canvasProgram->setLED (led.x, led.y, led.colour.withBrightness (led.brightness));
  269. }
  270. /**
  271. A struct that represents an active LED on the Lightpad.
  272. Has a position, colour and brightness.
  273. */
  274. struct ActiveLED
  275. {
  276. uint32 x;
  277. uint32 y;
  278. Colour colour;
  279. float brightness;
  280. /** Returns true if this LED occupies the given co-ordiantes */
  281. bool occupies (uint32 xPos, uint32 yPos) const
  282. {
  283. if (xPos == x && yPos == y)
  284. return true;
  285. return false;
  286. }
  287. };
  288. Array<ActiveLED> activeLeds;
  289. /**
  290. enum for the two modes
  291. */
  292. enum DisplayMode
  293. {
  294. colourPalette = 0,
  295. canvas
  296. };
  297. DisplayMode currentMode = colourPalette;
  298. //==============================================================================
  299. BitmapLEDProgram* canvasProgram;
  300. DrumPadGridProgram* colourPaletteProgram;
  301. ColourGrid layout;
  302. PhysicalTopologySource topologySource;
  303. Block::Ptr activeBlock;
  304. float scaleX = 0.0;
  305. float scaleY = 0.0;
  306. bool doublePress = false;
  307. //==============================================================================
  308. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
  309. };
  310. #endif // MAINCOMPONENT_H_INCLUDED