/*  Stereo plate reverb for Teensy 4
 *
 *  Author: Piotr Zapart
 *          www.hexefx.com
 *
 * Copyright (c) 2020 by Piotr Zapart
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***
 * Algorithm based on plate reverbs developed for SpinSemi FV-1 DSP chip
 * 
 * Allpass + modulated delay line based lush plate reverb
 * 
 * Input parameters are float in range 0.0 to 1.0:
 * 
 * size - reverb time
 * hidamp - hi frequency loss in the reverb tail
 * lodamp - low frequency loss in the reverb tail
 * lowpass - output/master lowpass filter, useful for darkening the reverb sound 
 * diffusion - lower settings will make the reverb tail more "echoey", optimal value 0.65
 * 
 */


#ifndef _EFFECT_PLATERVBSTEREO_H
#define _EFFECT_PLATERVBSTEREO_H

#include <Arduino.h>
#include "Audio.h"
#include "AudioStream.h"
#include "arm_math.h"


// if uncommented will place all the buffers in the DMAMEM section ofd the memory
// works with single instance of the reverb only
#define REVERB_USE_DMAMEM

/***
 * Loop delay modulation: comment/uncomment to switch sin/cos 
 * modulation for the 1st or 2nd tap, 3rd tap is always modulated
 * more modulation means more chorus type sounding reverb tail
 */
//#define TAP1_MODULATED
#define TAP2_MODULATED

class AudioEffectPlateReverb : public AudioStream
{
public:
    AudioEffectPlateReverb();
    virtual void update();

    void size(float n)
    {
        n = constrain(n, 0.0f, 1.0f);
        n = map (n, 0.0f, 1.0f, 0.2f, rv_time_k_max);
        float32_t attn = 0.5f * map(n, 0.0f, rv_time_k_max, 0.5f, 1.0f);
        __disable_irq();
        rv_time_k = n;
        input_attn = attn;
         __enable_irq();
    }

    void hidamp(float n)
    {
        n = constrain(n, 0.0f, 1.0f);
        __disable_irq();
        lp_hidamp_k = 1.0f - n;
         __enable_irq();
    }
    
    void lodamp(float n)
    {
        n = constrain(n, 0.0f, 1.0f);
        __disable_irq();
        lp_lodamp_k = -n;
        rv_time_scaler = 1.0f - n * 0.12f;        // limit the max reverb time, otherwise it will clip
         __enable_irq();
    }

    void lowpass(float n)
    {
        n = constrain(n, 0.0f, 1.0f);
        n = map(n*n*n, 0.0f, 1.0f, 0.05f, 1.0f);
        master_lowpass_f = n;
    }
    
    void diffusion(float n)
    {
        n = constrain(n, 0.0f, 1.0f);
        n = map(n, 0.0f, 1.0f, 0.005f, 0.65f);
        __disable_irq();
        in_allp_k = n;
        loop_allp_k = n;
         __enable_irq();
    }

    float32_t get_size(void) {return rv_time_k;}
    bool get_bypass(void) {return bypass;}
    void set_bypass(bool state) {bypass = state;};
    void tgl_bypass(void) {bypass ^=1;}
private:
    bool bypass = false;
    audio_block_t *inputQueueArray[2];
#ifndef REVERB_USE_DMAMEM
    float32_t input_blockL[AUDIO_BLOCK_SAMPLES];
    float32_t input_blockR[AUDIO_BLOCK_SAMPLES];
#endif
    float32_t input_attn;

    float32_t in_allp_k; // input allpass coeff (default 0.6)
#ifndef REVERB_USE_DMAMEM
    float32_t in_allp1_bufL[224]; // input allpass buffers
    float32_t in_allp2_bufL[420];
    float32_t in_allp3_bufL[856];
    float32_t in_allp4_bufL[1089];
#endif
    uint16_t in_allp1_idxL;
    uint16_t in_allp2_idxL;
    uint16_t in_allp3_idxL;
    uint16_t in_allp4_idxL;
    float32_t in_allp_out_L;    // L allpass chain output
#ifndef REVERB_USE_DMAMEM
    float32_t in_allp1_bufR[156]; // input allpass buffers
    float32_t in_allp2_bufR[520];
    float32_t in_allp3_bufR[956];
    float32_t in_allp4_bufR[1289];
#endif
    uint16_t in_allp1_idxR;
    uint16_t in_allp2_idxR;
    uint16_t in_allp3_idxR;
    uint16_t in_allp4_idxR;
    float32_t in_allp_out_R;    // R allpass chain output
#ifndef REVERB_USE_DMAMEM
    float32_t lp_allp1_buf[2303]; // loop allpass buffers
    float32_t lp_allp2_buf[2905];
    float32_t lp_allp3_buf[3175];
    float32_t lp_allp4_buf[2398];
#endif
    uint16_t lp_allp1_idx;
    uint16_t lp_allp2_idx;
    uint16_t lp_allp3_idx;
    uint16_t lp_allp4_idx;
    float32_t loop_allp_k;         // loop allpass coeff (default 0.6)
    float32_t lp_allp_out;
#ifndef REVERB_USE_DMAMEM
    float32_t lp_dly1_buf[3423];
    float32_t lp_dly2_buf[4589];
    float32_t lp_dly3_buf[4365];
    float32_t lp_dly4_buf[3698];
#endif
    uint16_t lp_dly1_idx;
    uint16_t lp_dly2_idx;
    uint16_t lp_dly3_idx;
    uint16_t lp_dly4_idx;

    const uint16_t lp_dly1_offset_L = 201;
    const uint16_t lp_dly2_offset_L = 145;
    const uint16_t lp_dly3_offset_L = 1897;
    const uint16_t lp_dly4_offset_L = 280;

    const uint16_t lp_dly1_offset_R = 1897;
    const uint16_t lp_dly2_offset_R = 1245;
    const uint16_t lp_dly3_offset_R = 487;
    const uint16_t lp_dly4_offset_R = 780;  

    float32_t lp_hidamp_k;       // loop high band damping coeff
    float32_t lp_lodamp_k;       // loop low baand damping coeff

    float32_t lpf1;             // lowpass filters
    float32_t lpf2;
    float32_t lpf3;
    float32_t lpf4;

    float32_t hpf1;             // highpass filters
    float32_t hpf2;
    float32_t hpf3;
    float32_t hpf4;

    float32_t lp_lowpass_f;      // loop lowpass scaled frequency
    float32_t lp_hipass_f;       // loop highpass scaled frequency 

    float32_t master_lowpass_f;
    float32_t master_lowpass_l;
    float32_t master_lowpass_r;

    const float32_t rv_time_k_max = 0.95f;
    float32_t rv_time_k;         // reverb time coeff
    float32_t rv_time_scaler;    // with high lodamp settings lower the max reverb time to avoid clipping

    uint32_t lfo1_phase_acc;     // LFO 1
    uint32_t lfo1_adder;

    uint32_t lfo2_phase_acc;    // LFO 2
    uint32_t lfo2_adder;
};

#endif // _EFFECT_PLATEREV_H