/**
 * @file uzume_callback.c
 * @brief オーディオ信号処理を行うユーザー・コールバック
 */
#include "t_services.h"
#include "s_services.h"
#include "kernel_id.h"
#include "uzume.h"
#include "i2s_subsystem.h"
#include <fx32_nco.h>
#include <fract.h>
#include <limits.h>
#include <cdefBF592-A.h>
    /* Constant to create Hz on 48kHz sample*/
#define Hz (LONG_MAX/24000)
#define MAXDELAY 480
#define MINDELAY 96
#define LFOCYLE 2


typedef struct {
    fract32 line[MAXDELAY];
        // pointer to the newest sample in delayline
    int head;
} TDLEAYLINE;

static TNCO lfo;

static TDLEAYLINE delayline;

void init_delay( TDLEAYLINE *dl)
{
    /* ブロックサイズは UZUME_BLOCKSIZE マクロを使用すればわかる */

    int i;

    dl->head = 0;

    for ( i=0; i<MAXDELAY; i++)
        dl->line[i] = 0;


}

/**
 * \brief put the sample into the head of delayline
 * \param dl delay line object
 * \param sample sample to be put
 * \details
 * put the given parameter sample into the head of current delay line. the head is -1 ahead after putting.
 */
void put_delay( TDLEAYLINE * dl, fract32 sample)
{
        // move ahead the head pointer
    dl->head--;

        // Wrap arround the head pointer if required.
    if ( dl->head < 0 )
        dl->head = MAXDELAY -1;

        // put the sample to the head
    dl->line[dl->head] = sample;
}

/**
 * \brief Get a sample from the delayline with given offset
 * \param dl Delay line object
 * \param delay Integer offset represents the offset. Unit is sample.
 * \details
 * retrieve a sample from the delayline object.
 *
 */
fract32 get_delayed_sample( TDLEAYLINE * dl, int delay)
{
    int offset;

        // Calc the offset
    offset = dl->head + delay;

        // Wrap arround the offset if required.
    if (offset >= MAXDELAY)
        offset -= MAXDELAY;

    return dl->line[offset];
}

    /*  sw : 0-11 represents the sw1-12 */
int check_switch(int sw)
{
    if ( sw < 8 )
    {
        return ( ! ( *pPORTFIO & (1<<sw) ) );
    }
    else
    {
        return ( ! ( * pPORTGIO & ( 1<< sw)));
    }
}

/**
 * @brief 信号処理初期化関数
 * @details
 * この関数はUZUMEプラットフォームの初期化を行う。
 * フィルタの構築や変数の初期化などを実行すると良い。
 *
 * @ingroup AUDIO
 */
void init_audio(void)
{
    /* ブロックサイズは UZUME_BLOCKSIZE マクロを使用すればわかる */

    fract32_nco_init( &lfo, 0);
    init_delay( &delayline );
}



/**
 * @brief 信号処理関数
 * @param input_left 左チャンネル0入力配列
 * @param input_right 右チャンネル0入力配列
 * @param output_left 左チャンネル0出力配列
 * @param output_right 右チャンネル0出力配列
 * @ingroup AUDIO
 *
 * ユーザーが信号処理を行う関数。オーディオ信号に同期して呼び出される。
 * それぞれの引数は引数countがあらわす個数だけデータを持っている。
 * データは24bit符号付整数。32bit変数に左詰で渡される。
 *
 * この関数はステレオ信号を２系統あつかえる。すなわち、系統０と系統1がそれぞれ左右チャンネルを持つ。
 * この関数が使う資源で、あらかじめ初期化が必要なものは、@ref init_audio()の中で初期化しておく。
 */
void process_audio(
        AUDIOSAMPLE input_left[],
        AUDIOSAMPLE input_right[],
        AUDIOSAMPLE output_left[],
        AUDIOSAMPLE output_right[]
        )
{
    // ループ変数
    int i, delay;
    fract32 delayedsample, sample, width;
    fract32 cycle, feedback, mix;
    fract32 cosout[UZUME_BLOCKSIZE], sinout[UZUME_BLOCKSIZE];

    feedback = get_volume( 2 );
    mix = get_volume( 3 );

    cycle = mult_fr1x32x32( get_volume(0), LFOCYLE*Hz);
    fract32_nco_set_freq(&lfo, cycle);
    fract32_nco(&lfo, cosout, sinout, UZUME_BLOCKSIZE);

    width =  mult_fr1x32x32(
                get_volume(1),
                (MAXDELAY-MINDELAY)/2);


    for (i = 0; i < UZUME_BLOCKSIZE; i++) {
        sample = input_left[i] + input_right[i];

        delay =  mult_fr1x32x32(
                    cosout[i],
                    width) + (MAXDELAY+MINDELAY)/2;


        delayedsample = get_delayed_sample( &delayline, delay);
        put_delay(&delayline,
                mult_fr1x32x32(delayedsample, feedback)
                + sample  );

        output_left[i] = output_right[i] =
                mult_fr1x32x32( sample, mix )
                +mult_fr1x32x32( delayedsample, INT_MAX-mix );
    }


}

