#include "midiplayer.h"
#include "tone.h"
#include "audio.h"
#include "pcmdma.h"
//#include "filtr.h"
/**
 * @file
 * @brief Jednoduchý přehrávač midi souborů.
 * 
 * Kompletní midi obsahuje zvukové fonty, které jsou obrovské. Tohle je velice zjednodušené,
 * takže docela dobře přehrává skladby typu ragtime, orchestrální midi jsou skoro nepoužitelné.
 * Přesto se to pro jednoduché zvuky může hodit, protože je to poměrně nenáročné na systémové
 * prostředky. Může to fungovat dokonce i na 8-bitovém uP.
 * */
// static Filtr iir;

/// Generátory tónů
static Tone  gens[maxGens];
/// Generuj vzorek pro všechny tóny  @return Vzorek
static inline short genSample (void) {
  int res = 0;
  for (unsigned int i=0; i<maxGens; i++) res += gens[i].step();
  // Pro jistotu omezíme - předejdeme chrastění
  if (res > maxValue) res = maxValue;
  if (res < minValue) res = minValue;
  return (res);
}
/// Počítá další vzorek
short MidiPlayer::nextSample (void) {
  if (pause) pause -= 1;  // Časování tónu
  else ToneChange();      // Nový tón - MidiPlayer::ToneChange
  return genSample ();
}
static constexpr unsigned AudioMidiDelay  = 24;

static constexpr int      INPUT_BIT_RANGE = 16;
static constexpr unsigned SIGMA_MASK      = (1u << (INPUT_BIT_RANGE  + 0)) - 1u;
static constexpr unsigned SIGNED_OFFEST   = (1u << (INPUT_BIT_RANGE  - 1));
 // Předpokládá se na vstupu signed int o šířce INPUT_BIT_RANGE
 // přičemž 0 na vstupu odpovídá MAXPWM / 2 na výstupu. Vypadá to divně, ale funguje.
static unsigned pwm_sd (const int input) {
  static unsigned sigma  = 0;   // podstatné je, že proměnná je statická
  const  unsigned sample = (input + SIGNED_OFFEST) * MAXPWM;
  sigma &= SIGMA_MASK;          // v podstatě se odečte hodnota PWM
  sigma += sample;              // integrace prostým součtem
  return sigma  >> INPUT_BIT_RANGE;
}

/******************************************************************/
/// Konstruktor
MidiPlayer::MidiPlayer() noexcept : OneWay() /*, but(GpioPortA, 2, GPIO_Mode_IN)*/ {
  //but.setPuPd(GPIO_PuPd_UP);
  index = 0;
  pause = 0;
  melody  = scores[index++];
  running = true;
}
unsigned MidiPlayer::Send (uint16_t * const ptr, const unsigned len) {
  // if (!but.get()) running = true;
  
  if (!running) {
    for (unsigned n=0; n<len; n++) ptr [n] = 1000;
    return len;
  }
  
  for (unsigned n=0; n<len; n++) {
    const short s = nextSample();
    ptr [n] = pwm_sd (s);
  }
  return len;
}
void MidiPlayer::stop (void) {
//running = false;
  melody = scores[index++];
  if (!melody) {
    index  = 0;
    melody = scores[index++];
  }
}
void MidiPlayer::ToneChange (void) {
  unsigned char midt;
  
  for (;;) {              // Pro všechny tóny před pauzou
    unsigned char cmd = * melody++;
    if (cmd & 0x80) {     // event
      const unsigned geno = cmd & 0x0F;
      cmd >>= 4;
      switch (cmd) {
        case 0x8:         // off
          gens[geno].setMidiOff();
          break;
        case 0x9:         // on
          midt = * melody++;
          gens[geno].setMidiOn (midt); 
          break;
        default:
          stop();
          return;         // melodie končí eventem 0xf0
      }
    } else {              // pause
      midt = * melody++;
      // Když to trochu uteče, zase se z toho nestřílí, tak to nechme být.
      pause  = ((unsigned int) cmd << 8) + midt;  // v ms
      pause *= AudioMidiDelay;                    // ale máme vzorkování cca 24 kHz 
      return;
    }
  }
}