From 39344e0d849f0a194dca01a7696ee15ba67e2fa7 Mon Sep 17 00:00:00 2001 From: ed Date: Sat, 12 Nov 2016 10:22:36 +0000 Subject: [PATCH 01/10] WaveshapeProgram.h added, LittleFoot program started --- examples/BLOCKS/BlocksSynth/BlocksSynth.jucer | 4 +- .../BLOCKS/BlocksSynth/Source/MainComponent.h | 1 + .../BlocksSynth/Source/WaveshapeProgram.h | 125 ++++++++++++++++++ 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h diff --git a/examples/BLOCKS/BlocksSynth/BlocksSynth.jucer b/examples/BLOCKS/BlocksSynth/BlocksSynth.jucer index 816521b1f0..b64b7c775e 100644 --- a/examples/BLOCKS/BlocksSynth/BlocksSynth.jucer +++ b/examples/BLOCKS/BlocksSynth/BlocksSynth.jucer @@ -9,8 +9,10 @@ - + + diff --git a/examples/BLOCKS/BlocksSynth/Source/MainComponent.h b/examples/BLOCKS/BlocksSynth/Source/MainComponent.h index 9dee7a4274..83e52549e7 100644 --- a/examples/BLOCKS/BlocksSynth/Source/MainComponent.h +++ b/examples/BLOCKS/BlocksSynth/Source/MainComponent.h @@ -4,6 +4,7 @@ #include "../JuceLibraryCode/JuceHeader.h" #include "Audio.h" +#include "WaveshapeProgram.h" //============================================================================== /** diff --git a/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h new file mode 100644 index 0000000000..ec01409ef1 --- /dev/null +++ b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h @@ -0,0 +1,125 @@ +/* + ============================================================================== + + WaveshapeProgram.h + Created: 12 Nov 2016 9:28:17am + Author: Edward Davies + + ============================================================================== +*/ + +#ifndef WAVESHAPEPROGRAM_H_INCLUDED +#define WAVESHAPEPROGRAM_H_INCLUDED + +class WaveshapeProgram : LEDGrid::Program +{ +public: + WaveshapeProgram (LEDGrid& lg) : Program (lg) + { + + } + + void generateWaveshapes() + { + + } + + void setWaveshapeType (uint8 type) + { + ledGrid.setDataByte (0, type); + } + + String getLittleFootProgram() override + { + return R"littlefoot( + + int yOffset + + void repaint() + { + // Clear LEDs to black + fillRect (0xff000000, 0, 0, 15, 15); + + // Get the waveshape type + int type = getHeapByte (0); + int offset = 1 + (type * 180) + yOffset; + + for (int x = 0; x < 15; ++x) + { + // Find the corresponding Y co-ordinate for the current waveshape + int y = getHeapInt (offset); + + // Draw a vertical line if flag is set or draw an LED circle + if (y == -1) + { + for (int i = 0; i < 15; ++i) + drawLEDCircle (x, i); + } + else if (x % 2 == 0) + { + drawLEDCircle (x, y); + } + } + + // Increment the offset to draw a 'moving' waveshape + yOffset += 4; + if (yOffset == (4 * 30)) + yOffset -= (4 * 30); + } + + void drawLEDCircle (int x0, int y0) + { + setLED (x0, y0, 0xffff0000); + + int minLedIndex = 0; + int maxLedIndex = 14; + + setLED (min (x0 + 1, maxLedIndex), y0, 0xff660000); + setLED (max (x0 - 1, minLedIndex), y0, 0xff660000); + setLED (x0, min (y0 + 1, maxLedIndex), 0xff660000); + setLED (x0, max (y0 - 1, minLedIndex), 0xff660000); + + setLED (min (x0 + 1, maxLedIndex), min (y0 + 1, maxLedIndex), 0xff1a0000)); + setLED (min (x0 + 1, maxLedIndex), max (y0 - 1, minLedIndex), 0xff1a0000)); + setLED (max (x0 - 1, minLedIndex), min (y0 + 1, maxLedIndex), 0xff1a0000); + setLED (max (x0 - 1, minLedIndex), max (y0 - 1, minLedIndex), 0xff1a0000); + } + + int min (int a, int b) + { + if (a > b) + return b; + + return a; + } + + int max (int a, int b) + { + if (a > b) + return a; + + return b; + } + + )littlefoot"; + } + + uint32 getHeapSize() override + { + return totalDataSize; + } + +private: + static constexpr uint32 waveshapeType = 0; // 1 byte + static constexpr uint32 sineWaveOffset = 1; // 4 byte * 45 + static constexpr uint32 squareWaveOffset = 181; // 4 byte * 45 + static constexpr uint32 sawWaveOffset = 361; // 4 byte * 45 + static constexpr uint32 triangleWaveOffset = 541; // 4 byte * 45 + + static constexpr uint32 totalDataSize = triangleWaveOffset + (4 * 45); + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WaveshapeProgram) +}; + +#endif // WAVESHAPEPROGRAM_H_INCLUDED From 91e8d27b70b3cc5d6a313ddc2c594784f7b3af64 Mon Sep 17 00:00:00 2001 From: ed Date: Sat, 12 Nov 2016 20:09:20 +0000 Subject: [PATCH 02/10] LED drawing using LittleFoot --- .../BlocksSynth.xcodeproj/project.pbxproj | 4 +- .../VisualStudio2013/BlocksSynth.vcxproj | 1 + .../BlocksSynth.vcxproj.filters | 3 + .../VisualStudio2015/BlocksSynth.vcxproj | 1 + .../BlocksSynth.vcxproj.filters | 3 + .../BLOCKS/BlocksSynth/Source/MainComponent.h | 149 +-------------- .../BlocksSynth/Source/WaveshapeProgram.h | 171 ++++++++++++------ 7 files changed, 138 insertions(+), 194 deletions(-) diff --git a/examples/BLOCKS/BlocksSynth/Builds/MacOSX/BlocksSynth.xcodeproj/project.pbxproj b/examples/BLOCKS/BlocksSynth/Builds/MacOSX/BlocksSynth.xcodeproj/project.pbxproj index c39e263b18..6509ca5160 100644 --- a/examples/BLOCKS/BlocksSynth/Builds/MacOSX/BlocksSynth.xcodeproj/project.pbxproj +++ b/examples/BLOCKS/BlocksSynth/Builds/MacOSX/BlocksSynth.xcodeproj/project.pbxproj @@ -579,6 +579,7 @@ 82C32D94FAED75BF1FBB3FCB = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "setup_8.h"; path = "../../../../../modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_8.h"; sourceTree = "SOURCE_ROOT"; }; 83123049240352B97AEC1FBE = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ListenerList.h"; path = "../../../../../modules/juce_core/containers/juce_ListenerList.h"; sourceTree = "SOURCE_ROOT"; }; 8319007F2F0B140F09E10D2E = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "juce_ios_UIViewComponent.mm"; path = "../../../../../modules/juce_gui_extra/native/juce_ios_UIViewComponent.mm"; sourceTree = "SOURCE_ROOT"; }; + 831D05658D06983B575A395C = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WaveshapeProgram.h; path = ../../Source/WaveshapeProgram.h; sourceTree = "SOURCE_ROOT"; }; 83A8F8A11B1659400C525E81 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_PhysicalTopologySource.cpp"; path = "../../../../../modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.cpp"; sourceTree = "SOURCE_ROOT"; }; 83AFB3E647BB6ED40AA571A8 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "stream_encoder_framing.h"; path = "../../../../../modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder_framing.h"; sourceTree = "SOURCE_ROOT"; }; 8406536DA0B794016B0CC15B = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = jfdctint.c; path = "../../../../../modules/juce_graphics/image_formats/jpglib/jfdctint.c"; sourceTree = "SOURCE_ROOT"; }; @@ -1148,8 +1149,9 @@ 14CD2D0EE13A7D3289EBBC86, ); name = Audio; sourceTree = ""; }; 6FBEB977137848BF3903034E = {isa = PBXGroup; children = ( 23FE51CE68990B49B3B4AABD, + D98771DCE827466B36D51AF8, 1AE75CB0FA61A03429A4E540, - D98771DCE827466B36D51AF8, ); name = Source; sourceTree = ""; }; + 831D05658D06983B575A395C, ); name = Source; sourceTree = ""; }; 38998921858495104F35872D = {isa = PBXGroup; children = ( 6FBEB977137848BF3903034E, ); name = BlocksSynth; sourceTree = ""; }; 093A7AEDD681B83D66FC731D = {isa = PBXGroup; children = ( diff --git a/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2013/BlocksSynth.vcxproj b/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2013/BlocksSynth.vcxproj index 8fc7b21b72..3dfaa2cf69 100644 --- a/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2013/BlocksSynth.vcxproj +++ b/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2013/BlocksSynth.vcxproj @@ -1595,6 +1595,7 @@ + diff --git a/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2013/BlocksSynth.vcxproj.filters b/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2013/BlocksSynth.vcxproj.filters index 9e2bffc631..bc3a26218e 100644 --- a/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2013/BlocksSynth.vcxproj.filters +++ b/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2013/BlocksSynth.vcxproj.filters @@ -1920,6 +1920,9 @@ BlocksSynth\Source + + BlocksSynth\Source + Juce Modules\juce_audio_basics\buffers diff --git a/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2015/BlocksSynth.vcxproj b/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2015/BlocksSynth.vcxproj index 06f5a40c57..b01e0d7aa8 100644 --- a/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2015/BlocksSynth.vcxproj +++ b/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2015/BlocksSynth.vcxproj @@ -1595,6 +1595,7 @@ + diff --git a/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2015/BlocksSynth.vcxproj.filters b/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2015/BlocksSynth.vcxproj.filters index 2976110903..d4b62611a8 100644 --- a/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2015/BlocksSynth.vcxproj.filters +++ b/examples/BLOCKS/BlocksSynth/Builds/VisualStudio2015/BlocksSynth.vcxproj.filters @@ -1920,6 +1920,9 @@ BlocksSynth\Source + + BlocksSynth\Source + Juce Modules\juce_audio_basics\buffers diff --git a/examples/BLOCKS/BlocksSynth/Source/MainComponent.h b/examples/BLOCKS/BlocksSynth/Source/MainComponent.h index 83e52549e7..370f1a41d0 100644 --- a/examples/BLOCKS/BlocksSynth/Source/MainComponent.h +++ b/examples/BLOCKS/BlocksSynth/Source/MainComponent.h @@ -73,8 +73,7 @@ struct SynthGrid class MainComponent : public Component, public TopologySource::Listener, private TouchSurface::Listener, - private ControlButton::Listener, - private Timer + private ControlButton::Listener { public: MainComponent() @@ -83,8 +82,6 @@ public: // Register MainContentComponent as a listener to the PhysicalTopologySource object topologySource.addListener (this); - - generateWaveshapes(); }; ~MainComponent() @@ -154,6 +151,8 @@ private: if (waveshapeMode > 3) waveshapeMode = 0; + + waveshapeProgram->setWaveshapeType (waveshapeMode); } else if (currentMode == playMode) { @@ -215,48 +214,6 @@ private: setLEDProgram (activeBlock->getLEDGrid()); } - void timerCallback() override - { - // Clear all LEDs - for (uint32 x = 0; x < 15; ++x) - for (uint32 y = 0; y < 15; ++y) - 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; - } - - // For each X co-ordinate - for (uint32 x = 0; x < 15; ++x) - { - // Find the corresponding Y co-ordinate for the current waveshape - int y = waveshapeY[x + yOffset]; - - // Draw a vertical line if flag is set or draw an LED circle - if (y == -1) - { - for (uint32 i = 0; i < 15; ++i) - drawLEDCircle (x, i); - } - else if (x % 2 == 0) - { - drawLEDCircle (x, static_cast (y)); - } - } - - // Increment the offset to draw a 'moving' waveshape - if (++yOffset == 30) - yOffset -= 30; - } - /** Clears the old touch times */ void clearOldTouchTimes (const Time now) { @@ -283,19 +240,13 @@ private: if (currentMode == waveformSelectionMode) { // Create a new BitmapLEDProgram for the LEDGrid - bitmapProgram = new BitmapLEDProgram (*grid); + waveshapeProgram = new WaveshapeProgram (*grid, waveshapeMode); // Set the LEDGrid program - grid->setProgram (bitmapProgram); - - // Redraw at 25Hz - startTimerHz (25); + grid->setProgram (waveshapeProgram); } else if (currentMode == playMode) { - // Stop the redraw timer - stopTimer(); - // Create a new DrumPadGridProgram for the LEDGrid gridProgram = new DrumPadGridProgram (*grid); @@ -309,84 +260,6 @@ private: } } - /** Generates the X and Y co-ordiantes for 1.5 cycles of each of the 4 waveshapes and stores them in arrays */ - void generateWaveshapes() - { - // Set current phase position to 0 and work out the required phase increment for one cycle - double currentPhase = 0.0; - double phaseInc = (1.0 / 30.0) * (2.0 * double_Pi); - - for (int x = 0; x < 30; ++x) - { - // Scale and offset the sin output to the Lightpad display - double sineOutput = sin (currentPhase); - sineWaveY[x] = roundToInt ((sineOutput * 6.5) + 7.0); - - // Square wave output, set flags for when vertical line should be drawn - if (currentPhase < double_Pi) - { - if (x == 0) - squareWaveY[x] = -1; - else - squareWaveY[x] = 1; - } - else - { - if (squareWaveY[x - 1] == 1) - squareWaveY[x - 1] = -1; - - 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] != -1) - sawWaveY[x] = -1; - - // Triangle wave output - triangleWaveY[x] = x < 15 ? x : 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; - } - } - - /** 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) - { - setLED (x0, y0, waveshapeColour); - - const uint32 minLedIndex = 0; - const uint32 maxLedIndex = 14; - - 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)); - - 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 BlocksSynthMode { waveformSelectionMode = 0, @@ -398,8 +271,8 @@ private: //============================================================================== Audio audio; - DrumPadGridProgram* gridProgram = nullptr; - BitmapLEDProgram* bitmapProgram = nullptr; + DrumPadGridProgram* gridProgram = nullptr; + WaveshapeProgram* waveshapeProgram = nullptr; SynthGrid layout { 5, 5 }; PhysicalTopologySource topologySource; @@ -409,14 +282,8 @@ private: Colour waveshapeColour = Colours::red; - int sineWaveY[45]; - int squareWaveY[45]; - int sawWaveY[45]; - int triangleWaveY[45]; - int waveshapeMode = 0; - uint32 yOffset = 0; - + float scaleX = 0.0; float scaleY = 0.0; diff --git a/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h index ec01409ef1..7c243b2569 100644 --- a/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h +++ b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h @@ -11,46 +11,147 @@ #ifndef WAVESHAPEPROGRAM_H_INCLUDED #define WAVESHAPEPROGRAM_H_INCLUDED -class WaveshapeProgram : LEDGrid::Program +class WaveshapeProgram : public LEDGrid::Program { public: - WaveshapeProgram (LEDGrid& lg) : Program (lg) + WaveshapeProgram (LEDGrid& lg, int waveshape) : Program (lg) { - + setWaveshapeType (waveshape); + generateWaveshapes(); } void generateWaveshapes() { - + int sineWaveY[45]; + int squareWaveY[45]; + int sawWaveY[45]; + int triangleWaveY[45]; + + // Set current phase position to 0 and work out the required phase increment for one cycle + double currentPhase = 0.0; + double phaseInc = (1.0 / 30.0) * (2.0 * double_Pi); + + for (int x = 0; x < 30; ++x) + { + // Scale and offset the sin output to the Lightpad display + double sineOutput = sin (currentPhase); + sineWaveY[x] = roundToInt ((sineOutput * 6.5) + 7.0); + + // Square wave output, set flags for when vertical line should be drawn + if (currentPhase < double_Pi) + { + if (x == 0) + squareWaveY[x] = 20; + else + squareWaveY[x] = 1; + } + else + { + if (squareWaveY[x - 1] == 1) + squareWaveY[x - 1] = 20; + + 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] != 20) + sawWaveY[x] = 20; + + // Triangle wave output + triangleWaveY[x] = x < 15 ? x : 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; + } + + for (int i = 0; i < 45; ++i) + { + ledGrid.setDataBits ((sineWaveOffset * 8) + (32 * i), 32, sineWaveY[i]); + jassert (ledGrid.getDataByte (sineWaveOffset + (i * 4)) == sineWaveY[i]); + + ledGrid.setDataBits ((squareWaveOffset * 8) + (32 * i), 32, squareWaveY[i]); + jassert (ledGrid.getDataByte (squareWaveOffset + (i * 4)) == squareWaveY[i]); + + ledGrid.setDataBits ((sawWaveOffset * 8) + (32 * i), 32, sawWaveY[i]); + jassert (ledGrid.getDataByte (sawWaveOffset + (i * 4)) == sawWaveY[i]); + + ledGrid.setDataBits ((triangleWaveOffset * 8) + (32 * i), 32, triangleWaveY[i]); + jassert (ledGrid.getDataByte (triangleWaveOffset + (i * 4)) == triangleWaveY[i]); + } } - - void setWaveshapeType (uint8 type) + + void setWaveshapeType (int type) { - ledGrid.setDataByte (0, type); + ledGrid.setDataBits (0, 4, type); } String getLittleFootProgram() override { return R"littlefoot( - int yOffset + int yOffset; + + int min (int a, int b) + { + if (a > b) + return b; + + return a; + } + + int max (int a, int b) + { + if (a > b) + return a; + + return b; + } + + void drawLEDCircle (int x0, int y0) + { + setLED (x0, y0, 0xffffffff); + + int minLedIndex = 0; + int maxLedIndex = 14; + setLED (min (x0 + 1, maxLedIndex), y0, 0xff660000); + setLED (max (x0 - 1, minLedIndex), y0, 0xff660000); + setLED (x0, min (y0 + 1, maxLedIndex), 0xff660000); + setLED (x0, max (y0 - 1, minLedIndex), 0xff660000); + + setLED (min (x0 + 1, maxLedIndex), min (y0 + 1, maxLedIndex), 0xff1a0000); + setLED (min (x0 + 1, maxLedIndex), max (y0 - 1, minLedIndex), 0xff1a0000); + setLED (max (x0 - 1, minLedIndex), min (y0 + 1, maxLedIndex), 0xff1a0000); + setLED (max (x0 - 1, minLedIndex), max (y0 - 1, minLedIndex), 0xff1a0000); + } + void repaint() { // Clear LEDs to black fillRect (0xff000000, 0, 0, 15, 15); // Get the waveshape type - int type = getHeapByte (0); - int offset = 1 + (type * 180) + yOffset; - + int type = getHeapInt (0); + int offset = 4 + (type * 180) + yOffset; + for (int x = 0; x < 15; ++x) { // Find the corresponding Y co-ordinate for the current waveshape - int y = getHeapInt (offset); + int y = getHeapInt (offset + (x * 4)); // Draw a vertical line if flag is set or draw an LED circle - if (y == -1) + if (y == 20) { for (int i = 0; i < 15; ++i) drawLEDCircle (x, i); @@ -67,40 +168,6 @@ public: yOffset -= (4 * 30); } - void drawLEDCircle (int x0, int y0) - { - setLED (x0, y0, 0xffff0000); - - int minLedIndex = 0; - int maxLedIndex = 14; - - setLED (min (x0 + 1, maxLedIndex), y0, 0xff660000); - setLED (max (x0 - 1, minLedIndex), y0, 0xff660000); - setLED (x0, min (y0 + 1, maxLedIndex), 0xff660000); - setLED (x0, max (y0 - 1, minLedIndex), 0xff660000); - - setLED (min (x0 + 1, maxLedIndex), min (y0 + 1, maxLedIndex), 0xff1a0000)); - setLED (min (x0 + 1, maxLedIndex), max (y0 - 1, minLedIndex), 0xff1a0000)); - setLED (max (x0 - 1, minLedIndex), min (y0 + 1, maxLedIndex), 0xff1a0000); - setLED (max (x0 - 1, minLedIndex), max (y0 - 1, minLedIndex), 0xff1a0000); - } - - int min (int a, int b) - { - if (a > b) - return b; - - return a; - } - - int max (int a, int b) - { - if (a > b) - return a; - - return b; - } - )littlefoot"; } @@ -110,11 +177,11 @@ public: } private: - static constexpr uint32 waveshapeType = 0; // 1 byte - static constexpr uint32 sineWaveOffset = 1; // 4 byte * 45 - static constexpr uint32 squareWaveOffset = 181; // 4 byte * 45 - static constexpr uint32 sawWaveOffset = 361; // 4 byte * 45 - static constexpr uint32 triangleWaveOffset = 541; // 4 byte * 45 + static constexpr uint32 waveshapeType = 0; // 4 bytes + static constexpr uint32 sineWaveOffset = 4; // 4 byte * 45 + static constexpr uint32 squareWaveOffset = 184; // 4 byte * 45 + static constexpr uint32 sawWaveOffset = 364; // 4 byte * 45 + static constexpr uint32 triangleWaveOffset = 544; // 4 byte * 45 static constexpr uint32 totalDataSize = triangleWaveOffset + (4 * 45); From e5d677072b2b14898b7112f7379c9e51ddde0acd Mon Sep 17 00:00:00 2001 From: ed Date: Sun, 13 Nov 2016 19:14:20 +0000 Subject: [PATCH 03/10] Working LittleFoot program for waveshape drawing --- .../BLOCKS/BlocksSynth/Source/MainComponent.h | 24 +++- .../BlocksSynth/Source/WaveshapeProgram.h | 118 +++++++++--------- 2 files changed, 78 insertions(+), 64 deletions(-) diff --git a/examples/BLOCKS/BlocksSynth/Source/MainComponent.h b/examples/BLOCKS/BlocksSynth/Source/MainComponent.h index 370f1a41d0..482b47e1f3 100644 --- a/examples/BLOCKS/BlocksSynth/Source/MainComponent.h +++ b/examples/BLOCKS/BlocksSynth/Source/MainComponent.h @@ -73,7 +73,8 @@ struct SynthGrid class MainComponent : public Component, public TopologySource::Listener, private TouchSurface::Listener, - private ControlButton::Listener + private ControlButton::Listener, + private Timer { public: MainComponent() @@ -144,15 +145,18 @@ 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) + if (currentMode == waveformSelectionMode && touch.isTouchStart && allowTouch) { // Change the displayed waveshape to the next one ++waveshapeMode; if (waveshapeMode > 3) waveshapeMode = 0; - + waveshapeProgram->setWaveshapeType (waveshapeMode); + + allowTouch = false; + startTimer (500); } else if (currentMode == playMode) { @@ -240,10 +244,13 @@ private: if (currentMode == waveformSelectionMode) { // Create a new BitmapLEDProgram for the LEDGrid - waveshapeProgram = new WaveshapeProgram (*grid, waveshapeMode); + waveshapeProgram = new WaveshapeProgram (*grid); // Set the LEDGrid program grid->setProgram (waveshapeProgram); + + waveshapeProgram->setWaveshapeType (waveshapeMode); + waveshapeProgram->generateWaveshapes(); } else if (currentMode == playMode) { @@ -260,6 +267,11 @@ private: } } + void timerCallback() override + { + allowTouch = true; + } + enum BlocksSynthMode { waveformSelectionMode = 0, @@ -283,10 +295,12 @@ private: Colour waveshapeColour = Colours::red; int waveshapeMode = 0; - + float scaleX = 0.0; float scaleY = 0.0; + bool allowTouch = true; + //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent) }; diff --git a/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h index 7c243b2569..457def258e 100644 --- a/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h +++ b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h @@ -14,29 +14,27 @@ class WaveshapeProgram : public LEDGrid::Program { public: - WaveshapeProgram (LEDGrid& lg, int waveshape) : Program (lg) + WaveshapeProgram (LEDGrid& lg) : Program (lg) { - setWaveshapeType (waveshape); - generateWaveshapes(); } void generateWaveshapes() { - int sineWaveY[45]; - int squareWaveY[45]; - int sawWaveY[45]; - int triangleWaveY[45]; - + 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 double currentPhase = 0.0; double phaseInc = (1.0 / 30.0) * (2.0 * double_Pi); - + for (int x = 0; x < 30; ++x) { // Scale and offset the sin output to the Lightpad display double sineOutput = sin (currentPhase); sineWaveY[x] = roundToInt ((sineOutput * 6.5) + 7.0); - + // Square wave output, set flags for when vertical line should be drawn if (currentPhase < double_Pi) { @@ -49,19 +47,19 @@ public: { if (squareWaveY[x - 1] == 1) squareWaveY[x - 1] = 20; - + 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] != 20) sawWaveY[x] = 20; - + // Triangle wave output triangleWaveY[x] = x < 15 ? x : 14 - (x % 15); - + // Add half cycle to end of array so it loops correctly if (x < 15) { @@ -70,30 +68,39 @@ public: sawWaveY[x + 30] = sawWaveY[x]; triangleWaveY[x + 30] = triangleWaveY[x]; } - + // Increment the current phase currentPhase += phaseInc; } - + for (int i = 0; i < 45; ++i) { - ledGrid.setDataBits ((sineWaveOffset * 8) + (32 * i), 32, sineWaveY[i]); - jassert (ledGrid.getDataByte (sineWaveOffset + (i * 4)) == sineWaveY[i]); - - ledGrid.setDataBits ((squareWaveOffset * 8) + (32 * i), 32, squareWaveY[i]); - jassert (ledGrid.getDataByte (squareWaveOffset + (i * 4)) == squareWaveY[i]); - - ledGrid.setDataBits ((sawWaveOffset * 8) + (32 * i), 32, sawWaveY[i]); - jassert (ledGrid.getDataByte (sawWaveOffset + (i * 4)) == sawWaveY[i]); - - ledGrid.setDataBits ((triangleWaveOffset * 8) + (32 * i), 32, triangleWaveY[i]); - jassert (ledGrid.getDataByte (triangleWaveOffset + (i * 4)) == triangleWaveY[i]); + ledGrid.setDataByte (sineWaveOffset + i, sineWaveY[i]); + int sineByte = ledGrid.getDataByte (sineWaveOffset + i); + jassert (sineByte == sineWaveY[i]); + + ledGrid.setDataByte (squareWaveOffset + i, squareWaveY[i]); + int squareByte = ledGrid.getDataByte (squareWaveOffset + i); + jassert (squareByte == squareWaveY[i]); + + ledGrid.setDataByte (sawWaveOffset + i, sawWaveY[i]); + int sawByte = ledGrid.getDataByte (sawWaveOffset + i); + jassert (sawByte == sawWaveY[i]); + + ledGrid.setDataByte (triangleWaveOffset + i, triangleWaveY[i]); + int triangleByte = ledGrid.getDataByte (triangleWaveOffset + i); + jassert (triangleByte == triangleWaveY[i]); } } - - void setWaveshapeType (int type) + + void setWaveshapeType (uint8 type) { - ledGrid.setDataBits (0, 4, type); + ledGrid.setDataByte (0, type); + } + + uint32 getHeapSize() override + { + return totalDataSize; } String getLittleFootProgram() override @@ -101,27 +108,27 @@ public: return R"littlefoot( int yOffset; - + int min (int a, int b) { if (a > b) return b; - + return a; } - + int max (int a, int b) { if (a > b) return a; - + return b; } - + void drawLEDCircle (int x0, int y0) { - setLED (x0, y0, 0xffffffff); - + setLED (x0, y0, 0xffff0000); + int minLedIndex = 0; int maxLedIndex = 14; @@ -135,20 +142,19 @@ public: setLED (max (x0 - 1, minLedIndex), min (y0 + 1, maxLedIndex), 0xff1a0000); setLED (max (x0 - 1, minLedIndex), max (y0 - 1, minLedIndex), 0xff1a0000); } - + void repaint() { // Clear LEDs to black fillRect (0xff000000, 0, 0, 15, 15); // Get the waveshape type - int type = getHeapInt (0); - int offset = 4 + (type * 180) + yOffset; - + int type = getHeapByte (0); + int offset = 1 + (type * 45) + yOffset; + for (int x = 0; x < 15; ++x) { - // Find the corresponding Y co-ordinate for the current waveshape - int y = getHeapInt (offset + (x * 4)); + int y = getHeapByte (offset + x); // Draw a vertical line if flag is set or draw an LED circle if (y == 20) @@ -162,28 +168,22 @@ public: } } - // Increment the offset to draw a 'moving' waveshape - yOffset += 4; - if (yOffset == (4 * 30)) - yOffset -= (4 * 30); + if (++yOffset == 30) + yOffset = 0; + } )littlefoot"; } - uint32 getHeapSize() override - { - return totalDataSize; - } - private: - static constexpr uint32 waveshapeType = 0; // 4 bytes - static constexpr uint32 sineWaveOffset = 4; // 4 byte * 45 - static constexpr uint32 squareWaveOffset = 184; // 4 byte * 45 - static constexpr uint32 sawWaveOffset = 364; // 4 byte * 45 - static constexpr uint32 triangleWaveOffset = 544; // 4 byte * 45 + 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 - static constexpr uint32 totalDataSize = triangleWaveOffset + (4 * 45); + static constexpr uint32 totalDataSize = triangleWaveOffset + 45; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WaveshapeProgram) From b2d0328f732534bcef6baa0d4bc52734c666efa3 Mon Sep 17 00:00:00 2001 From: ed Date: Mon, 14 Nov 2016 10:06:48 +0000 Subject: [PATCH 04/10] Code cleanup & WaveshapeProgram documentation --- .../BLOCKS/BlocksSynth/Source/MainComponent.h | 24 +++------ .../BlocksSynth/Source/WaveshapeProgram.h | 50 +++++++++---------- 2 files changed, 30 insertions(+), 44 deletions(-) diff --git a/examples/BLOCKS/BlocksSynth/Source/MainComponent.h b/examples/BLOCKS/BlocksSynth/Source/MainComponent.h index 482b47e1f3..68cd2ec22e 100644 --- a/examples/BLOCKS/BlocksSynth/Source/MainComponent.h +++ b/examples/BLOCKS/BlocksSynth/Source/MainComponent.h @@ -57,10 +57,10 @@ struct SynthGrid Array gridFillArray; Colour baseGridColour = Colours::green; - Colour touchColour = Colours::cyan; + Colour touchColour = Colours::red; Array tonics = { 4, 12, 20 }; - Array notes = { 1, 3, 6, 7, 9, 11, 14, 15, 17, 19, 22, 24 }; + Array notes = { 1, 3, 6, 7, 9, 11, 14, 15, 17, 19, 22, 24 }; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SynthGrid) @@ -73,8 +73,7 @@ struct SynthGrid class MainComponent : public Component, public TopologySource::Listener, private TouchSurface::Listener, - private ControlButton::Listener, - private Timer + private ControlButton::Listener { public: MainComponent() @@ -145,7 +144,7 @@ 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 (currentMode == waveformSelectionMode && touch.isTouchStart) { // Change the displayed waveshape to the next one ++waveshapeMode; @@ -154,9 +153,6 @@ private: waveshapeMode = 0; waveshapeProgram->setWaveshapeType (waveshapeMode); - - allowTouch = false; - startTimer (500); } else if (currentMode == playMode) { @@ -243,12 +239,13 @@ private: { if (currentMode == waveformSelectionMode) { - // Create a new BitmapLEDProgram for the LEDGrid + // Create a new WaveshapeProgram for the LEDGrid waveshapeProgram = new WaveshapeProgram (*grid); // Set the LEDGrid program grid->setProgram (waveshapeProgram); + // Initialise the program waveshapeProgram->setWaveshapeType (waveshapeMode); waveshapeProgram->generateWaveshapes(); } @@ -267,11 +264,6 @@ private: } } - void timerCallback() override - { - allowTouch = true; - } - enum BlocksSynthMode { waveformSelectionMode = 0, @@ -292,15 +284,11 @@ private: Array touchMessageTimesInLastSecond; - Colour waveshapeColour = Colours::red; - int waveshapeMode = 0; float scaleX = 0.0; float scaleY = 0.0; - bool allowTouch = true; - //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent) }; diff --git a/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h index 457def258e..3f0b98e4c8 100644 --- a/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h +++ b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h @@ -14,10 +14,16 @@ class WaveshapeProgram : public LEDGrid::Program { public: - WaveshapeProgram (LEDGrid& lg) : Program (lg) + WaveshapeProgram (LEDGrid& lg) : Program (lg) {} + + /** Sets the waveshape type to display on the grid */ + void setWaveshapeType (uint8 type) { + ledGrid.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]; @@ -39,14 +45,14 @@ public: if (currentPhase < double_Pi) { if (x == 0) - squareWaveY[x] = 20; + squareWaveY[x] = 255; else squareWaveY[x] = 1; } else { if (squareWaveY[x - 1] == 1) - squareWaveY[x - 1] = 20; + squareWaveY[x - 1] = 255; squareWaveY[x] = 13; } @@ -54,8 +60,8 @@ public: // 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] != 20) - sawWaveY[x] = 20; + if (sawWaveY[x] == 0 && sawWaveY[x - 1] != 255) + sawWaveY[x] = 255; // Triangle wave output triangleWaveY[x] = x < 15 ? x : 14 - (x % 15); @@ -73,31 +79,16 @@ public: currentPhase += phaseInc; } + // Store the values for each of the waveshapes at the correct offsets in the shared data heap for (int i = 0; i < 45; ++i) { - ledGrid.setDataByte (sineWaveOffset + i, sineWaveY[i]); - int sineByte = ledGrid.getDataByte (sineWaveOffset + i); - jassert (sineByte == sineWaveY[i]); - - ledGrid.setDataByte (squareWaveOffset + i, squareWaveY[i]); - int squareByte = ledGrid.getDataByte (squareWaveOffset + i); - jassert (squareByte == squareWaveY[i]); - - ledGrid.setDataByte (sawWaveOffset + i, sawWaveY[i]); - int sawByte = ledGrid.getDataByte (sawWaveOffset + i); - jassert (sawByte == sawWaveY[i]); - + ledGrid.setDataByte (sineWaveOffset + i, sineWaveY[i]); + ledGrid.setDataByte (squareWaveOffset + i, squareWaveY[i]); + ledGrid.setDataByte (sawWaveOffset + i, sawWaveY[i]); ledGrid.setDataByte (triangleWaveOffset + i, triangleWaveY[i]); - int triangleByte = ledGrid.getDataByte (triangleWaveOffset + i); - jassert (triangleByte == triangleWaveY[i]); } } - void setWaveshapeType (uint8 type) - { - ledGrid.setDataByte (0, type); - } - uint32 getHeapSize() override { return totalDataSize; @@ -150,14 +141,17 @@ public: // 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 == 20) + if (y == 255) { for (int i = 0; i < 15; ++i) drawLEDCircle (x, i); @@ -168,15 +162,19 @@ public: } } + // 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 From 8c55f73f45aadaaa9edeae4c08a83d37a0ad81cd Mon Sep 17 00:00:00 2001 From: ed Date: Mon, 14 Nov 2016 10:26:00 +0000 Subject: [PATCH 05/10] MSVC warnings --- examples/BLOCKS/BlocksSynth/Source/MainComponent.h | 4 ++-- examples/BLOCKS/BlocksSynth/Source/Oscillators.h | 2 +- examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/BLOCKS/BlocksSynth/Source/MainComponent.h b/examples/BLOCKS/BlocksSynth/Source/MainComponent.h index 68cd2ec22e..fe9cfb29a2 100644 --- a/examples/BLOCKS/BlocksSynth/Source/MainComponent.h +++ b/examples/BLOCKS/BlocksSynth/Source/MainComponent.h @@ -152,7 +152,7 @@ private: if (waveshapeMode > 3) waveshapeMode = 0; - waveshapeProgram->setWaveshapeType (waveshapeMode); + waveshapeProgram->setWaveshapeType (static_cast (waveshapeMode)); } else if (currentMode == playMode) { @@ -246,7 +246,7 @@ private: grid->setProgram (waveshapeProgram); // Initialise the program - waveshapeProgram->setWaveshapeType (waveshapeMode); + waveshapeProgram->setWaveshapeType (static_cast (waveshapeMode)); waveshapeProgram->generateWaveshapes(); } else if (currentMode == playMode) diff --git a/examples/BLOCKS/BlocksSynth/Source/Oscillators.h b/examples/BLOCKS/BlocksSynth/Source/Oscillators.h index 5e8c4694dd..f068ac86a4 100644 --- a/examples/BLOCKS/BlocksSynth/Source/Oscillators.h +++ b/examples/BLOCKS/BlocksSynth/Source/Oscillators.h @@ -81,7 +81,7 @@ public: virtual bool canPlaySound (SynthesiserSound*) override = 0; /** Subclasses should override this to render a waveshape */ - virtual double renderWaveShape (double currentPhase) = 0; + virtual double renderWaveShape (const double currentPhase) = 0; private: LinearSmoothedValue amplitude, phaseIncrement; diff --git a/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h index 3f0b98e4c8..4bd37e0f7b 100644 --- a/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h +++ b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h @@ -39,7 +39,7 @@ public: { // Scale and offset the sin output to the Lightpad display double sineOutput = sin (currentPhase); - sineWaveY[x] = roundToInt ((sineOutput * 6.5) + 7.0); + sineWaveY[x] = static_cast (roundToInt ((sineOutput * 6.5) + 7.0)); // Square wave output, set flags for when vertical line should be drawn if (currentPhase < double_Pi) @@ -64,7 +64,7 @@ public: sawWaveY[x] = 255; // Triangle wave output - triangleWaveY[x] = x < 15 ? x : 14 - (x % 15); + triangleWaveY[x] = x < 15 ? static_cast (x) : static_cast (14 - (x % 15)); // Add half cycle to end of array so it loops correctly if (x < 15) From 15cab580e2d25f8ec2d520f20c6f16c6743229d8 Mon Sep 17 00:00:00 2001 From: ed Date: Mon, 14 Nov 2016 10:35:14 +0000 Subject: [PATCH 06/10] Xcode strict warnings --- examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h index 4bd37e0f7b..41ba2042cd 100644 --- a/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h +++ b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h @@ -80,7 +80,7 @@ public: } // Store the values for each of the waveshapes at the correct offsets in the shared data heap - for (int i = 0; i < 45; ++i) + for (uint8 i = 0; i < 45; ++i) { ledGrid.setDataByte (sineWaveOffset + i, sineWaveY[i]); ledGrid.setDataByte (squareWaveOffset + i, squareWaveY[i]); From 24364ec43cd2bbe88821defb03172442c58c33e0 Mon Sep 17 00:00:00 2001 From: ed Date: Mon, 14 Nov 2016 10:58:42 +0000 Subject: [PATCH 07/10] auto-generated comment removed from top of WaveshapeProgram.h --- examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h index 41ba2042cd..074b00b74e 100644 --- a/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h +++ b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h @@ -1,12 +1,3 @@ -/* - ============================================================================== - - WaveshapeProgram.h - Created: 12 Nov 2016 9:28:17am - Author: Edward Davies - - ============================================================================== -*/ #ifndef WAVESHAPEPROGRAM_H_INCLUDED #define WAVESHAPEPROGRAM_H_INCLUDED From 1cc8a9532e3067f0f1512bf13687a9ad7d7ee44e Mon Sep 17 00:00:00 2001 From: ed Date: Mon, 14 Nov 2016 11:30:57 +0000 Subject: [PATCH 08/10] Class comment added to top of WaveshapeProgram.h --- examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h index 074b00b74e..b907966221 100644 --- a/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h +++ b/examples/BLOCKS/BlocksSynth/Source/WaveshapeProgram.h @@ -2,6 +2,9 @@ #ifndef WAVESHAPEPROGRAM_H_INCLUDED #define WAVESHAPEPROGRAM_H_INCLUDED +/** + A Program to draw moving waveshapes onto the LEDGrid +*/ class WaveshapeProgram : public LEDGrid::Program { public: From 020f858aba919a5c479b63a5bfa403a9d960a35f Mon Sep 17 00:00:00 2001 From: ed Date: Mon, 14 Nov 2016 12:41:39 +0000 Subject: [PATCH 09/10] Doxygen updated for BlocksSynth tutorial and LittleFoot Language pages --- extras/BLOCKS/doxygen/pages/example_blocks_synth.dox | 8 +++++--- extras/BLOCKS/doxygen/pages/the_littlefoot_language.dox | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/extras/BLOCKS/doxygen/pages/example_blocks_synth.dox b/extras/BLOCKS/doxygen/pages/example_blocks_synth.dox index cd6934e0e4..0e29eeb328 100644 --- a/extras/BLOCKS/doxygen/pages/example_blocks_synth.dox +++ b/extras/BLOCKS/doxygen/pages/example_blocks_synth.dox @@ -9,7 +9,7 @@ BlocksSynth is a JUCE application that turns your Lightpad into a simple monopho Navigate to the JUCE/examples/BLOCKS/BlocksSynth/Builds/ directory and open the code project in your IDE of choice. Run the application and connect your Lightpad (if you do not know how to do this, see @ref connecting_blocks) - it should now display a simple 5x5 grid where each pad plays a note in the chromatic scale using a sine wave starting from the bottom-left (C3). It is possible to play any of the 25 notes but for ease of use tonics (the root note of the scale) are highlighted in white and notes in the C-major scale are highlighted in green. When a note has been played it is possible to change the amplitude using touch pressure and to pitch bend between adjacent notes by sliding left and right. Pressing the mode button on the Lightpad will change to the waveshape selection screen where the currently selected waveshape is rendered on the LEDs and you can switch between the 4 different waveshapes by touching anywhere on the %Block surface. -The concept of a BLOCKS topology and the methods for receiving callbacks from the Block object are covered in the @ref example_blocks_monitor example and the basic methods for displaying grids and setting LEDs on the %Block are covered in the @ref example_blocks_drawing example. This example will cover how to render more complex displays on the LEDGrid and how to do some simple audio synthesis using data from the Lightpad. +The concept of a BLOCKS topology and the methods for receiving callbacks from the Block object are covered in the @ref example_blocks_monitor example and the basic methods for displaying grids and setting LEDs on the %Block are covered in the @ref example_blocks_drawing example. This example will cover how to render custom programs on the LEDGrid using the Littlefoot language and how to do some simple audio synthesis using data from the Lightpad. @section blocks_synth_note_grid Note Grid @@ -19,7 +19,9 @@ In the synthesiser mode the Lightpad displays a 5x5 grid constructed using the D @section blocks_synth_waveshape_display Waveshape Display -In the waveshape selection mode the LEDGrid::Program is set to an instance of BitmapLEDProgram and uses a Timer to draw moving waveshapes onto the LEDs of the Lightpad. In the constructor of MainComponent the MainComponent::generateWaveshapes() method is called - this function generates 45 Y coordinates scaled and offset to fit the LED grid of the Lightpad for each of the 4 waveshapes and stores them in 4 separate arrays: sineWaveY, squareWaveY, sawWaveY and triangleWaveY. Then in the MainComponent::timerCallback() method, a for loop iterates over the 15 LEDs on the X-axis and draws an LED 'circle' using the MainComponent::drawLEDCircle() method at the corresponding Y coordinate for the selected waveshape. The read position of the array is offset using the yOffset variable which is incremented each timer callback and wraps back around when the end of the array is reached to draw a 'moving' waveshape. +In the waveshape selection mode the LEDGrid::Program is set to an instance of the WaveshapeProgram class, which is contained in the WaveshapeProgram.h file. This class inherits from %LEDGrid::Program so that it can be loaded onto the %LEDGrid and its LittleFoot program can be executed on the Lightpad. The class itself is relatively simple and contains a method to set which waveshape should be displayed, a method to load the coordinates for each of the four waveshapes into the heap and two pure virtual methods overridden from %LEDGrid::Program - LEDGrid::Program::getLittleFootProgram() and LEDGrid::Program::getHeapSize(). The heap is the area of shared memory that is used by the program to communicate with the host computer and the size of this memory is set using the getHeapSize() method. In the private section of WaveshapeProgram the structure of the shared data heap is laid out with variables containing the offsets for each section and the totalDataSize variable contains the total size (in bytes) that is required and is returned by the WaveshapeProgram::getHeapSize() method. The heap contains space for a variable that determines which waveshape type to display and the Y coordinates for 1.5 cycles of each of the four waveshapes. + +The WaveshapeProgram::getLittleFootProgram() method returns the LittleFoot program that will be executed on the BLOCKS device. The repaint() method of this program is called at approximately 25Hz and is used to draw the moving waveshape on the LEDs of the Lightpad. Each time this method is called, it clears the LEDs by setting them all to black then calculates the heap offset based on the waveshape type that has been set and uses a for loop to iterate over the 15 LEDs on the X-axis and draw an LED 'circle' using the drawLEDCircle() method at the corresponding Y coordinate for the selected waveshape. The read position of the heap is offset using the yOffset variable which is incremented each repaint() call and wraps back around when the end of the heap section for the selected waveshape is reached to draw a 'moving' waveshape. \image html BlocksSynth_waveshape.gif "A sine wave dispayed in the waveshape selection mode" @@ -30,6 +32,6 @@ The Oscillators.h file contains the waveshape rendering code. It contai @section blocks_synth_summary Summary -This tutorial and the accompanying code project have expanded on the topics covered by previous tutorials, showing you how to display more complex programs on the %LEDGrid and how to control simple audio synthesis parameters using the Lightpad. +This tutorial and the accompanying code project have expanded on the topics covered by previous tutorials, showing you how to display more complex, custom programs on the %LEDGrid using the LittleFoot language and how to control simple audio synthesis parameters using the Lightpad. */ diff --git a/extras/BLOCKS/doxygen/pages/the_littlefoot_language.dox b/extras/BLOCKS/doxygen/pages/the_littlefoot_language.dox index 83edfcd6e7..4e115eb8ba 100644 --- a/extras/BLOCKS/doxygen/pages/the_littlefoot_language.dox +++ b/extras/BLOCKS/doxygen/pages/the_littlefoot_language.dox @@ -19,6 +19,6 @@ The %BitmapLEDProgram class is a simple example of a LittleFoot program. The repaint() method of the LittleFoot program is called at approximately 25 Hz, and each time it simply inspects the heap (the shared area of memory used to communicate between your application code and your LittleFoot program) and sets the LEDs based on the heap's content. To update the heap, and hence the LEDS, your application code calls BitmapLEDProgram::setLED. -A more advanced example can be found in the source code of the DrumPadGridProgram class. +A more advanced example can be found in the source code of the DrumPadGridProgram class or in the @ref example_blocks_synth example. */ From e384fa71d89a23df9e0e4e13f77c9cfddf9145d6 Mon Sep 17 00:00:00 2001 From: ed Date: Tue, 15 Nov 2016 10:14:27 +0000 Subject: [PATCH 10/10] Added Timer to MainComponent to stop touches from triggering multiple waveshape mode changes --- examples/BLOCKS/BlocksSynth/Source/MainComponent.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/examples/BLOCKS/BlocksSynth/Source/MainComponent.h b/examples/BLOCKS/BlocksSynth/Source/MainComponent.h index fe9cfb29a2..58f79f440f 100644 --- a/examples/BLOCKS/BlocksSynth/Source/MainComponent.h +++ b/examples/BLOCKS/BlocksSynth/Source/MainComponent.h @@ -73,7 +73,8 @@ struct SynthGrid class MainComponent : public Component, public TopologySource::Listener, private TouchSurface::Listener, - private ControlButton::Listener + private ControlButton::Listener, + private Timer { public: MainComponent() @@ -144,7 +145,7 @@ 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) + if (currentMode == waveformSelectionMode && touch.isTouchStart && allowTouch) { // Change the displayed waveshape to the next one ++waveshapeMode; @@ -153,6 +154,9 @@ private: waveshapeMode = 0; waveshapeProgram->setWaveshapeType (static_cast (waveshapeMode)); + + allowTouch = false; + startTimer (250); } else if (currentMode == playMode) { @@ -264,6 +268,9 @@ private: } } + /** Stops touch events from triggering multiple waveshape mode changes */ + void timerCallback() override { allowTouch = true; } + enum BlocksSynthMode { waveformSelectionMode = 0, @@ -289,6 +296,8 @@ private: float scaleX = 0.0; float scaleY = 0.0; + bool allowTouch = true; + //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent) };