1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-02-01 03:10:06 +00:00

Updated BlocksSynth example to draw waveshapes on the Lightpad using a LittleFoot program and updated documentation for BlocksSynth example

This commit is contained in:
ed 2016-11-15 10:38:33 +00:00
commit ee4fe9e463
11 changed files with 226 additions and 149 deletions

View file

@ -9,8 +9,10 @@
<FILE id="SfPbCK" name="Oscillators.h" compile="0" resource="0" file="Source/Oscillators.h"/>
<FILE id="K6DQIH" name="Audio.h" compile="0" resource="0" file="Source/Audio.h"/>
</GROUP>
<FILE id="atp2Hj" name="MainComponent.h" compile="0" resource="0" file="Source/MainComponent.h"/>
<FILE id="r82Xw7" name="Main.cpp" compile="1" resource="0" file="Source/Main.cpp"/>
<FILE id="atp2Hj" name="MainComponent.h" compile="0" resource="0" file="Source/MainComponent.h"/>
<FILE id="HmjVmY" name="WaveshapeProgram.h" compile="0" resource="0"
file="Source/WaveshapeProgram.h"/>
</GROUP>
</MAINGROUP>
<EXPORTFORMATS>

View file

@ -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 = "<group>"; };
6FBEB977137848BF3903034E = {isa = PBXGroup; children = (
23FE51CE68990B49B3B4AABD,
D98771DCE827466B36D51AF8,
1AE75CB0FA61A03429A4E540,
D98771DCE827466B36D51AF8, ); name = Source; sourceTree = "<group>"; };
831D05658D06983B575A395C, ); name = Source; sourceTree = "<group>"; };
38998921858495104F35872D = {isa = PBXGroup; children = (
6FBEB977137848BF3903034E, ); name = BlocksSynth; sourceTree = "<group>"; };
093A7AEDD681B83D66FC731D = {isa = PBXGroup; children = (

View file

@ -1595,6 +1595,7 @@
<ClInclude Include="..\..\Source\Oscillators.h"/>
<ClInclude Include="..\..\Source\Audio.h"/>
<ClInclude Include="..\..\Source\MainComponent.h"/>
<ClInclude Include="..\..\Source\WaveshapeProgram.h"/>
<ClInclude Include="..\..\..\..\..\modules\juce_audio_basics\buffers\juce_AudioChannelSet.h"/>
<ClInclude Include="..\..\..\..\..\modules\juce_audio_basics\buffers\juce_AudioDataConverters.h"/>
<ClInclude Include="..\..\..\..\..\modules\juce_audio_basics\buffers\juce_AudioSampleBuffer.h"/>

View file

@ -1920,6 +1920,9 @@
<ClInclude Include="..\..\Source\MainComponent.h">
<Filter>BlocksSynth\Source</Filter>
</ClInclude>
<ClInclude Include="..\..\Source\WaveshapeProgram.h">
<Filter>BlocksSynth\Source</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\..\modules\juce_audio_basics\buffers\juce_AudioChannelSet.h">
<Filter>Juce Modules\juce_audio_basics\buffers</Filter>
</ClInclude>

View file

@ -1595,6 +1595,7 @@
<ClInclude Include="..\..\Source\Oscillators.h"/>
<ClInclude Include="..\..\Source\Audio.h"/>
<ClInclude Include="..\..\Source\MainComponent.h"/>
<ClInclude Include="..\..\Source\WaveshapeProgram.h"/>
<ClInclude Include="..\..\..\..\..\modules\juce_audio_basics\buffers\juce_AudioChannelSet.h"/>
<ClInclude Include="..\..\..\..\..\modules\juce_audio_basics\buffers\juce_AudioDataConverters.h"/>
<ClInclude Include="..\..\..\..\..\modules\juce_audio_basics\buffers\juce_AudioSampleBuffer.h"/>

View file

@ -1920,6 +1920,9 @@
<ClInclude Include="..\..\Source\MainComponent.h">
<Filter>BlocksSynth\Source</Filter>
</ClInclude>
<ClInclude Include="..\..\Source\WaveshapeProgram.h">
<Filter>BlocksSynth\Source</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\..\modules\juce_audio_basics\buffers\juce_AudioChannelSet.h">
<Filter>Juce Modules\juce_audio_basics\buffers</Filter>
</ClInclude>

View file

@ -4,6 +4,7 @@
#include "../JuceLibraryCode/JuceHeader.h"
#include "Audio.h"
#include "WaveshapeProgram.h"
//==============================================================================
/**
@ -56,10 +57,10 @@ struct SynthGrid
Array<DrumPadGridProgram::GridFill> gridFillArray;
Colour baseGridColour = Colours::green;
Colour touchColour = Colours::cyan;
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 };
Array<int> notes = { 1, 3, 6, 7, 9, 11, 14, 15, 17, 19, 22, 24 };
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SynthGrid)
@ -82,8 +83,6 @@ public:
// Register MainContentComponent as a listener to the PhysicalTopologySource object
topologySource.addListener (this);
generateWaveshapes();
};
~MainComponent()
@ -146,13 +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 (static_cast<uint8> (waveshapeMode));
allowTouch = false;
startTimer (250);
}
else if (currentMode == playMode)
{
@ -214,48 +218,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<uint32> (y));
}
}
// Increment the offset to draw a 'moving' waveshape
if (++yOffset == 30)
yOffset -= 30;
}
/** Clears the old touch times */
void clearOldTouchTimes (const Time now)
{
@ -281,20 +243,18 @@ private:
{
if (currentMode == waveformSelectionMode)
{
// Create a new BitmapLEDProgram for the LEDGrid
bitmapProgram = new BitmapLEDProgram (*grid);
// Create a new WaveshapeProgram for the LEDGrid
waveshapeProgram = new WaveshapeProgram (*grid);
// Set the LEDGrid program
grid->setProgram (bitmapProgram);
grid->setProgram (waveshapeProgram);
// Redraw at 25Hz
startTimerHz (25);
// Initialise the program
waveshapeProgram->setWaveshapeType (static_cast<uint8> (waveshapeMode));
waveshapeProgram->generateWaveshapes();
}
else if (currentMode == playMode)
{
// Stop the redraw timer
stopTimer();
// Create a new DrumPadGridProgram for the LEDGrid
gridProgram = new DrumPadGridProgram (*grid);
@ -308,83 +268,8 @@ 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));
}
/** Stops touch events from triggering multiple waveshape mode changes */
void timerCallback() override { allowTouch = true; }
enum BlocksSynthMode
{
@ -397,8 +282,8 @@ private:
//==============================================================================
Audio audio;
DrumPadGridProgram* gridProgram = nullptr;
BitmapLEDProgram* bitmapProgram = nullptr;
DrumPadGridProgram* gridProgram = nullptr;
WaveshapeProgram* waveshapeProgram = nullptr;
SynthGrid layout { 5, 5 };
PhysicalTopologySource topologySource;
@ -406,19 +291,13 @@ private:
Array<juce::Time> touchMessageTimesInLastSecond;
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;
bool allowTouch = true;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};

View file

@ -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<double> amplitude, phaseIncrement;

View file

@ -0,0 +1,184 @@
#ifndef WAVESHAPEPROGRAM_H_INCLUDED
#define WAVESHAPEPROGRAM_H_INCLUDED
/**
A Program to draw moving waveshapes onto the LEDGrid
*/
class WaveshapeProgram : public LEDGrid::Program
{
public:
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];
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] = static_cast<uint8> (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] = 255;
else
squareWaveY[x] = 1;
}
else
{
if (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)
{
ledGrid.setDataByte (sineWaveOffset + i, sineWaveY[i]);
ledGrid.setDataByte (squareWaveOffset + i, squareWaveY[i]);
ledGrid.setDataByte (sawWaveOffset + i, sawWaveY[i]);
ledGrid.setDataByte (triangleWaveOffset + i, triangleWaveY[i]);
}
}
uint32 getHeapSize() override
{
return totalDataSize;
}
String getLittleFootProgram() override
{
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, 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);
}
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
static constexpr uint32 totalDataSize = triangleWaveOffset + 45;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WaveshapeProgram)
};
#endif // WAVESHAPEPROGRAM_H_INCLUDED

View file

@ -9,7 +9,7 @@ BlocksSynth is a JUCE application that turns your Lightpad into a simple monopho
Navigate to the <tt>JUCE/examples/BLOCKS/BlocksSynth/Builds/</tt> 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 <code>MainComponent</code> the <code>MainComponent::generateWaveshapes()</code> 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: <code>sineWaveY</code>, <code>squareWaveY</code>, <code>sawWaveY</code> and <code>triangleWaveY</code>. Then in the <code>MainComponent::timerCallback()</code> method, a <code>for</code> loop iterates over the 15 LEDs on the X-axis and draws an LED 'circle' using the <code>MainComponent::drawLEDCircle()</code> method at the corresponding Y coordinate for the selected waveshape. The read position of the array is offset using the <code>yOffset</code> 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 <code>WaveshapeProgram.h</code> 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 <code>getHeapSize()</code> method. In the private section of <code>WaveshapeProgram</code> the structure of the shared data heap is laid out with variables containing the offsets for each section and the <code>totalDataSize</code> variable contains the total size (in bytes) that is required and is returned by the <code>WaveshapeProgram::getHeapSize()</code> 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 <code>WaveshapeProgram::getLittleFootProgram()</code> method returns the LittleFoot program that will be executed on the BLOCKS device. The <code>repaint()</code> 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 <code>for</code> loop to iterate over the 15 LEDs on the X-axis and draw an LED 'circle' using the <code>drawLEDCircle()</code> method at the corresponding Y coordinate for the selected waveshape. The read position of the heap is offset using the <code>yOffset</code> variable which is incremented each <code>repaint()</code> 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 <tt>Oscillators.h</tt> 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.
*/

View file

@ -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.
*/