Skip to content

Instantly share code, notes, and snippets.

@Ghillermo
Created November 18, 2021 02:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ghillermo/753d66df96c8d02061e28cf1aca5dbbc to your computer and use it in GitHub Desktop.
Save Ghillermo/753d66df96c8d02061e28cf1aca5dbbc to your computer and use it in GitHub Desktop.
#include <MIDI.h>
#include <FastLED.h>
#include "EncoderStepCounter.h"
//PINES EN USO:
#define LEDPIN 9// Led Ring
//BOTONERA:
#define PREV A0 //Siguiente pista
#define NEXT A1 //Anterior pista
#define NOTEADD A6 //Subir semitono
#define NOTEDEC A7 //Bajar semitono
#define OFFSETADD A5 //Rotación horaria
#define OFFSETDEC A4 //Rotación antihoraria
#define NUMBERADD A2 //Más notas
#define NUMBERDEC A3 //Menos notas
//ENCODER
//ENCODER
#define ENCODER_PIN1 2 //PIN CLOCK
#define ENCODER_INT1 digitalPinToInterrupt(ENCODER_PIN1)
#define ENCODER_PIN2 3 //PIN DATA
#define ENCODER_INT2 digitalPinToInterrupt(ENCODER_PIN2)
EncoderStepCounter encoder(ENCODER_PIN1, ENCODER_PIN2, HALF_STEP);
#define ENC_SW 4
//Valores y globales:
#define DELAYVAL 200 // Time (in milliseconds) to pause between pixels
#define BPM 120
#define MIDI_VELOCITY 123 //El CC midi 123 es la intensidad, la reduciremos a cero cuando queramos silencio
#define NUM_LEDS 16
#define STEPS 16
int Delay = 15000 / BPM;
MIDI_CREATE_DEFAULT_INSTANCE();
byte Pista = 0;
byte Paso = 0;
CRGBArray<NUM_LEDS> leds;
class Track { //Clase track. El secuenciador lleva una serie de tracks superpuestos.
private:
byte Note; //Tono
byte Offset; //Rotación
byte Number; //Número de notas
unsigned int Euclidean; //Distribución euclídea de las notas
public:
unsigned int rotateBitShiftRight(unsigned int chunk, byte rotation) {//bitshift but trim part is parsed to the opposite side:
chunk = (chunk >> rotation) | (chunk << (16 - rotation)); //Suma lógica (OR) del valor con bitshift desde LSB y complementaria desde MSB
return chunk;
}
unsigned int rotateBitShiftLeft(unsigned int chunk, byte rotation) {
chunk = (chunk << rotation) | (chunk >> (16 - rotation)); //Suma lógica (OR) del valor con bitshift desde LSB y complementaria desde MSB
return chunk;
}
void setTrack() { //Constructot
Note = 78; //C3
Offset = 0;
Number = 0;
Euclidean = 0b0000000000000000;
}
void populateEuclidean() { //Rellena un patrón, cargándolo de la memoria del programa.
const unsigned int euclideanPatterns[17] = { //Precalculated Euclid patterns
0b0000000000000000,
0b1000000000000000,
0b1000000010000000,
0b1000010000100000,
0b1000100010001000,
0b1001001001001000,
0b1001010010010100,
0b1001010100101010,
0b1010101010101010,
0b1011010101101010,
0b1011010110110101,
0b1011011011011011,
0b1011101110111011,
0b1011110111101111,
0b1011111110111111,
0b1111111111111110,
0b1111111111111111
};
Euclidean = rotateBitShiftRight(euclideanPatterns[Number], Offset); //Rotated to Offset value
}
void addOffset() {
if ( Offset > 15 ) {
Offset = 0;
} else {
Offset++;
}
Euclidean = rotateBitShiftRight( Euclidean , 1 );
}
void decOffset() {
if (Offset > 0) {
Offset--;
} else {
Offset = 15;
}
Euclidean = rotateBitShiftLeft( Euclidean , 1 );
}
void addNumber() {
if (Number < STEPS) {
Number++;
}
else {
Number = 0;
}
populateEuclidean();
}
void decNumber() {
if ( Number > 0) {
Number--;
}
else {
Number = STEPS;
}
populateEuclidean();
}
void addNote() {
Note++;
}
void decNote() {
Note--;
}
byte getNote() {
return Note;
}
unsigned int getEuclidean() {
return Euclidean;
}
};
Track track[6]; //Creamos un array de 6 tracks.
class Anillo { //Un Anillo es un secuenciador con leds que toca notas.
private:
byte Output[STEPS];//array 16 colores
byte Posicion;//posición del puntero
byte ColorActual;//color puntero
byte Anterior; //Ultima nota tocada
public:
void setAnillo() {
for (int i = 0; i < STEPS; i++) {
Output[i] = 0;
}
Posicion = 0;
ColorActual = 0;
}
void setAnillo(byte pos, byte col) {
Posicion = pos;
ColorActual = col;
}
voidSetPosicion(byte val) {
Posicion = val;
}
void crearFondo() { //Calcula la suma aleatoria de los 6 patrones.
for (int i = 15; i >= 0; i--) {
byte posibilidades[6] = {0, 0, 0, 0, 0, 0,};
byte contador = 0;
for (int j = 0; j < 6; j++) {
if (bitRead(track[j].getEuclidean(), i)) {
//Serial.print("1-");
posibilidades[contador] = track[j].getNote();
contador++;
}
}
if (contador > 0) {
Output[i] = posibilidades[random(contador)];
leds[i] = CHSV(map(Output[i], 60, 106, 0, 255), 250, 100);
}
else {
leds[i] = CRGB::Black;
Output[i] = 0;
//Serial.print("0-");
}
}
}
void mostrarPatronActual() { //Muestra con más brillo el patrón seleccionado.
for (int i = 0; i < 16; i++) {
if ( bitRead(track[Pista].getEuclidean(), i)) {
leds[i].maximizeBrightness(150);
}
}
}
void crearPuntero() { //Marca el paso actual
setAnillo(Paso, CRGB::Orange);
leds[Posicion] = CRGB::Orange;
}
//borrar
void borrar() {
FastLED.clear();
}
//mostrar
void mostrar() { //Necesario para que se actualice la info de los leds.
FastLED.show();
}
void actualiza(byte val) { //Rutina que actualiza la pantalla en cada beat.
borrar();
crearFondo();
crearPuntero();
mostrarPatronActual();
mostrar();
}
void silencio() {
MIDI.sendNoteOff(Anterior, 0, 1);
//MIDI.sendControlChange(MIDI_VELOCITY, 0, 1);
}
void toca(byte val) {
if (Output[val] != 0) {
MIDI.sendNoteOn(Output[val], 100, 1);
Anterior = Output[val];
}
}
};
Anillo seq;
void botones() {
static byte boton = 0;
static byte i = A0;
static byte contador = 0;
const int Theshold = 900;
const byte Debounce = 10;
static unsigned long int ultimaComprobacion = 0;
if (boton == 0) {
if (contador > Debounce) {
//Serial.print(i);
//Serial.print(" - ");
//Serial.println(contador);
contador = 0;
boton = i;
return;
}
else if (analogRead(i) > Theshold) {
if ((millis() - ultimaComprobacion) > 1)
{
//Serial.print(analogRead(i));
contador ++;
//Serial.print(contador);
//Serial.print(" ");
ultimaComprobacion = millis();
}
}
else {
contador = 0;
i++;
if (i > A7) {
i = A0;
}
}
} else if (analogRead(boton) > Theshold) {
return; //No realiza la acción hasta soltar el boton.
}
else {
switch (boton) {
case PREV: (Pista == 0) ? Pista = 5 : Pista--; break;
case NEXT: (Pista == 5) ? Pista = 0 : Pista++; break;
case OFFSETADD: track[Pista].addOffset(); break;
case OFFSETDEC: track[Pista].decOffset(); break;
case NUMBERADD: track[Pista].addNumber(); break;
case NUMBERDEC: track[Pista].decNumber(); break;
case NOTEADD: track[Pista].addNote(); break;
case NOTEDEC: track[Pista].decNote(); break;
//Serial.println();
//Serial.print("Botón pulsado: ");
//Serial.print(boton);
}
boton = 0;
seq.actualiza(Paso); //Si hay cambios actualiza el display.
}
}
bool encoderSW() {
if ( digitalRead(ENC_SW) == 1) {
return 0;
}
while ( digitalRead(ENC_SW) == 0) {} //return 1;
while ( digitalRead(ENC_SW) == 1) {}
while ( digitalRead(ENC_SW) == 0) {}
}
void setup() {
MIDI.begin(MIDI_CHANNEL_OMNI);
//Serial.begin(115200);
FastLED.addLeds<NEOPIXEL, LEDPIN>(leds, NUM_LEDS);
FastLED.setBrightness(25);
pinMode(A1, INPUT);
pinMode(A2, INPUT);
pinMode(A3, INPUT);
pinMode(A4, INPUT);
pinMode(A5, INPUT);
pinMode(A6, INPUT);
pinMode(A7, INPUT);
//ENCODER
encoder.begin();
pinMode (ENC_SW, INPUT_PULLUP);
attachInterrupt(ENCODER_INT1, interrupt, CHANGE);
attachInterrupt(ENCODER_INT2, interrupt, CHANGE);
randomSeed(analogRead(A0));
for (int i = 0; i < 6; i++) {
track[i].setTrack();
}
}
void interrupt() {
encoder.tick();
}
void loop() {
EVERY_N_MILLISECONDS_I(bpm, Delay) {
static byte cont = 0;
seq.silencio();
Paso = cont;
seq.actualiza(cont);
seq.toca(cont);
cont++;
bpm.setPeriod(Delay);
if (cont >= STEPS) {
cont = 0;
}
}
botones();
////Serial.println(myEnc.read());
encoderSW();
static unsigned int position = BPM;
signed char pos = encoder.getPosition();
if (pos != 0) {
position += pos;
if (position == 0) {
position = 1;
}
encoder.reset();
//Serial.println(position);
Delay = 15000 / position;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment