Files

copied
Last update 5 years 8 months
Filesframes
..
bootloader
drivers
hardware_design
resources
__init__.py
frames.cc
keyframer.cc
keyframer.h
makefile
poly_lfo.cc
poly_lfo.h
resources.cc
resources.h
ui.cc
ui.h
keyframer.cc
// Copyright 2013 Olivier Gillet. // // Author: Olivier Gillet (ol.gillet@gmail.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // // See http://creativecommons.org/licenses/MIT/ for more information. // // ----------------------------------------------------------------------------- // // Keyframe interpolator #include "frames/keyframer.h" #include <algorithm> #ifndef TEST #include "stmlib/system/storage.h" #endif // TEST #include "frames/resources.h" namespace frames { using namespace std; /* static */ const uint8_t Keyframer::palette_[kNumPaletteEntries][3] = { { 255, 0, 0 }, { 255, 64, 0 }, { 255, 255, 0 }, { 64, 255, 0 }, { 0, 255, 64 }, { 0, 0, 255 }, { 255, 0, 255 }, { 255, 0, 64 }, }; #ifndef TEST stmlib::Storage<0x8020000, 4> storage; #endif // TEST void Keyframer::Init() { #ifndef TEST if (!storage.ParsimoniousLoad(keyframes_, SETTINGS_SIZE, &version_token_)) { for (uint8_t i = 0; i < kNumChannels; ++i) { settings_[i].easing_curve = EASING_CURVE_LINEAR; settings_[i].response = 0; } extra_settings_ = 0; dc_offset_frame_modulation_ = 32767; Clear(); } #endif // TEST } void Keyframer::Save(uint32_t extra_settings) { extra_settings_ = extra_settings; #ifndef TEST storage.ParsimoniousSave(keyframes_, SETTINGS_SIZE, &version_token_); #endif // TEST } void Keyframer::Calibrate(int32_t dc_offset_frame_modulation) { dc_offset_frame_modulation_ = dc_offset_frame_modulation; #ifndef TEST storage.ParsimoniousSave(keyframes_, SETTINGS_SIZE, &version_token_); #endif // TEST } void Keyframer::Clear() { Keyframe empty; empty.timestamp = 0; empty.id = 0; fill(&empty.values[0], &empty.values[kNumChannels], 0); fill(&keyframes_[0], &keyframes_[kMaxNumKeyframe], empty); num_keyframes_ = 0; id_counter_ = 0; } uint16_t Keyframer::FindKeyframe(uint16_t timestamp) { if (!num_keyframes_) { return 0; } Keyframe dummy; dummy.timestamp = timestamp; return lower_bound( keyframes_, keyframes_ + num_keyframes_, dummy, KeyframeLess()) - keyframes_; } /* static */ uint16_t Keyframer::ConvertToDacCode(uint16_t gain, uint8_t response) { // Exponential response is easy, straight to the 2164. int32_t exponential = 65535 - gain; // Use a lookup table and interpolation to linearize the 2164 int32_t a = lut_vca_linear[gain >> 6]; int32_t b = lut_vca_linear[(gain >> 6) + 1]; int32_t linear = a + ((b - a) * ((gain << 10) & 0xffff) >> 16); // Blend linear and exponential responses. uint16_t balance = lut_response_balance[response]; return (linear + ((exponential - linear) * balance >> 15)) >> 4; } inline uint16_t Keyframer::Easing( int32_t from, int32_t to, uint32_t scale, EasingCurve curve) { int32_t shaped_scale = scale; if (curve == EASING_CURVE_STEP) { shaped_scale = scale < 32768 ? 0 : 65535; } else if (curve >= EASING_CURVE_IN_QUARTIC) { const uint16_t* easing_curve = lookup_table_table[ curve - EASING_CURVE_IN_QUARTIC]; int32_t scale_a = easing_curve[scale >> 6]; int32_t scale_b = easing_curve[(scale >> 6) + 1]; shaped_scale = scale_a + (((scale_b - scale_a) >> 1) * \ ((scale << 10) & 0xffff) >> 15); } return from + ((to - from) * (shaped_scale >> 1) >> 15); } uint16_t Keyframer::SampleAnimation( uint8_t channel, uint16_t tick, bool easing) { uint16_t sample = Easing( tick > 32768 ? 65535 : 0, tick > 32768 ? 0 : 65535, (tick << 1) & 0xffff, easing ? settings_[channel].easing_curve : EASING_CURVE_LINEAR); if (!easing) { int32_t linear = sample; int32_t exponential = lut_exponential[sample >> 8]; uint16_t balance = lut_response_balance[settings_[channel].response]; sample = linear + ((exponential - linear) * balance >> 15); } return sample; } int16_t Keyframer::FindNearestKeyframe(uint16_t timestamp, uint16_t tolerance) { if (!num_keyframes_) { return -1; } uint16_t index = FindKeyframe(timestamp); uint16_t search_start = index ? index - 1 : 0; uint16_t search_end = index < num_keyframes_ - 1 ? index + 2 : num_keyframes_; for (uint16_t i = search_start; i < search_end; ++i) { uint16_t t = keyframes_[i].timestamp; int32_t distance = static_cast<int32_t>(t) - static_cast<int32_t>(timestamp); if (distance < tolerance && -distance < tolerance) { return i; } } return -1; } bool Keyframer::AddKeyframe(uint16_t timestamp, uint16_t* values) { if (num_keyframes_ == kMaxNumKeyframe) { return false; } uint16_t insertion_point = FindKeyframe(timestamp); if (insertion_point >= num_keyframes_ || keyframes_[insertion_point].timestamp != timestamp) { for (int16_t i = num_keyframes_ - 1; i >= insertion_point; --i) { keyframes_[i + 1] = keyframes_[i]; } keyframes_[insertion_point].timestamp = timestamp; keyframes_[insertion_point].id = id_counter_++; ++num_keyframes_; } copy(values, values + kNumChannels, keyframes_[insertion_point].values); return true; } bool Keyframer::RemoveKeyframe(uint16_t timestamp) { if (!num_keyframes_) { return false; } uint16_t splice_point = FindKeyframe(timestamp); if (keyframes_[splice_point].timestamp != timestamp) { return false; } for (uint16_t i = splice_point; i < num_keyframes_ - 1; ++i) { keyframes_[i] = keyframes_[i + 1]; } --num_keyframes_; return true; } void Keyframer::Evaluate(uint16_t timestamp) { if (!num_keyframes_) { copy(immediate_, immediate_ + kNumChannels, levels_); fill(color_, color_ + 3, 0xff); position_ = -1; nearest_keyframe_ = -1; } else { uint16_t position = FindKeyframe(timestamp); position_ = position; // Check for the areas before the first keyframe, and after the last // keyframe. if (position == 0 || position == num_keyframes_) { const Keyframe& source = keyframes_[position == 0 ? 0 : num_keyframes_ - 1]; copy(source.values, source.values + kNumChannels, levels_); const uint8_t* palette = palette_[source.id & (kNumPaletteEntries - 1)]; copy(palette, palette + 3, color_); } else { // This is where the real interpolation takes place. const Keyframe& a = keyframes_[position - 1]; const Keyframe& b = keyframes_[position]; uint32_t scale = timestamp - a.timestamp; scale <<= 16; scale /= (b.timestamp - a.timestamp); for (uint8_t i = 0; i < kNumChannels; ++i) { int32_t from = a.values[i]; int32_t to = b.values[i]; levels_[i] = Easing(from, to, scale, settings_[i].easing_curve); } for (uint8_t i = 0; i < 3; ++i) { uint8_t a_color = palette_[a.id & (kNumPaletteEntries - 1)][i]; uint8_t b_color = palette_[b.id & (kNumPaletteEntries - 1)][i]; color_[i] = a_color + ((b_color - a_color) * scale >> 16); } } uint16_t t_this = timestamp - \ (position == 0 ? 0 : keyframes_[position - 1].timestamp); uint16_t t_next = keyframes_[position].timestamp - timestamp; nearest_keyframe_ = t_next < t_this ? position + 1 : position; } for (uint16_t i = 0; i < kNumChannels; ++i) { dac_code_[i] = ConvertToDacCode(levels_[i], settings_[i].response); } } } // namespace frames
Report a bug