/*
 * Copyright (c) 2019 Christian Schoenebeck
 *
 * http://www.linuxsampler.org
 *
 * This file is part of LinuxSampler and released under the same terms.
 * See README file for details.
 */

#ifndef LS_LFO_H
#define LS_LFO_H

#include "../common/global.h"
#include "../common/optional.h"

namespace LinuxSampler {

    // *************** types ***************
    // *

    /** @brief Low Frequency Oscillator (public API class)
     *
     * Generalized, convenient cluster class encapsulating all the sampler's
     * Low Frequency Oscillator implementations.
     *
     * @b NOTE: This class is @b NOT used by the sampler at all! This class is
     * solely exported to the sampler's public API to allow third-party apps
     * (e.g. Gigedit) to use our LFO implementations (e.g. for visualizing an
     * LFO's wave form in an instrument editor GUI application) in a convenient
     * way and by an unified API, and exactly with same LFO wave form values
     * that would be generated by the sampler at playback time.
     *
     * @b NOTE: For sampler internal purposes use the template variant class
     * LFOCluster for efficiency reasons instead! See the discussion on
     * LFOCluster for details why.
     */
    class LFO {
    public:
        /**
         * Oscillator's fundamental function type / wave form type.
         */
        enum wave_t {
            wave_sine,
            wave_triangle,
            wave_saw,
            wave_square,
        };

        /**
         * Whether the LFO should have positive AND negative value range
         * (signed) or only a positive value range (unsigned).
         */
        enum range_type_t {
            range_signed,  ///< LFO's level will wave between -max ... +max
            range_unsigned ///< LFO's level will wave between 0 ... +max
        };

        /**
         * Defines the start level of the LFO wave within the given value range.
         */
        enum start_level_t {
            start_level_max, ///< wave starts from given maximum level
            start_level_mid, ///< wave starts from the middle of the given value range
            start_level_min  ///< wave starts from given minimum level
        };

        struct SetupOpt {
            optional<wave_t> waveType; ///< LFO's fundamental function type, that its wave form type, e.g. sine, triangle, saw, etc. (default: sine)
            optional<range_type_t> rangeType;
            optional<float> frequency; ///< frequency of the oscillator in Hz (default: 1 Hz)
            optional<float> phase; ///< optional phase displacement of the LFO function in degrees, between 0°..360° (default: 0°)
            optional<start_level_t> startLevel; ///< on which initial value level the LFO wave should start (default: start_level_mid)
            optional<uint16_t> internalDepth; ///< firm, internal oscillator's amplitude (0..1200, default: 0)
            optional<uint16_t> midiControllerDepth; ///< defines how strong the external MIDI controller has influence on the oscillator's amplitude (0..1200, default: 0)
            optional<bool> flipPolarity; ///< if true: inverts the oscillator wave vertically (default: false)
            optional<float> samplerate; ///< sample rate to be assumed by the LFO (default: 44100)
            optional<float> maxValue;
        };

        /** @brief Constructs LFO with default values.
         *
         * Initializes the LFO with default values for all LFO parameters
         * (that is for e.g. frequency, sample rate, wave form type, etc.).
         *
         * You should call setup() afterwards to actually initialize the LFO
         * with the desired parameters.
         */
        LFO();

        /** @brief Frees LFO's resources.
         *
         * De-allocates all resources previously been allocated by this LFO
         * object.
         */
        virtual ~LFO();

        /**
         * Setup the LFO with the relevant parameters (e.g. frequency, wave
         * form type, etc.).
         */
        void setup(const SetupOpt& opt);

        /**
         * Calculates exactly one sample point of the LFO wave.
         *
         * @returns next LFO level
         */
        float render();

        /**
         * Update LFO depth with a new external MIDI controller value.
         *
         * @param midiCCValue - new MIDI controller value (0..127)
         */
        void setMIDICtrlValue(uint8_t midiCCValue);

    private:
        struct LFOPriv* SELF;
    };

} // namespace LinuxSampler

#endif // LS_LFO_H
