Permalink
Cannot retrieve contributors at this time
| // Copyright 2011 Emilie Gillet. | |
| // | |
| // Author: Emilie Gillet (emilie.o.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/>. | |
| // | |
| // ----------------------------------------------------------------------------- | |
| // | |
| // Global clock. | |
| #include "grids/pattern_generator.h" | |
| #include <avr/eeprom.h> | |
| #include <avr/pgmspace.h> | |
| #include "avrlib/op.h" | |
| #include "grids/resources.h" | |
| namespace grids { | |
| using namespace avrlib; | |
| /* static */ | |
| Options PatternGenerator::options_; | |
| /* static */ | |
| uint8_t PatternGenerator::pulse_; | |
| /* static */ | |
| uint8_t PatternGenerator::step_; | |
| /* static */ | |
| bool PatternGenerator::first_beat_; | |
| /* static */ | |
| bool PatternGenerator::beat_; | |
| /* static */ | |
| uint8_t PatternGenerator::euclidean_step_[kNumParts]; | |
| /* static */ | |
| uint8_t PatternGenerator::state_; | |
| /* static */ | |
| uint8_t PatternGenerator::pulse_duration_counter_; | |
| /* static */ | |
| uint8_t PatternGenerator::part_perturbation_[kNumParts]; | |
| /* static */ | |
| PatternGeneratorSettings PatternGenerator::settings_; | |
| /* static */ | |
| uint8_t PatternGenerator::factory_testing_; | |
| /* extern */ | |
| PatternGenerator pattern_generator; | |
| static const prog_uint8_t* drum_map[5][5] = { | |
| { node_10, node_8, node_0, node_9, node_11 }, | |
| { node_15, node_7, node_13, node_12, node_6 }, | |
| { node_18, node_14, node_4, node_5, node_3 }, | |
| { node_23, node_16, node_21, node_1, node_2 }, | |
| { node_24, node_19, node_17, node_20, node_22 }, | |
| }; | |
| /* static */ | |
| uint8_t PatternGenerator::ReadDrumMap( | |
| uint8_t step, | |
| uint8_t instrument, | |
| uint8_t x, | |
| uint8_t y) { | |
| uint8_t i = x >> 6; | |
| uint8_t j = y >> 6; | |
| const prog_uint8_t* a_map = drum_map[i][j]; | |
| const prog_uint8_t* b_map = drum_map[i + 1][j]; | |
| const prog_uint8_t* c_map = drum_map[i][j + 1]; | |
| const prog_uint8_t* d_map = drum_map[i + 1][j + 1]; | |
| uint8_t offset = (instrument * kStepsPerPattern) + step; | |
| uint8_t a = pgm_read_byte(a_map + offset); | |
| uint8_t b = pgm_read_byte(b_map + offset); | |
| uint8_t c = pgm_read_byte(c_map + offset); | |
| uint8_t d = pgm_read_byte(d_map + offset); | |
| return U8Mix(U8Mix(a, b, x << 2), U8Mix(c, d, x << 2), y << 2); | |
| } | |
| /* static */ | |
| void PatternGenerator::EvaluateDrums() { | |
| // At the beginning of a pattern, decide on perturbation levels. | |
| if (step_ == 0) { | |
| for (uint8_t i = 0; i < kNumParts; ++i) { | |
| uint8_t randomness = options_.swing | |
| ? 0 : settings_.options.drums.randomness >> 2; | |
| part_perturbation_[i] = U8U8MulShift8(Random::GetByte(), randomness); | |
| } | |
| } | |
| uint8_t instrument_mask = 1; | |
| uint8_t x = settings_.options.drums.x; | |
| uint8_t y = settings_.options.drums.y; | |
| uint8_t accent_bits = 0; | |
| for (uint8_t i = 0; i < kNumParts; ++i) { | |
| uint8_t level = ReadDrumMap(step_, i, x, y); | |
| if (level < 255 - part_perturbation_[i]) { | |
| level += part_perturbation_[i]; | |
| } else { | |
| // The sequencer from Anushri uses a weird clipping rule here. Comment | |
| // this line to reproduce its behavior. | |
| level = 255; | |
| } | |
| uint8_t threshold = ~settings_.density[i]; | |
| if (level > threshold) { | |
| if (level > 192) { | |
| accent_bits |= instrument_mask; | |
| } | |
| state_ |= instrument_mask; | |
| } | |
| instrument_mask <<= 1; | |
| } | |
| if (output_clock()) { | |
| state_ |= accent_bits ? OUTPUT_BIT_COMMON : 0; | |
| state_ |= step_ == 0 ? OUTPUT_BIT_RESET : 0; | |
| } else { | |
| state_ |= accent_bits << 3; | |
| } | |
| } | |
| /* static */ | |
| void PatternGenerator::EvaluateEuclidean() { | |
| // Refresh only on sixteenth notes. | |
| if (step_ & 1) { | |
| return; | |
| } | |
| // Euclidean pattern generation | |
| uint8_t instrument_mask = 1; | |
| uint8_t reset_bits = 0; | |
| for (uint8_t i = 0; i < kNumParts; ++i) { | |
| uint8_t length = (settings_.options.euclidean_length[i] >> 3) + 1; | |
| uint8_t density = settings_.density[i] >> 3; | |
| uint16_t address = U8U8Mul(length - 1, 32) + density; | |
| while (euclidean_step_[i] >= length) { | |
| euclidean_step_[i] -= length; | |
| } | |
| uint32_t step_mask = 1L << static_cast<uint32_t>(euclidean_step_[i]); | |
| uint32_t pattern_bits = pgm_read_dword(lut_res_euclidean + address); | |
| if (pattern_bits & step_mask) { | |
| state_ |= instrument_mask; | |
| } | |
| if (euclidean_step_[i] == 0) { | |
| reset_bits |= instrument_mask; | |
| } | |
| instrument_mask <<= 1; | |
| } | |
| if (output_clock()) { | |
| state_ |= reset_bits ? OUTPUT_BIT_COMMON : 0; | |
| state_ |= (reset_bits == 0x07) ? OUTPUT_BIT_RESET : 0; | |
| } else { | |
| state_ |= reset_bits << 3; | |
| } | |
| } | |
| /* static */ | |
| void PatternGenerator::LoadSettings() { | |
| options_.unpack(eeprom_read_byte(NULL)); | |
| factory_testing_ = eeprom_read_byte((uint8_t*)(1)) + 1; | |
| } | |
| /* static */ | |
| void PatternGenerator::SaveSettings() { | |
| eeprom_write_byte(NULL, options_.pack()); | |
| ++factory_testing_; | |
| if (factory_testing_ >= 5) { | |
| factory_testing_ = 5; | |
| } | |
| eeprom_write_byte((uint8_t*)(1), factory_testing_); | |
| } | |
| /* static */ | |
| void PatternGenerator::Evaluate() { | |
| state_ = 0; | |
| pulse_duration_counter_ = 0; | |
| Random::Update(); | |
| // Highest bits: clock and random bit. | |
| state_ |= 0x40; | |
| state_ |= Random::state() & 0x80; | |
| if (output_clock()) { | |
| state_ |= OUTPUT_BIT_CLOCK; | |
| } | |
| // Refresh only at step changes. | |
| if (pulse_ != 0) { | |
| return; | |
| } | |
| if (options_.output_mode == OUTPUT_MODE_EUCLIDEAN) { | |
| EvaluateEuclidean(); | |
| } else { | |
| EvaluateDrums(); | |
| } | |
| } | |
| /* static */ | |
| int8_t PatternGenerator::swing_amount() { | |
| if (options_.swing && output_mode() == OUTPUT_MODE_DRUMS) { | |
| int8_t value = U8U8MulShift8(settings_.options.drums.randomness, 42 + 1); | |
| return (!(step_ & 2)) ? value : -value; | |
| } else { | |
| return 0; | |
| } | |
| } | |
| } // namespace grids | |