/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * 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.
 */

package com.example.android.common.midi.synth;

/**
 * Very simple Attack, Decay, Sustain, Release envelope with linear ramps.
 *
 * Times are in seconds.
 */
public class EnvelopeADSR extends SynthUnit {
    private static final int IDLE = 0;
    private static final int ATTACK = 1;
    private static final int DECAY = 2;
    private static final int SUSTAIN = 3;
    private static final int RELEASE = 4;
    private static final int FINISHED = 5;
    private static final float MIN_TIME = 0.001f;

    private float mAttackRate;
    private float mRreleaseRate;
    private float mSustainLevel;
    private float mDecayRate;
    private float mCurrent;
    private int mSstate = IDLE;

    public EnvelopeADSR() {
        setAttackTime(0.003f);
        setDecayTime(0.08f);
        setSustainLevel(0.3f);
        setReleaseTime(1.0f);
    }

    public void setAttackTime(float time) {
        if (time < MIN_TIME)
            time = MIN_TIME;
        mAttackRate = 1.0f / (SynthEngine.FRAME_RATE * time);
    }

    public void setDecayTime(float time) {
        if (time < MIN_TIME)
            time = MIN_TIME;
        mDecayRate = 1.0f / (SynthEngine.FRAME_RATE * time);
    }

    public void setSustainLevel(float level) {
        if (level < 0.0f)
            level = 0.0f;
        mSustainLevel = level;
    }

    public void setReleaseTime(float time) {
        if (time < MIN_TIME)
            time = MIN_TIME;
        mRreleaseRate = 1.0f / (SynthEngine.FRAME_RATE * time);
    }

    public void on() {
        mSstate = ATTACK;
    }

    public void off() {
        mSstate = RELEASE;
    }

    @Override
    public float render() {
        switch (mSstate) {
        case ATTACK:
            mCurrent += mAttackRate;
            if (mCurrent > 1.0f) {
                mCurrent = 1.0f;
                mSstate = DECAY;
            }
            break;
        case DECAY:
            mCurrent -= mDecayRate;
            if (mCurrent < mSustainLevel) {
                mCurrent = mSustainLevel;
                mSstate = SUSTAIN;
            }
            break;
        case RELEASE:
            mCurrent -= mRreleaseRate;
            if (mCurrent < 0.0f) {
                mCurrent = 0.0f;
                mSstate = FINISHED;
            }
            break;
        }
        return mCurrent;
    }

    public boolean isDone() {
        return mSstate == FINISHED;
    }

}