1 // Copyright 2014 Emilie Gillet. 2 // 3 // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 // 23 // See http://creativecommons.org/licenses/MIT/ for more information. 24 // 25 // ----------------------------------------------------------------------------- 26 // 27 // CV scaler. 28 29 #ifndef WARPS_CV_SCALER_H_ 30 #define WARPS_CV_SCALER_H_ 31 32 #include "stmlib/stmlib.h" 33 34 #include "warps/drivers/adc.h" 35 #include "warps/drivers/codec.h" 36 #include "warps/drivers/normalization_probe.h" 37 38 #include "warps/settings.h" 39 40 namespace warps { 41 42 class Parameters; 43 44 class NormalizationDetector { 45 public: NormalizationDetector()46 NormalizationDetector() { } ~NormalizationDetector()47 ~NormalizationDetector() { } 48 Init(float lp_coefficient,float threshold)49 void Init(float lp_coefficient, float threshold) { 50 score_ = 0.0f; 51 state_ = false; 52 threshold_ = threshold; 53 lp_coefficient_ = lp_coefficient; 54 } 55 Process(float x,float y)56 void Process(float x, float y) { 57 // x is supposed to be centered! 58 score_ += lp_coefficient_ * (x * y - score_); 59 60 float hysteresis = state_ ? -0.05f * threshold_ : 0.0f; 61 state_ = score_ >= (threshold_ + hysteresis); 62 } 63 normalized()64 inline bool normalized() const { return state_; } score()65 inline float score() const { return score_; } 66 67 private: 68 float score_; 69 float lp_coefficient_; 70 float threshold_; 71 72 bool state_; 73 74 DISALLOW_COPY_AND_ASSIGN(NormalizationDetector); 75 }; 76 77 class CvScaler { 78 public: CvScaler()79 CvScaler() { } ~CvScaler()80 ~CvScaler() { } 81 82 void Init(CalibrationData* calibration_data); 83 void Read(Parameters* parameters); 84 85 void DetectAudioNormalization(Codec::Frame* in, size_t size); 86 StartCalibration()87 void StartCalibration() { 88 normalization_probe_enabled_ = false; 89 normalization_probe_forced_state_ = false; 90 } 91 CalibrateC1()92 void CalibrateC1() { 93 cv_c1_ = adc_.float_value(ADC_LEVEL_1_CV); 94 } 95 CalibrateOffsets()96 void CalibrateOffsets() { 97 for (size_t i = 1; i < 4; ++i) { 98 calibration_data_->offset[i] = adc_.float_value(i); 99 } 100 } 101 CalibrateC3()102 bool CalibrateC3() { 103 float c3 = adc_.float_value(ADC_LEVEL_1_CV); // 0.5818181818181818 104 float c1 = cv_c1_; // 0.8 105 float delta = c3 - c1; 106 bool success = delta > -0.4f && delta < -0.1f; 107 if (success) { 108 calibration_data_->pitch_scale = 24.0f / (c3 - c1); 109 calibration_data_->pitch_offset = 12.0f - \ 110 calibration_data_->pitch_scale * c1; 111 calibration_data_->offset[0] = c1 + 0.5f * (c1 - c3); 112 } 113 normalization_probe_enabled_ = true; 114 return success; 115 } 116 StartNormalizationCalibration()117 void StartNormalizationCalibration() { 118 normalization_probe_enabled_ = false; 119 normalization_probe_forced_state_ = false; 120 } 121 CalibrateLow()122 void CalibrateLow() { 123 cv_low_[0] = adc_.float_value(ADC_LEVEL_1_CV); 124 cv_low_[1] = adc_.float_value(ADC_LEVEL_2_CV); 125 normalization_probe_forced_state_ = true; 126 } 127 CalibrateHigh()128 bool CalibrateHigh() { 129 bool success = true; 130 for (int i = 0; i < 2; ++i) { 131 float high = adc_.float_value(ADC_LEVEL_1_CV + i); 132 float threshold = (cv_low_[i] + high) * 0.5f; 133 bool within_range = threshold >= 0.8f && threshold <= 0.9f; 134 if (within_range) { 135 calibration_data_->normalization_detection_threshold[i] = threshold; 136 } 137 success = success && within_range; 138 } 139 normalization_probe_enabled_ = true; 140 return success; 141 } 142 ready_for_calibration()143 inline bool ready_for_calibration() const { 144 return adc_.float_value(ADC_LEVEL_2_CV) > 0.8f && \ 145 adc_.float_value(ADC_LEVEL_1_CV) > 0.7f; 146 } 147 easter_egg_digit()148 inline uint8_t easter_egg_digit() const { 149 if (lp_state_[ADC_LEVEL_1_POT] < 0.05f && \ 150 lp_state_[ADC_LEVEL_2_POT] < 0.05f && \ 151 lp_state_[ADC_PARAMETER_POT] < 0.05f) { 152 return static_cast<uint8_t>( 153 UnwrapPot(lp_state_[ADC_ALGORITHM_POT]) * 8.0f + 0.5f); 154 } else { 155 return 0; 156 } 157 } 158 adc_value(size_t index)159 inline uint8_t adc_value(size_t index) const { 160 return adc_.value(index) >> 8; 161 } 162 normalization(size_t index)163 inline uint8_t normalization(size_t index) const { 164 return normalization_detector_[index].normalized() ? 255 : 0; 165 } 166 167 float UnwrapPot(float x) const; 168 169 private: 170 void DetectNormalization(); 171 172 Adc adc_; 173 CalibrationData* calibration_data_; 174 NormalizationProbe normalization_probe_; 175 NormalizationDetector normalization_detector_[4]; 176 177 bool normalization_probe_value_[2]; 178 bool normalization_probe_enabled_; 179 bool normalization_probe_forced_state_; 180 181 float lp_state_[ADC_LAST]; 182 float note_cv_; 183 float note_pot_; 184 float note_pot_quantized_; 185 float cv_c1_; 186 float cv_low_[2]; 187 188 DISALLOW_COPY_AND_ASSIGN(CvScaler); 189 }; 190 191 } // namespace warps 192 193 #endif // WARPS_CV_SCALER_H_ 194