Problems about using multiple audio objects, T4

Status
Not open for further replies.

M4ngu

Well-known member
Hi there,
I'm working in a 8 track sequencer, using Teensy 4 (which is amazing btw) The project uses a few big arrays (64 patterns with 8 tracks and 64 steps each) and I have arrays for the patterns itself, probability, velocity, play mode... but I'm facing some weird problems because I want to make it to play audio, apart of the midi & trigger outs.
For the leds I'm using WS2812Serial.h which has no glitches when using audio like other WS2812 libraries.
So actually has 8 basic drum synths, a noise generator connected to 8 envelopes, 8 filters and about 16 mixers to put all this together, the thing is that some stuff just stop working, like the code for saving the projects to the SD card needs to be disabled or the machine gets stuck on start up (loading presets works btw), every time I add other audio object, even if it's not connected to anything, something happens like a drum synth stops working, first 2 tracks start to play backwards...
Tried with the SdFat library but it does not work if the Audio library is there.
I've been measuring the audio memory and it was not too big (about 30 if I remember well, and I have set 100) so that shouldn't be the problem.
the statics when compiling are:
RAM: 32.2% (used 168628 bytes from 524288 bytes)
Flash: 3.5% (used 71184 bytes from 2031616 bytes)
the code is about 2500 lines so posting here may be not convenient,
this is the part that saves the project to a SD card, maybe someone can tell me a better way to do this (my coding skills are pretty limited indeed):
Code:
void saveAll()
{
  File dataFile;

  SD.remove("val.dat");
  dataFile = SD.open("val.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&val, sizeof(val));
  dataFile.close();

  SD.remove("prob.dat");
  dataFile = SD.open("prob.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&prob, sizeof(prob));
  dataFile.close();

  SD.remove("steps.dat");
  dataFile = SD.open("steps.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&steps, sizeof(steps));
  dataFile.close();

  SD.remove("pMode.dat");
  dataFile = SD.open("pMode.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&pMode, sizeof(pMode));
  dataFile.close();

  SD.remove("clockDiv.dat");
  dataFile = SD.open("clockDiv.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&clockDiv, sizeof(clockDiv));
  dataFile.close();

  SD.remove("euclNum.dat");
  dataFile = SD.open("euclNum.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&euclNum, sizeof(euclNum));
  dataFile.close();

  SD.remove("dot.dat");
  dataFile = SD.open("dot.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&dot, sizeof(dot));
  dataFile.close();

  SD.remove("roll.dat");
  dataFile = SD.open("roll.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&roll, sizeof(roll));
  dataFile.close();

  SD.remove("midiNote.dat");
  dataFile = SD.open("midiNote.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&midiNote, sizeof(midiNote));
  dataFile.close();

  SD.remove("menuVar.dat");
  dataFile = SD.open("menuVar.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&menuVar, sizeof(menuVar));
  dataFile.close();

  SD.remove("noInitPatt.dat");
  dataFile = SD.open("noInitPatt.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&noInitPatt, sizeof(noInitPatt));
  dataFile.close();

  SD.remove("raw.dat");
  dataFile = SD.open("raw.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&raw, sizeof(raw));
  dataFile.close();
}

and if some one has a clue, would like to know:
-is there a limit for the audio objects in a project?
-would help to allocate some of the bigger arrays maybe in the program memory to save some ram?
-could the 'save' function be done in a better way(so it does not create any conflict)?
thanks a lot in advance
 
The symptoms suggest that you are running out of memory or having a problem indexing outside an array.
some of the bigger arrays
How many and how big? Are you using malloc or new to allocate them?
It might be best to post a zip archive of your code.

Pete
 
i've just read something about this, probably I'm using too much global variables, will start modifying the code in that direction, also I've been using lots of byte or unit8_t to save ram but looks like in terms of speed unit32_t is the best, specially in functions etc, at least I do have now something to start tweaking,
those are some of the variables and arrays:

Code:
uint64_t dot[64][8];
byte prob[64][8][64];
uint64_t roll[64][8];
byte val[64][8][64]; //  velocidad midi 
byte steps[64][8]; // duracion de cada linea
byte pMode[64][8];     // 0 normal, 1 reverse, 2 pendulum
byte clockDiv[64][8];    // division de reloj del row -1 ( 0 para clock divison = 1 ),
byte euclNum[64][8];
unsigned long long noInitPatt;
unsigned long long masterTrack;
bool resetSeq_;
bool playDrum[8] = { true, true, true, true, true, true, true, true };
byte midiNote[8];
byte actPatt;
byte nextPatt;
byte pattCounter;
byte part[65][64];
byte loopSize[65];
byte actPart = 64;
unsigned long long noInitPart; 
int multButtMax = 0;
int multButtMin = 63;
int multButt = -1; //  el ultimo botón pulsado y si se ha pulsado algo
byte multButt_ = 64; // esto es para ajustar con el primer loop
bool liveRecord_;
// otras variables relativas a las secuencias:
int pos[8];       // posicion  de reproducción
int count[8];       // contador para el clock divider
byte page[8]; // contador de paginas para del display d cada pista
bool dir[8] = { true, true, true, true, true, true, true, true }; // direccion de la reproduccion para el modo pendulum
bool mute[8];
byte sculpt[8]; // random offset
int i;               // loop helpers
byte n;
int x;
int y;
int y_; // pulso de la secuencia que representa el display
int x_; //
byte suma;
int xx;
int yy;
int zz;
// control de tempo:
bool intTempo = false; // tempo interno, false seria externo (clock input)
int tempo = 120; // 120/min = 2/seg = 500 milis;  tempo en la medida tradicional,
elapsedMicros temp;
unsigned int temp_ = 125000; // deducido a partir de tempo o viceversa, lo q debe durar temp
unsigned int t32 = temp_ / 2;
 
uint32_t might speed up your code a bit but lack of speed isn't your problem right now. It appears to be lack of space. Currently, the uint8_t arrays that you've shown in your message take up nearly 80kB. If you change them to uint32_t that will increase your memory requirement to 320kB which will only make the problem much worse.
I would still like to see *all* your code.

Pete
 
well, I moved many variables that were declared as global to the main loop or functions, on every step things became more normal, first 2 tracks are not playing reversed anymore and everything is working, all drums are back to life now and the save function is active too without making the machine to crash on startup,
anyway for sure there are a lot of things that could be improved.
I would still like to see *all* your code.
here is it, any tip would be much appreciated
there are 3 files ( 5 indeed but one has all the code commented and the other is just a led pattern for the start up...so those 3 are the only important ones)
ini.h :
Code:
// constantes, variables y ajustes

#define bitSet64(value, bit) ((value) |= (1ULL << (bit)))
#define bitClear64(value, bit) ((value) &= ~(1ULL << (bit)))
#define bitWrite64(value, bit, bitvalue) (bitvalue ? bitSet64(value, bit) : bitClear64(value, bit))

#define sonido
//#define debug
#define midi_
//#define midiSync
//#define SAMPLER
#define DRUM
#define DRUMS
#include <math.h>
#include <SD.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SerialFlash.h>

#ifdef sonido
//------------------AUDIO DESIGN TOOL----------------

// GUItool: begin automatically generated code
#ifdef SAMPLER
String muestra[64] = {  "1.raw",  "2.raw",  "3.raw",  "4.raw",  "5.raw",  "6.raw",  "7.raw",  "8.raw",  "9.raw",  "10.raw",
                        "11.raw", "12.raw", "13.raw", "14.raw", "15.raw", "16.raw", "17.raw", "18.raw", "19.raw", "20.raw",
                        "21.raw", "22.raw", "23.raw", "24.raw", "25.raw", "26.raw", "27.raw", "28.raw", "29.raw", "30.raw",
                        "31.raw", "32.raw", "33.raw", "34.raw", "35.raw", "36.raw", "37.raw", "38.raw", "39.raw", "40.raw",
                        "41.raw", "42.raw", "43.raw", "44.raw", "45.raw", "46.raw", "47.raw", "48.raw", "49.raw", "50.raw",
                        "51.raw", "52.raw", "53.raw", "54.raw", "55.raw", "56.raw", "57.raw", "58.raw", "59.raw", "60.raw",
                        "61.raw", "62.raw", "63.raw", "64.raw"
                     };
AudioPlaySdRaw           sample0;        //xy=220,122
AudioPlaySdRaw           sample1;        //xy=220,242
AudioPlaySdRaw           sample2;        //xy=220,362
AudioPlaySdRaw           sample3;        //xy=220,482
AudioPlaySdRaw           sample4;        //xy=220,602
AudioPlaySdRaw           sample5;        //xy=220,722
AudioPlaySdRaw           sample6;        //xy=220,842
AudioPlaySdRaw           sample7;        //xy=220,962
AudioConnection          patchCord12(sample0, 0, mixer0, 2);
AudioConnection          patchCord14(sample1, 0, mixer1, 2);
AudioConnection          patchCord16(sample2, 0, mixer2, 2);
AudioConnection          patchCord18(sample3, 0, mixer3, 2);
AudioConnection          patchCord20(sample4, 0, mixer4, 2);
AudioConnection          patchCord22(sample5, 0, mixer5, 2);
AudioConnection          patchCord23(sample6, 0, mixer6, 2);
AudioConnection          patchCord24(sample7, 0, mixer7, 2);
#endif

AudioSynthNoiseWhite     noise1;         //xy=57,331.2500047683716

AudioMixer4              mixer1;         //xy=379,202
AudioMixer4              mixer2;         //xy=379,322
AudioMixer4              mixer3;         //xy=379,442
AudioMixer4              mixer4;         //xy=379,562
AudioMixer4              mixer0;         //xy=381,94.75
AudioMixer4              mixer5;         //xy=379,682
AudioMixer4              mixer6;         //xy=379,802
AudioMixer4              mixer7;         //xy=379,922
#ifdef DRUMS
AudioSynthSimpleDrum     drum1;          //xy=219,162
AudioSynthSimpleDrum     drum2;          //xy=219,282
AudioSynthSimpleDrum     drum0;          //xy=220,35
AudioSynthSimpleDrum     drum3;          //xy=219,402
AudioSynthSimpleDrum     drum4;          //xy=219,522
AudioSynthSimpleDrum     drum5;          //xy=219,642
AudioSynthSimpleDrum     drum6;          //xy=219,762
AudioSynthSimpleDrum     drum7;          //xy=219,882
AudioConnection          patchCord9(drum1, 0, mixer1, 0);
AudioConnection          patchCord10(drum2, 0, mixer2, 0);
AudioConnection          patchCord11(drum0, 0, mixer0, 0);
AudioConnection          patchCord13(drum3, 0, mixer3, 0);
AudioConnection          patchCord15(drum4, 0, mixer4, 0);
AudioConnection          patchCord17(drum5, 0, mixer5, 0);
AudioConnection          patchCord19(drum6, 0, mixer6, 0);
AudioConnection          patchCord21(drum7, 0, mixer7, 0);
#endif
AudioEffectEnvelope      envelope0;      //xy=229.75,76.75
AudioEffectEnvelope      envelope1;      //xy=230,202
AudioEffectEnvelope      envelope2;      //xy=230,322
AudioEffectEnvelope      envelope3;      //xy=230,442
AudioEffectEnvelope      envelope4;      //xy=230,562
AudioEffectEnvelope      envelope5;      //xy=230,682
AudioEffectEnvelope      envelope6;      //xy=230,802
AudioEffectEnvelope      envelope7;      //xy=230,922
AudioFilterBiquad        biquad1;        //xy=522,202
AudioFilterBiquad        biquad2;        //xy=522,322
AudioFilterBiquad        biquad0;        //xy=523.2500076293945,109.50000095367432
AudioFilterBiquad        biquad3;        //xy=522,442
AudioFilterBiquad        biquad4;        //xy=522,562
AudioFilterBiquad        biquad5;        //xy=522,682
//AudioFilterStateVariable filter1;        //xy=525,33.75

AudioFilterBiquad        biquad6;        //xy=522,802
AudioFilterBiquad        biquad7;        //xy=522,922
AudioMixer4              mixer10;        //xy=707,710
AudioMixer4              mixer11;        //xy=709,845
AudioMixer4              mixer13;        //xy=710,598
AudioMixer4              mixer12;        //xy=714,384
AudioMixer4              mixer9;         //xy=719,276
AudioMixer4              mixer8;         //xy=721,150
AudioMixer4              mixer16;        //xy=808,483
//AudioEffectFreeverb      freeverb1;      //xy=950,487
AudioMixer4              mixer15;        //xy=1086,599
AudioMixer4              mixer14;        //xy=1090,371
AudioOutputI2S           i2s1;           //xy=1216,481
AudioConnection          patchCord1(noise1, envelope1);
AudioConnection          patchCord2(noise1, envelope2);
AudioConnection          patchCord3(noise1, envelope3);
AudioConnection          patchCord4(noise1, envelope4);
AudioConnection          patchCord5(noise1, envelope5);
AudioConnection          patchCord6(noise1, envelope6);
AudioConnection          patchCord7(noise1, envelope7);
AudioConnection          patchCord8(noise1, envelope0);
AudioConnection          patchCord25(envelope0, 0, mixer0, 1);
AudioConnection          patchCord26(envelope1, 0, mixer1, 1);
AudioConnection          patchCord27(envelope2, 0, mixer2, 1);
AudioConnection          patchCord28(envelope3, 0, mixer3, 1);
AudioConnection          patchCord29(envelope4, 0, mixer4, 1);
AudioConnection          patchCord30(envelope5, 0, mixer5, 1);
AudioConnection          patchCord31(envelope6, 0, mixer6, 1);
AudioConnection          patchCord32(envelope7, 0, mixer7, 1);
AudioConnection          patchCord33(mixer1, biquad1);
AudioConnection          patchCord34(mixer2, biquad2);
AudioConnection          patchCord35(mixer3, biquad3);
AudioConnection          patchCord36(mixer4, biquad4);
//AudioConnection          patchCord37(mixer0, 0, filter1, 0);
AudioConnection          patchCord37(mixer0,  biquad0);

AudioConnection          patchCord38(mixer5, biquad5);
AudioConnection          patchCord39(mixer6, biquad6);
AudioConnection          patchCord40(mixer7, biquad7);
AudioConnection          patchCord41(biquad1, 0, mixer8, 1);
AudioConnection          patchCord42(biquad1, 0, mixer9, 1);
AudioConnection          patchCord43(biquad1, 0, mixer12, 1);
AudioConnection          patchCord44(biquad2, 0, mixer8, 2);
AudioConnection          patchCord45(biquad2, 0, mixer9, 2);
AudioConnection          patchCord46(biquad2, 0, mixer12, 2);
AudioConnection          patchCord47(biquad3, 0, mixer8, 3);
AudioConnection          patchCord48(biquad3, 0, mixer9, 3);
AudioConnection          patchCord49(biquad3, 0, mixer12, 3);
AudioConnection          patchCord50(biquad4, 0, mixer10, 0);
AudioConnection          patchCord51(biquad4, 0, mixer11, 0);
AudioConnection          patchCord52(biquad4, 0, mixer13, 0);
AudioConnection          patchCord53(biquad5, 0, mixer10, 1);
AudioConnection          patchCord54(biquad5, 0, mixer11, 1);
AudioConnection          patchCord55(biquad5, 0, mixer13, 1);
//AudioConnection          patchCord56(filter1, 0, mixer8, 0);
//AudioConnection          patchCord57(filter1, 0, mixer9, 0);
//AudioConnection          patchCord58(filter1, 0, mixer12, 0);
AudioConnection          patchCord56(biquad0, 0, mixer8, 0);
AudioConnection          patchCord57(biquad0, 0, mixer9, 0);
AudioConnection          patchCord58(biquad0, 0, mixer12, 0);
AudioConnection          patchCord59(biquad6, 0, mixer10, 2);
AudioConnection          patchCord60(biquad6, 0, mixer11, 2);
AudioConnection          patchCord61(biquad6, 0, mixer13, 2);
AudioConnection          patchCord62(biquad7, 0, mixer10, 3);
AudioConnection          patchCord63(biquad7, 0, mixer11, 3);
AudioConnection          patchCord64(biquad7, 0, mixer13, 3);
AudioConnection          patchCord65(mixer10, 0, mixer14, 1);
AudioConnection          patchCord66(mixer11, 0, mixer15, 1);
AudioConnection          patchCord67(mixer13, 0, mixer16, 1);
AudioConnection          patchCord68(mixer12, 0, mixer16, 0);
AudioConnection          patchCord69(mixer9, 0, mixer15, 0);
AudioConnection          patchCord70(mixer8, 0, mixer14, 0);
//AudioConnection          patchCord71(mixer16, freeverb1);
//AudioConnection          patchCord72(freeverb1, 0, mixer14, 2);
//AudioConnection          patchCord73(freeverb1, 0, mixer15, 2);
AudioConnection          patchCord74(mixer15, 0, i2s1, 1);
AudioConnection          patchCord75(mixer14, 0, i2s1, 0);
// GUItool: end automatically generated code


//------------------AUDIO DESIGN TOOL----------------
#ifdef DRUMS
AudioSynthSimpleDrum     *drum[8] =
{
  &drum0, &drum1, &drum2, &drum3, &drum4, &drum5, &drum6, &drum7
};
#endif

#ifdef SAMPLER
AudioPlaySdRaw           *sample[8] =
{
  &sample0, &sample1, &sample2, &sample3, &sample4, &sample5, &sample6, &sample7
};
#endif
AudioEffectEnvelope      *envelope[8] =
{
  &envelope0, &envelope1, &envelope2, &envelope3, &envelope4, &envelope5, &envelope6, &envelope7
};
AudioFilterBiquad        *biquad[8] =
{
  &biquad0, &biquad1, &biquad2, &biquad3, &biquad4, &biquad5, &biquad6, &biquad7
};
AudioMixer4              *mixer[17] =
{
  &mixer0, &mixer1, &mixer2,  &mixer3,  &mixer4,  &mixer5,  &mixer6,  &mixer7,
  &mixer8, &mixer9, &mixer10, &mixer11, &mixer12, &mixer13, &mixer14, &mixer15,
  &mixer16
};
#endif

/*
  AudioPlayMemory          sample[8];
  #include "AudioSampleHat01wavv.h"
  #include "AudioSampleHat02wavv.h"
  #include "AudioSampleKick02wavq.h"
  #include "AudioSampleKickwavq.h"
  #include "AudioSampleSnarewavq.h"
  #include "AudioSampleSnare01wavq.h"
  #include "AudioSampleSnare02wavq.h"
*/

#ifdef midi_
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
#endif

#ifdef midiSync
byte counter;
byte CLOCK = 248;
byte START = 250;
byte CONTINUE = 251;
byte STOP = 252;
#endif

#include <EEPROM.h>
//int eeAddress;   //Location we want the data to be put.

//  L E D S
#include <WS2812Serial.h>
#define numled 72
#define PIN0 17
byte BRIGHTNESS = 30;
byte drawingMemory[numled * 3];       //  3 bytes per LED
DMAMEM byte displayMemory[numled * 12]; // 12 bytes per LED
WS2812Serial leds(numled, displayMemory, drawingMemory, PIN0, WS2812_GRB);

int ledRef = 0; // contador para refresco de los leds
// numero de ciclos del loop necesarios para la escritura de los leds
#define ledRefMini 40 //50 // reloj perfecto pero mucha latencia?
int ledRef_ = ( ledRefMini * 81 ) - 1; // los 80 ciclos del grid y el pico final!!

// E N C O D E R :
//#define ENCODER_OPTIMIZE_INTERRUPTS
#define ENCODER_DO_NOT_USE_INTERRUPTS
#include <Encoder.h>
#define encA 14
#define encB 15
#define encClick 16
Encoder myEnc( encA, encB );
byte enc4;
byte _enc4;
int enc;
int _enc;
byte encPos;
byte actRow;
byte actCol;
bool encButt;
bool _encButt;
bool encButt_;
bool shuttle;

#define row0  30 // pins for 4051 row
#define row1  31
#define row2  8
#define col0  12 // pins for column
#define col1  13
#define col2  27
#define col3  26
#define pad 33      // COLUMN, pin de lectura del los pad del panel
#define triggIn  19  // entrada de relog
#define resetInput 18


bool butt[8][10];    // lectura de los pads,
bool _butt[8][10];  // comparador
bool butt_[8][8];  // estado del boton, las dos ultimas columnas no se estanb utilizando
bool recModulation;
bool recMode = true; //'true' sería insert note,false sería delete, muy guapo para performance
const byte triggOut[] =  {2, 3, 4, 5, 6, 9, 10, 11};// pines de salida para las 8 pistas de triggers
bool resetIn;
bool _resetIn;
bool trigg;       // entrada del clock
bool _trigg;

#define maxSteps 63 // longitud maxima de la secuencia ( quitandole -1 )

//  VARIABLES PARA ALMACENAR COMO PRESET:
uint64_t dot[64][8];
uint64_t roll[64][8];
byte prob[64][8][(maxSteps + 1)];
byte val[64][8][maxSteps + 1]; // esta variablepodríamos usarlapara la velocidad midi x ejemplo
byte steps[64][8]; // duracion de cada linea
byte pMode[64][8];     // 0 normal, 1 reverse, 2 pendulum
byte clockDiv[64][8];    // division de reloj del row -1 ( 0 para clock divison = 1 ),
byte euclNum[64][8];
uint64_t noInitPatt;
uint64_t masterTrack;
uint64_t noInitProject;
byte part[65][64];
uint64_t noInitPart; // incluir en los presets
//--------GLOBAL SETTINGS---------
bool playSample[8] = { false, false, false, false, false, false, false, false };
bool playDrum[8] = { true, true, true, true, true, true, true, true };
byte midiNote[8];
uint8_t raw[8] = { 2, 3, 4, 5, 6, 7, 8, 9 }; // archivo que reproducira cada sampler/pista
bool resetSeq_;
byte actPatt;
byte nextPatt;
byte pattCounter;
byte loopSize[65];
byte actPart = 64;

byte multButtMax = 0;
byte multButtMin = 63;
int multButt = -1; // esto nos sirve para saber el ultimo botón pulsado y si se ha pulsado algo
byte multButt_ = 64; // esto es para ajustar con el primer loop
bool liveRecord_;

// otras variables relativas a las secuencias:
int pos[8];       // posicion del puntero de reproducción
int count[8];       // contador para el clock divider
byte page[8]; // contador de paginas para del display d cada pista
bool dir[8] = { true, true, true, true, true, true, true, true }; // direccion de la reproduccion para el modo pendulum
//int numeroClave; // variable para mi script de random, calculado a partir del contador de espera de los leds.

// implementaciones avanzadas

//byte ramification; // ciclo acumulativo de remates
/* tipose de rwamidficacion
   proprcional
   aritmetica */

//byte alg; // algoritmo de remates
/*  nothing
    inverse
    reverse
    re-order 3.3.2.3.3.2. -> 3.3.3.3.2.2. */
//bool logic; // para las pistas lógicas
//byte logicType; // quizasa no hace falta bool, solo el tipo > 0
bool mute[8];
int8_t  sculpt[8]; // random offset
int i;               // loop helpers
byte x;
byte y;
byte suma;

// control de tempo:
bool intTempo = false; // tempo interno, false seria externo (clock input)
int tempo = 120; // 120/min = 2/seg = 500 milis;  tempo en la medida tradicional,
elapsedMicros temp;
uint32_t temp_ = 125000; // deducido a partir de tempo o viceversa, lo q debe durar temp
unsigned int t32 = temp_ / 2;
int stepCount; // byte es poco para un contador de pulsos
byte beatCount; // este es para los trsillos y el swing
byte swingAmount; // de 0 a 100
unsigned int swingTemp_; //el tiempo de retraso que lleva el golpe de swing, máximo 1/3 del pulso
bool t32_; // esto no me mola, usaremos algo distinto para el contador del relog
//bool t64;

#define triggLength  1000 //duracion de los triggers en microsegundos
byte editMode; // probando con tempo
byte lastEditMode;
// 0 - normal
// 1 - length     -> azul
// 2 - play mode -> no deberia ser un menu, encoder y selector de pistas
// 3 - clock div  -> verde
// 4 - probabilidad
// 5 - roll
// 6 - rotate
// 7 - euclidean -> no deberia ser un menu, encoder y selector de pistas
// 8 - tempo -> no deberia ser un menu, encoder
// 9 - midi -> no deberia ser un menu, encoder y selector de pistas
// 10 - val
// 11 - audio
// 12 - brillo y settings?
// 13 - swing -> no deberia ser un menu, encoder
// 14 - modulacion
// 15 - pattern
// 16 - drum settings
// 17 - mute
// 18 - invert
// 19 - editPart
// 20 - part grid
// 21 - song editor
// 22 - shuttle
// 23 - escoger muestra

bool edit; // esto se usa para que el encoder no se mueva,
//comprobar en que menus haria falta movimiento

// M E N U  - H O R I Z O N T A L
bool menu2 = false;

// M E N U  V E R T I C A L
bool menuVertical; // activa la vision de menu horizontal
byte menuIndex; // 0 audio, 1 mezcla, 2 modulacion
byte menuParam; // parámtros del 1 al 8
int menuVar[3][8][8]; // [menuIndex][actTrack][menuParam]

//drum synth
#define drumVol 0
#define drumDecay 1
#define drumFreq 2
#define drumMod 3
#define drumSecond 4
#define noiseVol 5
#define noiseDecay 6

//---------------

#define volumen 0
#define panoramica 1
#define revAmount 2
#define filterFreq 3 
#define resonancia 4
#define filterType 5 

//channel mixer
//reverb settings
#define revSize 0
#define revDamp 1
#define revL 2
#define revR 3
/*
  Mix(1, 2, 3):
  -vol
  -pan
  -reverb
  Mod:
*/
// P U S H  B U T T O N S - en principio no estoy usando los 'bool' excepto en el encoder

// pasa por lo modos de edicion
#define playStopButt 0
#define tempoButt 0
#define homeButt 1
#define settingsButt 1
#define lengthButt 2
#define divButt 2
#define pattButt 3
#define saveAllButt 3  // esto deberia ser save project?
#define playModeButt 4
#define masterTrackButt 4
#define fillButt 5
#define euclButt 5
#define muteButt 6
// mute all
#define provButt 7
// prob roll
#define invButt 8 // tambien invert all
// inv all
#define rollButt 9
#define midiButt 9
#define rotateButt 10 //tambien rotate all
//rotate all
#define valButt 11
#define modButt 11
#define trippButt 12 //tresillos
#define swingButt 12
#define audioButt 13 // drumSynth
#define shiftButt 14
#define zoomButt 15
#define brightButt 15
// botones de funcion del grid:
// D R U M   S Y T N H - linea 7
#define drumSynthVol 49
#define drumSynthDecay 50
#define drumSynthFreq 51
#define drumSynthMod 52
#define drumSynthSecond 53
#define noiseSynthVol 54
#define noiseSynthDecay 55
// M I X E R - ultima linea
#define mixerVol 57
#define mixerPan 58
#define mixerReverbSend 59
#define mixerFilterFreq 60
#define mixerFilterRes 61
#define mixerFilterType 62

bool shiftButt_; // para las egundas funsiones de cada boton, tambien como track select?
bool zoom; //estado del zoom
byte zoomTrack;
// ENTRADAS DE CV Y MATRIZ DE MODULACIÓN:
const byte cvIn[4] = { 22 , 22 , 22 , 22 };// 22, 23, 24, 25
int cvRead[4]; //lectura de CV
int8_t modMatrix[3][8][7]; // cuatro canales de CV, de -100 a 100%
byte modSource[3][8][7]; // [menuIndex][actTrack][menuParam]
long rndNumber = random(16);

tukra.ino :
Code:
//
// T U K R A trigger sequencer
//
//     Tesseract Modular
//
//      Mangu Díaz 2020

// unsigned const int * samples[] = {AudioSampleSnare, AudioSampleHihat};

#include "ini.h"

void setup()
{

#ifdef sonido
  AudioMemory(60); //120
  for (i = 0; i < 8; i++)
  {
    envelope[i]->attack(0.0);
    envelope[i]->hold(0.0);
    envelope[i]->sustain(0.0);
    envelope[i]->release(0.0);
    envelope[i]->releaseNoteOn(0.0);
  }
  noise1.amplitude(0.5);

  SD.begin(BUILTIN_SDCARD);
#endif

#ifdef midi_
  MIDI.begin();
#endif

#ifdef midiSync
  MIDI.setHandleRealTimeSystem(RealTimeSystem);
#endif

#ifdef debug
  while (!Serial)
    ;
  Serial.print("T U K R A  Firmware Debug Mode\n");
  Serial.print("ledRef_ : ");
  Serial.print(ledRef_);
  Serial.print("\n");
  intTempo = false;
#endif
  for (i = 0; i < 8; i++)
  {
    pinMode(triggOut[i], OUTPUT);
  }

  // setting pins for buttons etc:
  pinMode(triggIn, INPUT);
  pinMode(encClick, INPUT_PULLUP);
  pinMode(resetInput, INPUT);

  //  multiplexer:
  pinMode(row0, OUTPUT);
  pinMode(row1, OUTPUT);
  pinMode(row2, OUTPUT);
  pinMode(col0, OUTPUT);
  pinMode(col1, OUTPUT);
  pinMode(col2, OUTPUT);
  pinMode(col3, OUTPUT);
  pinMode(pad, INPUT_PULLUP);

  // ENTRADAS DE CV
  analogReadResolution(10);
  pinMode(cvIn[0], INPUT); //empezamos jugando solo con una entrada
  //pinMode(cvIn[1], INPUT);
  //pinMode(cvIn[2], INPUT);
  //pinMode(cvIn[3], INPUT);

  // LEDS:
  leds.setBrightness(BRIGHTNESS);
  leds.begin();

  //inicializar el projecto
  for (i = 0; i < 64; i++)
  {
    actPatt = i;
    initPatt();
  }
  actPatt = 0;
  part[64][0] = 0;
  loopSize[64] = 0;
  loadAll();
  // readPreset();

#include "bootSeq.h"
}

void loop()
{
#ifdef midiSync
  MIDI.read();
#endif
  // byte menuParam;
  ledRef++;

  // M A T R I X   B U T T O N   R E A D:
  if ((ledRef % ledRefMini) == 0)
  {
    butt[x][y] = digitalRead(pad);
    if (y == 9)
    {
      x++;
      if (x > 7)
      {
        x = 0;
      }
      uint8_t r0 = bitRead(x, 0);
      uint8_t r1 = bitRead(x, 1);
      uint8_t r2 = bitRead(x, 2);
      digitalWrite(row0, r0);
      digitalWrite(row1, r1);
      digitalWrite(row2, r2);
    }
    y++;
    if (y > 9)
    {
      y = 0;
    }
    uint8_t c0 = bitRead(y, 0);
    uint8_t c1 = bitRead(y, 1);
    uint8_t c2 = bitRead(y, 2);
    uint8_t c3 = bitRead(y, 3);
    digitalWrite(col0, c0);
    digitalWrite(col1, c1);
    digitalWrite(col2, c2);
    digitalWrite(col3, c3);

    // G R I D   8 X 8

    if (y < 8)
    {
      //reseteamos los valores para empezar
      uint32_t redVal = 0;
      uint32_t blueVal = 0;
      uint32_t greenVal = 0;
      byte x_;
      byte y_;
      suma = (x * 8) + y;
      if (butt[x][y] != _butt[x][y])
      {
        if (butt[x][y] == LOW)
        {
          multButt_++;
          if ((shiftButt_) && (y == 0) && (editMode != 15)) // selección de pista
          {
            actRow = x;
            zoomTrack = x; 
          }
          else
          {
            butt_[x][y] = true;
            if ((!menuVertical) && (editMode != 14)) // hay conflicto aqui con el menu horizontal y el menu modulacion
            {
              encPos = suma;
              actRow = x;
              actPos();
            }
          }
        }
        else
        {
          multButt_--;
          /*
            #ifdef debug
            Serial.print("botones pulsados..."); Serial.print(multButt_); Serial.print("\n");
            #endif
          */
        }
        _butt[x][y] = butt[x][y];
      }
      if (menuVertical) // MENU VERTICAL
      {
        if ((y > 0) && ((menuVar[menuIndex][actRow][y - 1] / 128) >= (7 - x)))
        {
          redVal = (7 - x) * 10;
          greenVal = 70 - ((7 - x) * 10);
        }
        if (((y == menuParam + 1) && (x == 7))) // esto nos indica el parametro seleccionado
        {
          blueVal = 30;
        }
        if ((y == 0) && (x == actRow)) // selección de pista
        {
          blueVal = 100;
        }
        if (butt_[x][y] == true)
        {
          if (y == 0)
          {
            actRow = x;
          }
          else
          {
            menuVar[menuIndex][actRow][y - 1] = (128 * (7 - x));
            menuParam = y - 1;
          }
        }
      }

      // nuevo Menu Horizontal 
      // falta por implementar el encoder!
      else if (menu2)
      {
        if ((menuVar[menuIndex][x][menuParam] / 128) >= y)
        {
          redVal = y * 10;
          blueVal = 70 - (y * 10);
        }
        if (butt_[x][y] == true)
        {
          menuVar[menuIndex][x][menuParam] = (128 * y);
        }
      }
      else // si no hay menu horizontal o vertical procesamos todo lo demas,
      {
        if (zoom == false)
        {
          page[x] = (pos[x] / 8);
          y_ = (y + (page[x] * 8));
          x_ = x;
        }
        else
        {
          x_ = zoomTrack;
          y_ = (suma);
        }

        if (bitRead(dot[actPatt][x_], y_) == true) // hay un golpe ahi
        {
          blueVal = 50;
        }

        if ((pos[x_] == y_)) // posicion de reproduccion
        {
          redVal = 50; 
        }

        if (encPos == suma) //posicion del encoder
        {
          greenVal = 50;
          if (encButt_ == true) // E N C O D E R .  B U T T O N
          {
            switch (editMode)
            {
            case 0:
              bitWrite64(dot[actPatt][x_], y_, !bitRead(dot[actPatt][x_], y_));
              bitSet64(noInitPatt, actPatt); // el patron tiene contenido
              break;
            case 5: //roll
              if (bitRead(dot[actPatt][x_], y_) == true)
              {
                if (bitRead(roll[actPatt][x_], y_) == true)
                {
                  bitClear64(roll[actPatt][x_], y_);
                }
                else
                {
                  bitSet64(roll[actPatt][x_], y_);
                }
              }
              break;
            default:
              edit = !edit;
              if (edit == false)
              {
                actPos();
              }
              break;
            }
            encButt_ = false;
          }
        }

        switch (editMode) // B U T T O N   G R I D   M O D E
        {
        case 0: // H O M E
          if (butt_[x][y] == true)
          {
            if (shiftButt_)
            {
              switch (suma)
              {
              case 1: // R E C   M O D E
                liveRecord_ = !liveRecord_;
                break;
              case 2: // P A R T   E D I T O R
                editMode = 19;
                break;
              case 9: // S H U T T L E
                shuttle = true;
                break;
              case 17: // INVERTIR DIRECCIÓN
                invertDir();
                break;
              case 18: // I N I T   P A R T
                initPatt();
                break;
              case 10: // part grid
                editMode = 20;
                break;
              case 25: // S E L E C T   R A W
                editMode = 23;
                edit = true;
                break;
              //----------MENU HORIZONTAL-----------
              case mixerVol: // MIXER -> VOLUMEN
                menu2 = true;
                edit = true;
                menuIndex = 1;
                menuParam = 0;
                break;
              case mixerPan: // MIXER -> PAN
                menu2 = true;
                edit = true;
                menuIndex = 1;
                menuParam = 1;
                break;
              case mixerReverbSend: // MIXER -> REVERB SEND
                menu2 = true;
                edit = true;
                menuIndex = 1;
                menuParam = 2;
                break;
              case mixerFilterFreq: // MIXER -> FILTER CUTOFF
                menu2 = true;
                edit = true;
                menuIndex = 1;
                menuParam = 3;
                break;
              case mixerFilterRes: // MIXER -> FILTER RES
                menu2 = true;
                edit = true;
                menuIndex = 1;
                menuParam = 4;
                break;
              case mixerFilterType: // MIXER -> FILTER TYPE
                menu2 = true;
                edit = true;
                menuIndex = 1;
                menuParam = 5;
                break;
              case drumSynthVol: // DRUM -> VOLUMEN
                menu2 = true;
                edit = true;
                menuIndex = 0;
                menuParam = 0;
                break;
              case drumSynthDecay: // DRUM -> DECAY
                menu2 = true;
                edit = true;
                menuIndex = 0;
                menuParam = 1;
                break;
              case drumSynthFreq: // DRUM -> PITCH
                menu2 = true;
                edit = true;
                menuIndex = 0;
                menuParam = 2;
                break;
              case drumSynthMod: // DRUM -> PITCH ENV
                menu2 = true;
                edit = true;
                menuIndex = 0;
                menuParam = 3;
                break;
              case drumSynthSecond: // DRUM -> 5º
                menu2 = true;
                edit = true;
                menuIndex = 0;
                menuParam = 4;
                break;
              case noiseSynthVol: // NOISE -> VOL
                menu2 = true;
                edit = true;
                menuIndex = 0;
                menuParam = 5;
                break;
              case noiseSynthDecay: // NOISE -> DECAY
                menu2 = true;
                edit = true;
                menuIndex = 0;
                menuParam = 6;
                break;
              }
            }
            else if (liveRecord_)
            {
              liveRecord();
            }
            else
            {
              bitWrite64(dot[actPatt][x_], y_, !bitRead(dot[actPatt][x_], y_));
              bitSet64(noInitPatt, actPatt);
            }
          }
          break;
        case 1: // S T E P S
          if (steps[actPatt][x_] >= (zoom * x * 8) + y)
          {
            blueVal += 10;
          }
          if (butt_[x][y] == true)
          {
            steps[actPatt][x_] = y_;
          }
          break;
        case 2: // P L A Y   M O D E
          break;
        case 3: // div, step repeat
          if (clockDiv[actPatt][x_] >= y)
          {
            greenVal += 10;
          }
          if (butt_[x][y] == true)
          {
            clockDiv[actPatt][x_] = y_;
          }
          break;
        case 4: // prob
          if (bitRead(dot[actPatt][x_], y_) == true)
          {
            redVal = (225 / (prob[actPatt][x_][y_] + 1));
            blueVal = (prob[actPatt][x_][y_] * 14);
          }
          break;
        case 5: // roll
          if (bitRead(roll[actPatt][x_], y_) == true)
          {
            redVal += 40;
          }
          if (butt_[x][y] == true)
          {
            bitWrite64(roll[actPatt][x_], y_, !bitRead(roll[actPatt][x_], y_));
          }
          break;
        case 10: // VAL,
                 //quitamos el condicional ...solo sirve para jugar con la modulacion cruzada!
          //if  ( bitRead(dot[actPatt][x_], y_) == true ) // hay un golpe ahi
          //{
          redVal = val[actPatt][x_][y_] / 2;
          blueVal = 127 - val[actPatt][x_][y_];
          //}
          if (butt_[x][y] == true)
          {
            multButt = suma;
            if (multButt > multButtMax)
            {
              multButtMax = multButt;
            }
            if (multButt < multButtMin)
            {
              multButtMin = multButt;
            }
          }
          else
          {
            if ((multButt != -1) && (multButt_ == 0)) // esto nos indica que nigun boton esta pulsado pero no se ha reseteado
            {
              // reset:
              multButtMin = 63;
              multButtMax = 0;
              multButt = -1;
            }
          }
          break;
        case 14:     // M O D   M A T R I X
          if (y > 0) // grid de puntos de modulacion, offset 'y-1'
          {
            if (modMatrix[menuIndex][x][y - 1] > 0) // colores para modulación positiva
            {
              redVal = 30 + (modMatrix[menuIndex][x][y - 1] * 2);
              greenVal = (100 - modMatrix[menuIndex][x][y - 1]) / 2;
            }
            else if (modMatrix[menuIndex][x][y - 1] < 0) // modulación negativa
            {
              blueVal = 30 + (modMatrix[menuIndex][x][y - 1] * -2);
              redVal = (100 + modMatrix[menuIndex][x][y - 1]) / 2;
              greenVal = 0;
            }
            else
            {
              redVal = 0;
              blueVal = 0;
            }

            if (butt_[x][y] == true)
            {
              encPos = suma;
              edit = true;
              actRow = x;
              actCol = y;
            }
          }
          else // primera columna, seleccion de CV del parametro seleccionado
          {
            redVal = 0;
            blueVal = 0;
            greenVal = 0;
            if (x == modSource[menuIndex][actRow][actCol - 1])
            {
              greenVal = 100;
            }
            if (menuIndex == x - 6)
            {
              blueVal = 100;
            }
            if (butt_[x][y] == true)
            {
              if (x < 5) // seleccion del cv de 1 a 4, luego ya veremos
              {
                modSource[menuIndex][actRow][actCol - 1] = x;
              }
              // seleccion del menuIndex, de momento 0 o 1 (drumSynth y audio)
              else if (x > 5)
              {
                menuIndex = x - 6;
              }
            }
          }
          break;
        case 15: // P A T T E R N
          redVal = 0;
          blueVal = 0;
          if (actPatt == suma) // patron en reproduccion
          {
            greenVal = 50;
          }
          else if ((suma >= part[64][0]) && (suma <= part[64][loopSize[64]])) // rango de patrones del loop
          {
            redVal = 100;
          }
          else // fuera del loop
          {
            blueVal = 20;
            redVal = bitRead(noInitPatt, suma) * 50;
          }
          if (butt_[x][y] == true)
          {
            multButt = suma;
            if (multButt > multButtMax)
            {
              multButtMax = multButt;
            }
            if (multButt < multButtMin)
            {
              multButtMin = multButt;
            }
            if (shiftButt_ == true)
            {
              copyPatt();
              actPatt = suma;
            }
          }
          else
          {
            if ((multButt != -1) && (multButt_ == 0)) // esto nos indica que nigun boton esta pulsado en este ciclo pero que hay un loop por procesar!
            {
              makeLoop();
              // reset:
              multButtMin = 63;
              multButtMax = 0;
              multButt = -1;
            }
          }
          break;
        case 18: // invert sequence
          if (butt_[x][y] == true)
          {
            invertSeq(actRow);
          }
          break;
        case 17: // M U T E
          if (mute[x_] == true)
          {
            redVal += 15;
            greenVal += 10;
          }
          if (butt_[x][y] == true)
          {
            mute[actRow] = !mute[actRow];
          }
          break;
        case 19: // p a t t e r n
          if (suma > loopSize[actPart])
          {
            redVal = 0;
            greenVal = 0;
            blueVal = 0;
          }
          else
          {
            redVal = part[actPart][suma] * 2;
            greenVal = (part[actPart][suma] % 3) * 30;
            blueVal = 63 - (part[actPart][suma]);
          }
          if (butt_[x][y] == true)
          {
            if (shiftButt_ == true)
            {
              //hacer algo
              part[actPart][suma] = actPatt;
            }
            else
            {
              //abrir o guardar parte
              actPatt = part[actPart][suma];
            }
          }
          break;
        case 20: // P A T T E R N   G R I D
          blueVal = 20 * bitRead(noInitPart, suma);
          if (suma == actPart)
          {
            redVal = 80;
          }
          else
          {
            redVal = 40;
            greenVal += 20;
          }
          if (butt_[x][y] == true)
          {
            if (shiftButt_ == true)
            {
              //guardar la parte actual en ese slot
              for (i = 0; i < 64; i++)
              {
                part[suma][i] = part[actPart][i];
              }
              loopSize[suma] = loopSize[actPart];
              bitSet64(noInitPart, suma);
            }
            else
            {
              //abrir parte
              actPart = suma;
            }
          }
          break;
        } // end of GRID
      }   // final del condicional 'no horizontal menu'
      leds.setPixelColor((suma), leds.Color(redVal, greenVal, blueVal));
      butt_[x][y] = false;
    } // fin de si y < 8

    if (y > 7) // F U N C T I O N  B U T T O N S
    {
      byte buttCount = (x * 2) + y - 8;
      if (butt[x][y] != _butt[x][y])
      {
        if (butt[x][y] == LOW)
        {
          if (shiftButt_)
          {
            switch (buttCount) // f u n c i o n e s   s e c u n d a r i a s
            {
            case euclButt:
              if (editMode == 7)
              {
                editMode = lastEditMode;
                lastEditMode = 7;
              }
              else
              {
                lastEditMode = editMode;
                editMode = 7;
              }
              break;
            case brightButt: // edit BRIGHTNESS
              editMode = 12;
              edit = true;
              break;
            case tempoButt: // edit tempo
              editMode = 8;
              edit = true;
              break;
            case modButt: // mod settings
              editMode = 14;
              edit = false;
              menuVertical = false;
              leds.setPixelColor(64, leds.Color((1023 - cvRead[0]) / 10, cvRead[0] / 10, 0));
              leds.setPixelColor(65, leds.Color((1023 - cvRead[1]) / 10, cvRead[1] / 10, 0));
              leds.setPixelColor(66, leds.Color((1023 - cvRead[2]) / 10, cvRead[2] / 10, 0));
              leds.setPixelColor(67, leds.Color((1023 - cvRead[3]) / 10, cvRead[3] / 10, 0));
              break;
            case midiButt:
              editMode = 9;
              break;
            case swingButt: // swing control
              editMode = 13;
              edit = true;
              break;
            case divButt:
              editMode = 3;
              edit = true;
              menuVertical = false;
              break;
            case saveAllButt: // save project
              saveAll();
              break;
            case masterTrackButt: // el track 1 resetea a los demas
              bitWrite64(masterTrack, actPatt, !bitRead(masterTrack, actPatt));
              break;
            case audioButt: // M I X E R settings
              lastEditMode = editMode;
              // editMode = 11; 16 ??
              menuIndex = 1; // corresponde all array de audio
              menuVertical = true;
              break;
            }
          }
          else
          {
            switch (buttCount)
            {
            case homeButt: // H O M E
              if ((editMode == 0) && (zoom == true))
              {
                zoom = false;
              }
              else
              {
                lastEditMode = editMode;
                editMode = 0;
                menuVertical = false;
                menu2 = false;
                edit = false; //igual esta la tenemos q sacar de aqui
              }
              break;
            case playStopButt:
              intTempo = !intTempo;
              if (intTempo)
              {
                temp_ = 15000000 / tempo;
                t32 = temp_ / 2;
                swingCalculator();
                resetSeq();
                writeTrigg();
                temp = 0;
                beatCount = 1;
              }
              break;
            case zoomButt:
              zoom = !zoom;
              if (zoom == true)
              {
                zoomTrack = actRow;
              }
              break;
            case lengthButt:
              if (editMode == 1)
              {
                editMode = lastEditMode;
                lastEditMode = 1;
              }
              else
              {
                lastEditMode = editMode;
                editMode = 1;
                edit = true;
                menuVertical = false;
              }
              break;
            case playModeButt:
              if (editMode == 2)
              {
                editMode = lastEditMode;
                lastEditMode = 2;
              }
              else
              {
                lastEditMode = editMode;
                editMode = 2;
              }
              break;
            case provButt:
              if (editMode == 4)
              {
                editMode = lastEditMode;
                lastEditMode = 4;
              }
              else
              {
                lastEditMode = editMode;
                editMode = 4;
              }
              break;
            case rollButt:
              if (editMode == 5)
              {
                editMode = lastEditMode;
                lastEditMode = 5;
              }
              else
              {
                lastEditMode = editMode;
                editMode = 5;
              }
              break;
            case valButt: // val edit
              if (editMode == 10)
              {
                editMode = lastEditMode;
                lastEditMode = 10;
              }
              else
              {
                lastEditMode = editMode;
                editMode = 10;
              }
              break;
            case rotateButt:
              if (editMode == 6)
              {
                editMode = lastEditMode;
                lastEditMode = 6;
              }
              else
              {
                lastEditMode = editMode;
                editMode = 6;
              }
              break;
            case audioButt: // drum settings
              if (editMode == 16)
              {
                editMode = lastEditMode;
                lastEditMode = 16;
                menuVertical = false;
              }
              else
              {
                lastEditMode = editMode;
                editMode = 16;
                menuIndex = 0; // corresponde all array de DRUM SETTING
                menuVertical = true;
              }
              break;
            case shiftButt: // trackSelect
              shiftButt_ = true;
              break;
            case muteButt: // M U T E
              if (editMode == 17)
              {
                editMode = lastEditMode;
                lastEditMode = 17;
              }
              else
              {
                lastEditMode = editMode;
                editMode = 17;
                menuVertical = false;
                //edit = true;
              }
              break;
            case pattButt:
              if (editMode == 15)
              {
                editMode = lastEditMode;
                lastEditMode = 15;
              }
              else
              {
                lastEditMode = editMode;
                editMode = 15;
                menuVertical = false;
                edit = false;
                encPos = actPatt;
                actPos();
              }
              break;
            case invButt: // invertir los golpes
              if (editMode == 18)
              {
                editMode = lastEditMode;
                lastEditMode = 18;
              }
              else
              {
                lastEditMode = editMode;
                editMode = 18;
              }
              break;
            }
          }
        }
        else if (buttCount == shiftButt) // release
        {
          shiftButt_ = false;
        }
        _butt[x][y] = butt[x][y];
      }
    } // fin si x> 8
  }   // fin ledref 
  if (ledRef == ledRef_)
  {
    cvRead[0] = 1023 - analogRead(cvIn[0]);
    cvRead[1] = 1023 - analogRead(cvIn[1]);
    cvRead[2] = 1023 - analogRead(cvIn[2]);
    cvRead[3] = 1023 - analogRead(cvIn[3]);
    // tolerancia:
    // cvRead[cvSelect] = constrain( cvRead[cvSelect], 0, 1023 );
    // borrar esto luego:
    leds.setPixelColor(64, leds.Color(cvRead[0] / 5, cvRead[0] / 10, 0));
    leds.setPixelColor(65, leds.Color(cvRead[1] / 5, cvRead[1] / 10, 0));
    leds.setPixelColor(66, leds.Color(cvRead[2] / 5, cvRead[2] / 10, 0));
    leds.setPixelColor(67, leds.Color(cvRead[3] / 5, cvRead[3] / 10, 0));

#include "enc.h"
    // lectura de encoder click:
    encButt = digitalRead(encClick);
    if (encButt != _encButt)
    {
      if (encButt == LOW)
      {
        encButt_ = true;
      }
      _encButt = encButt;
    }
    if (edit == true) // avisador del encoder!!
    {
      leds.setPixelColor(71, leds.Color(100 - enc, enc, 0));
    }
    else
    {
      leds.setPixelColor(71, leds.Color(0, 0, 0));
    }
    leds.show();
    ledRef = 0;
  }

  // escritura de los pulsos etc:
  // R E L O G   I N T E R N O
  if (intTempo == true)
  {
    if ((((beatCount == 0) || (beatCount == 2)) && (temp >= (temp_ - swingTemp_))) || (temp >= (temp_ + swingTemp_)))
    {
      temp = 0;
      t32_ = true;
      if ((beatCount == 0) || (beatCount == 2))
      {
        t32 = (temp_ + swingTemp_) / 2; // remainder 0
      }
      else
      {
        t32 = (temp_ - swingTemp_) / 2; // remainder 1 de 'beatCount % 2'
      }
      go();
      writeTrigg();
      beatCount++;
      rndNumber = random(16);
      if (beatCount > 3)
      {
        beatCount = 0;
      }
    }
  }
  else // el tempo no es interno, se lee la entrada del relog
  {
    // R E S E T
    resetIn = digitalRead(resetInput);
    if (_resetIn != resetIn)
    {
      if (resetIn == LOW) //podriamos poner un condicional!
      {
        //resetSeq();
        resetSeq_ = true;
      }
      _resetIn = resetIn;
    }

    trigg = digitalRead(triggIn);
    if (trigg != _trigg)
    {
      if (trigg == LOW)
      {
        temp_ = temp;
        if (temp_ > 1500000)
        {
          temp_ = 1500000;
        }
        if (temp_ < 60000)
        {
          temp_ = 60000;
        }
        swingCalculator();
        if ((beatCount == 0) || (beatCount == 2))
        {
#ifdef debug
          //yy = temp / xx;
          //zz = AudioMemoryUsageMax();
          //Serial.print("\n temp..."); Serial.print(temp); Serial.print(" - temp_..."); Serial.print(temp_);
          // Serial.print(" ciclos..."); Serial.print(xx);
          // Serial.print(" promedio(microsegundos)..."); Serial.print(yy); Serial.print(" audio memory "); Serial.print(zz);
          //xx = 0;
#endif
          temp = 0;
          t32_ = true;
          t32 = (temp_ + swingTemp_) / 2;
          go();
          writeTrigg();
          beatCount++;
          rndNumber = random(16);
          if (beatCount > 3)
          {
            beatCount = 0;
          }
        }
        else // calculamos el roll del offbeat
        {
          t32 = (temp_ - swingTemp_) / 2;
        }
      }
      _trigg = trigg;
    }
    if (((beatCount == 1) || (beatCount == 3)) && (temp >= (temp_ - swingTemp_)))
    {
      temp = 0;
      t32_ = true;
      go();
      writeTrigg();
      beatCount++;
      rndNumber = random(16);
      if (beatCount > 3)
      {
        beatCount = 0;
      }
    }
  } // fin tempo externo

  if (temp >= triggLength)
  {
    clearTrigg();
  }

  if ((t32_ == true) && (temp >= t32))
  {
    rollOn();
    t32_ = false;
  }
}
void go() // avanzar en la secuencia
{
  for (i = 0; i < 8; i++)
  {
    count[i]--;
    if (count[i] < 0)
    {
      count[i] = clockDiv[actPatt][i];
      switch (pMode[actPatt][i])
      {
      case 0:
        pos[i]++;
        if (pos[i] > steps[actPatt][i])
        {
          pos[i] = 0;
        }
        break;
      case 1:
        pos[i]--;
        if (pos[i] < 0)
        {
          pos[i] = steps[actPatt][i];
        }
        break;
      case 2: // pendulum
        if (dir[i] == true)
        {
          pos[i]++;
          if (pos[i] > steps[actPatt][i])
          {
            pos[i] = steps[actPatt][i];
            dir[i] = false;
          }
        }
        else
        {
          pos[i]--;
          if (pos[i] < 0)
          {
            pos[i] = 0;
            dir[i] = true;
          }
        }
        break;
      case 3: // 4 random
        // do the random shit
        break;
      }
    }
  }

  if (((pMode[actPatt][0] == 0) && (pos[0] == 0)) || ((pMode[actPatt][0] == 1) && (pos[0] == steps[actPatt][0])))
  {
    // lectura del la cadena PARTE
    if (loopSize > 0) //loppsiz podria eliminarse
    {
      pattCounter++;
      if (pattCounter > loopSize[actPart])
      {
        pattCounter = 0;
      }
      if (pattCounter == loopSize[actPart])
      {
        nextPatt = part[actPart][0];
      }
      else
      {
        nextPatt = part[actPart][pattCounter + 1];
      }
    }
    actPatt = part[actPart][pattCounter];
    //actPatt = nextPatt; // pasamos al siguiente patron
    if (bitRead(masterTrack, actPatt) == true)
    {
      resetSeq();
    }
  }
  stepCount++; // esto es para 'ramifications'
}
void rollOn() // rehacer todo esto, de momento es solo un tipo de redoble
{

  for (i = 0; i < 8; i++)
  {
    if ((bitRead(roll[actPatt][i], (pos[i])) == true) && (bitRead(dot[actPatt][i], pos[i]) == true) && (count[i] == clockDiv[actPatt][i]) && (mute[i] == false) && (rndNumber + sculpt[i] >= prob[actPatt][i][pos[i]]))
    {
      digitalWrite(triggOut[i], HIGH);

#ifdef sonido
      drumSynth();
#endif

#ifdef midi_
      MIDI.sendNoteOn(midiNote[i], val[actPatt][i][pos[i]], 1);
#endif
    }
  }
}

void writeTrigg() 
{ // si estos parametros no vamos a automatizarlos podemos quitarlos de aqui
  // reasignar estos parametros...
  /*
    tempFloat = fscale( 0, 1023, 0.00, 1.00, menuVar[1][0][revSize], 0.3 , 0);
    freeverb1.roomsize(tempFloat);
    tempFloat = fscale( 0, 1023, 0.00, 1.00, menuVar[1][0][revDamp], 0.3 , 0);
    freeverb1.damping(tempFloat);
    tempFloat = fscale( 0, 1023, 0.00, 1.00, menuVar[1][0][revL], 0.3 , 0);
    mixer[14]->gain(3, tempFloat);
    tempFloat = fscale( 0, 1023, 0.00, 1.00, menuVar[1][0][revR], 0.3 , 0);
    mixer[15]->gain(3, tempFloat);
  */
  for (i = 0; i < 8; i++)
  {
    if ((count[i] == clockDiv[actPatt][i]) && (bitRead(dot[actPatt][i], pos[i]) == true) && (mute[i] == false) && (rndNumber + sculpt[i] >= prob[actPatt][i][pos[i]]))

    {
      digitalWrite(triggOut[i], LOW);

#ifdef midi_
      MIDI.sendNoteOn(midiNote[i], val[actPatt][i][pos[i]], 1);
#endif

#ifdef sonido
      drumSynth();
#endif
    }
  }
}

void clearTrigg()
{
  for (i = 0; i < 8; i++)
  {
    digitalWrite(triggOut[i], HIGH);
#ifdef sonido
    //   envelope1.noteOff();
#endif
  }
#ifdef midi_
   MIDI.sendControlChange(123,0,0);
  // all note off
#endif
}

void resetSeq()
{
  for (i = 0; i < 8; i++)
  {
    if (pMode[actPatt][i] == 1)
    {
      pos[i] = steps[actPatt][i];
    }
    else
    {
      pos[i] = 0;
    }
  }
  beatCount = 0;
}

void actPos()
{
  enc4 = encPos;
  _enc4 = enc4;
  enc = (enc4 * 4);
  _enc = enc;
  myEnc.write(enc);
}

void preset() // preset  write
{
  uint32_t eeAddress = 0;

  EEPROM.put(eeAddress, midiNote);
  eeAddress += sizeof(midiNote);
  EEPROM.put(eeAddress, menuVar);
  eeAddress += sizeof(menuVar);
  EEPROM.put(eeAddress, modMatrix);
  eeAddress += sizeof(modMatrix);
  EEPROM.put(eeAddress, modSource);
  eeAddress += sizeof(modSource);
}

void readPreset()
{
  uint32_t eeAddress = 0;
  EEPROM.get(eeAddress, midiNote);
  eeAddress += sizeof(midiNote);
  EEPROM.get(eeAddress, menuVar);
  eeAddress += sizeof(menuVar);
  EEPROM.get(eeAddress, modMatrix);
  eeAddress += sizeof(modMatrix);
  EEPROM.get(eeAddress, modSource);
  eeAddress += sizeof(modSource);
}

void euclFunct(int track) // cuclidean caculator
{
  int bucket = 0;
  for (i = 0; i <= steps[actPatt][track]; i++)
  {
    bucket += euclNum[actPatt][track];
    if (bucket > steps[actPatt][track])
    {
      bucket -= (steps[actPatt][track] + 1);
      bitSet64(dot[actPatt][track], i);
    }
    else
    {
      bitClear64(dot[actPatt][track], i);
    }
  }
  rotateSeq(track);
}
// atencion, la rotacion no esta afectando a los otros parametros! pero esto podría implementarse facilmente
// pero tendríamos que declarar funciones diferentes  para no rotar la secuencia cada vez que hagamos un euclidiano!
// void rotateAll rotateAllNeg
void rotateSeq(uint32_t track) //  rotate a sequence
{
  bool h = bitRead(dot[actPatt][track], steps[actPatt][track]);
  for (i = steps[actPatt][track]; i > 0; i--)
  {
    bitWrite64(dot[actPatt][track], i, bitRead(dot[actPatt][track], i - 1));
  }
  bitWrite64(dot[actPatt][track], 0, h);
}

void rotateSeqNeg(uint32_t track)
{
  bool h = bitRead(dot[actPatt][track], 0);
  for (i = 0; i < steps[actPatt][track]; i++)
  {
    bitWrite64(dot[actPatt][track], i, bitRead(dot[actPatt][track], i + 1));
  }
  bitWrite64(dot[actPatt][track], steps[actPatt][track], h);
}

void liveRecord()
{
  // para cuatizar los golpes,
  if (temp < t32)
  {
    digitalWrite(triggOut[x], LOW);
#ifdef midi_
    MIDI.sendNoteOn(midiNote[x], (y * 16), 1);
#endif
#ifdef sonido
    i = x;
    drumSynth();
#endif
    bitWrite64(dot[actPatt][x], pos[x], recMode);
    if (recModulation)
    {
      val[actPatt][x][pos[x]] = (y * 16) - 1; // en caso de que el rango sea 0-127
    }
  }
  else
  {
    bitWrite64(dot[actPatt][x], pos[x] + 1, recMode);
    if (recModulation)
    {
      val[actPatt][x][pos[x] + 1] = (y * 16) - 1; // en caso de que el rango sea 0-127
    }
  }
  // escribimos o borramos el golpe, recMode 'insert'(true) o 'clear'(false)
}

float fscale(float originalMin, float originalMax, float newBegin, float newEnd, float inputValue, float curve)
{
  //version recortada, la curva debe ser de -1.0 a 1.0 y los valores sin invertir
  float OriginalRange = 0;
  float NewRange = 0;
  float zeroRefCurVal = 0;
  float normalizedCurVal = 0;
  float rangedValue = 0;
  curve = pow(10, curve); // convert linear scale into lograthimic exponent for other pow function
  OriginalRange = originalMax - originalMin;
  NewRange = newEnd - newBegin;
  zeroRefCurVal = inputValue - originalMin;
  normalizedCurVal = zeroRefCurVal / OriginalRange; // normalize to 0 - 1 float
  rangedValue = (pow(normalizedCurVal, curve) * NewRange) + newBegin;
  return rangedValue;
}
void swingCalculator()
{
  swingTemp_ = (swingAmount * (temp_ / 3) / 100);
}

void saveAll()
{
  /*
    if (!SD.exists("1"))
    {
    //crear directorio
    SD.mkdir("1");
    }
    else
    {
    //borrar archivos antes de escribir los nuevos
    }
*/
  File dataFile;
  SD.remove("val.dat");
  dataFile = SD.open("val.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&val, sizeof(val));
  dataFile.close();

  SD.remove("prob.dat");
  dataFile = SD.open("prob.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&prob, sizeof(prob));
  dataFile.close();

  SD.remove("steps.dat");
  dataFile = SD.open("steps.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&steps, sizeof(steps));
  dataFile.close();

  SD.remove("pMode.dat");
  dataFile = SD.open("pMode.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&pMode, sizeof(pMode));
  dataFile.close();

  SD.remove("clockDiv.dat");
  dataFile = SD.open("clockDiv.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&clockDiv, sizeof(clockDiv));
  dataFile.close();

  SD.remove("euclNum.dat");
  dataFile = SD.open("euclNum.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&euclNum, sizeof(euclNum));
  dataFile.close();

  SD.remove("dot.dat");
  dataFile = SD.open("dot.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&dot, sizeof(dot));
  dataFile.close();

  SD.remove("roll.dat");
  dataFile = SD.open("roll.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&roll, sizeof(roll));
  dataFile.close();

  SD.remove("midiNote.dat");
  dataFile = SD.open("midiNote.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&midiNote, sizeof(midiNote));
  dataFile.close();

  SD.remove("menuVar.dat");
  dataFile = SD.open("menuVar.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&menuVar, sizeof(menuVar));
  dataFile.close();

  SD.remove("noInitPatt.dat");
  dataFile = SD.open("noInitPatt.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&noInitPatt, sizeof(noInitPatt));
  dataFile.close();

  SD.remove("raw.dat");
  dataFile = SD.open("raw.dat", FILE_WRITE);
  dataFile.write((uint8_t *)&raw, sizeof(raw));
  dataFile.close();
}
void loadAll()
{
  File dataFile;
  dataFile = SD.open("val.dat", FILE_READ);
  dataFile.read((uint8_t *)&val, sizeof(val));
  dataFile = SD.open("prob.dat", FILE_READ);
  dataFile.read((uint8_t *)&prob, sizeof(prob));
  dataFile = SD.open("steps.dat", FILE_READ);
  dataFile.read((uint8_t *)&steps, sizeof(steps));
  dataFile = SD.open("pMode.dat", FILE_READ);
  dataFile.read((uint8_t *)&pMode, sizeof(pMode));
  dataFile = SD.open("clockDiv.dat", FILE_READ);
  dataFile.read((uint8_t *)&clockDiv, sizeof(clockDiv));
  dataFile = SD.open("euclNum.dat", FILE_READ);
  dataFile.read((uint8_t *)&euclNum, sizeof(euclNum));
  dataFile = SD.open("dot.dat", FILE_READ);
  dataFile.read((uint8_t *)&dot, sizeof(dot));
  dataFile = SD.open("roll.dat", FILE_READ);
  dataFile.read((uint8_t *)&roll, sizeof(roll));
  dataFile = SD.open("midiNote.dat", FILE_READ);
  dataFile.read((uint8_t *)&midiNote, sizeof(midiNote));
  dataFile = SD.open("menuVar.dat", FILE_READ);
  dataFile.read((uint8_t *)&menuVar, sizeof(menuVar));
  dataFile = SD.open("noInitPatt.dat", FILE_READ);
  dataFile.read((uint8_t *)&noInitPatt, sizeof(noInitPatt));
  dataFile = SD.open("raw.dat", FILE_READ);
  dataFile.read((uint8_t *)&raw, sizeof(raw));

  dataFile.close();
}
void initPatt()
{
  // inicializacion de las variables, patron inicial
  byte a;
  byte b;
  for (a = 0; a < 8; a++)
  {
    steps[actPatt][a] = 7;
    pMode[actPatt][a] = 0;
    clockDiv[actPatt][a] = 0;
    euclNum[actPatt][a] = 0;
    midiNote[a] = a * 2;
    roll[actPatt][a] = 0;
    dot[actPatt][a] = 0;
    // default settings for filter & volume
    menuVar[0][a][drumMod] = 512;
    menuVar[1][a][filterFreq] = 896;
    menuVar[1][a][volumen] = 896;
    menuVar[1][a][panoramica] = 512;
    for (b = 0; b <= maxSteps; b++)
    {
      prob[actPatt][a][b] = 0;
      val[actPatt][a][b] = 120;
    }
  }
}

void invertSeq(byte track)
{
  dot[actPatt][track] = ~dot[actPatt][track];
}

void copyPatt()
{
  for (i = 0; i < 8; i++)
  {
    dot[suma][i] = dot[actPatt][i];
    roll[suma][i] = roll[actPatt][i];
    steps[suma][i] = steps[actPatt][i];
    pMode[suma][i] = pMode[actPatt][i];
    clockDiv[suma][i] = clockDiv[actPatt][i];
    euclNum[suma][i] = euclNum[actPatt][i];

    for (byte n = 0; n < 64; n++)
    {
      prob[suma][i][n] = prob[actPatt][i][n];
      val[suma][i][n] = val[actPatt][i][n];
    }
  }
  bitSet64(noInitPatt, suma);
}
void drumSynth() // i es el track!
{
#ifdef sonido
  // VOLUMEN Y PANORAMICA
  // calculamos este valor combinando los valores de 'volumen' y 'panorámica'

  // L:
  float tempFloat2 = (calcMod(1, i, volumen) * (constrain((1023 - calcMod(1, i, panoramica)) * 2, 0, 1023))) / 1023;
  float tempFloat = fscale(0, 1024, 0.0, 1.0, tempFloat2, 0.3);
  mixer[(8 + ((i / 4) * 2))]->gain((i % 4), tempFloat);

  // R:
  tempFloat2 = (calcMod(1, i, volumen) * (constrain(calcMod(1, i, panoramica) * 2, 0, 1023))) / 1023;
  tempFloat = fscale(0, 1024, 0.0, 1.0, tempFloat2, 0.3);
  mixer[(9 + ((i / 4) * 2))]->gain((i % 4), tempFloat);

  // R E V E R B
  //tempFloat = fscale(0, 1024, 0.0, 1.0, calcMod(1, i, revAmount), 0.3);
  //mixer[(12 + ((i / 4) * 2))]->gain((i % 4), tempFloat);

  // F I L T R O
  // resonancia:
  tempFloat2 = fscale(0, 1024, 0.7, 5.0, calcMod(1, i, resonancia), 0);

  //cutOff:
  tempFloat = fscale(0, 1024, 20, 20000, calcMod(1, i, filterFreq), 0.4);

  if (menuVar[1][i][filterType] < 333)
  {
    biquad[i]->setLowpass(0, tempFloat, tempFloat2);
  }
  else if (menuVar[1][i][filterType] > 666)
  {
    biquad[i]->setHighpass(0, tempFloat, tempFloat2);
  }
  else
  {
    biquad[i]->setBandpass(0, tempFloat, tempFloat2);
  }
  /*
    // esto es para el otro filtro, ya veremos si lo usamos!
    tempFloat = fscale(0, 1024, 20, 20000, calcMod(1, i, filterFreq), 0.4);
    filter1.frequency(tempFloat);

    tempFloat = fscale(0, 1024, 0.7, 5.0, calcMod(1, i, resonancia), 0);
    filter1.resonance(tempFloat);
*/
#ifdef DRUM
  if (playDrum[i] == true)
  {
// D R U M
#ifdef DRUMS
    tempFloat = fscale(0, 1023, 0.00, 1.00, calcMod(0, i, drumVol), 0.3);
    mixer[i]->gain(0, tempFloat);

    tempFloat = fscale(0, 1023, 40, 2000, calcMod(0, i, drumFreq), 0.4);
    drum[i]->frequency(tempFloat);

    tempFloat = fscale(0, 1023, 10, 2000, calcMod(0, i, drumDecay), 0.4);
    drum[i]->length(tempFloat);

    tempFloat = fscale(0, 1023, 0.0, 1.0, calcMod(0, i, drumMod), 0);
    drum[i]->pitchMod(tempFloat);

    tempFloat = fscale(0, 1023, 0.0, 1.0, calcMod(0, i, drumSecond), 0);
    drum[i]->secondMix(tempFloat);
#endif

    //  N O I S E
    tempFloat = fscale(0, 1023, 0.00, 0.50, calcMod(0, i, noiseVol), 0.3);
    mixer[i]->gain(1, tempFloat);

    tempFloat = fscale(0, 1023, 1, 2000, calcMod(0, i, noiseDecay), 0.4);
    envelope[i]->decay(tempFloat);

#ifdef DRUMS
    drum[i]->noteOn();
#endif
    envelope[i]->noteOn();
  }
#endif

#ifdef SAMPLER
  if (playSample[i] == true)
  {
    sample[i]->play(muestra[raw[i]].c_str());
  }
  //      sample[i].play(AudioSampleKickwavq);
#endif
#endif
}
void makeLoop()
{
  if (multButtMax != multButtMin)
  {
    loopSize[64] = multButtMax - multButtMin; // el array de partes va a tener un contador también,
    for (i = 0; i <= loopSize[64]; i++)
    {
      part[64][i] = i + multButtMin;
    }
    actPart = 64; // parte temporal
  }
  else
  {
    actPatt = multButtMax;
  }
  /*
    if (intTempo)
    {
    nextPatt = part[64][0]; //esperamos al siguiente ciclo si esta andando
    }
    else
    {
    actPatt = part[64][0];
    nextPatt = part[64][0];
    }
  */
  /*
    #ifdef debug
    Serial.print("\n actPatt..."); Serial.print(actPatt); Serial.print(" - nextPatt..."); Serial.print(nextPatt); //Serial.print("\n");
    #endif
  */
}
void invertDir()
{
  for (i = 0; i < 8; i++)
  {
    switch (pMode[actPatt][i])
    {
    case 0:
      pMode[actPatt][i] = 1;
      break;
    case 1:
      pMode[actPatt][i] = 0;
      break;
    case 2:
      dir[i] = !dir[i];
      break;
    }
  }
}
// M I D I   S Y N C H
#ifdef midiSync
/*
  void RealTimeSystem(byte realtimebyte) {
  if (realtimebyte == 248) {
    //Serial1.write(248);
    counter++;
    if (counter == 24)
    {
      counter = 0;
      // CORCHEA
    }

    if (counter == 12)
    {
      //ROLL
    }
  }

  if (realtimebyte == START || realtimebyte == CONTINUE) {
    counter = 0;
    // PLAY
  }

  if (realtimebyte == STOP) {
    // STOP
  }
  }
*/
#endif
void checkDir()
{ /*
    for (i = 0; i < 64; i++ )
    {
    // folder = String(i);
    if ( SD.exists("1") )
    {
      bitSet64(noInitProject, i);
    }
    } */
}
int calcMod(byte menuIndex2, byte track2, byte menuParam2)
{
  int result;
  int inputValue2 = menuVar[menuIndex2][track2][menuParam2];
  if (modMatrix[menuIndex2][track2][menuParam2] == 0)
  {
    result = inputValue2;
  }
  else
  {
    if (modSource[menuIndex2][track2][menuParam2] != 4)
    {
      result = constrain((inputValue2 + (cvRead[modSource[menuIndex2][track2][menuParam2]] * modMatrix[menuIndex2][track2][menuParam2] / 100)), 0, 1023);
    }
    else if (modSource[menuIndex2][track2][menuParam2] == 4) // cvSource 4 -> calculamos con VAL
    {
      result = constrain((inputValue2 + ((val[actPatt][track2][pos[track2]] * 8) * modMatrix[menuIndex2][track2][menuParam2] / 100)), 0, 1023);
    }
  }
  return result;
}

enc.h :
Code:
// E N C O D E R:

// lectura inicial de del encoder
enc = myEnc.read();
if ( enc != _enc )
{
  if ( enc > 255 )
  {
    enc = 0;
    myEnc.write(0);
  }
  else if ( enc < 0 )
  {
    enc = 255;
    myEnc.write(255);
  }
  enc4 = ( enc / 4);
  _enc = enc;
}
// valores sobre los que trabajamos ( div 4 )
if ( enc4 != _enc4 )
{
  if ( shuttle == true)
  {
    if ( enc4 > _enc4 )
    {
      go();
      writeTrigg();
    }
    else
    {
      invertDir();// invertir la dirección
      go();
      writeTrigg();
      invertDir();
    }
  }
  else if (menuVertical == true)
  {
    if ( enc4 > _enc4 )
    {
      menuVar[menuIndex][actRow][menuParam] += 10 ;
    }
    else
    {
      menuVar[menuIndex][actRow][menuParam] -= 10 ;
    }
    menuVar[menuIndex][actRow][menuParam] = constrain( menuVar[menuIndex][actRow][menuParam], 0, 1023 );
  }
  else if ((edit == false ) || ( editMode == 0) || ( editMode == 5 ))
  {
    encPos = enc4;
    if ( zoom == false )
    {
      actRow = ( encPos / 8 );
      actCol = ( encPos % 8 );
    }
    else
    {
      actRow = zoomTrack;
      actCol = encPos;
    }
  }
  else switch ( editMode )
    {
      case 1: // EDIT LENGTH, el formato podria ser distinto, ajustando la duracion con el click del encoder
        if (( enc4 > _enc4 ) && ( steps[actPatt][actRow] < maxSteps ))
        {
          steps[actPatt][actRow]++ ;
        }
        else if (( enc4 < _enc4 ) && ( steps[actPatt][actRow] > 0 ))
        {
          steps[actPatt][actRow]-- ;
        }
        break;
      case 2: // EDIT PLAY MODE
        if ( enc4 != _enc4)
        {
          if ( enc4 > _enc4 ) 
          {
            pMode[actPatt][actRow]++ ;
          }
          else
          {
            pMode[actPatt][actRow]-- ;
          }
          if ( pMode[actPatt][actRow] > 250 )
          {
            pMode[actPatt][actRow] = 2;
          }
          else if ( pMode[actPatt][actRow] > 2 )
          {
            pMode[actPatt][actRow] = 0;
          }
        }
        break;
      case 3: // EDIT STEP REPEAT
        if (( enc4 > _enc4 ) && ( clockDiv[actPatt][actRow] < 8 ))
        {
          clockDiv[actPatt][actRow]++ ;
        }
        else if (( enc4 < _enc4 ) && ( clockDiv[actPatt][actRow] > 0 ))
        {
          clockDiv[actPatt][actRow]-- ;
        }
        break;
      case 4: // P R O B A B I L I D A D - funciona al reves, menos es mas!
        if ( bitRead(dot[actPatt][actRow], actCol) == true )
        {
          if (( enc4 > _enc4 ) && ( prob[actPatt][actRow][actCol] > 0 ))
          {
            prob[actPatt][actRow][actCol]-- ;
          }
          else if (( enc4 < _enc4 ) && ( prob[actPatt][actRow][actCol] < 15 ))
          {
            prob[actPatt][actRow][actCol]++ ;
          }
        }
        break;
      case 6:
        // R O T A T E
        if ( enc4 > _enc4 )
        {
          rotateSeq(actRow);
        }
        else
        {
          rotateSeqNeg(actRow);
        }
        break;
      case 7:
        // E U C L I D E A N
        if ( enc4 > _enc4 )
        {
          if ( euclNum[actPatt][actRow] <= steps[actPatt][actRow] )
          {
            euclNum[actPatt][actRow]++ ;
          }
        }
        else if ( euclNum[actPatt][actRow] > 0 )
        {
          euclNum[actPatt][actRow]-- ;
        }
        euclFunct(actRow);
        break;
      case 8:
        // T E M P O
        if ( enc4 > _enc4 )
        {
          tempo++ ;
        }
        else
        {
          tempo-- ;
        }
        tempo = constrain(tempo, 10, 250);
        temp_ = 15000000 / tempo;
        // max   1500000
        // min     60000
        t32 = temp_ / 2;
        swingCalculator();
        break;
      case 9:  // edicion nota midi
        if ( enc4 > _enc4 )
        {
          if ( midiNote[actRow] < 127 )
          {
            midiNote[actRow] ++ ;
          }
        }
        else
        {
          if ( midiNote[actRow] > 0 )
          {
            midiNote[actRow] -- ;
          }
        }
        MIDI.sendNoteOn( midiNote[actRow], 120, 1);
        break;
      case 10: // V A L
        // edicion del valor q usaremos para velocidad midi
        if (multButt == -1) // no hay multiples bottones,
        {
          if ( enc4 > _enc4 )
          {
            if ( val[actPatt][actRow][actCol] < 127 )
            {
              val[actPatt][actRow][actCol] ++ ;
            }
          }
          else
          {
            if ( val[actPatt][actRow][actCol] > 0 )
            {
              val[actPatt][actRow][actCol] -- ;
            }
          }
        }
        else if (!zoom)// si hay pulsado! no hay zoom
        {
          byte xx;
          byte yy;
          uint8_t longitud = 127 / (1 + multButtMax - multButtMin);
          byte rampa = 0;
          if ( enc4 > _enc4 )
          {
            for (i = multButtMin; i <= multButtMax; i ++)
            {
              xx = i / 8;
              page[xx] = ( pos[xx] / 8 );
              yy = (i % 8) + (page[xx] *8);
              rampa ++;
              val[actPatt][xx][yy] = rampa * longitud;
            }
          }
          else // enc4 < _enc4
          {
            for ( i = multButtMax; i >= multButtMin; i -- )
            {
              xx = i / 8;
              page[xx] = ( pos[xx] / 8 );
              yy = (i % 8) + (page[xx] *8);
              rampa ++;
              val[actPatt][xx][yy] = rampa * longitud;
            }
          }
        }
        else // si hay zoom:
        {
          byte longitud = 1 + multButtMax - multButtMin;
          byte rampa = 0;
          if ( enc4 > _enc4 )
          {
            for (i = multButtMin; i <= multButtMax; i ++)
            {
              rampa ++;
              val[actPatt][zoomTrack][i] = rampa * (127 / longitud);
            }
          }
          else // enc4 < _enc4
          {
            for ( i = multButtMax; i >= multButtMin; i -- )
            {
              rampa ++;
              val[actPatt][zoomTrack][i] = rampa * (127 / longitud);
            }
          }
        }
        break;
      case 13: // S W I N G
        if ( enc4 > _enc4 )
        {
          if (swingAmount < 100)
          {
            swingAmount ++ ;
          }
        }
        else
        {
          if (swingAmount > 0)
          {
            swingAmount -- ;
          }
        }
        swingCalculator();
        break;
      case 12: // B R I G H T N E S S
        if ( enc4 > _enc4 )
        {
          BRIGHTNESS ++ ;
        }
        else
        {
          BRIGHTNESS -- ;
        }
        constrain(BRIGHTNESS, 5, 250);
        leds.setBrightness(BRIGHTNESS);
        break;
      case 14: // modulacion...de audio de momento
        if (actCol > 0)
        {
          if ( enc4 > _enc4 )
          {
            modMatrix[menuIndex][actRow][actCol - 1] ++ ; //  el -1 es por el offset de los parametrosmostrados
          }
          else
          {
            modMatrix[menuIndex][actRow][actCol - 1] -- ;
          }
          modMatrix[menuIndex][actRow][actCol - 1] = constrain(modMatrix[menuIndex][actRow][actCol - 1], -100, 100);
          break;
        }
      // no se x q esto no funciona
      case 17: //M U T E
        encPos -= (encPos % 8) ;
        break;
      case 23:
        if ( enc4 > _enc4 )
        {
          raw[actRow]++ ;
        }
        else
        {
          raw[actRow] -- ;
        }
        if (raw[actRow] > 63)
        {
          raw[actRow] = 0;
        }
        else if (raw[actRow] < 0)
        {
          raw[actRow] = 63;
        }
        break;
    }
  _enc4 = enc4;
}
 
Moving the global variables to local (within a function) has probably also moved the bug. It may still be clobbering memory but at the moment it is not clobbering something that is critical. It might never cause a problem again. Or, later on when you make a few changes, it'll rear its ugly head again but in a completely different way.
At the moment, I can't see any obvious places where an array index is out of bounds. But having three very similar names for arrays can lead to confusion:
Code:
bool butt[8][10];    // lectura de los pads,
bool _butt[8][10];  // comparador
bool butt_[8][8];  // estado del boton, las dos ultimas columnas no se estanb utilizando
especially when one of them has different size than the other two.

But if the code is working, just keep going and hope that it stays that way :)
If it dies, post your code again and I'll have a look at it.

Pete
 
I've moved some variables, but also reduced some of them or deleted some unused ones.
At the moment, I can't see any obvious places where an array index is out of bounds. But having three very similar names for arrays can lead to confusion:
Code:
bool butt[8][10];    // lectura de los pads,
bool _butt[8][10];  // comparador
bool butt_[8][8];  // estado del boton, las dos ultimas columnas no se estanb utilizando
especially when one of them has different size than the other two.
Pete
This part is correct, the first variable is to read the button matrix, the second to compare with the previous reading and the third to store the state. The last columns of the matrix are function buttons and that variable is not used in the code.
 
Status
Not open for further replies.
Back
Top