/*----------------------------------------------------------------------------
 *
 * File:
 * eas_reverbdata.h
 *
 * Contents and purpose:
 * Contains the prototypes for the Reverb effect.
 *
 *
 * Copyright Sonic Network Inc. 2006

 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *----------------------------------------------------------------------------
 * Revision Control:
 *   $Revision: 499 $
 *   $Date: 2006-12-11 16:07:20 -0800 (Mon, 11 Dec 2006) $
 *----------------------------------------------------------------------------
*/

#ifndef _EAS_REVERBDATA_H
#define _EAS_REVERBDATA_H

#include "eas_types.h"
#include "eas_audioconst.h"

/*------------------------------------
 * defines
 *------------------------------------
*/

/*
CIRCULAR() calculates the array index using modulo arithmetic.
The "trick" is that modulo arithmetic is simplified by masking
the effective address where the mask is (2^n)-1. This only works
if the buffer size is a power of two.
*/
#define CIRCULAR(base,offset,size) (EAS_U32)(               \
            (                                               \
                ((EAS_I32)(base)) + ((EAS_I32)(offset))     \
            )                                               \
            & size                                          \
                                            )

/* reverb parameters are updated every 2^(REVERB_UPDATE_PERIOD_IN_BITS) samples */
#if defined (_SAMPLE_RATE_8000)

#define REVERB_UPDATE_PERIOD_IN_BITS        5
#define REVERB_BUFFER_SIZE_IN_SAMPLES       2048

#elif defined (_SAMPLE_RATE_16000)

#define REVERB_UPDATE_PERIOD_IN_BITS        6
#define REVERB_BUFFER_SIZE_IN_SAMPLES       4096

#elif defined (_SAMPLE_RATE_22050)

#define REVERB_UPDATE_PERIOD_IN_BITS        7
#define REVERB_BUFFER_SIZE_IN_SAMPLES       4096

#elif defined (_SAMPLE_RATE_32000)

#define REVERB_UPDATE_PERIOD_IN_BITS        7
#define REVERB_BUFFER_SIZE_IN_SAMPLES       8192

#elif defined (_SAMPLE_RATE_44100)

#define REVERB_UPDATE_PERIOD_IN_BITS        8
#define REVERB_BUFFER_SIZE_IN_SAMPLES       8192

#elif defined (_SAMPLE_RATE_48000)

#define REVERB_UPDATE_PERIOD_IN_BITS        8
#define REVERB_BUFFER_SIZE_IN_SAMPLES       8192

#endif

// Define a mask for circular addressing, so that array index
// can wraparound and stay in array boundary of 0, 1, ..., (buffer size -1)
// The buffer size MUST be a power of two
#define REVERB_BUFFER_MASK                  (REVERB_BUFFER_SIZE_IN_SAMPLES -1)

#define REVERB_MAX_ROOM_TYPE            4   // any room numbers larger than this are invalid
#define REVERB_MAX_NUM_REFLECTIONS      5   // max num reflections per channel

/* synth parameters are updated every SYNTH_UPDATE_PERIOD_IN_SAMPLES */
#define REVERB_UPDATE_PERIOD_IN_SAMPLES (EAS_I32)(0x1L << REVERB_UPDATE_PERIOD_IN_BITS)

/*
calculate the update counter by bitwise ANDING with this value to
generate a 2^n modulo value
*/
#define REVERB_MODULO_UPDATE_PERIOD_IN_SAMPLES  (EAS_I32)(REVERB_UPDATE_PERIOD_IN_SAMPLES -1)

/* synth parameters are updated every SYNTH_UPDATE_PERIOD_IN_SECONDS seconds */
#define REVERB_UPDATE_PERIOD_IN_SECONDS     (REVERB_UPDATE_PERIOD_IN_SAMPLES / _OUTPUT_SAMPLE_RATE)

// xfade parameters
#define REVERB_XFADE_PERIOD_IN_SECONDS      (100.0 / 1000.0)        // xfade once every this many seconds

#define REVERB_XFADE_PERIOD_IN_SAMPLES      (REVERB_XFADE_PERIOD_IN_SECONDS * _OUTPUT_SAMPLE_RATE)

#define REVERB_XFADE_PHASE_INCREMENT    (EAS_I16)(65536 / ((EAS_I16)REVERB_XFADE_PERIOD_IN_SAMPLES/(EAS_I16)REVERB_UPDATE_PERIOD_IN_SAMPLES))

/**********/
/* the entire synth uses various flags in a bit field */

/* if flag is set, synth reset has been requested */
#define REVERB_FLAG_RESET_IS_REQUESTED          0x01    /* bit 0 */
#define MASK_REVERB_RESET_IS_REQUESTED          0x01
#define MASK_REVERB_RESET_IS_NOT_REQUESTED      (EAS_U32)(~MASK_REVERB_RESET_IS_REQUESTED)

/*
by default, we always want to update ALL channel parameters
when we reset the synth (e.g., during GM ON)
*/
#define DEFAULT_REVERB_FLAGS                    0x0

/* coefficients for generating sin, cos */
#define REVERB_PAN_G2   4294940151          /* -0.82842712474619 = 2 - 4/sqrt(2) */
/*
EAS_I32 nPanG1 = +1.0 for sin
EAS_I32 nPanG1 = -1.0 for cos
*/
#define REVERB_PAN_G0   23170               /* 0.707106781186547 = 1/sqrt(2) */

/*************************************************************/
// define the input injection points
#define GUARD               5                       // safety guard of this many samples

#define MAX_AP_TIME         (double) (20.0/1000.0)  // delay time in milliseconds
#define MAX_DELAY_TIME      (double) (65.0/1000.0)  // delay time in milliseconds

#define MAX_AP_SAMPLES      (int)(((double) MAX_AP_TIME)    * ((double) _OUTPUT_SAMPLE_RATE))
#define MAX_DELAY_SAMPLES   (int)(((double) MAX_DELAY_TIME) * ((double) _OUTPUT_SAMPLE_RATE))

#define AP0_IN              0
#define AP1_IN              (AP0_IN     + MAX_AP_SAMPLES    + GUARD)
#define DELAY0_IN           (AP1_IN     + MAX_AP_SAMPLES    + GUARD)
#define DELAY1_IN           (DELAY0_IN  + MAX_DELAY_SAMPLES + GUARD)

// Define the max offsets for the end points of each section
// i.e., we don't expect a given section's taps to go beyond
// the following limits
#define AP0_OUT             (AP0_IN     + MAX_AP_SAMPLES    -1)
#define AP1_OUT             (AP1_IN     + MAX_AP_SAMPLES    -1)
#define DELAY0_OUT          (DELAY0_IN  + MAX_DELAY_SAMPLES -1)
#define DELAY1_OUT          (DELAY1_IN  + MAX_DELAY_SAMPLES -1)

#define REVERB_DEFAULT_ROOM_NUMBER      1       // default preset number
#define DEFAULT_AP0_LENGTH              (int)(((double) (17.0/1000.0))  * ((double) _OUTPUT_SAMPLE_RATE))
#define DEFAULT_AP0_GAIN                19400
#define DEFAULT_AP1_LENGTH              (int)(((double) (16.5/1000.0))  * ((double) _OUTPUT_SAMPLE_RATE))
#define DEFAULT_AP1_GAIN                -19400

#define EAS_REVERB_WET_MAX              32767
#define EAS_REVERB_WET_MIN              0
#define EAS_REVERB_DRY_MAX              32767
#define EAS_REVERB_DRY_MIN              0

/* parameters for each allpass */
typedef struct
{
    EAS_U16             m_zApOut;       // delay offset for ap out

    EAS_I16             m_nApGain;      // gain for ap

    EAS_U16             m_zApIn;        // delay offset for ap in

} S_ALLPASS_OBJECT;


/* parameters for each allpass */
typedef struct
{
    EAS_PCM             m_zLpf;                     // actual state variable, not a length

    EAS_I16             m_nLpfFwd;                  // lpf forward gain

    EAS_I16             m_nLpfFbk;                  // lpf feedback gain

    EAS_U16             m_zDelay[REVERB_MAX_NUM_REFLECTIONS];   // delay offset for ap out

    EAS_I16             m_nGain[REVERB_MAX_NUM_REFLECTIONS];    // gain for ap

} S_EARLY_REFLECTION_OBJECT;

//demo
typedef struct
{
    EAS_I16             m_nLpfFbk;
    EAS_I16             m_nLpfFwd;

    EAS_I16             m_nEarly;
    EAS_I16             m_nWet;
    EAS_I16             m_nDry;

    EAS_I16             m_nEarlyL_LpfFbk;
    EAS_I16             m_nEarlyL_LpfFwd;

    EAS_I16             m_nEarlyL_Delay0; //8
    EAS_I16             m_nEarlyL_Gain0;
    EAS_I16             m_nEarlyL_Delay1;
    EAS_I16             m_nEarlyL_Gain1;
    EAS_I16             m_nEarlyL_Delay2;
    EAS_I16             m_nEarlyL_Gain2;
    EAS_I16             m_nEarlyL_Delay3;
    EAS_I16             m_nEarlyL_Gain3;
    EAS_I16             m_nEarlyL_Delay4;
    EAS_I16             m_nEarlyL_Gain4;

    EAS_I16             m_nEarlyR_Delay0; //18
    EAS_I16             m_nEarlyR_Gain0;
    EAS_I16             m_nEarlyR_Delay1;
    EAS_I16             m_nEarlyR_Gain1;
    EAS_I16             m_nEarlyR_Delay2;
    EAS_I16             m_nEarlyR_Gain2;
    EAS_I16             m_nEarlyR_Delay3;
    EAS_I16             m_nEarlyR_Gain3;
    EAS_I16             m_nEarlyR_Delay4;
    EAS_I16             m_nEarlyR_Gain4;

    EAS_U16             m_nMaxExcursion; //28
    EAS_I16             m_nXfadeInterval;

    EAS_I16             m_nAp0_ApGain; //30
    EAS_I16             m_nAp0_ApOut;
    EAS_I16             m_nAp1_ApGain;
    EAS_I16             m_nAp1_ApOut;

    EAS_I16             m_rfu4;
    EAS_I16             m_rfu5;
    EAS_I16             m_rfu6;
    EAS_I16             m_rfu7;
    EAS_I16             m_rfu8;
    EAS_I16             m_rfu9;
    EAS_I16             m_rfu10; //43

} S_REVERB_PRESET;

typedef struct
{
    S_REVERB_PRESET     m_sPreset[REVERB_MAX_ROOM_TYPE];    //array of presets

} S_REVERB_PRESET_BANK;

/* parameters for each reverb */
typedef struct
{
    /* controls entire reverb playback volume */
    /* to conserve memory, use the MSB and ignore the LSB */
    EAS_U8              m_nMasterVolume;

    /* update counter keeps track of when synth params need updating */
    /* only needs to be as large as REVERB_UPDATE_PERIOD_IN_SAMPLES */
    EAS_I16             m_nUpdateCounter;

    EAS_U16             m_nMinSamplesToAdd;         /* ComputeReverb() generates this many samples */

    EAS_U8              m_nFlags;                   /* misc flags/bit fields */

    EAS_PCM             *m_pOutputBuffer;
    EAS_PCM             *m_pInputBuffer;

    EAS_U16             m_nNumSamplesInOutputBuffer;
    EAS_U16             m_nNumSamplesInInputBuffer;

    EAS_U16             m_nNumInputSamplesRead;     // if m_nNumInputSamplesRead >= NumSamplesInInputBuffer
                                                    // then get a new input buffer
    EAS_PCM             *m_pNextInputSample;

    EAS_U16             m_nBaseIndex;                                   // base index for circular buffer

    // reverb delay line offsets, allpass parameters, etc:

    EAS_PCM             m_nRevOutFbkR;              // combine feedback reverb right out with dry left in

    S_ALLPASS_OBJECT    m_sAp0;                     // allpass 0 (left channel)

    EAS_U16             m_zD0In;                    // delay offset for delay line D0 in

    EAS_PCM             m_nRevOutFbkL;              // combine feedback reverb left out with dry right in

    S_ALLPASS_OBJECT    m_sAp1;                     // allpass 1 (right channel)

    EAS_U16             m_zD1In;                    // delay offset for delay line D1 in

    // delay output taps, notice criss cross order
    EAS_U16             m_zD0Self;                  // self feeds forward d0 --> d0

    EAS_U16             m_zD1Cross;                 // cross feeds across d1 --> d0

    EAS_PCM             m_zLpf0;                    // actual state variable, not a length

    EAS_U16             m_zD1Self;                  // self feeds forward d1 --> d1

    EAS_U16             m_zD0Cross;                 // cross feeds across d0 --> d1

    EAS_PCM             m_zLpf1;                    // actual state variable, not a length

    EAS_I16             m_nSin;                     // gain for self taps

    EAS_I16             m_nCos;                     // gain for cross taps

    EAS_I16             m_nSinIncrement;            // increment for gain

    EAS_I16             m_nCosIncrement;            // increment for gain

    EAS_I16             m_nLpfFwd;                  // lpf forward gain (includes scaling for mixer)

    EAS_I16             m_nLpfFbk;                  // lpf feedback gain

    EAS_U16             m_nXfadeInterval;           // update/xfade after this many samples

    EAS_U16             m_nXfadeCounter;            // keep track of when to xfade

    EAS_I16             m_nPhase;                   // -1 <= m_nPhase < 1
                                                    // but during sin,cos calculations
                                                    // use m_nPhase/2

    EAS_I16             m_nPhaseIncrement;          // add this to m_nPhase each frame

    EAS_I16             m_nNoise;                   // random noise sample

    EAS_U16             m_nMaxExcursion;            // the taps can excurse +/- this amount

    EAS_BOOL            m_bUseNoise;                // if EAS_TRUE, use noise as input signal

    EAS_BOOL            m_bBypass;                  // if EAS_TRUE, then bypass reverb and copy input to output

    EAS_I16             m_nCurrentRoom;             // preset number for current room

    EAS_I16             m_nNextRoom;                // preset number for next room

    EAS_I16             m_nWet;                     // gain for wet (processed) signal

    EAS_I16             m_nDry;                     // gain for dry (unprocessed) signal

    EAS_I16             m_nEarly;                   // gain for early (widen) signal

    S_EARLY_REFLECTION_OBJECT   m_sEarlyL;          // left channel early reflections
    S_EARLY_REFLECTION_OBJECT   m_sEarlyR;          // right channel early reflections

    EAS_PCM             m_nDelayLine[REVERB_BUFFER_SIZE_IN_SAMPLES];    // one large delay line for all reverb elements

    S_REVERB_PRESET     pPreset;

    S_REVERB_PRESET_BANK    m_sPreset;

} S_REVERB_OBJECT;


/*------------------------------------
 * prototypes
 *------------------------------------
*/

/*----------------------------------------------------------------------------
 * ReverbUpdateXfade
 *----------------------------------------------------------------------------
 * Purpose:
 * Update the xfade parameters as required
 *
 * Inputs:
 * nNumSamplesToAdd - number of samples to write to buffer
 *
 * Outputs:
 *
 *
 * Side Effects:
 * - xfade parameters will be changed
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT ReverbUpdateXfade(S_REVERB_OBJECT* pReverbData, EAS_INT nNumSamplesToAdd);

/*----------------------------------------------------------------------------
 * ReverbCalculateNoise
 *----------------------------------------------------------------------------
 * Purpose:
 * Calculate a noise sample and limit its value
 *
 * Inputs:
 * nMaxExcursion - noise value is limited to this value
 * pnNoise - return new noise sample in this (not limited)
 *
 * Outputs:
 * new limited noise value
 *
 * Side Effects:
 * - *pnNoise noise value is updated
 *
 *----------------------------------------------------------------------------
*/
static EAS_U16 ReverbCalculateNoise(EAS_U16 nMaxExcursion, EAS_I16 *pnNoise);

/*----------------------------------------------------------------------------
 * ReverbCalculateSinCos
 *----------------------------------------------------------------------------
 * Purpose:
 * Calculate a new sin and cosine value based on the given phase
 *
 * Inputs:
 * nPhase   - phase angle
 * pnSin    - input old value, output new value
 * pnCos    - input old value, output new value
 *
 * Outputs:
 *
 * Side Effects:
 * - *pnSin, *pnCos are updated
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT ReverbCalculateSinCos(EAS_I16 nPhase, EAS_I16 *pnSin, EAS_I16 *pnCos);

/*----------------------------------------------------------------------------
 * Reverb
 *----------------------------------------------------------------------------
 * Purpose:
 * apply reverb to the given signal
 *
 * Inputs:
 * nNu
 * pnSin    - input old value, output new value
 * pnCos    - input old value, output new value
 *
 * Outputs:
 * number of samples actually reverberated
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT Reverb(S_REVERB_OBJECT* pReverbData, EAS_INT nNumSamplesToAdd, EAS_PCM *pOutputBuffer, EAS_PCM *pInputBuffer);

/*----------------------------------------------------------------------------
 * ReverbReadInPresets()
 *----------------------------------------------------------------------------
 * Purpose: sets global reverb preset bank to defaults
 *
 * Inputs:
 *
 * Outputs:
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT ReverbReadInPresets(S_REVERB_OBJECT* pReverbData);


/*----------------------------------------------------------------------------
 * ReverbUpdateRoom
 *----------------------------------------------------------------------------
 * Purpose:
 * Update the room's preset parameters as required
 *
 * Inputs:
 *
 * Outputs:
 *
 *
 * Side Effects:
 * - reverb paramters (fbk, fwd, etc) will be changed
 * - m_nCurrentRoom := m_nNextRoom
 *----------------------------------------------------------------------------
*/
static EAS_RESULT ReverbUpdateRoom(S_REVERB_OBJECT* pReverbData);

#endif /* #ifndef _EAS_REVERBDATA_H */


