Browse Source

BLOCKS: Remove juce_blocks_basics module

v6.1.6
reuk 4 years ago
parent
commit
06544850fb
No known key found for this signature in database GPG Key ID: 9ADCD339CFC98A11
78 changed files with 27 additions and 18700 deletions
  1. +22
    -0
      BREAKING-CHANGES.txt
  2. +2
    -2
      LICENSE.md
  3. +2
    -2
      README.md
  4. +0
    -699
      examples/BLOCKS/BlocksDrawingDemo.h
  5. +0
    -1036
      examples/BLOCKS/BlocksMonitorDemo.h
  6. +0
    -866
      examples/BLOCKS/BlocksSynthDemo.h
  7. +0
    -24
      examples/BLOCKS/CMakeLists.txt
  8. +0
    -1
      examples/CMakeLists.txt
  9. +1
    -2
      examples/DemoRunner/CMakeLists.txt
  10. +0
    -8
      examples/DemoRunner/Source/Demos/DemoPIPs1.cpp
  11. +0
    -24
      extras/BLOCKS/Makefile
  12. +0
    -68
      extras/BLOCKS/standalone_sdk/README.md
  13. +0
    -15
      extras/BLOCKS/standalone_sdk/SDK/BlocksHeader.h
  14. +0
    -46
      extras/BLOCKS/standalone_sdk/SDK/Build/Linux/Makefile
  15. +0
    -299
      extras/BLOCKS/standalone_sdk/SDK/Build/MacOS/BLOCKS-SDK.xcodeproj/project.pbxproj
  16. +0
    -28
      extras/BLOCKS/standalone_sdk/SDK/Build/Windows/BLOCKS-SDK.sln
  17. +0
    -150
      extras/BLOCKS/standalone_sdk/SDK/Build/Windows/BLOCKS-SDK.vcxproj
  18. +0
    -49
      extras/BLOCKS/standalone_sdk/examples/BlockFinder/BlockFinder.cpp
  19. +0
    -39
      extras/BLOCKS/standalone_sdk/examples/BlockFinder/BlockFinder.h
  20. +0
    -72
      extras/BLOCKS/standalone_sdk/examples/BlockFinder/Linux/Makefile
  21. +0
    -49
      extras/BLOCKS/standalone_sdk/examples/BlockFinder/Linux/main.cpp
  22. +0
    -368
      extras/BLOCKS/standalone_sdk/examples/BlockFinder/MacOS/BlockFinder.xcodeproj/project.pbxproj
  23. +0
    -72
      extras/BLOCKS/standalone_sdk/examples/BlockFinder/MacOS/Makefile
  24. +0
    -36
      extras/BLOCKS/standalone_sdk/examples/BlockFinder/MacOS/main.mm
  25. +0
    -41
      extras/BLOCKS/standalone_sdk/examples/BlockFinder/Windows/BlockFinder.sln
  26. +0
    -172
      extras/BLOCKS/standalone_sdk/examples/BlockFinder/Windows/BlockFinder.vcxproj
  27. +0
    -49
      extras/BLOCKS/standalone_sdk/examples/BlockFinder/Windows/main.cpp
  28. +0
    -1
      extras/Build/CMake/JUCEConfig.cmake.in
  29. +0
    -1
      extras/Projucer/Source/Utility/Helpers/jucer_MiscUtilities.cpp
  30. +0
    -1
      extras/UnitTestRunner/CMakeLists.txt
  31. +0
    -1
      modules/CMakeLists.txt
  32. +0
    -9
      modules/juce_blocks_basics/README.txt
  33. +0
    -128
      modules/juce_blocks_basics/blocks/juce_Block.cpp
  34. +0
    -552
      modules/juce_blocks_basics/blocks/juce_Block.h
  35. +0
    -358
      modules/juce_blocks_basics/blocks/juce_BlockConfigManager.h
  36. +0
    -237
      modules/juce_blocks_basics/blocks/juce_BlocksVersion.cpp
  37. +0
    -87
      modules/juce_blocks_basics/blocks/juce_BlocksVersion.h
  38. +0
    -141
      modules/juce_blocks_basics/blocks/juce_ControlButton.h
  39. +0
    -105
      modules/juce_blocks_basics/blocks/juce_LEDGrid.h
  40. +0
    -64
      modules/juce_blocks_basics/blocks/juce_LEDRow.h
  41. +0
    -53
      modules/juce_blocks_basics/blocks/juce_StatusLight.h
  42. +0
    -152
      modules/juce_blocks_basics/blocks/juce_TouchList.h
  43. +0
    -138
      modules/juce_blocks_basics/blocks/juce_TouchSurface.h
  44. +0
    -785
      modules/juce_blocks_basics/juce_LittleFootFunctions.dox
  45. +0
    -44
      modules/juce_blocks_basics/juce_blocks_basics.cpp
  46. +0
    -85
      modules/juce_blocks_basics/juce_blocks_basics.h
  47. +0
    -125
      modules/juce_blocks_basics/littlefoot/LittleFoot Language README.txt
  48. +0
    -2454
      modules/juce_blocks_basics/littlefoot/juce_LittleFootCompiler.h
  49. +0
    -455
      modules/juce_blocks_basics/littlefoot/juce_LittleFootRemoteHeap.h
  50. +0
    -877
      modules/juce_blocks_basics/littlefoot/juce_LittleFootRunner.h
  51. +0
    -96
      modules/juce_blocks_basics/protocol/Protocol Format.txt
  52. +0
    -289
      modules/juce_blocks_basics/protocol/juce_BitPackingUtilities.h
  53. +0
    -367
      modules/juce_blocks_basics/protocol/juce_BlockModels.h
  54. +0
    -612
      modules/juce_blocks_basics/protocol/juce_BlocksProtocolDefinitions.h
  55. +0
    -325
      modules/juce_blocks_basics/protocol/juce_HostPacketBuilder.h
  56. +0
    -383
      modules/juce_blocks_basics/protocol/juce_HostPacketDecoder.h
  57. +0
    -101
      modules/juce_blocks_basics/topology/internal/juce_BandwidthStatsLogger.cpp
  58. +0
    -1151
      modules/juce_blocks_basics/topology/internal/juce_BlockImplementation.cpp
  59. +0
    -130
      modules/juce_blocks_basics/topology/internal/juce_BlockSerialReader.cpp
  60. +0
    -785
      modules/juce_blocks_basics/topology/internal/juce_ConnectedDeviceGroup.cpp
  61. +0
    -139
      modules/juce_blocks_basics/topology/internal/juce_DepreciatedVersionReader.cpp
  62. +0
    -797
      modules/juce_blocks_basics/topology/internal/juce_Detector.cpp
  63. +0
    -70
      modules/juce_blocks_basics/topology/internal/juce_DetectorHolder.cpp
  64. +0
    -50
      modules/juce_blocks_basics/topology/internal/juce_DeviceInfo.cpp
  65. +0
    -183
      modules/juce_blocks_basics/topology/internal/juce_MIDIDeviceDetector.cpp
  66. +0
    -113
      modules/juce_blocks_basics/topology/internal/juce_MidiDeviceConnection.cpp
  67. +0
    -112
      modules/juce_blocks_basics/topology/juce_BlockGraph.cpp
  68. +0
    -66
      modules/juce_blocks_basics/topology/juce_BlockGraph.h
  69. +0
    -204
      modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.cpp
  70. +0
    -98
      modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.h
  71. +0
    -126
      modules/juce_blocks_basics/topology/juce_RuleBasedTopologySource.cpp
  72. +0
    -90
      modules/juce_blocks_basics/topology/juce_RuleBasedTopologySource.h
  73. +0
    -120
      modules/juce_blocks_basics/topology/juce_Topology.h
  74. +0
    -87
      modules/juce_blocks_basics/topology/juce_TopologySource.h
  75. +0
    -87
      modules/juce_blocks_basics/visualisers/juce_BitmapLEDProgram.cpp
  76. +0
    -42
      modules/juce_blocks_basics/visualisers/juce_BitmapLEDProgram.h
  77. +0
    -881
      modules/juce_blocks_basics/visualisers/juce_DrumPadLEDProgram.cpp
  78. +0
    -126
      modules/juce_blocks_basics/visualisers/juce_DrumPadLEDProgram.h

+ 22
- 0
BREAKING-CHANGES.txt View File

@@ -4,6 +4,28 @@ JUCE breaking changes
Develop
=======

Change
------
The `juce_blocks_basics` module was removed.

Possible Issues
---------------
Projects depending on `juce_blocks_basics` will not build.

Workaround
----------
The BLOCKS API is now located in a separate repository:
https://github.com/WeAreROLI/roli_blocks_basics
Projects which used to depend on `juce_blocks_basics` can use
`roli_blocks_basics` instead.

Rationale
---------
ROLI is no longer involved with the development of JUCE. Therefore, development
on the BLOCKS API has been moved out of the JUCE repository, and to a new
repository managed by ROLI.


Change
------
The live build functionality of the Projucer has been removed.


+ 2
- 2
LICENSE.md View File

@@ -24,8 +24,8 @@ JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE, ARE DISCLAIMED.

The juce_audio_basics, juce_audio_devices, juce_blocks_basics, juce_core and
juce_events modules are permissively licensed under the terms of the [ISC
The juce_audio_basics, juce_audio_devices, juce_core and juce_events modules
are permissively licensed under the terms of the [ISC
license](http://www.isc.org/downloads/software-support-policy/isc-license).

For more information, visit the website:


+ 2
- 2
README.md View File

@@ -83,8 +83,8 @@ but if you would like to contribute any changes please contact us.

## License

The core JUCE modules (juce_audio_basics, juce_audio_devices, juce_blocks_basics, juce_core
and juce_events) are permissively licensed under the terms of the
The core JUCE modules (juce_audio_basics, juce_audio_devices, juce_core and juce_events)
are permissively licensed under the terms of the
[ISC license](http://www.isc.org/downloads/software-support-policy/isc-license/).
Other modules are covered by a
[GPL/Commercial license](https://www.gnu.org/licenses/gpl-3.0.en.html).


+ 0
- 699
examples/BLOCKS/BlocksDrawingDemo.h View File

@@ -1,699 +0,0 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: BlocksDrawingDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Blocks application to draw shapes.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_blocks_basics,
juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: BlocksDrawingDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
/**
Represents a single LED on a Lightpad
*/
struct LEDComponent : public Component
{
LEDComponent() : ledColour (Colours::black) { setInterceptsMouseClicks (false, false); }
void setColour (Colour newColour)
{
ledColour = newColour;
repaint();
}
void paint (Graphics& g) override
{
g.setColour (ledColour);
g.fillEllipse (getLocalBounds().toFloat());
}
Colour ledColour;
};
//==============================================================================
/**
A component that is used to represent a Lightpad on-screen
*/
class DrawableLightpadComponent : public Component
{
public:
DrawableLightpadComponent ()
{
for (auto x = 0; x < 15; ++x)
for (auto y = 0; y < 15; ++y)
addAndMakeVisible (leds.add (new LEDComponent()));
}
void paint (Graphics& g) override
{
auto r = getLocalBounds().toFloat();
// Clip the drawing area to only draw in the block area
{
Path clipArea;
clipArea.addRoundedRectangle (r, r.getWidth() / 20.0f);
g.reduceClipRegion (clipArea);
}
// Fill a black square for the Lightpad
g.fillAll (Colours::black);
}
void resized() override
{
auto r = getLocalBounds().reduced (10);
auto circleWidth = r.getWidth() / 15;
auto circleHeight = r.getHeight() / 15;
for (auto x = 0; x < 15; ++x)
for (auto y = 0; y < 15; ++y)
leds.getUnchecked ((x * 15) + y)->setBounds (r.getX() + (x * circleWidth),
r.getY() + (y * circleHeight),
circleWidth, circleHeight);
}
void mouseDown (const MouseEvent& e) override
{
for (auto x = 0; x < 15; ++x)
for (auto y = 0; y < 15; ++y)
if (leds.getUnchecked ((x * 15) + y)->getBounds().contains (e.position.toInt()))
listeners.call ([&] (Listener& l) { l.ledClicked (x, y, e.pressure); });
}
void mouseDrag (const MouseEvent& e) override
{
for (auto x = 0; x < 15; ++x)
{
for (auto y = 0; y < 15; ++y)
{
if (leds.getUnchecked ((x * 15) + y)->getBounds().contains (e.position.toInt()))
{
auto t = e.eventTime;
if (lastLED == Point<int> (x, y) && t.toMilliseconds() - lastMouseEventTime.toMilliseconds() < 50)
return;
listeners.call ([&] (Listener& l) { l.ledClicked (x, y, e.pressure); });
lastLED = { x, y };
lastMouseEventTime = t;
}
}
}
}
//==============================================================================
/** Sets the colour of one of the LEDComponents */
void setLEDColour (int x, int y, Colour c)
{
x = jmin (x, 14);
y = jmin (y, 14);
leds.getUnchecked ((x * 15) + y)->setColour (c);
}
//==============================================================================
struct Listener
{
virtual ~Listener() {}
/** Called when an LEDComponent has been clicked */
virtual void ledClicked (int x, int y, float z) = 0;
};
void addListener (Listener* l) { listeners.add (l); }
void removeListener (Listener* l) { listeners.remove (l); }
private:
OwnedArray<LEDComponent> leds;
ListenerList<Listener> listeners;
Time lastMouseEventTime;
Point<int> lastLED;
};
//==============================================================================
/**
A struct that handles the setup and layout of the DrumPadGridProgram
*/
struct ColourGrid
{
ColourGrid (int cols, int rows)
: numColumns (cols),
numRows (rows)
{
constructGridFillArray();
}
/** Creates a GridFill object for each pad in the grid and sets its colour
and fill before adding it to an array of GridFill objects
*/
void constructGridFillArray()
{
gridFillArray.clear();
auto counter = 0;
for (auto i = 0; i < numColumns; ++i)
{
for (auto j = 0; j < numRows; ++j)
{
DrumPadGridProgram::GridFill fill;
Colour colourToUse = colourArray.getUnchecked (counter);
fill.colour = colourToUse.withBrightness (colourToUse == currentColour ? 1.0f : 0.1f);
if (colourToUse == Colours::black)
fill.fillType = DrumPadGridProgram::GridFill::FillType::hollow;
else
fill.fillType = DrumPadGridProgram::GridFill::FillType::filled;
gridFillArray.add (fill);
if (++counter == colourArray.size())
counter = 0;
}
}
}
/** Sets which colour should be active for a given touch co-ordinate. Returns
true if the colour has changed
*/
bool setActiveColourForTouch (int x, int y)
{
auto colourHasChanged = false;
auto xindex = x / 5;
auto yindex = y / 5;
auto newColour = colourArray.getUnchecked ((yindex * 3) + xindex);
if (currentColour != newColour)
{
currentColour = newColour;
constructGridFillArray();
colourHasChanged = true;
}
return colourHasChanged;
}
//==============================================================================
int numColumns, numRows;
Array<DrumPadGridProgram::GridFill> gridFillArray;
Array<Colour> colourArray = { Colours::white, Colours::red, Colours::green,
Colours::blue, Colours::hotpink, Colours::orange,
Colours::magenta, Colours::cyan, Colours::black };
Colour currentColour = Colours::hotpink;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourGrid)
};
//==============================================================================
/**
The main component
*/
class BlocksDrawingDemo : public Component,
public TopologySource::Listener,
private TouchSurface::Listener,
private ControlButton::Listener,
private DrawableLightpadComponent::Listener,
private Timer
{
public:
//==============================================================================
BlocksDrawingDemo()
{
activeLeds.clear();
// Register MainContentComponent as a listener to the PhysicalTopologySource object
topologySource.addListener (this);
infoLabel.setText ("Connect a Lightpad Block to draw.", dontSendNotification);
infoLabel.setJustificationType (Justification::centred);
addAndMakeVisible (infoLabel);
addAndMakeVisible (lightpadComponent);
lightpadComponent.setVisible (false);
lightpadComponent.addListener (this);
clearButton.setButtonText ("Clear");
clearButton.onClick = [this] { clearLEDs(); };
clearButton.setAlwaysOnTop (true);
addAndMakeVisible (clearButton);
brightnessSlider.setRange (0.0, 1.0);
brightnessSlider.setValue (1.0);
brightnessSlider.setAlwaysOnTop (true);
brightnessSlider.setTextBoxStyle (Slider::TextEntryBoxPosition::NoTextBox, false, 0, 0);
brightnessSlider.onValueChange = [this]
{
brightnessLED.setColour (layout.currentColour
.withBrightness (layout.currentColour == Colours::black ? 0.0f
: static_cast<float> (brightnessSlider.getValue())));
};
addAndMakeVisible (brightnessSlider);
brightnessLED.setAlwaysOnTop (true);
brightnessLED.setColour (layout.currentColour.withBrightness (static_cast<float> (brightnessSlider.getValue())));
addAndMakeVisible (brightnessLED);
#if JUCE_IOS
connectButton.setButtonText ("Connect");
connectButton.onClick = [] { BluetoothMidiDevicePairingDialogue::open(); };
connectButton.setAlwaysOnTop (true);
addAndMakeVisible (connectButton);
#endif
setSize (600, 600);
topologyChanged();
}
~BlocksDrawingDemo() override
{
if (activeBlock != nullptr)
detachActiveBlock();
lightpadComponent.removeListener (this);
topologySource.removeListener (this);
}
void resized() override
{
infoLabel.centreWithSize (getWidth(), 100);
auto bounds = getLocalBounds().reduced (20);
// top buttons
auto topButtonArea = bounds.removeFromTop (getHeight() / 20);
topButtonArea.removeFromLeft (20);
clearButton.setBounds (topButtonArea.removeFromLeft (80));
#if JUCE_IOS
topButtonArea.removeFromRight (20);
connectButton.setBounds (topButtonArea.removeFromRight (80));
#endif
bounds.removeFromTop (20);
auto orientation = Desktop::getInstance().getCurrentOrientation();
if (orientation == Desktop::DisplayOrientation::upright
|| orientation == Desktop::DisplayOrientation::upsideDown)
{
auto brightnessControlBounds = bounds.removeFromBottom (getHeight() / 10);
brightnessSlider.setSliderStyle (Slider::SliderStyle::LinearHorizontal);
brightnessLED.setBounds (brightnessControlBounds.removeFromLeft (getHeight() / 10));
brightnessSlider.setBounds (brightnessControlBounds);
}
else
{
auto brightnessControlBounds = bounds.removeFromRight (getWidth() / 10);
brightnessSlider.setSliderStyle (Slider::SliderStyle::LinearVertical);
brightnessLED.setBounds (brightnessControlBounds.removeFromTop (getWidth() / 10));
brightnessSlider.setBounds (brightnessControlBounds);
}
// lightpad component
auto sideLength = jmin (bounds.getWidth() - 40, bounds.getHeight() - 40);
lightpadComponent.centreWithSize (sideLength, sideLength);
}
/** Overridden from TopologySource::Listener. Called when the topology changes */
void topologyChanged() override
{
lightpadComponent.setVisible (false);
infoLabel.setVisible (true);
// Reset the activeBlock object
if (activeBlock != nullptr)
detachActiveBlock();
// Get the array of currently connected Block objects from the PhysicalTopologySource
auto blocks = topologySource.getCurrentTopology().blocks;
// Iterate over the array of Block objects
for (auto b : blocks)
{
// Find the first Lightpad
if (b->getType() == Block::Type::lightPadBlock)
{
activeBlock = b;
// Register MainContentComponent as a listener to the touch surface
if (auto surface = activeBlock->getTouchSurface())
surface->addListener (this);
// Register MainContentComponent as a listener to any buttons
for (auto button : activeBlock->getButtons())
button->addListener (this);
// Get the LEDGrid object from the Lightpad and set its program to the program for the current mode
if (auto grid = activeBlock->getLEDGrid())
{
// Work out scale factors to translate X and Y touches to LED indexes
scaleX = (float) (grid->getNumColumns() - 1) / (float) activeBlock->getWidth();
scaleY = (float) (grid->getNumRows() - 1) / (float) activeBlock->getHeight();
setLEDProgram (*activeBlock);
}
// Make the on screen Lightpad component visible
lightpadComponent.setVisible (true);
infoLabel.setVisible (false);
break;
}
}
}
private:
//==============================================================================
/** Overridden from TouchSurface::Listener. Called when a Touch is received on the Lightpad */
void touchChanged (TouchSurface&, const TouchSurface::Touch& touch) override
{
// Translate X and Y touch events to LED indexes
auto xLed = roundToInt (touch.x * scaleX);
auto yLed = roundToInt (touch.y * scaleY);
if (currentMode == colourPalette)
{
if (layout.setActiveColourForTouch (xLed, yLed))
{
if (auto* colourPaletteProgram = getPaletteProgram())
{
colourPaletteProgram->setGridFills (layout.numColumns, layout.numRows, layout.gridFillArray);
brightnessLED.setColour (layout.currentColour
.withBrightness (layout.currentColour == Colours::black ? 0.0f
: static_cast<float> (brightnessSlider.getValue())));
}
}
}
else if (currentMode == canvas)
{
drawLED ((uint32) xLed, (uint32) yLed, touch.z, layout.currentColour);
}
}
/** Overridden from ControlButton::Listener. Called when a button on the Lightpad is pressed */
void buttonPressed (ControlButton&, Block::Timestamp) override {}
/** Overridden from ControlButton::Listener. Called when a button on the Lightpad is released */
void buttonReleased (ControlButton&, Block::Timestamp) override
{
if (currentMode == canvas)
{
// Wait 500ms to see if there is a second press
if (! isTimerRunning())
startTimer (500);
else
doublePress = true;
}
else if (currentMode == colourPalette)
{
// Switch to canvas mode and set the LEDGrid program
currentMode = canvas;
setLEDProgram (*activeBlock);
}
}
void ledClicked (int x, int y, float z) override
{
drawLED ((uint32) x, (uint32) y,
z == 0.0f ? static_cast<float> (brightnessSlider.getValue())
: z * static_cast<float> (brightnessSlider.getValue()), layout.currentColour);
}
void timerCallback() override
{
if (doublePress)
{
clearLEDs();
// Reset the doublePress flag
doublePress = false;
}
else
{
// Switch to colour palette mode and set the LEDGrid program
currentMode = colourPalette;
setLEDProgram (*activeBlock);
}
stopTimer();
}
/** Removes TouchSurface and ControlButton listeners and sets activeBlock to nullptr */
void detachActiveBlock()
{
if (auto surface = activeBlock->getTouchSurface())
surface->removeListener (this);
for (auto button : activeBlock->getButtons())
button->removeListener (this);
activeBlock = nullptr;
}
/** Sets the LEDGrid Program for the selected mode */
void setLEDProgram (Block& block)
{
if (currentMode == canvas)
{
block.setProgram (std::make_unique<BitmapLEDProgram>(block));
// Redraw any previously drawn LEDs
redrawLEDs();
}
else if (currentMode == colourPalette)
{
block.setProgram (std::make_unique <DrumPadGridProgram>(block));
// Setup the grid layout
if (auto* program = getPaletteProgram())
program->setGridFills (layout.numColumns, layout.numRows, layout.gridFillArray);
}
}
void clearLEDs()
{
if (auto* canvasProgram = getCanvasProgram())
{
// Clear the LED grid
for (uint32 x = 0; x < 15; ++x)
{
for (uint32 y = 0; y < 15; ++ y)
{
canvasProgram->setLED (x, y, Colours::black);
lightpadComponent.setLEDColour ((int) x, (int) y, Colours::black);
}
}
// Clear the ActiveLED array
activeLeds.clear();
}
}
/** Sets an LED on the Lightpad for a given touch co-ordinate and pressure */
void drawLED (uint32 x0, uint32 y0, float z, Colour drawColour)
{
if (auto* canvasProgram = getCanvasProgram())
{
// Check if the activeLeds array already contains an ActiveLED object for this LED
auto index = getLEDAt (x0, y0);
// If the colour is black then just set the LED to black and return
if (drawColour == Colours::black)
{
if (index >= 0)
{
canvasProgram->setLED (x0, y0, Colours::black);
lightpadComponent.setLEDColour ((int) x0, (int) y0, Colours::black);
activeLeds.remove (index);
}
return;
}
// If there is no ActiveLED object for this LED then create one,
// add it to the array, set the LED on the Block and return
if (index < 0)
{
ActiveLED led;
led.x = x0;
led.y = y0;
led.colour = drawColour;
led.brightness = z;
activeLeds.add (led);
canvasProgram->setLED (led.x, led.y, led.colour.withBrightness (led.brightness));
lightpadComponent.setLEDColour ((int) led.x, (int) led.y, led.colour.withBrightness (led.brightness));
return;
}
// Get the ActiveLED object for this LED
auto currentLed = activeLeds.getReference (index);
// If the LED colour is the same as the draw colour, add the brightnesses together.
// If it is different, blend the colours
if (currentLed.colour == drawColour)
currentLed.brightness = jmin (currentLed.brightness + z, 1.0f);
else
currentLed.colour = currentLed.colour.interpolatedWith (drawColour, z);
// Set the LED on the Block and change the ActiveLED object in the activeLeds array
if (canvasProgram != nullptr)
canvasProgram->setLED (currentLed.x, currentLed.y, currentLed.colour.withBrightness (currentLed.brightness));
lightpadComponent.setLEDColour ((int) currentLed.x, (int) currentLed.y, currentLed.colour.withBrightness (currentLed.brightness));
activeLeds.set (index, currentLed);
}
}
/** Redraws the LEDs on the Lightpad from the activeLeds array */
void redrawLEDs()
{
if (auto* canvasProgram = getCanvasProgram())
{
// Iterate over the activeLeds array and set the LEDs on the Block
for (auto led : activeLeds)
{
canvasProgram->setLED (led.x, led.y, led.colour.withBrightness (led.brightness));
lightpadComponent.setLEDColour ((int) led.x, (int) led.y, led.colour.withBrightness (led.brightness));
}
}
}
//==============================================================================
BitmapLEDProgram* getCanvasProgram()
{
if (activeBlock != nullptr)
return dynamic_cast<BitmapLEDProgram*> (activeBlock->getProgram());
return nullptr;
}
DrumPadGridProgram* getPaletteProgram()
{
if (activeBlock != nullptr)
return dynamic_cast<DrumPadGridProgram*> (activeBlock->getProgram());
return nullptr;
}
//==============================================================================
/**
A struct that represents an active LED on the Lightpad.
Has a position, colour and brightness.
*/
struct ActiveLED
{
uint32 x, y;
Colour colour;
float brightness;
/** Returns true if this LED occupies the given co-ordinates */
bool occupies (uint32 xPos, uint32 yPos) const
{
return xPos == x && yPos == y;
}
};
Array<ActiveLED> activeLeds;
int getLEDAt (uint32 x, uint32 y) const
{
for (auto i = 0; i < activeLeds.size(); ++i)
if (activeLeds.getReference (i).occupies (x, y))
return i;
return -1;
}
//==============================================================================
enum DisplayMode
{
colourPalette = 0,
canvas
};
DisplayMode currentMode = colourPalette;
//==============================================================================
ColourGrid layout { 3, 3 };
PhysicalTopologySource topologySource;
Block::Ptr activeBlock;
float scaleX = 0.0f;
float scaleY = 0.0f;
bool doublePress = false;
Label infoLabel;
DrawableLightpadComponent lightpadComponent;
TextButton clearButton;
LEDComponent brightnessLED;
Slider brightnessSlider;
#if JUCE_IOS
TextButton connectButton;
#endif
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlocksDrawingDemo)
};

+ 0
- 1036
examples/BLOCKS/BlocksMonitorDemo.h
File diff suppressed because it is too large
View File


+ 0
- 866
examples/BLOCKS/BlocksSynthDemo.h View File

@@ -1,866 +0,0 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: BlocksSynthDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Blocks synthesiser application.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_blocks_basics,
juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: BlocksSynthDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
/**
Base class for oscillators
*/
class OscillatorBase : public SynthesiserVoice
{
public:
OscillatorBase()
{
amplitude.reset (getSampleRate(), 0.1);
phaseIncrement.reset (getSampleRate(), 0.1);
}
void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int) override
{
frequency = MidiMessage::getMidiNoteInHertz (midiNoteNumber);
phaseIncrement.setTargetValue (((MathConstants<double>::twoPi) * frequency) / sampleRate);
amplitude.setTargetValue (velocity);
// Store the initial note and work out the maximum frequency deviations for pitch bend
initialNote = midiNoteNumber;
maxFreq = MidiMessage::getMidiNoteInHertz (initialNote + 4) - frequency;
minFreq = frequency - MidiMessage::getMidiNoteInHertz (initialNote - 4);
}
void stopNote (float, bool) override
{
clearCurrentNote();
amplitude.setTargetValue (0.0);
}
void pitchWheelMoved (int newValue) override
{
// Change the phase increment based on pitch bend amount
auto frequencyOffset = ((newValue > 0 ? maxFreq : minFreq) * (newValue / 127.0));
phaseIncrement.setTargetValue (((MathConstants<double>::twoPi) * (frequency + frequencyOffset)) / sampleRate);
}
void controllerMoved (int, int) override {}
void channelPressureChanged (int newChannelPressureValue) override
{
// Set the amplitude based on pressure value
amplitude.setTargetValue (newChannelPressureValue / 127.0);
}
void renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples) override
{
while (--numSamples >= 0)
{
auto output = getSample() * amplitude.getNextValue();
for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
outputBuffer.addSample (i, startSample, static_cast<float> (output));
++startSample;
}
}
using SynthesiserVoice::renderNextBlock;
/** Returns the next sample */
double getSample()
{
auto output = renderWaveShape (phasePos);
phasePos += phaseIncrement.getNextValue();
if (phasePos > MathConstants<double>::twoPi)
phasePos -= MathConstants<double>::twoPi;
return output;
}
/** Subclasses should override this to say whether they can play the given sound */
bool canPlaySound (SynthesiserSound*) override = 0;
/** Subclasses should override this to render a waveshape */
virtual double renderWaveShape (const double currentPhase) = 0;
private:
SmoothedValue<double> amplitude, phaseIncrement;
double frequency = 0.0;
double phasePos = 0.0;
double sampleRate = 44100.0;
int initialNote = 0;
double maxFreq = 0.0, minFreq = 0.0;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OscillatorBase)
};
//==============================================================================
/**
Sine sound struct - applies to MIDI channel 1
*/
struct SineSound : public SynthesiserSound
{
SineSound () {}
bool appliesToNote (int) override { return true; }
bool appliesToChannel (int midiChannel) override { return (midiChannel == 1); }
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SineSound)
};
/**
Sine voice struct that renders a sin waveshape
*/
struct SineVoice : public OscillatorBase
{
SineVoice() {}
bool canPlaySound (SynthesiserSound* sound) override { return dynamic_cast<SineSound*> (sound) != nullptr; }
double renderWaveShape (const double currentPhase) override { return sin (currentPhase); }
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SineVoice)
};
//==============================================================================
/**
Square sound struct - applies to MIDI channel 2
*/
struct SquareSound : public SynthesiserSound
{
SquareSound() {}
bool appliesToNote (int) override { return true; }
bool appliesToChannel (int midiChannel) override { return (midiChannel == 2); }
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SquareSound)
};
/**
Square voice struct that renders a square waveshape
*/
struct SquareVoice : public OscillatorBase
{
SquareVoice() {}
bool canPlaySound (SynthesiserSound* sound) override { return dynamic_cast<SquareSound*> (sound) != nullptr; }
double renderWaveShape (const double currentPhase) override { return (currentPhase < MathConstants<double>::pi ? 0.0 : 1.0); }
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SquareVoice)
};
//==============================================================================
/**
Sawtooth sound - applies to MIDI channel 3
*/
struct SawSound : public SynthesiserSound
{
SawSound() {}
bool appliesToNote (int) override { return true; }
bool appliesToChannel (int midiChannel) override { return (midiChannel == 3); }
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SawSound)
};
/**
Sawtooth voice that renders a sawtooth waveshape
*/
struct SawVoice : public OscillatorBase
{
SawVoice() {}
bool canPlaySound (SynthesiserSound* sound) override { return dynamic_cast<SawSound*> (sound) != nullptr; }
double renderWaveShape (const double currentPhase) override { return (1.0 / MathConstants<double>::pi) * currentPhase - 1.0; }
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SawVoice)
};
//==============================================================================
/**
Triangle sound - applies to MIDI channel 4
*/
struct TriangleSound : public SynthesiserSound
{
TriangleSound() {}
bool appliesToNote (int) override { return true; }
bool appliesToChannel (int midiChannel) override { return (midiChannel == 4); }
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TriangleSound)
};
/**
Triangle voice that renders a triangle waveshape
*/
struct TriangleVoice : public OscillatorBase
{
TriangleVoice() {}
bool canPlaySound (SynthesiserSound* sound) override { return dynamic_cast<TriangleSound*> (sound) != nullptr; }
double renderWaveShape (const double currentPhase) override
{
return currentPhase < MathConstants<double>::pi ? -1.0 + (2.0 / MathConstants<double>::pi) * currentPhase
: 3.0 - (2.0 / MathConstants<double>::pi) * currentPhase;
}
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TriangleVoice)
};
//==============================================================================
/**
Class to handle the Audio functionality
*/
class Audio : public AudioIODeviceCallback
{
public:
Audio()
{
// Set up the audio device manager
#ifndef JUCE_DEMO_RUNNER
audioDeviceManager.initialiseWithDefaultDevices (0, 2);
#endif
audioDeviceManager.addAudioCallback (this);
// Set up the synthesiser and add each of the waveshapes
synthesiser.clearVoices();
synthesiser.clearSounds();
synthesiser.addVoice (new SineVoice());
synthesiser.addVoice (new SquareVoice());
synthesiser.addVoice (new SawVoice());
synthesiser.addVoice (new TriangleVoice());
synthesiser.addSound (new SineSound());
synthesiser.addSound (new SquareSound());
synthesiser.addSound (new SawSound());
synthesiser.addSound (new TriangleSound());
}
~Audio() override
{
audioDeviceManager.removeAudioCallback (this);
}
/** Audio callback */
void audioDeviceIOCallback (const float** /*inputChannelData*/, int /*numInputChannels*/,
float** outputChannelData, int numOutputChannels, int numSamples) override
{
AudioBuffer<float> sampleBuffer (outputChannelData, numOutputChannels, numSamples);
sampleBuffer.clear();
synthesiser.renderNextBlock (sampleBuffer, MidiBuffer(), 0, numSamples);
}
void audioDeviceAboutToStart (AudioIODevice* device) override
{
synthesiser.setCurrentPlaybackSampleRate (device->getCurrentSampleRate());
}
void audioDeviceStopped() override {}
/** Called to turn a synthesiser note on */
void noteOn (int channel, int noteNum, float velocity)
{
synthesiser.noteOn (channel, noteNum, velocity);
}
/** Called to turn a synthesiser note off */
void noteOff (int channel, int noteNum, float velocity)
{
synthesiser.noteOff (channel, noteNum, velocity, false);
}
/** Called to turn all synthesiser notes off */
void allNotesOff()
{
for (auto i = 1; i < 5; ++i)
synthesiser.allNotesOff (i, false);
}
/** Send pressure change message to synthesiser */
void pressureChange (int channel, float newPressure)
{
synthesiser.handleChannelPressure (channel, static_cast<int> (newPressure * 127));
}
/** Send pitch change message to synthesiser */
void pitchChange (int channel, float pitchChange)
{
synthesiser.handlePitchWheel (channel, static_cast<int> (pitchChange * 127));
}
private:
#ifndef JUCE_DEMO_RUNNER
AudioDeviceManager audioDeviceManager;
#else
AudioDeviceManager& audioDeviceManager { getSharedAudioDeviceManager (0, 2) };
#endif
Synthesiser synthesiser;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Audio)
};
//==============================================================================
/**
A Program to draw moving waveshapes onto the LEDGrid
*/
class WaveshapeProgram : public Block::Program
{
public:
WaveshapeProgram (Block& b) : Program (b) {}
/** Sets the waveshape type to display on the grid */
void setWaveshapeType (uint8 type)
{
block.setDataByte (0, type);
}
/** Generates the Y coordinates for 1.5 cycles of each of the four waveshapes and stores them
at the correct offsets in the shared data heap. */
void generateWaveshapes()
{
uint8 sineWaveY[45];
uint8 squareWaveY[45];
uint8 sawWaveY[45];
uint8 triangleWaveY[45];
// Set current phase position to 0 and work out the required phase increment for one cycle
auto currentPhase = 0.0;
auto phaseInc = (1.0 / 30.0) * MathConstants<double>::twoPi;
for (auto x = 0; x < 30; ++x)
{
// Scale and offset the sin output to the Lightpad display
auto sineOutput = std::sin (currentPhase);
sineWaveY[x] = static_cast<uint8> (roundToInt ((sineOutput * 6.5) + 7.0));
// Square wave output, set flags for when vertical line should be drawn
if (currentPhase < MathConstants<double>::pi)
{
if (x == 0)
squareWaveY[x] = 255;
else
squareWaveY[x] = 1;
}
else
{
if (x > 0 && squareWaveY[x - 1] == 1)
squareWaveY[x - 1] = 255;
squareWaveY[x] = 13;
}
// Saw wave output, set flags for when vertical line should be drawn
sawWaveY[x] = 14 - ((x / 2) % 15);
if (sawWaveY[x] == 0 && sawWaveY[x - 1] != 255)
sawWaveY[x] = 255;
// Triangle wave output
triangleWaveY[x] = x < 15 ? static_cast<uint8> (x) : static_cast<uint8> (14 - (x % 15));
// Add half cycle to end of array so it loops correctly
if (x < 15)
{
sineWaveY[x + 30] = sineWaveY[x];
squareWaveY[x + 30] = squareWaveY[x];
sawWaveY[x + 30] = sawWaveY[x];
triangleWaveY[x + 30] = triangleWaveY[x];
}
// Increment the current phase
currentPhase += phaseInc;
}
// Store the values for each of the waveshapes at the correct offsets in the shared data heap
for (uint8 i = 0; i < 45; ++i)
{
block.setDataByte (sineWaveOffset + i, sineWaveY[i]);
block.setDataByte (squareWaveOffset + i, squareWaveY[i]);
block.setDataByte (sawWaveOffset + i, sawWaveY[i]);
block.setDataByte (triangleWaveOffset + i, triangleWaveY[i]);
}
}
String getLittleFootProgram() override
{
return R"littlefoot(
#heapsize: 256
int yOffset;
void drawLEDCircle (int x0, int y0)
{
blendPixel (0xffff0000, x0, y0);
int minLedIndex = 0;
int maxLedIndex = 14;
blendPixel (0xff660000, min (x0 + 1, maxLedIndex), y0);
blendPixel (0xff660000, max (x0 - 1, minLedIndex), y0);
blendPixel (0xff660000, x0, min (y0 + 1, maxLedIndex));
blendPixel (0xff660000, x0, max (y0 - 1, minLedIndex));
blendPixel (0xff1a0000, min (x0 + 1, maxLedIndex), min (y0 + 1, maxLedIndex));
blendPixel (0xff1a0000, min (x0 + 1, maxLedIndex), max (y0 - 1, minLedIndex));
blendPixel (0xff1a0000, max (x0 - 1, minLedIndex), min (y0 + 1, maxLedIndex));
blendPixel (0xff1a0000, max (x0 - 1, minLedIndex), max (y0 - 1, minLedIndex));
}
void repaint()
{
// Clear LEDs to black
fillRect (0xff000000, 0, 0, 15, 15);
// Get the waveshape type
int type = getHeapByte (0);
// Calculate the heap offset
int offset = 1 + (type * 45) + yOffset;
for (int x = 0; x < 15; ++x)
{
// Get the corresponding Y coordinate for each X coordinate
int y = getHeapByte (offset + x);
// Draw a vertical line if flag is set or draw an LED circle
if (y == 255)
{
for (int i = 0; i < 15; ++i)
drawLEDCircle (x, i);
}
else if (x % 2 == 0)
{
drawLEDCircle (x, y);
}
}
// Increment and wrap the Y offset to draw a 'moving' waveshape
if (++yOffset == 30)
yOffset = 0;
}
)littlefoot";
}
private:
//==============================================================================
/** Shared data heap is laid out as below. There is room for the waveshape type and
the Y coordinates for 1.5 cycles of each of the four waveshapes. */
static constexpr uint32 waveshapeType = 0; // 1 byte
static constexpr uint32 sineWaveOffset = 1; // 1 byte * 45
static constexpr uint32 squareWaveOffset = 46; // 1 byte * 45
static constexpr uint32 sawWaveOffset = 91; // 1 byte * 45
static constexpr uint32 triangleWaveOffset = 136; // 1 byte * 45
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WaveshapeProgram)
};
//==============================================================================
/**
A struct that handles the setup and layout of the DrumPadGridProgram
*/
struct SynthGrid
{
SynthGrid (int cols, int rows)
: numColumns (cols),
numRows (rows)
{
constructGridFillArray();
}
/** Creates a GridFill object for each pad in the grid and sets its colour
and fill before adding it to an array of GridFill objects
*/
void constructGridFillArray()
{
gridFillArray.clear();
for (auto i = 0; i < numRows; ++i)
{
for (auto j = 0; j < numColumns; ++j)
{
DrumPadGridProgram::GridFill fill;
auto padNum = (i * 5) + j;
fill.colour = notes.contains (padNum) ? baseGridColour
: tonics.contains (padNum) ? Colours::white
: Colours::black;
fill.fillType = DrumPadGridProgram::GridFill::FillType::gradient;
gridFillArray.add (fill);
}
}
}
int getNoteNumberForPad (int x, int y) const
{
auto xIndex = x / 3;
auto yIndex = y / 3;
return 60 + ((4 - yIndex) * 5) + xIndex;
}
//==============================================================================
int numColumns, numRows;
float width, height;
Array<DrumPadGridProgram::GridFill> gridFillArray;
Colour baseGridColour = Colours::green;
Colour touchColour = Colours::red;
Array<int> tonics = { 4, 12, 20 };
Array<int> notes = { 1, 3, 6, 7, 9, 11, 14, 15, 17, 19, 22, 24 };
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SynthGrid)
};
//==============================================================================
/**
The main component
*/
class BlocksSynthDemo : public Component,
public TopologySource::Listener,
private TouchSurface::Listener,
private ControlButton::Listener,
private Timer
{
public:
BlocksSynthDemo()
{
// Register BlocksSynthDemo as a listener to the PhysicalTopologySource object
topologySource.addListener (this);
#if JUCE_IOS
connectButton.setButtonText ("Connect");
connectButton.onClick = [] { BluetoothMidiDevicePairingDialogue::open(); };
addAndMakeVisible (connectButton);
#endif
setSize (600, 400);
topologyChanged();
}
~BlocksSynthDemo() override
{
if (activeBlock != nullptr)
detachActiveBlock();
topologySource.removeListener (this);
}
void paint (Graphics& g) override
{
g.setColour (getLookAndFeel().findColour (Label::textColourId));
g.drawText ("Connect a Lightpad Block to play.",
getLocalBounds(), Justification::centred, false);
}
void resized() override
{
#if JUCE_IOS
connectButton.setBounds (getRight() - 100, 20, 80, 30);
#endif
}
/** Overridden from TopologySource::Listener, called when the topology changes */
void topologyChanged() override
{
// Reset the activeBlock object
if (activeBlock != nullptr)
detachActiveBlock();
// Get the array of currently connected Block objects from the PhysicalTopologySource
auto blocks = topologySource.getBlocks();
// Iterate over the array of Block objects
for (auto b : blocks)
{
// Find the first Lightpad
if (b->getType() == Block::Type::lightPadBlock)
{
activeBlock = b;
// Register BlocksSynthDemo as a listener to the touch surface
if (auto surface = activeBlock->getTouchSurface())
surface->addListener (this);
// Register BlocksSynthDemo as a listener to any buttons
for (auto button : activeBlock->getButtons())
button->addListener (this);
// Get the LEDGrid object from the Lightpad and set its program to the program for the current mode
if (auto grid = activeBlock->getLEDGrid())
{
// Work out scale factors to translate X and Y touches to LED indexes
scaleX = static_cast<float> (grid->getNumColumns() - 1) / (float) activeBlock->getWidth();
scaleY = static_cast<float> (grid->getNumRows() - 1) / (float) activeBlock->getHeight();
setLEDProgram (*activeBlock);
}
break;
}
}
}
private:
/** Overridden from TouchSurface::Listener. Called when a Touch is received on the Lightpad */
void touchChanged (TouchSurface&, const TouchSurface::Touch& touch) override
{
if (currentMode == waveformSelectionMode && touch.isTouchStart && allowTouch)
{
if (auto* waveshapeProgram = getWaveshapeProgram())
{
// Change the displayed waveshape to the next one
++waveshapeMode;
if (waveshapeMode > 3)
waveshapeMode = 0;
waveshapeProgram->setWaveshapeType (static_cast<uint8> (waveshapeMode));
allowTouch = false;
startTimer (250);
}
}
else if (currentMode == playMode)
{
if (auto* gridProgram = getGridProgram())
{
// Translate X and Y touch events to LED indexes
auto xLed = roundToInt (touch.startX * scaleX);
auto yLed = roundToInt (touch.startY * scaleY);
// Limit the number of touches per second
constexpr auto maxNumTouchMessagesPerSecond = 100;
auto now = Time::getCurrentTime();
clearOldTouchTimes (now);
auto midiChannel = waveshapeMode + 1;
// Send the touch event to the DrumPadGridProgram and Audio class
if (touch.isTouchStart)
{
gridProgram->startTouch (touch.startX, touch.startY);
audio.noteOn (midiChannel, layout.getNoteNumberForPad (xLed, yLed), touch.z);
}
else if (touch.isTouchEnd)
{
gridProgram->endTouch (touch.startX, touch.startY);
audio.noteOff (midiChannel, layout.getNoteNumberForPad (xLed, yLed), 1.0);
}
else
{
if (touchMessageTimesInLastSecond.size() > maxNumTouchMessagesPerSecond / 3)
return;
gridProgram->sendTouch (touch.x, touch.y, touch.z,
layout.touchColour);
// Send pitch change and pressure values to the Audio class
audio.pitchChange (midiChannel, (touch.x - touch.startX) / (float) activeBlock->getWidth());
audio.pressureChange (midiChannel, touch.z);
}
touchMessageTimesInLastSecond.add (now);
}
}
}
/** Overridden from ControlButton::Listener. Called when a button on the Lightpad is pressed */
void buttonPressed (ControlButton&, Block::Timestamp) override {}
/** Overridden from ControlButton::Listener. Called when a button on the Lightpad is released */
void buttonReleased (ControlButton&, Block::Timestamp) override
{
// Turn any active synthesiser notes off
audio.allNotesOff();
// Switch modes
if (currentMode == waveformSelectionMode)
currentMode = playMode;
else if (currentMode == playMode)
currentMode = waveformSelectionMode;
// Set the LEDGrid program to the new mode
setLEDProgram (*activeBlock);
}
/** Clears the old touch times */
void clearOldTouchTimes (const Time now)
{
for (auto i = touchMessageTimesInLastSecond.size(); --i >= 0;)
if (touchMessageTimesInLastSecond.getReference(i) < now - RelativeTime::seconds (0.33))
touchMessageTimesInLastSecond.remove (i);
}
/** Removes TouchSurface and ControlButton listeners and sets activeBlock to nullptr */
void detachActiveBlock()
{
if (auto surface = activeBlock->getTouchSurface())
surface->removeListener (this);
for (auto button : activeBlock->getButtons())
button->removeListener (this);
activeBlock = nullptr;
}
/** Sets the LEDGrid Program for the selected mode */
void setLEDProgram (Block& block)
{
if (currentMode == waveformSelectionMode)
{
// Set the LEDGrid program
block.setProgram (std::make_unique<WaveshapeProgram>(block));
// Initialise the program
if (auto* waveshapeProgram = getWaveshapeProgram())
{
waveshapeProgram->setWaveshapeType (static_cast<uint8> (waveshapeMode));
waveshapeProgram->generateWaveshapes();
}
}
else if (currentMode == playMode)
{
// Set the LEDGrid program
auto error = block.setProgram (std::make_unique<DrumPadGridProgram>(block));
if (error.failed())
{
DBG (error.getErrorMessage());
jassertfalse;
}
// Setup the grid layout
if (auto* gridProgram = getGridProgram())
gridProgram->setGridFills (layout.numColumns, layout.numRows, layout.gridFillArray);
}
}
/** Stops touch events from triggering multiple waveshape mode changes */
void timerCallback() override { allowTouch = true; }
//==============================================================================
DrumPadGridProgram* getGridProgram()
{
if (activeBlock != nullptr)
return dynamic_cast<DrumPadGridProgram*> (activeBlock->getProgram());
return nullptr;
}
WaveshapeProgram* getWaveshapeProgram()
{
if (activeBlock != nullptr)
return dynamic_cast<WaveshapeProgram*> (activeBlock->getProgram());
return nullptr;
}
//==============================================================================
enum BlocksSynthMode
{
waveformSelectionMode = 0,
playMode
};
BlocksSynthMode currentMode = playMode;
//==============================================================================
Audio audio;
SynthGrid layout { 5, 5 };
PhysicalTopologySource topologySource;
Block::Ptr activeBlock;
Array<Time> touchMessageTimesInLastSecond;
int waveshapeMode = 0;
float scaleX = 0.0f;
float scaleY = 0.0f;
bool allowTouch = true;
//==============================================================================
#if JUCE_IOS
TextButton connectButton;
#endif
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlocksSynthDemo)
};

+ 0
- 24
examples/BLOCKS/CMakeLists.txt View File

@@ -1,24 +0,0 @@
# ==============================================================================
#
# This file is part of the JUCE library.
# Copyright (c) 2020 - Raw Material Software Limited
#
# JUCE is an open source library subject to commercial or open-source
# licensing.
#
# By using JUCE, you agree to the terms of both the JUCE 6 End-User License
# Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
#
# End User License Agreement: www.juce.com/juce-6-licence
# Privacy Policy: www.juce.com/juce-privacy-policy
#
# Or: You may also use this code under the terms of the GPL v3 (see
# www.gnu.org/licenses).
#
# JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
# EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
# DISCLAIMED.
#
# ==============================================================================

_juce_add_pips()

+ 0
- 1
examples/CMakeLists.txt View File

@@ -51,7 +51,6 @@ function(_juce_add_pips)
endfunction()

add_subdirectory(Audio)
add_subdirectory(BLOCKS)
add_subdirectory(DSP)
add_subdirectory(GUI)
add_subdirectory(Plugins)


+ 1
- 2
examples/DemoRunner/CMakeLists.txt View File

@@ -65,7 +65,6 @@ target_compile_definitions(DemoRunner PRIVATE
target_link_libraries(DemoRunner PRIVATE
juce::juce_analytics
juce::juce_audio_utils
juce::juce_blocks_basics
juce::juce_box2d
juce::juce_dsp
juce::juce_opengl
@@ -76,6 +75,6 @@ target_link_libraries(DemoRunner PRIVATE
juce::juce_recommended_lto_flags
juce::juce_recommended_warning_flags)

foreach(folder IN ITEMS ../Assets ../Audio ../BLOCKS ../DSP ../GUI ../Utilities)
foreach(folder IN ITEMS ../Assets ../Audio ../DSP ../GUI ../Utilities)
juce_add_bundle_resources_directory(DemoRunner ${folder})
endforeach()

+ 0
- 8
examples/DemoRunner/Source/Demos/DemoPIPs1.cpp View File

@@ -42,10 +42,6 @@
#include "../../../Audio/PluckedStringsDemo.h"
#include "../../../Audio/SimpleFFTDemo.h"
#include "../../../BLOCKS/BlocksDrawingDemo.h"
#include "../../../BLOCKS/BlocksMonitorDemo.h"
#include "../../../BLOCKS/BlocksSynthDemo.h"
#include "../../../DSP/ConvolutionDemo.h"
#include "../../../DSP/FIRFilterDemo.h"
#include "../../../DSP/GainDemo.h"
@@ -85,11 +81,7 @@ void registerDemos_One() noexcept
REGISTER_DEMO (MidiDemo, Audio, false)
REGISTER_DEMO (MPEDemo, Audio, false)
REGISTER_DEMO (PluckedStringsDemo, Audio, false)
REGISTER_DEMO (SimpleFFTDemo, Audio, false)
REGISTER_DEMO (BlocksDrawingDemo, BLOCKS, false)
REGISTER_DEMO (BlocksMonitorDemo, BLOCKS, false)
REGISTER_DEMO (BlocksSynthDemo, BLOCKS, false)
REGISTER_DEMO (ConvolutionDemo, DSP, false)
REGISTER_DEMO (FIRFilterDemo, DSP, false)


+ 0
- 24
extras/BLOCKS/Makefile View File

@@ -1,24 +0,0 @@
# Execute this Makefile in an empty directory to create the contents of the
# standalone SDK repository.

ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))

JUCE_MODULES := juce_audio_basics juce_audio_devices juce_blocks_basics juce_core juce_events
SDK_JUCE_MODULES := $(addprefix SDK/,$(JUCE_MODULES))

FILES := $(wildcard $(ROOT_DIR)/standalone_sdk/*)
SDK_FILES := $(notdir $(FILES))

all: $(SDK_FILES) $(SDK_JUCE_MODULES)

# Create $(SDK_JUCE_MODULES)
SDK/%: $(ROOT_DIR)/../../modules/%
rm -rf $@
cp -r $< $@

$(SDK_JUCE_MODULES): $(SDK_FILES)

# Create $(SDK_FILES)
%: $(ROOT_DIR)/standalone_sdk/%
rm -rf $@
cp -r $< $@

+ 0
- 68
extras/BLOCKS/standalone_sdk/README.md View File

@@ -1,68 +0,0 @@
# The BLOCKS SDK

## Overview

This repository contains the source code of the BLOCKS SDK, which is licensed under the permissive ISC license.

Much more comprehensive documentation can be found at http://developer.roli.com/documentation/the_standalone_blocks_sdk.html.

The `SDK` directory contains the source code required to compile a library containing the BLOCKS SDK functionality. In the `SDK/Build` directory you will find an Xcode project, a Visual Studio solution and a Linux Makefile which are configured to create a static library on the macOS, Windows and Linux platforms respectively. Simply use the one appropriate for your system. Once you have created this library you can access BLOCKS functionality by linking this library into your application and `#include`-ing the provided header file, `BlocksHeader.h`.

The `examples` directory contains some sample code which uses the BLOCKS SDK library. Here we again provide an Xcode project, a Visual Studio solution and a Linux Makefile to allow the examples to be built on the corresponding platform. The Xcode project and Visual Studio solution compile the BLOCKS SDK library automatically, whereas the Linux Makefile requires the BLOCKS SDK library to be manually built first.

## Quick start guide

### macOS with Xcode

Open `examples/BlockFinder/MacOS/BlockFinder.xcodeproj` with Xcode and compile and execute the example application. This will both create the BLOCKS SDK library and provide a very simple demonstration of how to use it. Use this as a base to develop your own BLOCKS creation!

### Windows with Visual Studio

Use the same procedure as macOS, but instead open `examples/BlockFinder/Windows/BlockFinder.vcxproj`.

### Linux

Use the following procedure to compile the demo app:

```shell
cd SDK/Build/Linux
make
cd ../../../examples/BlockFinder/Linux/
make
```

This will produce the executable `BlockFinder` in the `Debug` directory.

## Using the SDK library

### Compiling the library

The source code for the BLOCKS SDK library is contained within the `SDK` directory of this repository. Here you will find header files that you can include in your own projects and the `Build` subdirectory contains an Xcode project, a Visual Studio project and a Linux Makefile for compiling the SDK source code into a static library. Use the appropriate choice for your platform, select either the "Debug" or "Release" configuration, and build the project.

For macOS this will produce `libBLOCKS-SDK.a` in either the `SDK/Build/MacOS/Debug/` or `SDK/Build/MacOS/Release/` directory, for Linux this will produce `libBLOCKS-SDK.a` in either the `SDK/Build/Linux/Debug/` or `SDK/Build/Linux/Release/` directory, and for Windows this will produce `BLOCKS-SDK.lib` in either the `SDK\Build\Windows\x64\Debug` or `SDK\Build\Windows\x64\Release` folder.

### Using the SDK header file

To use BLOCKS classes and functions in your application you must include the `BlocksHeader.h` file in your source code. You also need to tell the compiler to look in the `SDK` directory for additional header files, which you can configure inside your Xcode or Visual Studio project. If you are using the command line to compile your application then you can see an example of how to do this in `examples/BlockFinder/Linux/Makefile` or `examples/BlockFinder/MacOS/Makefile`.

### Linking against the SDK library

You must also tell your compiler where to find the SDK static library before your BLOCKS application will compile, and include all of the dependencies for your platform, which are listed in the Dependencies section of this README. Again, this is configured in your Xcode or Visual Studio project, but if you are using the command line you can see an example of how to do this in `examples/BlockFinder/Linux/Makefile` or `examples/BlockFinder/MacOS/Makefile`.

## Dependencies

- A C++11 compatible compiler

### macOS frameworks

- Accelerate
- AudioToolbox
- CoreAudio
- CoreMIDI

### Linux packages

- x11
- alsa
- libcurl


+ 0
- 15
extras/BLOCKS/standalone_sdk/SDK/BlocksHeader.h View File

@@ -1,15 +0,0 @@
#pragma once
#ifndef JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED
#define JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED 1
#endif
#ifndef JUCE_DONT_DECLARE_PROJECTINFO
#define JUCE_DONT_DECLARE_PROJECTINFO 1
#endif
#include <juce_audio_basics/juce_audio_basics.h>
#include <juce_audio_devices/juce_audio_devices.h>
#include <juce_blocks_basics/juce_blocks_basics.h>
#include <juce_core/juce_core.h>
#include <juce_events/juce_events.h>

+ 0
- 46
extras/BLOCKS/standalone_sdk/SDK/Build/Linux/Makefile View File

@@ -1,46 +0,0 @@
ifndef AR
AR := ar
endif
ifndef CONFIG
CONFIG := Debug
endif
TARGET := libBLOCKS-SDK.a
JUCE_OUTDIR := $(CONFIG)
JUCE_OBJDIR := build/$(CONFIG)
JUCE_INCLUDES := -I../..
JUCE_SDKDEFINES := -DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1 -DJUCE_STANDALONE_APPLICATION=1
JUCE_CXXFLAGS = -std=c++11 $(DEPFLAGS) -march=native -DLINUX=1 $(JUCE_SDKDEFINES) $(shell pkg-config --cflags alsa freetype2 libcurl x11 xext xinerama) -pthread $(JUCE_INCLUDES)
ifeq ($(CONFIG),Debug)
JUCE_CXXFLAGS += -DDEBUG=1 -D_DEBUG=1 -g -ggdb -O0
endif
ifeq ($(CONFIG),Release)
JUCE_CXXFLAGS += -DNDEBUG=1 -Os
endif
JUCE_MODULES := juce_audio_basics juce_audio_devices juce_blocks_basics juce_core juce_events
JUCE_SOURCE := $(foreach MODULE_NAME,$(JUCE_MODULES),../../$(MODULE_NAME)/$(MODULE_NAME).cpp)
JUCE_OBJECTS := $(foreach MODULE_NAME,$(JUCE_MODULES),$(JUCE_OBJDIR)/$(MODULE_NAME)/$(MODULE_NAME).o)
.PHONY: clean
$(JUCE_OUTDIR)/$(TARGET): check-pkg-config $(JUCE_OBJECTS)
@mkdir -p $(JUCE_OUTDIR)
$(AR) -cr $@ $(JUCE_OBJECTS)
check-pkg-config:
@command -v pkg-config >/dev/null 2>&1 || { echo >&2 "pkg-config not installed. Please, install it."; exit 1; }
@pkg-config --print-errors alsa freetype2 libcurl x11 xext xinerama
clean:
rm -rf $(JUCE_OUTDIR)/$(TARGET) $(JUCE_OBJDIR)
$(JUCE_OBJDIR)/%.o: ../../%.cpp
-@mkdir -p $(dir $@)
$(CXX) $(JUCE_CXXFLAGS) -o "$@" -c "$<"

+ 0
- 299
extras/BLOCKS/standalone_sdk/SDK/Build/MacOS/BLOCKS-SDK.xcodeproj/project.pbxproj View File

@@ -1,299 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {

/* Begin PBXBuildFile section */
E2F016B31DB93C320054ECA4 /* juce_audio_basics.mm in Sources */ = {isa = PBXBuildFile; fileRef = E2F016B21DB93C320054ECA4 /* juce_audio_basics.mm */; };
E2F016B51DB93C3D0054ECA4 /* juce_audio_devices.mm in Sources */ = {isa = PBXBuildFile; fileRef = E2F016B41DB93C3D0054ECA4 /* juce_audio_devices.mm */; };
E2F016B71DB93C480054ECA4 /* juce_blocks_basics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E2F016B61DB93C480054ECA4 /* juce_blocks_basics.cpp */; };
E2F016B91DB93C540054ECA4 /* juce_core.mm in Sources */ = {isa = PBXBuildFile; fileRef = E2F016B81DB93C540054ECA4 /* juce_core.mm */; };
E2F016BB1DB93C5C0054ECA4 /* juce_events.mm in Sources */ = {isa = PBXBuildFile; fileRef = E2F016BA1DB93C5C0054ECA4 /* juce_events.mm */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
E2BD1B8A1DB62B4000C4F301 /* libBLOCKS-SDK.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libBLOCKS-SDK.a"; sourceTree = BUILT_PRODUCTS_DIR; };
E2F016B21DB93C320054ECA4 /* juce_audio_basics.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = juce_audio_basics.mm; path = ../../juce_audio_basics/juce_audio_basics.mm; sourceTree = "<group>"; };
E2F016B41DB93C3D0054ECA4 /* juce_audio_devices.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = juce_audio_devices.mm; path = ../../juce_audio_devices/juce_audio_devices.mm; sourceTree = "<group>"; };
E2F016B61DB93C480054ECA4 /* juce_blocks_basics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = juce_blocks_basics.cpp; path = ../../juce_blocks_basics/juce_blocks_basics.cpp; sourceTree = "<group>"; };
E2F016B81DB93C540054ECA4 /* juce_core.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = juce_core.mm; path = ../../juce_core/juce_core.mm; sourceTree = "<group>"; };
E2F016BA1DB93C5C0054ECA4 /* juce_events.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = juce_events.mm; path = ../../juce_events/juce_events.mm; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
E2BD1B871DB62B4000C4F301 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
E2BD1B811DB62B4000C4F301 = {
isa = PBXGroup;
children = (
E2F016B21DB93C320054ECA4 /* juce_audio_basics.mm */,
E2F016B41DB93C3D0054ECA4 /* juce_audio_devices.mm */,
E2F016B61DB93C480054ECA4 /* juce_blocks_basics.cpp */,
E2F016B81DB93C540054ECA4 /* juce_core.mm */,
E2F016BA1DB93C5C0054ECA4 /* juce_events.mm */,
E2BD1B8B1DB62B4000C4F301 /* Products */,
);
sourceTree = "<group>";
};
E2BD1B8B1DB62B4000C4F301 /* Products */ = {
isa = PBXGroup;
children = (
E2BD1B8A1DB62B4000C4F301 /* libBLOCKS-SDK.a */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section */
E2BD1B881DB62B4000C4F301 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */

/* Begin PBXNativeTarget section */
E2BD1B891DB62B4000C4F301 /* BLOCKS-SDK */ = {
isa = PBXNativeTarget;
buildConfigurationList = E2BD1B8E1DB62B4000C4F301 /* Build configuration list for PBXNativeTarget "BLOCKS-SDK" */;
buildPhases = (
E2BD1B861DB62B4000C4F301 /* Sources */,
E2BD1B871DB62B4000C4F301 /* Frameworks */,
E2BD1B881DB62B4000C4F301 /* Headers */,
E2ECDF741DB62D7E006D016F /* ShellScript */,
);
buildRules = (
);
dependencies = (
);
name = "BLOCKS-SDK";
productName = "BLOCKS-SDK";
productReference = E2BD1B8A1DB62B4000C4F301 /* libBLOCKS-SDK.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
E2BD1B821DB62B4000C4F301 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0800;
ORGANIZATIONNAME = "ROLI Ltd.";
TargetAttributes = {
E2BD1B891DB62B4000C4F301 = {
CreatedOnToolsVersion = 8.0;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = E2BD1B851DB62B4000C4F301 /* Build configuration list for PBXProject "BLOCKS-SDK" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = E2BD1B811DB62B4000C4F301;
productRefGroup = E2BD1B8B1DB62B4000C4F301 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
E2BD1B891DB62B4000C4F301 /* BLOCKS-SDK */,
);
};
/* End PBXProject section */

/* Begin PBXShellScriptBuildPhase section */
E2ECDF741DB62D7E006D016F /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "mkdir -p ${PROJECT_DIR}/${CONFIGURATION}\ncp ${BUILT_PRODUCTS_DIR}/${EXECUTABLE_NAME} ${PROJECT_DIR}/${CONFIGURATION}";
};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
E2BD1B861DB62B4000C4F301 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E2F016B31DB93C320054ECA4 /* juce_audio_basics.mm in Sources */,
E2F016B51DB93C3D0054ECA4 /* juce_audio_devices.mm in Sources */,
E2F016B71DB93C480054ECA4 /* juce_blocks_basics.cpp in Sources */,
E2F016B91DB93C540054ECA4 /* juce_core.mm in Sources */,
E2F016BB1DB93C5C0054ECA4 /* juce_events.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */

/* Begin XCBuildConfiguration section */
E2BD1B8C1DB62B4000C4F301 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
E2BD1B8D1DB62B4000C4F301 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
};
name = Release;
};
E2BD1B8F1DB62B4000C4F301 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
EXECUTABLE_PREFIX = lib;
GCC_PREPROCESSOR_DEFINITIONS = (
"JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1",
"JUCE_STANDALONE_APPLICATION=1",
"DEBUG=1",
);
GCC_WARN_UNDECLARED_SELECTOR = NO;
HEADER_SEARCH_PATHS = ../..;
PRODUCT_NAME = "$(TARGET_NAME)";
USER_HEADER_SEARCH_PATHS = "";
};
name = Debug;
};
E2BD1B901DB62B4000C4F301 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
EXECUTABLE_PREFIX = lib;
GCC_PREPROCESSOR_DEFINITIONS = (
"JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1",
"JUCE_STANDALONE_APPLICATION=1",
"NDEBUG=1",
);
GCC_WARN_UNDECLARED_SELECTOR = NO;
HEADER_SEARCH_PATHS = ../..;
PRODUCT_NAME = "$(TARGET_NAME)";
USER_HEADER_SEARCH_PATHS = "";
};
name = Release;
};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
E2BD1B851DB62B4000C4F301 /* Build configuration list for PBXProject "BLOCKS-SDK" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E2BD1B8C1DB62B4000C4F301 /* Debug */,
E2BD1B8D1DB62B4000C4F301 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
E2BD1B8E1DB62B4000C4F301 /* Build configuration list for PBXNativeTarget "BLOCKS-SDK" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E2BD1B8F1DB62B4000C4F301 /* Debug */,
E2BD1B901DB62B4000C4F301 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = E2BD1B821DB62B4000C4F301 /* Project object */;
}

+ 0
- 28
extras/BLOCKS/standalone_sdk/SDK/Build/Windows/BLOCKS-SDK.sln View File

@@ -1,28 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BLOCKS-SDK", "BLOCKS-SDK.vcxproj", "{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Debug|x64.ActiveCfg = Debug|x64
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Debug|x64.Build.0 = Debug|x64
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Debug|x86.ActiveCfg = Debug|Win32
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Debug|x86.Build.0 = Debug|Win32
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Release|x64.ActiveCfg = Release|x64
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Release|x64.Build.0 = Release|x64
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Release|x86.ActiveCfg = Release|Win32
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

+ 0
- 150
extras/BLOCKS/standalone_sdk/SDK/Build/Windows/BLOCKS-SDK.vcxproj View File

@@ -1,150 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\BlocksHeader.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\juce_audio_basics\juce_audio_basics.cpp" />
<ClCompile Include="..\..\juce_audio_devices\juce_audio_devices.cpp" />
<ClCompile Include="..\..\juce_blocks_basics\juce_blocks_basics.cpp" />
<ClCompile Include="..\..\juce_core\juce_core.cpp" />
<ClCompile Include="..\..\juce_events\juce_events.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>BLOCKSSDK</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_STANDALONE_APPLICATION=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_STANDALONE_APPLICATION=1;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_STANDALONE_APPLICATION=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_STANDALONE_APPLICATION=1;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

+ 0
- 49
extras/BLOCKS/standalone_sdk/examples/BlockFinder/BlockFinder.cpp View File

@@ -1,49 +0,0 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#include "BlockFinder.h"
using namespace juce;
BlockFinder::BlockFinder()
{
// Register to receive topologyChanged() callbacks from pts.
pts.addListener (this);
}
void BlockFinder::topologyChanged()
{
// We have a new topology, so find out what it isand store it in a local
// variable.
auto currentTopology = pts.getCurrentTopology();
Logger::writeToLog ("\nNew BLOCKS topology.");
// The blocks member of a BlockTopology contains an array of blocks. Here we
// loop over them and print some information.
Logger::writeToLog ("Detected " + String (currentTopology.blocks.size()) + " blocks:");
for (auto& block : currentTopology.blocks)
{
Logger::writeToLog ("");
Logger::writeToLog (" Description: " + block->getDeviceDescription());
Logger::writeToLog (" Battery level: " + String (block->getBatteryLevel()));
Logger::writeToLog (" UID: " + String (block->uid));
Logger::writeToLog (" Serial number: " + block->serialNumber);
}
}

+ 0
- 39
extras/BLOCKS/standalone_sdk/examples/BlockFinder/BlockFinder.h View File

@@ -1,39 +0,0 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#pragma once
#include <BlocksHeader.h>
// Monitors a PhysicalTopologySource for changes to the connected BLOCKS and
// prints some information about the BLOCKS that are available.
class BlockFinder : private juce::TopologySource::Listener
{
public:
// Register as a listener to the PhysicalTopologySource, so that we receive
// callbacks in topologyChanged().
BlockFinder();
private:
// Called by the PhysicalTopologySource when the BLOCKS topology changes.
void topologyChanged() override;
// The PhysicalTopologySource member variable which reports BLOCKS changes.
juce::PhysicalTopologySource pts;
};

+ 0
- 72
extras/BLOCKS/standalone_sdk/examples/BlockFinder/Linux/Makefile View File

@@ -1,72 +0,0 @@
ifeq ($(shell uname),Darwin)
PLATFORM = MacOS
else
PLATFORM = Linux
endif

# C++ compiler.
CXX := g++ -std=c++11

# Build configuration (Debug or Release).
ifndef CONFIG
CONFIG := Debug
endif
ifeq ($(CONFIG),Debug)
CXXFLAGS += -O0 -g -DDEBUG=1 -D_DEBUG=1
else
CXXFLAGS += -O3 -DNDEBUG
endif

# The name of your application.
APP_NAME := BlockFinder

# The path to the modules directory in the BLOCKS SDK directory.
SDK_PATH := ../../../SDK

# The path to temporary build files.
OBJECT_DIR := build/$(CONFIG)

# The path to the compiled BLOCKSSDK library.
BLOCKS_LIBRARY := $(SDK_PATH)/Build/$(PLATFORM)/$(CONFIG)/libBLOCKS-SDK.a

# The source code for this application.
SOURCE_FILES := $(wildcard ../*.cpp) $(foreach EXT,.cpp .mm,$(wildcard *$(EXT)))

# Make a list of object files from .cpp files.
SOURCE_OBJ := $(addprefix $(OBJECT_DIR)/,$(notdir $(addsuffix .o,$(basename $(SOURCE_FILES)))))

# Header include paths (prefix with -I).
INCLUDES := -I$(SDK_PATH)

# Frameworks and libraries.
ifeq ($(PLATFORM),MacOS)
LIBS := -framework Accelerate -framework AudioToolbox -framework Carbon -framework Cocoa -framework CoreAudio -framework CoreMIDI -framework IOKit -framework OpenGL -framework QuartzCore
else
LIBS := -L/usr/X11R6/lib/ $(shell pkg-config --libs alsa libcurl x11) -ldl -lpthread -lrt
CXXFLAGS += -DLINUX=1
endif

##############################################################################
# Build rules #
##############################################################################

.PHONEY: clean

$(CONFIG)/$(APP_NAME): $(SOURCE_OBJ) $(BLOCKS_LIBRARY)
@mkdir -p $(dir $@)
$(CXX) $^ -o $@ $(LIBS)

$(OBJECT_DIR)/%.o: ../%.cpp
@mkdir -p $(dir $@)
$(CXX) $(CXXFLAGS) $(INCLUDES) -o $@ -c $<

$(OBJECT_DIR)/%.o: %.cpp
@mkdir -p $(dir $@)
$(CXX) $(CXXFLAGS) $(INCLUDES) -o $@ -c $<

$(OBJECT_DIR)/%.o: %.mm
@mkdir -p $(dir $@)
$(CXX) $(CXXFLAGS) $(INCLUDES) -o $@ -c $<

clean:
rm -rf $(CONFIG) $(OBJECT_DIR)

+ 0
- 49
extras/BLOCKS/standalone_sdk/examples/BlockFinder/Linux/main.cpp View File

@@ -1,49 +0,0 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#include "../BlockFinder.h"
// A simple JUCE app containing our BlockFinder. This is a quick way of
// setting up an event loop so we can receive Block topology change events.
class MyJUCEApp : public juce::JUCEApplicationBase
{
public:
MyJUCEApp() {}
~MyJUCEApp() {}
void initialise (const juce::String&) override {}
void shutdown() override {}
const juce::String getApplicationName() override { return "BlockFinder"; }
const juce::String getApplicationVersion() override { return "1.0.0"; }
bool moreThanOneInstanceAllowed() override { return true; }
void anotherInstanceStarted (const juce::String&) override {}
void suspended() override {}
void resumed() override {}
void systemRequestedQuit() override {}
void unhandledException(const std::exception*, const juce::String&,
int lineNumber) override {}
private:
// Our BLOCKS class.
BlockFinder finder;
};
START_JUCE_APPLICATION (MyJUCEApp)

+ 0
- 368
extras/BLOCKS/standalone_sdk/examples/BlockFinder/MacOS/BlockFinder.xcodeproj/project.pbxproj View File

@@ -1,368 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {

/* Begin PBXBuildFile section */
E232CCB11DB8CCEA00276BB3 /* BlockFinder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E232CCAF1DB8CCEA00276BB3 /* BlockFinder.cpp */; };
E242E7661DB77E9D0022D78D /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = E242E7651DB77E9D0022D78D /* main.mm */; };
E2E162A21DBA6DF60049A6D5 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2ECDFC41DB6864E006D016F /* Cocoa.framework */; };
E2E162A41DBA6F160049A6D5 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2ECDFC21DB6864E006D016F /* AudioToolbox.framework */; };
E2ECDFC01DB685D2006D016F /* libBLOCKS-SDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E2ECDFBF1DB685D2006D016F /* libBLOCKS-SDK.a */; };
E2ECDFCA1DB6864E006D016F /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2ECDFC11DB6864E006D016F /* Accelerate.framework */; };
E2ECDFCE1DB6864E006D016F /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2ECDFC51DB6864E006D016F /* CoreAudio.framework */; };
E2ECDFCF1DB6864E006D016F /* CoreMIDI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2ECDFC61DB6864E006D016F /* CoreMIDI.framework */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
E25109931E5376C000596664 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = E251098F1E5376C000596664 /* BLOCKS-SDK.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = E2BD1B8A1DB62B4000C4F301;
remoteInfo = "BLOCKS-SDK";
};
E25109961E53775E00596664 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = E251098F1E5376C000596664 /* BLOCKS-SDK.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = E2BD1B891DB62B4000C4F301;
remoteInfo = "BLOCKS-SDK";
};
/* End PBXContainerItemProxy section */

/* Begin PBXCopyFilesBuildPhase section */
E2ECDFB01DB684F9006D016F /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
E232CCAF1DB8CCEA00276BB3 /* BlockFinder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BlockFinder.cpp; path = ../BlockFinder.cpp; sourceTree = "<group>"; };
E232CCB01DB8CCEA00276BB3 /* BlockFinder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BlockFinder.h; path = ../BlockFinder.h; sourceTree = "<group>"; };
E242E7651DB77E9D0022D78D /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
E251098F1E5376C000596664 /* BLOCKS-SDK.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "BLOCKS-SDK.xcodeproj"; path = "../../../SDK/Build/MacOS/BLOCKS-SDK.xcodeproj"; sourceTree = "<group>"; };
E2ECDFB21DB684F9006D016F /* BlockFinder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = BlockFinder; sourceTree = BUILT_PRODUCTS_DIR; };
E2ECDFBF1DB685D2006D016F /* libBLOCKS-SDK.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libBLOCKS-SDK.a"; path = "../../../../SDK/Build/MacOS/Debug/libBLOCKS-SDK.a"; sourceTree = "<group>"; };
E2ECDFC11DB6864E006D016F /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
E2ECDFC21DB6864E006D016F /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
E2ECDFC31DB6864E006D016F /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
E2ECDFC41DB6864E006D016F /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
E2ECDFC51DB6864E006D016F /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
E2ECDFC61DB6864E006D016F /* CoreMIDI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMIDI.framework; path = System/Library/Frameworks/CoreMIDI.framework; sourceTree = SDKROOT; };
E2ECDFC71DB6864E006D016F /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
E2ECDFC81DB6864E006D016F /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
E2ECDFC91DB6864E006D016F /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
E2ECDFAF1DB684F9006D016F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E2E162A41DBA6F160049A6D5 /* AudioToolbox.framework in Frameworks */,
E2E162A21DBA6DF60049A6D5 /* Cocoa.framework in Frameworks */,
E2ECDFCA1DB6864E006D016F /* Accelerate.framework in Frameworks */,
E2ECDFCE1DB6864E006D016F /* CoreAudio.framework in Frameworks */,
E2ECDFCF1DB6864E006D016F /* CoreMIDI.framework in Frameworks */,
E2ECDFC01DB685D2006D016F /* libBLOCKS-SDK.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
E25109901E5376C000596664 /* Products */ = {
isa = PBXGroup;
children = (
E25109941E5376C000596664 /* libBLOCKS-SDK.a */,
);
name = Products;
sourceTree = "<group>";
};
E2ECDFA91DB684F9006D016F = {
isa = PBXGroup;
children = (
E251098F1E5376C000596664 /* BLOCKS-SDK.xcodeproj */,
E232CCAF1DB8CCEA00276BB3 /* BlockFinder.cpp */,
E232CCB01DB8CCEA00276BB3 /* BlockFinder.h */,
E242E7651DB77E9D0022D78D /* main.mm */,
E2ECDFB31DB684F9006D016F /* Products */,
E2ECDFBE1DB685D2006D016F /* Frameworks */,
);
sourceTree = "<group>";
};
E2ECDFB31DB684F9006D016F /* Products */ = {
isa = PBXGroup;
children = (
E2ECDFB21DB684F9006D016F /* BlockFinder */,
);
name = Products;
sourceTree = "<group>";
};
E2ECDFBE1DB685D2006D016F /* Frameworks */ = {
isa = PBXGroup;
children = (
E2ECDFC11DB6864E006D016F /* Accelerate.framework */,
E2ECDFC21DB6864E006D016F /* AudioToolbox.framework */,
E2ECDFC31DB6864E006D016F /* Carbon.framework */,
E2ECDFC41DB6864E006D016F /* Cocoa.framework */,
E2ECDFC51DB6864E006D016F /* CoreAudio.framework */,
E2ECDFC61DB6864E006D016F /* CoreMIDI.framework */,
E2ECDFC71DB6864E006D016F /* IOKit.framework */,
E2ECDFC81DB6864E006D016F /* OpenGL.framework */,
E2ECDFC91DB6864E006D016F /* QuartzCore.framework */,
E2ECDFBF1DB685D2006D016F /* libBLOCKS-SDK.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
E2ECDFB11DB684F9006D016F /* BlockFinder */ = {
isa = PBXNativeTarget;
buildConfigurationList = E2ECDFB91DB684F9006D016F /* Build configuration list for PBXNativeTarget "BlockFinder" */;
buildPhases = (
E2ECDFAE1DB684F9006D016F /* Sources */,
E2ECDFAF1DB684F9006D016F /* Frameworks */,
E2ECDFB01DB684F9006D016F /* CopyFiles */,
E2ECDFD31DB6872A006D016F /* ShellScript */,
);
buildRules = (
);
dependencies = (
E25109971E53775E00596664 /* PBXTargetDependency */,
);
name = BlockFinder;
productName = BlocksMonitor;
productReference = E2ECDFB21DB684F9006D016F /* BlockFinder */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
E2ECDFAA1DB684F9006D016F /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0800;
ORGANIZATIONNAME = "ROLI Ltd.";
TargetAttributes = {
E2ECDFB11DB684F9006D016F = {
CreatedOnToolsVersion = 8.0;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = E2ECDFAD1DB684F9006D016F /* Build configuration list for PBXProject "BlockFinder" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = E2ECDFA91DB684F9006D016F;
productRefGroup = E2ECDFB31DB684F9006D016F /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = E25109901E5376C000596664 /* Products */;
ProjectRef = E251098F1E5376C000596664 /* BLOCKS-SDK.xcodeproj */;
},
);
projectRoot = "";
targets = (
E2ECDFB11DB684F9006D016F /* BlockFinder */,
);
};
/* End PBXProject section */

/* Begin PBXReferenceProxy section */
E25109941E5376C000596664 /* libBLOCKS-SDK.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libBLOCKS-SDK.a";
remoteRef = E25109931E5376C000596664 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */

/* Begin PBXShellScriptBuildPhase section */
E2ECDFD31DB6872A006D016F /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "mkdir -p ${PROJECT_DIR}/${CONFIGURATION}\ncp ${BUILT_PRODUCTS_DIR}/${EXECUTABLE_NAME} ${PROJECT_DIR}/${CONFIGURATION}";
};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
E2ECDFAE1DB684F9006D016F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E242E7661DB77E9D0022D78D /* main.mm in Sources */,
E232CCB11DB8CCEA00276BB3 /* BlockFinder.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
E25109971E53775E00596664 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = "BLOCKS-SDK";
targetProxy = E25109961E53775E00596664 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */

/* Begin XCBuildConfiguration section */
E2ECDFB71DB684F9006D016F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
E2ECDFB81DB684F9006D016F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
};
name = Release;
};
E2ECDFBA1DB684F9006D016F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
HEADER_SEARCH_PATHS = ../../../SDK;
LIBRARY_SEARCH_PATHS = "../../../SDK/Build/MacOS/${CONFIGURATION}";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
E2ECDFBB1DB684F9006D016F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
GCC_PREPROCESSOR_DEFINITIONS = "NDEBUG=1";
HEADER_SEARCH_PATHS = ../../../SDK;
LIBRARY_SEARCH_PATHS = "../../../SDK/Build/MacOS/${CONFIGURATION}";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
E2ECDFAD1DB684F9006D016F /* Build configuration list for PBXProject "BlockFinder" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E2ECDFB71DB684F9006D016F /* Debug */,
E2ECDFB81DB684F9006D016F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
E2ECDFB91DB684F9006D016F /* Build configuration list for PBXNativeTarget "BlockFinder" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E2ECDFBA1DB684F9006D016F /* Debug */,
E2ECDFBB1DB684F9006D016F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = E2ECDFAA1DB684F9006D016F /* Project object */;
}

+ 0
- 72
extras/BLOCKS/standalone_sdk/examples/BlockFinder/MacOS/Makefile View File

@@ -1,72 +0,0 @@
ifeq ($(shell uname),Darwin)
PLATFORM = MacOS
else
PLATFORM = Linux
endif

# C++ compiler.
CXX := g++ -std=c++11

# Build configuration (Debug or Release).
ifndef CONFIG
CONFIG := Debug
endif
ifeq ($(CONFIG),Debug)
CXXFLAGS += -O0 -g -DDEBUG=1 -D_DEBUG=1
else
CXXFLAGS += -O3 -DNDEBUG
endif

# The name of your application.
APP_NAME := BlockFinder

# The path to the modules directory in the BLOCKS SDK directory.
SDK_PATH := ../../../SDK

# The path to temporary build files.
OBJECT_DIR := build/$(CONFIG)

# The path to the compiled BLOCKSSDK library.
BLOCKS_LIBRARY := $(SDK_PATH)/Build/$(PLATFORM)/$(CONFIG)/libBLOCKS-SDK.a

# The source code for this application.
SOURCE_FILES := $(wildcard ../*.cpp) $(foreach EXT,.cpp .mm,$(wildcard *$(EXT)))

# Make a list of object files from .cpp files.
SOURCE_OBJ := $(addprefix $(OBJECT_DIR)/,$(notdir $(addsuffix .o,$(basename $(SOURCE_FILES)))))

# Header include paths (prefix with -I).
INCLUDES := -I$(SDK_PATH)

# Frameworks and libraries.
ifeq ($(PLATFORM),MacOS)
LIBS := -framework Accelerate -framework AudioToolbox -framework Carbon -framework Cocoa -framework CoreAudio -framework CoreMIDI -framework IOKit -framework OpenGL -framework QuartzCore
else
LIBS := -L/usr/X11R6/lib/ $(shell pkg-config --libs alsa libcurl x11) -ldl -lpthread -lrt
CXXFLAGS += -DLINUX=1
endif

##############################################################################
# Build rules #
##############################################################################

.PHONEY: clean

$(CONFIG)/$(APP_NAME): $(SOURCE_OBJ) $(BLOCKS_LIBRARY)
@mkdir -p $(dir $@)
$(CXX) $^ -o $@ $(LIBS)

$(OBJECT_DIR)/%.o: ../%.cpp
@mkdir -p $(dir $@)
$(CXX) $(CXXFLAGS) $(INCLUDES) -o $@ -c $<

$(OBJECT_DIR)/%.o: %.cpp
@mkdir -p $(dir $@)
$(CXX) $(CXXFLAGS) $(INCLUDES) -o $@ -c $<

$(OBJECT_DIR)/%.o: %.mm
@mkdir -p $(dir $@)
$(CXX) $(CXXFLAGS) $(INCLUDES) -o $@ -c $<

clean:
rm -rf $(CONFIG) $(OBJECT_DIR)

+ 0
- 36
extras/BLOCKS/standalone_sdk/examples/BlockFinder/MacOS/main.mm View File

@@ -1,36 +0,0 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#include "../BlockFinder.h"
#import <Cocoa/Cocoa.h>
int main (int argc, const char * argv[])
{
// Initialise the JUCE code.
juce::ScopedJuceInitialiser_GUI platform;
// Create our JUCE object.
BlockFinder finder;
// Run an event loop.
while ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
return 0;
}

+ 0
- 41
extras/BLOCKS/standalone_sdk/examples/BlockFinder/Windows/BlockFinder.sln View File

@@ -1,41 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlockFinder", "BlockFinder.vcxproj", "{881F9AC3-2B1A-4867-92AF-125332B5A87A}"
ProjectSection(ProjectDependencies) = postProject
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B} = {6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BLOCKS-SDK", "..\..\..\SDK\Build\Windows\BLOCKS-SDK.vcxproj", "{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{881F9AC3-2B1A-4867-92AF-125332B5A87A}.Debug|x64.ActiveCfg = Debug|x64
{881F9AC3-2B1A-4867-92AF-125332B5A87A}.Debug|x64.Build.0 = Debug|x64
{881F9AC3-2B1A-4867-92AF-125332B5A87A}.Debug|x86.ActiveCfg = Debug|Win32
{881F9AC3-2B1A-4867-92AF-125332B5A87A}.Debug|x86.Build.0 = Debug|Win32
{881F9AC3-2B1A-4867-92AF-125332B5A87A}.Release|x64.ActiveCfg = Release|x64
{881F9AC3-2B1A-4867-92AF-125332B5A87A}.Release|x64.Build.0 = Release|x64
{881F9AC3-2B1A-4867-92AF-125332B5A87A}.Release|x86.ActiveCfg = Release|Win32
{881F9AC3-2B1A-4867-92AF-125332B5A87A}.Release|x86.Build.0 = Release|Win32
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Debug|x64.ActiveCfg = Debug|x64
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Debug|x64.Build.0 = Debug|x64
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Debug|x86.ActiveCfg = Debug|Win32
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Debug|x86.Build.0 = Debug|Win32
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Release|x64.ActiveCfg = Release|x64
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Release|x64.Build.0 = Release|x64
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Release|x86.ActiveCfg = Release|Win32
{6EE7FF54-2573-4A5F-BF4A-9654DC2C580B}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

+ 0
- 172
extras/BLOCKS/standalone_sdk/examples/BlockFinder/Windows/BlockFinder.vcxproj View File

@@ -1,172 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{881F9AC3-2B1A-4867-92AF-125332B5A87A}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>BlockFinder</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)\$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)\$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)\$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)\$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\..\SDK</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>BLOCKS-SDK.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(IntDir)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\..\SDK</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>BLOCKS-SDK.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(IntDir)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\..\SDK</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>BLOCKS-SDK.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\SDK\Build\Windows\$(Configuration)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\..\SDK</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>BLOCKS-SDK.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\SDK\Build\Windows\x64\$(Configuration)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\BlockFinder.cpp" />
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\BlockFinder.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

+ 0
- 49
extras/BLOCKS/standalone_sdk/examples/BlockFinder/Windows/main.cpp View File

@@ -1,49 +0,0 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#include "../BlockFinder.h"
// A simple JUCE app containing our BlockFinder. This is a quick way of
// setting up an event loop so we can receive Block topology change events.
class MyJUCEApp : public juce::JUCEApplicationBase
{
public:
MyJUCEApp() {}
~MyJUCEApp() {}
void initialise (const juce::String&) override {}
void shutdown() override {}
const juce::String getApplicationName() override { return "BlockFinder"; }
const juce::String getApplicationVersion() override { return "1.0.0"; }
bool moreThanOneInstanceAllowed() override { return true; }
void anotherInstanceStarted (const juce::String&) override {}
void suspended() override {}
void resumed() override {}
void systemRequestedQuit() override {}
void unhandledException(const std::exception*, const juce::String&,
int lineNumber) override {}
private:
// Our BLOCKS class.
BlockFinder finder;
};
START_JUCE_APPLICATION (MyJUCEApp)

+ 0
- 1
extras/Build/CMake/JUCEConfig.cmake.in View File

@@ -41,7 +41,6 @@ set(_juce_modules
juce_audio_plugin_client
juce_audio_processors
juce_audio_utils
juce_blocks_basics
juce_box2d
juce_core
juce_cryptography


+ 0
- 1
extras/Projucer/Source/Utility/Helpers/jucer_MiscUtilities.cpp View File

@@ -265,7 +265,6 @@ StringArray getJUCEModules() noexcept
"juce_audio_plugin_client",
"juce_audio_processors",
"juce_audio_utils",
"juce_blocks_basics",
"juce_box2d",
"juce_core",
"juce_cryptography",


+ 0
- 1
extras/UnitTestRunner/CMakeLists.txt View File

@@ -35,7 +35,6 @@ target_compile_definitions(UnitTestRunner PRIVATE
target_link_libraries(UnitTestRunner PRIVATE
juce::juce_analytics
juce::juce_audio_utils
juce::juce_blocks_basics
juce::juce_dsp
juce::juce_opengl
juce::juce_osc


+ 0
- 1
modules/CMakeLists.txt View File

@@ -31,7 +31,6 @@ juce_add_modules(
juce_audio_plugin_client
juce_audio_processors
juce_audio_utils
juce_blocks_basics
juce_box2d
juce_core
juce_cryptography


+ 0
- 9
modules/juce_blocks_basics/README.txt View File

@@ -1,9 +0,0 @@
This is a JUCE module which discovers any connected BLOCKS devices and provides low-level
access to their physical properties.
Its job is to provide:
- topology and status information about the list of connected devices
- callbacks for touch events, control button presses and rotary dial movements
- simple control over individual LEDs
- optional rule-based virtual device aggregation functionality

+ 0
- 128
modules/juce_blocks_basics/blocks/juce_Block.cpp View File

@@ -1,128 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
static Block::UID getBlockUIDFromSerialNumber (const uint8* serial) noexcept
{
Block::UID n = {};
for (int i = 0; i < int (BlocksProtocol::BlockSerialNumber::maxLength); ++i)
n += n * 127 + serial[i];
return n;
}
static Block::UID getBlockUIDFromSerialNumber (const BlocksProtocol::BlockSerialNumber& serial) noexcept
{
return getBlockUIDFromSerialNumber (serial.data);
}
static Block::UID getBlockUIDFromSerialNumber (const String& serial) noexcept
{
if (serial.length() < int (BlocksProtocol::BlockSerialNumber::maxLength))
{
jassertfalse;
return getBlockUIDFromSerialNumber (serial.paddedRight ('0', BlocksProtocol::BlockSerialNumber::maxLength));
}
return getBlockUIDFromSerialNumber ((const uint8*) serial.toRawUTF8());
}
Block::Block (const String& serial)
: serialNumber (serial), uid (getBlockUIDFromSerialNumber (serial))
{
}
Block::Block (const String& serial, const String& version, const String& blockName)
: serialNumber (serial), versionNumber (version), name (blockName), uid (getBlockUIDFromSerialNumber (serial))
{
}
Block::~Block() {}
bool Block::isControlBlock() const
{
return isControlBlock (getType());
}
bool Block::isControlBlock (Block::Type type)
{
return type == Block::Type::liveBlock
|| type == Block::Type::loopBlock
|| type == Block::Type::touchBlock
|| type == Block::Type::developerControlBlock;
}
void Block::addProgramLoadedListener (ProgramLoadedListener* listener) { programLoadedListeners.add (listener); }
void Block::removeProgramLoadedListener (ProgramLoadedListener* listener) { programLoadedListeners.remove (listener); }
void Block::addDataInputPortListener (DataInputPortListener* listener) { dataInputPortListeners.add (listener); }
void Block::removeDataInputPortListener (DataInputPortListener* listener) { dataInputPortListeners.remove (listener); }
void Block::addConfigItemListener (ConfigItemListener* listener) { configItemListeners.add (listener); }
void Block::removeConfigItemListener (ConfigItemListener* listener) { configItemListeners.remove (listener); }
void Block::addProgramEventListener (ProgramEventListener* listener) { programEventListeners.add (listener); }
void Block::removeProgramEventListener (ProgramEventListener* listener) { programEventListeners.remove (listener); }
bool Block::ConnectionPort::operator== (const ConnectionPort& other) const noexcept { return edge == other.edge && index == other.index; }
bool Block::ConnectionPort::operator!= (const ConnectionPort& other) const noexcept { return ! operator== (other); }
Block::Program::Program (Block& b) : block (b) {}
//==============================================================================
TouchSurface::TouchSurface (Block& b) : block (b) {}
TouchSurface::~TouchSurface() {}
TouchSurface::Listener::~Listener() {}
void TouchSurface::addListener (Listener* l) { listeners.add (l); }
void TouchSurface::removeListener (Listener* l) { listeners.remove (l); }
//==============================================================================
ControlButton::ControlButton (Block& b) : block (b) {}
ControlButton::~ControlButton() {}
ControlButton::Listener::~Listener() {}
void ControlButton::addListener (Listener* l) { listeners.add (l); }
void ControlButton::removeListener (Listener* l) { listeners.remove (l); }
//==============================================================================
LEDGrid::LEDGrid (Block& b) : block (b) {}
LEDGrid::~LEDGrid() {}
LEDGrid::Renderer::~Renderer() {}
//==============================================================================
LEDRow::LEDRow (Block& b) : block (b) {}
LEDRow::~LEDRow() {}
//==============================================================================
StatusLight::StatusLight (Block& b) : block (b) {}
StatusLight::~StatusLight() {}
} // namespace juce

+ 0
- 552
modules/juce_blocks_basics/blocks/juce_Block.h View File

@@ -1,552 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
Represents an individual BLOCKS device.
@tags{Blocks}
*/
class Block : public ReferenceCountedObject
{
public:
//==============================================================================
/** Destructor. */
~Block() override;
/** The different block types.
@see Block::getType()
*/
enum Type
{
unknown = 0, /**< Unknown block type. */
lightPadBlock, /**< Lightpad block type. */
liveBlock, /**< Live control block type. */
loopBlock, /**< Loop control block type. */
developerControlBlock, /**< Developer control block type. */
touchBlock, /**< Touch control block type. */
seaboardBlock, /**< Seaboard block type. */
lumiKeysBlock /**< LUMI Keys block type */
};
/** The Block class is reference-counted, so always use a Block::Ptr when
you are keeping references to them.
*/
using Ptr = ReferenceCountedObjectPtr<Block>;
/** The Block class is reference-counted, so Block::Array is useful when
you are storing lists of them.
*/
using Array = ReferenceCountedArray<Block>;
/** The Block's serial number. */
const String serialNumber;
/** The Block's version number */
String versionNumber;
/** The Block's name */
String name;
/** This type is used for the unique block identifier. */
using UID = uint64;
/** This Block's UID.
This will be globally unique, and remains constant for a particular device.
*/
const UID uid;
//==============================================================================
/** Two blocks are considered equal if they have the same UID. */
bool operator== (const Block& other) const noexcept { return uid == other.uid; }
/** Two blocks are considered equal if they have the same UID. */
bool operator!= (const Block& other) const noexcept { return uid != other.uid; }
//==============================================================================
/** Returns the type of this device.
@see Block::Type
*/
virtual Type getType() const = 0;
/** Returns true if this a control block. **/
bool isControlBlock() const;
/** Returns true if Block::Type is a control block. */
static bool isControlBlock (Block::Type);
/** Returns a human-readable description of this device type. */
virtual String getDeviceDescription() const = 0;
/** Returns the battery level in the range 0.0 to 1.0. */
virtual float getBatteryLevel() const = 0;
/** Returns true if the battery is charging. */
virtual bool isBatteryCharging() const = 0;
//==============================================================================
/** Returns true if this block is connected and active. */
virtual bool isConnected() const = 0;
/** Returns the time this block object was connected to the topology.
Only valid when isConnected == true.
@see Block::isConnected
*/
virtual Time getConnectionTime() const = 0;
/** Returns true if this block or the master block this block is connected to,
is connected via bluetooth.
Only valid when isConnected == true.
@see Block::isConnected, Block::isMasterBlock
*/
virtual bool isConnectedViaBluetooth() const = 0;
/** Returns true if this block is directly connected to the application,
as opposed to only being connected to a different block via a connection port.
@see Block::ConnectionPort
*/
virtual bool isMasterBlock() const = 0;
/** Returns the UID of the master block this block is connected to. */
virtual UID getConnectedMasterUID() const = 0;
//==============================================================================
/** Returns the width of the device in logical device units. */
virtual int getWidth() const = 0;
/** Returns the height of the device in logical device units. */
virtual int getHeight() const = 0;
/** Returns true if the device is a physical hardware block (i.e. not a virtual block). */
virtual bool isHardwareBlock() const = 0;
/** Returns the length of one logical device unit as physical millimeters. */
virtual float getMillimetersPerUnit() const = 0;
/** A simple struct representing the area of a block. */
struct BlockArea
{
int x, y, width, height;
};
/** Returns the area that this block covers within the layout of the group as a whole.
The coordinates are in logical block units, and are relative to the origin, which is the master block's top-left corner.
*/
virtual BlockArea getBlockAreaWithinLayout() const = 0;
/** Returns the rotation of this block relative to the master block in 90 degree steps clockwise. */
virtual int getRotation() const = 0;
//==============================================================================
/** If this block has a grid of LEDs, this will return an object to control it.
Note that the pointer that is returned belongs to this object, and the caller must
neither delete it or use it after the lifetime of this Block object has finished.
If there are no LEDs, then this method will return nullptr.
*/
virtual LEDGrid* getLEDGrid() const = 0;
/** If this block has a row of LEDs, this will return an object to control it.
Note that the pointer that is returned belongs to this object, and the caller must
neither delete it or use it after the lifetime of this Block object has finished.
If there are no LEDs, then this method will return nullptr.
*/
virtual LEDRow* getLEDRow() = 0;
/** If this block has any status LEDs, this will return an array of objects to control them.
Note that the objects in the array belong to this Block object, and the caller must
neither delete them or use them after the lifetime of this Block object has finished.
*/
virtual juce::Array<StatusLight*> getStatusLights() const = 0;
/** If this block has a pressure-sensitive surface, this will return an object to
access its data.
Note that the pointer returned does is owned by this object, and the caller must
neither delete it or use it after the lifetime of this Block object has finished.
If the device is not touch-sensitive, then this method will return nullptr.
*/
virtual TouchSurface* getTouchSurface() const = 0;
/** If this block has any control buttons, this will return an array of objects to control them.
Note that the objects in the array belong to this Block object, and the caller must
neither delete them or use them after the lifetime of this Block object has finished.
*/
virtual juce::Array<ControlButton*> getButtons() const = 0;
//==============================================================================
/** This returns true if the block supports generating graphics by drawing into a JUCE
Graphics context. This should only be true for virtual on-screen blocks; hardware
blocks will instead use the LED Grid for visuals.
*/
virtual bool supportsGraphics() const = 0;
//==============================================================================
/** These are the edge-connectors that a device may have. */
struct ConnectionPort
{
enum class DeviceEdge
{
north,
south,
east,
west
};
/** The side of the device on which this port is located. */
DeviceEdge edge;
/** The index of this port along the device edge.
For north and south edges, index 0 is the left-most port.
For east and west edges, index 0 is the top-most port.
*/
int index;
bool operator== (const ConnectionPort&) const noexcept;
bool operator!= (const ConnectionPort&) const noexcept;
};
/** Returns a list of the connectors that this device has. */
virtual juce::Array<ConnectionPort> getPorts() const = 0;
//==============================================================================
/** A program that can be loaded onto a block. */
struct Program
{
Program (Block&);
virtual ~Program() = default;
/** Returns the LittleFoot program to execute on the BLOCKS device. */
virtual String getLittleFootProgram() = 0;
/** Returns an array of search paths to use when resolving includes. **/
virtual juce::Array<File> getSearchPaths() { return {}; }
Block& block;
};
/** Sets the Program to run on this block.
The supplied Program's lifetime will be managed by this class, so do not
use the Program in other places in your code.
Optional parameter to determine if program is set temporarily or saved
to flash as the default prgram./
*/
enum class ProgramPersistency { setAsTemp, setAsDefault };
virtual Result setProgram (std::unique_ptr<Program>,
ProgramPersistency persistency = ProgramPersistency::setAsTemp) = 0;
/** Returns a pointer to the currently loaded program. */
virtual Program* getProgram() const = 0;
/** Listener interface to be informed of program loaded events*/
struct ProgramLoadedListener
{
virtual ~ProgramLoadedListener() = default;
/** Called whenever a program has been loaded. */
virtual void handleProgramLoaded (Block&) = 0;
};
/** Adds a new listener for program load completions. */
void addProgramLoadedListener (ProgramLoadedListener*);
/** Removes a listener for program load completions. */
void removeProgramLoadedListener (ProgramLoadedListener*);
//==============================================================================
/** A message that can be sent to the currently loaded program. */
struct ProgramEventMessage
{
int32 values[3];
};
/** Sends a message to the currently loaded program.
To receive the message the program must provide a littlefoot function called
handleMessage with the following form:
@code
void handleMessage (int param1, int param2, int param3)
{
// Do something with the two integer parameters that the app has sent...
}
@endcode
*/
virtual void sendProgramEvent (const ProgramEventMessage&) = 0;
/** Interface for objects listening to custom program events. */
struct ProgramEventListener
{
virtual ~ProgramEventListener() = default;
/** Called whenever a message from a block is received. */
virtual void handleProgramEvent (Block& source, const ProgramEventMessage&) = 0;
};
/** Adds a new listener for custom program events from the block. */
void addProgramEventListener (ProgramEventListener*);
/** Removes a listener for custom program events from the block. */
void removeProgramEventListener (ProgramEventListener*);
//==============================================================================
/** Returns the overall memory of the block. */
virtual uint32 getMemorySize() = 0;
/** Returns the size of the data block that setDataByte and other functions can write to. */
virtual uint32 getHeapMemorySize() = 0;
/** Sets a single byte on the littlefoot heap. */
virtual void setDataByte (size_t offset, uint8 value) = 0;
/** Sets multiple bytes on the littlefoot heap. */
virtual void setDataBytes (size_t offset, const void* data, size_t num) = 0;
/** Sets multiple bits on the littlefoot heap. */
virtual void setDataBits (uint32 startBit, uint32 numBits, uint32 value) = 0;
/** Sets a single, 32 bit or less, value on the littlefoot heap. */
template <typename Type>
void setData (uint32 offset, Type value)
{
const auto numBytes = sizeof (Type);
for (auto byte = numBytes; --byte > 0u;)
{
auto v = *reinterpret_cast<unsigned*> (&value);
v = (v >> (numBytes - byte) * 8) & 0xFF;
setDataByte (offset + byte, uint8 (v));
}
}
/** Gets a byte from the littlefoot heap. */
virtual uint8 getDataByte (size_t offset) = 0;
/** Sets the current program as the block's default state. */
virtual void saveProgramAsDefault() = 0;
/** Resets the loaded program to the block's default state. */
virtual void resetProgramToDefault() = 0;
//==============================================================================
/** Metadata for a given config item */
struct ConfigMetaData
{
static constexpr int32 numOptionNames = 16;
enum class ConfigType
{
integer,
floating,
boolean,
colour,
options
};
ConfigMetaData (uint32 itemIndex)
: item (itemIndex)
{}
// Constructor to work around VS2015 bugs...
ConfigMetaData (uint32 itemIndex,
int32 itemValue,
Range<int32> rangeToUse,
bool active,
const char* itemName,
ConfigType itemType,
const char* options[ConfigMetaData::numOptionNames],
const char* groupName)
: item (itemIndex),
value (itemValue),
range (rangeToUse),
isActive (active),
name (itemName),
type (itemType),
group (groupName)
{
for (int i = 0; i < numOptionNames; ++i)
optionNames[i] = options[i];
}
ConfigMetaData (const ConfigMetaData& other)
{
*this = other;
}
const ConfigMetaData& operator= (const ConfigMetaData& other)
{
if (this != &other)
{
item = other.item;
value = other.value;
range = other.range;
isActive = other.isActive;
name = other.name;
type = other.type;
group = other.group;
for (int i = 0; i < numOptionNames; ++i)
optionNames[i] = other.optionNames[i];
}
return *this;
}
bool operator== (const ConfigMetaData& other) const
{
for (int32 optionIndex = 0; optionIndex < numOptionNames; ++optionIndex)
if (optionNames[optionIndex] != other.optionNames[optionIndex])
return false;
return item == other.item
&& value == other.value
&& range == other.range
&& isActive == other.isActive
&& name == other.name
&& group == other.group;
}
bool operator != (const ConfigMetaData& other) const
{
return ! (*this == other);
}
uint32 item = 0;
int32 value = 0;
Range<int32> range;
bool isActive = false;
String name;
ConfigType type = ConfigType::integer;
String optionNames[numOptionNames] = {};
String group;
};
/** Listener interface to be informed of block config changes */
struct ConfigItemListener
{
virtual ~ConfigItemListener() = default;
/** Called whenever a config changes. */
virtual void handleConfigItemChanged (Block&, const ConfigMetaData&, uint32 index) = 0;
/*-* Callled following a config sync request*/
virtual void handleConfigSyncEnded (Block&) = 0;
};
/** Adds a new listener for config item changes. */
void addConfigItemListener (ConfigItemListener*);
/** Removes a listener for config item changes. */
void removeConfigItemListener (ConfigItemListener*);
/** Returns the maximum number of config items available */
virtual uint32 getMaxConfigIndex() = 0;
/** Determine if this is a valid config item index */
virtual bool isValidUserConfigIndex (uint32 item) = 0;
/** Get local config item value */
virtual int32 getLocalConfigValue (uint32 item) = 0;
/** Set local config item value */
virtual void setLocalConfigValue (uint32 item, int32 value) = 0;
/** Set local config item range */
virtual void setLocalConfigRange (uint32 item, int32 min, int32 max) = 0;
/** Set if config item is active or not */
virtual void setLocalConfigItemActive (uint32 item, bool isActive) = 0;
/** Determine if config item is active or not */
virtual bool isLocalConfigItemActive (uint32 item) = 0;
/** Get config item metadata */
virtual ConfigMetaData getLocalConfigMetaData (uint32 item) = 0;
/** Request sync of factory config with block */
virtual void requestFactoryConfigSync() = 0;
/** Reset all items active status */
virtual void resetConfigListActiveStatus() = 0;
/** Perform factory reset on Block */
virtual void factoryReset() = 0;
/** Reset this Block */
virtual void blockReset() = 0;
/** Set Block name */
virtual bool setName (const String& name) = 0;
//==============================================================================
/** Allows the user to provide a function that will receive log messages from the block. */
virtual void setLogger (std::function<void (const Block& block, const String&)> loggingCallback) = 0;
/** Sends a firmware update packet to a block, and waits for a reply. Returns an error code. */
virtual bool sendFirmwareUpdatePacket (const uint8* data, uint8 size,
std::function<void (uint8, uint32)> packetAckCallback) = 0;
//==============================================================================
/** Interface for objects listening to input data port. */
struct DataInputPortListener
{
virtual ~DataInputPortListener() = default;
/** Called whenever a message from a block is received. */
virtual void handleIncomingDataPortMessage (Block& source, const void* messageData, size_t messageSize) = 0;
};
/** Adds a new listener for the data input port. */
virtual void addDataInputPortListener (DataInputPortListener*);
/** Removes a listener for the data input port. */
virtual void removeDataInputPortListener (DataInputPortListener*);
/** Sends a message to the block. */
virtual void sendMessage (const void* messageData, size_t messageSize) = 0;
//==============================================================================
/** This type is used for timestamping events. It represents a number of milliseconds since the block
device was booted.
*/
using Timestamp = uint32;
protected:
//==============================================================================
Block (const String& serialNumberToUse);
Block (const String& serial, const String& version, const String& name);
ListenerList<ProgramLoadedListener> programLoadedListeners;
ListenerList<ProgramEventListener> programEventListeners;
ListenerList<ConfigItemListener> configItemListeners;
ListenerList<DataInputPortListener> dataInputPortListeners;
private:
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Block)
};
} // namespace juce

+ 0
- 358
modules/juce_blocks_basics/blocks/juce_BlockConfigManager.h View File

@@ -1,358 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
// This file provides interfaces for managing the internal configuration of Blocks
// and synchronises with the connected Block
using namespace BlocksProtocol;
using ConfigType = Block::ConfigMetaData::ConfigType;
/** Manages the configuration of blocks
@tags{Blocks}
*/
struct BlockConfigManager
{
/** Structure describing a configuration */
struct ConfigDescription
{
ConfigItemId item;
int32 value;
int32 min;
int32 max;
bool isActive;
const char* name;
ConfigType type;
const char* optionNames[configMaxOptions];
const char* group;
static_assert (configMaxOptions == Block::ConfigMetaData::numOptionNames, "Config options size and config metadata size should be the same");
Block::ConfigMetaData toConfigMetaData() const
{
return Block::ConfigMetaData ((uint32) item, value, { min, max }, isActive, name, type, (const char**) optionNames, group);
}
};
BlockConfigManager (Array<ConfigDescription> defaultConfig)
{
for (auto c : defaultConfig)
{
uint32 itemIndex;
if (getIndexForItem (c.item, itemIndex))
configList[itemIndex] = c;
}
}
void setDeviceIndex (TopologyIndex newDeviceIndex) { deviceIndex = newDeviceIndex; }
void setDeviceComms (PhysicalTopologySource::DeviceConnection* newConn) { deviceConnection = newConn; }
static constexpr uint32 numConfigItems = 69;
static constexpr const char* midiSettingsGroup = "MIDI Settings";
static constexpr const char* pitchGroup = "Pitch";
static constexpr const char* playGroup = "Play mode";
static constexpr const char* sensitivityGroup = "Sensitivity";
static constexpr const char* rhythmGroup = "Rhythm";
static constexpr const char* coloursGroup = "Colors";
ConfigDescription configList[numConfigItems] =
{
{ midiStartChannel, 2, 1, 16, false, "MIDI Start Channel", ConfigType::integer, {}, midiSettingsGroup },
{ midiEndChannel, 16, 1, 16, false, "MIDI End Channel", ConfigType::integer, {}, midiSettingsGroup },
{ midiUseMPE, 1, 0, 2, false, "MIDI Mode", ConfigType::options, { "Multi Channel",
"MPE",
"Single Channel" }, midiSettingsGroup },
{ pitchBendRange, 48, 1, 96, false, "Pitch Bend Range", ConfigType::integer, {}, midiSettingsGroup },
{ midiChannelRange, 15, 1, 15, false, "No. MIDI Channels", ConfigType::integer, {}, midiSettingsGroup },
{ MPEZone, 0, 0, 1, false, "MPE Zone", ConfigType::options, { "Lower Zone",
"Upper Zone"}, midiSettingsGroup },
{ octave, 0, -4, 6, false, "Octave", ConfigType::integer, {}, pitchGroup },
{ transpose, 0, -11, 11, false, "Transpose", ConfigType::integer, {}, pitchGroup },
{ slideCC, 74, 0, 127, false, "Slide CC", ConfigType::integer, {}, playGroup },
{ slideMode, 0, 0, 2, false, "Slide Mode", ConfigType::options, { "Absolute",
"Relative Unipolar",
"Relative Bipolar" }, playGroup },
{ velocitySensitivity, 100, 0, 127, false, "Strike Sensitivity", ConfigType::integer, {}, sensitivityGroup },
{ glideSensitivity, 100, 0, 127, false, "Glide Sensitivity", ConfigType::integer, {}, sensitivityGroup },
{ slideSensitivity, 100, 0, 127, false, "Slide Sensitivity", ConfigType::integer, {}, sensitivityGroup },
{ pressureSensitivity, 100, 0, 127, false, "Pressure Sensitivity", ConfigType::integer, {}, sensitivityGroup },
{ liftSensitivity, 100, 0, 127, false, "Lift Sensitivity", ConfigType::integer, {}, sensitivityGroup },
{ fixedVelocity, 0, 0, 1, false, "Fixed Velocity", ConfigType::boolean, {}, sensitivityGroup },
{ fixedVelocityValue, 127, 1, 127, false, "Fixed Velocity Value", ConfigType::integer, {}, sensitivityGroup },
{ pianoMode, 0, 0, 1, false, "Piano Mode", ConfigType::boolean, {}, playGroup },
{ glideLock, 0, 0, 127, false, "Glide Rate", ConfigType::integer, {}, playGroup },
{ glideLockEnable, 0, 0, 1, false, "Glide Lock Enable", ConfigType::boolean, {}, playGroup },
{ mode, 4, 1, 5, false, "Mode", ConfigType::integer, {}, playGroup },
{ volume, 100, 0, 127, false, "Volume", ConfigType::integer, {}, playGroup },
{ scale, 0, 0, 18, false, "Scale", ConfigType::integer, {}, playGroup }, // NOTE: Should be options
{ hideMode, 0, 0, 1, false, "Hide Mode", ConfigType::boolean, {}, playGroup },
{ chord, 0, 0, 127, false, "Chord", ConfigType::integer, {}, playGroup }, // NOTE: Should be options
{ arpPattern, 0, 0, 127, false, "Arp Pattern", ConfigType::integer, {}, playGroup },
{ tempo, 120, 1, 300, false, "Tempo", ConfigType::integer, {}, rhythmGroup },
{ key, 0, 0, 11, false, "Key", ConfigType::options, { "C", "C#", "D", "D#",
"E", "F", "F#", "G",
"G#", "A", "A#", "B"}, playGroup },
{ autoTransposeToKey, 0, 0, 1, false, "Auto Transpose To Key",ConfigType::boolean, {}, pitchGroup },
{ xTrackingMode, 1, 1, 4, false, "Glide Tracking Mode", ConfigType::options, { "Multi-Channel",
"Last Played",
"Highest",
"Lowest",
"Disabled" }, playGroup },
{ yTrackingMode, 1, 1, 4, false, "Slide Tracking Mode", ConfigType::options, { "Multi-Channel",
"Last Played",
"Highest",
"Lowest",
"Disabled" }, playGroup },
{ zTrackingMode, 1, 0, 4, false, "Pressure Tracking Mode", ConfigType::options, { "Poly Aftertouch",
"Last Played",
"Highest",
"Lowest",
"Disabled",
"Hardest" }, playGroup },
{ gammaCorrection, 0, 0, 1, false, "Gamma Correction", ConfigType::boolean, {}, coloursGroup },
{ globalKeyColour, INT32_MIN, INT32_MIN, INT32_MAX, false, "Global Key Color", ConfigType::colour, {}, coloursGroup },
{ rootKeyColour, INT32_MIN, INT32_MIN, INT32_MAX, false, "Root Key Color" , ConfigType::colour, {}, coloursGroup },
{ brightness, 100, 0, 100, false, "Brightness", ConfigType::integer, {}, coloursGroup },
// These can be defined for unique usage for a given Littlefoot script
{ user0, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user1, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user2, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user3, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user4, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user5, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user6, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user7, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user8, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user9, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user10, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user11, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user12, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user13, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user14, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user15, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user16, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user17, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user18, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user19, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user20, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user21, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user22, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user23, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user24, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user25, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user26, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user27, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user28, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user29, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user30, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user31, 0, 0, 127, false, {}, ConfigType::integer, {}, {} }
};
//==============================================================================
int32 getItemValue (ConfigItemId item)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
return configList[itemIndex].value;
return 0;
}
void setItemValue (ConfigItemId item, int32 value)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
configList[itemIndex].value = value;
setBlockConfig (item, value);
}
int32 getItemMin (ConfigItemId item)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
return configList[itemIndex].min;
return 0;
}
void setItemMin (ConfigItemId item, int32 min)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
configList[itemIndex].min = min;
}
int32 getItemMax (ConfigItemId item)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
return configList[itemIndex].max;
return 0;
}
void setItemMax (ConfigItemId item, int32 max)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
configList[itemIndex].max = max;
// Send updateConfig message to Block
}
bool getItemActive (ConfigItemId item)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
return configList[itemIndex].isActive;
return false;
}
void setItemActive (ConfigItemId item, bool isActive)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
configList[itemIndex].isActive = isActive;
// Send setConfigState message to Block
}
String getOptionName (ConfigItemId item, uint8 optionIndex)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex) && optionIndex < configMaxOptions)
return configList[itemIndex].optionNames[optionIndex];
return {};
}
Block::ConfigMetaData getMetaData (ConfigItemId item)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
return configList[itemIndex].toConfigMetaData();
return { static_cast<juce::uint32> (item) };
}
void resetConfigListActiveStatus()
{
for (auto& i : configList)
i.isActive = false;
}
//==============================================================================
// Set Block Configuration
void setBlockConfig (ConfigItemId item, int32 value)
{
buildAndSendPacket ([item, value] (HostPacketBuilder<32>& p) { p.addConfigSetMessage (item, value); });
}
void requestBlockConfig (ConfigItemId item)
{
buildAndSendPacket ([item] (HostPacketBuilder<32>& p) { p.addRequestMessage (item); });
}
void requestFactoryConfigSync()
{
buildAndSendPacket ([] (HostPacketBuilder<32>& p) { p.addRequestFactorySyncMessage(); });
}
void requestUserConfigSync()
{
buildAndSendPacket ([] (HostPacketBuilder<32>& p) { p.addRequestUserSyncMessage(); });
}
void handleConfigUpdateMessage (int32 item, int32 value, int32 min, int32 max)
{
uint32 index;
if (getIndexForItem ((ConfigItemId) item, index))
{
configList[index].value = value;
configList[index].min = min;
configList[index].max = max;
configList[index].isActive = true;
}
}
void handleConfigSetMessage(int32 item, int32 value)
{
uint32 index;
if (getIndexForItem ((ConfigItemId) item, index))
configList[index].value = value;
}
private:
bool getIndexForItem (ConfigItemId item, uint32& index)
{
for (uint32 i = 0; i < numConfigItems; ++i)
{
if (configList[i].item == item)
{
index = i;
return true;
}
}
return false;
}
template <typename PacketBuildFn>
void buildAndSendPacket (PacketBuildFn buildFn)
{
if (deviceConnection == nullptr)
return;
HostPacketBuilder<32> packet;
packet.writePacketSysexHeaderBytes (deviceIndex);
buildFn (packet);
packet.writePacketSysexFooter();
deviceConnection->sendMessageToDevice (packet.getData(), (size_t) packet.size());
}
TopologyIndex deviceIndex {};
PhysicalTopologySource::DeviceConnection* deviceConnection {};
};
} // namespace juce

+ 0
- 237
modules/juce_blocks_basics/blocks/juce_BlocksVersion.cpp View File

@@ -1,237 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
BlocksVersion::BlocksVersion (const String& versionString)
{
evaluate (versionString);
}
String BlocksVersion::toString (bool extended) const
{
String output = String (major) + "." + String (minor) + "." + String (patch);
if (extended)
{
if (releaseType.isNotEmpty())
output += "-" + releaseType + "-" + String (releaseCount);
if (commit.isNotEmpty())
output += "-" + commit;
if (forced)
output += "-f";
}
return output;
}
static std::regex getRegEx()
{
static const std::string majorMinorPatchRegex { "([0-9]+)\\.([0-9]+)\\.([0-9]+)" };
static const std::string releaseAndCommitDetailsRegex { "(?:-(alpha|beta|rc))?(?:-([0-9]+))?(?:-g([A-Za-z0-9]+))?" };
static const std::string forcedUpdateRegex { "(-f)?" };
static const std::regex regEx ("(?:.+)?" + majorMinorPatchRegex + releaseAndCommitDetailsRegex + forcedUpdateRegex + "(?:.+)?");
return regEx;
}
bool BlocksVersion::isValidVersion (const String& versionString)
{
return std::regex_match (versionString.toRawUTF8(), getRegEx());
}
bool BlocksVersion::evaluate (const String& versionString)
{
std::cmatch groups;
const bool result = std::regex_match (versionString.toRawUTF8(), groups, getRegEx());
jassert (result);
auto toInt = [] (const std::sub_match<const char*> match)
{
return std::atoi (match.str().c_str());
};
enum tags { FULL, MAJOR, MINOR, PATCH, RELEASE, COUNT, COMMIT, FORCED};
major = toInt (groups[MAJOR]);
minor = toInt (groups[MINOR]);
patch = toInt (groups[PATCH]);
releaseType = String (groups[RELEASE]);
releaseCount = toInt (groups[COUNT]);
commit = String (groups[COMMIT]);
forced = groups[FORCED].matched;
return result;
}
bool BlocksVersion::isEqualTo (const BlocksVersion& other) const
{
return major == other.major &&
minor == other.minor &&
patch == other.patch &&
releaseType == other.releaseType &&
releaseCount == other.releaseCount;
}
bool BlocksVersion::isGreaterThan (const BlocksVersion& other) const
{
if (major != other.major) return (major > other.major);
if (minor != other.minor) return (minor > other.minor);
if (patch != other.patch) return (patch > other.patch);
return releaseTypeGreaterThan (other);
}
bool BlocksVersion::releaseTypeGreaterThan (const BlocksVersion& other) const
{
auto getReleaseTypePriority = [] (const BlocksVersion& version)
{
String releaseTypes[4] = { "alpha", "beta", "rc", {} };
for (int i = 0; i < 4; ++i)
if (version.releaseType == releaseTypes[i])
return i;
return -1;
};
if (releaseType != other.releaseType)
return getReleaseTypePriority (*this) > getReleaseTypePriority (other);
return releaseCount > other.releaseCount;
}
bool BlocksVersion::operator== (const BlocksVersion& other) const
{
return isEqualTo (other);
}
bool BlocksVersion::operator!=(const BlocksVersion& other) const
{
return ! (*this == other);
}
bool BlocksVersion::operator> (const BlocksVersion& other) const
{
return isGreaterThan (other);
}
bool BlocksVersion::operator< (const BlocksVersion& other) const
{
return ! (*this > other) && (*this != other);
}
bool BlocksVersion::operator<= (const BlocksVersion& other) const
{
return (*this < other) || (*this == other);
}
bool BlocksVersion::operator>= (const BlocksVersion& other) const
{
return (*this > other) || (*this == other);
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class BlocksVersionUnitTests : public UnitTest
{
public:
BlocksVersionUnitTests()
: UnitTest ("BlocksVersionUnitTests", UnitTestCategories::blocks)
{}
void runTest() override
{
beginTest ("Compare patch number");
expect (BlocksVersion ("4.6.7") < BlocksVersion ("4.6.11"));
expect (BlocksVersion ("4.6.6") > BlocksVersion ("4.6.2"));
expect (BlocksVersion ("4.6.5") <= BlocksVersion ("4.6.8"));
expect (BlocksVersion ("4.6.4") >= BlocksVersion ("4.6.3"));
beginTest ("Compare minor number");
expect (BlocksVersion ("4.5.9") < BlocksVersion ("4.6.7"));
expect (BlocksVersion ("4.15.2") > BlocksVersion ("4.6.6"));
expect (BlocksVersion ("4.4.8") <= BlocksVersion ("4.6.5"));
expect (BlocksVersion ("4.7.4") >= BlocksVersion ("4.6.3"));
beginTest ("Compare major number");
expect (BlocksVersion ("4.6.9") < BlocksVersion ("8.5.7"));
expect (BlocksVersion ("15.6.2") > BlocksVersion ("4.9.6"));
expect (BlocksVersion ("4.6.8") <= BlocksVersion ("7.4.5"));
expect (BlocksVersion ("5.6.4") >= BlocksVersion ("4.7.3"));
beginTest ("Compare build number");
expect (BlocksVersion ("0.3.2-alpha-3-gjduh") < BlocksVersion ("0.3.2-alpha-12-gjduh"));
expect (BlocksVersion ("0.3.2-alpha-4-gjduh") > BlocksVersion ("0.3.2-alpha-1-gjduh"));
expect (BlocksVersion ("0.3.2-beta-5-gjduh") <= BlocksVersion ("0.3.2-beta-6-gjduh"));
expect (BlocksVersion ("0.3.2-beta-6-gjduh") >= BlocksVersion ("0.3.2-beta-3-gjduh"));
beginTest ("Compare build type");
expect (BlocksVersion ("0.3.2-alpha-3-gjduhenf") < BlocksVersion ("0.3.2-beta-1-gjduhenf"));
expect (BlocksVersion ("0.3.2-beta-3-gjduhenf") < BlocksVersion ("0.3.2"));
expect (BlocksVersion ("0.3.2") > BlocksVersion ("0.3.2-alpha-3-gjduhenf"));
beginTest ("Compare equal numbers");
expect (BlocksVersion ("4.6.7") == BlocksVersion ("4.6.7"));
expect (BlocksVersion ("4.6.7-alpha-3-gsdfsf") == BlocksVersion ("4.6.7-alpha-3-gsdfsf"));
beginTest ("Identify forced version");
expect (BlocksVersion("0.2.2-2-g25eaec8a-f").forced);
expect (BlocksVersion("0.2.2-2-f").forced);
expect (! BlocksVersion("0.2.2-2-g25eaec8-d7").forced);
beginTest ("Valid Strings");
expect (BlocksVersion::isValidVersion ("Rainbow 0.4.5-beta-1-g4c36e"));
expect (! BlocksVersion::isValidVersion ("0.4-beta-1-g4c36e"));
expect (! BlocksVersion::isValidVersion ("a.0.4-beta-1-g4c36e"));
expect (BlocksVersion::isValidVersion ("BLOCKS control 0.2.2-2-g25eaec8a-f.syx"));
expect (BlocksVersion("BLOCKS control 0.2.2-2-g25eaec8a-f.syx") == BlocksVersion("0.2.2-2-g25eaec8a-f"));
beginTest ("Default constructors");
{
BlocksVersion v1 ("4.5.9");
BlocksVersion v2 (v1);
BlocksVersion v3;
v3 = v1;
expect (v2 == v1);
expect (v3 == v1);
BlocksVersion emptyVersion;
expect (emptyVersion == BlocksVersion ("0.0.0"));
}
}
};
static BlocksVersionUnitTests BlocksVersionUnitTests;
#endif
} // namespace juce

+ 0
- 87
modules/juce_blocks_basics/blocks/juce_BlocksVersion.h View File

@@ -1,87 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
namespace juce
{
/**
Represents the version number of a block device.
@tags{Blocks}
*/
struct BlocksVersion
{
public:
/** The main value in a version number x.0.0 */
int major = 0;
/** The secondary value in a version number 1.x.0 */
int minor = 0;
/** The tertiary value in a version number 1.0.x */
int patch = 0;
/** The release tag for this version, such as "beta", "alpha", "rc", etc */
String releaseType;
/** A numerical value associated with the release tag, such as "beta 4" */
int releaseCount = 0;
/** The associated git commit that generated this firmware version */
String commit;
/** Identify "forced" firmware builds **/
bool forced = false;
String toString (bool extended = false) const;
/** Constructs a version number from an formatted String */
BlocksVersion (const String&);
/** Constructs a version number from another BlocksVersion */
BlocksVersion (const BlocksVersion& other) = default;
/** Creates an empty version number **/
BlocksVersion() = default;
/** Returns true if string format is valid */
static bool isValidVersion (const String& versionString);
bool operator == (const BlocksVersion&) const;
bool operator != (const BlocksVersion&) const;
bool operator < (const BlocksVersion&) const;
bool operator > (const BlocksVersion&) const;
bool operator <= (const BlocksVersion&) const;
bool operator >= (const BlocksVersion&) const;
private:
/** @internal */
bool evaluate (const String& versionString);
bool releaseTypeGreaterThan (const BlocksVersion& otherReleaseType) const;
bool isGreaterThan (const BlocksVersion& other) const;
bool isEqualTo (const BlocksVersion& other) const;
};
} // namespace juce

+ 0
- 141
modules/juce_blocks_basics/blocks/juce_ControlButton.h View File

@@ -1,141 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
Represents a button on a block device.
@tags{Blocks}
*/
class ControlButton
{
public:
ControlButton (Block&);
/** Destructor. */
virtual ~ControlButton();
/** These are all known types of control buttons.
You can find out which buttons a device has by calling getButtons(),
and can get the name of a button type with getButtonName().
*/
enum ButtonFunction
{
mode, /**< The side button on a lightpad block and the first button on a live/loop block. */
volume, /**< The volume button on a live/loop block. */
// common to all types of block
up, /**< The up button on a control block. */
down, /**< The down button on a control block. */
// live block buttons
scale, /**< The scale button on a live block. */
chord, /**< The chord button on a live block. */
arp, /**< The arp button on a live block. */
sustain, /**< The sustain button on a live block. */
octave, /**< The octave button on a live block. */
love, /**< The love button on a live block. */
// loop block buttons
click, /**< The click button on a loop block. */
snap, /**< The snap button on a loop block. */
back, /**< The back button on a loop block. */
playOrPause, /**< The play or pause button on a loop block. */
record, /**< The record button on a loop block. */
learn, /**< The learn button on a loop block. */
// developer block buttons
button0, /**< Button 0 on a developer block. */
button1, /**< Button 1 on a developer block. */
button2, /**< Button 2 on a developer block. */
button3, /**< Button 3 on a developer block. */
button4, /**< Button 4 on a developer block. */
button5, /**< Button 5 on a developer block. */
button6, /**< Button 6 on a developer block. */
button7, /**< Button 7 on a developer block. */
// touch block buttons
velocitySensitivity, /**< The velocity sensitivity button on a touch block. */
glideSensitivity, /**< The glide sensitivity button on a touch block. */
slideSensitivity, /**< The slide sensitivity button on a touch block. */
pressSensitivity, /**< The press sensitivity button on a touch block. */
liftSensitivity, /**< The lift sensitivity button on a touch block. */
fixedVelocity, /**< The fixed velocity button on a touch block. */
glideLock, /**< The glide lock button on a touch block. */
pianoMode /**< The piano mode button on a touch block. */
};
/** Returns the button's type. */
virtual ButtonFunction getType() const = 0;
/** Returns the button's description. */
virtual String getName() const = 0;
/** Returns the position of this button on the device, in device units.
For buttons that are on the side of the device, this may want to return a value that
is beyond the physical block size.
*/
virtual float getPositionX() const = 0;
/** Returns the position of this button on the device, in device units.
For buttons that are on the side of the device, this may want to return a value that
is beyond the physical block size.
*/
virtual float getPositionY() const = 0;
/** Returns true if this button has a controllable light. */
virtual bool hasLight() const = 0;
/** If the button can light-up, this sets its colour. */
virtual bool setLightColour (LEDColour newColour) = 0;
/** A listener that can be attached to a ControlButton object so that it
gets called when the button is pushed or released.
*/
struct Listener
{
virtual ~Listener();
/** Called when the button is pressed. */
virtual void buttonPressed (ControlButton&, Block::Timestamp) = 0;
/** Called when the button is released. */
virtual void buttonReleased (ControlButton&, Block::Timestamp) = 0;
};
/** Adds a listener to the control button. */
void addListener (Listener*);
/** Removes a listener from the control button. */
void removeListener (Listener*);
/** The control block that this button belongs to. */
Block& block;
protected:
ListenerList<Listener> listeners;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ControlButton)
};
} // namespace juce

+ 0
- 105
modules/juce_blocks_basics/blocks/juce_LEDGrid.h View File

@@ -1,105 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
A simple ARGB colour class for setting LEDs.
@tags{Blocks}
*/
struct LEDColour
{
LEDColour() = default;
LEDColour (const LEDColour&) = default;
LEDColour& operator= (const LEDColour&) = default;
LEDColour (uint32 argbColour) noexcept : argb (argbColour) {}
template <typename ColourType>
LEDColour (const ColourType& colour) : LEDColour (colour.getARGB()) {}
uint8 getAlpha() const noexcept { return (uint8) (argb >> 24); }
uint8 getRed() const noexcept { return (uint8) (argb >> 16); }
uint8 getGreen() const noexcept { return (uint8) (argb >> 8); }
uint8 getBlue() const noexcept { return (uint8) argb; }
uint32 getARGB() const noexcept { return argb; }
uint32 argb = 0;
};
//==============================================================================
/**
Represents a 2D grid of LEDs on a block device.
@tags{Blocks}
*/
class LEDGrid
{
public:
LEDGrid (Block&);
/** Destructor. */
virtual ~LEDGrid();
//==============================================================================
/** Returns the number of columns in the LED grid. */
virtual int getNumColumns() const = 0;
/** Returns the number of rows in the LED grid. */
virtual int getNumRows() const = 0;
//==============================================================================
/** An interface to use for LEDGrid rendering. */
struct Renderer : public ReferenceCountedObject
{
~Renderer() override;
virtual void renderLEDGrid (LEDGrid&) = 0;
/** The Renderer class is reference-counted, so always use a Renderer::Ptr when
you are keeping references to them.
*/
using Ptr = ReferenceCountedObjectPtr<Renderer>;
};
/** Set the visualiser that will create visuals for this block (nullptr for none).
Note that the LEDGrid will NOT take ownership of this object, so the caller
must ensure that it doesn't get deleted while in use here.
*/
void setRenderer (Renderer::Ptr newRenderer) noexcept { renderer = newRenderer; }
/** Returns the visualiser currently attached to this block (nullptr for none). */
Renderer::Ptr getRenderer() const noexcept { return renderer; }
/** The device that this LEDGrid belongs to. */
Block& block;
private:
Renderer::Ptr renderer;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LEDGrid)
};
} // namespace juce

+ 0
- 64
modules/juce_blocks_basics/blocks/juce_LEDRow.h View File

@@ -1,64 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
Represents an LED strip on a device.
@tags{Blocks}
*/
class LEDRow
{
public:
LEDRow (Block&);
/** Destructor. */
virtual ~LEDRow();
//==============================================================================
/** Return the number of LEDs in the row. */
virtual int getNumLEDs() const = 0;
/** Sets the colour of the corresponding LED. */
virtual void setLEDColour (int index, LEDColour newColour) = 0;
/** Overlays all LEDs with a single colour.
Whilst the overlay is set subsequent calls to setLEDColour will happen
*behind* the overlay, and will be invisible to the user until the
overlay is removed.
*/
virtual void setOverlayColour (LEDColour newColour) = 0;
/* Removes an overlay colour. */
virtual void resetOverlayColour() = 0;
/** The device that these lights belong to. */
Block& block;
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LEDRow)
};
} // namespace juce

+ 0
- 53
modules/juce_blocks_basics/blocks/juce_StatusLight.h View File

@@ -1,53 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
Represents a status LED on a device.
@tags{Blocks}
*/
class StatusLight
{
public:
StatusLight (Block&);
/** Destructor. */
virtual ~StatusLight();
//==============================================================================
/** Returns a name to describe this light. */
virtual String getName() const = 0;
/** Changes the light's colour. */
virtual bool setColour (LEDColour newColour) = 0;
/** The device that this LED belongs to. */
Block& block;
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StatusLight)
};
} // namespace juce

+ 0
- 152
modules/juce_blocks_basics/blocks/juce_TouchList.h View File

@@ -1,152 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
Utility class to hold a list of TouchSurface::Touch objects with different
indices and blockUIDs, where each touch has a mapping to some kind of
user-supplied data value.
The Type template is a user-defined type of object that will be stored for
each touch element. The type must be default-constructable and copyable.
@tags{Blocks}
*/
template <typename Type>
class TouchList
{
public:
/** Creates an empty touch list. */
TouchList() = default;
/** Destructor. */
~TouchList() = default;
/** Returns the number of entries in the touch list. */
int size() const noexcept { return touches.size(); }
/** Returns the user data object that corresponds to the given touch.
This will also update the stored state of the TouchEntry::touch value
for this touch index.
*/
Type& getValue (const TouchSurface::Touch& touch)
{
auto* t = find (touch);
if (t == nullptr)
{
touches.add ({ touch, {} });
return touches.getReference (touches.size() - 1).value;
}
else
{
t->touch = touch;
return t->value;
}
}
/** Returns true if a touch is already in the list. */
bool contains (const TouchSurface::Touch& touch) const noexcept
{
return find (touch) != nullptr;
}
/** Updates the entry for the given touch, copying in the new state.
If no entry with the same index and blockUID exists then a new entry is
created. If given a touch which is a touch-end, this will *remove* any
corresponding entries from the list.
*/
void updateTouch (const TouchSurface::Touch& touch)
{
if (touch.isTouchEnd)
{
for (int i = touches.size(); --i >= 0;)
if (matches (touches.getReference(i).touch, touch))
touches.remove (i);
jassert (! contains (touch));
}
else
{
auto t = find (touch);
if (t == nullptr)
touches.add ({ touch, {} });
else
t->touch = touch;
}
}
/** Holds the current state of a touch, along with the user-data associated with it. */
struct TouchEntry
{
TouchSurface::Touch touch;
Type value;
};
/** If a touch is in the list, returns a pointer to the TouchEntry.
Otherwise, returns nullptr.
*/
const TouchEntry* find (const TouchSurface::Touch& touch) const noexcept
{
for (auto& t : touches)
if (matches (t.touch, touch))
return &t;
return nullptr;
}
TouchEntry* find (const TouchSurface::Touch& touch) noexcept
{
return const_cast<TouchEntry*> (static_cast<const TouchList&> (*this).find (touch));
}
/** Allows iterator access to the list of touch entries. */
TouchEntry* begin() noexcept { return touches.begin(); }
const TouchEntry* begin() const noexcept { return touches.begin(); }
/** Allows iterator access to the list of touch entries. */
TouchEntry* end() noexcept { return touches.end(); }
const TouchEntry* end() const noexcept { return touches.end(); }
/** Retrieve a reference to particular item in the list of touch entries. */
TouchEntry& operator[] (const int index) { return touches.getReference (index); }
/** Resets all contents, doest not generate any call-backs. */
void clear() noexcept { touches.clear(); }
private:
//==============================================================================
static bool matches (const TouchSurface::Touch& t1,
const TouchSurface::Touch& t2) noexcept
{
return t1.index == t2.index && t1.blockUID == t2.blockUID;
}
Array<TouchEntry> touches;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TouchList)
};
} // namespace juce

+ 0
- 138
modules/juce_blocks_basics/blocks/juce_TouchSurface.h View File

@@ -1,138 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
Represents the touch surface of a BLOCKS device.
@tags{Blocks}
*/
class TouchSurface
{
public:
TouchSurface (Block&);
/** Destructor. */
virtual ~TouchSurface();
//==============================================================================
/** Structure used to describe touch properties */
struct Touch
{
/** A touch index, which will stay constant for each finger as it is tracked. */
int index;
/** The X position of this touch on the device, in logical units starting from 0 (left).
See Block::getWidth() for the maximum X value on the device.
*/
float x;
/** An approximation of the velocity at which the X value is changing, measured in
units/second. This is intended as a useful hint to help with gesture detection, but
may be 0 if the device doesn't provide this data.
*/
float xVelocity;
/** The Y position of this touch on the device, in logical units starting from 0 (top).
See Block::getHeight() to find the maximum Y on the device.
*/
float y;
/** An approximation of the velocity at which the Y value is changing, measured in
units/second. This is intended as a useful hint to help with gesture detection, but
may be 0 if the device doesn't provide this data.
*/
float yVelocity;
/** The current pressure of this touch, in the range 0.0 (no pressure) to 1.0 (very hard). */
float z;
/** The rate at which pressure is currently changing, measured in units/second. This is
intended as a useful hint to help with gesture detection, but may be 0 if the device
doesn't provide this data.
*/
float zVelocity;
/** The timestamp of this event, in milliseconds since the device was booted. */
Block::Timestamp eventTimestamp;
/** True if this is the first event for this finger/index. */
bool isTouchStart;
/** True if this is the final event as this finger/index is lifted off. */
bool isTouchEnd;
/** The ID of the block that generated this touch. */
Block::UID blockUID;
/** The initial X position of the touchStart event corresponding to this finger/index. */
float startX;
/** The initial Y position of the touchStart event corresponding to this finger/index. */
float startY;
};
//==============================================================================
/** Forces a touch-off message for all active touches. */
virtual void cancelAllActiveTouches() noexcept = 0;
/** For the on-screen seaboard view, this will return the number of keys.
For other types of touch-surface, it will return 0. */
virtual int getNumberOfKeywaves() const = 0;
//==============================================================================
/** Receives callbacks when a touch moves or changes pressure. */
struct Listener
{
virtual ~Listener();
virtual void touchChanged (TouchSurface&, const Touch&) = 0;
};
/** Testing feature: this allows you to inject touches onto a touch surface. */
void callListenersTouchChanged (const TouchSurface::Touch& t)
{
listeners.call ([this, &t] (Listener& l) { l.touchChanged (*this, t); });
}
/** Adds a listener to be called when the surface is touched. */
void addListener (Listener*);
/** Removes a previously-registered listener. */
void removeListener (Listener*);
//==============================================================================
/** The block that owns this touch surface. */
Block& block;
protected:
//==============================================================================
ListenerList<Listener> listeners;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TouchSurface)
};
} // namespace juce

+ 0
- 785
modules/juce_blocks_basics/juce_LittleFootFunctions.dox View File

@@ -1,785 +0,0 @@
/** @defgroup LittleFootFunctions LittleFoot Functions
Functions available in the LittleFoot language
@{
*/
/** Reads and returns the value of a single byte from the heap.
@param byteIndex the index (in bytes) of the byte to read
@returns the value of the byte
*/
int getHeapByte (int byteIndex);
/** Reads 4 bytes from the heap and returns the value as an integer.
@param byteIndex the index (in bytes) of the start of the 4 bytes to read
@returns the value of the 4 bytes as an integer
*/
int getHeapInt (int byteIndex);
/** Reads a sequence of bits from the heap and returns the value as an integer.
@param startBitIndex the index (in bits) of the start of the sequence of bits to read
@param numBits how many bits to read
@returns the value of the sequence of bits as an integer
*/
int getHeapBits (int startBitIndex, int numBits);
/** Writes a single byte to the heap.
@param byteIndex the index (in bytes) of the byte to set
@param newValue the new value to set this byte to
*/
void setHeapByte (int byteIndex, int newValue);
/** Writes 4 bytes to the heap.
@param byteIndex the index (in bytes) of the start of the 4 bytes to set
@param newValue the new value to set the 4 bytes to
*/
void setHeapInt (int byteIndex, int newValue);
/** Returns the smaller of two integer values.
@param a The first parameter
@param b The second parameter
@retval The minimum of a and b
*/
int min (int a, int b);
/** Returns the smaller of two floating point values.
@param a The first parameter
@param b The second parameter
@retval The minimum of a and b
*/
float min (float a, float b);
/** Returns the larger of two integer values.
@param a The first parameter
@param b The second parameter
@retval The maximum of a and b
*/
int max (int a, int b);
/** Returns the larger of two floating point values.
@param a The first parameter
@param b The second parameter
@retval The maximum of a and b
*/
float max (float a, float b);
/** Constrains an integer value to keep it within a given range.
@param lowerLimit the minimum value to return
@param upperLimit the maximum value to return
@param valueToConstrain the value to try to return
@returns the closest value to valueToConstrain which lies between lowerLimit
and upperLimit (inclusive)
*/
int clamp (int lowerLimit, int upperLimit, int valueToConstrain);
/** Constrains a floating point value to keep it within a given range.
@param lowerLimit the minimum value to return
@param upperLimit the maximum value to return
@param valueToConstrain the value to try to return
@returns the closest value to valueToConstrain which lies between lowerLimit
and upperLimit (inclusive)
*/
float clamp (float lowerLimit, float upperLimit, float valueToConstrain);
/** Returns the absolute value of an integer value.
@param arg The argument to compute the absolute value of
@retval either -arg if arg is negative or arg if arg is positive
*/
int abs (int arg);
/** Returns the absolute value of a floating point value.
@param arg The argument to compute the absolute value of
@retval either -arg if arg is negative or arg if arg is positive
*/
float abs (float arg);
/** Remaps a value from a source range to a target range.
@param value the value within the source range to map
@param sourceMin the minimum value of the source range
@param sourceMax the maximum value of the source range
@param destMin the minimum value of the destination range
@param destMax the maximum value of the destination range
@returns the original value mapped to the destination range
*/
float map (float value, float sourceMin, float sourceMax, float destMin, float destMax);
/** Remaps a value from a source range to the range 0 - 1.0.
@param value the value within the source range to map
@param sourceMin the minimum value of the source range
@param sourceMax the maximum value of the source range
@returns the original value mapped to the range 0 - 1.0
*/
float map (float value, float sourceMin, float sourceMax);
/** Performs a modulo operation (can cope with the dividend being negative).
The divisor must be greater than zero.
@returns the result of the modulo operation
*/
int mod (int dividend, int divisor);
/** Returns a random floating-point number.
@returns a random value in the range 0 (inclusive) to 1.0 (exclusive)
*/
float getRandomFloat();
/** Returns a random integer, limited to a given range.
@returns a random integer between 0 (inclusive) and maxValue (exclusive).
*/
int getRandomInt (int maxValue);
/** Returns the number of milliseconds since a fixed event (usually system startup).
@returns a monotonically increasing value which is unaffected by changes to the
system clock. It should be accurate to within a few millisecseconds.
*/
int getMillisecondCounter();
/** Returns the length of time spent in the current function call in milliseconds.
@returns the length of time spent in the current function call in milliseconds.
*/
int getTimeInCurrentFunctionCall();
/** Logs an integer value to the console.
@param data The 32 bit signed integer to log to the topology as an integer
*/
void log (int data);
/** Logs a hexadecimal value to the console.
@param data The 32 bit signed integer to log to the topology as a hexidecimal int
*/
void logHex (int data);
/** Sends a 1-byte short midi message. */
void sendMIDI (int byte0);
/** Sends a 2-byte short midi message. */
void sendMIDI (int byte0, int byte1);
/** Sends a 3-byte short midi message. */
void sendMIDI (int byte0, int byte1, int byte2);
/** Sends a key-down message.
@param channel the midi channel, in the range 0 to 15
@param noteNumber the key number, in the range 0 to 127
@param velocity the velocity, in the range 0 to 127
*/
void sendNoteOn (int channel, int noteNumber, int velocity);
/** Sends a key-up message.
@param channel the midi channel, in the range 0 to 15
@param noteNumber the key number, in the range 0 to 127
@param velocity the velocity, in the range 0 to 127
*/
void sendNoteOff (int channel, int noteNumber, int velocity);
/** Sends an aftertouch message.
@param channel the midi channel, in the range 0 to 15
@param noteNumber the key number, in the range 0 to 127
@param level the amount of aftertouch, in the range 0 to 127
*/
void sendAftertouch (int channel, int noteNumber, int level);
/** Sends a controller message.
@param channel the midi channel, in the range 0 to 15
@param controller the type of controller
@param value the controller value
*/
void sendCC (int channel, int controller, int value);
/** Sends a pitch bend message.
@param channel the midi channel, in the range 0 to 15
@param position the wheel position, in the range 0 to 16383
*/
void sendPitchBend (int channel, int position);
/** Sends a channel-pressure change event.
@param channel the midi channel, in the range 0 to 15
@param pressure the pressure, in the range 0 to 127
*/
void sendChannelPressure (int channel, int pressure);
/** Sets the MIDI channel range.
@param useMPE whether to use MPE mode
@param lowChannel the lowest MIDI channel
@param highChannel the highest MIDI channel
*/
void setChannelRange (bool useMPE, int lowChannel, int highChannel);
/** Assigns a MIDI channel to a note number.
@param noteNumber the note number to assign the channel to
@returns the MIDI channel that has been assigned
*/
int assignChannel (int noteNumber);
/** Deassigns a channel from a note number.
@param noteNumber the note number to deassign
@param channel the MIDI channel
*/
void deassignChannel (int noteNumber, int channel);
/** Returns the channel that is being used for control messages.
@returns the channel that is being used for control messages. (If MPE is enabled then this will be the first channel.)
*/
int getControlChannel();
/** Sets whether duplicate notes should be filtered out when MPE is enabled. */
void useMPEDuplicateFilter (bool active);
/** Use this method to draw the display.
The block will call this approximately 25 times per second.
*/
void repaint();
/** Called when a button is pushed.
@param index the index of the button that was pushed
*/
void handleButtonDown (int index);
/** Called when a button is released.
@param index the index of the button that was released
*/
void handleButtonUp (int index);
/** Call this when a control block button is pressed to trigger the buttons default behaviour.
@param buttonIndex the index of the button
@note Requires >= 0.2.5 firmware
@note Only valid with a control block
@see initControl onControlRelease
*/
void onControlPress (int buttonIndex);
/** Call this when a control block button is released to trigger the buttons default behaviour.
@param buttonIndex the index of the button
@note Requires >= 0.2.5 firmware
@note Only valid with a control block
@see initControl onControlPress
*/
void onControlRelease (int buttonIndex);
/** Called when a touch event starts.
Block units follow the number of DNA connectors on the side of the device.
For instance, a Lightpad Block is 2x2 and a Control Block is 2x1.
@param index the touch index, which will stay constant for each finger as it is tracked
@param x the X position of this touch on the device, in block units starting from 0 (left)
@param y the Y position of this touch on the device, in block units starting from 0 (top)
@param z the current pressure of this touch, in the range 0.0 (no pressure) to 1.0 (very hard)
@param vz the rate at which pressure is currently changing, measured in units/second
*/
void touchStart (int index, float x, float y, float z, float vz);
/** Called when a touch event moves.
Block units follow the number of DNA connectors on the side of the device.
For instance, a Lightpad Block is 2x2 and a Control Block is 2x1.
@param index the touch index, which will stay constant for each finger as it is tracked
@param x the X position of this touch on the device, in block units starting from 0 (left)
@param y the Y position of this touch on the device, in block units starting from 0 (top)
@param z the current pressure of this touch, in the range 0.0 (no pressure) to 1.0 (very hard)
@param vz the rate at which pressure is currently changing, measured in units/second
*/
void touchMove (int index, float x, float y, float z, float vz);
/** Called when a touch event ends.
Block units follow the number of DNA connectors on the side of the device.
For instance, a Lightpad Block is 2x2 and a Control Block is 2x1.
@param index the touch index, which will stay constant for each finger as it is tracked
@param x the X position of this touch on the device, in block units starting from 0 (left)
@param y the Y position of this touch on the device, in block units starting from 0 (top)
@param z the current pressure of this touch, in the range 0.0 (no pressure) to 1.0 (very hard)
@param vz the rate at which pressure is currently changing, measured in units/second
*/
void touchEnd (int index, float x, float y, float z, float vz);
/** Called when a program is loaded onto the block and is about to start. Do any setup here. */
void initialise();
/** Called when a block receives a MIDI message. */
void handleMIDI (int byte0, int byte1, int byte2);
/** Called when a block receives a message.
@see sendMessageToBlock
*/
void handleMessage (int param1, int param2, int param3);
/** Combines a set of 8-bit ARGB values into a 32-bit colour and returns the result.
@return a 32-bit colour
@param alpha The alpha in range 0 - 255 inclusive
@param red The red in range 0 - 255 inclusive
@param green The green in range 0 - 255 inclusive
@param blue The blue in range 0 - 255 inclusive
*/
int makeARGB (int alpha, int red, int green, int blue);
/** Blends the overlaid ARGB colour onto the base one and returns the new colour.
@param baseColour the colour to blend on to
@param overlaidColour The colour to blend in to the baseColour
@returns The blended colour
*/
int blendARGB (int baseColour, int overlaidColour);
/** Displays an animation indicating the current battery level of this block.
A control block will light up its top LEDs indicating battery level and a lightpad
block will draw the battery level on the display.
@note Requires >= 0.2.5 firmware
*/
void displayBatteryLevel();
/** Clears the display and sets all the LEDs to black. */
void clearDisplay();
/** Clears the display and sets all the LEDs to a specified colour.
@param rgb the colour to use (0xff...)
*/
void clearDisplay (int rgb);
/** Sets a pixel to a specified colour with full alpha.
@param rgb the colour to use (0xff...)
@param x the x coordinate of the pixel to fill
@param y the y coordinate of the pixel to fill
*/
void fillPixel (int rgb, int x, int y);
/** Blends the current pixel colour with a specified colour.
@param argb the colour to use
@param x the x coordinate of the pixel to blend
@param y the y coordinate of the pixel to blend
*/
void blendPixel (int argb, int x, int y);
/** Fills a rectangle on the display with a specified colour.
@param rgb the colour to use (0xff...)
@param x the x coordinate of the rectangle to draw
@param y the y coordinate of the rectangle to draw
@param width the width of the rectangle to draw
@param height the height of the rectangle to draw
*/
void fillRect (int rgb, int x, int y, int width, int height);
/** Blends a rectangle on the display with a specified colour.
@param argb the colour to use
@param x the x coordinate of the rectangle to blend
@param y the y coordinate of the rectangle to blend
@param width the width of the rectangle to blend
@param height the height of the rectangle to blend
*/
void blendRect (int argb, int x, int y, int width, int height);
/** Fills a rectangle on the display with four corner colours blended together.
@param colourNW the colour to use in the north west corner of the rectangle
@param colourNE the colour to use in the north east corner of the rectangle
@param colourSE the colour to use in the south east corner of the rectangle
@param colourSW the colour to use in the south west corner of the rectangle
@param x the x coordinate of the rectangle
@param y the y coordinate of the rectangle
@param width the width of the rectangle
@param height the height of the rectangle
*/
void blendGradientRect (int colourNW, int colourNE, int colourSE, int colourSW, int x, int y, int width, int height);
/** Blends a circle on the display with a specified colour.
@param argb the colour to use
@param xCentre the x position of the circle's centre in block units
@param yCentre the y position of the circle's centre in block units
@param radius the radius of the circle in block units
@param fill if true then the circle will be filled, if false the circle will be an outline
@note Requires >= 0.2.5 firmware
*/
void blendCircle (int argb, float xCentre, float yCentre, float radius, bool fill);
/** Draws a number on the display.
@param value the number to draw between 0 and 99
@param colour the colour to use
@param x the x coordinate to use
@param y the y coordinate to use
*/
void drawNumber (int value, int colour, int x, int y);
/** Returns the current firmware version of this block.
@returns The firmware version of the form 0xMJMIRV (where MJ = Major, MI = Minor, RV = Revision)
@note Requires >= 0.2.5 firmware
*/
int getFirmwareVersion();
/** Returns the battery level of this block, between 0 and 1.0.
@returns the battery level of this block, between 0 and 1.0.
@note Requires >= 0.2.5 firmware
*/
float getBatteryLevel();
/** Returns true if this block's battery is charging.
@returns true if this block's battery is charging
@note Requires >= 0.2.5 firmware
*/
bool isBatteryCharging();
/** Sets whether status overlays should be displayed on this block.
@note Requires >= 0.2.5 firmware
*/
void setStatusOverlayActive (bool active);
/** Sets whether power saving mode should be enabled on this block.
@note Requires >= 0.2.5 firmware
*/
void setPowerSavingEnabled (bool enabled);
/** Returns the type of the block with a given ID.
@returns an enum indicating the type of block @see Block::Type
@note Requires >= 0.2.5 firmware
*/
int getBlockTypeForID (int blockID);
/** Sends a message to the block with the specified ID.
This will be processed in the handleMessage() callback.
@param blockID the ID of the block to send this message to
@param param1 the first chunk of data to send
@param param2 the second chunk of data to send
@param param3 the third chunk of data to send
*/
void sendMessageToBlock (int blockID, int param1, int param2, int param3);
/** Sends a message to the host. To receive this the host will need to implement
the Block::ProgramEventListener::handleProgramEvent() method.
@param param1 the first chunk of data to send
@param param2 the second chunk of data to send
@param param3 the third chunk of data to send
*/
void sendMessageToHost (int param1, int param2, int param3);
/** Draws a pressure point with a specified colour and pressure.
@param argb the colour to use
@param touchX the x position of the touch in block units
@param touchY the y position of the touch in block units
@param touchZ the pressure value of the touch
*/
void addPressurePoint (int argb, float touchX, float touchY, float touchZ);
/** Draws the pressure map on the display. */
void drawPressureMap();
/** Fades the pressure map on the display. */
void fadePressureMap();
/** Links a another block to this control block.
@param blockID the ID of the block to link
@note Requires >= 0.2.5 firmware
@note Only valid with a control block
*/
void linkBlockIDtoController (int blockID);
/** Repaints the control block display.
@note Requires >= 0.2.5 firmware
@note Only valid with a control block
*/
void repaintControl();
/** Initialises one of the control block buttons.
@param buttonIndex the index of the button to initialise
@param modeToUse the mode to use for this button @see ControlMode
@param outputType the control type to use @see ControlType
@param val the current value to set this button to
@param min the minimum value for this button
@param max the maximum value for this button
@param index the item index
@param onColourToUse the colour to use when this button is on
@param offColourToUse the colour to use when this button is off
@note Requires >= 0.2.5 firmware
@note Only valid with a control block
*/
void initControl (int buttonIndex, int modeToUse, int outputType, int val, int min, int max,
int index, int onColourToUse, int offColourToUse);
/** Control type for use with initControl
@see initControl
*/
enum ControlType
{
internalConfig = 0,
midiCC,
sysex
};
/** Control mode for use with initControl
@see initControl
*/
enum ControlMode
{
toggle = 0,
togglePulse,
gate,
trigger,
cycle,
incDec,
triState
};
/** Forces a touch event to be handled as seaboard playing.
@param touchIndex the index of the touch event
@note Requires >= 0.2.5 firmware
@note Only valid on a Seaboard
*/
void handleTouchAsSeaboard (int touchIndex);
/** Returns the number of blocks in the current topology.
@note Requires >= 0.2.5 firmware
*/
int getNumBlocksInTopology();
/** Returns the ID of a block at a given index in the topology.
@param index The index of the block to find in the topology
@returns int The id of the block
@note Requires >= 0.2.5 firmware
*/
int getBlockIDForIndex (int index);
/** Returns true if this block is directly connected to the host,
as opposed to only being connected to a different block via a connection port.
@note Requires >= 0.2.5 firmware
*/
bool isMasterBlock();
/** Returns true if this block is connected to the host computer, this can be
directly or through connections to other blocks.
@note Requires >= 0.2.5 firmware
*/
bool isConnectedToHost();
/** Returns the ID of a block connected to a specified port on this block.
@note Requires >= 0.2.5 firmware
*/
int getBlockIDOnPort (int port);
/** Returns the port number that is connected to the master block. Returns 0xFF if there is
no port connected to master.
@returns the port number that is connected to the master block. Returns 0xFF if there is
no port connected to master.
@note Requires >= 0.2.5 firmware
*/
int getPortToMaster();
/** Returns the horizontal distance between this block and the master block in block units.
@note Requires >= 0.2.5 firmware
*/
int getHorizontalDistFromMaster();
/** Returns the vertical distance between this block and the master block in block units.
@note Requires >= 0.2.5 firmware
*/
int getVerticalDistFromMaster();
/** Returns the angle of this block relative to the master block in degrees.
@note Requires >= 0.2.5 firmware
*/
int getAngleFromMaster();
/** Sets whether this block should auto-rotate when its angle relative to the master block changes.
@note Requires >= 0.2.5 firmware
*/
void setAutoRotate (bool enabled);
/** Returns the index of this block in the current cluster.
@note Requires >= 0.2.5 firmware
*/
int getClusterIndex();
/** Returns how many blocks wide the current cluster is.
@returns the width of the cluster (note that a single block will return 1 here)
@note Requires >= 0.2.5 firmware
*/
int getClusterWidth();
/** Returns how many blocks high the current cluster is.
@returns the height of the cluster (note that a single block will return 1 here)
@note Requires >= 0.2.5 firmware
*/
int getClusterHeight();
/** Returns the x index of this block in the current cluster.
@returns int The cluster x position. (0, 0) is considered to be the top left block
@note Requires >= 0.2.5 firmware
*/
int getClusterXpos();
/** Returns the y index of this block in the current cluster.
@returns int The cluster x position. (0, 0) is considered to be the top left block
@note Requires >= 0.2.5 firmware
*/
int getClusterYpos();
/** Returns the number of blocks in the current cluster.
@returns the number of blocks in the current cluster.
@note Requires >= 0.2.5 firmware
*/
int getNumBlocksInCurrentCluster();
/** Returns the block ID for a block in the current cluster.
@param index the cluster index of the block to get the ID of
@note Requires >= 0.2.5 firmware
*/
int getBlockIdForBlockInCluster (int index);
/** Returns true if the master block is in the current cluster.
@note Requires >= 0.2.5 firmware
*/
bool isMasterInCurrentCluster();
/** Returns current value of one of the local config items.
@param item the config item to get (see ConfigItemId enum in juce_BlocksProtocolDefinitions.h)
@note Requires >= 0.2.5 firmware
*/
int getLocalConfig (int item);
/** Sets the current value of one of the local config items.
@param item the config item to set the value of (see ConfigItemId enum in juce_BlocksProtocolDefinitions.h)
@param value the value to set the config to
@note Requires >= 0.2.5 firmware
*/
void setLocalConfig (int item, int value);
/** Sets the local config of a block to the config item of a remote block.
@param longAddress the address of the remote block
@param item the config item (see ConfigItemId enum in juce_BlocksProtocolDefinitions.h)
@note Requires >= 0.2.5 firmware
*/
void requestRemoteConfig (int longAddress, int item);
/** Sets the config of a remote block.
@param longAddress the address of the remote block
@param item the config item (see ConfigItemId enum in juce_BlocksProtocolDefinitions.h)
@param value the value to set the config to
@note Requires >= 0.2.5 firmware
*/
void setRemoteConfig (int longAddress, int item, int value);
/** Sets the range of one of the local config items.
@param item the config item to set the range of (see ConfigItemId enum in juce_BlocksProtocolDefinitions.h)
@param min the minimum value this config item should use
@param max the maximum value this config item should use
@note Requires >= 0.2.5 firmware
*/
void setLocalConfigItemRange (int item, int min, int max);
/** Sets whether a local config item should be active.
@param item the config item to set active (see ConfigItemId enum in juce_BlocksProtocolDefinitions.h)
@param isActive sets whether the config should be active or not
@param saveToFlash if true then this config item will be saved to the flash memory of the block
@note Requires >= 0.2.5 firmware
*/
void setLocalConfigActiveState (int item, bool isActive, bool saveToFlash);
/** @} */

+ 0
- 44
modules/juce_blocks_basics/juce_blocks_basics.cpp View File

@@ -1,44 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include "juce_blocks_basics.h"
#include <regex>
namespace juce
{
#include "littlefoot/juce_LittleFootRemoteHeap.h"
}
#include "protocol/juce_BitPackingUtilities.h"
#include "protocol/juce_BlocksProtocolDefinitions.h"
#include "protocol/juce_HostPacketDecoder.h"
#include "protocol/juce_HostPacketBuilder.h"
#include "blocks/juce_BlockConfigManager.h"
#include "protocol/juce_BlockModels.h"
#include "blocks/juce_Block.cpp"
#include "blocks/juce_BlocksVersion.cpp"
#include "topology/juce_BlockGraph.cpp"
#include "topology/juce_PhysicalTopologySource.cpp"
#include "topology/juce_RuleBasedTopologySource.cpp"
#include "visualisers/juce_DrumPadLEDProgram.cpp"
#include "visualisers/juce_BitmapLEDProgram.cpp"

+ 0
- 85
modules/juce_blocks_basics/juce_blocks_basics.h View File

@@ -1,85 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this module, and is read by
the Projucer to automatically generate project code that uses it.
For details about the syntax and how to create or use a module, see the
JUCE Module Format.md file.
BEGIN_JUCE_MODULE_DECLARATION
ID: juce_blocks_basics
vendor: juce
version: 6.0.8
name: Provides low-level control over ROLI BLOCKS devices
description: JUCE wrapper for low-level control over ROLI BLOCKS devices.
website: http://developer.roli.com
license: ISC
minimumCppStandard: 14
dependencies: juce_audio_devices
END_JUCE_MODULE_DECLARATION
*******************************************************************************/
#pragma once
//==============================================================================
#include <juce_events/juce_events.h>
#include <juce_audio_devices/juce_audio_devices.h>
namespace juce
{
class TouchSurface;
class LEDGrid;
class LEDRow;
class StatusLight;
class LightRing;
class ControlButton;
}
#include "blocks/juce_Block.h"
#include "blocks/juce_TouchSurface.h"
#include "blocks/juce_LEDGrid.h"
#include "blocks/juce_LEDRow.h"
#include "blocks/juce_ControlButton.h"
#include "blocks/juce_TouchList.h"
#include "blocks/juce_StatusLight.h"
#include "blocks/juce_BlocksVersion.h"
#include "topology/juce_Topology.h"
#include "topology/juce_BlockGraph.h"
#include "topology/juce_TopologySource.h"
#include "topology/juce_PhysicalTopologySource.h"
#include "topology/juce_RuleBasedTopologySource.h"
#include "visualisers/juce_DrumPadLEDProgram.h"
#include "visualisers/juce_BitmapLEDProgram.h"
namespace juce
{
#include "littlefoot/juce_LittleFootRunner.h"
#include "littlefoot/juce_LittleFootCompiler.h"
}

+ 0
- 125
modules/juce_blocks_basics/littlefoot/LittleFoot Language README.txt View File

@@ -1,125 +0,0 @@
@verbatim
This is a brief description of the LittleFoot language syntax
Littlefoot basically looks like C, but has no pointers, and the only types are:
- int (32-bit signed integer)
- float (32-bit float)
- bool
The top-level syntax of a program is a list of global variables and global functions. Order
of declaration isn't important, you can use functions and variables that are declared later
in the file without needing to pre-declare anything.
Comments are the same format as C/C++/java/etc
So for example:
// global variables. These are initialised to 0 or false when the program is loaded, and
// you can't currently provide any other initial values
int foo, bar;
int getTheNextNumber()
{
return addTwoNumbers (++foo, 2.0) * 3;
}
float addTwoNumbers (int x, float y)
{
return float (x) + y;
}
The usual control-flow operators are provided, all with C++ style syntax:
if/else
for
while
do...while
continue
break
return
(There isn't currently a switch statement though)
Arithmetic ops are the usual suspects, (with the standard operator precedence):
+, -, *, /, %
||, &&, |, &, ~, ^
++, --, +=, -=, *=, /=, %=, |=, &=, ^=
==, !=, <, >, <=, >=, !
<<, >>, <<=, >>=, >>>
Ternary operator (x ? y : z)
Local variables are declared in C++-style syntax:
void foo()
{
int x = 123;
float y = 12.0, z = 1.0e5;
bool b = y > 20.0;
}
Casts of primitive types are done with function-style syntax, e.g.
int x = int (123.0);
float f = float (getIntegerValue());

Arrays of any type can be created in the global scope using C-style syntax.
Elements of arrays can be set and read from functions.
Arrays cannot be created within functions or returned from functions.

For example:

int foo[12];

void initialise()
{
for (int i = 0; i < 12; ++i)
foo[i] = i;
}
The program communicates with the host computer by using a shared area of memory
called the heap which the host can change. There are some built-in functions
available for the program to use to read from the heap:
int getHeapByte (int byteIndex); // reads a single byte from the heap
int getHeapInt (int byteIndex); // reads 4 bytes from the heap as an integer
int getHeapBits (int startBitIndex, int numBits); // reads a sequence of bits from the heap and returns it as an integer
void setHeapByte (int byteIndex, int newValue); // writes a single byte to the heap
void setHeapInt (int byteIndex, int newValue); // writes 4 bytes to the heap
Depending on the context, there will also be some built-in functions that the
program can use to do what it needs to do. Currently in the standard Pad BLOCK program,
you have the following functions available:
int makeARGB (int alpha, int red, int green, int blue); // combines a set of 8-bit ARGB values into a 32-bit colour
int blendARGB (int baseColour, int overlaidColour); // blends the overlaid ARGB colour onto the base one and returns the new colour
void fillPixel (int rgb, int x, int y); // sets a LED colour on the display
void fillRect (int rgb, int x, int y, int width, int height); // fills a rectangle on the display
A BLOCKs program needs to provide a repaint() function which the block will call
at approximately 25Hz to draw the display. For example, here's a simple program that
draws a moving rectangle:
int rectangleX;
void repaint()
{
fillRect (0xff000044, 0, 0, 15, 15); // fill the display with dark blue
fillRect (0xffffffff, rectangleX, 5, 4, 4); // draw a white rectangle
rectangleX = (rectangleX + 1) % 15; // animate our position and make it wrap
}
The host can also send simple event messages to the program, and to receive these you must
provide a function called "handleMessage", e.g.
void handleMessage (int param1, int param2)
{
// do something with the two integer parameters that the app has sent...
}
@endverbatim

+ 0
- 2454
modules/juce_blocks_basics/littlefoot/juce_LittleFootCompiler.h
File diff suppressed because it is too large
View File


+ 0
- 455
modules/juce_blocks_basics/littlefoot/juce_LittleFootRemoteHeap.h View File

@@ -1,455 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#ifndef JUCE_DUMP_LITTLEFOOT_HEAP_STATUS
#define JUCE_DUMP_LITTLEFOOT_HEAP_STATUS 0
#endif
#if JUCE_DUMP_LITTLEFOOT_HEAP_STATUS
#define JUCE_LOG_LITTLEFOOT_HEAP(text) DBG(text)
#else
#define JUCE_LOG_LITTLEFOOT_HEAP(text)
#endif
namespace littlefoot
{
//==============================================================================
/**
This class manages the synchronisation of a remote block of heap memory used
by a littlefoot program running on a block.
Data in the block can be changed by calling setByte, setBytes, setBits etc, and
these changes will be flushed to the device when sendChanges is called.
@tags{Blocks}
*/
template <typename ImplementationClass>
struct LittleFootRemoteHeap
{
LittleFootRemoteHeap (uint32 blockSizeToUse) noexcept : blockSize (blockSizeToUse)
{
resetDeviceStateToUnknown();
}
void reset()
{
JUCE_LOG_LITTLEFOOT_HEAP ("Resetting heap state");
clearTargetData();
resetDeviceStateToUnknown();
lastPacketIndexReceived = 0;
}
void clearTargetData() noexcept
{
JUCE_LOG_LITTLEFOOT_HEAP ("Clearing target heap data");
zeromem (targetData, sizeof (targetData));
needsSyncing = true;
programStateKnown = false;
}
void resetDeviceStateToUnknown()
{
JUCE_LOG_LITTLEFOOT_HEAP ("Resetting device state to unknown");
needsSyncing = true;
programStateKnown = false;
messagesSent.clear();
resetDataRangeToUnknown (0, ImplementationClass::maxBlockSize);
}
void resetDataRangeToUnknown (size_t offset, size_t size) noexcept
{
auto* latestState = getLatestExpectedDataState();
for (size_t i = 0; i < size; ++i)
latestState[offset + i] = unknownByte;
}
void setByte (size_t offset, uint8 value) noexcept
{
if (offset >= blockSize)
{
jassertfalse;
return;
}
if (targetData[offset] != value)
{
targetData[offset] = value;
needsSyncing = true;
if (offset < programSize)
programStateKnown = false;
}
}
void setBytes (size_t offset, const uint8* data, size_t num) noexcept
{
for (size_t i = 0; i < num; ++i)
setByte (offset + i, data[i]);
}
void setBits (uint32 startBit, uint32 numBits, uint32 value) noexcept
{
if (startBit + numBits > 8 * blockSize)
{
jassertfalse;
return;
}
if (readLittleEndianBitsInBuffer (targetData, startBit, numBits) != value)
{
JUCE_LOG_LITTLEFOOT_HEAP ("Set bits sync " << String (startBit) << " " << String (numBits) << String (value));
writeLittleEndianBitsInBuffer (targetData, startBit, numBits, value);
needsSyncing = true;
if (startBit < programSize)
programStateKnown = false;
}
}
uint8 getByte (size_t offset) noexcept
{
if (offset < blockSize)
return targetData [offset];
jassertfalse;
return 0;
}
bool isFullySynced() const noexcept
{
return ! needsSyncing;
}
static bool isAllZero (const uint8* data, size_t size) noexcept
{
for (size_t i = 0; i < size; ++i)
if (data[i] != 0)
return false;
return true;
}
void sendChanges (ImplementationClass& bi, bool forceSend)
{
if ((needsSyncing && messagesSent.isEmpty()) || forceSend)
{
for (int maxChanges = 30; --maxChanges >= 0;)
{
if (isAllZero (targetData, blockSize))
break;
uint16 data[ImplementationClass::maxBlockSize];
auto* latestState = getLatestExpectedDataState();
for (uint32 i = 0; i < blockSize; ++i)
data[i] = latestState[i];
uint32 packetIndex = messagesSent.isEmpty() ? lastPacketIndexReceived
: messagesSent.getLast()->packetIndex;
packetIndex = (packetIndex + 1) & ImplementationClass::maxPacketCounter;
if (! Diff (data, targetData, blockSize).createChangeMessage (bi, data, messagesSent, packetIndex))
break;
dumpStatus();
}
}
for (auto* m : messagesSent)
{
if (m->dispatchTime >= Time::getCurrentTime() - RelativeTime::milliseconds (250))
break;
m->dispatchTime = Time::getCurrentTime();
bi.sendMessageToDevice (m->packet);
JUCE_LOG_LITTLEFOOT_HEAP ("Sending packet " << (int) m->packetIndex << " - " << m->packet.size() << " bytes, device " << bi.getDeviceIndex());
if (getTotalSizeOfMessagesSent() > 200)
break;
}
}
void handleACKFromDevice (ImplementationClass& bi, uint32 packetIndex) noexcept
{
if (packetIndex == lastPacketIndexReceived)
return;
JUCE_LOG_LITTLEFOOT_HEAP ("ACK " << (int) packetIndex << " device " << (int) bi.getDeviceIndex()
<< ", last packet received " << String (lastPacketIndexReceived));
lastPacketIndexReceived = packetIndex;
for (int i = messagesSent.size(); --i >= 0;)
{
auto& m = *messagesSent.getUnchecked(i);
if (m.packetIndex == packetIndex)
{
for (uint32 j = 0; j < blockSize; ++j)
deviceState[j] = m.resultDataState[j];
programStateKnown = false;
messagesSent.removeRange (0, i + 1);
dumpStatus();
sendChanges (bi, false);
if (messagesSent.isEmpty())
{
JUCE_LOG_LITTLEFOOT_HEAP ("Heap fully synced");
needsSyncing = false;
}
return;
}
}
resetDeviceStateToUnknown();
}
bool isProgramLoaded() noexcept
{
if (! programStateKnown)
{
uint8 deviceMemory[ImplementationClass::maxBlockSize];
for (size_t i = 0; i < blockSize; ++i)
deviceMemory[i] = (uint8) deviceState[i];
littlefoot::Program prog (deviceMemory, (uint32) blockSize);
programLoaded = prog.checksumMatches();
programSize = prog.getProgramSize();
programStateKnown = true;
}
return programLoaded;
}
const size_t blockSize;
static constexpr uint16 unknownByte = 0x100;
private:
uint16 deviceState[ImplementationClass::maxBlockSize] = { 0 };
uint8 targetData[ImplementationClass::maxBlockSize] = { 0 };
uint32 programSize = 0;
bool needsSyncing = true, programStateKnown = true, programLoaded = false;
uint16* getLatestExpectedDataState() noexcept
{
return messagesSent.isEmpty() ? deviceState
: messagesSent.getLast()->resultDataState;
}
struct ChangeMessage
{
typename ImplementationClass::PacketBuilder packet;
Time dispatchTime;
uint32 packetIndex;
uint16 resultDataState[ImplementationClass::maxBlockSize];
};
OwnedArray<ChangeMessage> messagesSent;
uint32 lastPacketIndexReceived = 0;
int getTotalSizeOfMessagesSent() const noexcept
{
int total = 0;
for (auto* m : messagesSent)
if (m->dispatchTime != Time())
total += m->packet.size();
return total;
}
void dumpStatus()
{
#if JUCE_DUMP_LITTLEFOOT_HEAP_STATUS
int differences = 0;
constexpr int diffLen = 50;
char areas[diffLen + 1] = { 0 };
for (int i = 0; i < diffLen; ++i)
areas[i] = '.';
for (int i = 0; i < (int) blockSize; ++i)
{
if (targetData[i] != deviceState[i])
{
++differences;
areas[i * diffLen / (int) blockSize] = 'X';
}
}
double proportionOK = ((int) blockSize - differences) / (double) blockSize;
ignoreUnused (proportionOK);
JUCE_LOG_LITTLEFOOT_HEAP ("Heap: " << areas << " " << String (roundToInt (100 * proportionOK))
<< "% " << (isProgramLoaded() ? "Ready" : "Loading"));
#endif
}
struct Diff
{
Diff (uint16* current, const uint8* target, size_t blockSizeToUse)
: newData (target), blockSize (blockSizeToUse)
{
ranges.ensureStorageAllocated ((int) blockSize);
for (int i = 0; i < (int) blockSize; ++i)
ranges.add ({ i, 1, newData[i] == current[i], false });
coalesceUniformRegions();
coalesceSequences();
trim();
}
bool createChangeMessage (const ImplementationClass& bi,
const uint16* currentState,
OwnedArray<ChangeMessage>& messagesCreated,
uint32 nextPacketIndex)
{
if (ranges.isEmpty())
return false;
auto deviceIndex = bi.getDeviceIndex();
if (deviceIndex < 0)
return false;
auto& message = *messagesCreated.add (new ChangeMessage());
message.packetIndex = nextPacketIndex;
for (uint32 i = 0; i < blockSize; ++i)
message.resultDataState[i] = currentState[i];
auto& p = message.packet;
p.writePacketSysexHeaderBytes ((uint8) deviceIndex);
p.beginDataChanges (nextPacketIndex);
uint8 lastValue = 0;
bool packetOverflow = false;
for (auto& r : ranges)
{
if (r.isSkipped)
{
packetOverflow = ! p.skipBytes (r.length);
}
else if (r.isMixed)
{
jassert (r.length > 1);
packetOverflow = ! p.setMultipleBytes (newData + r.index, r.length);
if (! packetOverflow)
lastValue = newData[r.index + r.length - 1];
}
else
{
auto value = newData[r.index];
packetOverflow = ! p.setMultipleBytes (value, lastValue, r.length);
if (! packetOverflow)
lastValue = value;
}
if (packetOverflow)
break;
if (! r.isSkipped)
for (int i = r.index; i < r.index + r.length; ++i)
message.resultDataState[i] = newData[i];
}
p.endDataChanges (! packetOverflow);
p.writePacketSysexFooter();
return packetOverflow;
}
private:
struct ByteSequence
{
int index, length;
bool isSkipped, isMixed;
};
const uint8* const newData;
const size_t blockSize;
Array<ByteSequence> ranges;
void coalesceUniformRegions()
{
for (int i = ranges.size(); --i > 0;)
{
auto& r1 = ranges.getReference (i - 1);
auto r2 = ranges.getReference (i);
if (r1.isSkipped == r2.isSkipped
&& (r1.isSkipped || newData[r1.index] == newData[r2.index]))
{
r1.length += r2.length;
ranges.remove (i);
i = jmin (ranges.size() - 1, i + 1);
}
}
}
void coalesceSequences()
{
for (int i = ranges.size(); --i > 0;)
{
auto& r1 = ranges.getReference (i - 1);
auto r2 = ranges.getReference (i);
if (! (r1.isSkipped || r2.isSkipped)
&& (r1.isMixed || r1.length == 1)
&& (r2.isMixed || r2.length == 1))
{
if (r1.length + r2.length < 32)
{
r1.length += r2.length;
r1.isMixed = true;
ranges.remove (i);
i = jmin (ranges.size() - 1, i + 1);
}
}
}
}
void trim()
{
while (ranges.size() > 0 && ranges.getLast().isSkipped)
ranges.removeLast();
}
};
};
}

+ 0
- 877
modules/juce_blocks_basics/littlefoot/juce_LittleFootRunner.h View File

@@ -1,877 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace littlefoot
{
/*
This file contains classes and definitions for executing a littlefoot
bytecode program that was created with the littlefoot compiler.
*/
#if ! (defined (LITTLEFOOT_DEBUG_TRACE) || RUNNING_ON_REAL_BLOCK_DEVICE)
#define LITTLEFOOT_DEBUG_TRACE 0
#endif
#if ! (defined (LITTLEFOOT_DUMP_PROGRAM) || RUNNING_ON_REAL_BLOCK_DEVICE)
#define LITTLEFOOT_DUMP_PROGRAM 0
#endif
using int8 = signed char;
using uint8 = unsigned char;
using int16 = signed short;
using uint16 = unsigned short;
using int32 = signed int;
using uint32 = unsigned int;
using FunctionID = int16;
#define LITTLEFOOT_OPCODES(OP, OP_INT8, OP_INT16, OP_INT32) \
OP (halt) \
OP_INT16 (jump) \
OP_INT16 (jumpIfTrue) \
OP_INT16 (jumpIfFalse) \
OP_INT16 (call) \
OP_INT8 (retVoid) \
OP_INT8 (retValue) \
OP_INT16 (callNative) \
OP (drop) \
OP_INT8 (dropMultiple) \
OP_INT8 (pushMultiple0) \
OP (push0) \
OP (push1) \
OP_INT8 (push8) \
OP_INT16 (push16) \
OP_INT32 (push32) \
OP (dup) \
OP (dupOffset_01) \
OP (dupOffset_02) \
OP (dupOffset_03) \
OP (dupOffset_04) \
OP (dupOffset_05) \
OP (dupOffset_06) \
OP (dupOffset_07) \
OP_INT8 (dupOffset) \
OP_INT16 (dupOffset16) \
OP_INT8 (dropToStack) \
OP_INT16 (dropToStack16) \
OP_INT16 (dupFromGlobal) \
OP_INT16 (dropToGlobal) \
OP (int32ToFloat) \
OP (floatToInt32) \
OP (add_int32) \
OP (add_float) \
OP (mul_int32) \
OP (mul_float) \
OP (sub_int32) \
OP (sub_float) \
OP (div_int32) \
OP (div_float) \
OP (mod_int32) \
OP (bitwiseOr) \
OP (bitwiseAnd) \
OP (bitwiseXor) \
OP (bitwiseNot) \
OP (bitShiftLeft) \
OP (bitShiftRight) \
OP (logicalOr) \
OP (logicalAnd) \
OP (logicalNot) \
OP (testZE_int32) \
OP (testNZ_int32) \
OP (testGT_int32) \
OP (testGE_int32) \
OP (testLT_int32) \
OP (testLE_int32) \
OP (testZE_float) \
OP (testNZ_float) \
OP (testGT_float) \
OP (testGE_float) \
OP (testLT_float) \
OP (testLE_float) \
OP (getHeapByte) \
OP (getHeapInt) \
OP (getHeapBits) \
OP (setHeapByte) \
OP (setHeapInt) \
enum class OpCode : uint8
{
#define LITTLEFOOT_OP(name) name,
LITTLEFOOT_OPCODES (LITTLEFOOT_OP, LITTLEFOOT_OP, LITTLEFOOT_OP, LITTLEFOOT_OP)
#undef LITTLEFOOT_OP
endOfOpcodes
};
/** Available value types */
enum class Type : uint8
{
void_ = 'v',
int_ = 'i',
bool_ = 'b',
float_ = 'f'
};
const int numBytesInType = 4;
//==============================================================================
/** Defines a native function that the program can call.
@tags{Blocks}
*/
struct NativeFunction
{
using ImplementationFunction = int32 (*) (void*, const int32*);
/** Creates a NativeFunction from its signature and an implementation function.
The format of nameAndArgumentTypes is "name/[return type][arg1][arg2..]"
So for example "int foobar (float, bool)" would be "foobar/ifb"
*/
NativeFunction (const char* nameAndArgumentTypes, ImplementationFunction fn)
: nameAndArguments (nameAndArgumentTypes), function (fn),
functionID (createID (nameAndArgumentTypes)), returnType(), numArgs()
{
auto slash = indexOfSlash (nameAndArgumentTypes);
if (slash > 0)
{
returnType = static_cast<Type> (nameAndArgumentTypes[slash + 1]);
while (nameAndArgumentTypes[slash + 2 + numArgs] != 0)
++numArgs;
}
}
const char* nameAndArguments; /**< This signature must have the form "name/[return type][arg1][arg2..]" */
ImplementationFunction function; /**< A static function that will be called. */
FunctionID functionID; /**< The ID is a hash of the name + arguments, but not the return type. */
Type returnType; /**< The function's return type. */
uint8 numArgs; /**< The number of arguments that the function takes. */
/** Converts a function signature to its hashed ID. */
static FunctionID createID (const char* nameAndArgTypes)
{
auto slash = indexOfSlash (nameAndArgTypes);
jassert (slash > 0); // The slash can't be the first character in this string!
jassert (nameAndArgTypes[slash + 1] != 0); // The slash must be followed by a return type character
jassert (String (nameAndArgTypes).substring (0, slash).containsOnly ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"));
jassert (! String ("0123456789").containsChar (String (nameAndArgTypes)[0]));
jassert (String (nameAndArgTypes).substring (slash + 1).containsOnly ("vifb"));
jassert (String (nameAndArgTypes).substring (slash + 2).containsOnly ("ifb")); // arguments must only be of these types
uint32 hash = 0, i = 0;
for (; nameAndArgTypes[i] != 0; ++i)
if (i != (uint32) (slash + 1))
hash = hash * 31u + (uint32) nameAndArgTypes[i];
return static_cast<FunctionID> (hash + i);
}
private:
static int indexOfSlash (const char* nameAndArgs) noexcept
{
jassert (nameAndArgs != nullptr && nameAndArgs[0] != 0); // the name cannot be an empty string!
for (int i = 0; nameAndArgs[i] != 0; ++i)
if (nameAndArgs[i] == '/')
return i;
return -1;
}
};
//==============================================================================
/**
A reference to a block of memory which contains a complete program.
Data format:
2 bytes - program checksum
2 bytes - program size
2 bytes - num functions
2 bytes - num globals
2 bytes - amount of heap space needed (bytes)
2 bytes - ID of function 1
2 bytes - byte offset of function 1 code
2 bytes - ID of function 2
2 bytes - byte offset of function 2 code
etc..
...function code...
@tags{Blocks}
*/
struct Program
{
Program (const void* data, uint32 totalMemorySize) noexcept
: programStart (static_cast<const uint8*> (data)), maxProgramSize (totalMemorySize)
{
jassert (data != nullptr);
}
uint16 getStoredChecksum() const noexcept
{
return (uint16) readInt16 (programStart);
}
uint16 calculateChecksum() const noexcept
{
auto size = getProgramSize();
auto n = (uint16) size;
for (uint32 i = 2; i < size; ++i)
n = static_cast<uint16> (n + (n * 2) + programStart[i]);
return n;
}
bool checksumMatches() const noexcept
{
return calculateChecksum() == getStoredChecksum();
}
uint32 getNumFunctions() const noexcept
{
return (uint16) readInt16 (programStart + 4);
}
FunctionID getFunctionID (uint32 functionIndex) const noexcept
{
if (auto f = getFunctionEntry (functionIndex))
return static_cast<FunctionID> (readInt16 (f));
return {};
}
const uint8* getFunctionStartAddress (uint32 functionIndex) const noexcept
{
if (auto f = getFunctionEntry (functionIndex))
{
uint32 address = (uint16) readInt16 (f + sizeof (FunctionID));
if (address < getProgramSize())
return programStart + address;
}
return {};
}
const uint8* getFunctionEndAddress (uint32 functionIndex) const noexcept
{
return ++functionIndex >= getNumFunctions() ? programStart + getProgramSize()
: getFunctionStartAddress (functionIndex);
}
uint32 getProgramSize() const noexcept
{
auto size = (uint16) readInt16 (programStart + 2);
return size < programHeaderSize ? programHeaderSize
: (size > maxProgramSize ? maxProgramSize : size);
}
/** Returns the number of bytes of heap space the program needs */
uint16 getHeapSizeBytes() const noexcept
{
return (uint16) readInt16 (programStart + 8);
}
/** Returns the number of global variables the program uses */
uint16 getNumGlobals() const noexcept
{
return (uint16) readInt16 (programStart + 6);
}
uint32 getTotalSpaceNeeded() const noexcept
{
return getProgramSize() + getHeapSizeBytes();
}
#if JUCE_DEBUG
//==============================================================================
/** Prints the assembly code for a given function. */
void dumpFunctionDisassembly (OutputStream& out, uint32 functionIndex) const
{
out << newLine << "Function #" << (int) functionIndex
<< " (" << String::toHexString (getFunctionID (functionIndex)) << ")" << newLine;
if (auto codeStart = getFunctionStartAddress (functionIndex))
if (auto codeEnd = getFunctionEndAddress (functionIndex))
for (auto prog = codeStart; prog < codeEnd;)
out << getOpDisassembly (prog) << newLine;
}
String getOpDisassembly (const uint8*& prog) const
{
String s;
s << String::toHexString ((int) (prog - programStart)).paddedLeft ('0', 4) << ": ";
auto op = (OpCode) *prog++;
switch (op)
{
#define LITTLEFOOT_OP(name) case OpCode::name: s << #name; break;
#define LITTLEFOOT_OP_INT8(name) case OpCode::name: s << #name << " " << String::toHexString ((int) *prog++).paddedLeft ('0', 2); break;
#define LITTLEFOOT_OP_INT16(name) case OpCode::name: s << #name << " " << String::toHexString ((int) readInt16 (prog)).paddedLeft ('0', 4); prog += 2; break;
#define LITTLEFOOT_OP_INT32(name) case OpCode::name: s << #name << " " << String::toHexString ((int) readInt32 (prog)).paddedLeft ('0', 8); prog += 4; break;
LITTLEFOOT_OPCODES (LITTLEFOOT_OP, LITTLEFOOT_OP_INT8, LITTLEFOOT_OP_INT16, LITTLEFOOT_OP_INT32)
#undef LITTLEFOOT_OP
#undef LITTLEFOOT_OP_INT8
#undef LITTLEFOOT_OP_INT16
#undef LITTLEFOOT_OP_INT32
case OpCode::endOfOpcodes:
default: s << "???"; break;
}
return s;
}
/** Calls dumpFunctionDisassembly() for all functions. */
void dumpAllFunctions (OutputStream& out) const
{
if (programStart != nullptr)
{
DBG ("Program size: " << (int) getProgramSize() << " bytes");
for (uint32 i = 0; i < getNumFunctions(); ++i)
dumpFunctionDisassembly (out, i);
}
}
#endif
/** For a given op code, this returns the number of program bytes that follow it. */
static uint8 getNumExtraBytesForOpcode (OpCode op) noexcept
{
switch (op)
{
#define LITTLEFOOT_OP(name) case OpCode::name: return 0;
#define LITTLEFOOT_OP_INT8(name) case OpCode::name: return 1;
#define LITTLEFOOT_OP_INT16(name) case OpCode::name: return 2;
#define LITTLEFOOT_OP_INT32(name) case OpCode::name: return 4;
LITTLEFOOT_OPCODES (LITTLEFOOT_OP, LITTLEFOOT_OP_INT8, LITTLEFOOT_OP_INT16, LITTLEFOOT_OP_INT32)
#undef LITTLEFOOT_OP
#undef LITTLEFOOT_OP_INT8
#undef LITTLEFOOT_OP_INT16
#undef LITTLEFOOT_OP_INT32
case OpCode::endOfOpcodes:
default: jassertfalse; break;
}
return 0;
}
//==============================================================================
static float intToFloat (int32 value) noexcept { float v; copyFloatMem (&v, &value); return v; }
static int32 floatToInt (float value) noexcept { int32 v; copyFloatMem (&v, &value); return v; }
static int16 readInt16 (const uint8* d) noexcept { return (int16) (d[0] | (((uint16) d[1]) << 8)); }
static int32 readInt32 (const uint8* d) noexcept { return (int32) (d[0] | (((uint32) d[1]) << 8) | (((uint32) d[2]) << 16) | (((uint32) d[3]) << 24)); }
static void writeInt16 (uint8* d, int16 v) noexcept { d[0] = (uint8) v; d[1] = (uint8) (v >> 8); }
static void writeInt32 (uint8* d, int32 v) noexcept { d[0] = (uint8) v; d[1] = (uint8) (v >> 8); d[2] = (uint8) (v >> 16); d[3] = (uint8) (v >> 24); }
//==============================================================================
static constexpr uint32 programHeaderSize = 10;
const uint8* programStart = nullptr;
const uint32 maxProgramSize;
private:
const uint8* getFunctionEntry (uint32 index) const noexcept
{
auto offset = programHeaderSize + index * (sizeof (FunctionID) + sizeof (int16));
return offset <= (uint32) (getProgramSize() - 4) ? programStart + offset : nullptr;
}
static void copyFloatMem (void* dest, const void* src) noexcept
{
for (int i = 0; i < 4; ++i)
((uint8*) dest)[i] = ((const uint8*) src)[i];
}
};
//==============================================================================
/**
Loads a program, and lets the user execute its functions.
The programAndHeapSpace is the number of bytes allocated for program + heap.
stackAndGlobalsSpace is the size of the globals + stack area.
Memory layout:
Program code goes at address 0, followed by any shared data the program needs
globals are at the top end of the buffer
stack space stretches downwards from the start of the globals
@tags{Blocks}
*/
template <int programAndHeapSpace, int stackAndGlobalsSpace>
struct Runner
{
Runner() noexcept : program (allMemory, sizeof (allMemory)) { reset(); }
/** Installs an array of native functions that the code can use.
Note that this doesn't take ownership of any memory involved, so the caller mustn't pass any dangling pointers
*/
void setNativeFunctions (const NativeFunction* functions, int numFunctions, void* userDataForCallback) noexcept
{
nativeFunctions = functions;
numNativeFunctions = numFunctions;
nativeFunctionCallbackContext = userDataForCallback;
}
/** Returns the number of native functions available. */
int getNumNativeFunctions() const noexcept { return numNativeFunctions; }
/** Returns one of the native functions available. The index must not be out of range. */
const NativeFunction& getNativeFunction (int index) const noexcept { jassert (index >= 0 && index < numNativeFunctions); return nativeFunctions[index]; }
/** Clears the memory state. */
void reset() noexcept
{
for (uint32 i = 0; i < sizeof (allMemory); ++i)
allMemory[i] = 0;
}
/** Clears all the non-program data. */
void clearHeapAndGlobals() noexcept
{
auto* start = getProgramAndDataStart() + program.getProgramSize();
auto* end = allMemory + sizeof (allMemory);
for (auto m = start; m < end; ++m)
*m = 0;
}
/** Return codes from a function call */
enum class ErrorCode : uint8
{
ok = 0,
executionTimedOut,
unknownInstruction,
stackOverflow,
stackUnderflow,
illegalAddress,
divisionByZero,
unknownFunction
};
/** Returns a text description for an error code */
static const char* getErrorDescription (ErrorCode e) noexcept
{
switch (e)
{
case ErrorCode::ok: return "OK";
case ErrorCode::executionTimedOut: return "Timed-out";
case ErrorCode::unknownInstruction: return "Illegal instruction";
case ErrorCode::stackOverflow: return "Stack overflow";
case ErrorCode::stackUnderflow: return "Stack underflow";
case ErrorCode::illegalAddress: return "Illegal access";
case ErrorCode::divisionByZero: return "Division by zero";
case ErrorCode::unknownFunction: return "Unknown function";
default: return "Unknown error";
}
}
/** Calls one of the functions in the program, by its textual signature. */
ErrorCode callFunction (const char* functionSignature) noexcept
{
return FunctionExecutionContext (*this, functionSignature).run();
}
/** Calls one of the functions in the program, by its function ID. */
ErrorCode callFunction (FunctionID function) noexcept
{
return FunctionExecutionContext (*this, function).run();
}
/** */
static constexpr uint32 totalProgramAndHeapSpace = programAndHeapSpace;
/** */
static constexpr uint32 totalStackAndGlobalsSpace = stackAndGlobalsSpace;
/** */
static uint32 getMaximumProgramSize() noexcept { return programAndHeapSpace; }
/** */
uint8* getProgramAndDataStart() const noexcept { return const_cast<uint8*> (allMemory); }
/** */
uint8* getProgramAndDataEnd() const noexcept { return reinterpret_cast<uint8*> (stackStart); }
/** */
uint32 getProgramAndDataSize() const noexcept { return (uint32) (getProgramAndDataEnd() - getProgramAndDataStart()); }
/** */
uint8* getProgramHeapStart() const noexcept { return heapStart; }
/** */
uint8* getProgramHeapEnd() const noexcept { return getProgramAndDataEnd(); }
/** */
uint16 getProgramHeapSize() const noexcept { return heapSize; }
/** */
bool isProgramValid() const noexcept { return heapStart != nullptr; }
/** Sets a byte of data. */
void setDataByte (uint32 index, uint8 value) noexcept
{
if (index < programAndHeapSpace)
{
auto& dest = getProgramAndDataStart()[index];
if (index < program.getProgramSize() && dest != value)
heapStart = nullptr; // force a re-initialise of the memory layout when the program changes
dest = value;
}
}
/** */
void setHeapByte (uint32 index, uint8 value) noexcept
{
auto* addr = getProgramHeapStart() + index;
if (addr < getProgramHeapEnd())
*addr = value;
}
/** */
uint8 getHeapByte (uint32 index) const noexcept
{
const auto* addr = getProgramHeapStart() + index;
return addr < getProgramHeapEnd() ? *addr : 0;
}
/** */
uint32 getHeapBits (uint32 startBit, uint32 numBits) const noexcept
{
if (startBit + numBits > 8 * getProgramHeapSize())
{
jassertfalse;
return 0;
}
return readLittleEndianBitsInBuffer (getProgramHeapStart(), startBit, numBits);
}
/** */
int32 setHeapInt (uint32 byteOffset, uint32 value) noexcept
{
if (byteOffset + 3 < getProgramHeapSize())
Program::writeInt32 (getProgramHeapStart() + byteOffset, (int32) value);
return 0;
}
/** */
int32 getHeapInt (uint32 byteOffset) const noexcept
{
return byteOffset + 3 < getProgramHeapSize() ? Program::readInt32 (getProgramHeapStart() + byteOffset) : 0;
}
//==============================================================================
/** */
uint8 allMemory[(size_t) (((programAndHeapSpace + stackAndGlobalsSpace) + 3) & ~3)];
/** */
Program program;
//==============================================================================
/**
*/
struct FunctionExecutionContext
{
FunctionExecutionContext() = default;
FunctionExecutionContext (const FunctionExecutionContext&) = default;
FunctionExecutionContext& operator= (const FunctionExecutionContext&) = default;
/** */
FunctionExecutionContext (Runner& r, const char* functionSignature) noexcept
: FunctionExecutionContext (r, NativeFunction::createID (functionSignature)) {}
/** */
FunctionExecutionContext (Runner& r, FunctionID function) noexcept
: runner (&r.reinitialiseProgramLayoutIfProgramHasChanged()),
programBase (r.program.programStart), heapStart (r.heapStart),
stack (r.stackEnd), stackStart (r.stackStart), stackEnd (r.stackEnd),
globals (r.globals), heapSize (r.heapSize),
programSize (r.program.getProgramSize()),
numGlobals (r.program.getNumGlobals())
{
if (r.heapStart != nullptr)
{
auto& prog = r.program;
auto numFunctions = prog.getNumFunctions();
for (uint32 i = 0; i < numFunctions; ++i)
{
if (prog.getFunctionID (i) == function)
{
programCounter = prog.getFunctionStartAddress (i);
programEnd = r.getProgramHeapStart();
tos = *--stack = 0;
return;
}
}
}
programCounter = nullptr;
}
/** */
bool isValid() const noexcept
{
return programCounter != nullptr && runner->heapStart != nullptr;
}
/** */
void reset() noexcept
{
programCounter = nullptr;
}
/** */
template <typename... Args>
void setArguments (Args... args) noexcept { pushArguments (args...); push0(); /* (dummy return address) */ }
/** */
template <typename TimeOutCheckFunction>
ErrorCode run (TimeOutCheckFunction hasTimedOut) noexcept
{
if (! isValid())
return ErrorCode::unknownFunction;
error = ErrorCode::unknownInstruction;
uint16 opsPerformed = 0;
for (;;)
{
if (programCounter >= programEnd)
return error;
if ((++opsPerformed & 63) == 0 && hasTimedOut())
return ErrorCode::executionTimedOut;
dumpDebugTrace();
auto op = (OpCode) *programCounter++;
#define LITTLEFOOT_PERFORM_OP(name) case OpCode::name: name(); break;
#define LITTLEFOOT_PERFORM_OP_INT8(name) case OpCode::name: name ((int8) *programCounter++); break;
#define LITTLEFOOT_PERFORM_OP_INT16(name) case OpCode::name: name (readProgram16()); break;
#define LITTLEFOOT_PERFORM_OP_INT32(name) case OpCode::name: name (readProgram32()); break;
switch (op)
{
LITTLEFOOT_OPCODES (LITTLEFOOT_PERFORM_OP, LITTLEFOOT_PERFORM_OP_INT8, LITTLEFOOT_PERFORM_OP_INT16, LITTLEFOOT_PERFORM_OP_INT32)
case OpCode::endOfOpcodes:
default: setError (ErrorCode::unknownInstruction); break;
}
jassert (programCounter != nullptr);
}
}
private:
//==============================================================================
Runner* runner;
const uint8* programCounter = nullptr;
const uint8* programEnd;
const uint8* programBase;
uint8* heapStart;
int32* stack;
int32* stackStart;
int32* stackEnd;
int32* globals;
uint16 heapSize, programSize, numGlobals;
int32 tos; // top of stack
ErrorCode error;
template <typename Type1, typename... Args> void pushArguments (Type1 arg1, Args... args) noexcept { pushArguments (args...); pushArguments (arg1); }
void pushArguments (int32 arg1) noexcept { push32 (arg1); }
void pushArguments (float arg1) noexcept { push32 (Program::floatToInt (arg1)); }
int16 readProgram16() noexcept { auto v = Program::readInt16 (programCounter); programCounter += sizeof (int16); return v; }
int32 readProgram32() noexcept { auto v = Program::readInt32 (programCounter); programCounter += sizeof (int32); return v; }
void setError (ErrorCode e) noexcept { error = e; programCounter = programEnd; jassert (error == ErrorCode::ok); }
bool checkStackUnderflow() noexcept { if (stack <= stackEnd) return true; setError (ErrorCode::stackUnderflow); return false; }
bool flushTopToStack() noexcept { if (--stack < stackStart) { setError (ErrorCode::stackOverflow); return false; } *stack = tos; return true; }
using IntBinaryOp = int32 (int32, int32);
using FloatBinaryOp = float (float, float);
void binaryOp (IntBinaryOp f) noexcept { if (checkStackUnderflow()) tos = f (*stack++, tos); }
void binaryOp (FloatBinaryOp f) noexcept { if (checkStackUnderflow()) tos = Program::floatToInt (f (Program::intToFloat (*stack++), Program::intToFloat (tos))); }
void halt() noexcept { setError (ErrorCode::ok); }
void jump (int16 addr) noexcept { if (((uint16) addr) >= programSize) return setError (ErrorCode::illegalAddress); programCounter = programBase + (uint16) addr; }
void jumpIfTrue (int16 addr) noexcept { bool v = tos; drop(); if (v) jump (addr); }
void jumpIfFalse (int16 addr) noexcept { bool v = tos; drop(); if (! v) jump (addr); }
void call (int16 fnAddr) noexcept { if (flushTopToStack()) { tos = (int32) (programCounter - programBase); jump (fnAddr); } }
void retVoid (int8 numArgs) noexcept { if (tos == 0) return setError (ErrorCode::ok); auto retAddr = (int16) tos; stack += (uint8) numArgs; if (checkStackUnderflow()) { tos = *stack++; jump (retAddr); } }
void retValue (int8 numArgs) noexcept { auto retAddr = (int16) *stack++; if (retAddr == 0) return setError (ErrorCode::ok); stack += (uint8) numArgs; if (checkStackUnderflow()) jump (retAddr); }
void drop() noexcept { if (checkStackUnderflow()) tos = *stack++; }
void dropMultiple (int8 num) noexcept { if (num < 0) { stack -= num; checkStackUnderflow(); } else { stack += num - 1; drop(); }}
void pushMultiple0 (int8 num) noexcept { if (stack - num <= stackStart) return setError (ErrorCode::stackOverflow); flushTopToStack(); for (int i = (uint8) num; --i > 0;) *--stack = 0; tos = 0; }
void push0() noexcept { push32 (0); }
void push1() noexcept { push32 (1); }
void push8 (int8 value) noexcept { push32 (value); }
void push16 (int16 value) noexcept { push32 (value); }
void push32 (int32 value) noexcept { flushTopToStack(); tos = value; }
void dup() noexcept { flushTopToStack(); }
void dupOffset_01() noexcept { dupOffset16 (1); }
void dupOffset_02() noexcept { dupOffset16 (2); }
void dupOffset_03() noexcept { dupOffset16 (3); }
void dupOffset_04() noexcept { dupOffset16 (4); }
void dupOffset_05() noexcept { dupOffset16 (5); }
void dupOffset_06() noexcept { dupOffset16 (6); }
void dupOffset_07() noexcept { dupOffset16 (7); }
void dupOffset (int8 offset) noexcept { dupOffset16 ((uint8) offset); }
void dupOffset16 (int16 offset) noexcept { if (flushTopToStack()) { auto addr = stack + offset; if (addr < stackStart || addr >= stackEnd) return setError (ErrorCode::illegalAddress); tos = *addr; } }
void dropToStack (int8 offset) noexcept { dropToStack16 ((uint8) offset); }
void dropToStack16 (int16 offset) noexcept { auto addr = stack + offset; if (addr < stackStart || addr >= stackEnd) return setError (ErrorCode::illegalAddress); *addr = tos; drop(); }
void dupFromGlobal (int16 index) noexcept { if (flushTopToStack()) { if (((uint16) index) >= numGlobals) return setError (ErrorCode::illegalAddress); tos = globals [(uint16) index]; } }
void dropToGlobal (int16 index) noexcept { if (((uint16) index) >= numGlobals) return setError (ErrorCode::illegalAddress); globals [(uint16) index] = tos; drop(); }
void int32ToFloat() noexcept { tos = Program::floatToInt (static_cast<float> (tos)); }
void floatToInt32() noexcept { tos = static_cast<int32> (Program::intToFloat (tos)); }
void add_int32() noexcept { binaryOp ([] (int32 a, int32 b) { return a + b; }); }
void add_float() noexcept { binaryOp ([] (float a, float b) { return a + b; }); }
void mul_int32() noexcept { binaryOp ([] (int32 a, int32 b) { return a * b; }); }
void mul_float() noexcept { binaryOp ([] (float a, float b) { return a * b; }); }
void sub_int32() noexcept { binaryOp ([] (int32 a, int32 b) { return a - b; }); }
void sub_float() noexcept { binaryOp ([] (float a, float b) { return a - b; }); }
void div_int32() noexcept { if (tos == 0) return setError (ErrorCode::divisionByZero); binaryOp ([] (int32 a, int32 b) { return a / b; }); }
void div_float() noexcept { if (tos == 0) return setError (ErrorCode::divisionByZero); binaryOp ([] (float a, float b) { return a / b; }); }
void mod_int32() noexcept { if (tos == 0) return setError (ErrorCode::divisionByZero); binaryOp ([] (int32 a, int32 b) { return a % b; }); }
void bitwiseOr() noexcept { binaryOp ([] (int32 a, int32 b) { return a | b; }); }
void bitwiseAnd() noexcept { binaryOp ([] (int32 a, int32 b) { return a & b; }); }
void bitwiseXor() noexcept { binaryOp ([] (int32 a, int32 b) { return a ^ b; }); }
void bitShiftLeft() noexcept { binaryOp ([] (int32 a, int32 b) { return a << b; }); }
void bitShiftRight() noexcept { binaryOp ([] (int32 a, int32 b) { return a >> b; }); }
void logicalOr() noexcept { binaryOp ([] (int32 a, int32 b) { return (int32) (a || b); }); }
void logicalAnd() noexcept { binaryOp ([] (int32 a, int32 b) { return (int32) (a && b); }); }
void logicalNot() noexcept { tos = ! tos; }
void bitwiseNot() noexcept { tos = ~tos; }
void testZE_int32() noexcept { tos = (tos == 0); }
void testNZ_int32() noexcept { tos = (tos != 0); }
void testGT_int32() noexcept { tos = (tos > 0); }
void testGE_int32() noexcept { tos = (tos >= 0); }
void testLT_int32() noexcept { tos = (tos < 0); }
void testLE_int32() noexcept { tos = (tos <= 0); }
void testZE_float() noexcept { tos = (Program::intToFloat (tos) == 0.0f); }
void testNZ_float() noexcept { tos = (Program::intToFloat (tos) != 0.0f); }
void testGT_float() noexcept { tos = (Program::intToFloat (tos) > 0.0f); }
void testGE_float() noexcept { tos = (Program::intToFloat (tos) >= 0.0f); }
void testLT_float() noexcept { tos = (Program::intToFloat (tos) < 0.0f); }
void testLE_float() noexcept { tos = (Program::intToFloat (tos) <= 0.0f); }
void getHeapByte() noexcept { tos = runner->getHeapByte ((uint32) tos); }
void getHeapInt() noexcept { tos = runner->getHeapInt ((uint32) tos); }
void getHeapBits() noexcept { if (checkStackUnderflow()) tos = runner->getHeapBits ((uint32) tos, (uint32) *stack++); }
void setHeapByte() noexcept { if (checkStackUnderflow()) runner->setHeapByte ((uint32) tos, (uint8) *stack++); drop(); }
void setHeapInt() noexcept { if (checkStackUnderflow()) runner->setHeapInt ((uint32) tos, (uint32) *stack++); drop(); }
void callNative (FunctionID functionID) noexcept
{
auto numFunctions = runner->numNativeFunctions;
auto* functions = runner->nativeFunctions;
for (int i = 0; i < numFunctions; ++i)
{
const auto& f = functions[i];
if (f.functionID == functionID)
{
if (flushTopToStack())
{
tos = f.function (runner->nativeFunctionCallbackContext, stack);
stack += f.numArgs;
if (checkStackUnderflow() && f.returnType == Type::void_)
drop();
}
return;
}
}
setError (ErrorCode::unknownFunction);
}
void dumpDebugTrace() const
{
#if LITTLEFOOT_DEBUG_TRACE // Dumps the program counter and stack, for debugging
MemoryOutputStream dump;
auto progCopy = programCounter;
dump << String (runner->program.getOpDisassembly (progCopy)).paddedRight (' ', 26)
<< String::toHexString (tos) << ' ';
for (auto s = stack; s < stackEnd; ++s)
dump << String::toHexString (*s) << ' ';
DBG (dump.toString());
#endif
}
};
private:
//==============================================================================
const NativeFunction* nativeFunctions;
int numNativeFunctions = 0;
void* nativeFunctionCallbackContext = nullptr;
uint8* heapStart = nullptr;
int32* stackStart = nullptr;
int32* stackEnd = nullptr;
int32* globals = nullptr;
uint16 heapSize = 0;
Runner& reinitialiseProgramLayoutIfProgramHasChanged() noexcept
{
if (heapStart == nullptr && program.checksumMatches())
{
auto numGlobals = program.getNumGlobals();
globals = reinterpret_cast<int32*> (allMemory + sizeof (allMemory)) - numGlobals;
heapStart = getProgramAndDataStart() + program.getProgramSize();
heapSize = program.getHeapSizeBytes();
stackEnd = globals;
stackStart = reinterpret_cast<int32*> (heapStart + heapSize);
if ((uint8*) globals < heapStart || stackStart + 32 > stackEnd)
{
jassertfalse;
heapStart = nullptr;
}
else
{
for (uint32 i = 0; i < numGlobals; ++i)
globals[i] = 0; // clear globals
#if LITTLEFOOT_DUMP_PROGRAM
MemoryOutputStream m;
program.dumpAllFunctions (m);
DBG (m.toString());
#endif
}
}
return *this;
}
};
}

+ 0
- 96
modules/juce_blocks_basics/protocol/Protocol Format.txt View File

@@ -1,96 +0,0 @@
A blocks packet consists of a stream of packed bits.

The first 7 bits of a message is the index of the device within the topology to which the
message should be delivered, or from which it originated. The 0x40 bit of this will be set
to indicate that it's a device->host message, or clear for host->device
The next 32 bits are a timestamp, in milliseconds since the source device was booted.
If sending from the host to the device, or for types of packet
where the timestamp is irrelevant, this can be 0.
This is followed by a sequence of 1 or more messages. Each message starts with
a 7 bit message type, followed by message-type-specific data.
---------------------------------------------------------------------------------------
Device topology update (device -> host)
7 bits - message type (0x01)
7 bits - number of device-info blocks to follow
8 bits - number of connection-info blocks to follow
This is followed by a series of device-info blocks of the form:
10 bits - device model identifier (see code for list of types)
32 bits - device GUID
10 bits - device firmware version
6 bits - battery status
Next follows by a series of connection-info blocks of the form:
7 bits - device 1 index (i.e. index in the list of devices sent above)
5 bits - device 1 port type
7 bits - device 2 index
5 bits - device 2 port type
Ports are indicated by being either North, South, East or West on a device, plus
an index to indicate their position along that edge.
---------------------------------------------------------------------------------------
Control button down/up (device -> host)
7 bits - message type (down = 0x20, up = 0x21)
7 bits - device index
12 bits - control button ID (see code for values)
---------------------------------------------------------------------------------------
Touch start/move/end (device -> host)
7 bits - message type (start = 0x10, move = 0x11, end = 0x12)
7 bits - device index
5 bits - touch index
12 bits - touch X position
12 bits - touch Y position
8 bits - touch Z position
---------------------------------------------------------------------------------------
Touch start/move/end with velocity (device -> host)
7 bits - message type (start = 0x13, move = 0x14, end = 0x15)
7 bits - device index
5 bits - touch index
12 bits - touch X position
12 bits - touch Y position
8 bits - touch Z position
8 bits - X velocity
8 bits - Y velocity
8 bits - Z velocity
---------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------
Device command message (host -> device)
7 bits - message type (0x01)
8 bits - command type
Command types:
resetDevice = 0x00,
requestTopologyMessage = 0x01,
setHighResTouchDetectionMode = 0x02,
setLowResTouchDetectionMode = 0x03,
---------------------------------------------------------------------------------------
Modify shared state data block
7 bits - message type (0x02)
..then repeatedly:
3 bits - type of data change command
...extra command-specific bits..
3 bits - type of data change command
..etc..
3 bits - end of sequence command

+ 0
- 289
modules/juce_blocks_basics/protocol/juce_BitPackingUtilities.h View File

@@ -1,289 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace BlocksProtocol
{
/**
All sysex messages to or from a BLOCKS device begin with these header bytes.
The next byte that follows indicates the device index within the topology, where
the 0x40 bit is set for device->host messages, and clear for host->device messages.
The lower 6 bits contain the topology index of the destination or source device.
*/
static const uint8 roliSysexHeader[] = { 0xf0, 0x00, 0x21, 0x10, 0x77 };
static uint8 calculatePacketChecksum (const uint8* data, uint32 size) noexcept
{
uint8 checksum = (uint8) size;
for (uint32 i = 0; i < size; ++i)
checksum = static_cast<uint8> (checksum + (checksum * 2 + data[i]));
return checksum & 0x7f;
}
//==============================================================================
/**
Helper class to define an integer with a specific bit size.
@tags{Blocks}
*/
template <int numBits>
struct IntegerWithBitSize
{
IntegerWithBitSize() = default;
IntegerWithBitSize (const IntegerWithBitSize&) = default;
IntegerWithBitSize& operator= (const IntegerWithBitSize&) = default;
IntegerWithBitSize (uint32 v) noexcept : value (v)
{
static_assert (numBits <= 32, "numBits must be <= 32");
jassert (v <= maxValue);
}
enum
{
bits = numBits,
maxValue = static_cast<uint32> ((1ULL << numBits) - 1ULL)
};
operator uint32() const noexcept { return value; }
uint32 get() const noexcept { return value; }
uint8 getScaledToByte() const noexcept
{
return (uint8) (numBits < 8 ? (uint32) (value << (8 - numBits))
: (uint32) (value >> (numBits - 8)));
}
float toUnipolarFloat() const noexcept { return (float) value / (float) maxValue; }
float toBipolarFloat() const noexcept { return (float) static_cast<int32> (value << (32 - numBits)) / (float) 0x80000000u; }
static IntegerWithBitSize fromUnipolarFloat (float value) noexcept
{
static_assert (numBits <= 31, "numBits must be <= 31");
return IntegerWithBitSize ((uint32) jlimit (0, (int) maxValue, (int) (value * maxValue)));
}
static IntegerWithBitSize fromBipolarFloat (float value) noexcept
{
static_assert (numBits <= 31, "numBits must be <= 31");
return IntegerWithBitSize (maxValue & (uint32) jlimit ((int) -(maxValue / 2), (int) (maxValue / 2), (int) (value * (maxValue / 2))));
}
uint32 value = 0;
};
//==============================================================================
/**
This helper class allocates a block of 7-bit bytes and can push sequences of bits into it.
@see Packed7BitArrayReader
@tags{Blocks}
*/
template <int allocatedBytes>
struct Packed7BitArrayBuilder
{
const void* getData() const noexcept { return data; }
int size() const noexcept { return bytesWritten + (bitsInCurrentByte > 0 ? 1 : 0); }
bool hasCapacity (int bitsNeeded) const noexcept
{
return ((bytesWritten + 2) * 7 + bitsInCurrentByte + bitsNeeded) <= allocatedBytes * 7;
}
void writeHeaderSysexBytes (uint8 deviceIndex) noexcept
{
jassert (bytesWritten + bitsInCurrentByte == 0);
for (int i = 0; i < (int) sizeof (roliSysexHeader); ++i)
data[bytesWritten++] = roliSysexHeader[i];
jassert (deviceIndex < 128);
data[bytesWritten++] = deviceIndex & 0x7f;
}
void writePacketSysexFooter() noexcept
{
if (bitsInCurrentByte != 0)
{
bitsInCurrentByte = 0;
++bytesWritten;
}
jassert (hasCapacity (0));
uint32 headerBytes = (uint32) sizeof (roliSysexHeader) + 1;
data[bytesWritten] = calculatePacketChecksum (data + headerBytes, (uint32) bytesWritten - headerBytes);
++bytesWritten;
data[bytesWritten++] = 0xf7;
}
template <int numBits>
Packed7BitArrayBuilder& operator<< (IntegerWithBitSize<numBits> value) noexcept
{
writeBits (value.value, numBits);
return *this;
}
void writeBits (uint32 value, int numBits) noexcept
{
jassert (numBits <= 32);
jassert (hasCapacity (numBits));
jassert (numBits == 32 || (value >> numBits) == 0);
while (numBits > 0)
{
if (bitsInCurrentByte == 0)
{
if (numBits < 7)
{
data[bytesWritten] = (uint8) value;
bitsInCurrentByte = numBits;
return;
}
if (numBits == 7)
{
data[bytesWritten++] = (uint8) value;
return;
}
data[bytesWritten++] = (uint8) (value & 0x7f);
value >>= 7;
numBits -= 7;
}
else
{
const int bitsToDo = jmin (7 - bitsInCurrentByte, numBits);
data[bytesWritten] = static_cast<uint8> (data[bytesWritten] | ((value & (uint32) ((1 << bitsToDo) - 1)) << bitsInCurrentByte));
value >>= bitsToDo;
numBits -= bitsToDo;
bitsInCurrentByte += bitsToDo;
if (bitsInCurrentByte == 7)
{
bitsInCurrentByte = 0;
++bytesWritten;
}
}
}
}
/** Describes the current building state */
struct State
{
int bytesWritten, bitsInCurrentByte;
};
State getState() const noexcept
{
return { bytesWritten, bitsInCurrentByte };
}
void restore (State state) noexcept
{
bytesWritten = state.bytesWritten;
bitsInCurrentByte = state.bitsInCurrentByte;
}
private:
uint8 data[(size_t) allocatedBytes];
int bytesWritten = 0, bitsInCurrentByte = 0;
};
//==============================================================================
/**
This helper class reads from a block of 7-bit bytes as sequences of bits.
@see Packed7BitArrayBuilder
@tags{Blocks}
*/
struct Packed7BitArrayReader
{
Packed7BitArrayReader (const void* sourceData, int numBytes) noexcept
: data (static_cast<const uint8*> (sourceData)), totalBits (numBytes * 7)
{
}
int getRemainingBits() const noexcept
{
return totalBits - bitsReadInCurrentByte;
}
template <typename Target>
Target read() noexcept
{
return Target (readBits (Target::bits));
}
uint32 readBits (int numBits) noexcept
{
jassert (numBits <= 32);
jassert (getRemainingBits() >= numBits);
uint32 value = 0;
int bitsSoFar = 0;
while (numBits > 0)
{
const auto valueInCurrentByte = (uint32) (*data >> bitsReadInCurrentByte);
const int bitsAvailable = 7 - bitsReadInCurrentByte;
if (bitsAvailable > numBits)
{
value |= ((valueInCurrentByte & (uint32) ((1 << numBits) - 1)) << bitsSoFar);
bitsReadInCurrentByte += numBits;
break;
}
value |= (valueInCurrentByte << bitsSoFar);
numBits -= bitsAvailable;
bitsSoFar += bitsAvailable;
bitsReadInCurrentByte = 0;
++data;
totalBits -= 7;
}
return value;
}
static bool checksumIsOK (const uint8* data, uint32 size) noexcept
{
return size > 1 && calculatePacketChecksum (data, size - 1) == data[size - 1];
}
private:
const uint8* data;
int totalBits, bitsReadInCurrentByte = 0;
};
} // namespace BlocksProtocol
} // namespace juce

+ 0
- 367
modules/juce_blocks_basics/protocol/juce_BlockModels.h View File

@@ -1,367 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace BlocksProtocol
{
#ifndef DOXYGEN
// This file isn't part of the public API, it's where we encode the knowledge base
// of all the different types of block we know about..
struct BlockDataSheet
{
BlockDataSheet (const BlocksProtocol::BlockSerialNumber& serial) : serialNumber (serial)
{
if (serialNumber.isPadBlock()) initialiseForPadBlock2x2();
if (serialNumber.isLiveBlock()) initialiseForControlBlockLive();
if (serialNumber.isLoopBlock()) initialiseForControlBlockLoop();
if (serialNumber.isDevCtrlBlock()) initialiseForControlBlockDeveloper();
if (serialNumber.isTouchBlock()) initialiseForControlBlockTouch();
if (serialNumber.isSeaboardBlock()) initialiseForSeaboardBlock();
if (serialNumber.isLumiKeysBlock()) initialiseForLumiKeysBlock();
}
Block::ConnectionPort convertPortIndexToConnectorPort (BlocksProtocol::ConnectorPort port) const noexcept
{
return ports[(int) port.get()];
}
const BlocksProtocol::BlockSerialNumber serialNumber;
Block::Type apiType = Block::Type::unknown;
const char* description = nullptr;
int widthUnits = 0, heightUnits = 0;
int lightGridWidth = 0, lightGridHeight = 0, lightGridStartIndex = 0;
bool hasTouchSurface = false;
int numKeywaves = 0;
int numLEDRowLEDs = 0;
uint32 programAndHeapSize = 0;
struct ButtonInfo
{
ControlButton::ButtonFunction type;
float x, y;
};
struct StatusLEDInfo
{
String name;
float x, y;
};
Array<ButtonInfo> buttons;
Array<StatusLEDInfo> statusLEDs;
Array<Block::ConnectionPort> ports;
Array<const char*> dials;
Array<BlockConfigManager::ConfigDescription> defaultConfig;
private:
//==============================================================================
void initialiseForPadBlock2x2()
{
apiType = Block::Type::lightPadBlock;
description = "Pad BLOCK (2x2)";
widthUnits = 2;
heightUnits = 2;
lightGridWidth = 15;
lightGridHeight = 15;
addPorts (2, 2, 2, 2);
hasTouchSurface = true;
programAndHeapSize = BlocksProtocol::padBlockProgramAndHeapSize;
addModeButton();
}
void initialiseForControlBlockLoop()
{
initialiseControlBlock ("Loop BLOCK", Block::Type::loopBlock,
ControlButton::ButtonFunction::mode,
ControlButton::ButtonFunction::volume,
ControlButton::ButtonFunction::click,
ControlButton::ButtonFunction::snap,
ControlButton::ButtonFunction::back,
ControlButton::ButtonFunction::playOrPause,
ControlButton::ButtonFunction::record,
ControlButton::ButtonFunction::learn,
ControlButton::ButtonFunction::down,
ControlButton::ButtonFunction::up);
}
void initialiseForControlBlockLive()
{
initialiseControlBlock ("Live BLOCK", Block::Type::liveBlock,
ControlButton::ButtonFunction::mode,
ControlButton::ButtonFunction::volume,
ControlButton::ButtonFunction::scale,
ControlButton::ButtonFunction::chord,
ControlButton::ButtonFunction::arp,
ControlButton::ButtonFunction::sustain,
ControlButton::ButtonFunction::octave,
ControlButton::ButtonFunction::love,
ControlButton::ButtonFunction::down,
ControlButton::ButtonFunction::up);
}
void initialiseForControlBlockDeveloper()
{
initialiseControlBlock ("Dev Ctrl BLOCK", Block::Type::developerControlBlock,
ControlButton::ButtonFunction::button0,
ControlButton::ButtonFunction::button1,
ControlButton::ButtonFunction::button2,
ControlButton::ButtonFunction::button3,
ControlButton::ButtonFunction::button4,
ControlButton::ButtonFunction::button5,
ControlButton::ButtonFunction::button6,
ControlButton::ButtonFunction::button7,
ControlButton::ButtonFunction::down,
ControlButton::ButtonFunction::up);
}
void initialiseForControlBlockTouch()
{
initialiseControlBlock ("Touch BLOCK", Block::Type::touchBlock,
ControlButton::ButtonFunction::velocitySensitivity,
ControlButton::ButtonFunction::glideSensitivity,
ControlButton::ButtonFunction::slideSensitivity,
ControlButton::ButtonFunction::pressSensitivity,
ControlButton::ButtonFunction::liftSensitivity,
ControlButton::ButtonFunction::fixedVelocity,
ControlButton::ButtonFunction::glideLock,
ControlButton::ButtonFunction::pianoMode,
ControlButton::ButtonFunction::down,
ControlButton::ButtonFunction::up);
}
void initialiseControlBlock (const char* name, Block::Type type,
ControlButton::ButtonFunction b1, ControlButton::ButtonFunction b2,
ControlButton::ButtonFunction b3, ControlButton::ButtonFunction b4,
ControlButton::ButtonFunction b5, ControlButton::ButtonFunction b6,
ControlButton::ButtonFunction b7, ControlButton::ButtonFunction b8,
ControlButton::ButtonFunction b9, ControlButton::ButtonFunction b10)
{
apiType = type;
description = name;
widthUnits = 2;
heightUnits = 1;
programAndHeapSize = BlocksProtocol::controlBlockProgramAndHeapSize;
addPorts (2, 1, 2, 1);
float x1 = 0.2f;
float x2 = 0.6f;
float x3 = 1.0f;
float x4 = 1.4f;
float x5 = 1.8f;
float y1 = 0.405f;
float y2 = 0.798f;
addButtons (b1, x1, y1,
b2, x2, y1,
b3, x3, y1,
b4, x4, y1,
b5, x5, y1,
b6, x1, y2,
b7, x2, y2,
b8, x3, y2,
b9, x4, y2,
b10, x5, y2);
numLEDRowLEDs = 15;
}
void initialiseForSeaboardBlock()
{
apiType = Block::Type::seaboardBlock;
description = "Seaboard BLOCK (6x3)";
widthUnits = 6;
heightUnits = 3;
lightGridWidth = 0;
lightGridHeight = 0;
numKeywaves = 24;
addPortsSW (Block::ConnectionPort::DeviceEdge::west, 1);
addPortsNE (Block::ConnectionPort::DeviceEdge::north, 2);
addPortsNE (Block::ConnectionPort::DeviceEdge::east, 1);
hasTouchSurface = true;
programAndHeapSize = BlocksProtocol::padBlockProgramAndHeapSize;
addModeButton();
}
void initialiseForLumiKeysBlock()
{
apiType = Block::Type::lumiKeysBlock;
description = "LUMI Keys BLOCK (6x3)";
widthUnits = 6;
heightUnits = 3;
lightGridWidth = 0;
lightGridHeight = 0;
numKeywaves = 24;
addButtons (ControlButton::ButtonFunction::mode, 0.2f, 0.2f,
ControlButton::ButtonFunction::down, 0.6f, 0.2f,
ControlButton::ButtonFunction::up, 1.0f, 0.2f);
addPortsSW (Block::ConnectionPort::DeviceEdge::west, 2);
addPortsNE (Block::ConnectionPort::DeviceEdge::north, 4);
addPortsNE (Block::ConnectionPort::DeviceEdge::east, 2);
hasTouchSurface = true;
programAndHeapSize = BlocksProtocol::padBlockProgramAndHeapSize;
defaultConfig.add ({ mode, 0, 0, 3, false,
"Color Mode", ConfigType::options,
{ "Multi-color Mode",
"Single Color Mode",
"Piano Mode",
"Night Mode"
},
BlockConfigManager::playGroup });
defaultConfig.add ({ zTrackingMode, 0, 0, 1, false,
"Pressure Tracking Mode", ConfigType::options,
{ "Poly Aftertouch", "Channel Pressure" },
BlockConfigManager::playGroup });
}
//==============================================================================
void addStatusLED (const char* name, float x, float y)
{
statusLEDs.add ({ name, x, y });
}
template <typename... Args>
void addButtons (ControlButton::ButtonFunction fn, float x, float y, Args... others)
{
addButtons (fn, x, y);
addButtons (others...);
}
void addButtons (ControlButton::ButtonFunction fn, float x, float y)
{
buttons.add ({ fn, x, y });
}
void addModeButton()
{
addButtons (ControlButton::ButtonFunction::mode, -1.0f, -1.0f);
}
void addPorts (int numNorth, int numEast, int numSouth, int numWest)
{
addPortsNE (Block::ConnectionPort::DeviceEdge::north, numNorth);
addPortsNE (Block::ConnectionPort::DeviceEdge::east, numEast);
addPortsSW (Block::ConnectionPort::DeviceEdge::south, numSouth);
addPortsSW (Block::ConnectionPort::DeviceEdge::west, numWest);
}
void addPortsNE (Block::ConnectionPort::DeviceEdge edge, int num)
{
for (int i = 0; i < num; ++i)
ports.add ({ edge, i});
}
void addPortsSW (Block::ConnectionPort::DeviceEdge edge, int num)
{
for (int i = 0; i < num; ++i)
ports.add ({ edge, num - i - 1});
}
};
//==============================================================================
static const char* getButtonNameForFunction (ControlButton::ButtonFunction fn) noexcept
{
using BF = ControlButton::ButtonFunction;
switch (fn)
{
case BF::mode: return "Mode";
case BF::volume: return "Volume";
case BF::up: return "Up";
case BF::down: return "Down";
case BF::scale: return "Scale";
case BF::chord: return "Chord";
case BF::arp: return "Arp";
case BF::sustain: return "Sustain";
case BF::octave: return "Octave";
case BF::love: return "Love";
case BF::click: return "Click";
case BF::snap: return "Snap";
case BF::back: return "Back";
case BF::playOrPause: return "Play/Pause";
case BF::record: return "Record";
case BF::learn: return "Learn";
case BF::button0: return "0";
case BF::button1: return "1";
case BF::button2: return "2";
case BF::button3: return "3";
case BF::button4: return "4";
case BF::button5: return "5";
case BF::button6: return "6";
case BF::button7: return "7";
case BF::velocitySensitivity: return "Velocity Sensitivity";
case BF::glideSensitivity: return "Glide Sensitivity";
case BF::slideSensitivity: return "Slide Sensitivity";
case BF::pressSensitivity: return "Press Sensitivity";
case BF::liftSensitivity: return "Lift Sensitivity";
case BF::fixedVelocity: return "Fixed Velocity";
case BF::glideLock: return "Glide Lock";
case BF::pianoMode: return "Piano Mode";
default: break;
}
jassertfalse;
return nullptr;
}
#endif
} // namespace BlocksProtocol
} // namespace juce

+ 0
- 612
modules/juce_blocks_basics/protocol/juce_BlocksProtocolDefinitions.h View File

@@ -1,612 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace BlocksProtocol
{
/** This value is incremented when the format of the API changes in a way which
breaks compatibility.
*/
static constexpr uint32 currentProtocolVersion = 1;
using ProtocolVersion = IntegerWithBitSize<8>;
//==============================================================================
/** A timestamp for a packet, in milliseconds since device boot-up */
using PacketTimestamp = IntegerWithBitSize<32>;
/** This relative timestamp is for use inside a packet, and it represents a
number of milliseconds that should be added to the packet's timestamp.
*/
using PacketTimestampOffset = IntegerWithBitSize<5>;
//==============================================================================
/** Messages that a device may send to the host. */
enum class MessageFromDevice
{
deviceTopology = 0x01,
packetACK = 0x02,
firmwareUpdateACK = 0x03,
deviceTopologyExtend = 0x04,
deviceTopologyEnd = 0x05,
deviceVersion = 0x06,
deviceName = 0x07,
touchStart = 0x10,
touchMove = 0x11,
touchEnd = 0x12,
touchStartWithVelocity = 0x13,
touchMoveWithVelocity = 0x14,
touchEndWithVelocity = 0x15,
configMessage = 0x18,
controlButtonDown = 0x20,
controlButtonUp = 0x21,
programEventMessage = 0x28,
logMessage = 0x30
};
/** Messages that the host may send to a device. */
enum class MessageFromHost
{
deviceCommandMessage = 0x01,
sharedDataChange = 0x02,
programEventMessage = 0x03,
firmwareUpdatePacket = 0x04,
configMessage = 0x10,
factoryReset = 0x11,
blockReset = 0x12,
setName = 0x20
};
/** Messages that the host may send to a device that do not have the usual message format */
namespace SpecialMessageFromHost
{
constexpr uint8 resetMaster[6] = { 0xf0, 0x00, 0x21, 0x10, 0x49, 0xf7 };
}
/** This is the first item in a BLOCKS message, identifying the message type. */
using MessageType = IntegerWithBitSize<7>;
//==============================================================================
/** This is a type of index identifier used to refer to a block within a group.
It refers to the index of a device in the list of devices that was most recently
sent via a topology change message
(It's not a global UID for a block unit).
NB: to send a message to all devices, pass the getDeviceIndexForBroadcast() value.
*/
using TopologyIndex = uint8;
static constexpr int topologyIndexBits = 7;
/** Use this value as the index if you want a message to be sent to all devices in
the group.
*/
static constexpr TopologyIndex topologyIndexForBroadcast = 63;
using DeviceCount = IntegerWithBitSize<7>;
using ConnectionCount = IntegerWithBitSize<8>;
//==============================================================================
/** Battery charge level. */
using BatteryLevel = IntegerWithBitSize<5>;
/** Battery charger connection flag. */
using BatteryCharging = IntegerWithBitSize<1>;
//==============================================================================
/** ConnectorPort is an index, starting at 0 for the leftmost port on the
top edge, and going clockwise.
*/
using ConnectorPort = IntegerWithBitSize<5>;
//==============================================================================
/** Structure for generic block data
@tags{Blocks}
*/
template <size_t MaxSize>
struct BlockStringData
{
uint8 data[MaxSize] = {};
uint8 length = 0;
static const size_t maxLength { MaxSize };
bool isNotEmpty() const
{
return length > 0;
}
String asString() const
{
return String ((const char*) data, length);
}
bool operator== (const BlockStringData& other) const
{
if (length != other.length)
return false;
for (int i = 0; i < length; ++i)
if (data[i] != other.data[i])
return false;
return true;
}
bool operator!= (const BlockStringData& other) const
{
return ! ( *this == other );
}
};
using VersionNumber = BlockStringData<21>;
using BlockName = BlockStringData<33>;
//==============================================================================
/** Structure describing a block's serial number
@tags{Blocks}
*/
struct BlockSerialNumber : public BlockStringData<16>
{
bool isValid() const noexcept
{
for (auto c : data)
if (c == 0)
return false;
return isAnyControlBlock() || isPadBlock() || isSeaboardBlock() || isLumiKeysBlock();
}
bool isPadBlock() const noexcept { return hasPrefix ("LPB") || hasPrefix ("LPM"); }
bool isLiveBlock() const noexcept { return hasPrefix ("LIC"); }
bool isLoopBlock() const noexcept { return hasPrefix ("LOC"); }
bool isDevCtrlBlock() const noexcept { return hasPrefix ("DCB"); }
bool isTouchBlock() const noexcept { return hasPrefix ("TCB"); }
bool isSeaboardBlock() const noexcept { return hasPrefix ("SBB"); }
bool isLumiKeysBlock() const noexcept { return hasPrefix ("LKB"); }
bool isAnyControlBlock() const noexcept { return isLiveBlock() || isLoopBlock() || isDevCtrlBlock() || isTouchBlock(); }
bool hasPrefix (const char* prefix) const noexcept { return memcmp (data, prefix, 3) == 0; }
};
//==============================================================================
/** Structure for the device status
@tags{Blocks}
*/
struct DeviceStatus
{
BlockSerialNumber serialNumber;
TopologyIndex index;
BatteryLevel batteryLevel;
BatteryCharging batteryCharging;
};
//==============================================================================
/** Structure for the device connection
@tags{Blocks}
*/
struct DeviceConnection
{
TopologyIndex device1, device2;
ConnectorPort port1, port2;
bool operator== (const DeviceConnection& other) const
{
return isEqual (other);
}
bool operator!= (const DeviceConnection& other) const
{
return ! isEqual (other);
}
private:
bool isEqual (const DeviceConnection& other) const
{
return device1 == other.device1
&& device2 == other.device2
&& port1 == other.port1
&& port2 == other.port2;
}
};
//==============================================================================
/** Structure for the device version
@tags{Blocks}
*/
struct DeviceVersion
{
TopologyIndex index;
VersionNumber version;
};
//==============================================================================
/** Structure used for the device name
@tags{Blocks}
*/
struct DeviceName
{
TopologyIndex index;
BlockName name;
};
static constexpr uint8 maxBlocksInTopologyPacket = 6;
static constexpr uint8 maxConnectionsInTopologyPacket = 24;
//==============================================================================
/** Configuration Item Identifiers. */
enum ConfigItemId
{
// MIDI
midiStartChannel = 0,
midiEndChannel = 1,
midiUseMPE = 2,
pitchBendRange = 3,
octave = 4,
transpose = 5,
slideCC = 6,
slideMode = 7,
octaveTopology = 8,
midiChannelRange = 9,
MPEZone = 40,
// Touch
velocitySensitivity = 10,
glideSensitivity = 11,
slideSensitivity = 12,
pressureSensitivity = 13,
liftSensitivity = 14,
fixedVelocity = 15,
fixedVelocityValue = 16,
pianoMode = 17,
glideLock = 18,
glideLockEnable = 19,
// Live
mode = 20,
volume = 21,
scale = 22,
hideMode = 23,
chord = 24,
arpPattern = 25,
tempo = 26,
key = 27,
autoTransposeToKey = 28,
// Tracking
xTrackingMode = 30,
yTrackingMode = 31,
zTrackingMode = 32,
// Graphics
gammaCorrection = 33,
globalKeyColour = 34,
rootKeyColour = 35,
brightness = 36,
// User
user0 = 64,
user1 = 65,
user2 = 66,
user3 = 67,
user4 = 68,
user5 = 69,
user6 = 70,
user7 = 71,
user8 = 72,
user9 = 73,
user10 = 74,
user11 = 75,
user12 = 76,
user13 = 77,
user14 = 78,
user15 = 79,
user16 = 80,
user17 = 81,
user18 = 82,
user19 = 83,
user20 = 84,
user21 = 85,
user22 = 86,
user23 = 87,
user24 = 88,
user25 = 89,
user26 = 90,
user27 = 91,
user28 = 92,
user29 = 93,
user30 = 94,
user31 = 95
};
static constexpr uint8 numberOfUserConfigs = 32;
static constexpr uint8 maxConfigIndex = uint8 (ConfigItemId::user0) + numberOfUserConfigs;
static constexpr uint8 configUserConfigNameLength = 32;
static constexpr uint8 configMaxOptions = 16;
static constexpr uint8 configOptionNameLength = 16;
//==============================================================================
/** The coordinates of a touch.
@tags{Blocks}
*/
struct TouchPosition
{
using Xcoord = IntegerWithBitSize<12>;
using Ycoord = IntegerWithBitSize<12>;
using Zcoord = IntegerWithBitSize<8>;
Xcoord x;
Ycoord y;
Zcoord z;
enum { bits = Xcoord::bits + Ycoord::bits + Zcoord::bits };
};
/** The velocities for each dimension of a touch.
@tags{Blocks}
*/
struct TouchVelocity
{
using VXcoord = IntegerWithBitSize<8>;
using VYcoord = IntegerWithBitSize<8>;
using VZcoord = IntegerWithBitSize<8>;
VXcoord vx;
VYcoord vy;
VZcoord vz;
enum { bits = VXcoord::bits + VYcoord::bits + VZcoord::bits };
};
/** The index of a touch, i.e. finger number. */
using TouchIndex = IntegerWithBitSize<5>;
using PacketCounter = IntegerWithBitSize<10>;
//==============================================================================
enum DeviceCommands
{
beginAPIMode = 0x00,
requestTopologyMessage = 0x01,
endAPIMode = 0x02,
ping = 0x03,
debugMode = 0x04,
saveProgramAsDefault = 0x05
};
using DeviceCommand = IntegerWithBitSize<9>;
//==============================================================================
enum ConfigCommands
{
setConfig = 0x00,
requestConfig = 0x01, // Request a config update
requestFactorySync = 0x02, // Requests all active factory config data
requestUserSync = 0x03, // Requests all active user config data
updateConfig = 0x04, // Set value, min and max
updateUserConfig = 0x05, // As above but contains user config metadata
setConfigState = 0x06, // Set config activation state and whether it is saved in flash
factorySyncEnd = 0x07,
clusterConfigSync = 0x08,
factorySyncReset = 0x09
};
using ConfigCommand = IntegerWithBitSize<4>;
using ConfigItemIndex = IntegerWithBitSize<8>;
using ConfigItemValue = IntegerWithBitSize<32>;
//==============================================================================
/** An ID for a control-block button type */
using ControlButtonID = IntegerWithBitSize<12>;
//==============================================================================
using RotaryDialIndex = IntegerWithBitSize<7>;
using RotaryDialAngle = IntegerWithBitSize<14>;
using RotaryDialDelta = IntegerWithBitSize<14>;
//==============================================================================
enum DataChangeCommands
{
endOfPacket = 0,
endOfChanges = 1,
skipBytesFew = 2,
skipBytesMany = 3,
setSequenceOfBytes = 4,
setFewBytesWithValue = 5,
setFewBytesWithLastValue = 6,
setManyBytesWithValue = 7
};
using PacketIndex = IntegerWithBitSize<16>;
using DataChangeCommand = IntegerWithBitSize<3>;
using ByteCountFew = IntegerWithBitSize<4>;
using ByteCountMany = IntegerWithBitSize<8>;
using ByteValue = IntegerWithBitSize<8>;
using ByteSequenceContinues = IntegerWithBitSize<1>;
using FirmwareUpdateACKCode = IntegerWithBitSize<7>;
using FirmwareUpdateACKDetail = IntegerWithBitSize<32>;
using FirmwareUpdatePacketSize = IntegerWithBitSize<7>;
static constexpr uint32 numProgramMessageInts = 3;
static constexpr uint32 apiModeHostPingTimeoutMs = 5000;
static constexpr uint32 padBlockProgramAndHeapSize = 7200;
static constexpr uint32 padBlockStackSize = 800;
static constexpr uint32 controlBlockProgramAndHeapSize = 3000;
static constexpr uint32 controlBlockStackSize = 800;
//==============================================================================
/** Contains the number of bits required to encode various items in the packets */
enum BitSizes
{
topologyMessageHeader = (int) MessageType::bits + (int) ProtocolVersion::bits + (int) DeviceCount::bits + (int) ConnectionCount::bits,
topologyDeviceInfo = (int) BlockSerialNumber::maxLength * 7 + (int) BatteryLevel::bits + (int) BatteryCharging::bits,
topologyConnectionInfo = topologyIndexBits + (int) ConnectorPort::bits + topologyIndexBits + (int) ConnectorPort::bits,
typeDeviceAndTime = (int) MessageType::bits + (int) PacketTimestampOffset::bits,
touchMessage = (int) typeDeviceAndTime + (int) TouchIndex::bits + (int) TouchPosition::bits,
touchMessageWithVelocity = (int) touchMessage + (int) TouchVelocity::bits,
programEventMessage = (int) MessageType::bits + 32 * numProgramMessageInts,
packetACK = (int) MessageType::bits + (int) PacketCounter::bits,
firmwareUpdateACK = (int) MessageType::bits + (int) FirmwareUpdateACKCode::bits + (int) FirmwareUpdateACKDetail::bits,
controlButtonMessage = (int) typeDeviceAndTime + (int) ControlButtonID::bits,
configSetMessage = (int) MessageType::bits + (int) ConfigCommand::bits + (int) ConfigItemIndex::bits + (int) ConfigItemValue::bits,
configRespMessage = (int) MessageType::bits + (int) ConfigCommand::bits + (int) ConfigItemIndex::bits + ((int) ConfigItemValue::bits * 3),
configSyncEndMessage = (int) MessageType::bits + (int) ConfigCommand::bits,
};
//==============================================================================
// These are the littlefoot functions provided for use in BLOCKS programs
static constexpr const char* ledProgramLittleFootFunctions[] =
{
"min/iii",
"min/fff",
"max/iii",
"max/fff",
"clamp/iiii",
"clamp/ffff",
"abs/ii",
"abs/ff",
"map/ffffff",
"map/ffff",
"mod/iii",
"getRandomFloat/f",
"getRandomInt/ii",
"log/vi",
"logHex/vi",
"getMillisecondCounter/i",
"getFirmwareVersion/i",
"getTimeInCurrentFunctionCall/i",
"getBatteryLevel/f",
"isBatteryCharging/b",
"isMasterBlock/b",
"isConnectedToHost/b",
"setStatusOverlayActive/vb",
"getNumBlocksInTopology/i",
"getBlockIDForIndex/ii",
"getBlockIDOnPort/ii",
"getPortToMaster/i",
"getBlockTypeForID/ii",
"sendMessageToBlock/viiii",
"sendMessageToHost/viii",
"getHorizontalDistFromMaster/i",
"getVerticalDistFromMaster/i",
"getAngleFromMaster/i",
"setAutoRotate/vb",
"getClusterIndex/i",
"getClusterWidth/i",
"getClusterHeight/i",
"getClusterXpos/i",
"getClusterYpos/i",
"getNumBlocksInCurrentCluster/i",
"getBlockIdForBlockInCluster/ii",
"isMasterInCurrentCluster/b",
"setClusteringActive/vb",
"makeARGB/iiiii",
"blendARGB/iii",
"fillPixel/viii",
"blendPixel/viii",
"fillRect/viiiii",
"blendRect/viiiii",
"blendGradientRect/viiiiiiii",
"blendCircle/vifffb",
"addPressurePoint/vifff",
"drawPressureMap/v",
"fadePressureMap/v",
"drawNumber/viiii",
"clearDisplay/v",
"clearDisplay/vi",
"displayBatteryLevel/v",
"sendMIDI/vi",
"sendMIDI/vii",
"sendMIDI/viii",
"sendNoteOn/viii",
"sendNoteOff/viii",
"sendAftertouch/viii",
"sendCC/viii",
"sendPitchBend/vii",
"sendPitchBend/viii",
"sendChannelPressure/vii",
"addPitchCorrectionPad/viiffff",
"setPitchCorrectionEnabled/vb",
"getPitchCorrectionPitchBend/iii",
"setChannelRange/vbii",
"assignChannel/ii",
"deassignChannel/vii",
"getControlChannel/i",
"useMPEDuplicateFilter/vb",
"getSensorValue/iii",
"handleTouchAsSeaboard/vi",
"setPowerSavingEnabled/vb",
"getLocalConfig/ii",
"setLocalConfig/vii",
"requestRemoteConfig/vii",
"setRemoteConfig/viii",
"setLocalConfigItemRange/viii",
"setLocalConfigActiveState/vibb",
"linkBlockIDtoController/vi",
"repaintControl/v",
"onControlPress/vi",
"onControlRelease/vi",
"initControl/viiiiiiiii",
"setButtonMode/vii",
"setButtonType/viii",
"setButtonMinMaxDefault/viiii",
"setButtonColours/viii",
"setButtonTriState/vii",
"padControllerInitDefault/vb",
"padControllerReset/v",
"padControllerRegenDefault/v",
"padControllerRepaint/v",
"padControllerDrawPad/vi",
"setUseDefaultKeyHandler/vb",
"setUseDefaultKeyHandler/vbb",
nullptr
};
} // namespace BlocksProtocol
} // namespace juce

+ 0
- 325
modules/juce_blocks_basics/protocol/juce_HostPacketBuilder.h View File

@@ -1,325 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace BlocksProtocol
{
/**
Helper class for constructing a packet for sending to a BLOCKS device
@tags{Blocks}
*/
template <int maxPacketBytes>
struct HostPacketBuilder
{
HostPacketBuilder() noexcept {}
HostPacketBuilder (const HostPacketBuilder&) = delete;
HostPacketBuilder (HostPacketBuilder&&) = default;
const void* getData() const noexcept { return data.getData(); }
int size() const noexcept { return data.size(); }
//==============================================================================
void writePacketSysexHeaderBytes (TopologyIndex deviceIndex) noexcept
{
static_assert (maxPacketBytes > 10, "Not enough bytes for a sensible message!");
jassert ((deviceIndex & 64) == 0);
data.writeHeaderSysexBytes (deviceIndex);
}
void writePacketSysexFooter() noexcept
{
data.writePacketSysexFooter();
}
//==============================================================================
bool deviceControlMessage (DeviceCommand command) noexcept
{
if (! data.hasCapacity ((int) MessageType::bits + (int) DeviceCommand::bits))
return false;
writeMessageType (MessageFromHost::deviceCommandMessage);
data << command;
return true;
}
//==============================================================================
bool beginDataChanges (PacketIndex packetIndex) noexcept
{
if (! data.hasCapacity ((int) MessageType::bits + (int) PacketIndex::bits + (int) DataChangeCommand::bits))
return false;
writeMessageType (MessageFromHost::sharedDataChange);
data << packetIndex;
return true;
}
bool endDataChanges (bool isLastChange) noexcept
{
if (! data.hasCapacity (DataChangeCommand::bits))
return false;
data << DataChangeCommand ((uint32) isLastChange ? endOfChanges : endOfPacket);
return true;
}
bool skipBytes (int numToSkip) noexcept
{
if (numToSkip <= 0)
return true;
auto state = data.getState();
while (numToSkip > ByteCountMany::maxValue)
{
if (! skipBytes (ByteCountMany::maxValue))
{
data.restore (state);
return false;
}
numToSkip -= ByteCountMany::maxValue;
}
if (numToSkip > ByteCountFew::maxValue)
{
if (! data.hasCapacity (DataChangeCommand::bits * 2 + ByteCountMany::bits))
{
data.restore (state);
return false;
}
data << DataChangeCommand ((uint32) skipBytesMany) << ByteCountMany ((uint32) numToSkip);
return true;
}
if (! data.hasCapacity (DataChangeCommand::bits * 2 + ByteCountFew::bits))
{
data.restore (state);
return false;
}
data << DataChangeCommand ((uint32) skipBytesFew) << ByteCountFew ((uint32) numToSkip);
return true;
}
bool setMultipleBytes (const uint8* values, int num) noexcept
{
if (num <= 0)
return true;
if (! data.hasCapacity (DataChangeCommand::bits * 2 + num * (1 + ByteValue::bits)))
return false;
data << DataChangeCommand ((uint32) setSequenceOfBytes);
for (int i = 0; i < num; ++i)
data << ByteValue ((uint32) values[i])
<< ByteSequenceContinues (i < num - 1 ? 1 : 0);
return true;
}
bool setMultipleBytes (uint8 value, uint8 lastValue, int num) noexcept
{
if (num <= 0)
return true;
if (num == 1)
return setMultipleBytes (&value, 1); // (this is a more compact message)
auto state = data.getState();
if (num > ByteCountMany::maxValue)
{
if (! setMultipleBytes (value, lastValue, ByteCountMany::maxValue))
{
data.restore (state);
return false;
}
return setMultipleBytes (value, lastValue, num - ByteCountMany::maxValue);
}
if (num > ByteCountFew::maxValue)
{
if (! data.hasCapacity (DataChangeCommand::bits * 2 + ByteCountMany::bits + ByteValue::bits))
{
data.restore (state);
return false;
}
data << DataChangeCommand ((uint32) setManyBytesWithValue)
<< ByteCountMany ((uint32) num)
<< ByteValue ((uint32) value);
return true;
}
if (value == lastValue)
{
if (! data.hasCapacity (DataChangeCommand::bits * 2 + ByteCountFew::bits))
{
data.restore (state);
return false;
}
data << DataChangeCommand ((uint32) setFewBytesWithLastValue) << ByteCountFew ((uint32) num);
return true;
}
if (! data.hasCapacity (DataChangeCommand::bits * 2 + ByteCountFew::bits + ByteValue::bits))
{
data.restore (state);
return false;
}
data << DataChangeCommand ((uint32) setFewBytesWithValue) << ByteCountFew ((uint32) num)
<< ByteValue ((uint32) value);
return true;
}
bool addProgramEventMessage (const int32* messageData)
{
if (! data.hasCapacity (BitSizes::programEventMessage))
return false;
writeMessageType (MessageFromHost::programEventMessage);
for (uint32 i = 0; i < numProgramMessageInts; ++i)
data << IntegerWithBitSize<32> ((uint32) messageData[i]);
return true;
}
bool addFirmwareUpdatePacket (const uint8* packetData, uint8 size)
{
if (! data.hasCapacity (MessageType::bits + FirmwareUpdatePacketSize::bits + 7 * size))
return false;
writeMessageType (MessageFromHost::firmwareUpdatePacket);
data << FirmwareUpdatePacketSize (size);
for (uint8 i = 0; i < size; ++i)
data << IntegerWithBitSize<7> ((uint32) packetData[i]);
return true;
}
//==============================================================================
bool addConfigSetMessage (int32 item, int32 value)
{
if (! data.hasCapacity (BitSizes::configSetMessage))
return false;
writeMessageType(MessageFromHost::configMessage);
ConfigCommand type = ConfigCommands::setConfig;
data << type << IntegerWithBitSize<8> ((uint32) item) << IntegerWithBitSize<32>((uint32) value);
return true;
}
bool addRequestMessage (int32 item)
{
if (! data.hasCapacity (BitSizes::configSetMessage))
return false;
writeMessageType(MessageFromHost::configMessage);
ConfigCommand type = ConfigCommands::requestConfig;
data << type << IntegerWithBitSize<32> (0) << IntegerWithBitSize<8> ((uint32) item);
return true;
}
bool addRequestFactorySyncMessage()
{
if (! data.hasCapacity ((int) MessageType::bits + (int) ConfigCommand::bits))
return false;
writeMessageType (MessageFromHost::configMessage);
ConfigCommand type = ConfigCommands::requestFactorySync;
data << type;
return true;
}
bool addRequestUserSyncMessage()
{
if (! data.hasCapacity ((int) MessageType::bits + (int) ConfigCommand::bits))
return false;
writeMessageType (MessageFromHost::configMessage);
ConfigCommand type = ConfigCommands::requestUserSync;
data << type;
return true;
}
//==============================================================================
bool addFactoryReset()
{
if (! data.hasCapacity (MessageType::bits))
return false;
writeMessageType (MessageFromHost::factoryReset);
return true;
}
bool addBlockReset()
{
if (! data.hasCapacity (MessageType::bits))
return false;
writeMessageType (MessageFromHost::blockReset);
return true;
}
bool addSetBlockName (const String& name)
{
if (name.length() > 32 || ! data.hasCapacity (MessageType::bits + 7 + (7 * name.length())))
return false;
writeMessageType (MessageFromHost::setName);
data << IntegerWithBitSize<7> ((uint32) name.length());
for (auto i = 0; i < name.length(); ++i)
data << IntegerWithBitSize<7> ((uint32) name.toRawUTF8()[i]);
data << IntegerWithBitSize<7> (0);
return true;
}
//==============================================================================
private:
Packed7BitArrayBuilder<maxPacketBytes> data;
void writeMessageType (MessageFromHost type) noexcept
{
data << MessageType ((uint32) type);
}
};
} // namespace BlocksProtocol
} // namespace juce

+ 0
- 383
modules/juce_blocks_basics/protocol/juce_HostPacketDecoder.h View File

@@ -1,383 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace BlocksProtocol
{
/**
Parses data packets from a BLOCKS device, and translates them into callbacks
on a handler object
@tags{Blocks}
*/
template <typename Handler>
struct HostPacketDecoder
{
static void processNextPacket (Handler& handler, TopologyIndex deviceIndex, const void* data, int size)
{
if (Packed7BitArrayReader::checksumIsOK (static_cast<const uint8*> (data), (uint32) size))
{
Packed7BitArrayReader reader (data, size - 1);
if (reader.getRemainingBits() < (int) PacketTimestamp::bits)
{
jassertfalse; // not a valid message..
return;
}
auto packetTimestamp = reader.read<PacketTimestamp>();
deviceIndex &= 63; // top bit is used as a direction indicator
for (;;)
{
auto nextMessageType = getMessageType (reader);
if (nextMessageType == 0)
break;
if (! processNextMessage (handler, reader, (MessageFromDevice) nextMessageType, deviceIndex, packetTimestamp))
break;
}
}
}
static uint32 getMessageType (Packed7BitArrayReader& reader)
{
if (reader.getRemainingBits() < MessageType::bits)
return 0;
return reader.read<MessageType>().get();
}
static bool processNextMessage (Handler& handler, Packed7BitArrayReader& reader,
MessageFromDevice messageType, TopologyIndex deviceIndex,
PacketTimestamp packetTimestamp)
{
switch (messageType)
{
case MessageFromDevice::deviceTopology: return handleTopology (handler, reader, true);
case MessageFromDevice::deviceTopologyExtend: return handleTopology (handler, reader, false);
case MessageFromDevice::deviceTopologyEnd: return handleTopologyEnd (handler, reader);
case MessageFromDevice::deviceVersion: return handleVersion (handler, reader);
case MessageFromDevice::deviceName: return handleName (handler, reader);
case MessageFromDevice::touchStart: return handleTouch (handler, reader, deviceIndex, packetTimestamp, true, false);
case MessageFromDevice::touchMove: return handleTouch (handler, reader, deviceIndex, packetTimestamp, false, false);
case MessageFromDevice::touchEnd: return handleTouch (handler, reader, deviceIndex, packetTimestamp, false, true);
case MessageFromDevice::touchStartWithVelocity: return handleTouchWithVelocity (handler, reader, deviceIndex, packetTimestamp, true, false);
case MessageFromDevice::touchMoveWithVelocity: return handleTouchWithVelocity (handler, reader, deviceIndex, packetTimestamp, false, false);
case MessageFromDevice::touchEndWithVelocity: return handleTouchWithVelocity (handler, reader, deviceIndex, packetTimestamp, false, true);
case MessageFromDevice::controlButtonDown: return handleButtonDownOrUp (handler, reader, deviceIndex, packetTimestamp, true);
case MessageFromDevice::controlButtonUp: return handleButtonDownOrUp (handler, reader, deviceIndex, packetTimestamp, false);
case MessageFromDevice::programEventMessage: return handleCustomMessage (handler, reader, deviceIndex, packetTimestamp);
case MessageFromDevice::packetACK: return handlePacketACK (handler, reader, deviceIndex);
case MessageFromDevice::firmwareUpdateACK: return handleFirmwareUpdateACK (handler, reader, deviceIndex);
case MessageFromDevice::configMessage: return handleConfigMessage (handler, reader, deviceIndex);
case MessageFromDevice::logMessage: return handleLogMessage (handler, reader, deviceIndex);
default:
jassertfalse; // got an invalid message type, could be a corrupt packet, or a
// message type that the host doesn't expect to get
return false;
}
}
static bool handleTopology (Handler& handler, Packed7BitArrayReader& reader, bool newTopology)
{
if (reader.getRemainingBits() < (int) DeviceCount::bits + (int) ConnectionCount::bits)
{
jassertfalse; // not enough data available for this message type!
return false;
}
auto deviceProtocolVersion = reader.read<ProtocolVersion>();
if (deviceProtocolVersion > currentProtocolVersion)
{
jassertfalse;
return false;
}
const uint32 numDevices = reader.read<DeviceCount>();
const uint32 numConnections = reader.read<ConnectionCount>();
if ((uint32) reader.getRemainingBits() < numDevices * BitSizes::topologyDeviceInfo
+ numConnections * BitSizes::topologyConnectionInfo)
{
jassertfalse; // not enough data available for this message type!
return false;
}
if (newTopology)
handler.beginTopology ((int) numDevices, (int) numConnections);
else
handler.extendTopology ((int) numDevices, (int) numConnections);
for (uint32 i = 0; i < numDevices; ++i)
handleTopologyDevice (handler, reader);
for (uint32 i = 0; i < numConnections; ++i)
handleTopologyConnection (handler, reader);
// Packet must be last in topology, otherwise wait for topology end message
if (numDevices < maxBlocksInTopologyPacket && numConnections < maxConnectionsInTopologyPacket)
handler.endTopology();
return true;
}
static bool handleTopologyEnd (Handler& handler, Packed7BitArrayReader& reader)
{
auto deviceProtocolVersion = reader.read<ProtocolVersion>();
if (deviceProtocolVersion > currentProtocolVersion)
{
jassertfalse;
return false;
}
handler.endTopology();
return true;
}
static void handleTopologyDevice (Handler& handler, Packed7BitArrayReader& reader)
{
DeviceStatus status;
for (uint32 i = 0; i < BlockSerialNumber::maxLength; ++i)
{
status.serialNumber.data[i] = (uint8) reader.readBits (7);
++status.serialNumber.length;
}
status.index = (TopologyIndex) reader.readBits (topologyIndexBits);
status.batteryLevel = reader.read<BatteryLevel>();
status.batteryCharging = reader.read<BatteryCharging>();
handler.handleTopologyDevice (status);
}
static void handleTopologyConnection (Handler& handler, Packed7BitArrayReader& reader)
{
DeviceConnection connection;
connection.device1 = (uint8) reader.readBits (topologyIndexBits);
connection.port1 = reader.read<ConnectorPort>();
connection.device2 = (uint8) reader.readBits (topologyIndexBits);
connection.port2 = reader.read<ConnectorPort>();
handler.handleTopologyConnection (connection);
}
static bool handleVersion (Handler& handler, Packed7BitArrayReader& reader)
{
DeviceVersion version;
version.index = (TopologyIndex) reader.readBits (topologyIndexBits);
version.version.length = (uint8) reader.readBits (7);
for (uint32 i = 0; i < version.version.length; ++i)
version.version.data[i] = (uint8) reader.readBits (7);
handler.handleVersion (version);
return true;
}
static bool handleName (Handler& handler, Packed7BitArrayReader& reader)
{
DeviceName name;
name.index = (TopologyIndex) reader.readBits (topologyIndexBits);
name.name.length = (uint8) reader.readBits (7);
for (uint32 i = 0; i < name.name.length; ++i)
name.name.data[i] = (uint8) reader.readBits (7);
handler.handleName (name);
return true;
}
static bool handleTouch (Handler& handler, Packed7BitArrayReader& reader, TopologyIndex deviceIndex,
PacketTimestamp packetTimestamp, bool isStart, bool isEnd)
{
if (reader.getRemainingBits() < (int) BitSizes::touchMessage - (int) MessageType::bits)
{
jassertfalse; // not enough data available for this message type!
return false;
}
auto timeOffset = reader.read<PacketTimestampOffset>();
auto touchIndex = reader.read<TouchIndex>();
auto x = reader.read<TouchPosition::Xcoord>();
auto y = reader.read<TouchPosition::Ycoord>();
auto z = reader.read<TouchPosition::Zcoord>();
handleTouch (handler, deviceIndex, packetTimestamp.get() + timeOffset.get(),
touchIndex, { x, y, z }, { 0, 0, 0 }, isStart, isEnd);
return true;
}
static bool handleTouchWithVelocity (Handler& handler, Packed7BitArrayReader& reader, TopologyIndex deviceIndex,
PacketTimestamp packetTimestamp, bool isStart, bool isEnd)
{
if (reader.getRemainingBits() < (int) BitSizes::touchMessageWithVelocity - (int) MessageType::bits)
{
jassertfalse; // not enough data available for this message type!
return false;
}
auto timeOffset = reader.read<PacketTimestampOffset>();
auto touchIndex = reader.read<TouchIndex>();
auto x = reader.read<TouchPosition::Xcoord>();
auto y = reader.read<TouchPosition::Ycoord>();
auto z = reader.read<TouchPosition::Zcoord>();
auto vx = reader.read<TouchVelocity::VXcoord>();
auto vy = reader.read<TouchVelocity::VYcoord>();
auto vz = reader.read<TouchVelocity::VZcoord>();
handleTouch (handler, deviceIndex, packetTimestamp.get() + timeOffset.get(),
touchIndex, { x, y, z }, { vx, vy, vz }, isStart, isEnd);
return true;
}
static void handleTouch (Handler& handler, TopologyIndex deviceIndex, uint32 timestamp, TouchIndex touchIndex,
TouchPosition position, TouchVelocity velocity, bool isStart, bool isEnd)
{
handler.handleTouchChange (deviceIndex, timestamp, touchIndex, position, velocity, isStart, isEnd);
}
static bool handleButtonDownOrUp (Handler& handler, Packed7BitArrayReader& reader, TopologyIndex deviceIndex,
PacketTimestamp packetTimestamp, bool isDown)
{
if (reader.getRemainingBits() < (int) BitSizes::controlButtonMessage - (int) MessageType::bits)
{
jassertfalse; // not enough data available for this message type!
return false;
}
auto timeOffset = reader.read<PacketTimestampOffset>();
auto buttonID = reader.read<ControlButtonID>();
handler.handleControlButtonUpDown (deviceIndex, packetTimestamp.get() + timeOffset.get(), buttonID, isDown);
return true;
}
static bool handleCustomMessage (Handler& handler, Packed7BitArrayReader& reader,
TopologyIndex deviceIndex, PacketTimestamp packetTimestamp)
{
if (reader.getRemainingBits() < BitSizes::programEventMessage - (int) MessageType::bits)
{
jassertfalse; // not enough data available for this message type!
return false;
}
int32 data[numProgramMessageInts] = {};
for (uint32 i = 0; i < numProgramMessageInts; ++i)
data[i] = (int32) reader.read<IntegerWithBitSize<32>>().get();
handler.handleCustomMessage (deviceIndex, packetTimestamp.get(), data);
return true;
}
static bool handlePacketACK (Handler& handler, Packed7BitArrayReader& reader, TopologyIndex deviceIndex)
{
if (reader.getRemainingBits() < BitSizes::packetACK - (int) MessageType::bits)
{
jassertfalse; // not enough data available for this message type!
return false;
}
handler.handlePacketACK (deviceIndex, reader.read<PacketCounter>());
return true;
}
static bool handleFirmwareUpdateACK (Handler& handler, Packed7BitArrayReader& reader, TopologyIndex deviceIndex)
{
if (reader.getRemainingBits() < FirmwareUpdateACKCode::bits)
{
jassertfalse; // not enough data available for this message type!
return false;
}
auto ackCode = reader.read<FirmwareUpdateACKCode>();
auto ackDetail = reader.read<FirmwareUpdateACKDetail>();
handler.handleFirmwareUpdateACK (deviceIndex, ackCode, ackDetail);
return true;
}
static bool handleConfigMessage (Handler& handler, Packed7BitArrayReader& reader, TopologyIndex deviceIndex)
{
ConfigCommand type = reader.read<ConfigCommand>().get();
if (type == updateConfig)
{
auto item = (int32) reader.read<IntegerWithBitSize<8>>().get();
auto value = (int32) reader.read<IntegerWithBitSize<32>>().get();
auto min = (int32) reader.read<IntegerWithBitSize<32>>().get();
auto max = (int32) reader.read<IntegerWithBitSize<32>>().get();
handler.handleConfigUpdateMessage (deviceIndex, item, value, min, max);
return true;
}
if (type == setConfig)
{
auto item = (int32) reader.read<IntegerWithBitSize<8>>().get();
auto value = (int32) reader.read<IntegerWithBitSize<32>>().get();
handler.handleConfigSetMessage (deviceIndex, item, value);
return true;
}
if (type == factorySyncEnd)
{
handler.handleConfigFactorySyncEndMessage (deviceIndex);
}
if (type == factorySyncReset)
{
handler.handleConfigFactorySyncResetMessage (deviceIndex);
}
return true;
}
static bool handleLogMessage (Handler& handler, Packed7BitArrayReader& reader, TopologyIndex deviceIndex)
{
String message;
while (reader.getRemainingBits() >= 7)
{
uint32 c = reader.read<IntegerWithBitSize<7>>();
message << (char) c;
}
handler.handleLogMessage (deviceIndex, message);
return true;
}
};
} // namespace BlocksProtocol
} // namespace juce

+ 0
- 101
modules/juce_blocks_basics/topology/internal/juce_BandwidthStatsLogger.cpp View File

@@ -1,101 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace
{
struct PortIOStats
{
PortIOStats (const char* nm) : name (nm) {}
const char* const name;
int byteCount = 0;
int messageCount = 0;
int bytesPerSec = 0;
int largestMessageBytes = 0;
int lastMessageBytes = 0;
void update (double elapsedSec)
{
if (byteCount > 0)
{
bytesPerSec = (int) (byteCount / elapsedSec);
byteCount = 0;
Logger::writeToLog (getString());
}
}
String getString() const
{
return String (name) + ": "
+ "count=" + String (messageCount).paddedRight (' ', 7)
+ "rate=" + (String (bytesPerSec / 1024.0f, 1) + " Kb/sec").paddedRight (' ', 11)
+ "largest=" + (String (largestMessageBytes) + " bytes").paddedRight (' ', 11)
+ "last=" + (String (lastMessageBytes) + " bytes").paddedRight (' ', 11);
}
void registerMessage (int numBytes) noexcept
{
byteCount += numBytes;
++messageCount;
lastMessageBytes = numBytes;
largestMessageBytes = jmax (largestMessageBytes, numBytes);
}
};
static PortIOStats inputStats { "Input" }, outputStats { "Output" };
static uint32 startTime = 0;
static void resetOnSecondBoundary()
{
auto now = Time::getMillisecondCounter();
double elapsedSec = (now - startTime) / 1000.0;
if (elapsedSec >= 1.0)
{
inputStats.update (elapsedSec);
outputStats.update (elapsedSec);
startTime = now;
}
}
static void registerBytesOut (int numBytes)
{
outputStats.registerMessage (numBytes);
resetOnSecondBoundary();
}
static void registerBytesIn (int numBytes)
{
inputStats.registerMessage (numBytes);
resetOnSecondBoundary();
}
}
String getMidiIOStats()
{
return inputStats.getString() + " " + outputStats.getString();
}
} // namespace juce

+ 0
- 1151
modules/juce_blocks_basics/topology/internal/juce_BlockImplementation.cpp
File diff suppressed because it is too large
View File


+ 0
- 130
modules/juce_blocks_basics/topology/internal/juce_BlockSerialReader.cpp View File

@@ -1,130 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class BlockSerialReader
: private MIDIDeviceConnection::Listener,
private Timer
{
public:
//==============================================================================
BlockSerialReader (MIDIDeviceConnection& deviceConnectionToUse) : deviceConnection (deviceConnectionToUse)
{
deviceConnection.addListener (this);
startTimer (10);
}
~BlockSerialReader() override
{
deviceConnection.removeListener (this);
}
bool hasSerial() const { return serial.isNotEmpty(); }
String getSerial() const { return serial; }
private:
MIDIDeviceConnection& deviceConnection;
String serial;
bool shouldStop() { return hasSerial(); }
//==============================================================================
void timerCallback() override
{
if (shouldStop())
{
stopTimer();
return;
}
sendRequest();
startTimer (300);
}
void sendRequest()
{
const uint8 dumpRequest[] = { 0xf0, 0x00, 0x21, 0x10, 0x78, 0x3f, 0xf7 };
deviceConnection.sendMessageToDevice (dumpRequest, sizeof (dumpRequest));
}
void handleIncomingMidiMessage (const MidiMessage& message) override
{
if (hasSerial())
return;
if (isResponse (message))
parseResponse (message);
}
void connectionBeingDeleted (const MIDIDeviceConnection&) override
{
stopTimer();
}
bool isResponse (const MidiMessage message)
{
const uint8 roliDumpHeader[] = { 0xf0, 0x00, 0x21, 0x10, 0x78};
return memcmp (message.getRawData(), roliDumpHeader, sizeof (roliDumpHeader)) == 0;
}
void parseResponse (const MidiMessage& message)
{
int index = findMacAddressStart (message);
if (index >= 0)
{
const int macSize = 17;
const int offset = index + macSize;
const int serialSize = 16;
if (message.getRawDataSize() - offset < serialSize)
{
jassertfalse;
return;
}
serial = String ((const char*)message.getRawData() + offset, serialSize);
}
}
int findMacAddressStart (const MidiMessage& message)
{
const uint8 macStart[] = { '4', '8', ':', 'B', '6', ':', '2', '0', ':' };
return findSequence (macStart, sizeof (macStart), message);
}
int findSequence (const uint8* sequence, int sequenceSize, const MidiMessage& message)
{
for (int i = 0; i < message.getRawDataSize() - sequenceSize; i++)
{
if (memcmp (message.getRawData() + i, sequence, size_t (sequenceSize)) == 0)
return i;
}
return -1;
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockSerialReader)
};
}

+ 0
- 785
modules/juce_blocks_basics/topology/internal/juce_ConnectedDeviceGroup.cpp View File

@@ -1,785 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace
{
static Block::Timestamp deviceTimestampToHost (uint32 timestamp) noexcept
{
return static_cast<Block::Timestamp> (timestamp);
}
}
template <typename Detector>
struct ConnectedDeviceGroup : private AsyncUpdater,
private Timer
{
//==============================================================================
ConnectedDeviceGroup (Detector& d, const String& name, PhysicalTopologySource::DeviceConnection* connection)
: detector (d), deviceName (name), deviceConnection (connection)
{
if (auto midiDeviceConnection = static_cast<MIDIDeviceConnection*> (deviceConnection.get()))
{
ScopedLock lock (midiDeviceConnection->criticalSecton);
setMidiMessageCallback();
}
else
{
setMidiMessageCallback();
}
initialiseSerialReader();
startTimer (200);
sendTopologyRequest();
}
~ConnectedDeviceGroup() override
{
for (const auto& device : currentDeviceInfo)
detector.handleDeviceRemoved (device);
}
bool isStillConnected (const StringArray& detectedDevices) const noexcept
{
return detectedDevices.contains (deviceName) && ! failedToGetTopology();
}
bool contains (Block::UID uid)
{
return getIndexFromDeviceID (uid) >= 0;
}
void handleBlockRestarting (Block::UID deviceID)
{
forceApiDisconnected (deviceID);
}
//==============================================================================
// The following methods will be called by the HostPacketDecoder:
void beginTopology (int numDevices, int numConnections)
{
incomingTopologyDevices.clearQuick();
incomingTopologyDevices.ensureStorageAllocated (numDevices);
incomingTopologyConnections.clearQuick();
incomingTopologyConnections.ensureStorageAllocated (numConnections);
}
void extendTopology (int numDevices, int numConnections)
{
incomingTopologyDevices.ensureStorageAllocated (incomingTopologyDevices.size() + numDevices);
incomingTopologyConnections.ensureStorageAllocated (incomingTopologyConnections.size() + numConnections);
}
void handleTopologyDevice (BlocksProtocol::DeviceStatus status)
{
incomingTopologyDevices.add (status);
}
void handleTopologyConnection (BlocksProtocol::DeviceConnection connection)
{
incomingTopologyConnections.add (connection);
}
void endTopology()
{
lastTopologyReceiveTime = Time::getCurrentTime();
if (incomingTopologyDevices.isEmpty()
|| incomingTopologyConnections.size() < incomingTopologyDevices.size() - 1)
{
LOG_CONNECTIVITY ("Invalid topology or device list received.");
LOG_CONNECTIVITY ("Device size : " << incomingTopologyDevices.size());
LOG_CONNECTIVITY ("Connections size : " << incomingTopologyConnections.size());
scheduleNewTopologyRequest();
return;
}
LOG_CONNECTIVITY ("Valid topology received");
updateCurrentDeviceList();
updateCurrentDeviceConnections();
}
void handleVersion (BlocksProtocol::DeviceVersion version)
{
setVersion (version.index, version.version);
}
void handleName (BlocksProtocol::DeviceName name)
{
if (name.name.length <= 1)
return;
if (const auto info = getDeviceInfoFromIndex (name.index))
{
if (info->name == name.name)
return;
info->name = name.name;
detector.handleDeviceUpdated (*info);
}
}
void handleControlButtonUpDown (BlocksProtocol::TopologyIndex deviceIndex, uint32 timestamp,
BlocksProtocol::ControlButtonID buttonID, bool isDown)
{
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
detector.handleButtonChange (deviceID, deviceTimestampToHost (timestamp), buttonID.get(), isDown);
}
void handleCustomMessage (BlocksProtocol::TopologyIndex deviceIndex, uint32 timestamp, const int32* data)
{
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
detector.handleCustomMessage (deviceID, deviceTimestampToHost (timestamp), data);
}
void handleTouchChange (BlocksProtocol::TopologyIndex deviceIndex,
uint32 timestamp,
BlocksProtocol::TouchIndex touchIndex,
BlocksProtocol::TouchPosition position,
BlocksProtocol::TouchVelocity velocity,
bool isStart, bool isEnd)
{
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
{
TouchSurface::Touch touch;
touch.index = (int) touchIndex.get();
touch.x = (float) position.x.toUnipolarFloat();
touch.y = (float) position.y.toUnipolarFloat();
touch.z = (float) position.z.toUnipolarFloat();
touch.xVelocity = velocity.vx.toBipolarFloat();
touch.yVelocity = velocity.vy.toBipolarFloat();
touch.zVelocity = velocity.vz.toBipolarFloat();
touch.eventTimestamp = deviceTimestampToHost (timestamp);
touch.isTouchStart = isStart;
touch.isTouchEnd = isEnd;
touch.blockUID = deviceID;
setTouchStartPosition (touch);
detector.handleTouchChange (deviceID, touch);
}
}
void setTouchStartPosition (TouchSurface::Touch& touch)
{
auto& startPos = touchStartPositions.getValue (touch);
if (touch.isTouchStart)
startPos = { touch.x, touch.y };
touch.startX = startPos.x;
touch.startY = startPos.y;
}
void handlePacketACK (BlocksProtocol::TopologyIndex deviceIndex,
BlocksProtocol::PacketCounter counter)
{
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
{
detector.handleSharedDataACK (deviceID, counter);
updateApiPing (deviceID);
}
}
void handleFirmwareUpdateACK (BlocksProtocol::TopologyIndex deviceIndex,
BlocksProtocol::FirmwareUpdateACKCode resultCode,
BlocksProtocol::FirmwareUpdateACKDetail resultDetail)
{
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
{
detector.handleFirmwareUpdateACK (deviceID, (uint8) resultCode.get(), (uint32) resultDetail.get());
updateApiPing (deviceID);
}
}
void handleConfigUpdateMessage (BlocksProtocol::TopologyIndex deviceIndex,
int32 item, int32 value, int32 min, int32 max)
{
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
detector.handleConfigUpdateMessage (deviceID, item, value, min, max);
}
void handleConfigSetMessage (BlocksProtocol::TopologyIndex deviceIndex,
int32 item, int32 value)
{
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
detector.handleConfigSetMessage (deviceID, item, value);
}
void handleConfigFactorySyncEndMessage (BlocksProtocol::TopologyIndex deviceIndex)
{
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
detector.handleConfigFactorySyncEndMessage (deviceID);
}
void handleConfigFactorySyncResetMessage (BlocksProtocol::TopologyIndex deviceIndex)
{
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
detector.handleConfigFactorySyncResetMessage (deviceID);
}
void handleLogMessage (BlocksProtocol::TopologyIndex deviceIndex, const String& message)
{
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
detector.handleLogMessage (deviceID, message);
}
//==============================================================================
template <typename PacketBuilder>
bool sendMessageToDevice (const PacketBuilder& builder) const
{
if (deviceConnection->sendMessageToDevice (builder.getData(), (size_t) builder.size()))
{
#if DUMP_BANDWIDTH_STATS
registerBytesOut (builder.size());
#endif
return true;
}
return false;
}
PhysicalTopologySource::DeviceConnection* getDeviceConnection()
{
return deviceConnection.get();
}
Array<BlockDeviceConnection> getCurrentDeviceConnections()
{
Array<BlockDeviceConnection> connections;
for (const auto& connection : currentDeviceConnections)
if (isApiConnected (getDeviceIDFromIndex (connection.device1)) && isApiConnected (getDeviceIDFromIndex (connection.device2)))
connections.add (getBlockDeviceConnection (connection));
return connections;
}
Detector& detector;
String deviceName;
static constexpr double pingTimeoutSeconds = 6.0;
private:
//==============================================================================
Array<DeviceInfo> currentDeviceInfo;
Array<BlocksProtocol::DeviceStatus> incomingTopologyDevices;
Array<BlocksProtocol::DeviceConnection> incomingTopologyConnections, currentDeviceConnections;
std::unique_ptr<PhysicalTopologySource::DeviceConnection> deviceConnection;
CriticalSection incomingPacketLock;
Array<MemoryBlock> incomingPackets;
std::unique_ptr<DepreciatedVersionReader> depreciatedVersionReader;
std::unique_ptr<BlockSerialReader> masterSerialReader;
struct TouchStart { float x, y; };
TouchList<TouchStart> touchStartPositions;
static constexpr Block::UID invalidUid = 0;
Block::UID masterBlockUid = invalidUid;
//==============================================================================
void timerCallback() override
{
const auto now = Time::getCurrentTime();
if ((now > lastTopologyReceiveTime + RelativeTime::seconds (30.0))
&& now > lastTopologyRequestTime + RelativeTime::seconds (1.0)
&& numTopologyRequestsSent < 4)
sendTopologyRequest();
checkApiTimeouts (now);
startApiModeOnConnectedBlocks();
checkMasterBlockVersion();
checkMasterSerial();
}
//==============================================================================
void setMidiMessageCallback()
{
deviceConnection->handleMessageFromDevice = [this] (const void* data, size_t dataSize)
{
this->handleIncomingMessage (data, dataSize);
};
}
void handleIncomingMessage (const void* data, size_t dataSize)
{
MemoryBlock mb (data, dataSize);
{
const ScopedLock sl (incomingPacketLock);
incomingPackets.add (std::move (mb));
}
triggerAsyncUpdate();
#if DUMP_BANDWIDTH_STATS
registerBytesIn ((int) dataSize);
#endif
}
void handleAsyncUpdate() override
{
Array<MemoryBlock> packets;
packets.ensureStorageAllocated (32);
{
const ScopedLock sl (incomingPacketLock);
incomingPackets.swapWith (packets);
}
for (auto& packet : packets)
{
auto data = static_cast<const uint8*> (packet.getData());
BlocksProtocol::HostPacketDecoder<ConnectedDeviceGroup>
::processNextPacket (*this, *data, data + 1, (int) packet.getSize() - 1);
}
}
bool sendCommandMessage (BlocksProtocol::TopologyIndex deviceIndex, uint32 commandID) const
{
BlocksProtocol::HostPacketBuilder<64> p;
p.writePacketSysexHeaderBytes (deviceIndex);
p.deviceControlMessage (commandID);
p.writePacketSysexFooter();
return sendMessageToDevice (p);
}
//==============================================================================
Time lastTopologyRequestTime, lastTopologyReceiveTime;
int numTopologyRequestsSent = 0;
void scheduleNewTopologyRequest()
{
LOG_CONNECTIVITY ("Topology Request Scheduled");
numTopologyRequestsSent = 0;
lastTopologyReceiveTime = Time();
lastTopologyRequestTime = Time::getCurrentTime();
}
void sendTopologyRequest()
{
++numTopologyRequestsSent;
lastTopologyRequestTime = Time::getCurrentTime();
sendCommandMessage (0, BlocksProtocol::requestTopologyMessage);
}
bool failedToGetTopology() const noexcept
{
return numTopologyRequestsSent >= 4 && lastTopologyReceiveTime == Time();
}
//==============================================================================
void checkMasterBlockVersion()
{
if (depreciatedVersionReader == nullptr)
return;
const auto masterVersion = depreciatedVersionReader->getVersionNumber();
if (masterVersion.isNotEmpty())
{
const auto masterIndex = getIndexFromDeviceID (masterBlockUid);
if (masterIndex >= 0)
setVersion (BlocksProtocol::TopologyIndex (masterIndex), masterVersion);
else
jassertfalse;
}
}
void setVersion (const BlocksProtocol::TopologyIndex index, const BlocksProtocol::VersionNumber versionNumber)
{
if (versionNumber.length <= 1)
return;
if (const auto info = getDeviceInfoFromIndex (index))
{
if (info->version == versionNumber)
return;
if (info->uid == masterBlockUid)
depreciatedVersionReader.reset();
info->version = versionNumber;
detector.handleDeviceUpdated (*info);
}
}
//==============================================================================
void checkMasterSerial()
{
if (masterSerialReader == nullptr)
initialiseSerialReader();
if (masterSerialReader == nullptr)
return;
if (masterBlockUid != invalidUid && masterSerialReader->hasSerial())
{
auto uid = getBlockUIDFromSerialNumber (masterSerialReader->getSerial());
if (uid != masterBlockUid)
updateMasterUid (uid);
}
}
void updateMasterUid (const Block::UID newMasterUid)
{
LOG_CONNECTIVITY ("Updating master from " + String (masterBlockUid) + " to " + String (newMasterUid));
masterBlockUid = newMasterUid;
Array<DeviceInfo> devicesToUpdate;
for (auto& info : currentDeviceInfo)
{
if (info.masterUid != masterBlockUid)
{
info.masterUid = masterBlockUid;
info.isMaster = info.uid == masterBlockUid;
devicesToUpdate.add (info);
}
}
detector.handleDevicesUpdated (devicesToUpdate);
}
Block::UID determineMasterBlockUid (Array<BlocksProtocol::DeviceStatus> devices)
{
if (masterSerialReader != nullptr && masterSerialReader->hasSerial())
{
auto foundSerial = masterSerialReader->getSerial();
for (const auto& device : incomingTopologyDevices)
{
if (device.serialNumber.asString() == foundSerial)
{
LOG_CONNECTIVITY ("Found master from serial " + foundSerial);
return getBlockUIDFromSerialNumber (foundSerial);
}
}
}
if (devices.size() > 0)
{
LOG_CONNECTIVITY ("Found master from first device " + devices[0].serialNumber.asString());
return getBlockUIDFromSerialNumber (incomingTopologyDevices[0].serialNumber);
}
jassertfalse;
return invalidUid;
}
//==============================================================================
struct BlockPingTime
{
Block::UID blockUID;
Time lastPing;
Time connected;
};
Array<BlockPingTime> blockPings;
BlockPingTime* getPing (Block::UID uid)
{
for (auto& ping : blockPings)
if (uid == ping.blockUID)
return &ping;
return nullptr;
}
void removePing (Block::UID uid)
{
const auto remove = [uid] (const BlockPingTime& ping)
{
if (uid == ping.blockUID)
{
LOG_CONNECTIVITY ("API Disconnected by topology update " << ping.blockUID);
return true;
}
return false;
};
blockPings.removeIf (remove);
}
void updateApiPing (Block::UID uid)
{
const auto now = Time::getCurrentTime();
if (auto* ping = getPing (uid))
{
LOG_PING ("Ping: " << uid << " " << now.formatted ("%Mm %Ss"));
ping->lastPing = now;
}
else
{
LOG_CONNECTIVITY ("API Connected " << uid);
blockPings.add ({ uid, now, now });
if (const auto info = getDeviceInfoFromUID (uid))
detector.handleDeviceAdded (*info);
}
}
bool isApiConnected (Block::UID uid)
{
return getPing (uid) != nullptr;
}
void forceApiDisconnected (Block::UID uid)
{
for (auto dependentUID : detector.getDnaDependentDeviceUIDs (uid))
removeDevice (dependentUID);
removeDevice (uid);
if (uid == masterBlockUid)
{
masterBlockUid = invalidUid;
masterSerialReader.reset();
}
scheduleNewTopologyRequest();
}
void checkApiTimeouts (Time now)
{
Array<Block::UID> toRemove;
for (const auto& ping : blockPings)
{
if (ping.lastPing < now - RelativeTime::seconds (pingTimeoutSeconds))
{
LOG_CONNECTIVITY ("Ping timeout: " << ping.blockUID);
toRemove.add (ping.blockUID);
scheduleNewTopologyRequest();
}
}
for (const auto& uid : toRemove)
removeDevice (uid);
}
void startApiModeOnConnectedBlocks()
{
for (auto& info : currentDeviceInfo)
{
if (! isApiConnected (info.uid))
{
LOG_CONNECTIVITY ("API Try " << info.uid);
sendCommandMessage (info.index, BlocksProtocol::endAPIMode);
sendCommandMessage (info.index, BlocksProtocol::beginAPIMode);
}
}
}
//==============================================================================
Block::UID getDeviceIDFromIndex (BlocksProtocol::TopologyIndex index) noexcept
{
for (const auto& device : currentDeviceInfo)
if (device.index == index)
return device.uid;
scheduleNewTopologyRequest();
return {};
}
int getIndexFromDeviceID (Block::UID uid) const noexcept
{
for (auto& d : currentDeviceInfo)
if (d.uid == uid)
return d.index;
return -1;
}
DeviceInfo* getDeviceInfoFromUID (Block::UID uid) noexcept
{
for (auto& d : currentDeviceInfo)
if (d.uid == uid)
return &d;
return nullptr;
}
DeviceInfo* getDeviceInfoFromIndex (BlocksProtocol::TopologyIndex index) noexcept
{
for (auto& d : currentDeviceInfo)
if (d.index == index)
return &d;
return nullptr;
}
void removeDeviceInfo (Block::UID uid)
{
currentDeviceInfo.removeIf ([uid] (const DeviceInfo& info) { return info.uid == uid; });
}
const DeviceStatus* getIncomingDeviceStatus (BlockSerialNumber serialNumber) const
{
for (auto& device : incomingTopologyDevices)
if (device.serialNumber == serialNumber)
return &device;
return nullptr;
}
//==============================================================================
void removeDevice (Block::UID uid)
{
LOG_CONNECTIVITY ("Removing device: " << uid);
if (const auto info = getDeviceInfoFromUID (uid))
detector.handleDeviceRemoved (*info);
removeDeviceInfo (uid);
removePing (uid);
}
void updateCurrentDeviceList()
{
Array<Block::UID> toRemove;
//Update known devices
for (int i = currentDeviceInfo.size(); --i >= 0; )
{
auto& currentDevice = currentDeviceInfo.getReference (i);
if (const auto newStatus = getIncomingDeviceStatus (currentDevice.serial))
{
if (currentDevice.index != newStatus->index)
{
currentDevice.index = newStatus->index;
detector.handleIndexChanged (currentDevice.uid, currentDevice.index);
}
if (currentDevice.batteryCharging != newStatus->batteryCharging)
{
currentDevice.batteryCharging = newStatus->batteryCharging;
detector.handleBatteryChargingChanged (currentDevice.uid, currentDevice.batteryCharging);
}
if (currentDevice.batteryLevel != newStatus->batteryLevel)
{
currentDevice.batteryLevel = newStatus->batteryLevel;
detector.handleBatteryLevelChanged (currentDevice.uid, currentDevice.batteryLevel);
}
}
else
{
toRemove.add (currentDevice.uid);
}
}
for (const auto& uid : toRemove)
removeDevice (uid);
if (masterBlockUid == invalidUid)
{
masterBlockUid = determineMasterBlockUid (incomingTopologyDevices);
initialiseVersionReader();
}
//Add new devices
for (const auto& device : incomingTopologyDevices)
{
const auto uid = getBlockUIDFromSerialNumber (device.serialNumber);
if (getDeviceInfoFromUID (uid) == nullptr)
{
currentDeviceInfo.add ({ uid,
device.index,
device.serialNumber,
BlocksProtocol::VersionNumber(),
BlocksProtocol::BlockName(),
device.batteryLevel,
device.batteryCharging,
masterBlockUid });
}
}
}
//==============================================================================
Block::ConnectionPort convertConnectionPort (Block::UID uid, BlocksProtocol::ConnectorPort p) noexcept
{
if (auto* info = getDeviceInfoFromUID (uid))
return BlocksProtocol::BlockDataSheet (info->serial).convertPortIndexToConnectorPort (p);
jassertfalse;
return { Block::ConnectionPort::DeviceEdge::north, 0 };
}
BlockDeviceConnection getBlockDeviceConnection (const BlocksProtocol::DeviceConnection& connection)
{
BlockDeviceConnection dc;
dc.device1 = getDeviceIDFromIndex (connection.device1);
dc.device2 = getDeviceIDFromIndex (connection.device2);
if (dc.device1 <= 0 || dc.device2 <= 0)
jassertfalse;
dc.connectionPortOnDevice1 = convertConnectionPort (dc.device1, connection.port1);
dc.connectionPortOnDevice2 = convertConnectionPort (dc.device2, connection.port2);
return dc;
}
void updateCurrentDeviceConnections()
{
currentDeviceConnections.clearQuick();
currentDeviceConnections.swapWith (incomingTopologyConnections);
detector.handleConnectionsChanged();
}
void initialiseVersionReader()
{
if (auto midiDeviceConnection = static_cast<MIDIDeviceConnection*> (deviceConnection.get()))
depreciatedVersionReader = std::make_unique<DepreciatedVersionReader> (*midiDeviceConnection);
}
void initialiseSerialReader()
{
if (auto midiDeviceConnection = static_cast<MIDIDeviceConnection*> (deviceConnection.get()))
masterSerialReader = std::make_unique<BlockSerialReader> (*midiDeviceConnection);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectedDeviceGroup)
};
} // namespace juce

+ 0
- 139
modules/juce_blocks_basics/topology/internal/juce_DepreciatedVersionReader.cpp View File

@@ -1,139 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
Firmware below 0.3.0 does not report its version over the Blocks API.
This class can make requests and process responses to retrieve the master Block version.
*/
class DepreciatedVersionReader : private MIDIDeviceConnection::Listener,
private Timer
{
public:
//==============================================================================
DepreciatedVersionReader (MIDIDeviceConnection& deviceConnectionToUse)
: deviceConnection (deviceConnectionToUse)
{
deviceConnection.addListener (this);
startTimer (10);
}
//==============================================================================
~DepreciatedVersionReader() override
{
deviceConnection.removeListener (this);
}
//==============================================================================
BlocksProtocol::VersionNumber getVersionNumber()
{
if (! allRequestsComplete())
return {};
auto highestVersion = result[0];
for (size_t i = 1; i < numFirmwareApps; ++i)
{
const BlocksVersion highest { highestVersion.asString() };
const BlocksVersion test { result[i].asString() };
if (highest < test)
highestVersion = result[i];
}
return highestVersion;
}
private:
//==============================================================================
static constexpr size_t numFirmwareApps = 3;
BlocksProtocol::VersionNumber result[numFirmwareApps];
MIDIDeviceConnection& deviceConnection;
Atomic<size_t> currentRequest = 0;
//==============================================================================
bool allRequestsComplete() const { return currentRequest.get() >= numFirmwareApps; }
//==============================================================================
void makeNextRequest()
{
static constexpr size_t requestSize { 8 };
static constexpr uint8 requests[][requestSize] = {{ 0xf0, 0x00, 0x21, 0x10, 0x47, 0x03, 0x00, 0xf7 }, // Main App
{ 0xf0, 0x00, 0x21, 0x10, 0x47, 0x03, 0x03, 0xf7 }, // Bootloader
{ 0xf0, 0x00, 0x21, 0x10, 0x47, 0x03, 0x01, 0xf7 }}; // Stm32
static const BlocksVersion depreciatedVersion ("0.3.0");
if (currentRequest.get() == numFirmwareApps - 1
&& (BlocksVersion (result[0].asString()) >= depreciatedVersion
|| BlocksVersion (result[1].asString()) >= depreciatedVersion))
{
stopTimer();
}
else
{
deviceConnection.sendMessageToDevice (&requests[currentRequest.get()][0], requestSize);
}
}
//==============================================================================
void processVersionMessage (const uint8* data, const size_t size)
{
if (currentRequest.get() >= numFirmwareApps || size < 1 || size - 1 > VersionNumber::maxLength)
return;
result[currentRequest.get()].length = uint8 (size - 1);
memcpy (result[currentRequest.get()].data, data, result[currentRequest.get()].length);
++currentRequest;
allRequestsComplete() ? stopTimer() : startTimer (10);
}
//==============================================================================
void handleIncomingMidiMessage (const MidiMessage& message) override
{
const uint8 roliVersionHeader[] = { 0xf0, 0x00, 0x21, 0x10, 0x47, 0x03 };
const auto headerSize = sizeof (roliVersionHeader);
const auto data = message.getRawData();
const auto size = size_t (message.getRawDataSize());
if (memcmp (data, roliVersionHeader, headerSize) == 0)
processVersionMessage (data + headerSize, size - headerSize);
}
void connectionBeingDeleted (const MIDIDeviceConnection&) override {}
//==============================================================================
void timerCallback() override
{
startTimer (200);
makeNextRequest();
}
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DepreciatedVersionReader)
};
} // namespace juce

+ 0
- 797
modules/juce_blocks_basics/topology/internal/juce_Detector.cpp View File

@@ -1,797 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/** This is the main singleton object that keeps track of connected blocks */
struct Detector : public ReferenceCountedObject,
private Timer,
private AsyncUpdater
{
using BlockImpl = BlockImplementation<Detector>;
Detector() : defaultDetector (new MIDIDeviceDetector()), deviceDetector (*defaultDetector)
{
startTimer (10);
}
Detector (PhysicalTopologySource::DeviceDetector& dd) : deviceDetector (dd)
{
startTimer (10);
}
~Detector() override
{
jassert (activeTopologySources.isEmpty());
}
using Ptr = ReferenceCountedObjectPtr<Detector>;
static Detector::Ptr getDefaultDetector()
{
auto& d = getDefaultDetectorPointer();
if (d == nullptr)
d = new Detector();
return d;
}
static Detector::Ptr& getDefaultDetectorPointer()
{
static Detector::Ptr defaultDetector;
return defaultDetector;
}
void detach (PhysicalTopologySource* pts)
{
activeTopologySources.removeAllInstancesOf (pts);
if (activeTopologySources.isEmpty())
{
for (auto& b : currentTopology.blocks)
if (auto bi = BlockImpl::getFrom (b))
bi->sendCommandMessage (BlocksProtocol::endAPIMode);
currentTopology = {};
auto& d = getDefaultDetectorPointer();
if (d != nullptr && d->getReferenceCount() == 2)
getDefaultDetectorPointer() = nullptr;
}
}
bool isConnected (Block::UID deviceID) const noexcept
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED // This method must only be called from the message thread!
for (auto&& b : currentTopology.blocks)
if (b->uid == deviceID)
return true;
return false;
}
bool isConnectedViaBluetooth (const Block& block) const noexcept
{
if (const auto connection = getDeviceConnectionFor (block))
if (const auto midiConnection = dynamic_cast<const MIDIDeviceConnection*> (connection))
if (midiConnection->midiInput != nullptr)
return midiConnection->midiInput->getName().containsIgnoreCase ("bluetooth");
return false;
}
void handleDeviceAdded (const DeviceInfo& info)
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
const auto blockWasRemoved = containsBlockWithUID (blocksToRemove, info.uid);
const auto knownBlock = std::find_if (previouslySeenBlocks.begin(), previouslySeenBlocks.end(),
[uid = info.uid] (Block::Ptr block) { return uid == block->uid; });
Block::Ptr block;
if (knownBlock != previouslySeenBlocks.end())
{
block = *knownBlock;
if (auto* blockImpl = BlockImpl::getFrom (*block))
{
blockImpl->markReconnected (info);
previouslySeenBlocks.removeObject (block);
}
}
else
{
block = new BlockImpl (*this, info);
}
currentTopology.blocks.addIfNotAlreadyThere (block);
if (blockWasRemoved)
{
blocksToUpdate.addIfNotAlreadyThere (block);
blocksToAdd.removeObject (block);
}
else
{
blocksToAdd.addIfNotAlreadyThere (block);
blocksToUpdate.removeObject (block);
}
blocksToRemove.removeObject (block);
triggerAsyncUpdate();
}
void handleDeviceRemoved (const DeviceInfo& info)
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
const auto blockIt = std::find_if (currentTopology.blocks.begin(), currentTopology.blocks.end(),
[uid = info.uid] (Block::Ptr block) { return uid == block->uid; });
if (blockIt != currentTopology.blocks.end())
{
const Block::Ptr block { *blockIt };
if (auto blockImpl = BlockImpl::getFrom (block.get()))
blockImpl->markDisconnected();
currentTopology.blocks.removeObject (block);
previouslySeenBlocks.addIfNotAlreadyThere (block);
blocksToRemove.addIfNotAlreadyThere (block);
blocksToUpdate.removeObject (block);
blocksToAdd.removeObject (block);
triggerAsyncUpdate();
}
}
void handleConnectionsChanged()
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
triggerAsyncUpdate();
}
void handleDevicesUpdated (const Array<DeviceInfo>& infos)
{
bool shouldTriggerUpdate { false };
for (auto& info : infos)
{
if (containsBlockWithUID (blocksToRemove, info.uid))
continue;
const auto blockIt = std::find_if (currentTopology.blocks.begin(), currentTopology.blocks.end(),
[uid = info.uid] (Block::Ptr block) { return uid == block->uid; });
if (blockIt != currentTopology.blocks.end())
{
const Block::Ptr block { *blockIt };
if (auto blockImpl = BlockImpl::getFrom (block.get()))
blockImpl->updateDeviceInfo (info);
if (! containsBlockWithUID (blocksToAdd, info.uid))
{
blocksToUpdate.addIfNotAlreadyThere (block);
shouldTriggerUpdate = true;
}
}
}
if (shouldTriggerUpdate)
triggerAsyncUpdate();
}
void handleDeviceUpdated (const DeviceInfo& info)
{
handleDevicesUpdated ({ info });
}
void handleBatteryChargingChanged (Block::UID deviceID, const BlocksProtocol::BatteryCharging isCharging)
{
if (auto block = currentTopology.getBlockWithUID (deviceID))
if (auto blockImpl = BlockImpl::getFrom (*block))
blockImpl->batteryCharging = isCharging;
}
void handleBatteryLevelChanged (Block::UID deviceID, const BlocksProtocol::BatteryLevel batteryLevel)
{
if (auto block = currentTopology.getBlockWithUID (deviceID))
if (auto blockImpl = BlockImpl::getFrom (*block))
blockImpl->batteryLevel = batteryLevel;
}
void handleIndexChanged (Block::UID deviceID, const BlocksProtocol::TopologyIndex index)
{
if (auto block = currentTopology.getBlockWithUID (deviceID))
if (auto blockImpl = BlockImpl::getFrom (*block))
blockImpl->topologyIndex = index;
}
void notifyBlockIsRestarting (Block::UID deviceID)
{
for (auto& group : connectedDeviceGroups)
group->handleBlockRestarting (deviceID);
}
Array<Block::UID> getDnaDependentDeviceUIDs (Block::UID uid)
{
JUCE_ASSERT_MESSAGE_THREAD
Array<Block::UID> dependentDeviceUIDs;
if (auto block = getBlockImplementationWithUID (uid))
{
if (auto master = getBlockImplementationWithUID (block->masterUID))
{
auto graph = BlockGraph (currentTopology, [uid] (Block::Ptr b) { return b->uid != uid; });
const auto pathWithoutBlock = graph.getTraversalPathFromMaster (master);
for (const auto b : currentTopology.blocks)
{
if (b->uid != uid && ! pathWithoutBlock.contains (b))
{
TOPOLOGY_LOG ( "Dependent device: " + b->name);
dependentDeviceUIDs.add (b->uid);
}
}
}
}
return dependentDeviceUIDs;
}
void handleSharedDataACK (Block::UID deviceID, uint32 packetCounter) const
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
if (auto* bi = getBlockImplementationWithUID (deviceID))
bi->handleSharedDataACK (packetCounter);
}
void handleFirmwareUpdateACK (Block::UID deviceID, uint8 resultCode, uint32 resultDetail)
{
if (auto* bi = getBlockImplementationWithUID (deviceID))
bi->handleFirmwareUpdateACK (resultCode, resultDetail);
}
void handleConfigUpdateMessage (Block::UID deviceID, int32 item, int32 value, int32 min, int32 max)
{
if (auto* bi = getBlockImplementationWithUID (deviceID))
bi->handleConfigUpdateMessage (item, value, min, max);
}
void notifyBlockOfConfigChange (BlockImpl& bi, uint32 item)
{
if (item >= bi.getMaxConfigIndex())
bi.handleConfigItemChanged ({ item }, item);
else
bi.handleConfigItemChanged (bi.getLocalConfigMetaData (item), item);
}
void handleConfigSetMessage (Block::UID deviceID, int32 item, int32 value)
{
if (auto* bi = getBlockImplementationWithUID (deviceID))
{
bi->handleConfigSetMessage (item, value);
notifyBlockOfConfigChange (*bi, uint32 (item));
}
}
void handleConfigFactorySyncEndMessage (Block::UID deviceID)
{
if (auto* bi = getBlockImplementationWithUID (deviceID))
bi->handleConfigSyncEnded();
}
void handleConfigFactorySyncResetMessage (Block::UID deviceID)
{
if (auto* bi = getBlockImplementationWithUID (deviceID))
bi->resetConfigListActiveStatus();
}
void handleLogMessage (Block::UID deviceID, const String& message) const
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
if (auto* bi = getBlockImplementationWithUID (deviceID))
bi->handleLogMessage (message);
}
void handleButtonChange (Block::UID deviceID, Block::Timestamp timestamp, uint32 buttonIndex, bool isDown) const
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
if (auto* bi = getBlockImplementationWithUID (deviceID))
{
bi->pingFromDevice();
if (isPositiveAndBelow (buttonIndex, bi->getButtons().size()))
if (auto* cbi = dynamic_cast<BlockImpl::ControlButtonImplementation*> (bi->getButtons().getUnchecked (int (buttonIndex))))
cbi->broadcastButtonChange (timestamp, bi->modelData.buttons[(int) buttonIndex].type, isDown);
}
}
void handleTouchChange (Block::UID deviceID, const TouchSurface::Touch& touchEvent)
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
auto block = currentTopology.getBlockWithUID (deviceID);
if (block != nullptr)
{
if (auto* surface = dynamic_cast<BlockImpl::TouchSurfaceImplementation*> (block->getTouchSurface()))
{
TouchSurface::Touch scaledEvent (touchEvent);
scaledEvent.x *= (float) block->getWidth();
scaledEvent.y *= (float) block->getHeight();
scaledEvent.startX *= (float) block->getWidth();
scaledEvent.startY *= (float) block->getHeight();
surface->broadcastTouchChange (scaledEvent);
}
}
}
void cancelAllActiveTouches() noexcept
{
for (auto& block : currentTopology.blocks)
if (auto* surface = block->getTouchSurface())
surface->cancelAllActiveTouches();
}
void handleCustomMessage (Block::UID deviceID, Block::Timestamp timestamp, const int32* data)
{
if (auto* bi = getBlockImplementationWithUID (deviceID))
bi->handleCustomMessage (timestamp, data);
}
//==============================================================================
template <typename PacketBuilder>
bool sendMessageToDevice (Block::UID deviceID, const PacketBuilder& builder) const
{
for (auto* c : connectedDeviceGroups)
if (c->contains (deviceID))
return c->sendMessageToDevice (builder);
return false;
}
static Detector* getFrom (Block& b) noexcept
{
if (auto* bi = BlockImpl::getFrom (b))
return (bi->detector);
jassertfalse;
return nullptr;
}
PhysicalTopologySource::DeviceConnection* getDeviceConnectionFor (const Block& b)
{
for (const auto& d : connectedDeviceGroups)
{
if (d->contains (b.uid))
return d->getDeviceConnection();
}
return nullptr;
}
const PhysicalTopologySource::DeviceConnection* getDeviceConnectionFor (const Block& b) const
{
for (const auto& d : connectedDeviceGroups)
{
if (d->contains (b.uid))
return d->getDeviceConnection();
}
return nullptr;
}
std::unique_ptr<MIDIDeviceDetector> defaultDetector;
PhysicalTopologySource::DeviceDetector& deviceDetector;
Array<PhysicalTopologySource*> activeTopologySources;
BlockTopology currentTopology;
private:
Block::Array previouslySeenBlocks, blocksToAdd, blocksToRemove, blocksToUpdate;
void timerCallback() override
{
startTimer (1500);
auto detectedDevices = deviceDetector.scanForDevices();
handleDevicesRemoved (detectedDevices);
handleDevicesAdded (detectedDevices);
}
bool containsBlockWithUID (const Block::Array& blocks, Block::UID uid)
{
for (const auto block : blocks)
if (block->uid == uid)
return true;
return false;
}
void handleDevicesRemoved (const StringArray& detectedDevices)
{
for (int i = connectedDeviceGroups.size(); --i >= 0;)
if (! connectedDeviceGroups.getUnchecked(i)->isStillConnected (detectedDevices))
connectedDeviceGroups.remove (i);
}
void handleDevicesAdded (const StringArray& detectedDevices)
{
for (const auto& devName : detectedDevices)
{
if (! hasDeviceFor (devName))
{
if (auto d = deviceDetector.openDevice (detectedDevices.indexOf (devName)))
{
connectedDeviceGroups.add (new ConnectedDeviceGroup<Detector> (*this, devName, d));
}
}
}
}
bool hasDeviceFor (const String& devName) const
{
for (auto d : connectedDeviceGroups)
if (d->deviceName == devName)
return true;
return false;
}
BlockImpl* getBlockImplementationWithUID (Block::UID deviceID) const noexcept
{
if (auto block = currentTopology.getBlockWithUID (deviceID))
return BlockImpl::getFrom (*block);
return nullptr;
}
OwnedArray<ConnectedDeviceGroup<Detector>> connectedDeviceGroups;
//==============================================================================
/** This is a friend of the BlocksImplementation that will scan and set the
physical positions of the blocks.
Returns an array of blocks that were updated.
*/
struct BlocksLayoutTraverser
{
static Block::Array updateBlocks (const BlockTopology& topology)
{
Block::Array updated;
Array<Block::UID> visited;
for (auto& block : topology.blocks)
{
if (block->isMasterBlock() && ! visited.contains (block->uid))
{
if (auto* bi = BlockImpl::getFrom (block))
{
if (bi->rotation != 0 || bi->position.first != 0 || bi->position.second != 0)
{
bi->rotation = 0;
bi->position = {};
updated.add (block);
}
}
layoutNeighbours (*block, topology, visited, updated);
}
}
return updated;
}
private:
// returns the distance from corner clockwise
static int getUnitForIndex (Block::Ptr block, Block::ConnectionPort::DeviceEdge edge, int index)
{
if (block->getType() == Block::seaboardBlock)
{
if (edge == Block::ConnectionPort::DeviceEdge::north)
{
if (index == 0) return 1;
if (index == 1) return 4;
}
else if (edge != Block::ConnectionPort::DeviceEdge::south)
{
return 1;
}
}
else if (block->getType() == Block::lumiKeysBlock)
{
if (edge == Block::ConnectionPort::DeviceEdge::north)
{
switch (index)
{
case 0 : return 0;
case 1 : return 2;
case 2 : return 3;
case 3 : return 5;
default : jassertfalse;
}
}
else if (edge == Block::ConnectionPort::DeviceEdge::south)
{
jassertfalse;
}
}
if (edge == Block::ConnectionPort::DeviceEdge::south)
return block->getWidth() - (index + 1);
if (edge == Block::ConnectionPort::DeviceEdge::west)
return block->getHeight() - (index + 1);
return index;
}
// returns how often north needs to rotate by 90 degrees
static int getRotationForEdge (Block::ConnectionPort::DeviceEdge edge)
{
switch (edge)
{
case Block::ConnectionPort::DeviceEdge::north: return 0;
case Block::ConnectionPort::DeviceEdge::east: return 1;
case Block::ConnectionPort::DeviceEdge::south: return 2;
case Block::ConnectionPort::DeviceEdge::west: return 3;
default: break;
}
jassertfalse;
return 0;
}
static void layoutNeighbours (const Block::Ptr block,
const BlockTopology& topology,
Array<Block::UID>& visited,
Block::Array& updated)
{
visited.add (block->uid);
for (auto& connection : topology.connections)
{
if ((connection.device1 == block->uid && ! visited.contains (connection.device2))
|| (connection.device2 == block->uid && ! visited.contains (connection.device1)))
{
const auto theirUid = connection.device1 == block->uid ? connection.device2 : connection.device1;
const auto neighbourPtr = topology.getBlockWithUID (theirUid);
if (auto* neighbour = dynamic_cast<BlockImpl*> (neighbourPtr.get()))
{
const auto myBounds = block->getBlockAreaWithinLayout();
const auto& myPort = connection.device1 == block->uid ? connection.connectionPortOnDevice1 : connection.connectionPortOnDevice2;
const auto& theirPort = connection.device1 == block->uid ? connection.connectionPortOnDevice2 : connection.connectionPortOnDevice1;
const auto myOffset = getUnitForIndex (block, myPort.edge, myPort.index);
const auto theirOffset = getUnitForIndex (neighbourPtr, theirPort.edge, theirPort.index);
{
const auto neighbourRotation = (2 + block->getRotation()
+ getRotationForEdge (myPort.edge)
- getRotationForEdge (theirPort.edge)) % 4;
if (neighbour->rotation != neighbourRotation)
{
neighbour->rotation = neighbourRotation;
updated.addIfNotAlreadyThere (neighbourPtr);
}
}
std::pair<int, int> delta;
const auto theirBounds = neighbour->getBlockAreaWithinLayout();
switch ((block->getRotation() + getRotationForEdge (myPort.edge)) % 4)
{
case 0: // over me
delta = { myOffset - (theirBounds.width - (theirOffset + 1)), -theirBounds.height };
break;
case 1: // right of me
delta = { myBounds.width, myOffset - (theirBounds.height - (theirOffset + 1)) };
break;
case 2: // under me
delta = { (myBounds.width - (myOffset + 1)) - theirOffset, myBounds.height };
break;
case 3: // left of me
delta = { -theirBounds.width, (myBounds.height - (myOffset + 1)) - theirOffset };
break;
default:
break;
}
{
const auto neighbourX = myBounds.x + delta.first;
const auto neighbourY = myBounds.y + delta.second;
if (neighbour->position.first != neighbourX
|| neighbour->position.second != neighbourY)
{
neighbour->position.first = neighbourX;
neighbour->position.second = neighbourY;
updated.addIfNotAlreadyThere (neighbourPtr);
}
}
layoutNeighbours (neighbourPtr, topology, visited, updated);
}
}
}
}
};
//==============================================================================
#if DUMP_TOPOLOGY
static String idToSerialNum (const BlockTopology& topology, Block::UID uid)
{
for (auto* b : topology.blocks)
if (b->uid == uid)
return b->serialNumber;
return "???";
}
static String portEdgeToString (Block::ConnectionPort port)
{
switch (port.edge)
{
case Block::ConnectionPort::DeviceEdge::north: return "north";
case Block::ConnectionPort::DeviceEdge::south: return "south";
case Block::ConnectionPort::DeviceEdge::east: return "east";
case Block::ConnectionPort::DeviceEdge::west: return "west";
default: break;
}
return {};
}
static String portToString (Block::ConnectionPort port)
{
return portEdgeToString (port) + "_" + String (port.index);
}
static void dumpTopology (const BlockTopology& topology)
{
MemoryOutputStream m;
m << "=============================================================================" << newLine
<< "Topology: " << topology.blocks.size() << " device(s)" << newLine
<< newLine;
int index = 0;
for (auto block : topology.blocks)
{
m << "Device " << index++ << (block->isMasterBlock() ? ": (MASTER)" : ":") << newLine;
m << " Description: " << block->getDeviceDescription() << newLine
<< " Serial: " << block->serialNumber << newLine;
if (auto bi = BlockImplementation<Detector>::getFrom (*block))
m << " Short address: " << (int) bi->getDeviceIndex() << newLine;
m << " Battery level: " + String (roundToInt (100.0f * block->getBatteryLevel())) + "%" << newLine
<< " Battery charging: " + String (block->isBatteryCharging() ? "y" : "n") << newLine
<< " Width: " << block->getWidth() << newLine
<< " Height: " << block->getHeight() << newLine
<< " Millimeters per unit: " << block->getMillimetersPerUnit() << newLine
<< newLine;
}
for (auto& connection : topology.connections)
{
m << idToSerialNum (topology, connection.device1)
<< ":" << portToString (connection.connectionPortOnDevice1)
<< " <-> "
<< idToSerialNum (topology, connection.device2)
<< ":" << portToString (connection.connectionPortOnDevice2) << newLine;
}
m << "=============================================================================" << newLine;
Logger::outputDebugString (m.toString());
}
#endif
//==============================================================================
void updateBlockPositions()
{
const auto updated = BlocksLayoutTraverser::updateBlocks (currentTopology);
for (const auto block : updated)
{
if (containsBlockWithUID (blocksToAdd, block->uid) || containsBlockWithUID (blocksToRemove, block->uid))
continue;
blocksToUpdate.addIfNotAlreadyThere (block);
}
}
void updateBlockConnections()
{
currentTopology.connections.clearQuick();
for (auto d : connectedDeviceGroups)
currentTopology.connections.addArray (d->getCurrentDeviceConnections());
}
void handleAsyncUpdate() override
{
updateBlockConnections();
updateBlockPositions();
for (auto* d : activeTopologySources)
{
for (const auto block : blocksToAdd)
d->listeners.call ([&block] (TopologySource::Listener& l) { l.blockAdded (block); });
for (const auto block : blocksToRemove)
d->listeners.call ([&block] (TopologySource::Listener& l) { l.blockRemoved (block); });
for (const auto block : blocksToUpdate)
d->listeners.call ([&block] (TopologySource::Listener& l) { l.blockUpdated (block); });
}
const auto topologyChanged = blocksToAdd.size() > 0 || blocksToRemove.size() > 0 || blocksToUpdate.size() > 0;
if (topologyChanged)
{
#if DUMP_TOPOLOGY
dumpTopology (currentTopology);
#endif
for (auto* d : activeTopologySources)
d->listeners.call ([] (TopologySource::Listener& l) { l.topologyChanged(); });
}
blocksToUpdate.clear();
blocksToAdd.clear();
blocksToRemove.clear();
static const int maxBlocksToSave = 100;
if (previouslySeenBlocks.size() > maxBlocksToSave)
previouslySeenBlocks.removeRange (0, 2 * (previouslySeenBlocks.size() - maxBlocksToSave));
}
//==============================================================================
JUCE_DECLARE_WEAK_REFERENCEABLE (Detector)
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Detector)
};
} // namespace juce

+ 0
- 70
modules/juce_blocks_basics/topology/internal/juce_DetectorHolder.cpp View File

@@ -1,70 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
struct PhysicalTopologySource::DetectorHolder : private Timer
{
DetectorHolder (PhysicalTopologySource& pts)
: topologySource (pts),
detector (Detector::getDefaultDetector())
{
startTimerHz (30);
}
DetectorHolder (PhysicalTopologySource& pts, DeviceDetector& dd)
: topologySource (pts),
detector (new Detector (dd))
{
startTimerHz (30);
}
void timerCallback() override
{
if (! topologySource.hasOwnServiceTimer())
handleTimerTick();
}
void handleTimerTick()
{
auto blocks = detector->currentTopology.blocks;
if (blocks.size() == 0)
return;
if (nextIndexToTick >= blocks.size())
nextIndexToTick = 0;
if (auto* bi = BlockImplementation<Detector>::getFrom (*blocks [nextIndexToTick]))
bi->handleTimerTick();
nextIndexToTick = (nextIndexToTick + 1) % blocks.size();
}
PhysicalTopologySource& topologySource;
Detector::Ptr detector;
int nextIndexToTick {0};
};
} // namespace juce

+ 0
- 50
modules/juce_blocks_basics/topology/internal/juce_DeviceInfo.cpp View File

@@ -1,50 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
struct DeviceInfo
{
// VS2015 requires a constructor to avoid aggregate initialization
DeviceInfo (Block::UID buid, BlocksProtocol::TopologyIndex tidx,
BlocksProtocol::BlockSerialNumber s, BlocksProtocol::VersionNumber v,
BlocksProtocol::BlockName n, BlocksProtocol::BatteryLevel level,
BlocksProtocol::BatteryCharging charging, Block::UID master)
: uid (buid), index (tidx), serial (s), version (v), name (n),
batteryLevel (level), batteryCharging (charging), masterUid (master),
isMaster (uid == master)
{
}
Block::UID uid {};
BlocksProtocol::TopologyIndex index;
BlocksProtocol::BlockSerialNumber serial;
BlocksProtocol::VersionNumber version;
BlocksProtocol::BlockName name;
BlocksProtocol::BatteryLevel batteryLevel;
BlocksProtocol::BatteryCharging batteryCharging;
Block::UID masterUid;
bool isMaster {};
};
} // namespace juce

+ 0
- 183
modules/juce_blocks_basics/topology/internal/juce_MIDIDeviceDetector.cpp View File

@@ -1,183 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
struct MIDIDeviceDetector : public PhysicalTopologySource::DeviceDetector
{
MIDIDeviceDetector() {}
StringArray scanForDevices() override
{
StringArray result;
for (auto& pair : findDevices())
result.add (pair.input.identifier + " & " + pair.output.identifier);
return result;
}
PhysicalTopologySource::DeviceConnection* openDevice (int index) override
{
const auto allDevices = findDevices();
if (allDevices.size() > index)
{
const auto pair = allDevices[index];
auto dev = std::make_unique<MIDIDeviceConnection>();
if (auto lock = createMidiPortLock (pair.input.name, pair.output.name))
{
lockedFromOutside = false;
dev->setLockAgainstOtherProcesses (lock);
dev->midiInput = MidiInput::openDevice (pair.input.identifier, dev.get());
dev->midiOutput = MidiOutput::openDevice (pair.output.identifier);
if (dev->midiInput != nullptr)
{
dev->midiInput->start();
return dev.release();
}
}
else
{
lockedFromOutside = true;
}
}
return nullptr;
}
bool isLockedFromOutside() const override
{
return lockedFromOutside && ! findDevices().isEmpty();
}
static bool isBlocksMidiDeviceName (const String& name)
{
return name.indexOf (" BLOCK") > 0 || name.indexOf (" Block") > 0;
}
static String cleanBlocksDeviceName (String name)
{
name = name.trim();
if (name.endsWith (" IN)"))
return name.dropLastCharacters (4);
if (name.endsWith (" OUT)"))
return name.dropLastCharacters (5);
const int openBracketPosition = name.lastIndexOfChar ('[');
if (openBracketPosition != -1 && name.endsWith ("]"))
return name.dropLastCharacters (name.length() - openBracketPosition);
return name;
}
struct MidiInputOutputPair
{
MidiDeviceInfo input, output;
};
static Array<MidiInputOutputPair> findDevices()
{
Array<MidiInputOutputPair> result;
auto midiInputs = MidiInput::getAvailableDevices();
auto midiOutputs = MidiOutput::getAvailableDevices();
for (const auto& input : midiInputs)
{
if (isBlocksMidiDeviceName (input.name))
{
MidiInputOutputPair pair;
pair.input = input;
String cleanedInputName = cleanBlocksDeviceName (input.name);
int inputOccurences = 0;
int outputOccurences = 0;
for (const auto& p : result)
if (cleanBlocksDeviceName (p.input.name) == cleanedInputName)
++inputOccurences;
for (const auto& output : midiOutputs)
{
if (cleanBlocksDeviceName (output.name) == cleanedInputName)
{
if (outputOccurences == inputOccurences)
{
pair.output = output;
break;
}
++outputOccurences;
}
}
result.add (pair);
}
}
return result;
}
private:
bool lockedFromOutside = true;
/** For backwards compatibility, the block interprocess lock has to use the midi input name.
The below is necessary because blocks of the same type might duplicate a port name, so
must share an interprocess lock.
*/
std::shared_ptr<InterProcessLock> createMidiPortLock (const String& midiInName, const String& midiOutName)
{
const juce::String lockIdentifier = "blocks_sdk_"
+ File::createLegalFileName (midiInName)
+ "_" + File::createLegalFileName (midiOutName);
const auto existingLock = midiPortLocks.find (lockIdentifier);
if (existingLock != midiPortLocks.end())
if (existingLock->second.use_count() > 0)
return existingLock->second.lock();
auto interprocessLock = std::make_shared<InterProcessLock> (lockIdentifier);
if (interprocessLock->enter (500))
{
midiPortLocks[lockIdentifier] = interprocessLock;
return interprocessLock;
}
return nullptr;
}
std::map<juce::String, std::weak_ptr<InterProcessLock>> midiPortLocks;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MIDIDeviceDetector)
};
} // namespace juce

+ 0
- 113
modules/juce_blocks_basics/topology/internal/juce_MidiDeviceConnection.cpp View File

@@ -1,113 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
struct MIDIDeviceConnection : public PhysicalTopologySource::DeviceConnection,
public MidiInputCallback
{
MIDIDeviceConnection() {}
~MIDIDeviceConnection() override
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
listeners.call ([this] (Listener& l) { l.connectionBeingDeleted (*this); });
if (midiInput != nullptr)
midiInput->stop();
}
void setLockAgainstOtherProcesses (std::shared_ptr<InterProcessLock> newLock)
{
midiPortLock = newLock;
}
struct Listener
{
virtual ~Listener() {}
virtual void handleIncomingMidiMessage (const MidiMessage& message) = 0;
virtual void connectionBeingDeleted (const MIDIDeviceConnection&) = 0;
};
void addListener (Listener* l)
{
ScopedLock scopedLock (criticalSecton);
listeners.add (l);
}
void removeListener (Listener* l)
{
ScopedLock scopedLock (criticalSecton);
listeners.remove (l);
}
bool sendMessageToDevice (const void* data, size_t dataSize) override
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED // This method must only be called from the message thread!
jassert (dataSize > sizeof (BlocksProtocol::roliSysexHeader) + 1);
jassert (memcmp (data, BlocksProtocol::roliSysexHeader, sizeof (BlocksProtocol::roliSysexHeader) - 1) == 0);
jassert (static_cast<const uint8*> (data)[dataSize - 1] == 0xf7);
if (midiOutput != nullptr)
{
midiOutput->sendMessageNow (MidiMessage (data, (int) dataSize));
return true;
}
return false;
}
void handleIncomingMidiMessage (MidiInput*, const MidiMessage& message) override
{
ScopedTryLock lock (criticalSecton);
if (lock.isLocked())
{
const auto data = message.getRawData();
const int dataSize = message.getRawDataSize();
const int bodySize = dataSize - (int) (sizeof (BlocksProtocol::roliSysexHeader) + 1);
if (bodySize > 0 && memcmp (data, BlocksProtocol::roliSysexHeader, sizeof (BlocksProtocol::roliSysexHeader)) == 0)
if (handleMessageFromDevice != nullptr)
handleMessageFromDevice (data + sizeof (BlocksProtocol::roliSysexHeader), (size_t) bodySize);
listeners.call ([&] (Listener& l) { l.handleIncomingMidiMessage (message); });
}
}
std::unique_ptr<MidiInput> midiInput;
std::unique_ptr<MidiOutput> midiOutput;
CriticalSection criticalSecton;
private:
ListenerList<Listener> listeners;
std::shared_ptr<InterProcessLock> midiPortLock;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MIDIDeviceConnection)
};
} // namespace juce

+ 0
- 112
modules/juce_blocks_basics/topology/juce_BlockGraph.cpp View File

@@ -1,112 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
BlockGraph::BlockGraph (const BlockTopology t, std::function<bool (Block::Ptr)> filterIn)
: topology (t), filter (std::move (filterIn))
{
buildGraph();
}
BlockGraph::BlockGraph (BlockGraph&& other)
{
traversalPaths = std::move (other.traversalPaths);
topology = std::move (other.topology);
filter = std::move (other.filter);
}
BlockGraph::BlockTraversalPaths BlockGraph::getTraversalPaths() const
{
return traversalPaths;
}
Block::Array BlockGraph::getTraversalPathFromMaster (Block::Ptr masterBlock) const
{
for (const auto& path : traversalPaths)
{
if (! path.isEmpty() && path[0] == masterBlock)
return path;
}
return {};
}
String BlockGraph::asString() const
{
String outputString = "Traversal Path(s):";
for (const auto& path : traversalPaths)
{
outputString += "\n[master]-->";
for (auto& block : path)
outputString += block->serialNumber + "-->";
outputString += "[last]";
}
return outputString;
}
void BlockGraph::buildGraph()
{
traversalPaths.clear();
for (const auto block : topology.blocks)
{
if (block->isMasterBlock() && shouldIncludeBlock (block))
traversalPaths.add (buildPathFromMaster (block));
}
}
bool BlockGraph::shouldIncludeBlock (Block::Ptr block) const
{
if (filter == nullptr)
return true;
return filter (block);
}
Block::Array BlockGraph::buildPathFromMaster (Block::Ptr masterBlock)
{
jassert (masterBlock->isMasterBlock());
Block::Array orderedBlockList;
addAllConnectedToArray (masterBlock, orderedBlockList);
return orderedBlockList;
}
void BlockGraph::addAllConnectedToArray (Block::Ptr startBlock, Block::Array& store)
{
store.addIfNotAlreadyThere (startBlock);
for (const auto block : topology.getDirectlyConnectedBlocks (startBlock->uid))
{
if (shouldIncludeBlock (block) && store.addIfNotAlreadyThere (block))
addAllConnectedToArray (block, store);
}
}
} // namespace juce

+ 0
- 66
modules/juce_blocks_basics/topology/juce_BlockGraph.h View File

@@ -1,66 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
namespace juce
{
/**
Represents traversal paths from master blocks and any connected blocks.
@tags{Blocks}
*/
class BlockGraph
{
public:
/** Creates a BlockGraph object from a BlockTopology with an optional filter function. This
will build a block graph of traversal paths for each master.
*/
BlockGraph (const BlockTopology topology, std::function<bool (Block::Ptr block)> filter = nullptr);
BlockGraph (BlockGraph&&);
using BlockTraversalPaths = Array<Block::Array, CriticalSection>;
/** Get traversal paths for each master block in a topology */
BlockTraversalPaths getTraversalPaths() const;
/** Get traversal path for a specific master block in a topology */
Block::Array getTraversalPathFromMaster (Block::Ptr masterBlock) const;
/** Gets a string representation of all traversal paths */
String asString() const;
private:
void buildGraph();
Block::Array buildPathFromMaster (Block::Ptr masterBlock);
void addAllConnectedToArray (Block::Ptr startBlock, Block::Array& store);
bool shouldIncludeBlock (Block::Ptr block) const;
BlockTraversalPaths traversalPaths; // one path for each master block
BlockTopology topology;
std::function<bool (Block::Ptr)> filter;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockGraph)
};
} // namespace juce

+ 0
- 204
modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.cpp View File

@@ -1,204 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
//==============================================================================
/** These can be useful when debugging the topology. */
#ifndef LOG_BLOCKS_CONNECTIVITY
#define LOG_BLOCKS_CONNECTIVITY 0
#endif
#ifndef LOG_BLOCKS_PINGS
#define LOG_BLOCKS_PINGS 0
#endif
#ifndef DUMP_BANDWIDTH_STATS
#define DUMP_BANDWIDTH_STATS 0
#endif
#ifndef DUMP_TOPOLOGY
#define DUMP_TOPOLOGY 0
#endif
#define TOPOLOGY_LOG(text) \
JUCE_BLOCK_WITH_FORCED_SEMICOLON (juce::String buf ("Topology Src: "); \
juce::Logger::outputDebugString (buf << text);)
#if LOG_BLOCKS_CONNECTIVITY
#define LOG_CONNECTIVITY(text) TOPOLOGY_LOG(text)
#else
#define LOG_CONNECTIVITY(text)
#endif
#if LOG_BLOCKS_PINGS
#define LOG_PING(text) TOPOLOGY_LOG(text)
#else
#define LOG_PING(text)
#endif
#if DUMP_BANDWIDTH_STATS
#include "internal/juce_BandwidthStatsLogger.cpp"
#endif
#include "internal/juce_MidiDeviceConnection.cpp"
#include "internal/juce_MIDIDeviceDetector.cpp"
#include "internal/juce_DeviceInfo.cpp"
#include "internal/juce_DepreciatedVersionReader.cpp"
#include "internal/juce_BlockSerialReader.cpp"
#include "internal/juce_ConnectedDeviceGroup.cpp"
#include "internal/juce_BlockImplementation.cpp"
#include "internal/juce_Detector.cpp"
#include "internal/juce_DetectorHolder.cpp"
namespace juce
{
//==============================================================================
PhysicalTopologySource::PhysicalTopologySource (bool startDetached)
{
if (! startDetached)
setActive (true);
}
PhysicalTopologySource::PhysicalTopologySource (DeviceDetector& detectorToUse, bool startDetached)
: customDetector (&detectorToUse)
{
if (! startDetached)
setActive (true);
}
PhysicalTopologySource::~PhysicalTopologySource()
{
setActive (false);
}
void PhysicalTopologySource::setActive (bool shouldBeActive)
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
if (isActive() == shouldBeActive)
return;
if (shouldBeActive)
{
if (customDetector == nullptr)
detector = std::make_unique<DetectorHolder>(*this);
else
detector = std::make_unique<DetectorHolder>(*this, *customDetector);
detector->detector->activeTopologySources.add (this);
}
else
{
detector->detector->detach (this);
detector.reset();
}
listeners.call ([] (TopologySource::Listener& l){ l.topologyChanged(); });
}
bool PhysicalTopologySource::isActive() const
{
return detector != nullptr;
}
bool PhysicalTopologySource::isLockedFromOutside() const
{
if (detector != nullptr && detector->detector != nullptr)
return detector->detector->deviceDetector.isLockedFromOutside();
return false;
}
BlockTopology PhysicalTopologySource::getCurrentTopology() const
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED // This method must only be called from the message thread!
if (detector != nullptr)
return detector->detector->currentTopology;
return {};
}
void PhysicalTopologySource::cancelAllActiveTouches() noexcept
{
if (detector != nullptr)
detector->detector->cancelAllActiveTouches();
}
bool PhysicalTopologySource::hasOwnServiceTimer() const { return false; }
void PhysicalTopologySource::handleTimerTick()
{
if (detector != nullptr)
detector->handleTimerTick();
}
PhysicalTopologySource::DeviceConnection::DeviceConnection() {}
PhysicalTopologySource::DeviceConnection::~DeviceConnection() {}
PhysicalTopologySource::DeviceDetector::DeviceDetector() {}
PhysicalTopologySource::DeviceDetector::~DeviceDetector() {}
const char* const* PhysicalTopologySource::getStandardLittleFootFunctions() noexcept
{
return BlocksProtocol::ledProgramLittleFootFunctions;
}
template <typename ListType>
static bool collectionsMatch (const ListType& list1, const ListType& list2) noexcept
{
if (list1.size() != list2.size())
return false;
for (auto&& b : list1)
if (! list2.contains (b))
return false;
return true;
}
bool BlockTopology::operator== (const BlockTopology& other) const noexcept
{
return collectionsMatch (connections, other.connections) && collectionsMatch (blocks, other.blocks);
}
bool BlockTopology::operator!= (const BlockTopology& other) const noexcept
{
return ! operator== (other);
}
bool BlockDeviceConnection::operator== (const BlockDeviceConnection& other) const noexcept
{
return (device1 == other.device1 && device2 == other.device2
&& connectionPortOnDevice1 == other.connectionPortOnDevice1
&& connectionPortOnDevice2 == other.connectionPortOnDevice2)
|| (device1 == other.device2 && device2 == other.device1
&& connectionPortOnDevice1 == other.connectionPortOnDevice2
&& connectionPortOnDevice2 == other.connectionPortOnDevice1);
}
bool BlockDeviceConnection::operator!= (const BlockDeviceConnection& other) const noexcept
{
return ! operator== (other);
}
} // namespace juce

+ 0
- 98
modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.h View File

@@ -1,98 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
This topology source manages the topology of the physical Blocks devices
that are currently connected. It maintains a list of them and tells
listeners when physical devices are added or removed.
@tags{Blocks}
*/
class PhysicalTopologySource : public TopologySource
{
public:
/** Constructor. */
PhysicalTopologySource (bool startDetached = false);
/** Destructor. */
~PhysicalTopologySource() override;
/** Returns the current physical topology. */
BlockTopology getCurrentTopology() const override;
/** Reset all touches */
void cancelAllActiveTouches() noexcept override;
/** Sets the TopologySource as active, occupying the midi port and trying to connect to the block devices */
void setActive (bool shouldBeActive) override;
/** Returns true, if the TopologySource is currently trying to connect the block devices */
bool isActive() const override;
/** This method will tell, if an other PhysicalTopologySource has locked the Midi connection */
bool isLockedFromOutside() const override;
//==============================================================================
/** For custom transport systems, this represents a connected device */
struct DeviceConnection
{
DeviceConnection();
virtual ~DeviceConnection();
virtual bool sendMessageToDevice (const void* data, size_t dataSize) = 0;
std::function<void (const void* data, size_t dataSize)> handleMessageFromDevice;
};
/** For custom transport systems, this represents a connected device */
struct DeviceDetector
{
DeviceDetector();
virtual ~DeviceDetector();
virtual StringArray scanForDevices() = 0;
virtual DeviceConnection* openDevice (int index) = 0;
virtual bool isLockedFromOutside() const { return false; }
};
/** Constructor for custom transport systems. */
PhysicalTopologySource (DeviceDetector& detectorToUse, bool startDetached = false);
static const char* const* getStandardLittleFootFunctions() noexcept;
protected:
virtual bool hasOwnServiceTimer() const;
virtual void handleTimerTick();
private:
//==============================================================================
DeviceDetector* customDetector = nullptr;
friend struct Detector;
struct DetectorHolder;
std::unique_ptr<DetectorHolder> detector;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PhysicalTopologySource)
};
} // namespace juce

+ 0
- 126
modules/juce_blocks_basics/topology/juce_RuleBasedTopologySource.cpp View File

@@ -1,126 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
struct RuleBasedTopologySource::Internal : public TopologySource::Listener,
private AsyncUpdater
{
Internal (RuleBasedTopologySource& da, TopologySource& bd) : owner (da), detector (bd)
{
detector.addListener (this);
}
~Internal() override
{
detector.removeListener (this);
}
void clearRules()
{
if (! rules.isEmpty())
{
rules.clear();
triggerAsyncUpdate();
}
}
void addRule (Rule* r)
{
if (r != nullptr)
{
rules.add (r);
triggerAsyncUpdate();
}
}
void topologyChanged() override
{
cancelPendingUpdate();
regenerateTopology();
}
void handleAsyncUpdate() override
{
topologyChanged();
}
void regenerateTopology()
{
auto newTopology = detector.getCurrentTopology();
for (auto rule : rules)
rule->transformTopology (newTopology);
if (topology != newTopology)
{
topology = newTopology;
owner.listeners.call ([] (TopologySource::Listener& l) { l.topologyChanged(); });
}
}
void setActive (bool shouldBeActive)
{
detector.setActive (shouldBeActive);
}
bool isActive() const
{
return detector.isActive();
}
RuleBasedTopologySource& owner;
TopologySource& detector;
BlockTopology topology;
OwnedArray<Rule> rules;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Internal)
};
RuleBasedTopologySource::RuleBasedTopologySource (TopologySource& d)
{
internal.reset (new Internal (*this, d));
}
RuleBasedTopologySource::~RuleBasedTopologySource()
{
internal = nullptr;
}
BlockTopology RuleBasedTopologySource::getCurrentTopology() const { return internal->topology; }
void RuleBasedTopologySource::clearRules() { internal->clearRules(); }
void RuleBasedTopologySource::addRule (Rule* r) { internal->addRule (r); }
void RuleBasedTopologySource::setActive (bool shouldBeActive)
{
internal->setActive (shouldBeActive);
}
bool RuleBasedTopologySource::isActive() const
{
return internal->isActive();
}
} // namespace juce

+ 0
- 90
modules/juce_blocks_basics/topology/juce_RuleBasedTopologySource.h View File

@@ -1,90 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/** This topology source holds and applies a set of rules for transforming
one device topology into another one that may involve virtual and/or
aggregate devices.
Given an input PhysicalTopologySource and a set of Rule objects, this class
will apply the rules and present the resulting topology to clients.
@tags{Blocks}
*/
class RuleBasedTopologySource : public TopologySource
{
public:
/** Creates a RuleBasedTopologySource which wraps another TopologySource
passed in here.
*/
RuleBasedTopologySource (TopologySource&);
/** Destructor. */
~RuleBasedTopologySource() override;
//==============================================================================
/** Returns the currently active topology. */
BlockTopology getCurrentTopology() const override;
/** A rule that can transform parts of a topology. */
struct Rule
{
virtual ~Rule() = default;
/** Subclasses should implement this method and use it as their opportunity to
examine the given topology and modify it. For example they may want to substitute
one or more blocks for more specialised, aggregated Block objects.
*/
virtual void transformTopology (BlockTopology&) = 0;
};
/** Clears the list of active rules.
Calling this method will cause an asynchronous topology update if the new rule-set
results in a change to the topology.
*/
void clearRules();
/** Adds a rule to the list that will be applied.
The object passed-in will be owned by this object, so don't keep any references
to it.
Calling this method will cause an asynchronous topology update if the new rule-set
results in a change to the topology.
*/
void addRule (Rule*);
/** Sets the TopologySource as active, occupying the midi port and trying to connect to the block devices */
void setActive (bool shouldBeActive) override;
/** Returns true, if the TopologySource is currently trying to connect the block devices */
bool isActive() const override;
bool isLockedFromOutside() const override { return false; }
private:
//==============================================================================
struct Internal;
std::unique_ptr<Internal> internal;
};
} // namespace juce

+ 0
- 120
modules/juce_blocks_basics/topology/juce_Topology.h View File

@@ -1,120 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/** Describes a physical connection between two ports of two block devices.
@tags{Blocks}
*/
struct BlockDeviceConnection
{
Block::UID device1, device2;
Block::ConnectionPort connectionPortOnDevice1, connectionPortOnDevice2;
bool operator== (const BlockDeviceConnection&) const noexcept;
bool operator!= (const BlockDeviceConnection&) const noexcept;
};
/** Describes a set of blocks and the connections between them.
@tags{Blocks}
*/
struct BlockTopology
{
Block::Array blocks;
Array<BlockDeviceConnection> connections;
bool operator== (const BlockTopology&) const noexcept;
bool operator!= (const BlockTopology&) const noexcept;
Block::Ptr getBlockWithUID (Block::UID deviceID)
{
for (auto&& block : blocks)
if (block->uid == deviceID)
return block;
return {};
}
const Block::Ptr getBlockWithUID (Block::UID deviceID) const
{
for (auto&& block : blocks)
if (block->uid == deviceID)
return block;
return {};
}
Block::Array getDirectlyConnectedBlocks (Block::UID blockUID) const
{
Block::Array connectedBlocks;
for (const auto& connection : connections)
{
auto connectedDeviceUID = Block::UID { 0 };
if (connection.device1 == blockUID)
connectedDeviceUID = connection.device2;
else if (connection.device2 == blockUID)
connectedDeviceUID = connection.device1;
if (connectedDeviceUID != Block::UID { 0 })
if (auto newBlock = getBlockWithUID (connectedDeviceUID))
connectedBlocks.addIfNotAlreadyThere (newBlock);
}
return connectedBlocks;
}
Array<BlockDeviceConnection> getConnectionsBetweenBlocks (Block::UID uid1, Block::UID uid2) const
{
Array<BlockDeviceConnection> blockConnections;
for (const auto& connection : connections)
{
if ((connection.device1 == uid1 && connection.device2 == uid2)
|| (connection.device1 == uid2 && connection.device2 == uid1))
{
blockConnections.add (connection);
}
}
return blockConnections;
}
int getNumberOfConnectionsToBlock (Block::UID uid) const
{
int connectionCount = 0;
for (const auto& connection : connections)
if (connection.device1 == uid || connection.device2 == uid)
++connectionCount;
return connectionCount;
}
};
} // namespace juce

+ 0
- 87
modules/juce_blocks_basics/topology/juce_TopologySource.h View File

@@ -1,87 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/** Base class for an entity that provides access to a blocks topology.
@tags{Blocks}
*/
class TopologySource
{
public:
//==============================================================================
/** Destructor. */
virtual ~TopologySource() = default;
/** Returns the current topology that this object manages. */
virtual BlockTopology getCurrentTopology() const = 0;
/** Sets the TopologySource as active, occupying the midi port and trying to connect to the block devices */
virtual void setActive (bool shouldBeActive) = 0;
/** Returns true, if the TopologySource is currently trying to connect the block devices */
virtual bool isActive() const = 0;
/** Returns true if the topology is locked externally.*/
virtual bool isLockedFromOutside() const = 0;
//==============================================================================
/** Used to receive callbacks for topology changes */
struct Listener
{
virtual ~Listener() = default;
/** Called for any change in topology - devices changed, connections changed, etc. */
virtual void topologyChanged() {}
/** Called when a new block is added to the topology. */
virtual void blockAdded (const Block::Ptr) {}
/** Called when a block is removed from the topology. */
virtual void blockRemoved (const Block::Ptr) {}
/** Called when a known block is updated.
This could be because details have been received asynchronously. E.g. Block name.
*/
virtual void blockUpdated (const Block::Ptr) {}
};
void addListener (Listener* l) { listeners.add (l); }
void removeListener (Listener* l) { listeners.remove (l); }
/** Invoke this to force touches-off on all physical devices. */
virtual void cancelAllActiveTouches() noexcept {}
/** Gets blocks from the current topology. */
Block::Array getBlocks() const { return getCurrentTopology().blocks; }
/**Gets a block with given uid from the current topology*/
Block::Ptr getBlockWithUID (Block::UID uid) const { return getCurrentTopology().getBlockWithUID (uid); }
protected:
//==============================================================================
ListenerList<Listener> listeners;
};
} // namespace juce

+ 0
- 87
modules/juce_blocks_basics/visualisers/juce_BitmapLEDProgram.cpp View File

@@ -1,87 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
BitmapLEDProgram::BitmapLEDProgram (Block& b) : Program (b) {}
/*
The heap format for this program is just an array of 15x15 5:6:5 colours,
and the program just copies them onto the screen each frame.
*/
void BitmapLEDProgram::setLED (uint32 x, uint32 y, LEDColour colour)
{
if (auto ledGrid = block.getLEDGrid())
{
auto w = (uint32) ledGrid->getNumColumns();
auto h = (uint32) ledGrid->getNumRows();
if (x < w && y < h)
{
auto bit = (x + y * w) * 16;
block.setDataBits (bit, 5, (uint32) (colour.getRed() >> 3));
block.setDataBits (bit + 5, 6, (uint32) (colour.getGreen() >> 2));
block.setDataBits (bit + 11, 5, (uint32) (colour.getBlue() >> 3));
}
}
else
{
jassertfalse;
}
}
String BitmapLEDProgram::getLittleFootProgram()
{
String program (R"littlefoot(
#heapsize: 15 * 15 * 2
void repaint()
{
for (int y = 0; y < NUM_ROWS; ++y)
{
for (int x = 0; x < NUM_COLUMNS; ++x)
{
int bit = (x + y * NUM_COLUMNS) * 16;
fillPixel (makeARGB (255,
getHeapBits (bit, 5) << 3,
getHeapBits (bit + 5, 6) << 2,
getHeapBits (bit + 11, 5) << 3), x, y);
}
}
}
)littlefoot");
if (auto ledGrid = block.getLEDGrid())
return program.replace ("NUM_COLUMNS", String (ledGrid->getNumColumns()))
.replace ("NUM_ROWS", String (ledGrid->getNumRows()));
jassertfalse;
return {};
}
} // namespace juce

+ 0
- 42
modules/juce_blocks_basics/visualisers/juce_BitmapLEDProgram.h View File

@@ -1,42 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
A simple Program to set the colours of individual LEDs.
@tags{Blocks}
*/
struct BitmapLEDProgram : public Block::Program
{
BitmapLEDProgram (Block&);
/** Set the colour of the LED at coordinates {x, y}. */
void setLED (uint32 x, uint32 y, LEDColour);
private:
String getLittleFootProgram() override;
};
} // namespace juce

+ 0
- 881
modules/juce_blocks_basics/visualisers/juce_DrumPadLEDProgram.cpp View File

@@ -1,881 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
DrumPadGridProgram::DrumPadGridProgram (Block& b) : Program (b) {}
int DrumPadGridProgram::getPadIndex (float posX, float posY) const
{
posX = jmin (0.99f, posX / (float) block.getWidth());
posY = jmin (0.99f, posY / (float) block.getHeight());
const uint32 offset = block.getDataByte (visiblePads_byte) ? numColumns1_byte : numColumns0_byte;
const int numColumns = block.getDataByte (offset + numColumns0_byte);
const int numRows = block.getDataByte (offset + numRows0_byte);
return int (posX * (float) numColumns) + int (posY * (float) numRows) * numColumns;
}
void DrumPadGridProgram::startTouch (float startX, float startY)
{
const auto padIdx = getPadIndex (startX, startY);
for (size_t i = 0; i < 4; ++i)
{
if (block.getDataByte (touchedPads_byte + i) == 0)
{
block.setDataByte (touchedPads_byte + i, static_cast<uint8> (padIdx + 1));
break;
}
}
}
void DrumPadGridProgram::endTouch (float startX, float startY)
{
const auto padIdx = getPadIndex (startX, startY);
for (size_t i = 0; i < 4; ++i)
if (block.getDataByte (touchedPads_byte + i) == (padIdx + 1))
block.setDataByte (touchedPads_byte + i, 0);
}
void DrumPadGridProgram::sendTouch (float x, float y, float z, LEDColour colour)
{
Block::ProgramEventMessage e;
e.values[0] = 0x20000000
+ (jlimit (0, 255, roundToInt (x * (255.0f / (float) block.getWidth()))) << 16)
+ (jlimit (0, 255, roundToInt (y * (255.0f / (float) block.getHeight()))) << 8)
+ jlimit (0, 255, roundToInt (z * 255.0f));
e.values[1] = (int32) colour.getARGB();
block.sendProgramEvent (e);
}
//==============================================================================
void DrumPadGridProgram::setGridFills (int numColumns, int numRows, const Array<GridFill>& fills)
{
uint8 visiblePads = block.getDataByte (visiblePads_byte);
setGridFills (numColumns, numRows, fills, visiblePads * numColumns1_byte);
}
void DrumPadGridProgram::setGridFills (int numColumns, int numRows, const Array<GridFill>& fills, uint32 byteOffset)
{
jassert (numColumns * numRows == fills.size());
block.setDataByte (byteOffset + numColumns0_byte, (uint8) numColumns);
block.setDataByte (byteOffset + numRows0_byte, (uint8) numRows);
uint32 i = 0;
for (auto fill : fills)
{
if (i >= maxNumPads)
{
jassertfalse;
break;
}
const uint32 colourOffsetBytes = byteOffset + colours0_byte + i * colourSizeBytes;
const uint32 colourOffsetBits = colourOffsetBytes * 8;
block.setDataBits (colourOffsetBits, 5, (uint32) (fill.colour.getRed() >> 3));
block.setDataBits (colourOffsetBits + 5, 6, (uint32) (fill.colour.getGreen() >> 2));
block.setDataBits (colourOffsetBits + 11, 5, (uint32) (fill.colour.getBlue() >> 3));
block.setDataByte (byteOffset + fillTypes0_byte + i, static_cast<uint8> (fill.fillType));
++i;
}
}
void DrumPadGridProgram::triggerSlideTransition (int newNumColumns, int newNumRows,
const Array<GridFill>& newFills, SlideDirection direction)
{
uint8 newVisible = block.getDataByte (visiblePads_byte) ? 0 : 1;
setGridFills (newNumColumns, newNumRows, newFills, newVisible * numColumns1_byte);
block.setDataByte (visiblePads_byte, newVisible);
block.setDataByte (slideDirection_byte, (uint8) direction);
}
//==============================================================================
void DrumPadGridProgram::setPadAnimationState (uint32 padIdx, double loopTimeSecs, double currentProgress)
{
// Only 16 animated pads are supported.
jassert (padIdx < 16);
// Compensate for bluetooth latency & led resolution, tweaked by eye for POS app.
currentProgress = std::fmod (currentProgress + 0.1, 1.0);
uint16 aniValue = uint16 (roundToInt ((255 << 8) * currentProgress));
uint16 aniIncrement = loopTimeSecs > 0.0 ? uint16 (roundToInt (((255 << 8) / 25.0) / loopTimeSecs)) : 0;
uint32 offset = 8 * animationTimers_byte + 32 * padIdx;
block.setDataBits (offset, 16, aniValue);
block.setDataBits (offset + 16, 16, aniIncrement);
}
void DrumPadGridProgram::suspendAnimations()
{
for (uint32 i = 0; i < 16; ++i)
{
uint32 offset = 8 * animationTimers_byte + 32 * i;
block.setDataBits (offset + 16, 16, 0);
}
// Hijack touch dimming
block.setDataByte (touchedPads_byte, 255);
}
void DrumPadGridProgram::resumeAnimations()
{
// Unhijack touch dimming
block.setDataByte (touchedPads_byte, 0);
}
//==============================================================================
String DrumPadGridProgram::getLittleFootProgram()
{
if (block.versionNumber.isEmpty() || block.versionNumber.compare ("0.2.5") < 0)
return getLittleFootProgramPre25();
return getLittleFootProgramPost25();
}
String DrumPadGridProgram::getLittleFootProgramPre25() const
{
// Uses its own heatmap, not the one provided in newer firmware
// Also can't use blocks config, introduced in 2.5.
return R"littlefoot(
#heapsize: 1351
int dimFactor;
int dimDelay;
int slideAnimationProgress;
int lastVisiblePads;
int getGridColour (int index, int colourMapOffset)
{
int bit = (2 + colourMapOffset) * 8 + index * 16;
return makeARGB (255,
getHeapBits (bit, 5) << 3,
getHeapBits (bit + 5, 6) << 2,
getHeapBits (bit + 11, 5) << 3);
}
// Returns the current progress and also increments it for next frame
int getAnimationProgress (int index)
{
// Only 16 animated pads supported
if (index > 15)
return 0;
int offsetBits = 162 * 8 + index * 32;
int currentProgress = getHeapBits (offsetBits, 16);
int increment = getHeapBits (offsetBits + 16, 16);
int nextFrame = currentProgress + increment;
// Set incremented 16 bit number.
setHeapByte (162 + index * 4, nextFrame & 0xff);
setHeapByte (163 + index * 4, nextFrame >> 8);
return currentProgress;
}
void outlineRect (int colour, int x, int y, int w)
{
fillRect (colour, x, y, w, 1);
fillRect (colour, x, y + w - 1, w, 1);
fillRect (colour, x, y + 1, 1, w - 1);
fillRect (colour, x + w - 1, y + 1, 1, w - 1);
}
void drawPlus (int colour, int x, int y, int w)
{
fillRect (colour, x, y + (w / 2), w, 1);
fillRect (colour, x + (w / 2), y, 1, w);
}
void fillGradientRect (int colour, int x, int y, int w)
{
if (colour != 0xff000000)
{
int divisor = w + w - 1;
for (int yy = 0; yy < w; ++yy)
{
for (int xx = yy; xx < w; ++xx)
{
int gradColour = blendARGB (colour, makeARGB (((xx + yy) * 250) / divisor, 0, 0, 0));
setLED (x + xx, y + yy, gradColour);
setLED (x + yy, y + xx, gradColour);
}
}
}
}
// TODO: Tom M: This is massaged to work with 3x3 pads and for dots to sync
// with Apple POS loop length. Rework to be more robust & flexible.
void drawPizzaLED (int colour, int x, int y, int w, int progress)
{
--w;
x += 1;
int numToDo = ((8 * progress) / 255) + 1;
int totalLen = w * 4;
for (int i = 1; i <= numToDo; ++i)
{
setLED (x, y, colour);
if (i < w)
++x;
else if (i < (w * 2))
++y;
else if (i < (w * 3))
--x;
else if (i < totalLen)
--y;
}
}
void drawPad (int padX, int padY, int padW,
int colour, int fill, int animateProgress)
{
animateProgress >>= 8; // 16 bit to 8 bit
int halfW = padW / 2;
if (fill == 0) // Gradient fill
{
fillGradientRect (colour, padX, padY, padW);
}
else if (fill == 1) // Filled
{
fillRect (colour, padX, padY, padW, padW);
}
else if (fill == 2) // Hollow
{
outlineRect (colour, padX, padY, padW);
}
else if (fill == 3) // Hollow with plus
{
outlineRect (colour, padX, padY, padW);
drawPlus (0xffffffff, padX, padY, padW);
}
else if (fill == 4) // Pulsing dot
{
int pulseCol = blendARGB (colour, makeARGB (animateProgress, 0, 0, 0));
setLED (padX + halfW, padY + halfW, pulseCol);
}
else if (fill == 5) // Blinking dot
{
int blinkCol = animateProgress > 64 ? makeARGB (255, 0, 0, 0) : colour;
setLED (padX + halfW, padY + halfW, blinkCol);
}
else if (fill == 6) // Pizza filled
{
outlineRect (blendARGB (colour, makeARGB (220, 0, 0, 0)), padX, padY, padW); // Dim outline
setLED (padX + halfW, padY + halfW, colour); // Bright centre
drawPizzaLED (colour, padX, padY, padW, animateProgress);
}
else if (fill == 7) // Pizza hollow
{
outlineRect (blendARGB (colour, makeARGB (220, 0, 0, 0)), padX, padY, padW); // Dim outline
drawPizzaLED (colour, padX, padY, padW, animateProgress);
return;
}
}
void fadeHeatMap()
{
for (int i = 0; i < 225; ++i)
{
int colourOffset = 226 + i * 4;
int colour = getHeapInt (colourOffset);
int alpha = (colour >> 24) & 0xff;
if (alpha > 0)
{
alpha -= getHeapByte (1126 + i);
setHeapInt (colourOffset, alpha < 0 ? 0 : ((alpha << 24) | (colour & 0xffffff)));
}
}
}
void addToHeatMap (int x, int y, int colour)
{
if (x >= 0 && y >= 0 && x < 15 && y < 15)
{
int offset = 226 + 4 * (x + y * 15);
colour = blendARGB (getHeapInt (offset), colour);
setHeapInt (offset, colour);
int decay = ((colour >> 24) & 0xff) / 14; // change divisor to change trail times
offset = 1126 + (x + y * 15);
setHeapByte (offset, decay > 0 ? decay : 1);
}
}
int getHeatmapColour (int x, int y)
{
return getHeapInt (226 + 4 * (x + y * 15));
}
int isPadActive (int index)
{
if (getHeapInt (158) == 0) // None active
return 0;
++index;
return index == getHeapByte (158) ||
index == getHeapByte (159) ||
index == getHeapByte (160) ||
index == getHeapByte (161);
}
void updateDimFactor()
{
if (getHeapInt (158) == 0)
{
if (--dimDelay <= 0)
{
dimFactor -= 12;
if (dimFactor < 0)
dimFactor = 0;
}
}
else
{
dimFactor = 180;
dimDelay = 12;
}
}
void drawPads (int offsetX, int offsetY, int colourMapOffset)
{
int padsPerSide = getHeapByte (0 + colourMapOffset);
if (padsPerSide < 2)
return;
int blockW = 15 / padsPerSide;
int blockPlusGapW = blockW + (15 - padsPerSide * blockW) / (padsPerSide - 1);
for (int padY = 0; padY < padsPerSide; ++padY)
{
for (int padX = 0; padX < padsPerSide; ++padX)
{
int ledX = offsetX + padX * blockPlusGapW;
int ledY = offsetY + padY * blockPlusGapW;
if (ledX < 15 &&
ledY < 15 &&
(ledX + blockW) >= 0 &&
(ledY + blockW) >= 0)
{
int padIdx = padX + padY * padsPerSide;
bool padActive = isPadActive (padIdx);
int blendCol = padActive ? 255 : 0;
int blendAmt = padActive ? dimFactor >> 1 : dimFactor;
int colour = blendARGB (getGridColour (padIdx, colourMapOffset),
makeARGB (blendAmt, blendCol, blendCol, blendCol));
int fillType = getHeapByte (colourMapOffset + 52 + padIdx);
int animate = getAnimationProgress (padIdx);
drawPad (ledX, ledY, blockW, colour, fillType, animate);
}
}
}
}
void slideAnimatePads()
{
int nowVisible = getHeapByte (155);
if (lastVisiblePads != nowVisible)
{
lastVisiblePads = nowVisible;
if (slideAnimationProgress <= 0)
slideAnimationProgress = 15;
}
// If animation is complete, draw normally.
if (slideAnimationProgress <= 0)
{
drawPads (0, 0, 78 * nowVisible);
slideAnimationProgress = 0;
}
else
{
int direction = getHeapByte (156);
slideAnimationProgress -= 1;
int inPos = nowVisible == 0 ? 0 : 78;
int outPos = nowVisible == 0 ? 78 : 0;
if (direction == 0) // Up
{
drawPads (0, slideAnimationProgress - 16, outPos);
drawPads (0, slideAnimationProgress, inPos);
}
else if (direction == 1) // Down
{
drawPads (0, 16 - slideAnimationProgress, outPos);
drawPads (0, 0 - slideAnimationProgress, inPos);
}
else if (direction == 2) // Left
{
drawPads (16 - slideAnimationProgress, 0, outPos);
drawPads (slideAnimationProgress, 0, inPos);
}
else if (direction == 3) // Right
{
drawPads (16 - slideAnimationProgress, 0, outPos);
drawPads (0 - slideAnimationProgress, 0, inPos);
}
else // None
{
drawPads (0, 0, 78 * nowVisible);
slideAnimationProgress = 0;
}
}
}
void repaint()
{
// showErrorOnFail, showRepaintTime, showMovingDot
//enableDebug (true, true, false);
// Clear LEDs to black, update dim animation
fillRect (0xff000000, 0, 0, 15, 15);
updateDimFactor();
// Does the main painting of pads
slideAnimatePads();
// Overlay heatmap
for (int y = 0; y < 15; ++y)
for (int x = 0; x < 15; ++x)
blendLED (x, y, getHeatmapColour (x, y));
fadeHeatMap();
}
// DrumPadGridProgram::sendTouch results in this callback, giving
// us more touch updates per frame and therefore smoother trails.
void handleMessage (int pos, int colour, int xx)
{
handleMessage (pos, colour);
}
void handleMessage (int pos, int colour)
{
if ((pos >> 24) != 0x20)
return;
int tx = ((pos >> 16) & 0xff) - 13;
int ty = ((pos >> 8) & 0xff) - 13;
int tz = pos & 0xff;
tz = tz > 30 ? tz : 30;
int ledCentreX = tx >> 4;
int ledCentreY = ty >> 4;
int adjustX = (tx - (ledCentreX << 4)) >> 2;
int adjustY = (ty - (ledCentreY << 4)) >> 2;
for (int dy = -2; dy <= 2; ++dy)
{
for (int dx = -2; dx <= 2; ++dx)
{
int distance = dx * dx + dy * dy;
int level = distance == 0 ? 255 : (distance == 1 ? 132 : (distance < 5 ? 9 : (distance == 5 ? 2 : 0)));
level += (dx * adjustX);
level += (dy * adjustY);
level = (tz * level) >> 8;
if (level > 0)
addToHeatMap (ledCentreX + dx, ledCentreY + dy,
makeARGB (level, colour >> 16, colour >> 8, colour));
}
}
}
)littlefoot";
}
String DrumPadGridProgram::getLittleFootProgramPost25() const
{
// Uses heatmap provided in firmware (so the program's smaller)
// Initialises config items introduced in firmware 2.5
return R"littlefoot(
#heapsize: 256
int dimFactor;
int dimDelay;
int slideAnimationProgress;
int lastVisiblePads;
bool gammaCorrected;
void initialise()
{
for (int i = 0; i < 32; ++i)
setLocalConfigActiveState (i, true, true);
// Enable gamma correction if supported on hardware
setLocalConfig (33, 1);
gammaCorrected = getLocalConfig (33) > 0;
}
int getGridColour (int index, int colourMapOffset)
{
int bit = (2 + colourMapOffset) * 8 + index * 16;
return makeARGB (255,
getHeapBits (bit, 5) << 3,
getHeapBits (bit + 5, 6) << 2,
getHeapBits (bit + 11, 5) << 3);
}
// Returns the current progress and also increments it for next frame
int getAnimationProgress (int index)
{
// Only 16 animated pads supported
if (index > 15)
return 0;
int offsetBits = 162 * 8 + index * 32;
int currentProgress = getHeapBits (offsetBits, 16);
int increment = getHeapBits (offsetBits + 16, 16);
int nextFrame = currentProgress + increment;
// Set incremented 16 bit number.
setHeapByte (162 + index * 4, nextFrame & 0xff);
setHeapByte (163 + index * 4, nextFrame >> 8);
return currentProgress;
}
void outlineRect (int colour, int x, int y, int w)
{
fillRect (colour, x, y, w, 1);
fillRect (colour, x, y + w - 1, w, 1);
fillRect (colour, x, y + 1, 1, w - 1);
fillRect (colour, x + w - 1, y + 1, 1, w - 1);
}
void drawPlus (int colour, int x, int y, int w)
{
fillRect (colour, x, y + (w / 2), w, 1);
fillRect (colour, x + (w / 2), y, 1, w);
}
void fillGradientRect (int colour, int x, int y, int w)
{
if (colour != 0xff000000)
{
int divisor = w + w - 1;
for (int yy = 0; yy < w; ++yy)
{
for (int xx = yy; xx < w; ++xx)
{
int gradColour = blendARGB (colour, makeARGB (((xx + yy) * 250) / divisor, 0, 0, 0));
fillPixel (gradColour, x + xx, y + yy);
fillPixel (gradColour, x + yy, y + xx);
}
}
}
}
// TODO: Tom M: This is massaged to work with 3x3 pads and for dots to sync
// with Apple POS loop length. Rework to be more robust & flexible.
void drawPizzaLED (int colour, int x, int y, int w, int progress)
{
--w;
x += 1;
int numToDo = ((8 * progress) / 255) + 1;
int totalLen = w * 4;
for (int i = 1; i <= numToDo; ++i)
{
fillPixel (colour, x, y);
if (i < w)
++x;
else if (i < (w * 2))
++y;
else if (i < (w * 3))
--x;
else if (i < totalLen)
--y;
}
}
void drawPad (int padX, int padY, int padW,
int colour, int fill, int animateProgress)
{
animateProgress >>= 8; // 16 bit to 8 bit
int halfW = padW / 2;
if (fill == 0) // Gradient fill
{
fillGradientRect (colour, padX, padY, padW);
}
else if (fill == 1) // Filled
{
fillRect (colour, padX, padY, padW, padW);
}
else if (fill == 2) // Hollow
{
outlineRect (colour, padX, padY, padW);
}
else if (fill == 3) // Hollow with plus
{
outlineRect (colour, padX, padY, padW);
drawPlus (0xffffffff, padX, padY, padW);
}
else if (fill == 4) // Pulsing dot
{
int pulseCol = blendARGB (colour, makeARGB (animateProgress, 0, 0, 0));
fillPixel (pulseCol, padX + halfW, padY + halfW);
}
else if (fill == 5) // Blinking dot
{
int blinkCol = animateProgress > 64 ? 0xff000000 : colour;
fillPixel (blinkCol, padX + halfW, padY + halfW);
}
else if (fill == 6) // Pizza filled
{
outlineRect (blendARGB (colour, 0xdc000000), padX, padY, padW); // Dim outline
fillPixel (colour, padX + halfW, padY + halfW); // Bright centre
drawPizzaLED (colour, padX, padY, padW, animateProgress);
}
else // Pizza hollow
{
outlineRect (blendARGB (colour, 0xdc000000), padX, padY, padW); // Dim outline
drawPizzaLED (colour, padX, padY, padW, animateProgress);
}
}
int isPadActive (int index)
{
if (getHeapInt (158) == 0) // None active
return 0;
++index;
return index == getHeapByte (158) ||
index == getHeapByte (159) ||
index == getHeapByte (160) ||
index == getHeapByte (161);
}
void updateDimFactor()
{
if (getHeapInt (158) == 0)
{
if (--dimDelay <= 0)
{
dimFactor -= 12;
if (dimFactor < 0)
dimFactor = 0;
}
}
else
{
dimFactor = gammaCorrected ? 100 : 180;
dimDelay = 12;
}
}
void drawPads (int offsetX, int offsetY, int colourMapOffset)
{
int padsPerSide = getHeapByte (0 + colourMapOffset);
if (padsPerSide < 2)
return;
int blockW = 15 / padsPerSide;
int blockPlusGapW = blockW + (15 - padsPerSide * blockW) / (padsPerSide - 1);
for (int padY = 0; padY < padsPerSide; ++padY)
{
for (int padX = 0; padX < padsPerSide; ++padX)
{
int ledX = offsetX + padX * blockPlusGapW;
int ledY = offsetY + padY * blockPlusGapW;
if (ledX < 15 &&
ledY < 15 &&
(ledX + blockW) >= 0 &&
(ledY + blockW) >= 0)
{
int padIdx = padX + padY * padsPerSide;
bool padActive = isPadActive (padIdx);
int blendCol = padActive ? 255 : 0;
int blendAmt = padActive ? dimFactor >> 1 : dimFactor;
int colour = blendARGB (getGridColour (padIdx, colourMapOffset),
makeARGB (blendAmt, blendCol, blendCol, blendCol));
int fillType = getHeapByte (colourMapOffset + 52 + padIdx);
int animate = getAnimationProgress (padIdx);
drawPad (ledX, ledY, blockW, colour, fillType, animate);
}
}
}
}
void slideAnimatePads()
{
int nowVisible = getHeapByte (155);
if (lastVisiblePads != nowVisible)
{
lastVisiblePads = nowVisible;
if (slideAnimationProgress <= 0)
slideAnimationProgress = 15;
}
// If animation is complete, draw normally.
if (slideAnimationProgress <= 0)
{
drawPads (0, 0, 78 * nowVisible);
slideAnimationProgress = 0;
}
else
{
int direction = getHeapByte (156);
slideAnimationProgress -= 1;
int inPos = nowVisible == 0 ? 0 : 78;
int outPos = nowVisible == 0 ? 78 : 0;
if (direction == 0) // Up
{
drawPads (0, slideAnimationProgress - 16, outPos);
drawPads (0, slideAnimationProgress, inPos);
}
else if (direction == 1) // Down
{
drawPads (0, 16 - slideAnimationProgress, outPos);
drawPads (0, 0 - slideAnimationProgress, inPos);
}
else if (direction == 2) // Left
{
drawPads (16 - slideAnimationProgress, 0, outPos);
drawPads (slideAnimationProgress, 0, inPos);
}
else if (direction == 3) // Right
{
drawPads (16 - slideAnimationProgress, 0, outPos);
drawPads (0 - slideAnimationProgress, 0, inPos);
}
else // None
{
drawPads (0, 0, 78 * nowVisible);
slideAnimationProgress = 0;
}
}
}
void repaint()
{
// showErrorOnFail, showRepaintTime, showMovingDot
//enableDebug (true, true, false);
// Clear LEDs to black, update dim animation
fillRect (0xff000000, 0, 0, 15, 15);
updateDimFactor();
// Does the main painting of pads
slideAnimatePads();
// Overlay heatmap
drawPressureMap();
fadePressureMap();
}
// DrumPadGridProgram::sendTouch results in this callback, giving
// us more touch updates per frame and therefore smoother trails.
void handleMessage (int pos, int colour, int dummy)
{
if ((pos >> 24) != 0x20)
return;
int tx = (pos >> 16) & 0xff;
int ty = (pos >> 8) & 0xff;
int tz = pos & 0xff;
addPressurePoint (colour,
tx * (2.0 / (256 + 20)),
ty * (2.0 / (256 + 20)),
tz * (1.0 / 3.0));
}
)littlefoot";
}
} // namespace juce

+ 0
- 126
modules/juce_blocks_basics/visualisers/juce_DrumPadLEDProgram.h View File

@@ -1,126 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
@tags{Blocks}
*/
struct DrumPadGridProgram : public Block::Program
{
DrumPadGridProgram (Block&);
//==============================================================================
/** These let the program dim pads which aren't having gestures performed on them. */
void startTouch (float startX, float startY);
void endTouch (float startX, float startY);
/** Creates trail effects similar to the onscreen pad trails. */
void sendTouch (float x, float y, float z, LEDColour);
//==============================================================================
/** Call this to match animations to the project tempo.
@param padIdx The pad to update. 16 animated pads are supported, so 0 - 15.
@param loopTimeSecs The length of time for the pad's animation to loop in seconds. 0 will stop the animation.
@param currentProgress The starting progress of the animation. 0.0 - 1.0.
*/
void setPadAnimationState (uint32 padIdx, double loopTimeSecs, double currentProgress);
/** If the app needs to close down or suspend, use these to pause & dim animations. */
void suspendAnimations();
void resumeAnimations();
//==============================================================================
/** Set how each pad in the grid looks. */
struct GridFill
{
enum FillType : uint8
{
gradient = 0,
filled = 1,
hollow = 2,
hollowPlus = 3,
// Animated pads
dotPulsing = 4,
dotBlinking = 5,
pizzaFilled = 6,
pizzaHollow = 7,
};
LEDColour colour;
FillType fillType;
};
void setGridFills (int numColumns, int numRows,
const Array<GridFill>&);
/** Set up a new pad layout, with a slide animation from the old to the new. */
enum SlideDirection : uint8
{
up = 0,
down = 1,
left = 2,
right = 3,
none = 255
};
void triggerSlideTransition (int newNumColumns, int newNumRows,
const Array<GridFill>& newFills, SlideDirection);
private:
//==============================================================================
/** Shared data heap is laid out as below. There is room for two sets of
pad layouts, colours and fill types to allow animation between two states. */
static constexpr uint32 numColumns0_byte = 0; // 1 byte
static constexpr uint32 numRows0_byte = 1; // 1 byte (ignored for the moment: always square pads to save cycles)
static constexpr uint32 colours0_byte = 2; // 2 byte x 25 (5:6:5 bits for rgb)
static constexpr uint32 fillTypes0_byte = 52; // 1 byte x 25
static constexpr uint32 numColumns1_byte = 78; // 1 byte
static constexpr uint32 numRows1_byte = 79; // 1 byte
static constexpr uint32 colours1_byte = 80; // 2 byte x 25 (5:6:5 bits for rgb)
static constexpr uint32 fillTypes1_byte = 130; // 1 byte x 25
static constexpr uint32 visiblePads_byte = 155; // 1 byte (i.e. which set of colours/fills to use, 0 or 1)
static constexpr uint32 slideDirection_byte = 156; // 1 byte
static constexpr uint32 touchedPads_byte = 158; // 1 byte x 4 (Zero means empty slot, so stores padIdx + 1)
static constexpr uint32 animationTimers_byte = 162; // 4 byte x 16 (16:16 bits counter:increment)
static constexpr uint32 totalHeapSize = 226;
static constexpr uint32 maxNumPads = 25;
static constexpr uint32 colourSizeBytes = 2;
int getPadIndex (float posX, float posY) const;
void setGridFills (int numColumns, int numRows, const Array<GridFill>& fills, uint32 byteOffset);
String getLittleFootProgram() override;
String getLittleFootProgramPre25() const;
String getLittleFootProgramPost25() const;
};
} // namespace juce

Loading…
Cancel
Save