//--------------------------------------------------------------
//  MEMS マイクの入力に対して音響効果を与える
//      音響効果：残響生成，周波数シフト
//
//  2017/04/10, Copyright (c) 2017 MIKAMI, Naoki
//--------------------------------------------------------------

#include "InitializeGUI.hpp"
#include "SAI_InOut.hpp"
#include "Reverberator.hpp"
#include "WeaverModulator.hpp"
#include "GuiChanger.hpp"
using namespace Mikami;

int main()
{
    const int FS = AUDIO_FREQUENCY_16K;    // 標本化周波数: 16 kHz
    // 入出力の準備
    SaiIO mySai(SaiIO::BOTH, 256, FS,
                INPUT_DEVICE_DIGITAL_MICROPHONE_2);

    ButtonGroup *onOff; // "ON", "OFF"
    ButtonGroup *menu;  // "THROUGH", "REVERB", "F_SHIFTER"
    SeekBar *barReverb, *barFqCh;
    NumericLabel<int> *frqLabel;
    WaveformDisplay *displayIn, *displayOut;
    // GUI 部品の初期化
    InitializeGUI(onOff, menu, barReverb, barFqCh,
                  frqLabel, displayIn, displayOut);
    // 処理に応じて GUI 部品の状態を変更する関数の割り当て
    void (*fPtr[])(SeekBar*, SeekBar*, NumericLabel<int>*)
        = { SetThrough, SetReverb, SetFrqShifter };

    ProcessingBase through;     // 0: 信号処理なしで出力
    Reverberator reverb;        // 1: 残響生成
    WeaverMod shifter(FS, 100); // 2: 周波数シフト（シフトの初期値：100 Hz）
    ProcessingBase *func[3] = { &through,
                                &reverb,
                                &shifter };

    Array<int16_t> snIn(mySai.GetLength());     // 入力波形表示で使用
    Array<int16_t> snOut(mySai.GetLength());    // 出力波形表示で使用

    mySai.RecordIn();   // 入力開始
    mySai.PlayOut();    // 出力開始
    mySai.PauseOut();   // 出力一時停止

    int menuNum = 0;
    while (true)
    {
        // On/OFF の設定
        int num;
        if (onOff->GetTouchedNumber(num))
        {
            if (num == 0) mySai.ResumeOut();    // 出力再開
            else          mySai.PauseOut();
        }

        // 信号処理の種類の切り替えに対応する GUI 部品の状態の設定
        if (menu->GetTouchedNumber(menuNum))
            fPtr[menuNum](barReverb, barFqCh, frqLabel);

        // 残響の長さを設定
        if ( (menuNum == 1) && (barReverb->Slide()) )
            reverb.SetDelay(barReverb->GetIntValue());

        // 周波数シフトの値の設定
        if ( (menuNum == 2) && (barFqCh->Slide()) )
        {
            frqLabel->Draw(barFqCh->GetIntValue());
            shifter.SetFrequency(barFqCh->GetIntValue());
        }

        //---------------------------------------------
        // １フレーム分の信号処理を行い，その結果を出力する
        if (mySai.IsCompleted())
        {
            for (int n=0; n<mySai.GetLength(); n++)
            {
                int16_t xL, xR;
                mySai.Input(xL, xR);
                int16_t xn = xL + xR;
                snIn[n] = xn;   // 表示用

                //-------------------------------------------------------
                int16_t yn = func[menuNum]->Execute(xn);    // 信号処理実行
                //-------------------------------------------------------

                mySai.Output(yn, yn);   // 左右チャンネルに同じ信号を出力
                snOut[n] = yn;  // 表示用
            }

            displayIn->Execute(snIn);   // 入力波形の表示
            displayOut->Execute(snOut); // 出力波形の表示
        }
        // １フレーム分の信号処理はここまで
        //---------------------------------------------
    }
}
