imported from "final" folder
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
// Copyleft (c) 2012, Zach Vorhies
|
||||
// Public domain, no rights reserved.
|
||||
|
||||
#ifndef APPROXIMATING_FUNCTION_H_
|
||||
#define APPROXIMATING_FUNCTION_H_
|
||||
|
||||
//#include <Arduino.h>
|
||||
|
||||
template<typename X, typename Y>
|
||||
const Y MapT(const X& x,
|
||||
const X& x1, const X& x2,
|
||||
const Y& y1, const Y& y2) {
|
||||
Y return_val = static_cast<Y>((x - x1) * (y2 - y1) / (x2 - x1) + y1);
|
||||
return return_val;
|
||||
}
|
||||
|
||||
template <typename KeyT, typename ValT>
|
||||
struct InterpData {
|
||||
InterpData(const KeyT& k, const ValT& v) : key(k), val(v) {}
|
||||
KeyT key;
|
||||
ValT val;
|
||||
};
|
||||
|
||||
template <typename KeyT, typename ValT>
|
||||
inline void SelectInterpPoints(const KeyT& k,
|
||||
const InterpData<KeyT, ValT>* array,
|
||||
const int n, // Number of elements in array.
|
||||
int* dest_lower_bound,
|
||||
int* dest_upper_bound) {
|
||||
if (n < 1) {
|
||||
*dest_lower_bound = *dest_upper_bound = -1;
|
||||
return;
|
||||
}
|
||||
if (k < array[0].key) {
|
||||
*dest_lower_bound = *dest_upper_bound = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n - 1; ++i) {
|
||||
const InterpData<KeyT, ValT>& curr = array[i];
|
||||
const InterpData<KeyT, ValT>& next = array[i+1];
|
||||
|
||||
if (curr.key <= k && k <= next.key) {
|
||||
*dest_lower_bound = i;
|
||||
*dest_upper_bound = i+1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*dest_lower_bound = n - 1;
|
||||
*dest_upper_bound = n - 1;
|
||||
}
|
||||
|
||||
template <typename KeyT, typename ValT>
|
||||
inline ValT Interp(const KeyT& k, const InterpData<KeyT, ValT>* array, const int n) {
|
||||
if (n < 1) {
|
||||
return ValT(0);
|
||||
}
|
||||
|
||||
int low_idx = -1;
|
||||
int high_idx = -1;
|
||||
|
||||
SelectInterpPoints<KeyT, ValT>(k, array, n, &low_idx, &high_idx);
|
||||
|
||||
if (low_idx == high_idx) {
|
||||
return array[low_idx].val;
|
||||
}
|
||||
|
||||
const InterpData<KeyT, ValT>* curr = &array[low_idx];
|
||||
const InterpData<KeyT, ValT>* next = &array[high_idx];
|
||||
// map(...) only works on integers. MapT<> is the same thing but it works on
|
||||
// all datatypes.
|
||||
return MapT<KeyT, ValT>(k, curr->key, next->key, curr->val, next->val);
|
||||
}
|
||||
|
||||
#endif // APPROXIMATING_FUNCTION_H_
|
||||
@@ -0,0 +1,211 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "./util.h"
|
||||
#include "./color_mapper.h"
|
||||
#include "./Keyboard.h"
|
||||
#include "./dprint.h"
|
||||
#include "fl/unused.h"
|
||||
|
||||
Key::Key() : on_(false), sustained_(false), sustain_pedal_on_(false),
|
||||
velocity_(0), idx_(0), event_time_(0) {}
|
||||
|
||||
void Key::SetOn(uint8_t vel, const ColorHSV& color, uint32_t now_ms) {
|
||||
if (curr_color_.v_ < color.v_) { // if the new color is "brighter" than the current color.
|
||||
velocity_ = vel;
|
||||
curr_color_ = color;
|
||||
event_time_ = now_ms;
|
||||
}
|
||||
orig_color_ = curr_color_;
|
||||
event_time_ = now_ms;
|
||||
on_ = true;
|
||||
}
|
||||
|
||||
void Key::SetOff(uint32_t now_ms) {
|
||||
orig_color_ = curr_color_;
|
||||
on_ = false;
|
||||
event_time_ = now_ms;
|
||||
sustained_ = false;
|
||||
}
|
||||
|
||||
void Key::SetSustained() {
|
||||
sustained_ = true;
|
||||
}
|
||||
|
||||
void Key::Update(uint32_t now_ms, uint32_t delta_ms, bool sustain_pedal_on) {
|
||||
if (sustained_ && !sustain_pedal_on) {
|
||||
sustained_ = false;
|
||||
SetOff(now_ms);
|
||||
}
|
||||
sustain_pedal_on_ = sustain_pedal_on;
|
||||
UpdateIntensity(now_ms, delta_ms);
|
||||
}
|
||||
|
||||
float Key::VelocityFactor() const { return velocity_ / 127.f; }
|
||||
|
||||
float Key::CalcAttackDecayFactor(uint32_t delta_ms) const {
|
||||
bool dampened_key = (idx_ < kFirstNoteNoDamp);
|
||||
float active_lights_factor = ::CalcDecayFactor(
|
||||
sustain_pedal_on_,
|
||||
on_,
|
||||
idx_,
|
||||
VelocityFactor(),
|
||||
dampened_key,
|
||||
delta_ms);
|
||||
return active_lights_factor;
|
||||
}
|
||||
|
||||
float Key::AttackRemapFactor(uint32_t now_ms) {
|
||||
if (on_) {
|
||||
return ::AttackRemapFactor(now_ms - event_time_);
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
float Key::IntensityFactor() const {
|
||||
return intensity_;
|
||||
}
|
||||
|
||||
void Key::UpdateIntensity(uint32_t now_ms, uint32_t delta_ms) {
|
||||
if (on_) {
|
||||
// Intensity can be calculated by a
|
||||
float intensity =
|
||||
CalcAttackDecayFactor(now_ms - event_time_) *
|
||||
VelocityFactor() *
|
||||
AttackRemapFactor(now_ms);
|
||||
|
||||
// This is FRAME RATE DEPENDENT FUNCTION!!!!
|
||||
// CHANGE TO TIME INDEPENDENT BEFORE SUBMIT.
|
||||
intensity_ = (.9f * intensity) + (.1f * intensity_);
|
||||
} else if(intensity_ > 0.0f) { // major cpu hotspot.
|
||||
|
||||
if (sustain_pedal_on_) {
|
||||
float delta_s = delta_ms / 1000.f;
|
||||
if (intensity_ > .5f) {
|
||||
const float kRate = .12f;
|
||||
// Time flexible decay function. Stays accurate
|
||||
// even as the frame rate changes.
|
||||
// Formula: A = Pe^(r*t)
|
||||
intensity_ = intensity_ * exp(-delta_s * kRate);
|
||||
} else {
|
||||
// Quickly fade at the bottom end of the transition.
|
||||
const float kRate = .05f;
|
||||
intensity_ -= delta_s * kRate;
|
||||
}
|
||||
} else {
|
||||
float delta_s = delta_ms / 1000.f;
|
||||
if (intensity_ > .5f) {
|
||||
const float kRate = 12.0f;
|
||||
// Time flexible decay function. Stays accurate
|
||||
// even as the frame rate changes.
|
||||
// Formula: A = Pe^(r*t)
|
||||
intensity_ = intensity_ * exp(-delta_s * kRate);
|
||||
} else {
|
||||
// Quickly fade at the bottom end of the transition.
|
||||
const float kRate = 2.0f;
|
||||
intensity_ -= delta_s * kRate;
|
||||
}
|
||||
|
||||
}
|
||||
intensity_ = constrain(intensity_, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardState::HandleNoteOn(uint8_t midi_note, uint8_t velocity, int color_selector_value, uint32_t now_ms) {
|
||||
if (0 == velocity) {
|
||||
// Some keyboards signify "NoteOff" with a velocity of zero.
|
||||
HandleNoteOff(midi_note, velocity, now_ms);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_KEYBOARD
|
||||
dprint("HandleNoteOn:");
|
||||
|
||||
dprint("midi_note = ");
|
||||
dprint(midi_note);
|
||||
|
||||
dprint(", velocity = ");
|
||||
dprintln(velocity);
|
||||
#endif
|
||||
|
||||
float brightness = ToBrightness(velocity);
|
||||
|
||||
dprint("brightness: "); dprintln(brightness);
|
||||
|
||||
ColorHSV pixel_color_hsv = SelectColor(midi_note, brightness,
|
||||
color_selector_value);
|
||||
|
||||
// TODO: Give a key access to the Keyboard owner, therefore it could inspect the
|
||||
// sustained variable instead of passing it here.
|
||||
Key* key = GetKey(midi_note);
|
||||
|
||||
dprint("key indx: "); dprintln(key->idx_);
|
||||
|
||||
key->SetOn(velocity, pixel_color_hsv, now_ms);
|
||||
}
|
||||
|
||||
void KeyboardState::HandleNoteOff(uint8_t midi_note, uint8_t /*velocity*/, uint32_t now_ms) {
|
||||
#ifdef DEBUG_KEYBOARD
|
||||
dprint("HandleNoteOff:");
|
||||
|
||||
dprint("midi_note = ");
|
||||
dprint(midi_note);
|
||||
|
||||
dprint(", velocity = ");
|
||||
dprintln(velocity);
|
||||
#endif
|
||||
|
||||
Key* key = GetKey(midi_note);
|
||||
|
||||
if (sustain_pedal_) {
|
||||
key->SetSustained();
|
||||
} else {
|
||||
key->SetOff(now_ms);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardState::HandleControlChange(uint8_t d1, uint8_t d2) {
|
||||
// Note that d1 and d2 just mean "data-1" and "data-2".
|
||||
// TODO: Find out what d1 and d2 should be called.
|
||||
const bool foot_pedal = (d1 == kMidiFootPedal);
|
||||
|
||||
if (foot_pedal) {
|
||||
// Spec says that if that values 0-63 are OFF, otherwise ON.
|
||||
sustain_pedal_ = (d2 >= 64);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardState::HandleAfterTouchPoly(uint8_t note, uint8_t pressure) {
|
||||
FL_UNUSED(note);
|
||||
FL_UNUSED(pressure);
|
||||
|
||||
dprintln("HandleAfterTouchPoly");
|
||||
|
||||
dprint("\tnote = ");
|
||||
dprint(note);
|
||||
|
||||
dprint(", pressure = ");
|
||||
dprintln(pressure);
|
||||
}
|
||||
|
||||
KeyboardState::KeyboardState() : sustain_pedal_(false), keys_() {
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
keys_[i].idx_ = i;
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardState::Update(uint32_t now_ms, uint32_t delta_ms) {
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
keys_[i].Update(now_ms, delta_ms, sustain_pedal_);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t KeyboardState::KeyIndex(int midi_pitch) {
|
||||
//return constrain(midi_pitch, 21, 108) - 21;
|
||||
return ::KeyIndex(midi_pitch);
|
||||
}
|
||||
|
||||
Key* KeyboardState::GetKey(int midi_pitch) {
|
||||
uint8_t idx = KeyIndex(midi_pitch);
|
||||
return &keys_[idx];
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
|
||||
#ifndef KEYBOARD_H_
|
||||
#define KEYBOARD_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "color.h"
|
||||
#include "./util.h"
|
||||
|
||||
class KeyboardState;
|
||||
|
||||
// // NOTE: AS OF NOV-12-2013 we've disable all of the auto-sustained
|
||||
// notes in the high end of the keyboard.
|
||||
enum {
|
||||
// kFirstNoteNoDamp = 69, // First key that has no dampener
|
||||
kFirstNoteNoDamp = 89, // DISABLED - Greater than last key.
|
||||
};
|
||||
|
||||
inline uint8_t KeyIndex(int midi_pitch) {
|
||||
return constrain(midi_pitch, 21, 108) - 21;
|
||||
}
|
||||
|
||||
struct Key {
|
||||
Key();
|
||||
void SetOn(uint8_t vel, const ColorHSV& color, uint32_t now_ms);
|
||||
void SetOff(uint32_t now_ms);
|
||||
void SetSustained();
|
||||
|
||||
void Update(uint32_t now_ms, uint32_t delta_ms, bool sustain_pedal_on);
|
||||
|
||||
float VelocityFactor() const;
|
||||
float CalcAttackDecayFactor(uint32_t delta_ms) const;
|
||||
float AttackRemapFactor(uint32_t now_ms);
|
||||
float IntensityFactor() const;
|
||||
void UpdateIntensity(uint32_t now_ms, uint32_t delta_ms);
|
||||
|
||||
bool on_; // Max number of MIDI keys.
|
||||
bool sustained_;
|
||||
bool sustain_pedal_on_;
|
||||
uint8_t velocity_;
|
||||
int idx_;
|
||||
unsigned long event_time_;
|
||||
|
||||
// 0.0 -> 1.0 How intense the key is, used for light sequences to represent
|
||||
// 0 -> 0% of lights on to 1.0 -> 100% of lights on. this is a smooth
|
||||
// value through time.
|
||||
float intensity_;
|
||||
ColorHSV orig_color_;
|
||||
ColorHSV curr_color_;
|
||||
};
|
||||
|
||||
// Interface into the Keyboard state.
|
||||
// Convenience class which holds all the keys in the keyboard. Also
|
||||
// has a convenience function will allows one to map the midi notes
|
||||
// (21-108) to the midi keys (0-88).
|
||||
class KeyboardState {
|
||||
public:
|
||||
|
||||
// NOTE: AS OF NOV-12-2013 we've disable all of the auto-sustained
|
||||
// notes in the high end of the keyboard.
|
||||
//enum {
|
||||
// kFirstNoteNoDamp = 69, // First key that has no dampener
|
||||
// kFirstNoteNoDamp = 89, // DISABLED - Greater than last key.
|
||||
//};
|
||||
|
||||
KeyboardState();
|
||||
void Update(uint32_t now_ms, uint32_t delta_ms);
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
// Called when the note is pressed.
|
||||
// Input:
|
||||
// channel - Ignored.
|
||||
// midi_note - Value between 21-108 which maps to the keyboard keys.
|
||||
// velocity - Value between 0-127
|
||||
void HandleNoteOn(uint8_t midi_note, uint8_t velocity, int color_selector_value, uint32_t now_ms);
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Called when the note is released.
|
||||
// Input:
|
||||
// channel - Ignored.
|
||||
// midi_note - Value between 21-108 which maps to the keyboard keys.
|
||||
// velocity - Value between 0-127
|
||||
void HandleNoteOff(uint8_t midi_note, uint8_t velocity, uint32_t now_ms);
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// This is uninmplemented because the test keyboard didn't
|
||||
// have this functionality. Right now the only thing it does is
|
||||
// print out that the key was pressed.
|
||||
void HandleAfterTouchPoly(uint8_t note, uint8_t pressure);
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Detects whether the foot pedal has been touched.
|
||||
void HandleControlChange(uint8_t d1, uint8_t d2);
|
||||
|
||||
|
||||
static uint8_t KeyIndex(int midi_pitch);
|
||||
|
||||
Key* GetKey(int midi_pitch);
|
||||
|
||||
static const int kNumKeys = 88;
|
||||
bool sustain_pedal_;
|
||||
Key keys_[kNumKeys];
|
||||
};
|
||||
|
||||
|
||||
#endif // KEYBOARD_H_
|
||||
|
||||
@@ -0,0 +1,485 @@
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "./Painter.h"
|
||||
#include "./led_layout_array.h"
|
||||
#include "./dprint.h"
|
||||
#include "./Keyboard.h"
|
||||
#include "fl/math_macros.h"
|
||||
#include "fl/math.h"
|
||||
#include "fl/warn.h"
|
||||
|
||||
namespace {
|
||||
|
||||
float LuminanceDecay(float time) {
|
||||
typedef InterpData<float, float> Datum;
|
||||
static const Datum kData[] = {
|
||||
Datum(0, 0),
|
||||
Datum(1, 0),
|
||||
Datum(10, 0),
|
||||
Datum(47, 60),
|
||||
Datum(120, 100),
|
||||
Datum(230, 160),
|
||||
Datum(250, 255),
|
||||
Datum(254, 255),
|
||||
Datum(255, 64),
|
||||
};
|
||||
|
||||
const float key = time * 255.f;
|
||||
static const int n = sizeof(kData) / sizeof(kData[0]);
|
||||
float approx_val = Interp(key, kData, n);
|
||||
|
||||
|
||||
static const float k = (1.0f / 255.f);
|
||||
const float out = approx_val * k;
|
||||
return out;
|
||||
}
|
||||
|
||||
float CalcLuminance(float time_delta_ms,
|
||||
bool sustain_pedal_on,
|
||||
const Key& key,
|
||||
int key_idx) {
|
||||
|
||||
if (key.curr_color_.v_ <= 0.0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
const bool dampened_key = (key_idx < kFirstNoteNoDamp);
|
||||
|
||||
const float decay_factor = CalcDecayFactor(sustain_pedal_on,
|
||||
key.on_,
|
||||
key_idx,
|
||||
key.velocity_ * (1.f/127.f), // Normalizing
|
||||
dampened_key,
|
||||
time_delta_ms);
|
||||
|
||||
if (key.on_) {
|
||||
//const float brigthness_factor = sin(key.orig_color_.v_ * PI / 2.0);
|
||||
float brigthness_factor = 0.0f;
|
||||
|
||||
if (kUseLedCurtin) {
|
||||
brigthness_factor = sqrt(sqrt(key.orig_color_.v_));
|
||||
} else {
|
||||
//brigthness_factor = key.orig_color_.v_ * key.orig_color_.v_;
|
||||
brigthness_factor = key.orig_color_.v_;
|
||||
}
|
||||
return LuminanceDecay(decay_factor) * brigthness_factor;
|
||||
//return 1.0f;
|
||||
} else {
|
||||
return decay_factor * key.orig_color_.v_;
|
||||
}
|
||||
}
|
||||
|
||||
float CalcSaturation(float time_delta_ms, const ColorHSV& color, bool key_on) {
|
||||
if (color.v_ <= 0.0) {
|
||||
return color.s_;
|
||||
}
|
||||
if (!key_on) {
|
||||
return 1.0f;
|
||||
}
|
||||
static const float kDefaultSaturationTime = 0.05f * 1000.f;
|
||||
|
||||
// At time = 0.0 the saturation_factor will be at 0.0 and then transition to 1.0
|
||||
float saturation_factor = mapf(time_delta_ms,
|
||||
0.0f, kDefaultSaturationTime,
|
||||
0.0f, 1.0f);
|
||||
// As time increases the saturation factor will continue
|
||||
// to grow past 1.0. We use min to clamp it back to 1.0.
|
||||
saturation_factor = MIN(1.0f, saturation_factor);
|
||||
// TODO - make the saturation interpolate between the original
|
||||
// color and the unsaturated state.
|
||||
return saturation_factor;
|
||||
}
|
||||
|
||||
} // namespace.
|
||||
|
||||
|
||||
void Painter::Paint(uint32_t now_ms,
|
||||
uint32_t delta_ms,
|
||||
VisState vis_state,
|
||||
KeyboardState* keyboard,
|
||||
LedRopeInterface* light_rope) {
|
||||
for (int i = 0; i < KeyboardState::kNumKeys; ++i) {
|
||||
Key& key = keyboard->keys_[i];
|
||||
|
||||
const float time_delta_ms = static_cast<float>(now_ms - key.event_time_);
|
||||
|
||||
const float lum = CalcLuminance(time_delta_ms, keyboard->sustain_pedal_, key, i);
|
||||
const float sat = CalcSaturation(time_delta_ms, key.curr_color_, key.on_);
|
||||
|
||||
//if (key.idx_ == 56) {
|
||||
// dprint("lum: "); dprint(lum*255.f); dprint(" sat:"); dprintln(sat*255.f);
|
||||
//}
|
||||
|
||||
key.curr_color_.v_ = lum;
|
||||
key.curr_color_.s_ = sat;
|
||||
|
||||
// Removing this line breaks one of the visualizers...
|
||||
// TODO: Figure out a cleaner solution.
|
||||
light_rope->Set(i, key.curr_color_.ToRGB());
|
||||
}
|
||||
|
||||
LedColumns led_columns = LedLayoutArray();
|
||||
|
||||
switch (vis_state) {
|
||||
case Painter::kBlockNote: {
|
||||
light_rope->DrawSequentialRepeat(kNumLightsPerNote);
|
||||
break;
|
||||
}
|
||||
case Painter::kColumnNote: {
|
||||
light_rope->DrawRepeat(led_columns.array, kNumKeys);
|
||||
break;
|
||||
}
|
||||
case Painter::kVUNote: {
|
||||
PaintVuNotes(now_ms, *keyboard, led_columns.array, kNumKeys, light_rope);
|
||||
break;
|
||||
}
|
||||
case Painter::kVUMidNote: {
|
||||
PaintVuMidNotesFade(delta_ms, *keyboard, led_columns.array, kNumKeys, light_rope);
|
||||
break;
|
||||
}
|
||||
|
||||
case Painter::kVegas: { // aka "vegas mode?"
|
||||
VegasVisualizer(*keyboard, led_columns.array, kNumKeys, light_rope);
|
||||
break;
|
||||
}
|
||||
|
||||
case Painter::kBrightSurprise: {
|
||||
PaintBrightSurprise(*keyboard, led_columns.array, kNumKeys, light_rope);
|
||||
break;
|
||||
}
|
||||
|
||||
case Painter::kVUSpaceInvaders: {
|
||||
PaintVuSpaceInvaders(now_ms, *keyboard, led_columns.array, kNumKeys, light_rope);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
dprint("Unknown mode: "); dprint(vis_state); dprint(".\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Painter::PaintVuNotes(uint32_t /*now_ms*/,
|
||||
const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope) {
|
||||
|
||||
|
||||
FASTLED_WARN("\n\n############## VU NOTES ################\n\n");
|
||||
|
||||
led_rope->RawBeginDraw();
|
||||
|
||||
for (int i = 0; i < led_column_table_length; ++i) {
|
||||
const Key& key = keyboard.keys_[i];
|
||||
|
||||
|
||||
// Map the white keys to the bottom and the black keys to the top.
|
||||
bool black_key = false;
|
||||
switch (key.idx_ % 12) {
|
||||
case 1:
|
||||
case 4:
|
||||
case 6:
|
||||
case 9:
|
||||
case 11:
|
||||
black_key = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const int pixel_count = led_column_table[i];
|
||||
const int draw_pixel_count = ceil(pixel_count * sqrt(key.curr_color_.v_));
|
||||
|
||||
const int black_pixel_count = pixel_count - draw_pixel_count;
|
||||
const Color3i& c = *led_rope->GetIterator(i);
|
||||
|
||||
|
||||
const bool reverse_correct = black_key == (key.idx_ % 2);
|
||||
|
||||
if (reverse_correct) {
|
||||
for (int j = 0; j < draw_pixel_count; ++j) {
|
||||
if (j < draw_pixel_count - 1) {
|
||||
led_rope->RawDrawPixel(c);
|
||||
} else {
|
||||
// Last pixel.
|
||||
ColorHSV hsv(random(512) / 512.f, random(512) / 512.f, 1.0);
|
||||
led_rope->RawDrawPixel(hsv.ToRGB());
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < black_pixel_count; ++j) {
|
||||
led_rope->RawDrawPixel(Color3i::Black());
|
||||
}
|
||||
} else {
|
||||
for (int j = 0; j < black_pixel_count; ++j) {
|
||||
led_rope->RawDrawPixel(Color3i::Black());
|
||||
}
|
||||
|
||||
for (int j = draw_pixel_count - 1; j >= 0; --j) {
|
||||
if (j < draw_pixel_count - 1) {
|
||||
led_rope->RawDrawPixel(c);
|
||||
} else {
|
||||
// Last pixel.
|
||||
ColorHSV hsv(random(512) / 512.f, random(512) / 512.f, 1.0);
|
||||
led_rope->RawDrawPixel(hsv.ToRGB());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
led_rope->RawCommitDraw();
|
||||
}
|
||||
|
||||
void Painter::PaintVuMidNotesFade(uint32_t /*delta_ms*/,
|
||||
const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope) {
|
||||
|
||||
FASTLED_WARN("\n\n############## VU MID NOTES FADE ################\n\n");
|
||||
|
||||
struct DrawPoints {
|
||||
int n_black0;
|
||||
int n_fade0;
|
||||
int n_fill;
|
||||
int n_fade1;
|
||||
int n_black1;
|
||||
float fade_factor; // 0->1.0
|
||||
|
||||
float SumBrightness() const {
|
||||
float out = 0;
|
||||
out += n_fill;
|
||||
out += (fade_factor * n_fade0);
|
||||
out += (fade_factor * n_fade1);
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
// Generator for the DrawPoints struct above.
|
||||
// n_led: How many led's there are in total.
|
||||
// factor: 0->1, indicates % of led's "on".
|
||||
struct F {
|
||||
static DrawPoints Generate(int n_led, float factor) {
|
||||
DrawPoints out;
|
||||
memset(&out, 0, sizeof(out));
|
||||
if (n_led == 0 || factor == 0.0f) {
|
||||
out.n_black0 = n_led;
|
||||
return out;
|
||||
}
|
||||
const int is_odd = (n_led % 2);
|
||||
const int n_half_lights = n_led / 2 + is_odd;
|
||||
const float f_half_fill = n_half_lights * factor;
|
||||
const int n_half_fill = static_cast<int>(f_half_fill); // Truncates float.
|
||||
|
||||
float fade_pix_perc = f_half_fill - static_cast<float>(n_half_fill);
|
||||
int n_fade_pix = fade_pix_perc < 1.0f;
|
||||
if (n_half_fill == 0) {
|
||||
n_fade_pix = 1;
|
||||
}
|
||||
int n_half_black = n_half_lights - n_half_fill - n_fade_pix;
|
||||
|
||||
int n_fill_pix = 0;
|
||||
if (n_half_fill > 0) {
|
||||
n_fill_pix = n_half_fill * 2 + (is_odd ? -1 : 0);
|
||||
}
|
||||
|
||||
out.n_black0 = n_half_black;
|
||||
out.n_fade0 = n_fade_pix;
|
||||
out.n_fill = n_fill_pix;
|
||||
out.n_fade1 = n_fade_pix;
|
||||
if (!n_fill_pix && is_odd) {
|
||||
out.n_fade1 = 0;
|
||||
}
|
||||
out.n_black1 = n_half_black;
|
||||
out.fade_factor = fade_pix_perc;
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
led_rope->RawBeginDraw();
|
||||
|
||||
for (int i = 0; i < led_column_table_length; ++i) {
|
||||
const Key& key = keyboard.keys_[i];
|
||||
|
||||
float active_lights_factor = key.IntensityFactor();
|
||||
|
||||
//if (key.curr_color_.v_ <= 0.f) {
|
||||
// active_lights_factor = 0.0;
|
||||
//}
|
||||
|
||||
const int n_led = led_column_table[i];
|
||||
|
||||
if (active_lights_factor > 0.0f) {
|
||||
DrawPoints dp = F::Generate(n_led, active_lights_factor);
|
||||
|
||||
ColorHSV hsv = key.curr_color_;
|
||||
hsv.v_ = 1.0;
|
||||
Color3i color = hsv.ToRGB();
|
||||
// Now figure out optional fade color
|
||||
Color3i fade_col;
|
||||
ColorHSV c = key.curr_color_;
|
||||
c.v_ = dp.fade_factor;
|
||||
fade_col = c.ToRGB();
|
||||
|
||||
// Output to graphics.
|
||||
led_rope->RawDrawPixels(Color3i::Black(), dp.n_black0);
|
||||
led_rope->RawDrawPixels(fade_col, dp.n_fade0);
|
||||
led_rope->RawDrawPixels(color, dp.n_fill);
|
||||
led_rope->RawDrawPixels(fade_col, dp.n_fade1);
|
||||
led_rope->RawDrawPixels(Color3i::Black(), dp.n_black1);
|
||||
|
||||
#ifdef DEBUG_PAINTER
|
||||
if (active_lights_factor > 0.0) {
|
||||
int total_lights_on = dp.SumBrightness();
|
||||
//dprint("total_lights_on: "); dprint(total_lights_on);
|
||||
//dprint(", total lights written: "); dprintln(total_lights_on + dp.n_black0 + dp.n_black1);
|
||||
|
||||
//float total = (dp.n_fade0 * dp.fade_factor) + (dp.n_fade1 * dp.fade_factor) + static_cast<float>(dp.n_fill);
|
||||
#define P(X) dprint(", "#X ": "); dprint(X);
|
||||
|
||||
//dprint("active_lights_factor: "); dprintln(active_lights_factor);
|
||||
|
||||
//P(dp.n_black0); P(dp.n_fade0); P(dp.n_fill); P(dp.n_fade1); P(dp.n_black1); P(dp.fade_factor);
|
||||
P(total_lights_on);
|
||||
P(active_lights_factor);
|
||||
//P(total);
|
||||
dprintln("");
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
led_rope->RawDrawPixels(Color3i::Black(), n_led);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
led_rope->RawCommitDraw();
|
||||
}
|
||||
|
||||
void Painter::VegasVisualizer(const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope) {
|
||||
|
||||
led_rope->RawBeginDraw();
|
||||
|
||||
uint32_t skipped_lights = 0;
|
||||
for (int i = 0; i < led_column_table_length; ++i) {
|
||||
const Key& key = keyboard.keys_[i];
|
||||
uint32_t painted_lights = 0;
|
||||
|
||||
// % of lights that are active.
|
||||
const float active_lights_factor = led_column_table[i] * sqrt(key.curr_color_.v_);
|
||||
const float inactive_lights_factor = 1.0f - active_lights_factor;
|
||||
const float taper_point_1 = inactive_lights_factor / 2.0f;
|
||||
const float taper_point_2 = taper_point_1 + active_lights_factor;
|
||||
|
||||
const int taper_idx_1 = static_cast<int>(floor(taper_point_1 * led_column_table[i]));
|
||||
const int taper_idx_2 = static_cast<int>(floor(taper_point_2 * led_column_table[i]));
|
||||
|
||||
const Color3i c = key.curr_color_.ToRGB();
|
||||
|
||||
for (int i = 0; i < taper_idx_1 / 2; ++i) {
|
||||
led_rope->RawDrawPixel(Color3i::Black());
|
||||
painted_lights++;
|
||||
}
|
||||
|
||||
int length = taper_idx_2 - taper_idx_1;
|
||||
for (int i = 0; i < min(200, length); ++i) {
|
||||
led_rope->RawDrawPixel(c);
|
||||
painted_lights++;
|
||||
}
|
||||
|
||||
length = led_column_table[i] - taper_idx_2;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
led_rope->RawDrawPixel(Color3i::Black());
|
||||
painted_lights++;
|
||||
}
|
||||
skipped_lights += MAX(0, static_cast<int32_t>(led_column_table[i]) - static_cast<int32_t>(painted_lights));
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < skipped_lights; ++i) {
|
||||
led_rope->RawDrawPixel(Color3i::Black());
|
||||
}
|
||||
|
||||
led_rope->RawCommitDraw();
|
||||
}
|
||||
|
||||
void Painter::PaintBrightSurprise(
|
||||
const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope) {
|
||||
|
||||
led_rope->RawBeginDraw();
|
||||
int total_counted = 0;
|
||||
|
||||
float r, g, b;
|
||||
r = g = b = 0;
|
||||
|
||||
for (int i = 0; i < KeyboardState::kNumKeys; ++i) {
|
||||
const Key& key = keyboard.keys_[i];
|
||||
|
||||
|
||||
if (key.curr_color_.v_ > 0.0f) {
|
||||
const Color3i rgb = key.curr_color_.ToRGB();
|
||||
r += rgb.r_;
|
||||
g += rgb.g_;
|
||||
b += rgb.b_;
|
||||
++total_counted;
|
||||
}
|
||||
}
|
||||
|
||||
float denom = total_counted ? total_counted : 1;
|
||||
r /= denom;
|
||||
g /= denom;
|
||||
b /= denom;
|
||||
|
||||
|
||||
const Color3i rgb(r, g, b);
|
||||
|
||||
for (int i = 0; i < led_column_table_length; ++i) {
|
||||
const int n = led_column_table[i];
|
||||
for (int i = 0; i < n; ++i) {
|
||||
led_rope->RawDrawPixel(rgb);
|
||||
}
|
||||
}
|
||||
led_rope->RawCommitDraw();
|
||||
}
|
||||
|
||||
void Painter::PaintVuSpaceInvaders(uint32_t /*now_ms*/,
|
||||
const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope) {
|
||||
led_rope->RawBeginDraw();
|
||||
|
||||
Color3i black = Color3i::Black();
|
||||
|
||||
for (int i = 0; i < led_column_table_length; ++i) {
|
||||
const Key& key = keyboard.keys_[i];
|
||||
|
||||
const int pixel_count = led_column_table[i];
|
||||
const int draw_pixel_count = ceil(pixel_count * sqrt(key.curr_color_.v_));
|
||||
|
||||
const int black_pixel_count = pixel_count - draw_pixel_count;
|
||||
|
||||
// If i is even
|
||||
if (i % 2 == 0) {
|
||||
for (int j = 0; j < black_pixel_count; ++j) {
|
||||
led_rope->RawDrawPixel(*led_rope->GetIterator(i));
|
||||
}
|
||||
for (int j = 0; j < draw_pixel_count; ++j) {
|
||||
led_rope->RawDrawPixel(black);
|
||||
}
|
||||
} else {
|
||||
|
||||
for (int j = 0; j < draw_pixel_count; ++j) {
|
||||
led_rope->RawDrawPixel(black);
|
||||
}
|
||||
|
||||
for (int j = 0; j < black_pixel_count; ++j) {
|
||||
led_rope->RawDrawPixel(*led_rope->GetIterator(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
led_rope->RawCommitDraw();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef PAINTER_H
|
||||
#define PAINTER_H
|
||||
|
||||
#include "./Keyboard.h"
|
||||
#include "./ApproximatingFunction.h"
|
||||
#include "./util.h"
|
||||
#include "./settings.h"
|
||||
#include "./led_rope_interface.h"
|
||||
|
||||
struct Painter {
|
||||
|
||||
enum VisState {
|
||||
kVUMidNote = 0,
|
||||
kColumnNote,
|
||||
kBlockNote,
|
||||
kVUNote,
|
||||
kVUSpaceInvaders,
|
||||
kVegas,
|
||||
kBrightSurprise,
|
||||
|
||||
kNumVisStates,
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
static void Paint(uint32_t now_ms,
|
||||
uint32_t delta_ms,
|
||||
VisState vis_state,
|
||||
KeyboardState* keyboard,
|
||||
LedRopeInterface* light_rope);
|
||||
private:
|
||||
static void PaintVuNotes(uint32_t now_ms,
|
||||
const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope);
|
||||
|
||||
static void PaintVuMidNotesFade(uint32_t delta_ms,
|
||||
const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope);
|
||||
|
||||
// This is a crazy effect, lets keep this around.
|
||||
static void VegasVisualizer(const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope);
|
||||
|
||||
static void PaintBrightSurprise(const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope);
|
||||
|
||||
|
||||
|
||||
static void PaintVuSpaceInvaders(uint32_t now_ms,
|
||||
const KeyboardState& keyboard,
|
||||
const int* led_column_table, int led_column_table_length,
|
||||
LedRopeInterface* led_rope);
|
||||
};
|
||||
|
||||
#endif // PAINTER_H
|
||||
@@ -0,0 +1,151 @@
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "./color.h"
|
||||
#include "./util.h"
|
||||
#include "fl/math_macros.h"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Color3i::Mul(const Color3i& other_color) {
|
||||
int r = r_;
|
||||
int g = g_;
|
||||
int b = b_;
|
||||
|
||||
r = r * int(other_color.r_) / 255;
|
||||
g = g * int(other_color.g_) / 255;
|
||||
b = b * int(other_color.b_) / 255;
|
||||
Set(r, g, b);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Color3i::Mulf(float scale) {
|
||||
const int s = static_cast<int>(scale * 255.0f);
|
||||
|
||||
int r = static_cast<int>(r_) * s / 255;
|
||||
int g = static_cast<int>(g_) * s / 255;
|
||||
int b = static_cast<int>(b_) * s / 255;
|
||||
|
||||
Set(r, g, b);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Color3i::Sub(const Color3i& color) {
|
||||
if (r_ < color.r_) r_ = 0;
|
||||
else r_ -= color.r_;
|
||||
if (g_ < color.g_) g_ = 0;
|
||||
else g_ -= color.g_;
|
||||
if (b_ < color.b_) b_ = 0;
|
||||
else b_ -= color.b_;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Color3i::Add(const Color3i& color) {
|
||||
if ((255 - r_) < color.r_) r_ = 255;
|
||||
else r_ += color.r_;
|
||||
if ((255 - g_) < color.g_) g_ = 255;
|
||||
else g_ += color.g_;
|
||||
if ((255 - b_) < color.b_) b_ = 255;
|
||||
else b_ += color.b_;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
uint8_t Color3i::Get(int rgb_index) const {
|
||||
const uint8_t* rgb = At(rgb_index);
|
||||
return rgb ? *rgb : 0;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Color3i::Set(int rgb_index, uint8_t val) {
|
||||
uint8_t* rgb = At(rgb_index);
|
||||
if (rgb) {
|
||||
*rgb = val;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Color3i::Interpolate(const Color3i& other_color, float t) {
|
||||
if (0.0f >= t) {
|
||||
Set(other_color);
|
||||
} else if (1.0f <= t) {
|
||||
return;
|
||||
}
|
||||
|
||||
Color3i new_color = other_color;
|
||||
new_color.Mul(1.0f - t);
|
||||
this->Mul(t);
|
||||
this->Add(new_color);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
uint8_t* Color3i::At(int rgb_index) {
|
||||
switch(rgb_index) {
|
||||
case 0: return &r_;
|
||||
case 1: return &g_;
|
||||
case 2: return &b_;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
const uint8_t* Color3i::At(int rgb_index) const {
|
||||
switch(rgb_index) {
|
||||
case 0: return &r_;
|
||||
case 1: return &g_;
|
||||
case 2: return &b_;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void ColorHSV::FromRGB(const Color3i& rgb) {
|
||||
typedef double FloatT;
|
||||
FloatT r = (FloatT) rgb.r_/255.f;
|
||||
FloatT g = (FloatT) rgb.g_/255.f;
|
||||
FloatT b = (FloatT) rgb.b_/255.f;
|
||||
FloatT max_rgb = MAX(r, MAX(g, b));
|
||||
FloatT min_rgb = MIN(r, MIN(g, b));
|
||||
v_ = max_rgb;
|
||||
|
||||
FloatT d = max_rgb - min_rgb;
|
||||
s_ = max_rgb == 0 ? 0 : d / max_rgb;
|
||||
|
||||
if (max_rgb == min_rgb) {
|
||||
h_ = 0; // achromatic
|
||||
} else {
|
||||
if (max_rgb == r) {
|
||||
h_ = (g - b) / d + (g < b ? 6 : 0);
|
||||
} else if (max_rgb == g) {
|
||||
h_ = (b - r) / d + 2;
|
||||
} else if (max_rgb == b) {
|
||||
h_ = (r - g) / d + 4;
|
||||
}
|
||||
h_ /= 6;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
Color3i ColorHSV::ToRGB() const {
|
||||
typedef double FloatT;
|
||||
FloatT r = 0;
|
||||
FloatT g = 0;
|
||||
FloatT b = 0;
|
||||
|
||||
int i = int(h_ * 6);
|
||||
FloatT f = h_ * 6.0 - static_cast<FloatT>(i);
|
||||
FloatT p = v_ * (1.0 - s_);
|
||||
FloatT q = v_ * (1.0 - f * s_);
|
||||
FloatT t = v_ * (1.0 - (1.0 - f) * s_);
|
||||
|
||||
switch(i % 6){
|
||||
case 0: r = v_, g = t, b = p; break;
|
||||
case 1: r = q, g = v_, b = p; break;
|
||||
case 2: r = p, g = v_, b = t; break;
|
||||
case 3: r = p, g = q, b = v_; break;
|
||||
case 4: r = t, g = p, b = v_; break;
|
||||
case 5: r = v_, g = p, b = q; break;
|
||||
}
|
||||
|
||||
return Color3i(round(r * 255), round(g * 255), round(b * 255));
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#ifndef COLOR_H_
|
||||
#define COLOR_H_
|
||||
|
||||
#include "fl/stdint.h"
|
||||
|
||||
struct Color3i {
|
||||
static Color3i Black() { return Color3i(0x0, 0x0, 0x0); }
|
||||
static Color3i White() { return Color3i(0xff, 0xff, 0xff); }
|
||||
static Color3i Red() { return Color3i(0xff, 0x00, 0x00); }
|
||||
static Color3i Orange() { return Color3i(0xff, 0xff / 2,00); }
|
||||
static Color3i Yellow() { return Color3i(0xff, 0xff,00); }
|
||||
static Color3i Green() { return Color3i(0x00, 0xff, 0x00); }
|
||||
static Color3i Cyan() { return Color3i(0x00, 0xff, 0xff); }
|
||||
static Color3i Blue() { return Color3i(0x00, 0x00, 0xff); }
|
||||
Color3i(uint8_t r, uint8_t g, uint8_t b) { Set(r,g,b); }
|
||||
Color3i() { Set(0xff, 0xff, 0xff); }
|
||||
Color3i(const Color3i& other) { Set(other); }
|
||||
Color3i& operator=(const Color3i& other) {
|
||||
if (this != &other) {
|
||||
Set(other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Set(uint8_t r, uint8_t g, uint8_t b) { r_ = r; g_ = g; b_ = b; }
|
||||
void Set(const Color3i& c) { Set(c.r_, c.g_, c.b_); }
|
||||
void Mul(const Color3i& other_color);
|
||||
void Mulf(float scale); // Input range is 0.0 -> 1.0
|
||||
void Mul(uint8_t val) {
|
||||
Mul(Color3i(val, val, val));
|
||||
}
|
||||
void Sub(const Color3i& color);
|
||||
void Add(const Color3i& color);
|
||||
uint8_t Get(int rgb_index) const;
|
||||
void Set(int rgb_index, uint8_t val);
|
||||
void Fill(uint8_t val) { Set(val, val, val); }
|
||||
uint8_t MaxRGB() const {
|
||||
uint8_t max_r_g = r_ > g_ ? r_ : g_;
|
||||
return max_r_g > b_ ? max_r_g : b_;
|
||||
}
|
||||
|
||||
template <typename PrintStream>
|
||||
inline void Print(PrintStream* stream) const {
|
||||
stream->print("RGB:\t");
|
||||
stream->print(r_); stream->print(",\t");
|
||||
stream->print(g_); stream->print(",\t");
|
||||
stream->print(b_);
|
||||
stream->print("\n");
|
||||
}
|
||||
|
||||
void Interpolate(const Color3i& other_color, float t);
|
||||
|
||||
uint8_t* At(int rgb_index);
|
||||
const uint8_t* At(int rgb_index) const;
|
||||
|
||||
uint8_t r_, g_, b_;
|
||||
};
|
||||
|
||||
|
||||
struct ColorHSV {
|
||||
ColorHSV() : h_(0), s_(0), v_(0) {}
|
||||
ColorHSV(float h, float s, float v) {
|
||||
Set(h,s,v);
|
||||
}
|
||||
explicit ColorHSV(const Color3i& color) {
|
||||
FromRGB(color);
|
||||
}
|
||||
ColorHSV& operator=(const Color3i& color) {
|
||||
FromRGB(color);
|
||||
return *this;
|
||||
}
|
||||
ColorHSV(const ColorHSV& other) {
|
||||
Set(other);
|
||||
}
|
||||
ColorHSV& operator=(const ColorHSV& other) {
|
||||
if (this != &other) {
|
||||
Set(other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
void Set(const ColorHSV& other) {
|
||||
Set(other.h_, other.s_, other.v_);
|
||||
}
|
||||
void Set(float h, float s, float v) {
|
||||
h_ = h;
|
||||
s_ = s;
|
||||
v_ = v;
|
||||
}
|
||||
|
||||
template <typename PrintStream>
|
||||
inline void Print(PrintStream* stream) {
|
||||
stream->print("HSV:\t");
|
||||
stream->print(h_); stream->print(",\t");
|
||||
stream->print(s_); stream->print(",\t");
|
||||
stream->print(v_); stream->print("\n");
|
||||
}
|
||||
|
||||
bool operator==(const ColorHSV& other) const {
|
||||
return h_ == other.h_ && s_ == other.s_ && v_ == other.v_;
|
||||
}
|
||||
bool operator!=(const ColorHSV& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
void FromRGB(const Color3i& rgb);
|
||||
|
||||
Color3i ToRGB() const;
|
||||
|
||||
float h_, s_, v_;
|
||||
};
|
||||
|
||||
|
||||
#endif // COLOR_H_
|
||||
@@ -0,0 +1,172 @@
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
#include "fl/stdint.h"
|
||||
|
||||
|
||||
#include "./color_mapper.h"
|
||||
|
||||
#include "./color.h"
|
||||
#include "./util.h"
|
||||
|
||||
// Serves as the pallet for selecting a color.
|
||||
struct ColorScheme {
|
||||
ColorScheme(const ColorHSV& c0,
|
||||
const ColorHSV& c1,
|
||||
const ColorHSV& c2,
|
||||
const ColorHSV& c3,
|
||||
const ColorHSV& c4,
|
||||
const ColorHSV& c5,
|
||||
const ColorHSV& c6,
|
||||
const ColorHSV& c7,
|
||||
const ColorHSV& c8,
|
||||
const ColorHSV& c9,
|
||||
const ColorHSV& c10,
|
||||
const ColorHSV& c11) {
|
||||
data[0] = c0;
|
||||
data[1] = c1;
|
||||
data[2] = c2;
|
||||
data[3] = c3;
|
||||
data[4] = c4;
|
||||
data[5] = c5;
|
||||
data[6] = c6;
|
||||
data[7] = c7;
|
||||
data[8] = c8;
|
||||
data[9] = c9;
|
||||
data[10] = c10;
|
||||
data[11] = c11;
|
||||
}
|
||||
ColorHSV data[12];
|
||||
};
|
||||
|
||||
const ColorScheme& SelectColorScheme(int indx) {
|
||||
static ColorScheme color_schemes[] = {
|
||||
|
||||
// Coda
|
||||
ColorScheme(
|
||||
ColorHSV(Color3i(0xff, 0x00, 0x00)), // C
|
||||
ColorHSV(Color3i(0x00, 0x80, 0xff)), // C
|
||||
ColorHSV(Color3i(0xff, 0xff, 0x00)), // D
|
||||
ColorHSV(Color3i(0x80, 0x00, 0xff)), // D#
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x00)), // E
|
||||
ColorHSV(Color3i(0xff, 0x00, 0x80)), // F
|
||||
ColorHSV(Color3i(0x00, 0xff, 0xff)), // F#
|
||||
ColorHSV(Color3i(0xff, 0x80, 0x00)), // G
|
||||
ColorHSV(Color3i(0x00, 0x00, 0xff)), // G#
|
||||
ColorHSV(Color3i(0x80, 0xff, 0x00)), // A
|
||||
ColorHSV(Color3i(0xff, 0x00, 0xff)), // A#
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x80))
|
||||
),
|
||||
|
||||
// Frequency
|
||||
ColorScheme(
|
||||
ColorHSV(Color3i(0xfc, 0xff, 0x00)), // C
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x73)), // C#
|
||||
ColorHSV(Color3i(0x00, 0xa7, 0xff)),
|
||||
ColorHSV(Color3i(0x00, 0x20, 0xff)),
|
||||
ColorHSV(Color3i(0x35, 0x00, 0xff)),
|
||||
ColorHSV(Color3i(0x56, 0x00, 0xb6)),
|
||||
ColorHSV(Color3i(0x4e, 0x00, 0x6c)),
|
||||
ColorHSV(Color3i(0x9f, 0x00, 0x00)), // G
|
||||
ColorHSV(Color3i(0xdb, 0x00, 0x00)),
|
||||
ColorHSV(Color3i(0xff, 0x36, 0x00)), // A
|
||||
ColorHSV(Color3i(0xff, 0xc1, 0x00)),
|
||||
ColorHSV(Color3i(0xbf, 0xff, 0x00)) // B
|
||||
),
|
||||
|
||||
// SCRIABIN
|
||||
ColorScheme(
|
||||
ColorHSV(Color3i(0xff, 0x00, 0x00)), // C
|
||||
ColorHSV(Color3i(0x8f, 0x00, 0xff)), // C#
|
||||
ColorHSV(Color3i(0xff, 0xff, 0x00)), // D
|
||||
ColorHSV(Color3i(0x71, 0x63, 0x95)), // D#
|
||||
ColorHSV(Color3i(0x4f, 0xa1, 0xc2)), // E
|
||||
ColorHSV(Color3i(0xc1, 0x01, 0x01)), // F
|
||||
ColorHSV(Color3i(0x00, 0x00, 0xff)), // F#
|
||||
ColorHSV(Color3i(0xff, 0x66, 0x00)), // G
|
||||
ColorHSV(Color3i(0x96, 0x00, 0xff)), // G#
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x00)), // A
|
||||
ColorHSV(Color3i(0x71, 0x63, 0x95)), // A#
|
||||
ColorHSV(Color3i(0x4f, 0xa1, 0xc2)) // B
|
||||
),
|
||||
|
||||
// LUIS BERTRAND CASTEL
|
||||
ColorScheme(
|
||||
ColorHSV(Color3i(0x00, 0x00, 0xff)), // C
|
||||
ColorHSV(Color3i(0x0d, 0x98, 0xba)), // C#
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x00)), // D
|
||||
ColorHSV(Color3i(0x80, 0x80, 0x00)), // D#
|
||||
ColorHSV(Color3i(0xff, 0xff, 0x00)), // E
|
||||
ColorHSV(Color3i(0xff, 0xd7, 0x00)), // F
|
||||
ColorHSV(Color3i(0xff, 0x5a, 0x00)), // F#
|
||||
ColorHSV(Color3i(0xff, 0x00, 0x00)), // G
|
||||
ColorHSV(Color3i(0xdc, 0x14, 0x3c)), // G#
|
||||
ColorHSV(Color3i(0x8f, 0x00, 0xff)), // A
|
||||
ColorHSV(Color3i(0x22, 0x00, 0xcd)), // A#
|
||||
ColorHSV(Color3i(0x5a, 0x00, 0x95)) // B
|
||||
),
|
||||
|
||||
// H VON HELMHOHOLTZ
|
||||
ColorScheme(
|
||||
ColorHSV(Color3i(0xff, 0xff, 0x06)), // C
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x00)), // C#
|
||||
ColorHSV(Color3i(0x21, 0x9e, 0xbd)), // D
|
||||
ColorHSV(Color3i(0x00, 0x80, 0xff)), // D#
|
||||
ColorHSV(Color3i(0x6f, 0x00, 0xff)), // E
|
||||
ColorHSV(Color3i(0x8f, 0x00, 0xff)), // F
|
||||
ColorHSV(Color3i(0xff, 0x00, 0x00)), // F#
|
||||
ColorHSV(Color3i(0xff, 0x20, 0x00)), // G
|
||||
ColorHSV(Color3i(0xff, 0x38, 0x00)), // G#
|
||||
ColorHSV(Color3i(0xff, 0x3f, 0x00)), // A
|
||||
ColorHSV(Color3i(0xff, 0x3f, 0x34)), // A#
|
||||
ColorHSV(Color3i(0xff, 0xa5, 0x00)) // B
|
||||
),
|
||||
|
||||
// ZIEVERINK
|
||||
ColorScheme(
|
||||
ColorHSV(Color3i(0x9a, 0xcd, 0x32)), // C
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x00)), // C#
|
||||
ColorHSV(Color3i(0x00, 0xdd, 0xdd)), // D
|
||||
ColorHSV(Color3i(0x00, 0x00, 0xff)), // D#
|
||||
ColorHSV(Color3i(0x6f, 0x00, 0xff)), // E
|
||||
ColorHSV(Color3i(0x8f, 0x00, 0xff)), // F
|
||||
ColorHSV(Color3i(0x7f, 0x1a, 0xe5)), // F#
|
||||
ColorHSV(Color3i(0xbd, 0x00, 0x20)), // G
|
||||
ColorHSV(Color3i(0xff, 0x00, 0x00)), // G#
|
||||
ColorHSV(Color3i(0xff, 0x44, 0x00)), // A
|
||||
ColorHSV(Color3i(0xff, 0xc4, 0x00)), // A#
|
||||
ColorHSV(Color3i(0xff, 0xff, 0x00)) // B
|
||||
),
|
||||
|
||||
// ROSICRUCIAN ORDER
|
||||
ColorScheme(
|
||||
ColorHSV(Color3i(0x9a, 0xcd, 0x32)), // C
|
||||
ColorHSV(Color3i(0x00, 0xff, 0x00)), // C#
|
||||
ColorHSV(Color3i(0x21, 0x9e, 0xbd)), // D
|
||||
ColorHSV(Color3i(0x00, 0x00, 0xff)), // D#
|
||||
ColorHSV(Color3i(0x8a, 0x2b, 0xe2)), // E
|
||||
ColorHSV(Color3i(0x8b, 0x00, 0xff)), // F
|
||||
ColorHSV(Color3i(0xf7, 0x53, 0x94)), // F#
|
||||
ColorHSV(Color3i(0xbd, 0x00, 0x20)), // G
|
||||
ColorHSV(Color3i(0xee, 0x20, 0x4d)), // G#
|
||||
ColorHSV(Color3i(0xff, 0x3f, 0x34)), // A
|
||||
ColorHSV(Color3i(0xff, 0xa5, 0x00)), // A#
|
||||
ColorHSV(Color3i(0xff, 0xff, 0x00)) // B
|
||||
),
|
||||
};
|
||||
|
||||
static const int n = sizeof(color_schemes) / sizeof(color_schemes[0]);
|
||||
indx = constrain(indx, 0, n - 1);
|
||||
|
||||
return color_schemes[indx];
|
||||
};
|
||||
|
||||
const ColorHSV SelectColor(int midi_note, float brightness, int color_selector_val) {
|
||||
const uint8_t fun_note = FundamentalNote(midi_note);
|
||||
const ColorScheme& color_scheme = SelectColorScheme(color_selector_val);
|
||||
ColorHSV color = color_scheme.data[fun_note];
|
||||
color.v_ = brightness;
|
||||
return color;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
#ifndef COLOR_MAPPER_H_
|
||||
#define COLOR_MAPPER_H_
|
||||
|
||||
#include "./color.h"
|
||||
|
||||
const ColorHSV SelectColor(int midi_note, float brightness, int color_selector_val);
|
||||
|
||||
#endif // COLOR_MAPPER_H_
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#if defined(__AVR__) || defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_RP2350) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_TEENSYLC)
|
||||
#define ENABLE_SKETCH 0
|
||||
#else
|
||||
#define ENABLE_SKETCH 1
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
#include "dprint.h"
|
||||
|
||||
bool is_debugging = false;
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fl/unused.h"
|
||||
|
||||
|
||||
extern bool is_debugging;
|
||||
//#define ENABLE_DPRINT
|
||||
#ifdef ENABLE_DPRINT
|
||||
#define dprint(x) if (is_debugging) { Serial.print(x); }
|
||||
#define dprintln(x) if (is_debugging) { Serial.println(x); }
|
||||
#else
|
||||
#define dprint(x) FL_UNUSED(x)
|
||||
#define dprintln(x) FL_UNUSED(x)
|
||||
#endif
|
||||
@@ -0,0 +1,58 @@
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
#include "./framebuffer.h"
|
||||
|
||||
#include "./color.h"
|
||||
|
||||
FrameBufferBase::FrameBufferBase(Color3i* array, int n_pixels)
|
||||
: color_array_(array), n_color_array_(n_pixels) {}
|
||||
|
||||
FrameBufferBase::~FrameBufferBase() {}
|
||||
|
||||
void FrameBufferBase::Set(int i, const Color3i& c) {
|
||||
color_array_[i] = c;
|
||||
}
|
||||
void FrameBufferBase::Set(int i, int length, const Color3i& color) {
|
||||
for (int j = 0; j < length; ++j) {
|
||||
Set(i + j, color);
|
||||
}
|
||||
}
|
||||
void FrameBufferBase::FillColor(const Color3i& color) {
|
||||
for (int i = 0; i < n_color_array_; ++i) {
|
||||
color_array_[i] = color;
|
||||
}
|
||||
}
|
||||
void FrameBufferBase::ApplyBlendSubtract(const Color3i& color) {
|
||||
for (int i = 0; i < n_color_array_; ++i) {
|
||||
color_array_[i].Sub(color);
|
||||
}
|
||||
}
|
||||
void FrameBufferBase::ApplyBlendAdd(const Color3i& color) {
|
||||
for (int i = 0; i < n_color_array_; ++i) {
|
||||
color_array_[i].Add(color);
|
||||
}
|
||||
}
|
||||
void FrameBufferBase::ApplyBlendMultiply(const Color3i& color) {
|
||||
for (int i = 0; i < n_color_array_; ++i) {
|
||||
color_array_[i].Mul(color);
|
||||
}
|
||||
}
|
||||
Color3i* FrameBufferBase::GetIterator(int i) {
|
||||
return color_array_ + i;
|
||||
}
|
||||
// Length in pixels.
|
||||
int FrameBufferBase::length() const { return n_color_array_; }
|
||||
|
||||
FrameBuffer::FrameBuffer(int n_pixels)
|
||||
: FrameBufferBase(static_cast<Color3i*>(malloc(sizeof(Color3i) * n_pixels)),
|
||||
n_pixels) {
|
||||
}
|
||||
|
||||
FrameBuffer::~FrameBuffer() {
|
||||
free(color_array_);
|
||||
color_array_ = NULL;
|
||||
n_color_array_ = 0;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
|
||||
#ifndef FRAME_BUFFER_H_
|
||||
#define FRAME_BUFFER_H_
|
||||
|
||||
struct Color3i;
|
||||
|
||||
class FrameBufferBase {
|
||||
public:
|
||||
FrameBufferBase(Color3i* array, int n_pixels);
|
||||
virtual ~FrameBufferBase();
|
||||
|
||||
void Set(int i, const Color3i& c);
|
||||
void Set(int i, int length, const Color3i& color);
|
||||
void FillColor(const Color3i& color);
|
||||
void ApplyBlendSubtract(const Color3i& color);
|
||||
void ApplyBlendAdd(const Color3i& color);
|
||||
void ApplyBlendMultiply(const Color3i& color);
|
||||
Color3i* GetIterator(int i);
|
||||
|
||||
// Length in pixels.
|
||||
int length() const;
|
||||
|
||||
protected:
|
||||
Color3i* color_array_;
|
||||
int n_color_array_;
|
||||
};
|
||||
|
||||
class FrameBuffer : public FrameBufferBase {
|
||||
public:
|
||||
FrameBuffer(int n_pixels);
|
||||
virtual ~FrameBuffer();
|
||||
};
|
||||
|
||||
#endif // FRAME_BUFFER_H_
|
||||
@@ -0,0 +1,205 @@
|
||||
|
||||
#include "./led_layout_array.h"
|
||||
#include "./settings.h"
|
||||
|
||||
LedColumns LedCurtinArray() {
|
||||
static const int kLedRepeatTable[] = {
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22,
|
||||
22
|
||||
};
|
||||
const int* out = kLedRepeatTable;
|
||||
const int size = sizeof(kLedRepeatTable) / sizeof(*kLedRepeatTable);
|
||||
return LedColumns(out, size);
|
||||
}
|
||||
|
||||
LedColumns LedLuminescentArray() {
|
||||
/////////////////////////////////////////////////////////
|
||||
// Repeat data for the LED piano.
|
||||
static const int kLedRepeatTable[] = {
|
||||
25,
|
||||
25,
|
||||
26,
|
||||
26,
|
||||
27,
|
||||
27,
|
||||
28,
|
||||
27,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
27,
|
||||
28,
|
||||
27,
|
||||
27,
|
||||
26,
|
||||
26,
|
||||
25,
|
||||
25,
|
||||
24,
|
||||
24,
|
||||
23,
|
||||
23,
|
||||
21,
|
||||
20,
|
||||
18,
|
||||
17,
|
||||
15,
|
||||
14,
|
||||
12,
|
||||
11,
|
||||
10,
|
||||
10,
|
||||
9,
|
||||
9,
|
||||
8,
|
||||
8,
|
||||
7,
|
||||
7,
|
||||
6,
|
||||
6,
|
||||
5,
|
||||
6,
|
||||
5,
|
||||
5,
|
||||
4,
|
||||
5,
|
||||
4,
|
||||
4,
|
||||
3,
|
||||
4,
|
||||
3,
|
||||
3,
|
||||
2,
|
||||
2,
|
||||
1
|
||||
};
|
||||
const int* out = kLedRepeatTable;
|
||||
const int size = sizeof(kLedRepeatTable) / sizeof(*kLedRepeatTable);
|
||||
return LedColumns(out, size);
|
||||
}
|
||||
|
||||
LedColumns LedLayoutArray() {
|
||||
if (kUseLedCurtin) {
|
||||
return LedCurtinArray();
|
||||
} else {
|
||||
return LedLuminescentArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
#ifndef LED_ARRAY_H_
|
||||
#define LED_ARRAY_H_
|
||||
|
||||
struct LedColumns {
|
||||
LedColumns(const int* a, int l) : array(a), length(l) {}
|
||||
LedColumns(const LedColumns& other) : array(other.array), length(other.length) {}
|
||||
const int* array;
|
||||
int length;
|
||||
};
|
||||
|
||||
LedColumns LedLayoutArray();
|
||||
|
||||
#endif // LED_ARRAY_H_
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
#ifndef LED_ROPE_INTERFACE_H_
|
||||
#define LED_ROPE_INTERFACE_H_
|
||||
|
||||
#include "./color.h"
|
||||
|
||||
class LedRopeInterface {
|
||||
public:
|
||||
virtual ~LedRopeInterface() {}
|
||||
virtual void Set(int i, const Color3i& c) = 0;
|
||||
|
||||
virtual void Set(int i, int length, const Color3i& color) {
|
||||
for (int j = 0; j < length; ++j) {
|
||||
Set(i + j, color);
|
||||
}
|
||||
}
|
||||
|
||||
virtual Color3i* GetIterator(int i) = 0;
|
||||
|
||||
virtual int length() const = 0;
|
||||
|
||||
virtual void DrawSequentialRepeat(int repeat) = 0;
|
||||
virtual void DrawRepeat(const int* value_array, int array_length) = 0;
|
||||
|
||||
virtual void RawBeginDraw() = 0;
|
||||
virtual void RawDrawPixel(const Color3i& c) = 0;
|
||||
virtual void RawDrawPixels(const Color3i& c, int n) = 0;
|
||||
virtual void RawDrawPixel(uint8_t r, uint8_t g, uint8_t b) = 0;
|
||||
virtual void RawCommitDraw() = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif // LED_ROPE_INTERFACE_H_
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef CONSTAINTS_H_
|
||||
#define CONSTAINTS_H_
|
||||
|
||||
enum {
|
||||
kNumKeys = 88, // Don't change this. 88 keys on a keyboard.
|
||||
kNumLightsPerNote = 20,
|
||||
|
||||
// Controls the speed of the light rope. Higher values result in
|
||||
// slower draw time, however the data integrity increases.
|
||||
kLightClockDivisor = 12,
|
||||
kNewLightClockDivisor = 16,
|
||||
|
||||
// Led Curtain is a mode that we used on the bus. When this is
|
||||
// zero it's assume that we are using the TCL led lighting.
|
||||
kUseLedCurtin = 0,
|
||||
|
||||
kShowFps = 0, // If true then the fps is printed to the console.
|
||||
|
||||
// Coda's keyboard indicates that this is the value when the
|
||||
// foot pedal is pressed. There is probably a more universal
|
||||
// way of detecting this value that works with more keyboards.
|
||||
kMidiFootPedal = 64,
|
||||
};
|
||||
|
||||
#endif // CONSTAINTS_H_
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "./util.h"
|
||||
|
||||
#include "ApproximatingFunction.h"
|
||||
#include "settings.h"
|
||||
|
||||
/*
|
||||
// C - 0, C# - 1, D - 2, D# - 3... B - 11.
|
||||
// http://cote.cc/w/wp-content/uploads/drupal/blog/logic-midi-note-numbers.png
|
||||
*/
|
||||
uint8_t FundamentalNote(int midi_note) {
|
||||
return midi_note % 12;
|
||||
}
|
||||
|
||||
float mapf(float x, float in_min, float in_max, float out_min, float out_max) {
|
||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
}
|
||||
|
||||
// Given an input time.
|
||||
float AttackRemapFactor(uint32_t delta_t_ms) {
|
||||
typedef InterpData<uint32_t, float> Datum;
|
||||
static const Datum kData[] = {
|
||||
Datum(0, .5),
|
||||
Datum(80, 1.0),
|
||||
};
|
||||
|
||||
static const int n = sizeof(kData) / sizeof(kData[0]);
|
||||
return Interp(delta_t_ms, kData, n);
|
||||
}
|
||||
|
||||
float MapDecayTime(uint8_t key_idx) {
|
||||
typedef InterpData<uint8_t, float> Datum;
|
||||
static const float bias = 1.3f;
|
||||
// key then time for decay in milliseconds.
|
||||
// First value is the KEY on the keyboard, second value is the
|
||||
// time. The KEY must be IN ORDER or else the algorithm will fail.
|
||||
static const Datum kInterpData[] = {
|
||||
Datum(0, 21.0f * 1000.0f * bias),
|
||||
Datum(11, 19.4 * 1000.0f * bias),
|
||||
Datum(22, 15.1f * 1000.0f * bias),
|
||||
Datum(35, 12.5f * 1000.0f * bias),
|
||||
Datum(44, 10.f * 1000.0f * bias),
|
||||
Datum(50, 8.1f * 1000.0f * bias),
|
||||
Datum(53, 5.3f * 1000.0f * bias),
|
||||
Datum(61, 4.0f * 1000.0f * bias),
|
||||
Datum(66, 5.0f * 1000.0f * bias),
|
||||
Datum(69, 4.6f * 1000.0f * bias),
|
||||
Datum(70, 4.4f * 1000.0f * bias),
|
||||
Datum(71, 4.3f * 1000.0f * bias),
|
||||
Datum(74, 3.9f * 1000.0f * bias),
|
||||
Datum(80, 1.9f * 1000.0f * bias),
|
||||
Datum(81, 1.8f * 1000.0f * bias),
|
||||
Datum(82, 1.7f * 1000.0f * bias),
|
||||
Datum(83, 1.5f * 1000.0f * bias),
|
||||
Datum(84, 1.3f * 1000.0f * bias),
|
||||
Datum(86, 1.0f * 1000.0f * bias),
|
||||
Datum(87, 0.9f * 1000.0f * bias),
|
||||
};
|
||||
|
||||
static const int n = sizeof(kInterpData) / sizeof(kInterpData[0]);
|
||||
float approx_val = Interp(key_idx, kInterpData, n);
|
||||
return approx_val;
|
||||
}
|
||||
|
||||
// Returns a value in the range 1->0 indicating how intense the note is. This
|
||||
// value will go to 0 as time progresses, and will be 1 when the note is first
|
||||
// pressed.
|
||||
float CalcDecayFactor(bool sustain_pedal_on,
|
||||
bool key_on,
|
||||
int key_idx,
|
||||
float velocity,
|
||||
bool dampened_key,
|
||||
float time_elapsed_ms) {
|
||||
|
||||
static const float kDefaultDecayTime = .2f * 1000.f;
|
||||
static const float kBias = 1.10;
|
||||
float decay_time = kDefaultDecayTime; // default - no sustain.
|
||||
if (key_on || sustain_pedal_on || !dampened_key) {
|
||||
decay_time = MapDecayTime(key_idx) * max(0.25f, velocity);
|
||||
}
|
||||
// decay_interp is a value which starts off as 1.0 to signify the start of the
|
||||
// key press and gradually decreases to 0.0. For example, if the decay time is 1 second
|
||||
// then at the time = 0s, decay_interp is 1.0, and after one second decay_interp is 0.0
|
||||
float intensity_factor = mapf(time_elapsed_ms,
|
||||
0.0, decay_time * kBias,
|
||||
1.0, 0.0); // Startup at full brightness -> no brighness.
|
||||
|
||||
|
||||
// When decay_interp reaches 0, the lighting sequence is effectively finished. However
|
||||
// because this is time based and time keeps on going this value will move into negative
|
||||
// territory, we take care of this by simply clamping all negative values to 0.0.
|
||||
intensity_factor = constrain(intensity_factor, 0.0f, 1.0f);
|
||||
return intensity_factor;
|
||||
}
|
||||
|
||||
float ToBrightness(int velocity) {
|
||||
typedef InterpData<int, float> Datum;
|
||||
static const Datum kData[] = {
|
||||
Datum(0, 0.02),
|
||||
Datum(32, 0.02),
|
||||
Datum(64, 0.10),
|
||||
Datum(80, 0.30),
|
||||
Datum(90, 0.90),
|
||||
Datum(100, 1.00),
|
||||
Datum(120, 1.00),
|
||||
Datum(127, 1.00)
|
||||
};
|
||||
|
||||
static const int n = sizeof(kData) / sizeof(kData[0]);
|
||||
const float val = Interp(velocity, kData, n);
|
||||
|
||||
return val;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef UTIL_H_
|
||||
#define UTIL_H_
|
||||
|
||||
|
||||
#include "fl/stdint.h"
|
||||
|
||||
#include "./ApproximatingFunction.h"
|
||||
#include "./settings.h"
|
||||
|
||||
#ifndef round
|
||||
#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
|
||||
#endif // round
|
||||
|
||||
/*
|
||||
// C - 0, C# - 1, D - 2, D# - 3... B - 11.
|
||||
// http://cote.cc/w/wp-content/uploads/drupal/blog/logic-midi-note-numbers.png
|
||||
*/
|
||||
uint8_t FundamentalNote(int midi_note);
|
||||
|
||||
float mapf(float x, float in_min, float in_max, float out_min, float out_max);
|
||||
|
||||
// Given an input time.
|
||||
float AttackRemapFactor(uint32_t delta_t_ms);
|
||||
|
||||
float MapDecayTime(uint8_t key_idx);
|
||||
|
||||
// Returns a value in the range 1->0 indicating how intense the note is. This
|
||||
// value will go to 0 as time progresses, and will be 1 when the note is first
|
||||
// pressed.
|
||||
float CalcDecayFactor(bool sustain_pedal_on,
|
||||
bool key_on,
|
||||
int key_idx,
|
||||
float velocity,
|
||||
bool dampened_key,
|
||||
float time_elapsed_ms);
|
||||
|
||||
float ToBrightness(int velocity);
|
||||
|
||||
#endif // UTIL_H_
|
||||
Reference in New Issue
Block a user