/*
 * Reverb.h
 * --------
 * Purpose: Mixing code for reverb.
 * Notes  : Ugh... This should really be removed at some point.
 * Authors: Olivier Lapicque
 *          OpenMPT Devs
 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
 */

#pragma once

#include "BuildSettings.h"

#ifndef NO_REVERB

#include "../soundlib/Mixer.h"	// For MIXBUFFERSIZE

OPENMPT_NAMESPACE_BEGIN

////////////////////////////////////////////////////////////////////////
// Reverberation

#define NUM_REVERBTYPES			29

mpt::ustring GetReverbPresetName(uint32 nPreset);

/////////////////////////////////////////////////////////////////////////////
//
// SW Reverb structures
//

// Length-1 (in samples) of the reflections delay buffer: 32K, 371ms@22kHz
#define SNDMIX_REFLECTIONS_DELAY_MASK	0x1fff
#define SNDMIX_PREDIFFUSION_DELAY_MASK	0x7f	// 128 samples
#define SNDMIX_REVERB_DELAY_MASK		0xfff	// 4K samples (92ms @ 44kHz)

union LR16
{
	struct { int16 l, r; } c;
	int32 lr;
};

struct SWRvbReflection
{
	uint32 Delay, DelayDest;
	LR16   Gains[2];	// g_ll, g_rl, g_lr, g_rr
};

struct SWRvbRefDelay
{
	uint32 nDelayPos, nPreDifPos, nRefOutPos;
	int32  lMasterGain;			// reflections linear master gain
	LR16   nCoeffs;				// room low-pass coefficients
	LR16   History;				// room low-pass history
	LR16   nPreDifCoeffs;		// prediffusion coefficients
	LR16   ReflectionsGain;		// master reflections gain
	SWRvbReflection Reflections[8];	// Up to 8 SW Reflections
	LR16   RefDelayBuffer[SNDMIX_REFLECTIONS_DELAY_MASK + 1]; // reflections delay buffer
	LR16   PreDifBuffer[SNDMIX_PREDIFFUSION_DELAY_MASK + 1]; // pre-diffusion
	LR16   RefOut[SNDMIX_REVERB_DELAY_MASK + 1]; // stereo output of reflections
};

struct SNDMIX_REVERB_PROPERTIES;


// Late reverberation
// Tank diffusers lengths
#define RVBDIF1L_LEN		(149*2)	// 6.8ms
#define RVBDIF1R_LEN		(223*2)	// 10.1ms
#define RVBDIF2L_LEN		(421*2)	// 19.1ms
#define RVBDIF2R_LEN		(647*2)	// 29.3ms
// Tank delay lines lengths
#define RVBDLY1L_LEN		(683*2)	// 30.9ms
#define RVBDLY1R_LEN	    (811*2) // 36.7ms
#define RVBDLY2L_LEN		(773*2)	// 35.1ms
#define RVBDLY2R_LEN	    (1013*2) // 45.9ms
// Tank delay lines mask
#define RVBDLY_MASK			2047

// Min/Max reflections delay
#define RVBMINREFDELAY		96		// 96 samples
#define RVBMAXREFDELAY		7500	// 7500 samples
// Min/Max reverb delay
#define RVBMINRVBDELAY		128		// 256 samples (11.6ms @ 22kHz)
#define RVBMAXRVBDELAY		3800	// 1900 samples (86ms @ 24kHz)

struct SWLateReverb
{
	uint32 nReverbDelay;		// Reverb delay (in samples)
	uint32 nDelayPos;			// Delay line position
	LR16   nDifCoeffs[2];		// Reverb diffusion
	LR16   nDecayDC[2];			// Reverb DC decay
	LR16   nDecayLP[2];			// Reverb HF decay
	LR16   LPHistory[2];		// Low-pass history
	LR16   Dif2InGains[2];		// 2nd diffuser input gains
	LR16   RvbOutGains[2];		// 4x2 Reverb output gains
	int32  lMasterGain;			// late reverb master gain
	int32  lDummyAlign;
	// Tank Delay lines
	LR16   Diffusion1[RVBDLY_MASK + 1];	// {dif1_l, dif1_r}
	LR16   Diffusion2[RVBDLY_MASK + 1];	// {dif2_l, dif2_r}
	LR16   Delay1[RVBDLY_MASK + 1];		// {dly1_l, dly1_r}
	LR16   Delay2[RVBDLY_MASK + 1];		// {dly2_l, dly2_r}
};

#define ENVIRONMENT_NUMREFLECTIONS		8

struct EnvironmentReflection
{
	int16  GainLL, GainRR, GainLR, GainRL;	// +/- 32K scale
	uint32 Delay;							// In samples
};

struct EnvironmentReverb
{
	int32  ReverbLevel;			// Late reverb gain (mB)
	int32  ReflectionsLevel;	// Master reflections gain (mB)
	int32  RoomHF;				// Room gain HF (mB)
	uint32 ReverbDecay;			// Reverb tank decay (0-7fff scale)
	int32  PreDiffusion;		// Reverb pre-diffusion amount (+/- 32K scale)
	int32  TankDiffusion;		// Reverb tank diffusion (+/- 32K scale)
	uint32 ReverbDelay;			// Reverb delay (in samples)
	float  flReverbDamping;		// HF tank gain [0.0, 1.0]
	int32  ReverbDecaySamples;	// Reverb decay time (in samples)
	EnvironmentReflection Reflections[ENVIRONMENT_NUMREFLECTIONS];
};


class CReverbSettings
{
public:
	uint32 m_nReverbDepth = 8; // 50%
	uint32 m_nReverbType = 0;
};


class CReverb
{
public:
	CReverbSettings m_Settings;

	// Shared reverb state
private:
	mixsample_t MixReverbBuffer[MIXBUFFERSIZE * 2];
public:
	mixsample_t gnRvbROfsVol = 0, gnRvbLOfsVol = 0;

private:
	const SNDMIX_REVERB_PROPERTIES *m_currentPreset = nullptr;

	uint32 gnReverbSend = 0;

	uint32 gnReverbSamples = 0;
	uint32 gnReverbDecaySamples = 0;

	// Internal reverb state
	bool g_bLastInPresent = 0;
	bool g_bLastOutPresent = 0;
	int g_nLastRvbIn_xl = 0;
	int g_nLastRvbIn_xr = 0;
	int g_nLastRvbIn_yl = 0;
	int g_nLastRvbIn_yr = 0;
	int g_nLastRvbOut_xl = 0;
	int g_nLastRvbOut_xr = 0;
	int32 gnDCRRvb_Y1[2] = { 0, 0 };
	int32 gnDCRRvb_X1[2] = { 0, 0 };

	// Reverb mix buffers
	SWRvbRefDelay g_RefDelay;
	SWLateReverb g_LateReverb;

public:
	CReverb();
public:
	void Initialize(bool bReset, uint32 MixingFreq);

	// can be called multiple times or never (if no data is sent to reverb)
	mixsample_t *GetReverbSendBuffer(uint32 nSamples);

	// call once after all data has been sent.
	void Process(mixsample_t *MixSoundBuffer, uint32 nSamples);

private:
	void Shutdown();
	// Pre/Post resampling and filtering
	uint32 ReverbProcessPreFiltering1x(int32 *pWet, uint32 nSamples);
	uint32 ReverbProcessPreFiltering2x(int32 *pWet, uint32 nSamples);
	void ReverbProcessPostFiltering1x(const int32 *pRvb, int32 *pDry, uint32 nSamples);
	void ReverbProcessPostFiltering2x(const int32 *pRvb, int32 *pDry, uint32 nSamples);
	void ReverbDCRemoval(int32 *pBuffer, uint32 nSamples);
	void ReverbDryMix(int32 *pDry, int32 *pWet, int lDryVol, uint32 nSamples);
	// Process pre-diffusion and pre-delay
	static void ProcessPreDelay(SWRvbRefDelay *pPreDelay, const int32 *pIn, uint32 nSamples);
	// Process reflections
	static void ProcessReflections(SWRvbRefDelay *pPreDelay, LR16 *pRefOut, int32 *pMixOut, uint32 nSamples);
	// Process Late Reverb (SW Reflections): stereo reflections output, 32-bit reverb output, SW reverb gain
	static void ProcessLateReverb(SWLateReverb *pReverb, LR16 *pRefOut, int32 *pMixOut, uint32 nSamples);
};


/////////////////////////////////////////////////////////////////////////////////
//
// I3DL2 reverb presets
//

#define SNDMIX_REVERB_PRESET_DEFAULT \
-10000,    0, 1.00f,0.50f,-10000,0.020f,-10000,0.040f,100.0f,100.0f

#define SNDMIX_REVERB_PRESET_GENERIC \
 -1000, -100, 1.49f,0.83f, -2602,0.007f,   200,0.011f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_PADDEDCELL \
 -1000,-6000, 0.17f,0.10f, -1204,0.001f,   207,0.002f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_ROOM \
 -1000, -454, 0.40f,0.83f, -1646,0.002f,    53,0.003f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_BATHROOM \
 -1000,-1200, 1.49f,0.54f,  -370,0.007f,  1030,0.011f,100.0f, 60.0f
#define SNDMIX_REVERB_PRESET_LIVINGROOM \
 -1000,-6000, 0.50f,0.10f, -1376,0.003f, -1104,0.004f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_STONEROOM \
 -1000, -300, 2.31f,0.64f,  -711,0.012f,    83,0.017f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_AUDITORIUM \
 -1000, -476, 4.32f,0.59f,  -789,0.020f,  -289,0.030f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_CONCERTHALL \
 -1000, -500, 3.92f,0.70f, -1230,0.020f,    -2,0.029f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_CAVE \
 -1000,    0, 2.91f,1.30f,  -602,0.015f,  -302,0.022f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_ARENA \
 -1000, -698, 7.24f,0.33f, -1166,0.020f,    16,0.030f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_HANGAR \
 -1000,-1000,10.05f,0.23f,  -602,0.020f,   198,0.030f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_CARPETEDHALLWAY \
 -1000,-4000, 0.30f,0.10f, -1831,0.002f, -1630,0.030f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_HALLWAY \
 -1000, -300, 1.49f,0.59f, -1219,0.007f,   441,0.011f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_STONECORRIDOR \
 -1000, -237, 2.70f,0.79f, -1214,0.013f,   395,0.020f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_ALLEY \
 -1000, -270, 1.49f,0.86f, -1204,0.007f,    -4,0.011f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_FOREST \
 -1000,-3300, 1.49f,0.54f, -2560,0.162f,  -613,0.088f, 79.0f,100.0f
#define SNDMIX_REVERB_PRESET_CITY \
 -1000, -800, 1.49f,0.67f, -2273,0.007f, -2217,0.011f, 50.0f,100.0f
#define SNDMIX_REVERB_PRESET_MOUNTAINS \
 -1000,-2500, 1.49f,0.21f, -2780,0.300f, -2014,0.100f, 27.0f,100.0f
#define SNDMIX_REVERB_PRESET_QUARRY \
 -1000,-1000, 1.49f,0.83f,-10000,0.061f,   500,0.025f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_PLAIN \
 -1000,-2000, 1.49f,0.50f, -2466,0.179f, -2514,0.100f, 21.0f,100.0f
#define SNDMIX_REVERB_PRESET_PARKINGLOT \
 -1000,    0, 1.65f,1.50f, -1363,0.008f, -1153,0.012f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_SEWERPIPE \
 -1000,-1000, 2.81f,0.14f,   429,0.014f,   648,0.021f, 80.0f, 60.0f
#define SNDMIX_REVERB_PRESET_UNDERWATER \
 -1000,-4000, 1.49f,0.10f,  -449,0.007f,  1700,0.011f,100.0f,100.0f

// Examples simulating General MIDI 2'musical' reverb presets
//
// Name  (Decay time)  Description
//
// Small Room  (1.1s)  A small size room with a length of 5m or so.
// Medium Room (1.3s)  A medium size room with a length of 10m or so.
// Large Room  (1.5s)  A large size room suitable for live performances.
// Medium Hall (1.8s)  A medium size concert hall.
// Large Hall  (1.8s)  A large size concert hall suitable for a full orchestra.
// Plate       (1.3s)  A plate reverb simulation.

#define SNDMIX_REVERB_PRESET_SMALLROOM \
 -1000, -600, 1.10f,0.83f,  -400,0.005f,   500,0.010f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_MEDIUMROOM \
 -1000, -600, 1.30f,0.83f, -1000,0.010f,  -200,0.020f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_LARGEROOM \
 -1000, -600, 1.50f,0.83f, -1600,0.020f, -1000,0.040f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_MEDIUMHALL \
 -1000, -600, 1.80f,0.70f, -1300,0.015f,  -800,0.030f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_LARGEHALL \
 -1000, -600, 1.80f,0.70f, -2000,0.030f, -1400,0.060f,100.0f,100.0f
#define SNDMIX_REVERB_PRESET_PLATE \
 -1000, -200, 1.30f,0.90f,     0,0.002f,     0,0.010f,100.0f, 75.0f

OPENMPT_NAMESPACE_END

#endif // NO_REVERB
