@@ -17,7 +17,7 @@ | |||
//============================================================================== | |||
// [BEGIN_USER_CODE_SECTION] | |||
// (You can add your own code in this section, and the Projucer will not overwrite it) | |||
#define DUMP_TOPOLOGY 1 | |||
// [END_USER_CODE_SECTION] | |||
@@ -4,6 +4,7 @@ | |||
#include "../JuceLibraryCode/JuceHeader.h" | |||
//============================================================================== | |||
/** | |||
A struct that handles the setup and layout of the DrumPadGridProgram | |||
*/ | |||
@@ -70,28 +71,31 @@ struct ColourGrid | |||
//============================================================================== | |||
int numColumns, numRows; | |||
float width, height; | |||
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 }; | |||
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 MainComponent : public Component, | |||
public TopologySource::Listener, | |||
private TouchSurface::Listener, | |||
private ControlButton::Listener, | |||
private Timer | |||
public TopologySource::Listener, | |||
private TouchSurface::Listener, | |||
private ControlButton::Listener, | |||
private Timer | |||
{ | |||
public: | |||
MainComponent() : layout (3, 3) | |||
MainComponent() | |||
{ | |||
setSize (600, 400); | |||
@@ -110,7 +114,8 @@ public: | |||
void paint (Graphics& g) override | |||
{ | |||
g.fillAll (Colours::lightgrey); | |||
g.drawText ("Connect a Lightpad Block to draw.", getLocalBounds(), Justification::centred, false); | |||
g.drawText ("Connect a Lightpad Block to draw.", | |||
getLocalBounds(), Justification::centred, false); | |||
} | |||
void resized() override {} | |||
@@ -176,7 +181,7 @@ private: | |||
} | |||
/** Overridden from ControlButton::Listener. Called when a button on the Lightpad is pressed */ | |||
void buttonPressed (ControlButton&, Block::Timestamp) override {}; | |||
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 | |||
@@ -257,7 +262,9 @@ private: | |||
grid->setProgram (colourPaletteProgram); | |||
// Setup the grid layout | |||
colourPaletteProgram->setGridFills (layout.numColumns, layout.numRows, layout.gridFillArray); | |||
colourPaletteProgram->setGridFills (layout.numColumns, | |||
layout.numRows, | |||
layout.gridFillArray); | |||
} | |||
} | |||
@@ -265,20 +272,12 @@ private: | |||
void drawLEDs (uint32 x0, uint32 y0, float z, Colour drawColour) | |||
{ | |||
// Check if the activeLeds array already contains an ActiveLED object for this LED | |||
int index = -1; | |||
for (int i = 0; i < activeLeds.size(); ++i) | |||
{ | |||
if (activeLeds.getReference(i).occupies (x0, y0)) | |||
{ | |||
index = i; | |||
break; | |||
} | |||
} | |||
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 != -1) | |||
if (index >= 0) | |||
{ | |||
canvasProgram->setLED (x0, y0, Colours::black); | |||
activeLeds.remove (index); | |||
@@ -289,7 +288,7 @@ private: | |||
// If there is no ActiveLED obejct for this LED then create one, | |||
// add it to the array, set the LED on the Block and return | |||
if (index == -1) | |||
if (index < 0) | |||
{ | |||
ActiveLED led; | |||
led.x = x0; | |||
@@ -333,37 +332,42 @@ private: | |||
*/ | |||
struct ActiveLED | |||
{ | |||
uint32 x; | |||
uint32 y; | |||
uint32 x, y; | |||
Colour colour; | |||
float brightness; | |||
/** Returns true if this LED occupies the given co-ordiantes */ | |||
/** Returns true if this LED occupies the given co-ordinates */ | |||
bool occupies (uint32 xPos, uint32 yPos) const | |||
{ | |||
if (xPos == x && yPos == y) | |||
return true; | |||
return false; | |||
return xPos == x && yPos == y; | |||
} | |||
}; | |||
Array<ActiveLED> activeLeds; | |||
/** | |||
enum for the two modes | |||
*/ | |||
int getLEDAt (uint32 x, uint32 y) const | |||
{ | |||
for (int 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; | |||
//============================================================================== | |||
BitmapLEDProgram* canvasProgram; | |||
DrumPadGridProgram* colourPaletteProgram; | |||
BitmapLEDProgram* canvasProgram = nullptr; | |||
DrumPadGridProgram* colourPaletteProgram = nullptr; | |||
ColourGrid layout; | |||
ColourGrid layout { 3, 3 }; | |||
PhysicalTopologySource topologySource; | |||
Block::Ptr activeBlock; | |||
@@ -17,7 +17,7 @@ | |||
//============================================================================== | |||
// [BEGIN_USER_CODE_SECTION] | |||
// (You can add your own code in this section, and the Projucer will not overwrite it) | |||
#define DUMP_TOPOLOGY 1 | |||
// [END_USER_CODE_SECTION] | |||
@@ -15,7 +15,6 @@ class BlockComponent : public Component, | |||
private Timer | |||
{ | |||
public: | |||
/** Constructor */ | |||
BlockComponent (Block::Ptr blockToUse) | |||
: block (blockToUse) | |||
{ | |||
@@ -38,7 +37,6 @@ public: | |||
startTimerHz (25); | |||
} | |||
/** Destructor */ | |||
~BlockComponent() | |||
{ | |||
// Remove any listeners | |||
@@ -53,19 +51,16 @@ public: | |||
void updateStatsAndTooltip() | |||
{ | |||
// Get the battery level of this Block and inform any subclasses | |||
const float batteryLevel = block->getBatteryLevel(); | |||
auto batteryLevel = block->getBatteryLevel(); | |||
handleBatteryLevelUpdate (batteryLevel); | |||
// Format the tooltip string | |||
const String ttString = "Name = " + block->getDeviceDescription() + "\n" | |||
+ "UID = " + String (block->uid) + "\n" | |||
+ "Serial number = " + block->serialNumber + "\n" | |||
+ "Battery level = " + String ((int) (batteryLevel * 100)) + "%" | |||
+ (block->isBatteryCharging() ? "++" : "--"); | |||
// Update the tooltip string if it has changed | |||
if (ttString != getTooltip()) | |||
setTooltip (ttString); | |||
// Update the tooltip | |||
setTooltip ("Name = " + block->getDeviceDescription() + "\n" | |||
+ "UID = " + String (block->uid) + "\n" | |||
+ "Serial number = " + block->serialNumber + "\n" | |||
+ "Battery level = " + String ((int) (batteryLevel * 100)) + "%" | |||
+ (block->isBatteryCharging() ? "++" | |||
: "--")); | |||
} | |||
/** Subclasses should override this to paint the Block object on the screen */ | |||
@@ -88,31 +83,35 @@ public: | |||
//============================================================================== | |||
/** Returns an integer index corresponding to a physical position on the hardware | |||
for each type of Control Block. */ | |||
for each type of Control Block. */ | |||
static int controlButtonFunctionToIndex (ControlButton::ButtonFunction f) | |||
{ | |||
static std::initializer_list<ControlButton::ButtonFunction> map[] = | |||
{{ControlButton::mode, ControlButton::button0}, | |||
{ControlButton::volume, ControlButton::button1}, | |||
{ControlButton::scale, ControlButton::button2, ControlButton::click}, | |||
{ControlButton::chord, ControlButton::button3, ControlButton::snap}, | |||
{ControlButton::arp,ControlButton:: button4, ControlButton::back}, | |||
{ControlButton::sustain, ControlButton::button5, ControlButton::playOrPause}, | |||
{ControlButton::octave, ControlButton::button6, ControlButton::record}, | |||
{ControlButton::love, ControlButton::button7, ControlButton::learn}, | |||
{ControlButton::up}, | |||
{ControlButton::down}}; | |||
for (size_t i = 0; i < (sizeof (map) / sizeof (map[0])); ++i) | |||
if (std::find (map[i].begin(), map[i].end(), f) != map[i].end()) | |||
return static_cast<int> (i); | |||
using CB = ControlButton; | |||
static Array<ControlButton::ButtonFunction> map[] = | |||
{ | |||
{ CB::mode, CB::button0 }, | |||
{ CB::volume, CB::button1 }, | |||
{ CB::scale, CB::button2, CB::click }, | |||
{ CB::chord, CB::button3, CB::snap }, | |||
{ CB::arp, CB::button4, CB::back }, | |||
{ CB::sustain, CB::button5, CB::playOrPause }, | |||
{ CB::octave, CB::button6, CB::record }, | |||
{ CB::love, CB::button7, CB::learn }, | |||
{ CB::up }, | |||
{ CB::down } | |||
}; | |||
for (int i = 0; i < numElementsInArray (map); ++i) | |||
if (map[i].contains (f)) | |||
return i; | |||
return -1; | |||
} | |||
private: | |||
/** Used to call repaint() periodically */ | |||
void timerCallback() override { repaint(); } | |||
void timerCallback() override { repaint(); } | |||
/** Overridden from TouchSurface::Listener */ | |||
void touchChanged (TouchSurface&, const TouchSurface::Touch& t) override { handleTouchChange (t); } | |||
@@ -123,14 +122,14 @@ private: | |||
/** Overridden from ControlButton::Listener */ | |||
void buttonReleased (ControlButton& b, Block::Timestamp t) override { handleButtonReleased (b.getType(), t); } | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockComponent) | |||
}; | |||
//============================================================================== | |||
/** | |||
Class that renders a Lightpad on the screen | |||
*/ | |||
class LightpadComponent : public BlockComponent | |||
class LightpadComponent : public BlockComponent | |||
{ | |||
public: | |||
LightpadComponent (Block::Ptr blockToUse) | |||
@@ -154,23 +153,25 @@ public: | |||
g.fillAll (Colours::black); | |||
// size ration between physical and on-screen blocks | |||
const Point<float> ratio (r.getWidth() / block->getWidth(), | |||
r.getHeight() / block->getHeight()); | |||
const float maxCircleSize = block->getWidth() / 3.0f; | |||
Point<float> ratio (r.getWidth() / block->getWidth(), | |||
r.getHeight() / block->getHeight()); | |||
float maxCircleSize = block->getWidth() / 3.0f; | |||
// iterate over the list of current touches and draw them on the onscreen Block | |||
for (auto touch : touches) | |||
{ | |||
const float circleSize = touch.touch.z * maxCircleSize; | |||
const Point<float> touchPosition = Point<float> (touch.touch.x, touch.touch.y); | |||
float circleSize = touch.touch.z * maxCircleSize; | |||
Point<float> touchPosition (touch.touch.x, | |||
touch.touch.y); | |||
const Colour c = colourArray[touch.touch.index]; | |||
const Rectangle<float> blob = | |||
(Rectangle<float> (circleSize, circleSize).withCentre (touchPosition)) * ratio; | |||
auto blob = Rectangle<float> (circleSize, circleSize) | |||
.withCentre (touchPosition) * ratio; | |||
const ColourGradient cg = ColourGradient (colourArray[touch.touch.index], blob.getCentreX(), blob.getCentreY(), | |||
Colours::transparentBlack, blob.getRight(), blob.getBottom(), | |||
true); | |||
ColourGradient cg (colourArray[touch.touch.index], blob.getCentreX(), blob.getCentreY(), | |||
Colours::transparentBlack, blob.getRight(), blob.getBottom(), | |||
true); | |||
g.setGradientFill (cg); | |||
g.fillEllipse (blob); | |||
@@ -181,21 +182,26 @@ public: | |||
private: | |||
/** An Array of colours to use for touches */ | |||
Array<Colour> colourArray = { Colours::red, Colours::blue, Colours::green, | |||
Colours::yellow, Colours::white, Colours::hotpink, | |||
Array<Colour> colourArray = { Colours::red, | |||
Colours::blue, | |||
Colours::green, | |||
Colours::yellow, | |||
Colours::white, | |||
Colours::hotpink, | |||
Colours::mediumpurple }; | |||
/** A list of current Touch events */ | |||
TouchList<TouchSurface::Touch> touches; | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LightpadComponent) | |||
}; | |||
//============================================================================== | |||
/** | |||
Class that renders a Control Block on the screen | |||
*/ | |||
class ControlBlockComponent : public BlockComponent | |||
class ControlBlockComponent : public BlockComponent | |||
{ | |||
public: | |||
ControlBlockComponent (Block::Ptr blockToUse) | |||
@@ -205,13 +211,12 @@ public: | |||
addAndMakeVisible (roundedRectangleButton); | |||
// Display the battery level on the LEDRow | |||
int numLedsToTurnOn = static_cast<int> (static_cast<float> (numLeds) * block->getBatteryLevel()); | |||
auto numLedsToTurnOn = static_cast<int> (numLeds * block->getBatteryLevel()); | |||
// add LEDs | |||
LEDComponent* ledComponent; | |||
for (int i = 0; i < numLeds; ++i) | |||
{ | |||
ledComponent = new LEDComponent(); | |||
auto ledComponent = new LEDComponent(); | |||
ledComponent->setOnState (i < numLedsToTurnOn); | |||
addAndMakeVisible (leds.add (ledComponent)); | |||
@@ -238,9 +243,9 @@ public: | |||
auto buttonRow1 = row.removeFromTop (rowHeight * 2).withSizeKeepingCentre (r.getWidth(), buttonWidth); | |||
auto buttonRow2 = row.removeFromTop (rowHeight * 2).withSizeKeepingCentre (r.getWidth(), buttonWidth); | |||
for (int i = 0; i < numLeds; ++i) | |||
for (auto* led : leds) | |||
{ | |||
leds.getUnchecked (i)->setBounds (ledRow.removeFromLeft (ledWidth).reduced (2)); | |||
led->setBounds (ledRow.removeFromLeft (ledWidth).reduced (2)); | |||
ledRow.removeFromLeft (5); | |||
} | |||
@@ -261,7 +266,7 @@ public: | |||
void paint (Graphics& g) override | |||
{ | |||
const auto r = getLocalBounds().toFloat(); | |||
auto r = getLocalBounds().toFloat(); | |||
// Fill a black rectangle for the Control Block | |||
g.setColour (Colours::black); | |||
@@ -281,7 +286,7 @@ public: | |||
void handleBatteryLevelUpdate (float batteryLevel) override | |||
{ | |||
// Update the number of LEDs that are on to represent the battery level | |||
int numLedsOn = static_cast<int> (static_cast<float> (numLeds) * batteryLevel); | |||
int numLedsOn = static_cast<int> (numLeds * batteryLevel); | |||
if (numLedsOn != previousNumLedsOn) | |||
for (int i = 0; i < numLeds; ++i) | |||
@@ -292,22 +297,22 @@ public: | |||
} | |||
private: | |||
//============================================================================== | |||
/** | |||
Base class that renders a Control Block button | |||
*/ | |||
struct ControlBlockSubComponent : public Component, | |||
public TooltipClient | |||
struct ControlBlockSubComponent : public Component, | |||
public TooltipClient | |||
{ | |||
ControlBlockSubComponent (Colour componentColourToUse) | |||
: componentColour (componentColourToUse), | |||
onState (false) | |||
: componentColour (componentColourToUse) | |||
{} | |||
/** Subclasses should override this to paint the button on the screen */ | |||
virtual void paint (Graphics&) override = 0; | |||
/** Sets the colour of the button */ | |||
void setColour (Colour c) { componentColour = c; } | |||
void setColour (Colour c) { componentColour = c; } | |||
/** Sets the on state of the button */ | |||
void setOnState (bool isOn) | |||
@@ -320,15 +325,15 @@ private: | |||
String getTooltip() override | |||
{ | |||
for (Component* comp = this; comp != nullptr; comp = comp->getParentComponent()) | |||
if (SettableTooltipClient* sttc = dynamic_cast<SettableTooltipClient*> (comp)) | |||
if (auto* sttc = dynamic_cast<SettableTooltipClient*> (comp)) | |||
return sttc->getTooltip(); | |||
return String(); | |||
return {}; | |||
} | |||
//============================================================================== | |||
Colour componentColour; | |||
bool onState; | |||
bool onState = false; | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ControlBlockSubComponent) | |||
@@ -372,8 +377,7 @@ private: | |||
void paint (Graphics& g) override | |||
{ | |||
const auto r = getLocalBounds().toFloat(); | |||
auto r = getLocalBounds().toFloat(); | |||
g.setColour (componentColour.withAlpha (0.2f)); | |||
g.fillRoundedRectangle (r.toFloat(), 20.0f); | |||
@@ -382,11 +386,11 @@ private: | |||
// is a button pressed? | |||
if (doubleButtonOnState[0] || doubleButtonOnState[1]) | |||
{ | |||
const float semiButtonWidth = r.getWidth() / 2.0f; | |||
const auto semiButtonBounds = r.withWidth (semiButtonWidth) | |||
.withX (doubleButtonOnState[1] ? semiButtonWidth : 0) | |||
.reduced (5.0f, 2.0f); | |||
auto semiButtonWidth = r.getWidth() / 2.0f; | |||
auto semiButtonBounds = r.withWidth (semiButtonWidth) | |||
.withX (doubleButtonOnState[1] ? semiButtonWidth : 0) | |||
.reduced (5.0f, 2.0f); | |||
g.fillEllipse (semiButtonBounds); | |||
} | |||
@@ -399,7 +403,7 @@ private: | |||
} | |||
private: | |||
bool doubleButtonOnState[2] = {false, false}; | |||
bool doubleButtonOnState[2] = { false, false }; | |||
}; | |||
/** Displays a button press or release interaction for a button at a given index */ | |||
@@ -46,10 +46,10 @@ public: | |||
} | |||
// Work out the maximum diplay area for each Block | |||
const Rectangle<int> bounds = getLocalBounds().reduced (20); | |||
auto bounds = getLocalBounds().reduced (20); | |||
auto squareRoot = sqrt (numBlockComponents); | |||
int gridSize = (int)squareRoot; | |||
auto squareRoot = std::sqrt (numBlockComponents); | |||
int gridSize = (int) squareRoot; | |||
if (squareRoot - gridSize > 0) | |||
gridSize++; | |||
@@ -64,7 +64,7 @@ public: | |||
for (auto block : blockComponents) | |||
{ | |||
Rectangle<int> blockBounds; | |||
const Block::Type type = block->block->getType(); | |||
auto type = block->block->getType(); | |||
// Can fit 2 ControlBlockComponents in the space of one LightpadBlockComponent | |||
if (type == Block::liveBlock || type == Block::loopBlock) | |||
@@ -105,11 +105,11 @@ public: | |||
blockComponents.clear(); | |||
// Get the array of currently connected Block objects from the PhysicalTopologySource | |||
Block::Array blocksArray = topologySource.getCurrentTopology().blocks; | |||
auto blocksArray = topologySource.getCurrentTopology().blocks; | |||
// Create a BlockComponent object for each Block object | |||
for (auto& block : blocksArray) | |||
if (BlockComponent* blockComponent = createBlockComponent (block)) | |||
if (auto* blockComponent = createBlockComponent (block)) | |||
addAndMakeVisible (blockComponents.add (blockComponent)); | |||
// Update the display | |||
@@ -120,16 +120,16 @@ private: | |||
/** Creates a BlockComponent object for a new Block and adds it to the content component */ | |||
BlockComponent* createBlockComponent (Block::Ptr newBlock) | |||
{ | |||
const Block::Type type = newBlock->getType(); | |||
auto type = newBlock->getType(); | |||
if (type == Block::lightPadBlock) | |||
return new LightpadComponent (newBlock); | |||
if (type == Block::loopBlock || type == Block::liveBlock) | |||
return new ControlBlockComponent (newBlock); | |||
// should only be connecting a Lightpad or Control Block! | |||
jassertfalse; | |||
return nullptr; | |||
} | |||
@@ -17,7 +17,7 @@ | |||
//============================================================================== | |||
// [BEGIN_USER_CODE_SECTION] | |||
// (You can add your own code in this section, and the Projucer will not overwrite it) | |||
#define DUMP_TOPOLOGY 1 | |||
// [END_USER_CODE_SECTION] | |||
@@ -27,9 +27,8 @@ public: | |||
void shutdown() override { mainWindow = nullptr; } | |||
//============================================================================== | |||
class MainWindow : public DocumentWindow | |||
struct MainWindow : public DocumentWindow | |||
{ | |||
public: | |||
MainWindow (String name) : DocumentWindow (name, | |||
Colours::lightgrey, | |||
DocumentWindow::allButtons) | |||
@@ -47,10 +46,8 @@ public: | |||
JUCEApplication::getInstance()->systemRequestedQuit(); | |||
} | |||
private: | |||
TooltipWindow tooltipWindow; | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) | |||
}; | |||
@@ -5,6 +5,7 @@ | |||
#include "../JuceLibraryCode/JuceHeader.h" | |||
#include "Audio.h" | |||
//============================================================================== | |||
/** | |||
A struct that handles the setup and layout of the DrumPadGridProgram | |||
*/ | |||
@@ -41,7 +42,7 @@ struct SynthGrid | |||
} | |||
} | |||
int getNoteNumberForPad (int x, int y) | |||
int getNoteNumberForPad (int x, int y) const | |||
{ | |||
int xIndex = x / 3; | |||
int yIndex = y / 3; | |||
@@ -64,6 +65,7 @@ struct SynthGrid | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SynthGrid) | |||
}; | |||
//============================================================================== | |||
/** | |||
The main component | |||
*/ | |||
@@ -74,7 +76,7 @@ class MainComponent : public Component, | |||
private Timer | |||
{ | |||
public: | |||
MainComponent() : layout (5, 5) | |||
MainComponent() | |||
{ | |||
setSize (600, 400); | |||
@@ -93,7 +95,8 @@ public: | |||
void paint (Graphics& g) override | |||
{ | |||
g.fillAll (Colours::lightgrey); | |||
g.drawText ("Connect a Lightpad Block to play.", getLocalBounds(), Justification::centred, false); | |||
g.drawText ("Connect a Lightpad Block to play.", | |||
getLocalBounds(), Justification::centred, false); | |||
} | |||
void resized() override {} | |||
@@ -106,7 +109,7 @@ public: | |||
detachActiveBlock(); | |||
// Get the array of currently connected Block objects from the PhysicalTopologySource | |||
Block::Array blocks = topologySource.getCurrentTopology().blocks; | |||
auto blocks = topologySource.getCurrentTopology().blocks; | |||
// Iterate over the array of Block objects | |||
for (auto b : blocks) | |||
@@ -180,10 +183,11 @@ private: | |||
if (touchMessageTimesInLastSecond.size() > maxNumTouchMessagesPerSecond / 3) | |||
return; | |||
gridProgram->sendTouch (touch.x, touch.y, touch.z, layout.touchColour); | |||
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) / static_cast<float> (activeBlock->getWidth())); | |||
audio.pitchChange (midiChannel, (touch.x - touch.startX) / activeBlock->getWidth()); | |||
audio.pressureChange (midiChannel, touch.z); | |||
} | |||
@@ -215,26 +219,18 @@ private: | |||
// Clear all LEDs | |||
for (uint32 x = 0; x < 15; ++x) | |||
for (uint32 y = 0; y < 15; ++y) | |||
bitmapProgram->setLED (x, y, Colours::black); | |||
setLED (x, y, Colours::black); | |||
// Determine which array to use based on waveshapeMode | |||
int* waveshapeY = nullptr; | |||
switch (waveshapeMode) | |||
{ | |||
case 0: | |||
waveshapeY = sineWaveY; | |||
break; | |||
case 1: | |||
waveshapeY = squareWaveY; | |||
break; | |||
case 2: | |||
waveshapeY = sawWaveY; | |||
break; | |||
case 3: | |||
waveshapeY = triangleWaveY; | |||
break; | |||
default: | |||
break; | |||
case 0: waveshapeY = sineWaveY; break; | |||
case 1: waveshapeY = squareWaveY; break; | |||
case 2: waveshapeY = sawWaveY; break; | |||
case 3: waveshapeY = triangleWaveY; break; | |||
default: break; | |||
} | |||
// For each X co-ordinate | |||
@@ -306,7 +302,9 @@ private: | |||
grid->setProgram (gridProgram); | |||
// Setup the grid layout | |||
gridProgram->setGridFills (layout.numColumns, layout.numRows, layout.gridFillArray); | |||
gridProgram->setGridFills (layout.numColumns, | |||
layout.numRows, | |||
layout.gridFillArray); | |||
} | |||
} | |||
@@ -341,6 +339,7 @@ private: | |||
// 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] != -1) | |||
sawWaveY[x] = -1; | |||
@@ -361,42 +360,47 @@ private: | |||
} | |||
} | |||
/** Simple wrapper function to set a LED colour */ | |||
void setLED (uint32 x, uint32 y, Colour colour) | |||
{ | |||
if (bitmapProgram != nullptr) | |||
bitmapProgram->setLED (x, y, colour); | |||
} | |||
/** Draws a 'circle' on the Lightpad around an origin co-ordinate */ | |||
void drawLEDCircle (uint32 x0, uint32 y0) | |||
{ | |||
bitmapProgram->setLED (x0, y0, waveshapeColour); | |||
setLED (x0, y0, waveshapeColour); | |||
const uint32 minLedIndex = 0; | |||
const uint32 maxLedIndex = 14; | |||
bitmapProgram->setLED (jmin (x0 + 1, maxLedIndex), y0, waveshapeColour.withBrightness (0.4f)); | |||
bitmapProgram->setLED (jmax (x0 - 1, minLedIndex), y0, waveshapeColour.withBrightness (0.4f)); | |||
bitmapProgram->setLED (x0, jmin (y0 + 1, maxLedIndex), waveshapeColour.withBrightness (0.4f)); | |||
bitmapProgram->setLED (x0, jmax (y0 - 1, minLedIndex), waveshapeColour.withBrightness (0.4f)); | |||
setLED (jmin (x0 + 1, maxLedIndex), y0, waveshapeColour.withBrightness (0.4f)); | |||
setLED (jmax (x0 - 1, minLedIndex), y0, waveshapeColour.withBrightness (0.4f)); | |||
setLED (x0, jmin (y0 + 1, maxLedIndex), waveshapeColour.withBrightness (0.4f)); | |||
setLED (x0, jmax (y0 - 1, minLedIndex), waveshapeColour.withBrightness (0.4f)); | |||
bitmapProgram->setLED (jmin (x0 + 1, maxLedIndex), jmin (y0 + 1, maxLedIndex), waveshapeColour.withBrightness (0.1f)); | |||
bitmapProgram->setLED (jmin (x0 + 1, maxLedIndex), jmax (y0 - 1, minLedIndex), waveshapeColour.withBrightness (0.1f)); | |||
bitmapProgram->setLED (jmax (x0 - 1, minLedIndex), jmin (y0 + 1, maxLedIndex), waveshapeColour.withBrightness (0.1f)); | |||
bitmapProgram->setLED (jmax (x0 - 1, minLedIndex), jmax (y0 - 1, minLedIndex), waveshapeColour.withBrightness (0.1f)); | |||
setLED (jmin (x0 + 1, maxLedIndex), jmin (y0 + 1, maxLedIndex), waveshapeColour.withBrightness (0.1f)); | |||
setLED (jmin (x0 + 1, maxLedIndex), jmax (y0 - 1, minLedIndex), waveshapeColour.withBrightness (0.1f)); | |||
setLED (jmax (x0 - 1, minLedIndex), jmin (y0 + 1, maxLedIndex), waveshapeColour.withBrightness (0.1f)); | |||
setLED (jmax (x0 - 1, minLedIndex), jmax (y0 - 1, minLedIndex), waveshapeColour.withBrightness (0.1f)); | |||
} | |||
/** | |||
enum for the two modes | |||
*/ | |||
enum BlocksSynthMode | |||
{ | |||
waveformSelectionMode = 0, | |||
playMode | |||
}; | |||
BlocksSynthMode currentMode = playMode; | |||
//============================================================================== | |||
Audio audio; | |||
DrumPadGridProgram* gridProgram; | |||
BitmapLEDProgram* bitmapProgram; | |||
DrumPadGridProgram* gridProgram = nullptr; | |||
BitmapLEDProgram* bitmapProgram = nullptr; | |||
SynthGrid layout; | |||
SynthGrid layout { 5, 5 }; | |||
PhysicalTopologySource topologySource; | |||
Block::Ptr activeBlock; | |||
@@ -7,7 +7,7 @@ | |||
/** | |||
Base class for oscillators | |||
*/ | |||
class Oscillator : public SynthesiserVoice | |||
class Oscillator : public SynthesiserVoice | |||
{ | |||
public: | |||
Oscillator() | |||
@@ -16,10 +16,6 @@ public: | |||
phaseIncrement.reset (getSampleRate(), 0.1); | |||
} | |||
virtual ~Oscillator() | |||
{ | |||
} | |||
void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int) override | |||
{ | |||
frequency = MidiMessage::getMidiNoteInHertz (midiNoteNumber); | |||
@@ -57,7 +53,7 @@ public: | |||
void renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples) override | |||
{ | |||
while(--numSamples >= 0) | |||
while (--numSamples >= 0) | |||
{ | |||
double output = getSample() * amplitude.getNextValue(); | |||
@@ -82,22 +78,20 @@ public: | |||
} | |||
/** Subclasses should override this to say whether they can play the given sound */ | |||
virtual bool canPlaySound (SynthesiserSound* sound) override = 0; | |||
virtual bool canPlaySound (SynthesiserSound*) override = 0; | |||
/** Subclasses should override this to render a waveshape */ | |||
virtual double renderWaveShape (const double currentPhase) = 0; | |||
virtual double renderWaveShape (double currentPhase) = 0; | |||
private: | |||
LinearSmoothedValue<double> amplitude; | |||
LinearSmoothedValue<double> phaseIncrement; | |||
LinearSmoothedValue<double> amplitude, phaseIncrement; | |||
double frequency; | |||
double frequency = 0; | |||
double phasePos = 0.0f; | |||
double sampleRate = 44100.0; | |||
int initialNote; | |||
double maxFreq; | |||
double minFreq; | |||
int initialNote = 0; | |||
double maxFreq = 0, minFreq = 0; | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Oscillator) | |||