|
- // Copyright 2012 Olivier Gillet.
- //
- // Author: Olivier Gillet (ol.gillet@gmail.com)
- //
- // 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/>.
-
- #include <avr/eeprom.h>
-
- #include "avrlib/adc.h"
- #include "avrlib/boot.h"
- #include "avrlib/op.h"
- #include "avrlib/watchdog_timer.h"
-
- #include "grids/clock.h"
- #include "grids/hardware_config.h"
- #include "grids/pattern_generator.h"
-
- using namespace avrlib;
- using namespace grids;
-
- Leds leds;
- Inputs inputs;
- AdcInputScanner adc;
- ShiftRegister shift_register;
- MidiInput midi;
-
- enum Parameter {
- PARAMETER_NONE,
- PARAMETER_WAITING,
- PARAMETER_CLOCK_RESOLUTION,
- PARAMETER_TAP_TEMPO,
- PARAMETER_SWING,
- PARAMETER_GATE_MODE,
- PARAMETER_OUTPUT_MODE,
- PARAMETER_CLOCK_OUTPUT
- };
-
- uint32_t tap_duration = 0;
- uint8_t led_pattern ;
- uint8_t led_off_timer;
-
- int8_t swing_amount;
-
- volatile Parameter parameter = PARAMETER_NONE;
- volatile bool long_press_detected = false;
- const uint8_t kUpdatePeriod = F_CPU / 32 / 8000;
-
- inline void UpdateLeds() {
- uint8_t pattern;
- if (parameter == PARAMETER_NONE) {
- if (led_off_timer) {
- --led_off_timer;
- if (!led_off_timer) {
- led_pattern = 0;
- }
- }
- pattern = led_pattern;
- if (pattern_generator.tap_tempo()) {
- if (pattern_generator.on_beat()) {
- pattern |= LED_CLOCK;
- }
- } else {
- if (pattern_generator.on_first_beat()) {
- pattern |= LED_CLOCK;
- }
- }
- } else {
- pattern = LED_CLOCK;
- switch (parameter) {
- case PARAMETER_CLOCK_RESOLUTION:
- pattern |= LED_BD >> pattern_generator.clock_resolution();
- break;
-
- case PARAMETER_CLOCK_OUTPUT:
- if (pattern_generator.output_clock()) {
- pattern |= LED_ALL;
- }
- break;
-
- case PARAMETER_SWING:
- if (pattern_generator.swing()) {
- pattern |= LED_ALL;
- }
- break;
-
- case PARAMETER_OUTPUT_MODE:
- if (pattern_generator.output_mode() == OUTPUT_MODE_DRUMS) {
- pattern |= LED_ALL;
- }
- break;
-
- case PARAMETER_TAP_TEMPO:
- if (pattern_generator.tap_tempo()) {
- pattern |= LED_ALL;
- }
- break;
-
- case PARAMETER_GATE_MODE:
- if (pattern_generator.gate_mode()) {
- pattern |= LED_ALL;
- }
- }
- }
- leds.Write(pattern);
- }
-
- inline void UpdateShiftRegister() {
- static uint8_t previous_state = 0;
- if (pattern_generator.state() != previous_state) {
- previous_state = pattern_generator.state();
- shift_register.Write(previous_state);
- if (!previous_state) {
- // Switch off the LEDs, but not now.
- led_off_timer = 200;
- } else {
- // Switch on the LEDs with a new pattern.
- led_pattern = pattern_generator.led_pattern();
- led_off_timer = 0;
- }
- }
- }
-
- uint8_t ticks_granularity[] = { 6, 3, 1 };
-
- inline void HandleClockResetInputs() {
- static uint8_t previous_inputs;
-
- uint8_t inputs_value = ~inputs.Read();
- uint8_t num_ticks = 0;
- uint8_t increment = ticks_granularity[pattern_generator.clock_resolution()];
-
- // CLOCK
- if (clock.bpm() < 40 && !clock.locked()) {
- if ((inputs_value & INPUT_CLOCK) && !(previous_inputs & INPUT_CLOCK)) {
- num_ticks = increment;
- }
- if (!(inputs_value & INPUT_CLOCK) && (previous_inputs & INPUT_CLOCK)) {
- pattern_generator.ClockFallingEdge();
- }
- if (midi.readable()) {
- uint8_t byte = midi.ImmediateRead();
- if (byte == 0xf8) {
- num_ticks = 1;
- } else if (byte == 0xfa) {
- pattern_generator.Reset();
- }
- }
- } else {
- clock.Tick();
- clock.Wrap(swing_amount);
- if (clock.raising_edge()) {
- num_ticks = increment;
- }
- if (clock.past_falling_edge()) {
- pattern_generator.ClockFallingEdge();
- }
- }
-
- // RESET
- if ((inputs_value & INPUT_RESET) && !(previous_inputs & INPUT_RESET)) {
- pattern_generator.Reset();
-
- // !! HACK AHEAD !!
- //
- // Earlier versions of the firmware retriggered the outputs whenever a
- // RESET signal was received. This allowed for nice drill'n'bass effects,
- // but made synchronization with another sequencer a bit glitchy (risk of
- // double notes at the beginning of a pattern). It was later decided
- // to remove this behaviour and make the RESET transparent (just set the
- // step index without producing any trigger) - similar to the MIDI START
- // message. However, the factory testing script relies on the old behaviour.
- // To solve this problem, we reproduce this behaviour the first 5 times the
- // module is powered. After the 5th power-on (or settings change) cycle,
- // this odd behaviour disappears.
- if (pattern_generator.factory_testing() ||
- clock.bpm() >= 40 ||
- clock.locked()) {
- pattern_generator.Retrigger();
- clock.Reset();
- }
- }
- previous_inputs = inputs_value;
-
- if (num_ticks) {
- swing_amount = pattern_generator.swing_amount();
- pattern_generator.TickClock(num_ticks);
- }
- }
-
- enum SwitchState {
- SWITCH_STATE_JUST_PRESSED = 0xfe,
- SWITCH_STATE_PRESSED = 0x00,
- SWITCH_STATE_JUST_RELEASED = 0x01,
- SWITCH_STATE_RELEASED = 0xff
- };
-
- inline void HandleTapButton() {
- static uint8_t switch_state = 0xff;
- static uint16_t switch_hold_time = 0;
-
- switch_state = switch_state << 1;
- if (inputs.Read() & INPUT_SW_RESET) {
- switch_state |= 1;
- }
-
- if (switch_state == SWITCH_STATE_JUST_PRESSED) {
- if (parameter == PARAMETER_NONE) {
- if (!pattern_generator.tap_tempo()) {
- pattern_generator.Reset();
- if (pattern_generator.factory_testing() ||
- clock.bpm() >= 40 ||
- clock.locked()) {
- clock.Reset();
- }
- } else {
- uint32_t new_bpm = (F_CPU * 60L) / (32L * kUpdatePeriod * tap_duration);
- if (new_bpm >= 30 && new_bpm <= 480) {
- clock.Update(new_bpm, pattern_generator.clock_resolution());
- clock.Reset();
- clock.Lock();
- } else {
- clock.Unlock();
- }
- tap_duration = 0;
- }
- }
- switch_hold_time = 0;
- } else if (switch_state == SWITCH_STATE_PRESSED) {
- ++switch_hold_time;
- if (switch_hold_time == 500) {
- long_press_detected = true;
- }
- }
- }
-
- ISR(TIMER2_COMPA_vect, ISR_NOBLOCK) {
- static uint8_t switch_debounce_prescaler;
-
- ++tap_duration;
- ++switch_debounce_prescaler;
- if (switch_debounce_prescaler >= 10) {
- // Debounce RESET/TAP switch and perform switch action.
- HandleTapButton();
- switch_debounce_prescaler = 0;
- }
-
- HandleClockResetInputs();
- adc.Scan();
-
- pattern_generator.IncrementPulseCounter();
- UpdateShiftRegister();
- UpdateLeds();
- }
-
- static int16_t pot_values[8];
-
- void ScanPots() {
- if (long_press_detected) {
- if (parameter == PARAMETER_NONE) {
- // Freeze pot values
- for (uint8_t i = 0; i < 8; ++i) {
- pot_values[i] = adc.Read8(i);
- }
- parameter = PARAMETER_WAITING;
- } else {
- parameter = PARAMETER_NONE;
- pattern_generator.SaveSettings();
- }
- long_press_detected = false;
- }
-
- if (parameter == PARAMETER_NONE) {
- uint8_t bpm = adc.Read8(ADC_CHANNEL_TEMPO);
- bpm = U8U8MulShift8(bpm, 220) + 20;
- if (bpm != clock.bpm() && !clock.locked()) {
- clock.Update(bpm, pattern_generator.clock_resolution());
- }
- PatternGeneratorSettings* settings = pattern_generator.mutable_settings();
- settings->options.drums.x = ~adc.Read8(ADC_CHANNEL_X_CV);
- settings->options.drums.y = ~adc.Read8(ADC_CHANNEL_Y_CV);
- settings->options.drums.randomness = ~adc.Read8(ADC_CHANNEL_RANDOMNESS_CV);
- settings->density[0] = ~adc.Read8(ADC_CHANNEL_BD_DENSITY_CV);
- settings->density[1] = ~adc.Read8(ADC_CHANNEL_SD_DENSITY_CV);
- settings->density[2] = ~adc.Read8(ADC_CHANNEL_HH_DENSITY_CV);
- } else {
- for (uint8_t i = 0; i < 8; ++i) {
- int16_t value = adc.Read8(i);
- int16_t delta = value - pot_values[i];
- if (delta < 0) {
- delta = -delta;
- }
- if (delta > 32) {
- pot_values[i] = value;
- switch (i) {
- case ADC_CHANNEL_BD_DENSITY_CV:
- parameter = PARAMETER_CLOCK_RESOLUTION;
- pattern_generator.set_clock_resolution((255 - value) >> 6);
- clock.Update(clock.bpm(), pattern_generator.clock_resolution());
- pattern_generator.Reset();
- break;
-
- case ADC_CHANNEL_SD_DENSITY_CV:
- parameter = PARAMETER_TAP_TEMPO;
- pattern_generator.set_tap_tempo(!(value & 0x80));
- if (!pattern_generator.tap_tempo()) {
- clock.Unlock();
- }
- break;
-
- case ADC_CHANNEL_HH_DENSITY_CV:
- parameter = PARAMETER_SWING;
- pattern_generator.set_swing(!(value & 0x80));
- break;
-
- case ADC_CHANNEL_X_CV:
- parameter = PARAMETER_OUTPUT_MODE;
- pattern_generator.set_output_mode(!(value & 0x80) ? 1 : 0);
- break;
-
- case ADC_CHANNEL_Y_CV:
- parameter = PARAMETER_GATE_MODE;
- pattern_generator.set_gate_mode(!(value & 0x80));
- break;
-
- case ADC_CHANNEL_RANDOMNESS_CV:
- parameter = PARAMETER_CLOCK_OUTPUT;
- pattern_generator.set_output_clock(!(value & 0x80));
- break;
- }
- }
- }
- }
- }
-
- void Init() {
- sei();
- UCSR0B = 0;
-
- leds.set_mode(DIGITAL_OUTPUT);
- inputs.set_mode(DIGITAL_INPUT);
- inputs.EnablePullUpResistors();
-
- clock.Init();
- adc.Init();
- adc.set_num_inputs(ADC_CHANNEL_LAST);
- Adc::set_reference(ADC_DEFAULT);
- Adc::set_alignment(ADC_LEFT_ALIGNED);
- pattern_generator.Init();
- shift_register.Init();
- midi.Init();
-
- TCCR2A = _BV(WGM21);
- TCCR2B = 3;
- OCR2A = kUpdatePeriod - 1;
- TIMSK2 |= _BV(1);
- }
-
- int main(void) {
- ResetWatchdog();
- Init();
- clock.Update(120, pattern_generator.clock_resolution());
- while (1) {
- // Use any spare cycles to read the CVs and update the potentiometers
- ScanPots();
- }
- }
|