299 lines
7.7 KiB
C++
299 lines
7.7 KiB
C++
#include <EEPROM.h>
|
|
|
|
// pot data
|
|
typedef struct
|
|
{
|
|
uint8_t pin;
|
|
uint16_t state;
|
|
bool lock;
|
|
} POT_DATA;
|
|
|
|
// button data
|
|
typedef struct
|
|
{
|
|
uint8_t pin;
|
|
bool state;
|
|
uint8_t hold_seconds;
|
|
bool hold_trigger;
|
|
} BUTTON_DATA;
|
|
|
|
POT_DATA _pot[POT_NUMBER];
|
|
BUTTON_DATA _button[BUTTON_NUMBER];
|
|
|
|
// pattern memory layout 72 bytes(2 tracks with 16 steps)
|
|
// byte1: pattern_exist
|
|
// byte2: _transpose
|
|
// byte3: _harmonize
|
|
// byte4: _selected_mode
|
|
// byte5...: _sequencer[0].data, _sequencer[1].data
|
|
// EEPROM data access: 1024 bytes total
|
|
// total 14 patterns
|
|
// use for load and save pattern
|
|
#define PATTERN_SIZE (sizeof(SEQUENCER_TRACK_DATA)*TRACK_NUMBER)+4
|
|
#define PATTERN_NUMBER 14
|
|
|
|
SEQUENCER_TRACK_DATA _track_data;
|
|
|
|
void loadLastPattern()
|
|
{
|
|
uint8_t last_pattern;
|
|
|
|
EEPROM.get(1023, last_pattern);
|
|
|
|
if ( last_pattern < PATTERN_NUMBER ) {
|
|
loadPattern(last_pattern);
|
|
}
|
|
}
|
|
|
|
void resetPattern(uint8_t number)
|
|
{
|
|
uint16_t eeprom_address = PATTERN_SIZE;
|
|
|
|
// get address base for pattern
|
|
eeprom_address *= number;
|
|
|
|
// load defaults
|
|
ATOMIC(
|
|
_transpose = 0;
|
|
_harmonize = 0;
|
|
_selected_mode = 0;
|
|
);
|
|
// reset sequencer data
|
|
for ( uint8_t track = 0; track < TRACK_NUMBER; track++ ) {
|
|
ATOMIC(_sequencer[track].mute = true);
|
|
clearStackNote(track);
|
|
_track_data.step_init_point = 0;
|
|
_track_data.step_length = STEP_MAX_SIZE;
|
|
// initing note data
|
|
for ( uint8_t i = 0; i < STEP_MAX_SIZE; i++ ) {
|
|
_track_data.step[i].note = 48;
|
|
_track_data.step[i].accent = 0;
|
|
_track_data.step[i].glide = 0;
|
|
_track_data.step[i].rest = 0;
|
|
}
|
|
|
|
ATOMIC(memcpy((void*)&_sequencer[track].data, &_track_data, sizeof(SEQUENCER_TRACK_DATA)));
|
|
ATOMIC(_sequencer[track].mute = false);
|
|
}
|
|
// mark pattern slot as not in use
|
|
EEPROM.write(eeprom_address, 0);
|
|
}
|
|
|
|
void loadPattern(uint8_t number)
|
|
{
|
|
uint16_t eeprom_address = PATTERN_SIZE;
|
|
uint8_t pattern_exist, harmonize, selected_mode = 0;
|
|
int8_t transpose = 0;
|
|
|
|
if ( number >= PATTERN_NUMBER ) {
|
|
return;
|
|
}
|
|
|
|
// get address base for pattern
|
|
eeprom_address *= number;
|
|
|
|
// do we have pattern data to read it here?
|
|
EEPROM.get(eeprom_address, pattern_exist);
|
|
if ( pattern_exist != 1 ) {
|
|
resetPattern(number);
|
|
// save last pattern loaded
|
|
EEPROM.write(1023, number);
|
|
return;
|
|
}
|
|
|
|
// global pattern config
|
|
EEPROM.get(++eeprom_address, transpose);
|
|
EEPROM.get(++eeprom_address, harmonize);
|
|
EEPROM.get(++eeprom_address, selected_mode);
|
|
// constrains to avoid trash data from memory
|
|
if ( transpose > 12 ) {
|
|
transpose = 12;
|
|
} else if ( transpose < -12 ) {
|
|
transpose = -12;
|
|
}
|
|
if (selected_mode >= MODES_NUMBER) {
|
|
selected_mode = MODES_NUMBER-1;
|
|
}
|
|
ATOMIC(
|
|
_transpose = transpose;
|
|
_harmonize = harmonize;
|
|
_selected_mode = selected_mode;
|
|
);
|
|
// track data
|
|
for (uint8_t track=0; track < TRACK_NUMBER; track++) {
|
|
ATOMIC(_sequencer[track].mute = true);
|
|
clearStackNote(track);
|
|
EEPROM.get(++eeprom_address, _track_data); // 34 bytes long
|
|
// constrains to avoid trash data from memory
|
|
if ( _track_data.step_length > STEP_MAX_SIZE ) {
|
|
_track_data.step_length = STEP_MAX_SIZE;
|
|
}
|
|
ATOMIC(memcpy((void*)&_sequencer[track].data, &_track_data, sizeof(SEQUENCER_TRACK_DATA)));
|
|
ATOMIC(_sequencer[track].mute = false);
|
|
eeprom_address += (sizeof(SEQUENCER_TRACK_DATA)-1);
|
|
}
|
|
|
|
// save last pattern loaded
|
|
EEPROM.write(1023, number);
|
|
|
|
_selected_pattern = number;
|
|
|
|
}
|
|
|
|
void savePattern(uint8_t number)
|
|
{
|
|
uint16_t eeprom_address = PATTERN_SIZE;
|
|
|
|
if ( number >= PATTERN_NUMBER ) {
|
|
return;
|
|
}
|
|
|
|
// get address base for pattern
|
|
eeprom_address *= number;
|
|
|
|
// mark pattern slot as in use pattern_exist
|
|
EEPROM.write(eeprom_address, 1);
|
|
// global pattern config
|
|
EEPROM.write(++eeprom_address, _transpose);
|
|
EEPROM.write(++eeprom_address, _harmonize);
|
|
EEPROM.write(++eeprom_address, _selected_mode);
|
|
// track data
|
|
for (uint8_t track=0; track < TRACK_NUMBER; track++) {
|
|
memcpy(&_track_data, (void*)&_sequencer[track].data, sizeof(SEQUENCER_TRACK_DATA));
|
|
EEPROM.put(++eeprom_address, _track_data); // 34 bytes long
|
|
eeprom_address += (sizeof(SEQUENCER_TRACK_DATA)-1);
|
|
}
|
|
|
|
}
|
|
|
|
void connectPot(uint8_t pot_id, uint8_t pot_pin)
|
|
{
|
|
_pot[pot_id].pin = pot_pin;
|
|
// get first state data
|
|
_pot[pot_id].state = analogRead(_pot[pot_id].pin);
|
|
_pot[pot_id].lock = true;
|
|
}
|
|
|
|
void connectButton(uint8_t button_id, uint8_t button_pin)
|
|
{
|
|
_button[button_id].pin = button_pin;
|
|
// use internal pullup for buttons
|
|
pinMode(_button[button_id].pin, INPUT_PULLUP);
|
|
// get first state data
|
|
_button[button_id].state = digitalRead(_button[button_id].pin);
|
|
_button[button_id].hold_seconds = 0;
|
|
_button[button_id].hold_trigger = false;
|
|
}
|
|
|
|
void lockPotsState(bool lock)
|
|
{
|
|
for ( uint8_t i = 0; i < POT_NUMBER; i++ ) {
|
|
_pot[i].lock = lock;
|
|
}
|
|
}
|
|
|
|
bool pressed(uint8_t button_id)
|
|
{
|
|
bool value;
|
|
|
|
value = digitalRead(_button[button_id].pin);
|
|
|
|
// using internal pullup pressed button goes LOW
|
|
if ( value != _button[button_id].state && value == LOW ) {
|
|
_button[button_id].state = value;
|
|
return true;
|
|
} else {
|
|
_button[button_id].state = value;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool doublePressed(uint8_t button1_id, uint8_t button2_id)
|
|
{
|
|
bool value1, value2;
|
|
|
|
value1 = digitalRead(_button[button1_id].pin);
|
|
value2 = digitalRead(_button[button2_id].pin);
|
|
|
|
// using internal pullup pressed button goes LOW
|
|
if ( value1 == LOW && value2 == LOW ) {
|
|
_button[button1_id].state = LOW;
|
|
_button[button2_id].state = LOW;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool holded(uint8_t button_id, uint8_t seconds)
|
|
{
|
|
bool value;
|
|
|
|
value = digitalRead(_button[button_id].pin);
|
|
|
|
// using internal pullup pressed button goes LOW
|
|
if ( _button[button_id].hold_trigger == false && value == LOW ) {
|
|
if ( _button[button_id].hold_seconds == 0 ) {
|
|
_button[button_id].hold_seconds = (uint8_t)(millis()/1000);
|
|
} else if ( abs((uint8_t)(millis()/1000) - _button[button_id].hold_seconds) >= seconds ) {
|
|
_button[button_id].hold_trigger = true; // avoid released triger after action.
|
|
return true;
|
|
}
|
|
return false;
|
|
} else if ( value == HIGH ) {
|
|
_button[button_id].hold_trigger = false;
|
|
_button[button_id].hold_seconds = 0;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool released(uint8_t button_id)
|
|
{
|
|
bool value;
|
|
|
|
value = digitalRead(_button[button_id].pin);
|
|
|
|
// using internal pullup released button goes HIGH
|
|
if ( value != _button[button_id].state && value == HIGH && _button[button_id].hold_trigger == false ) {
|
|
_button[button_id].state = value;
|
|
return true;
|
|
} else {
|
|
_button[button_id].state = value;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int16_t getPotChanges(uint8_t pot_id, uint16_t min_value, uint16_t max_value)
|
|
{
|
|
uint16_t value, value_ranged, last_value_ranged;
|
|
uint8_t pot_sensitivity = POT_SENSITIVITY;
|
|
|
|
// get absolute value
|
|
value = analogRead(_pot[pot_id].pin);
|
|
|
|
// range that value and our last_value
|
|
value_ranged = (value / (ADC_RESOLUTION / ((max_value - min_value) + 1))) + min_value;
|
|
last_value_ranged = (_pot[pot_id].state / (ADC_RESOLUTION / ((max_value - min_value) + 1))) + min_value;
|
|
|
|
// a lock system to not mess with some data(pots are terrible for some kinda of user interface data controls, but lets keep it low cost!)
|
|
if ( _pot[pot_id].lock == true ) {
|
|
// user needs to move 1/8 of total adc range to get pot unlocked
|
|
if ( abs(value - _pot[pot_id].state) < (ADC_RESOLUTION/8) ) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if ( abs(value_ranged - last_value_ranged) >= pot_sensitivity ) {
|
|
_pot[pot_id].state = value;
|
|
if ( _pot[pot_id].lock == true ) {
|
|
_pot[pot_id].lock = false;
|
|
}
|
|
if ( value_ranged > max_value ) {
|
|
value_ranged = max_value;
|
|
}
|
|
return value_ranged;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|