Files

copied
Last update 6 years 1 month by Olivier Gillet
Filesrings
..
bootloader
drivers
dsp
hardware_design
resources
test
__init__.py
cv_scaler.cc
cv_scaler.h
makefile
meter.h
resources.cc
resources.h
rings.cc
settings.cc
settings.h
ui.cc
ui.h
cv_scaler.cc
// Copyright 2015 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. // // ----------------------------------------------------------------------------- // // Filtering and scaling of ADC values + input calibration. #include "rings/cv_scaler.h" #include <algorithm> #include "stmlib/dsp/dsp.h" #include "stmlib/system/storage.h" #include "stmlib/utils/random.h" #include "rings/dsp/part.h" #include "rings/dsp/patch.h" namespace rings { using namespace std; using namespace stmlib; /* static */ ChannelSettings CvScaler::channel_settings_[ADC_CHANNEL_LAST] = { { LAW_LINEAR, true, 1.00f }, // ADC_CHANNEL_CV_FREQUENCY { LAW_LINEAR, true, 0.1f }, // ADC_CHANNEL_CV_STRUCTURE { LAW_LINEAR, true, 0.1f }, // ADC_CHANNEL_CV_BRIGHTNESS { LAW_LINEAR, true, 0.05f }, // ADC_CHANNEL_CV_DAMPING { LAW_LINEAR, true, 0.01f }, // ADC_CHANNEL_CV_POSITION { LAW_LINEAR, false, 1.00f }, // ADC_CHANNEL_CV_V_OCT { LAW_LINEAR, false, 0.01f }, // ADC_CHANNEL_POT_FREQUENCY { LAW_LINEAR, false, 0.01f }, // ADC_CHANNEL_POT_STRUCTURE { LAW_LINEAR, false, 0.01f }, // ADC_CHANNEL_POT_BRIGHTNESS { LAW_LINEAR, false, 0.01f }, // ADC_CHANNEL_POT_DAMPING { LAW_LINEAR, false, 0.01f }, // ADC_CHANNEL_POT_POSITION { LAW_QUARTIC_BIPOLAR, false, 0.005f }, // ADC_CHANNEL_ATTENUVERTER_FREQUENCY { LAW_QUADRATIC_BIPOLAR, false, 0.005f }, //ADC_CHANNEL_ATTENUVERTER_STRUCTURE, { LAW_QUADRATIC_BIPOLAR, false, 0.005f }, // ADC_CHANNEL_ATTENUVERTER_BRIGHTNESS, { LAW_QUADRATIC_BIPOLAR, false, 0.005f }, // ADC_CHANNEL_ATTENUVERTER_DAMPING, { LAW_QUADRATIC_BIPOLAR, false, 0.005f }, // ADC_CHANNEL_ATTENUVERTER_POSITION, }; void CvScaler::Init(CalibrationData* calibration_data) { calibration_data_ = calibration_data; adc_.Init(); trigger_input_.Init(); transpose_ = 0.0f; fill(&adc_lp_[0], &adc_lp_[ADC_CHANNEL_LAST], 0.0f); normalization_probe_.Init(); normalization_detector_exciter_.Init(0.01f, 0.5f); normalization_detector_trigger_.Init(0.05f, 0.9f); normalization_detector_v_oct_.Init(0.01f, 0.5f); inhibit_strum_ = 0; fm_cv_ = 0.0f; normalization_probe_enabled_ = true; normalization_probe_forced_state_ = false; } void CvScaler::DetectAudioNormalization(Codec::Frame* in, size_t size) { int32_t count = 0; short* input_samples = &in->r; for (size_t i = 0; i < size; i += 8) { short s = input_samples[i * 2]; if (s > 50 && s < 1500) { ++count; } else if (s > -1500 && s < -50) { --count; } } float y = static_cast<float>(count) / static_cast<float>(size >> 3); float x = normalization_probe_value_[1] ? -1.0f : 1.0f; normalization_detector_exciter_.Process(x, y); if (normalization_detector_exciter_.normalized()) { for (size_t i = 0; i < size; ++i) { input_samples[i * 2] = 0; } } } void CvScaler::DetectNormalization() { if (normalization_probe_value_[0] == trigger_input_.DummyRead()) { normalization_detector_trigger_.Process(1.0f, 1.0f); } else { normalization_detector_trigger_.Process(1.0f, -1.0f); } float x = adc_.float_value(ADC_CHANNEL_CV_V_OCT) - calibration_data_->normalization_detection_threshold; float y = normalization_probe_value_[0] ? -1.0f : 1.0f; if (x > -0.5f && x < 0.5f) { x = x < 0.0f ? -1.0f : 1.0f; normalization_detector_v_oct_.Process(x, y); } else { normalization_detector_v_oct_.Process(0.0f, y); } normalization_probe_value_[1] = normalization_probe_value_[0]; normalization_probe_value_[0] = Random::GetWord() >> 31; bool new_state = normalization_probe_enabled_ ? normalization_probe_value_[0] : normalization_probe_forced_state_; normalization_probe_.Write(new_state); } #define ATTENUVERT(destination, NAME, min, max) \ { \ float value = adc_lp_[ADC_CHANNEL_CV_ ## NAME]; \ value *= adc_lp_[ADC_CHANNEL_ATTENUVERTER_ ## NAME]; \ value += adc_lp_[ADC_CHANNEL_POT_ ## NAME]; \ CONSTRAIN(value, min, max) \ destination = value; \ } void CvScaler::Read(Patch* patch, PerformanceState* performance_state) { // Process all CVs / pots. for (size_t i = 0; i < ADC_CHANNEL_LAST; ++i) { const ChannelSettings& settings = channel_settings_[i]; float value = adc_.float_value(i); if (settings.remove_offset) { value = calibration_data_->offset[i] - value; } switch (settings.law) { case LAW_QUADRATIC_BIPOLAR: { value = value - 0.5f; float value2 = value * value * 4.0f * 3.3f; value = value < 0.0f ? -value2 : value2; } break; case LAW_QUARTIC_BIPOLAR: { value = value - 0.5f; float value2 = value * value * 4.0f; float value4 = value2 * value2 * 3.3f; value = value < 0.0f ? -value4 : value4; } break; default: break; } adc_lp_[i] += settings.lp_coefficient * (value - adc_lp_[i]); } ATTENUVERT(patch->structure, STRUCTURE, 0.0f, 0.9995f); ATTENUVERT(patch->brightness, BRIGHTNESS, 0.0f, 1.0f); ATTENUVERT(patch->damping, DAMPING, 0.0f, 1.0f); ATTENUVERT(patch->position, POSITION, 0.0f, 1.0f); float fm = adc_lp_[ADC_CHANNEL_CV_FREQUENCY] * 48.0f; float error = fm - fm_cv_; if (fabs(error) >= 0.8f) { fm_cv_ = fm; } else { fm_cv_ += 0.02f * error; } performance_state->fm = fm_cv_ * adc_lp_[ADC_CHANNEL_ATTENUVERTER_FREQUENCY]; CONSTRAIN(performance_state->fm, -48.0f, 48.0f); float transpose = 60.0f * adc_lp_[ADC_CHANNEL_POT_FREQUENCY]; float hysteresis = transpose - transpose_ > 0.0f ? -0.3f : +0.3f; transpose_ = static_cast<int32_t>(transpose + hysteresis + 0.5f); float note = calibration_data_->pitch_offset; note += adc_lp_[ADC_CHANNEL_CV_V_OCT] * calibration_data_->pitch_scale; performance_state->note = note; performance_state->tonic = 12.0f + transpose_; DetectNormalization(); // Strumming / internal exciter triggering logic. bool internal_strum = normalization_detector_trigger_.normalized(); bool internal_exciter = normalization_detector_exciter_.normalized(); bool internal_note = normalization_detector_v_oct_.normalized(); performance_state->internal_exciter = internal_exciter; performance_state->internal_strum = internal_strum; performance_state->internal_note = internal_note; performance_state->strum = trigger_input_.rising_edge(); if (performance_state->internal_note) { // Remove quantization when nothing is plugged in the V/OCT input. performance_state->note = 0.0f; performance_state->tonic = 12.0f + transpose; } // Hysteresis on chord. float chord = calibration_data_->offset[ADC_CHANNEL_CV_STRUCTURE] - \ adc_.float_value(ADC_CHANNEL_CV_STRUCTURE); chord *= adc_lp_[ADC_CHANNEL_ATTENUVERTER_STRUCTURE]; chord += adc_lp_[ADC_CHANNEL_POT_STRUCTURE]; chord *= static_cast<float>(kNumChords - 1); hysteresis = chord - chord_ > 0.0f ? -0.1f : +0.1f; chord_ = static_cast<int32_t>(chord + hysteresis + 0.5f); CONSTRAIN(chord_, 0, kNumChords - 1); performance_state->chord = chord_; adc_.Convert(); trigger_input_.Read(); } } // namespace rings
Report a bug