/* FluidSynth - A Software Synthesizer
 *
 * Copyright (C) 2003  Peter Hanappe and others.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA
 */

#ifndef _FLUID_ADSR_ENVELOPE_H
#define _FLUID_ADSR_ENVELOPE_H

#include "fluidsynth_priv.h"
#include "fluid_sys.h"

/*
 * envelope data
 */
struct _fluid_env_data_t {
	unsigned int count;
	fluid_real_t coeff;
	fluid_real_t increment;
	fluid_real_t min;
	fluid_real_t max;
};

/* Indices for envelope tables */
enum fluid_voice_envelope_index_t{
	FLUID_VOICE_ENVDELAY,
	FLUID_VOICE_ENVATTACK,
	FLUID_VOICE_ENVHOLD,
	FLUID_VOICE_ENVDECAY,
	FLUID_VOICE_ENVSUSTAIN,
	FLUID_VOICE_ENVRELEASE,
	FLUID_VOICE_ENVFINISHED,
	FLUID_VOICE_ENVLAST
};

typedef enum fluid_voice_envelope_index_t fluid_adsr_env_section_t;

typedef struct _fluid_adsr_env_t fluid_adsr_env_t;

struct _fluid_adsr_env_t {
	fluid_env_data_t data[FLUID_VOICE_ENVLAST];
	unsigned int count;
	int section;
	fluid_real_t val;         /* the current value of the envelope */
};

/* For performance, all functions are inlined */

static FLUID_INLINE void 
fluid_adsr_env_calc(fluid_adsr_env_t* env, int is_volenv)
{
  fluid_env_data_t* env_data;
  fluid_real_t x;

  env_data = &env->data[env->section];

  /* skip to the next section of the envelope if necessary */
  while (env->count >= env_data->count)
  {
    // If we're switching envelope stages from decay to sustain, force the value to be the end value of the previous stage
    // Hmm, should this only apply to volenv? It was so before refactoring, so keep it for now. [DH]
    if (env->section == FLUID_VOICE_ENVDECAY && is_volenv)
      env->val = env_data->min * env_data->coeff;

    env_data = &env->data[++env->section];
    env->count = 0;
  }

  /* calculate the envelope value and check for valid range */
  x = env_data->coeff * env->val + env_data->increment;

  if (x < env_data->min)
  {
    x = env_data->min;
    env->section++;
    env->count = 0;
  }
  else if (x > env_data->max)
  {
    x = env_data->max;
    env->section++;
    env->count = 0;
  }

  env->val = x;
  env->count++;
}

/* This one cannot be inlined since it is referenced in 
   the event queue */
void 
fluid_adsr_env_set_data(fluid_adsr_env_t* env,
                        fluid_adsr_env_section_t section,
                        unsigned int count,
                        fluid_real_t coeff,
                        fluid_real_t increment,
                        fluid_real_t min,
                        fluid_real_t max);

static inline void 
fluid_adsr_env_reset(fluid_adsr_env_t* env)
{
  env->count = 0;
  env->section = 0;
  env->val = 0.0f;
}

static inline fluid_real_t 
fluid_adsr_env_get_val(fluid_adsr_env_t* env)
{
  return env->val;
}

static inline void
fluid_adsr_env_set_val(fluid_adsr_env_t* env, fluid_real_t val)
{
  env->val = val;
}

static inline fluid_adsr_env_section_t
fluid_adsr_env_get_section(fluid_adsr_env_t* env)
{
  return env->section;
}

static inline void 
fluid_adsr_env_set_section(fluid_adsr_env_t* env, 
                           fluid_adsr_env_section_t section)
{
  env->section = section;
  env->count = 0;
}

/* Used for determining which voice to kill. 
   Returns max amplitude from now, and forward in time.
*/
static inline fluid_real_t
fluid_adsr_env_get_max_val(fluid_adsr_env_t* env)
{
  if (env->section > FLUID_VOICE_ENVATTACK){
    return env->val * 1000;
  } else {
    return env->data[FLUID_VOICE_ENVATTACK].max;
  }
}

#endif

