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.

381 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. g.drawText ("Connect a Lightpad Block to draw.", getLocalBounds(), Justification::centred, false);
  92. }
  93. void resized() override {}
  94. /** Overridden from TopologySource::Listener. Called when the topology changes */
  95. void topologyChanged() override
  96. {
  97. // Reset the activeBlock object
  98. if (activeBlock != nullptr)
  99. detachActiveBlock();
  100. // Get the array of currently connected Block objects from the PhysicalTopologySource
  101. Block::Array blocks = topologySource.getCurrentTopology().blocks;
  102. // Iterate over the array of Block objects
  103. for (auto b : blocks)
  104. {
  105. // Find the first Lightpad
  106. if (b->getType() == Block::Type::lightPadBlock)
  107. {
  108. activeBlock = b;
  109. // Register MainContentComponent as a listener to the touch surface
  110. if (auto surface = activeBlock->getTouchSurface())
  111. surface->addListener (this);
  112. // Register MainContentComponent as a listener to any buttons
  113. for (auto button : activeBlock->getButtons())
  114. button->addListener (this);
  115. // Get the LEDGrid object from the Lightpad and set its program to the program for the current mode
  116. if (auto grid = activeBlock->getLEDGrid())
  117. {
  118. // Work out scale factors to translate X and Y touches to LED indexes
  119. scaleX = (float) (grid->getNumColumns() - 1) / activeBlock->getWidth();
  120. scaleY = (float) (grid->getNumRows() - 1) / activeBlock->getHeight();
  121. setLEDProgram (grid);
  122. }
  123. break;
  124. }
  125. }
  126. }
  127. private:
  128. /** Overridden from TouchSurface::Listener. Called when a Touch is received on the Lightpad */
  129. void touchChanged (TouchSurface&, const TouchSurface::Touch& touch) override
  130. {
  131. // Translate X and Y touch events to LED indexes
  132. int xLed = roundToInt (touch.x * scaleX);
  133. int yLed = roundToInt (touch.y * scaleY);
  134. if (currentMode == colourPalette)
  135. {
  136. if (layout.setActiveColourForTouch (xLed, yLed))
  137. colourPaletteProgram->setGridFills (layout.numColumns, layout.numRows, layout.gridFillArray);
  138. }
  139. else if (currentMode == canvas)
  140. {
  141. drawLEDs ((uint32) xLed, (uint32) yLed, touch.z, layout.currentColour);
  142. }
  143. }
  144. /** Overridden from ControlButton::Listener. Called when a button on the Lightpad is pressed */
  145. void buttonPressed (ControlButton&, Block::Timestamp) override {};
  146. /** Overridden from ControlButton::Listener. Called when a button on the Lightpad is released */
  147. void buttonReleased (ControlButton&, Block::Timestamp) override
  148. {
  149. if (currentMode == canvas)
  150. {
  151. // Wait 500ms to see if there is a second press
  152. if (! isTimerRunning())
  153. startTimer (500);
  154. else
  155. doublePress = true;
  156. }
  157. else if (currentMode == colourPalette)
  158. {
  159. // Switch to canvas mode and set the LEDGrid program
  160. currentMode = canvas;
  161. setLEDProgram (activeBlock->getLEDGrid());
  162. }
  163. }
  164. void timerCallback() override
  165. {
  166. if (doublePress)
  167. {
  168. // Clear the LED grid
  169. for (uint32 x = 0; x < 15; ++x)
  170. for (uint32 y = 0; y < 15; ++ y)
  171. canvasProgram->setLED (x, y, Colours::black);
  172. // Clear the ActiveLED array
  173. activeLeds.clear();
  174. // Reset the doublePress flag
  175. doublePress = false;
  176. }
  177. else
  178. {
  179. // Switch to colour palette mode and set the LEDGrid program
  180. currentMode = colourPalette;
  181. setLEDProgram (activeBlock->getLEDGrid());
  182. }
  183. stopTimer();
  184. }
  185. /** Removes TouchSurface and ControlButton listeners and sets activeBlock to nullptr */
  186. void detachActiveBlock()
  187. {
  188. if (auto surface = activeBlock->getTouchSurface())
  189. surface->removeListener (this);
  190. for (auto button : activeBlock->getButtons())
  191. button->removeListener (this);
  192. activeBlock = nullptr;
  193. }
  194. /** Sets the LEDGrid Program for the selected mode */
  195. void setLEDProgram (LEDGrid* grid)
  196. {
  197. if (currentMode == canvas)
  198. {
  199. // Create a new BitmapLEDProgram for the LEDGrid
  200. canvasProgram = new BitmapLEDProgram (*grid);
  201. // Set the LEDGrid program
  202. grid->setProgram (canvasProgram);
  203. // Redraw any previously drawn LEDs
  204. redrawLEDs();
  205. }
  206. else if (currentMode == colourPalette)
  207. {
  208. // Create a new DrumPadGridProgram for the LEDGrid
  209. colourPaletteProgram = new DrumPadGridProgram (*grid);
  210. // Set the LEDGrid program
  211. grid->setProgram (colourPaletteProgram);
  212. // Setup the grid layout
  213. colourPaletteProgram->setGridFills (layout.numColumns, layout.numRows, layout.gridFillArray);
  214. }
  215. }
  216. /** Sets an LED on the Lightpad for a given touch co-ordinate and pressure */
  217. void drawLEDs (uint32 x0, uint32 y0, float z, Colour drawColour)
  218. {
  219. // Check if the activeLeds array already contains an ActiveLED object for this LED
  220. int index = -1;
  221. for (int i = 0; i < activeLeds.size(); ++i)
  222. {
  223. if (activeLeds.getReference(i).occupies (x0, y0))
  224. {
  225. index = i;
  226. break;
  227. }
  228. }
  229. // If the colour is black then just set the LED to black and return
  230. if (drawColour == Colours::black)
  231. {
  232. if (index != -1)
  233. {
  234. canvasProgram->setLED (x0, y0, Colours::black);
  235. activeLeds.remove (index);
  236. }
  237. return;
  238. }
  239. // If there is no ActiveLED obejct for this LED then create one,
  240. // add it to the array, set the LED on the Block and return
  241. if (index == -1)
  242. {
  243. ActiveLED led;
  244. led.x = x0;
  245. led.y = y0;
  246. led.colour = drawColour;
  247. led.brightness = z;
  248. activeLeds.add (led);
  249. canvasProgram->setLED (led.x, led.y, led.colour.withBrightness (led.brightness));
  250. return;
  251. }
  252. // Get the ActiveLED object for this LED
  253. ActiveLED currentLed = activeLeds.getReference (index);
  254. // If the LED colour is the same as the draw colour, add the brightnesses together.
  255. // If it is different, blend the colours
  256. if (currentLed.colour == drawColour)
  257. currentLed.brightness = jmin (currentLed.brightness + z, 1.0f);
  258. else
  259. currentLed.colour = currentLed.colour.interpolatedWith (drawColour, z);
  260. // Set the LED on the Block and change the ActiveLED object in the activeLeds array
  261. canvasProgram->setLED (currentLed.x, currentLed.y, currentLed.colour.withBrightness (currentLed.brightness));
  262. activeLeds.set (index, currentLed);
  263. }
  264. /** Redraws the LEDs on the Lightpad from the activeLeds array */
  265. void redrawLEDs()
  266. {
  267. // Iterate over the activeLeds array and set the LEDs on the Block
  268. for (auto led : activeLeds)
  269. canvasProgram->setLED (led.x, led.y, led.colour.withBrightness (led.brightness));
  270. }
  271. /**
  272. A struct that represents an active LED on the Lightpad.
  273. Has a position, colour and brightness.
  274. */
  275. struct ActiveLED
  276. {
  277. uint32 x;
  278. uint32 y;
  279. Colour colour;
  280. float brightness;
  281. /** Returns true if this LED occupies the given co-ordiantes */
  282. bool occupies (uint32 xPos, uint32 yPos) const
  283. {
  284. if (xPos == x && yPos == y)
  285. return true;
  286. return false;
  287. }
  288. };
  289. Array<ActiveLED> activeLeds;
  290. /**
  291. enum for the two modes
  292. */
  293. enum DisplayMode
  294. {
  295. colourPalette = 0,
  296. canvas
  297. };
  298. DisplayMode currentMode = colourPalette;
  299. //==============================================================================
  300. BitmapLEDProgram* canvasProgram;
  301. DrumPadGridProgram* colourPaletteProgram;
  302. ColourGrid layout;
  303. PhysicalTopologySource topologySource;
  304. Block::Ptr activeBlock;
  305. float scaleX = 0.0;
  306. float scaleY = 0.0;
  307. bool doublePress = false;
  308. //==============================================================================
  309. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
  310. };
  311. #endif // MAINCOMPONENT_H_INCLUDED