diff --git a/peaks/calibration_data.cc b/peaks/calibration_data.cc new file mode 100644 index 0000000..7ef646b --- /dev/null +++ b/peaks/calibration_data.cc @@ -0,0 +1,50 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// +// Calibration settings. + +#include "peaks/calibration_data.h" + +#include "stmlib/system/storage.h" + +namespace peaks { + +using namespace stmlib; + +Storage<0x801c000, 1> calibration_storage; + +void CalibrationData::Init() { + if (!calibration_storage.Load(&calibration_settings_)) { + calibration_settings_.dac_offset[0] = 0; + calibration_settings_.dac_offset[1] = 0; + } +} + +void CalibrationData::Save() { + calibration_storage.Save(calibration_settings_); +} + +} // namespace peaks diff --git a/peaks/calibration_data.h b/peaks/calibration_data.h new file mode 100644 index 0000000..3b645d5 --- /dev/null +++ b/peaks/calibration_data.h @@ -0,0 +1,69 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// +// Calibration settings. + +#ifndef PEAKS_CALIBRATION_DATA_H_ +#define PEAKS_CALIBRATION_DATA_H_ + +#include "stmlib/stmlib.h" + +namespace peaks { + +struct CalibrationSettings { + int16_t dac_offset[2]; +}; + +class CalibrationData { + public: + CalibrationData() { } + ~CalibrationData() { } + + void Init(); + + inline uint16_t Offset(uint8_t channel, uint16_t value) const { + int32_t shifted_value = static_cast(value); + shifted_value += static_cast( + calibration_settings_.dac_offset[channel]); + CONSTRAIN(shifted_value, 0, 65535); + return static_cast(shifted_value); + } + + inline void set_dac_offset(uint8_t channel, int16_t offset) { + calibration_settings_.dac_offset[channel] = offset; + } + + void Save(); + + private: + CalibrationSettings calibration_settings_; + + DISALLOW_COPY_AND_ASSIGN(CalibrationData); +}; + +} // namespace peaks + +#endif // PEAKS_CALIBRATION_DATA_H_ diff --git a/peaks/modulations/lfo.h b/peaks/modulations/lfo.h index 86da7bc..56cdea2 100644 --- a/peaks/modulations/lfo.h +++ b/peaks/modulations/lfo.h @@ -84,7 +84,7 @@ class Lfo { set_shape_parameter_preset(parameter[1]); } set_reset_phase(0); - set_level(65535); + set_level(40960); } else { if (sync_) { set_level(parameter[0]); @@ -92,7 +92,7 @@ class Lfo { set_parameter(parameter[2] - 32768); set_reset_phase(parameter[3] - 32768); } else { - set_level(65535); + set_level(40960); set_rate(parameter[0]); set_shape_integer(parameter[1]); set_parameter(parameter[2] - 32768); diff --git a/peaks/modulations/mini_sequencer.h b/peaks/modulations/mini_sequencer.h index 21265eb..cc19522 100644 --- a/peaks/modulations/mini_sequencer.h +++ b/peaks/modulations/mini_sequencer.h @@ -92,7 +92,7 @@ class MiniSequencer { if (step_ >= num_steps_) { step_ = 0; } - return steps_[step_]; + return static_cast(steps_[step_]) * 40960 >> 16; } private: diff --git a/peaks/peaks.cc b/peaks/peaks.cc index 4ef78f3..f23457a 100755 --- a/peaks/peaks.cc +++ b/peaks/peaks.cc @@ -32,6 +32,7 @@ #include "peaks/drivers/gate_input.h" #include "peaks/drivers/system.h" +#include "peaks/calibration_data.h" #include "peaks/processors.h" #include "peaks/ui.h" @@ -39,6 +40,7 @@ using namespace peaks; using namespace stmlib; Adc adc; +CalibrationData calibration_data; Dac dac; GateInput gate_input; Leds leds; @@ -80,7 +82,13 @@ void TIM1_UP_IRQHandler(void) { TIM_ClearITPendingBit(TIM1, TIM_IT_Update); if (dac.ready()) { - dac.Write(32767 - cv_1, 32767 - cv_2); + if (ui.calibrating()) { + cv_1 = 0; + cv_2 = 0; + } + dac.Write( + calibration_data.Offset(0, 32767 - cv_1), + calibration_data.Offset(1, 32767 - cv_2)); ui.set_leds_brightness(cv_1, cv_2); control = gate_input.Read() | ui.ReadPanelGateState(); @@ -100,9 +108,10 @@ void Init() { gate_input.Init(); dac.Init(); + calibration_data.Init(); processors[0].Init(0); processors[1].Init(1); - ui.Init(); + ui.Init(&calibration_data); sys.StartTimers(); } diff --git a/peaks/ui.cc b/peaks/ui.cc index 5f59101..e21e752 100755 --- a/peaks/ui.cc +++ b/peaks/ui.cc @@ -36,6 +36,8 @@ #include +#include "peaks/calibration_data.h" + namespace peaks { using namespace std; @@ -78,7 +80,8 @@ const ProcessorFunction Ui::function_table_[FUNCTION_LAST][2] = { Storage<0x8020000, 16> storage; -void Ui::Init() { +void Ui::Init(CalibrationData* calibration_data) { + calibration_data_ = calibration_data; leds_.Init(); switches_.Init(); adc_.Init(); @@ -89,6 +92,8 @@ void Ui::Init() { fill(&snapped_[0], &snapped_[kNumAdcChannels], false); panel_gate_state_ = 0; + calibrating_ = switches_.pressed_immediate(1); + if (!storage.ParsimoniousLoad(&settings_, &version_token_)) { edit_mode_ = EDIT_MODE_TWIN; function_[0] = FUNCTION_ENVELOPE; @@ -152,6 +157,12 @@ void Ui::SaveState() { } inline void Ui::RefreshLeds() { + if (calibrating_) { + leds_.set_pattern(0xf); + leds_.set_twin_mode(true); + leds_.set_levels(0, 0); + return; + } uint8_t flash = (system_clock.milliseconds() >> 7) & 7; switch (edit_mode_) { case EDIT_MODE_FIRST: @@ -294,7 +305,13 @@ inline void Ui::RefreshLeds() { case FUNCTION_PLO: case FUNCTION_MINI_SEQUENCER: case FUNCTION_MOD_SEQUENCER: - b[i] = static_cast(brightness_[i] + 32768) >> 8; + { + int32_t brightness = int32_t(brightness_[i]) * 409 >> 8; + brightness += 32768; + brightness >>= 8; + CONSTRAIN(brightness, 0, 255); + b[i] = brightness; + } break; case FUNCTION_TURING_MACHINE: b[i] = static_cast(brightness_[i]) >> 5; @@ -479,6 +496,27 @@ void Ui::SetFunction(uint8_t index, Function f) { } void Ui::OnSwitchReleased(const Event& e) { + if (calibrating_) { + if (e.control_id == SWITCH_TWIN_MODE) { + // Save calibration. + calibration_data_->Save(); + + // Reset all settings to defaults. + edit_mode_ = EDIT_MODE_TWIN; + function_[0] = FUNCTION_ENVELOPE; + function_[1] = FUNCTION_ENVELOPE; + settings_.snap_mode = false; + + SaveState(); + ChangeControlMode(); + SetFunction(0, function_[0]); + SetFunction(1, function_[1]); + + // Done with calibration. + calibrating_ = false; + } + return; + } switch (e.control_id) { case SWITCH_TWIN_MODE: if (e.data > kLongPressDuration) { @@ -577,6 +615,17 @@ void Ui::OnSwitchReleased(const Event& e) { } void Ui::OnPotChanged(const Event& e) { + if (calibrating_) { + pot_value_[e.control_id] = e.data >> 8; + for (uint8_t i = 0; i < 2; ++i) { + int32_t coarse = pot_value_[i * 2]; + int32_t fine = pot_value_[i * 2 + 1]; + int32_t offset = ((coarse - 128) << 3) + ((fine - 128) >> 1); + calibration_data_->set_dac_offset(i, -offset); + } + return; + } + switch (edit_mode_) { case EDIT_MODE_TWIN: processors[0].set_parameter(e.control_id, e.data); diff --git a/peaks/ui.h b/peaks/ui.h index 2c5cfb3..5f611c3 100755 --- a/peaks/ui.h +++ b/peaks/ui.h @@ -115,12 +115,14 @@ struct Settings { uint8_t padding[4]; }; +class CalibrationData; + class Ui { public: Ui() { } ~Ui() { } - void Init(); + void Init(CalibrationData* calibration_data); void Poll(); void PollPots(); void DoEvents(); @@ -150,6 +152,8 @@ class Ui { return state; } + inline bool calibrating() const { return calibrating_; } + private: inline Function function() const { return edit_mode_ == EDIT_MODE_SECOND ? function_[1] : function_[0]; @@ -191,6 +195,9 @@ class Ui { bool snap_mode_; bool snapped_[kNumAdcChannels]; + CalibrationData* calibration_data_; + bool calibrating_; + // store last used function on each page Function last_basic_function_[2]; Function last_ext_env_function_[2];