#include <SPI.h> //zahrnutie knižnice pre prácu s SPI
#include <Ethernet.h> //zahrnutie knižnice pre prácu s ethernetovým pripojením
#include <SD.h> //zahrnutie knižnice pre prácu s microSD kartou

/*Zahrnutie knižnice poľa o veľkosti 44100 vzoriek s hodnotami vlny v tvare sínusu. */
/* Pri vzorkovacej frekvencii 44,1kHz bude po prejdení celého poľa vzorku po vzorke */
/* výsledkom jedna perióda vlny o frekvencii 1Hz */
#include "sineTime44.h"

/*Zahrnutie knižnice poľa o veľkosti 44100 vzoriek s hodnotami vlny v tvare trojuhoľníka. */
/* Pri vzorkovacej frekvencii 44,1kHz bude po prejdení celého poľa vzorku po vzorke */
/* výsledkom jedna perióda vlny o frekvencii 1Hz*/
#include "triTime44.h"

#define REQ_BUF_SZ   60  //veľkosť vyrovnávacej pamäte používanej na zachytenie HTTP požiadavok
#define LOAD_BUF_SZ   4  //veľkosť vyrovnávacej pamäte používanej pre načítavanie zo súboru

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //MAC adresa Ethernet Shieldu

/* Prvé tri čísla adresy musia byť zhodné s IP adresou routra */
/* a posledné trojčíslie musí byť na sieti unikátne */
IPAddress ip(192, 168, 4, 123);

byte gateway[] = { 192, 168, 4, 1 }; //adresa brány
byte subnet[] = { 255, 255, 255, 0 }; //podadresa siete
EthernetServer server(80);  // vytvorenie servera na porte 80
File webFile;               // súbor na načítavanie kódu webovej stránky
File saveFile;              // súbor na ukladané dáta
File loadFile;              // súbor na načítavanie parametrov efektov
char HTTP_req[REQ_BUF_SZ] = {0}; // pole znakov na HTTP požiadavku
char loadTX[LOAD_BUF_SZ] = {0};   // pole znakov načítaných hodnôt
char req_index = 0;              // index pola znakov HTTP požiadavky
char load_index = 0;            // index pola znakov načítavania
char save = 0;                  // inicializícia indexu na ukladanie

uint16_t in_ADC0, in_ADC1;  //premenné pre dve aktuálne hodnoty AD prevodníkov (A8, A9)
uint16_t POT0, POT1, POT2; //premenné pre 3 potenciometrov (A10, A1, AD0)
uint16_t out_DAC0, out_DAC1; //premenné pre aktuálne hodnoty pre DA prevodníky (DAC0, DAC1)

/* pridelovanie čísiel digitálnych pinov */
const int EFFECTSWITCH = 6; // prepínač na volenie efektu
const int LAN = 7;          // prepínač na volenie stavu
const int WAVEFORM = 5;     // prepínač na volenie tvaru vlny
const int LED1 = 8;         // LED1
const int LED2 = 9;         // LED2

/* inicializácia počiatočných parametrov */
volatile int switch_value = 0;  // stav spínača efektu
int effect;                 // index nastaveného efektu
volatile int state_value;       // stav spínača stavu
volatile int form_state;        // stav spínača tvaru vlny

// logická hodnota nesúca informáciu o zmene hodnoty potenciometra
int p0_value, p1_value, p2_value;

#define MAX_BUFF 10000    // definícia veľkosti poľa vyrovnávacej pamäte 
uint16_t Buff0[MAX_BUFF]; //deklarácia poľa vyrovnávacej pamäte pre signál z A8
uint16_t Buff1[MAX_BUFF]; //deklarácia poľa vyrovnávacej pamäte pre signál z A9
unsigned int Counter;     // index vyrovnávacej pamäte

/* Echo */
int fb_e = 0, direct_e = 0, d_e = 0;
//miera spätnej väzby, miera priamej vetvy, oneskorenie;

/* Tremolo */
int a_t = 0, f_t = 0, help_t = 0, form_state_t = 0, i_t = 0, LFO_t = 0;
//hĺbka, rýchlosť, pomocná premenná na pomocný výpočet, tvar vlny, index LFO, pomocná hĺbka;

/* Vibrato */
int a_v = 0, f_v = 0, help_v = 0, form_state_v = 0, i_v = 0;
//hĺbka, rýchlosť, pomocná premenná na pomocný výpočet, tvar vlny, index LFO;

/* Chorus */
int a_c = 0, f_c = 0, help_c = 0, form_state_c = 0, i_c = 0, fx_c = 0, direct_c = 0;
//hĺbka, rýchlosť, pomocná premenná na pomocný výpočet,
//tvar vlny, index LFO, miera fx vetvy, miera priamej vetvy;

/* Flanger */
int a_f = 0, f_f = 0, help_f = 0, form_state_f = 0, i_f = 0, fb_f = 0, direct_f = 0;
//hĺbka, rýchlosť, pomocná premenná na pomocný výpočet,
//tvar vlny, index LFO, miera spätnej väzby, miera priamej vetvy;


void setup()
{
  /*spustenie okna hlásení, */
  /* pri pripojení pedálového multiefektu k počítaču môžeme na obrazovke sledovať hlásenia */
  Serial.begin(9600);

  //výpis hlásenia prebieha inicializácia SD karty
  Serial.println("Initializing SD card...");

  /* ak sa nepodarilo microSD kartu inicializovať, výpis hlásenia o zlyhaní */
  if (!SD.begin(4)) {
    Serial.println("ERROR - SD card initialization failed!");
    return;
  }
  /* ak sa podarilo microSD kartu inicializovať, výpis hlásenia o úspechu */
  Serial.println("SUCCESS - SD card initialized.");

  /* otvorenie textového súboru na pamäťovej karte */
  /* a načítanie uložených parametrov efektov, */
  /* pri spustení programu (alebo resete)*/
  loadFile = SD.open("mem.txt");
  // open web page file
  if (loadFile) {
    while (loadFile.available()) {
      effect = loadFile.parseInt();
      fb_e = loadFile.parseInt();
      direct_e = loadFile.parseInt();
      d_e = loadFile.parseInt();
      a_t = loadFile.parseInt();
      f_t = loadFile.parseInt();
      help_t = loadFile.parseInt();
      form_state_t = loadFile.parseInt();
      i_t = loadFile.parseInt();
      LFO_t = loadFile.parseInt();
      a_v = loadFile.parseInt();
      f_v = loadFile.parseInt();
      help_v = loadFile.parseInt();
      form_state_v = loadFile.parseInt();
      i_v = loadFile.parseInt();
      a_c = loadFile.parseInt();
      f_c = loadFile.parseInt();
      help_c = loadFile.parseInt();
      form_state_c = loadFile.parseInt();
      i_c = loadFile.parseInt();
      fx_c = loadFile.parseInt();
      direct_c = loadFile.parseInt();
      a_f = loadFile.parseInt();
      f_f = loadFile.parseInt();
      help_f = loadFile.parseInt();
      form_state_f = loadFile.parseInt();
      i_f = loadFile.parseInt();
      fb_f = loadFile.parseInt();
      direct_f = loadFile.parseInt();
      loadFile.close();
    }
  }

  Ethernet.begin(mac, ip, gateway, subnet);  // inicializácia ethernetového zariadenia
  server.begin();                             // spustenie servera

  /* zapne časovacie hodiny v riadiacom kontroléri */
  pmc_set_writeprotect(false);
  pmc_enable_periph_clk(ID_TC4);

  /* výber časovacích hodín a kanálu */
  TC_Configure(/* hodiny */TC1,/* kanál */1, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC
                           | TC_CMR_TCCLKS_TIMER_CLOCK2);
  TC_SetRC(TC1, 1, 238);  // stanovuje frekvenciu časovača na 44,1 kHz

  TC_Start(TC1, 1); //spustenie časovača

  /* aktivácia časových prerušení na časovači */
  TC1->TC_CHANNEL[1].TC_IER = TC_IER_CPCS;
  TC1->TC_CHANNEL[1].TC_IDR = ~TC_IER_CPCS;

  /* aktivácia prerušovania vo vnorenom vektorovom kontroléri prerušení*/
  NVIC_EnableIRQ(TC4_IRQn);
  /* TC4_IRQn kde 4 = číslo časovača * počet kanálov (3) + číslo kanálu =  */
  /*  = 1*3+1 pre prvý časovač a prvý kanál */

  //konfigurácia AD prevodníkov
  ADC->ADC_MR |= 0x80;   // AD prevodníky v móde free running - zabezpečí čo najrýchlejší prevod
  ADC->ADC_CR = 2;       // spustenie prevodu
  ADC->ADC_CHER = 0x1CC0;  // aktivácia prevodníkov A0, A1, A8, A9, A10

  //konfigurácia DA prevodníkov
  analogWrite(DAC0, 0); // aktivuje prevodník DAC0
  analogWrite(DAC1, 0); // aktivuje prevodník DAC1

  //nastavuje pin 6 na vstupnú referenciu
  pinMode(EFFECTSWITCH, INPUT_PULLUP);

  // určuje zmenu stavu na pine 6 ako podmienku pre prerušovač
  attachInterrupt(digitalPinToInterrupt(EFFECTSWITCH), effect_handler, CHANGE);

  //nastavuje pin 7 na vstupnú referenciu
  pinMode(LAN, INPUT);

  // určuje zmenu stavu na pine 7 ako podmienku pre prerušovač
  attachInterrupt(digitalPinToInterrupt(LAN), state_handler, CHANGE);

  //nastavuje pin 5 na vstupnú referenciu
  pinMode(WAVEFORM, INPUT_PULLUP);

  // určuje zmenu stavu na pine 5 ako podmienku pre prerušovač
  attachInterrupt(digitalPinToInterrupt(WAVEFORM), form_handler, CHANGE);

  pinMode(LED1, OUTPUT);           //nastavuje pin č.8 ako výstup
  pinMode(LED2, OUTPUT);           //nastavuje pin č.9 ako výstup
  state_value = digitalRead(LAN);
}

void loop()
{
  //čítanie AD prevodníkov
  while ((ADC->ADC_ISR & 0x1CC0) != 0x1CC0); // čakanie na prevod

  /* priradenie aktuálnych hodnôt z jednotlivých prevodníkov, */
  /*   prislúchajúcich AD prevodníkom AD10 = A8 a AD11 = A9   */
  in_ADC0 = ADC->ADC_CDR[10];
  in_ADC1 = ADC->ADC_CDR[11];


  /* ak nastala zmena polohy potenciometra */
  /* priradenie aktuálnej hodnoty z prevodníka,  */
  /* do premennej daného potenciometra */
  /* a nastavenie logickej hodnoty na indikáciu zmeny */
  /* hodnoty z premennách potenciometrov sa budú do premenných parametrov */
  /* priraďovať iba v prípade zmeny, */
  /* aby sa hneď neprepisovali hodnoty načítané zo súboru na začiatku programu*/
  if (POT0 > (ADC->ADC_CDR[12] + 20) || POT0 < (ADC->ADC_CDR[12] - 20))
  {
    POT0 = ADC->ADC_CDR[12]; // AD12 = A10
    p0_value = 1;
  }
  if (POT1 > (ADC->ADC_CDR[6] + 20) || POT1 < (ADC->ADC_CDR[6] - 20))
  {
    POT1 = ADC->ADC_CDR[6];  // AD6 = A1
    p1_value = 1;
  }
  if (POT2 > (ADC->ADC_CDR[5] + 20) || POT2 < (ADC->ADC_CDR[5] - 20))
  {
    POT2 = ADC->ADC_CDR[7];  // AD7 = A0
    p2_value = 1;
  }

  /* ak užívateľ prepne tretím spínačom stav,  */
  /*    nebude prebiehať spracovávanie signálu, ale vytvorenie serveru a komunikácia s klientom */
  if (state_value == 1)
  {
    TC1->TC_CHANNEL[1].TC_IDR = TC_IER_CPCS; // deaktivovanie časovača
    digitalWrite(LED1, HIGH); // prvá LED svieti na signalizáciu spustenej sieťovej komunikácie
    EthernetClient client = server.available();  // pokúsiť sa získať klienta

    if (client) {  // kód nasledujúci po tomto riadku sa vykoná iba ak je k dispozícii klient
      boolean currentLineIsBlank = true;
      while (client.connected()) {
        if (client.available()) {
          char c = client.read(); // čítanie správy od klienta po jednom znaku

          /* uloženie znakov zo správy od klienta do vyrovnávacej pamäte registra*/
          if (req_index < (REQ_BUF_SZ - 1)) {
            HTTP_req[req_index] = c;
            req_index++;
          }

          /* posledný riadok od klienta je prázdny a končí znakom \n */
          //reakcia na správu od klienta môže nastať až po prijatí a uložení celej správy
          if (c == '\n' && currentLineIsBlank) {
            //odosiela štandardnú hlavičku HTTP odozvy
            client.println("HTTP/1.1 200 OK");

            //ak správa obsahuje kľúčové slovo, ktoré má správa obsahovať iba pri stlačení tlačidla..
            if (StrContains(HTTP_req, "ajax_inputs")) {
              /* odošle sa zvýšok HTTP správy pre klienta */
              client.println("Content-Type: text/xml");
              client.println("Connection: keep-alive");
              client.println();
              SetEffect(); //funkcia, ktorá nastaví zvolený efekt

              /* funkcia, ktorá odošle klientovy XML správu s HTML kódom,  */
              /*   tak aby mohol klient reagovať zmenou nápisov na tlačidlách, */
              /*    aby bolo na stránke názorne viditelné, ktorý efekt je práve zvolený */
              XML_response(client);
            }
            else {
              /* odošle sa zvýšok HTTP správy pre klienta */
              client.println("Content-Type: text/html");
              client.println("Connection: keep-alive");
              client.println();
              /* odošle klientovy stránku nahranú na microSD karte */
              webFile = SD.open("web.htm"); //otvorenie a načítanie súboru s HTML kódom stránky
              if (webFile) {
                while (webFile.available()) {
                  client.write(webFile.read()); //zapísanie kódu stránky, do správy pre klienta
                }
                webFile.close();
              }
            }
            req_index = 0; //resetovanie počítadla znakov
            //vymazanie obsahu vyrovnávacej pamäte registra z poslednej použitej správy od klienta
            StrClear(HTTP_req, REQ_BUF_SZ);
            break;
          }
          /* oddelenie stavu, kedy skončil jeden riadok správy a stavu, kedy skončila celá správa */
          if (c == '\n') { //koniec odstavca správy
            currentLineIsBlank = true;
          }
          else if (c != '\r') { //koniec riadka
            currentLineIsBlank = false;
          }
        }
      }
      delay(1);      // oneskorenie, ktoré umožní webovému prehliadaču prijať správu
      client.stop(); // uzatvorenie spojenia
    }
  }

  /* ak užívateľ prepne tretím spínačom stav, */
  /*  nebude prebiehať vytvorenie serveru a komunikácia s klientom, ale spracovávanie signálu */
  if (state_value == 0)
  {
    digitalWrite(LED1, LOW); // prvá LED nesvieti na signalizáciu stavu 0

    /* aktivácia časovača */
    TC1->TC_CHANNEL[1].TC_IER = TC_IER_CPCS;
    TC1->TC_CHANNEL[1].TC_IDR = ~TC_IER_CPCS;
  }
}

/* časť kódu, ktorá prebehne pri každom prerušení časovačom s frekvenciou 125kHz */
void TC4_Handler()
{
  TC_GetStatus(TC1, 1);
  if (effect == 0) // Prvý efekt: Echo
  {
    digitalWrite(LED2, HIGH); // rozsvietenie druhej LEDky na indikáciu zvoleného prvého efektu

    /* pridelenie hodnoty premennej spätná väzba od 1 do 9 podľa polohy druhého potenciometra */
    if (p1_value == 1)
    {
      fb_e = map(POT1 >> 2, 0, 1023, 1, 9);
      p1_value == 0;
    }
    /* POT1 >> 2 znamená: hodnota sa podelí štyrmi. To zabráni preskakovaniu hodnoty */
    /* kvôli neustálej malej variácii napätia na AD prevodníku*/

    /* dynamické určovanie hodnoty priamej vetvy podľa premennej spätná väzba */
    direct_e = (10 - fb_e);

    /* Výpočet a uloženie aktuálnej hodnoty do vyrovnávacej pamäte určenej z aktuálnej a predošlej vzorky. */
    /* Obidve vzorky sú najskôr podelené desiatimi, násobené svojimi premennými priama vetva a spätná väzba, */
    /* a až potom sa hodnoty sčítajú. */
    /* Týmto sa nastaví pomer spätnej väzby voči priamemu signálu a delením hodnôt sa zaručí, */
    /* že maximálna hodnota priraditeľná DA prevodníku nebude prekročená. */
    Buff0[Counter] = ((in_ADC0 / 10 * direct_e) + (Buff0[Counter] / 10 * fb_e));

    /* namapovanie premennej dĺžka oneskorenia pomocou údaju z prvého potenciometra, */
    /* v rozmedzí hodnôt od 1 do maximálnej veľkosti poľa vyrovnávacej pamäte */
    if (p0_value == 1)
    {
      d_e = map(POT0 >> 2, 0, 1023, 1, MAX_BUFF);
      p0_value == 0;
    }
    /* inkrementácia indexu, ktorý sleduje aktuálnu pozíciu poľa vyrovnávacej pamäte */
    /*    týmto sa zaručí, že nová nasledujúca vzorka neprepíše aktuálnu */
    /*   a pole vyrovnávacej pamäte sa bude postupne zapĺňať */
    Counter++;

    /* nulovanie indexu sledujúceho aktuálnu pozíciu vo vyrovnávacej pamäti, */
    /* pri prekročení požadovanej veľkosti oneskorenia */
    if (Counter >= d_e) Counter = 0;

    /* Priradenie oneskorenej vzorky z vyrovnávacej pamäte na výstup */
    /* a nastavenie jej hlasitosti pomocou hodnoty namapovanej z tretieho potenciometra*/
    out_DAC0 = map((Buff0[Counter]), 0, 4095, 1, POT2);

    /* Zápis hodnôt spracovaných vzoriek do DA prevodníkov */
    dacc_set_channel_selection(DACC_INTERFACE, 0);          //vybranie DAC0 na zápis
    dacc_write_conversion_data(DACC_INTERFACE, out_DAC0);  //zápis spracovanej oneskorenej vzorky na DAC0
    dacc_set_channel_selection(DACC_INTERFACE, 1);          //vybranie DAC1 na zápis
    dacc_write_conversion_data(DACC_INTERFACE, 0);        //zápis nulovej hodnoty na DAC1
  }

  else  if (effect == 1)// Druhý efekt: Tremolo
  {
    digitalWrite(LED2, LOW); // zhasnutie druhej LEDky, pretože nie je zvolený prvý efekt

    /* namapovanie premmenej hĺbka tremola */
    /* v rozmedzí hodnôt od 2047 po 4095 pomocou údaju z prvého potenciometra */
    if (p0_value == 1)
    {
      a_t = map(POT0 >> 1, 1, 2047, 2047, 4095 );
      p0_value == 0;
    }

    /* namapovanie premmenej rýchlosť tremola */
    /* v rozmedzí hodnôt od 1 do 10 pomocou údaju z druhého potenciometra */
    if (p1_value == 1)
    {
      f_t = map(POT1 >> 2, 1, 1023, 1, 10);
      p1_value == 0;
    }

    form_state_t = form_state; // uloženie hodnoty z prepínača tvaru vlny, konkrétne pre tremolo

    /* pomocná premenná hĺbky tremola, do ktorej sa uloží výsledok výpočtu, */
    /* aby sa predišlo jeho viacnásobnému počítaniu */
    help_t = (4095 - a_t);

    /* ak je požadovaný tvar vlny zvolený spínačom rovný 0, */
    /* nasleduje prispôsobenie aktuálnej hodnoty z poľa vlny sínus k zvolenej hĺbke */
    if (form_state_t == 0)
    {
      LFO_t = map((SineT44[i_t]), 0, 1000, help_t, 4095);
    }

    /* ak je požadovaný tvar vlny zvolený spínačom rovný 1, */
    /* nasleduje prispôsobenie aktuálnej hodnoty z poľa vlny trojuhoľník k zvolenej hĺbke */
    else if (form_state_t == 1)
    {
      LFO_t = map((TriT44[i_t]), 0, 1000, help_t, 4095);
    }

    /* pridelenie vzoriek do premenných výstupu */
    /* a prispôsobenie ich veľkosti k aktuálnej hodnote nízkofrekvenčného oscilátora */
    out_DAC0 = map(in_ADC0, 1, 4095, 1, LFO_t);
    out_DAC1 = map(in_ADC1, 1, 4095, 1, LFO_t);

    /* inkrementácia indexu sledujúceho aktuálnu hodnotu poľa vlny */
    /* rýchlosť efektu sa zväčší pri zväčšení premennej rýchlosti f_t, */
    /*  pretože niektoré hodnoty z poľa vlny budú preskočené */
    /*  následkom teda bude prejdenie celého poľa zvolenej vlny v kratšom čase */
    /* a zväčšenie frekvencie LFO*/
    i_t = i_t + f_t;

    /* Po prekročení maximálnej veľkosti poľa vlny sa vráti index ukazovateľa na jeho začiatok. */
    /* Konkrétne na hodnotu, o ktorú bola maximálna veľkosť prekročená. */
    /* Týmto sa zachová kontinuita a predíde sa neprirodzeným skokom vo výslednom signáli.*/
    if (i_t >= (44100)) i_t = i_t - 44100;

    /* Nastavenie výstupnej hlasitosti pomocou hodnoty namapovanej z tretieho potenciometra*/
    out_DAC0 = map(out_DAC0, 0, 4095, 1, POT2);
    out_DAC1 = map(out_DAC1, 0, 4095, 1, POT2);

    /* Zápis hodnôt spracovaných vzoriek do DA prevodníkov */
    dacc_set_channel_selection(DACC_INTERFACE, 0);            //vybranie DAC0 na zápis
    dacc_write_conversion_data(DACC_INTERFACE, out_DAC0);     //zápis do DAC0
    dacc_set_channel_selection(DACC_INTERFACE, 1);            //vybranie DAC1 na zápis
    dacc_write_conversion_data(DACC_INTERFACE, out_DAC1);     //zápis do DAC1

  }

  else  if (effect == 2)// Tretí efekt: Vibrato
  {
    digitalWrite(LED2, LOW); // zhasnutie druhej LEDky, pretože nie je zvolený prvý efekt

    /* uloženie aktuálnej hodnoty z AD prevodníka č.8 do prvej vyrovnávacej pamäte */
    Buff0[Counter] = in_ADC0;

    /* uloženie aktuálnej hodnoty z AD prevodníka č.9 do prvej vyrovnávacej pamäte */
    Buff1[Counter] = in_ADC1;

    /* Pridelenie premmenej hĺbka vibráta v rozmedzí hodnôt od 10 po 1, */
    /*   pomocou údaju z prvého potenciometra. */
    /*   aby sa hĺbka efektu zväčšovala pri točení potenciometra v smere hodinových ručičiek, */
    /*   je potrebné prideliť hodnoty do premennej v opačnom poradí, */
    /*   pretože premenná hĺbka je ďalej používaná ako deliteľ */
    if (p0_value == 1)
    {
      a_v = map(POT0 >> 2, 0, 1023, 10, 1 );
      p0_value == 0;
    }

    /* namapovanie premmenej rýchlosť vibráta v rozmedzí hodnôt od 1 do 10 */
    /*  pomocou údaju z druhého potenciometra */
    if (p1_value == 1)
    {
      f_v = map(POT1 >> 2, 0, 1023, 1, 10);
      p1_value == 0;
    }

    form_state_v = form_state; // uloženie hodnoty z prepínača tvaru vlny, konkrétne pre vibráto

    /* ak je požadovaný tvar vlny zvolený spínačom rovný 0, */
    /* nasleduje pomocný výpočet oneskorenia podľa aktuálneho indexu vyrovnávacej pamäte */
    /* a aktuálnej hodnoty z poľa tvaru vlny sínus podelenej zvolenou hĺbkou */
    if (form_state_v == 0)
    {
      help_v = (Counter - (  SineT44[i_v] / a_v ) );
    }

    /* ak je požadovaný tvar vlny zvolený spínačom rovný 1, */
    /* nasleduje pomocný výpočet oneskorenia podľa aktuálneho indexu vyrovnávacej pamäte */
    /* a aktuálnej hodnoty z poľa tvaru vlny trojuhoľník podelenej zvolenou hĺbkou */
    else if (form_state_v == 1)
    {
      help_v = (Counter - (  TriT44[i_v] / a_v ) );
    }

    /* Ak je výsledok pomocného výpočtu väčší, alebo rovný nule, */
    /* je pomocou neho možné prideliť oneskorenú vzorku z vyrovnávacej pamäte do premennej výstupu */
    if (help_v >= 0)
    {
      out_DAC0 = (Buff0[help_v]);
      out_DAC1 = (Buff1[help_v]);
    }

    /* Ak je však výsledok pomocného výpočtu menší ako nula, */
    /*  zasahovali by sme mimo rozmery poľa vyrovnávacej pamäte. */
    /*  Preto je potrebné presunúť sa na jeho opačný koniec. */
    /*  Dosiahnuté je to pripočítaním zápornej hodnoty pomocného výsledku, */
    /*  k maximálnej veľkosti poľa vyrovnávacej pamäte. */
    else if (help_v < 0) {
      out_DAC0 = (Buff0[MAX_BUFF + help_v]);
      out_DAC1 = (Buff1[MAX_BUFF + help_v]);
    }

    /* inkrementácia indexu sledujúceho aktuálnu hodnotu poľa vlny */
    /* rýchlosť efektu sa zväčší pri zväčšení premennej rýchlosti f_t, */
    /* pretože niektoré hodnoty z poľa vlny budú preskočené */
    /*  následkom teda bude prejdenie celého poľa zvolenej vlny v kratšom čase */
    /*  a zväčšenie frekvencie LFO */
    i_v = i_v + f_v;

    /* Po prekročení maximálnej veľkosti poľa vlny sa vráti index ukazovateľa na jeho začiatok. */
    /*  Konkrétne na hodnotu, o ktorú bola maximálna veľkosť prekročená. */
    /*  Týmto sa zachová kontinuita a predíde sa neprirodzeným skokom vo výslednom signáli.*/
    if (i_v >= (44100)) i_v = i_v - 44100;

    /* inkrementácia indexu, ktorý sleduje aktuálnu pozíciu poľa vyrovnávacej pamäte */
    /*   týmto sa zaručí, že nová nasledujúca vzorka neprepíše aktuálnu */
    /*   a pole vyrovnávacej pamäte sa bude postupne zapĺňať */
    Counter++;

    /*ak je hodnota indexu ukazovateľa aktuálnej pozície */
    /*  väčšia alebo rovná ako maximálna veľkosť vyrovnávacej pamäte, */
    /* nastane resetovanie indexu na nulu */
    if (Counter >= MAX_BUFF) Counter = 0;

    /* Nastavenie výstupnej hlasitosti pomocou hodnoty namapovanej z tretieho potenciometra*/
    out_DAC0 = map(out_DAC0, 0, 4095, 1, POT2);
    out_DAC1 = map(out_DAC1, 0, 4095, 1, POT2);

    /* Zápis hodnôt spracovaných vzoriek do DA prevodníkov */
    dacc_set_channel_selection(DACC_INTERFACE, 0);            //vybranie DAC0 na zápis
    dacc_write_conversion_data(DACC_INTERFACE, out_DAC0);     //zápis do DAC0
    dacc_set_channel_selection(DACC_INTERFACE, 1);            //vybranie DAC1 na zápis
    dacc_write_conversion_data(DACC_INTERFACE, out_DAC1);     //zápis do DAC1
  }

  else  if (effect == 3)// Štvrtý efekt: Chorus
  {
    digitalWrite(LED2, LOW); // zhasnutie druhej LEDky, pretože nie je zvolený prvý efekt

    Buff0[Counter] = in_ADC0; //uloženie aktuálnej hodnoty z AD prevodníka č.8 do prvej vyrovnávacej pamäte
    Buff1[Counter] = in_ADC1; //uloženie aktuálnej hodnoty z AD prevodníka č.9 do druhej vyrovnávacej pamäte

    /* namapovanie premmenej hĺbka chorusu v rozmedzí hodnôt od 10 po 1 pomocou údaju z prvého potenciometra */
    /*  Aby sa hĺbka efektu zväčšovala pri točení potenciometra v smere hodinových ručičiek, */
    /* je potrebné prideliť hodnoty do premennej v opačnom poradí, */
    /*  pretože premenná hĺbka je ďalej používaná ako deliteľ */
    if (p0_value == 1)
    {
      a_c = map(POT0 >> 2, 0, 1023, 10, 1 );
      p0_value == 0;
    }

    /* namapovanie premmenej rýchlosť chorusu v rozmedzí hodnôt od 1 do 10 */
    /*  pomocou údaju z druhého potenciometra */
    if (p1_value == 1)
    {
      f_c = map(POT1 >> 2, 0, 1023, 1, 10);
      p1_value == 0;
    }

    /* pridelenie hodnoty premennej vetva efektu od 1 do 9 podľa polohy tretieho potenciometra */
    if (p2_value == 1)
    {
      fx_c = map(POT2 >> 2, 0, 1023, 1, 9);
      p2_value == 0;
    }

    /* dynamické určovanie hodnoty priamej vetvy podľa premennej vetva efektu */
    direct_c = 10 - fx_c;

    form_state_c = form_state; // uloženie hodnoty z prepínača tvaru vlny, konkrétne pre chorus

    /* ak je požadovaný tvar vlny zvolený spínačom rovný 0, */
    /* nasleduje pomocný výpočet oneskorenia podľa aktuálneho indexu vyrovnávacej pamäte */
    /* a aktuálnej hodnoty z poľa tvaru vlny sínus podelenej zvolenou hĺbkou */
    if (form_state_c == 0)
    {
      help_c = (Counter - (  SineT44[i_c] / a_c) );
    }

    /* ak je požadovaný tvar vlny zvolený spínačom rovný 1, */
    /* nasleduje pomocný výpočet oneskorenia podľa aktuálneho indexu vyrovnávacej pamäte */
    /* a aktuálnej hodnoty z poľa tvaru vlny trojuhoľník podelenej zvolenou hĺbkou */
    else if (form_state_c == 1)
    {
      help_c = (Counter - (  TriT44[i_c] / a_c ) );
    }

    /* Ak je výsledok pomocného výpočtu väčší, alebo rovný nule, */
    /* je pomocou neho možné prideliť oneskorenú vzorku z vyrovnávacej pamäte do premennej výstupu*/
    if (help_c >= 0)
    {
      out_DAC0 = (Buff0[help_c]);
      out_DAC1 = (Buff1[help_c]);
    }

    /* Ak je však výsledok pomocného výpočtu menší ako nula, */
    /* zasahovali by sme mimo rozmery poľa vyrovnávacej pamäte. */
    /* Preto je potrebné presunúť sa na jeho opačný koniec. */
    /* Dosiahnuté je to pripočítaním zápornej hodnoty pomocného výsledku, */
    /* k maximálnej veľkosti poľa vyrovnávacej pamäte. */
    else if (help_c < 0) {
      out_DAC0 = (Buff0[MAX_BUFF + help_c]);
      out_DAC1 = (Buff1[MAX_BUFF + help_c]);
    }

    /* inkrementácia indexu sledujúceho aktuálnu hodnotu poľa vlny */
    /* rýchlosť efektu sa zväčší pri zväčšení premennej rýchlosti f_t, */
    /*  pretože niektoré hodnoty z poľa vlny budú preskočené */
    /*  následkom teda bude prejdenie celého poľa zvolenej vlny v kratšom čase */
    /*  a zväčšenie frekvencie LFO */
    i_c = i_c + f_c;

    /* Po prekročení maximálnej veľkosti poľa vlny sa vráti index ukazovateľa na jeho začiatok. */
    /* Konkrétne na hodnotu, o ktorú bola maximálna veľkosť prekročená. */
    /* Týmto sa zachová kontinuita a predíde sa neprirodzeným skokom vo výslednom signáli.*/
    if (i_c >= (44100)) i_c = i_c - 44100;

    /* Výpočet a pridelenie hodnoty vzorky do premennej výstupu, */
    /*    určenej zo spracovanej a nespracovanej vzorky. */
    /* Obidve vzorky sú najskôr podelené desiatimi, */
    /* násobené svojimi premennými efektová vetva a priama vetva, */
    /* a až potom sa hodnoty sčítajú. */
    /* Týmto sa nastaví pomer efektovej vetvy, voči priamemu signálu a delením hodnôt sa zaručí, */
    /* že maximálna hodnota priraditeľná DA prevodníku nebude prekročená. */
    out_DAC0 = ((out_DAC0 / 10 * fx_c) + (in_ADC0 / 10 * direct_c));
    out_DAC1 = ((out_DAC1 / 10 * fx_c) + (in_ADC1 / 10 * direct_c));

    /* inkrementácia indexu, ktorý sleduje aktuálnu pozíciu poľa vyrovnávacej pamäte */
    /*   týmto sa zaručí, že nová nasledujúca vzorka neprepíše aktuálnu */
    /*   a pole vyrovnávacej pamäte sa bude postupne zapĺňať */
    Counter++;

    /* Ak je hodnota indexu ukazovateľa aktuálnej pozície */
    /* väčšia alebo rovná ako maximálna veľkosť vyrovnávacej pamäte, */
    /* nastane resetovanie indexu na nulu */
    if (Counter >= MAX_BUFF) Counter = 0;

    /* Prispôsobenie výstupnej hlasitosti, tak aby bola čo najväčšia. */
    /* Pri tomto prispôsobení nedochádza k zmenšeniu dynamického rozsahu signálu*/
    out_DAC0 = map(out_DAC0, 0, 4095, 1, 4095);
    out_DAC1 = map(out_DAC1, 0, 4095, 1, 4095);

    /* Zápis hodnôt spracovaných vzoriek do DA prevodníkov */
    dacc_set_channel_selection(DACC_INTERFACE, 0);            //vybranie DAC0 na zápis
    dacc_write_conversion_data(DACC_INTERFACE, out_DAC0);     //zápis do DAC0
    dacc_set_channel_selection(DACC_INTERFACE, 1);            //vybranie DAC1 na zápis
    dacc_write_conversion_data(DACC_INTERFACE, out_DAC1);     //zápis do DAC1
  }

  else // Piaty efekt: Flanger
  {
    digitalWrite(LED2, LOW); // zhasnutie druhej LEDky, pretože nie je zvolený prvý efekt

    /* pridelenie hodnoty premennej spätná väzba flangeru od 1 do 9 podľa polohy tretieho potenciometra */
    if (p2_value == 1)
    {
      fb_f = map(POT2 >> 2, 0, 1023, 1, 9);
      p2_value == 0;
    }

    /* dynamické určovanie hodnoty priamej vetvy podľa premennej vetva efektu */
    direct_f = 10 - fb_f;

    /* Výpočet a uloženie aktuálnej hodnoty do vyrovnávacej pamäte určenej z predošlej a aktuálnej vzorky. */
    /* Obidve vzorky sú najskôr podelené desiatimi, násobené svojimi premennými spätná väzba apriama vetva, */
    /* a až potom sa hodnoty sčítajú. */
    /* Týmto sa nastaví pomer spätnej väzby voči priamemu signálu a delením hodnôt sa zaručí, */
    /* že maximálna hodnota priraditeľná DA prevodníku nebude prekročená. */
    Buff0[Counter] = ((out_DAC0 / 10 * fb_f) + (in_ADC0 / 10 * direct_f));
    Buff1[Counter] = ((out_DAC1 / 10 * fb_f) + (in_ADC1 / 10 * direct_f));

    /* namapovanie premmenej hĺbka flanger v rozmedzí hodnôt od 10 po 1 pomocou údaju z prvého potenciometra */
    /*  aby sa hĺbka efektu zväčšovala pri točení potenciometra v smere hodinových ručičiek, */
    /*  je potrebné prideliť hodnoty do premennej v opačnom poradí, */
    /* pretože premenná hĺbka je ďalej používaná ako deliteľ */
    if (p0_value == 1)
    {
      a_f = map(POT0 >> 2, 0, 1023, 10, 1 );
      p0_value == 0;
    }

    /* namapovanie premmenej rýchlosť flangera v rozmedzí hodnôt od 1 do 10 */
    /* pomocou údaju z druhého potenciometra */
    if (p1_value == 1)
    {
      f_f = map(POT1 >> 2, 0, 1023, 1, 10);
      p1_value == 0;
    }

    form_state_f = form_state; // uloženie hodnoty z prepínača tvaru vlny, konkrétne pre chorus

    /* ak je požadovaný tvar vlny zvolený spínačom rovný 0, */
    /* nasleduje pomocný výpočet oneskorenia podľa aktuálneho indexu vyrovnávacej pamäte */
    /* a aktuálnej hodnoty z poľa tvaru vlny sínus podelenej zvolenou hĺbkou */
    if (form_state_f == 0)
    {
      help_f = (Counter - (  SineT44[i_f] / a_f ) );
    }

    /* ak je požadovaný tvar vlny zvolený spínačom rovný 1, */
    /* nasleduje pomocný výpočet oneskorenia podľa aktuálneho indexu vyrovnávacej pamäte */
    /* a aktuálnej hodnoty z poľa tvaru vlny trojuhoľník podelenej zvolenou hĺbkou */
    else if (form_state_f == 1)
    {
      help_f = (Counter - (  TriT44[i_f] / a_f ) );
    }

    /* Ak je výsledok pomocného výpočtu väčší, alebo rovný nule, */
    /* je pomocou neho možné prideliť oneskorenú vzorku z vyrovnávacej pamäte do premennej výstupu*/
    if (help_f >= 0)
    {
      out_DAC0 = (Buff0[help_f]);
      out_DAC1 = (Buff1[help_f]);
    }

    /* Ak je však výsledok pomocného výpočtu menší ako nula, */
    /* zasahovali by sme mimo rozmery poľa vyrovnávacej pamäte. */
    /* Preto je potrebné presunúť sa na jeho opačný koniec. */
    /* Dosiahnuté je to pripočítaním zápornej hodnoty pomocného výsledku, */
    /* k maximálnej veľkosti poľa vyrovnávacej pamäte. */
    else if (help_f < 0) {
      out_DAC0 = (Buff0[MAX_BUFF + help_f]);
      out_DAC1 = (Buff1[MAX_BUFF + help_f]);
    }

    /* inkrementácia indexu sledujúceho aktuálnu hodnotu poľa vlny */
    /* rýchlosť efektu sa zväčší pri zväčšení premennej rýchlosti f_t, */
    /* pretože niektoré hodnoty z poľa vlny budú preskočené */
    /* následkom teda bude prejdenie celého poľa zvolenej vlny v kratšom čase */
    /* a zväčšenie frekvencie LFO */
    i_f = i_f + f_f;

    /* Po prekročení maximálnej veľkosti poľa vlny sa vráti index ukazovateľa na jeho začiatok. */
    /* Konkrétne na hodnotu, o ktorú bola maximálna veľkosť prekročená. */
    /* Týmto sa zachová kontinuita a predíde sa neprirodzeným skokom vo výslednom signáli.*/
    if (i_f >= (44100)) i_f = i_f - 44100;

    /* Výpočet a pridelenie hodnoty vzorky do premennej výstupu, */
    /*   určenej zo spracovanej a nespracovanej vzorky. */
    /* Obidve vzorky sú najskôr podelené desiatimi, násobené empiricky zvolenými konštantami, */
    /*  a až potom sa hodnoty sčítajú. */
    /*  Týmto sa nastaví pomer efektovej vetve voči priamemu signálu a delením hodnôt sa zaručí, */
    /* že maximálna hodnota priraditeľná DA prevodníku nebude prekročená. */
    out_DAC0 = ((out_DAC0 / 10 * 6) + (in_ADC0 / 10 * 4));
    out_DAC1 = ((out_DAC1 / 10 * 6) + (in_ADC1 / 10 * 4));

    /* inkrementácia indexu, ktorý sleduje aktuálnu pozíciu poľa vyrovnávacej pamäte */
    /*     týmto sa zaručí, že nová nasledujúca vzorka neprepíše aktuálnu */
    /*     a pole vyrovnávacej pamäte sa bude postupne zapĺňať */
    Counter++;

    /*ak je hodnota indexu ukazovateľa aktuálnej pozície */
    /* väčšia alebo rovná ako maximálna veľkosť vyrovnávacej pamäte, */
    /* nastane resetovanie indexu na nulu */
    if (Counter >= MAX_BUFF) Counter = 0;

    /* Prispôsobenie výstupnej hlasitosti, tak aby bola čo najväčšia. */
    /* Pri tomto prispôsobení nedochádza k zmenšeniu dynamického rozsahu signálu*/
    out_DAC0 = map(out_DAC0, 0, 4095, 1, 4095);
    out_DAC1 = map(out_DAC1, 0, 4095, 1, 4095);

    /* Zápis hodnôt spracovaných vzoriek do DA prevodníkov */
    dacc_set_channel_selection(DACC_INTERFACE, 0);            //vybranie DAC0 na zápis
    dacc_write_conversion_data(DACC_INTERFACE, out_DAC0);     //zápis do DAC0
    dacc_set_channel_selection(DACC_INTERFACE, 1);            //vybranie DAC1 na zápis
    dacc_write_conversion_data(DACC_INTERFACE, out_DAC1);     //zápis do DAC1
  }
}

/* Funkcia, ktorá určí či bude mať LFO tvar sínusu, alebo trojuhoľníka*/
/*  Nastane pri prerušení behu programu, ktoré vznikne vďaka stlačeniu spínača */
void form_handler()
{
  delayMicroseconds(100000);
  //ak sa zmenil stav daného spínača
  if (form_state != digitalRead(WAVEFORM))
    delayMicroseconds(100000);
  //uloženie nového stavu (logickej hodnoty) z digitálneho vstupu, na ktorý je pripojený daný spínač
  form_state = digitalRead(WAVEFORM);
}


/* Funkcia, ktorá zabezbečuje prepínanie efektov. Nastane pri prerušení behu programu, */
/* ktoré vznikne vďaka prepnutiu spínača */
void effect_handler()
{
  delayMicroseconds(100000);
  //ak sa zmenil stav spínača, nastane inkrementácia indexu effect
  if (switch_value != digitalRead(EFFECTSWITCH)) {
    effect++;
    delayMicroseconds(100000);
    //uloženie nového stavu (logickej hodnoty) z digitálneho vstupu, na ktorý je pripojený spínač
    switch_value = digitalRead(EFFECTSWITCH);
  }
  if (effect == 5) effect = 0; //resetovanie premennej effect pri prekročení rozsahu efektov
}

/* Funkcia, ktorá zabezbečuje prepínanie stavov. Nastane pri prerušení behu programu, */
/* ktoré vznikne vďaka prepnutiu spínača */
void state_handler()
{
  delayMicroseconds(100000);
  //ak sa zmenil stav spínača
  if (state_value != digitalRead(LAN))
    delayMicroseconds(100000);
  //uloženie nového stavu (logickej hodnoty) z digitálneho vstupu, na ktorý je pripojený spínač
  state_value = digitalRead(LAN);
}

/* Funkcia priraďjúca číslo novo vybraného efektu do premennej, */
/* podľa kľúča prijatého v HTTP správe od klienta. */
/* Ak je v HTTP správe správny kľúč spustí sa ukladanie, */
/* alebo načítavanie. */
void SetEffect(void)
{
  if (StrContains(HTTP_req, "effect=0")) {
    effect == 0;
  }
  else if (StrContains(HTTP_req, "effect=1")) {
    effect == 1;
  }
  else if (StrContains(HTTP_req, "effect=2")) {
    effect == 2;
  }
  else if (StrContains(HTTP_req, "effect=3")) {
    effect == 3;
  }
  else if (StrContains(HTTP_req, "effect=4")) {
    effect == 4;
  }
  else if (StrContains(HTTP_req, "effect=5")) {
    save = 1;
    Save();
  }
}


/* funkcia, ktorá odošle klientovy XML správu s HTML kódom, */
/*  tak aby mohol klient reagovať na zmenu efektu zmenou nápisov na tlačidlách */
/* vďaka tejto funkcii je na stránke názorne viditelné, ktorý efekt je práve zvolený */
void XML_response(EthernetClient cl)
{

  cl.print("<?xml version = \"1.0\" ?>");
  cl.print("<inputs>");

  /* efekt 1 */
  cl.print("<EFFECTE>");
  if (effect == 0) {
    cl.print("on");
  }
  else {
    cl.print("off");
  }
  cl.println("</EFFECTE>");

  /* efekt 2 */
  cl.print("<EFFECTT>");
  if (effect == 1) {
    cl.print("on");
  }
  else {
    cl.print("off");
  }
  cl.println("</EFFECTT>");

  /* efekt 3 */
  cl.print("<EFFECTV>");
  if (effect == 2) {
    cl.print("on");
  }
  else {
    cl.print("off");
  }
  cl.println("</EFFECTV>");

  /* efekt 4 */
  cl.print("<EFFECTC>");
  if (effect == 3) {
    cl.print("on");
  }
  else {
    cl.print("off");
  }
  cl.println("</EFFECTC>");

  /* efekt 5 */
  cl.print("<EFFECTF>");
  if (effect == 4) {
    cl.print("on");
  }
  else {
    cl.print("off");
  }
  cl.println("</EFFECTF>");


  cl.print("</inputs>");
}

/* funkcia, ktorá vyčistí reťazec *str zadaný ako parameter */
void StrClear(char *str, char length)
{
  for (int i = 0; i < length; i++) {
    str[i] = 0;
  }
}

/* funkcia hľadajúca reťazec *sfind v reťazci *str*/
/* ak je reťazec nájdený je vrátená hodnota 1, ak nájdený je vrátená hodnota 0 */
char StrContains(char *str, char *sfind)
{
  char found = 0;
  char index = 0;
  char len;

  len = strlen(str);

  if (strlen(sfind) > len) {
    return 0;
  }
  while (index < len) {
    if (str[index] == sfind[found]) {
      found++;
      if (strlen(sfind) == found) {
        return 1;
      }
    }
    else {
      found = 0;
    }
    index++;
  }

  return 0;
}

/* funkcia, ktorá prepíše textový súbor na pamäťovej karte */
/* a uloží do neho nové hodnoty parametrov oddelené čiarkou, */
/* aby ich vedela funkcia parseInt() pomocou tohoto znaku oddeliť */
/* pri načítavaní zo súboru na začiatku programu */
void Save()
{
  SD.remove("mem.txt");
  saveFile = SD.open("mem.txt", FILE_WRITE);
  if (saveFile) {
    saveFile.print(effect);      saveFile.print(",");
    saveFile.print(fb_e);      saveFile.print(",");
    saveFile.print(direct_e);  saveFile.print(",");
    saveFile.print(d_e);       saveFile.print(",");
    saveFile.print(a_t);       saveFile.print(",");
    saveFile.print(f_t);       saveFile.print(",");
    saveFile.print(help_t);    saveFile.print(",");
    saveFile.print(form_state_t);  saveFile.print(",");
    saveFile.print(i_t);       saveFile.print(",");
    saveFile.print(LFO_t);      saveFile.print(",");
    saveFile.print(a_v);       saveFile.print(",");
    saveFile.print(f_v);       saveFile.print(",");
    saveFile.print(help_v);    saveFile.print(",");
    saveFile.print(form_state_v);    saveFile.print(",");
    saveFile.print(i_v);       saveFile.print(",");
    saveFile.print(a_c);       saveFile.print(",");
    saveFile.print(f_c);       saveFile.print(",");
    saveFile.print(help_c);    saveFile.print(",");
    saveFile.print(form_state_c);   saveFile.print(",");
    saveFile.print(i_c);       saveFile.print(",");
    saveFile.print(fx_c);       saveFile.print(",");
    saveFile.print(direct_c);   saveFile.print(",");
    saveFile.print(a_f);       saveFile.print(",");
    saveFile.print(f_f);       saveFile.print(",");
    saveFile.print(help_f);    saveFile.print(",");
    saveFile.print(form_state_f);  saveFile.print(",");
    saveFile.print(i_f);       saveFile.print(",");
    saveFile.print(fb_f);      saveFile.print(",");
    saveFile.print(direct_f);  saveFile.print(",");
    saveFile.close();
  }
}


