// 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 .
//
// 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(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(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_