Skip to content

Commit

Permalink
Add index as ColorMapper parameter
Browse files Browse the repository at this point in the history
Add rainbow FastLED example
  • Loading branch information
tttapa committed Oct 25, 2019
1 parent 3bc975f commit bca6e11
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 21 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ fully implemented, feel free to open an issue, so I know where to focus on first

## Recent Breaking Changes

- ########################################
The color mapper for `NoteRangeFastLED` and the like now takes a second
parameter that represents the index of the LED within the LED strip.
- 3c01c7d5eb60e59720540d5a77095468e6984a58
The **maximum supported ADC resolution is now used by default** (e.g. 13 bits
on Teensy 3.x, 12 bits on ESP32).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* This example demonstrates the use of addressable LEDs that respond to
* incoming MIDI note events. This example uses a custom color mapper to
* get a rainbow effect across the LED strip.
*
* @boards AVR, AVR USB, Teensy 3.x, ESP32
*
* @note You might lose incoming MIDI data while the LED strip is being
* updated. To avoid this, don't use an Arduino UNO.
* See <https://github.com/FastLED/FastLED/wiki/Interrupt-problems>
*
* Connections
* -----------
*
* - 2: Data pin of NeoPixel LED strip with at least 8 pixels.
*
* Behavior
* --------
*
* If a MIDI Note On event for note 0x3C (C4 or middle C) is sent, the first LED
* will light up, if a Note Off event for that note is sent, the LED will turn
* off.
* If a MIDI Note On event for note 0x3D (C#4) is sent, the second LED will
* light up, etc.
* (A Note On event with a velocity of zero also counts as a Note Off event.)
*
* Mapping
* -------
*
* Route the MIDI output of a MIDI keyboard to the Arduino's MIDI input. Then
* play a middle C and some notes above it on the keyboard.
*
* Written by PieterP, 2019-10-15
* https://github.com/tttapa/Control-Surface
*/
#include <FastLED.h>
// Must be before Control Surface to enable FastLED features of Control Surface
#include <Control_Surface.h>

// Define the array of leds.
Array<CRGB, 8> leds = {};
// The data pin with the strip connected.
constexpr uint8_t ledpin = 2;

USBMIDI_Interface midi;

// Create a functor that maps the velocity and the index of a note to a color.
struct RainbowColorMapper {
CHSV operator()(uint8_t velocity, uint8_t index) const {
return {255 * index / leds.length, 255, 2 * velocity};
}
};

using namespace MIDI_Notes;
NoteRangeFastLED<leds.length, RainbowColorMapper> midiled = {leds, note(C, 4)};

void setup() {
// See FastLED examples and documentation for more information.
FastLED.addLeds<NEOPIXEL, ledpin>(leds.data, leds.length);
FastLED.setCorrection(TypicalPixelString);
midiled.setBrightness(128);
Control_Surface.begin();
}

void loop() {
Control_Surface.loop();
FastLED.show();
}
41 changes: 41 additions & 0 deletions examples/examples.h
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,47 @@
* https://github.com/tttapa/Control-Surface
*/

/**
* @example "10.Note-FastLED-ColorMapper.ino"
*
* 10.Note-FastLED-ColorMapper
* ===========================
*
* This example demonstrates the use of addressable LEDs that respond to
* incoming MIDI note events. This example uses a custom color mapper to
* get a rainbow effect across the LED strip.
*
* @boards AVR, AVR USB, Teensy 3.x, ESP32
*
* @note You might lose incoming MIDI data while the LED strip is being
* updated. To avoid this, don't use an Arduino UNO.
* See <https://github.com/FastLED/FastLED/wiki/Interrupt-problems>
*
* Connections
* -----------
*
* - 2: Data pin of NeoPixel LED strip with at least 8 pixels.
*
* Behavior
* --------
*
* If a MIDI Note On event for note 0x3C (C4 or middle C) is sent, the first LED
* will light up, if a Note Off event for that note is sent, the LED will turn
* off.
* If a MIDI Note On event for note 0x3D (C#4) is sent, the second LED will
* light up, etc.
* (A Note On event with a velocity of zero also counts as a Note Off event.)
*
* Mapping
* -------
*
* Route the MIDI output of a MIDI keyboard to the Arduino's MIDI input. Then
* play a middle C and some notes above it on the keyboard.
*
* Written by PieterP, 2019-10-15
* https://github.com/tttapa/Control-Surface
*/

/**
* @example "1.Note-LED.ino"
*
Expand Down
3 changes: 2 additions & 1 deletion src/MIDI_Inputs/LEDs/FastLED.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

BEGIN_CS_NAMESPACE

Color DefaultColorMapper::operator()(uint8_t value) const {
Color DefaultColorMapper::operator()(uint8_t value, uint8_t index) const {
(void)index;
uint8_t r = pgm_read_byte_near(NovationLaunchpadColorLUT + 3 * value + 2);
uint8_t g = pgm_read_byte_near(NovationLaunchpadColorLUT + 3 * value + 1);
uint8_t b = pgm_read_byte_near(NovationLaunchpadColorLUT + 3 * value + 0);
Expand Down
27 changes: 16 additions & 11 deletions src/MIDI_Inputs/LEDs/FastLED.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ struct Color {
#endif
};

/// The default mapping from a 7-bit MIDI value to an RGB color.
/// The default mapping from a 7-bit MIDI value to an RGB color.
/// This uses the Novation Launchpad mapping.
struct DefaultColorMapper {
/// Map from a 7-bit MIDI value to an RGB color.
Color operator()(uint8_t value) const;
Color operator()(uint8_t value, uint8_t index) const;
};

END_CS_NAMESPACE
Expand All @@ -37,13 +37,13 @@ BEGIN_CS_NAMESPACE
template <class ColorMapper>
class NoteCCFastLED {
public:
NoteCCFastLED(CRGB *leds, const ColorMapper &colormapper)
: leds(leds), colormapper{colormapper} {}
NoteCCFastLED(CRGB *ledcolors, const ColorMapper &colormapper)
: ledcolors(ledcolors), colormapper{colormapper} {}

/**
* @brief Set the maximum brightness of the LEDs.
* @param brightness
* The maximum brightness [0, 255]
* @param brightness
* The maximum brightness [0, 255]
*/
void setBrightness(uint8_t brightness) { this->brightness = brightness; }
/// Get the maximum brightness of the LEDs.
Expand All @@ -57,32 +57,35 @@ class NoteCCFastLED {
template <class T>
void update(const T &t, uint8_t index) {
uint8_t value = t.getValue(index);
leds[index] = CRGB{colormapper(value)}.nscale8_video(brightness);
ledcolors[index] =
CRGB{colormapper(value, index)}.nscale8_video(brightness);
}

template <class T>
void update(const T &t) {
for (uint8_t index = 0; index < t.length(); ++index) {
uint8_t value = t.getValue(index);
leds[index] = CRGB{colormapper(value)}.nscale8_video(brightness);
ledcolors[index] =
CRGB{colormapper(value, index)}.nscale8_video(brightness);
}
}

private:
CRGB *leds;
CRGB *ledcolors;
uint8_t brightness = 255;

public:
ColorMapper colormapper;
};

/// @addtogroup midi-input-elements-leds
/// @{

/**
* @brief
*
* @tparam RangeLen
* @tparam DefaultColorMapper
*
* @ingroup midi-input-elements-leds
*/
template <uint8_t RangeLen, class ColorMapper = DefaultColorMapper>
class NoteRangeFastLED
Expand Down Expand Up @@ -343,6 +346,8 @@ class CCValueFastLED

} // namespace Bankable

/// @}

END_CS_NAMESPACE

#endif
15 changes: 6 additions & 9 deletions test/MIDI_Inputs/test-MIDINote.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ TEST(NoteRange, NoteOnNoteOff) {

ChannelMessageMatcher midimsg3 = {0x80, CHANNEL_5, 0x3C, 0x7E};
MIDIInputElementNote::updateAllWith(midimsg3);

EXPECT_EQ(mn.getValue(0), 0x00);
EXPECT_EQ(mn.getValue(1), 0x7D);
}
Expand Down Expand Up @@ -336,20 +336,17 @@ TEST(NoteValueLED, NoteOnNoteOff) {
NoteValueLED mnl = {2, {0x3C, CHANNEL_5}};
mnl.begin();

::testing::Sequence seq;
::testing::InSequence seq;

EXPECT_CALL(ArduinoMock::getInstance(), pinMode(2, OUTPUT)).InSequence(seq);
EXPECT_CALL(ArduinoMock::getInstance(), digitalWrite(2, LOW))
.InSequence(seq);
EXPECT_CALL(ArduinoMock::getInstance(), pinMode(2, OUTPUT));
EXPECT_CALL(ArduinoMock::getInstance(), digitalWrite(2, LOW));
MIDIInputElementNote::beginAll();

EXPECT_CALL(ArduinoMock::getInstance(), digitalWrite(2, HIGH))
.InSequence(seq);
EXPECT_CALL(ArduinoMock::getInstance(), digitalWrite(2, HIGH));
ChannelMessageMatcher midimsg1 = {0x90, CHANNEL_5, 0x3C, 0x7E};
MIDIInputElementNote::updateAllWith(midimsg1);

EXPECT_CALL(ArduinoMock::getInstance(), digitalWrite(2, LOW))
.InSequence(seq);
EXPECT_CALL(ArduinoMock::getInstance(), digitalWrite(2, LOW));
ChannelMessageMatcher midimsg2 = {0x80, CHANNEL_5, 0x3C, 0x7E};
MIDIInputElementNote::updateAllWith(midimsg2);

Expand Down

0 comments on commit bca6e11

Please sign in to comment.