Files

copied
Last update 6 years 1 month by Olivier Gillet
Filesedges
..
bootloader
hardware_design
resources
__init__.py
adc_acquisition.cc
adc_acquisition.h
audio_buffer.cc
audio_buffer.h
digital_oscillator.cc
digital_oscillator.h
edges.cc
hardware_config.h
makefile
midi.h
midi_handler.cc
midi_handler.h
note_stack.h
resources.cc
resources.h
settings.cc
settings.h
storage.h
timer_oscillator.cc
timer_oscillator.h
ui.cc
ui.h
voice_allocator.cc
voice_allocator.h
settings.h
// Copyright 2012 Olivier Gillet. // // Author: Olivier Gillet (olivier@mutable-instruments.net) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // // Settings written to eeprom #ifndef EDGES_SETTINGS_H_ #define EDGES_SETTINGS_H_ #include "avrlibx/avrlibx.h" #include "avrlibx/utils/op.h" #include "edges/digital_oscillator.h" #include "edges/hardware_config.h" #include "edges/timer_oscillator.h" namespace edges { static const uint16_t kHysteresisThreshold = 32; struct ChannelData { uint8_t pw; bool quantized; bool arpeggio; uint8_t num_arpeggio_steps; int8_t arpeggio_step; // What are stored are not CV values; but instead values relative to the // first note of the sequence. For example, the sequence // 1000 2000 4000 2000 // is stored as: // +1000, +2000, -2000 // This sequence of values is added to the current CV. This allows // transposition. int16_t arpeggio_steps[7]; int16_t offset; int16_t scale; int16_t fm_offset; inline int16_t dac_to_pitch(int16_t dac_code) const { int16_t pitch = S16U12MulShift12(scale, dac_code); pitch += offset; if (pitch < 0) { pitch = 0; } if (quantized) { pitch += 64; // rounding pitch &= 0xff80; } if (pitch >= 16383) { pitch = 16383; } return pitch; } inline int16_t dac_to_fm(int16_t dac_code) const { return pw == PULSE_WIDTH_CV_CONTROLLED ? 0 : S16U12MulShift12(scale, dac_code) + fm_offset; } inline int16_t arpeggio_offset() const { if (!arpeggio || arpeggio_step == 0 || arpeggio_step >= num_arpeggio_steps || num_arpeggio_steps == 1) { return 0; } else { if (arpeggio_step == -1) { return arpeggio_steps[num_arpeggio_steps - 2]; } else { return arpeggio_steps[arpeggio_step - 1]; } } } inline void StartRecording() { num_arpeggio_steps = 1; arpeggio_step = -1; // -1 = play the step currently being recorded arpeggio = true; } inline void UpdateArpeggiatorStep( uint16_t root_dac_code, uint16_t dac_code) { if (num_arpeggio_steps > 1) { int16_t this_note = dac_to_pitch(dac_code); int16_t root_note = dac_to_pitch(root_dac_code); arpeggio_steps[num_arpeggio_steps - 2] = this_note - root_note; } } inline void StopRecording() { arpeggio_step = 0; } inline bool NextArpeggiatorStep() { if (num_arpeggio_steps < 8) { ++num_arpeggio_steps; return true; } else { StopRecording(); return false; } } }; struct SettingsData { ChannelData channel[4]; uint8_t midi_channel; uint8_t midi_mode; }; class Settings { public: Settings() { } ~Settings() { } static void Init(bool reset_to_factory_defaults); static PulseWidth pw(uint8_t channel) { return static_cast<PulseWidth>(data_.channel[channel].pw); } static bool quantized(uint8_t channel) { return data_.channel[channel].quantized; } static bool arpeggio(uint8_t channel) { return data_.channel[channel].arpeggio; } static OscillatorShape shape(uint8_t channel) { return static_cast<OscillatorShape>(data_.channel[channel].pw); } static inline uint8_t num_steps(uint8_t channel) { return data_.channel[channel].num_arpeggio_steps; } static inline int16_t dac_to_fm(uint8_t channel, int16_t dac_code) { #ifdef OCTAL_ADC return data_.channel[channel].dac_to_fm(dac_code); #else return 0; #endif // OCTAL_ADC } static int16_t dac_to_pitch(uint8_t channel, int16_t dac_code) { const ChannelData& data = data_.channel[channel]; int16_t pitch = data.dac_to_pitch(dac_code); if (data.quantized) { // When pitch is quantized, apply some hysteresis to DAC code. int16_t delta = dac_code - previous_code_[channel]; if (delta < 0) { delta = -delta; } if (delta < kHysteresisThreshold) { pitch = previous_pitch_[channel]; } else { previous_pitch_[channel] = pitch; previous_code_[channel] = dac_code; } } return pitch + data.arpeggio_offset(); } static inline uint8_t midi_channel() { return data_.midi_channel; } static inline uint8_t midi_mode() { return data_.midi_mode; } static void set_midi_channel(uint8_t midi_channel) { data_.midi_channel = midi_channel; Save(); } static void ToggleMidiMode() { data_.midi_mode = (data_.midi_mode + 1) & 1; Save(); } static void StepPW(uint8_t channel) { uint8_t value = data_.channel[channel].pw + 1; if (value > PULSE_WIDTH_CV_CONTROLLED) { value = PULSE_WIDTH_50; } data_.channel[channel].pw = value; } static void StepArpeggio(uint8_t channel) { ChannelData* data = &data_.channel[channel]; if (data->arpeggio_step != -1) { ++data->arpeggio_step; if (data->arpeggio_step >= data->num_arpeggio_steps) { data->arpeggio_step = 0; } } } static void ToggleQuantizer(uint8_t channel) { data_.channel[channel].quantized ^= true; } static void ToggleArpeggio(uint8_t channel) { data_.channel[channel].arpeggio ^= true; } static void Calibrate( uint8_t channel, int16_t dac_code_c2, int16_t dac_code_c4, int16_t dac_code_fm) { if (dac_code_c4 != dac_code_c2) { int16_t scale = (24 * 128 * 4096L) / (dac_code_c4 - dac_code_c2); data_.channel[channel].scale = scale; data_.channel[channel].offset = (60 << 7) - \ S16U12MulShift12(scale, (dac_code_c2 + dac_code_c4) >> 1); } data_.channel[channel].fm_offset = \ -S16U12MulShift12(data_.channel[channel].scale, dac_code_fm); Save(); } static ChannelData* mutable_channel_data(uint8_t channel) { return &data_.channel[channel]; } static void Save(); private: static int16_t previous_code_[kNumChannels]; static int16_t previous_pitch_[kNumChannels]; static int16_t pitch_bend_[kNumChannels]; static SettingsData data_; DISALLOW_COPY_AND_ASSIGN(Settings); }; extern Settings settings; } // namespace edges #endif // EDGES_SETTINGS_H_
Report a bug