#include "midiplayer.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 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(), led (LED_CFG), but (BUT_CFG), passcnt (0u) {
  index = 0;
  pause = 0;
  melody  = scores[index++];
  running = false;
  but.setPuPd (GPIO_PuPd_UP);
}
/// 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 ();
}
void MidiPlayer::pass() {
  const bool b = passcnt & 0x100000;
  led << b;
  passcnt += 1u;
}

unsigned MidiPlayer::Send (uint16_t * const ptr, const unsigned len) {
  const bool b = false; // but;
  if (!b and !running) running = true;
  
  if (!running) {
    for (unsigned n=0; n<len; n++) ptr [n] = MAXPWM >> 1;
    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;                // na konci každé melodie stop
  melody = scores[index++];
  if (!melody) {
    index   = 0;
    melody  = scores[index++];
    running = false; // test bez tlačítka
  }
}
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;
    }
  }
}