//////// アンプ //////////////////////////////////
static volatile int32_t Amp_on = 0; // アンプ設定値

static inline float Amp_process(float in) {
  static float curr_gain = 0;      // ゲイン現在値
  float        targ_gain = Amp_on; // ゲイン目標値
  curr_gain = targ_gain -
    ((targ_gain - curr_gain) * (255.0F / 256.0F));

  return in * curr_gain;
}
//////// PWMオーディオ出力部 /////////////////////
#define PWMA_GPIO  (28)           // PWM出力するGPIO番号
#define PWMA_SLICE (6)            // PWMスライス番号
#define PWMA_CHAN  (PWM_CHAN_A)   // PWMチャンネル
#define PWMA_CYCLE (FCLKSYS / FS) // PWM周期

static void pwm_irq_handler();
static void PWMA_init() {
  gpio_set_function(PWMA_GPIO, GPIO_FUNC_PWM);
  irq_set_exclusive_handler(PWM_IRQ_WRAP,
                            pwm_irq_handler);
  irq_set_enabled(PWM_IRQ_WRAP, true);
  pwm_set_irq_enabled(PWMA_SLICE, true);
  pwm_set_wrap(PWMA_SLICE, PWMA_CYCLE - 1);
  pwm_set_chan_level(PWMA_SLICE,
                     PWMA_CHAN, PWMA_CYCLE / 2);
  pwm_set_enabled(PWMA_SLICE, true);
}
static inline void PWMA_process(float in) {
  int32_t  level_int32 = float2int(in * 1024) +
                                     (PWMA_CYCLE / 2);
  uint16_t level = (level_int32 > 0) * level_int32;
  pwm_set_chan_level(PWMA_SLICE, PWMA_CHAN, level);
}
//////// 割り込みハンドラとメイン関数 ////////////
static volatile uint16_t s_time     = 0; // 開始時間
static volatile uint16_t max_s_time = 0; // 最大開始時間
static volatile uint16_t p_time     = 0; // 処理時間
static volatile uint16_t max_p_time = 0; // 最大処理時間

static void pwm_irq_handler() {
  pwm_clear_irq(PWMA_SLICE);
  s_time = pwm_get_counter(PWMA_SLICE);
  PWMA_process(
    Amp_process(Fil_process(Osc_process())));
  uint16_t end_time = pwm_get_counter(PWMA_SLICE);
  p_time = ((end_time - s_time) + PWMA_CYCLE)
                                         % PWMA_CYCLE;
  max_s_time += (s_time > max_s_time) *
                (s_time - max_s_time);
  max_p_time += (p_time > max_p_time) *
                (p_time - max_p_time);
}
int main() {
  set_sys_clock_khz(FCLKSYS / 1000, true);
  stdio_init_all();
  Osc_init(); Fil_init(); PWMA_init();
  while (true) {
    switch (getchar_timeout_us(0)) {
    case '1': Osc_pitch = 0; Amp_on = 1; break; // ド
    case '2': Osc_pitch = 1; Amp_on = 1; break; // レ
    case '3': Osc_pitch = 2; Amp_on = 1; break; // ミ
    case '4': Osc_pitch = 3; Amp_on = 1; break; // ファ
    case '5': Osc_pitch = 4; Amp_on = 1; break; // ソ
    case '6': Osc_pitch = 5; Amp_on = 1; break; // ラ
    case '7': Osc_pitch = 6; Amp_on = 1; break; // シ
    case '8': Osc_pitch = 7; Amp_on = 1; break; // ド
    case '0':                Amp_on = 0; break;
    case 'z': if (Fil_cut > 0)   { --Fil_cut; } break;
    case 'x': if (Fil_cut < 120) { ++Fil_cut; } break;
    case 'n': if (Fil_res > 0)   { --Fil_res; } break;
    case 'm': if (Fil_res < 5)   { ++Fil_res; } break;
    }
    static uint32_t loop_counter = 0; // ループ回数
    if ((++loop_counter & 0xFFFFF) == 0) {
      printf("p:%lu, c:%3lu, r:%lu, a:%ld, ",
                 Osc_pitch, Fil_cut, Fil_res, Amp_on);
      printf("start:%4u/%4u, processing:%4u/%4u\n",
              s_time, max_s_time, p_time, max_p_time);
    }
  }
}
