// AudioInput.h: Audio input processing using Gstreamer.
// 
//   Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
//   Free Software Foundation, Inc.
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// 
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

#ifndef GNASH_AUDIOINPUTGST_H
#define GNASH_AUDIOINPUTGST_H

#include "gst/gst.h"
#include "AudioInput.h"
#include <string>
#include <cstdint> // for C99 int types
#include <vector>
#include <cassert>

namespace gnash {
namespace media {
namespace gst {

/// \class GnashAudio
///
/// \brief Contains information about audio input devices while enumerating
///  information about attached hardware. This class is also referred back to
///  by GnashAudioPrivate to re-access enumerated information.
class GnashAudio {
    public:
        /// \brief Accessor to retreive a the private _element variable
        ///       from the GnashAudio class which contains a pointer
        ///       to the audio source element.
        ///
        /// @return GstElement* to the audio source element
        GstElement* getElementPtr() {return _element;}
        
        /// \brief Accessor to set the private _element variable from
        ///       the GnashAudio class.
        ///
        /// @param element The GstElement pointer to the audio source element.
        void setElementPtr(GstElement* element) {_element = element;}
        
        /// \brief Accessor to get the private _devLocation variable from
        ///       the GnashAudio class.
        ///
        /// @return The _devLocation private variable from GnashAudio class.
        gchar* getDevLocation() {return _devLocation;}
        
        /// \brief Accessor to set the private _devLocation variable from
        ///       the GnashAudio class.
        ///
        /// @param l A gchar* containing the physical location of the audio
        ///       input hardware device.
        void setDevLocation(gchar *l) {_devLocation = l;}
        
        /// \brief Accessor to return the private _gstreamerSrc variable
        ///       from the GnashAudio class.
        ///
        /// @return The _gstreamerSrc variable from the GnashAudio class.
        ///        which should contain the type of the Gstreamer audio source
        ///        element (e.g. pulsesrc).
        gchar* getGstreamerSrc() {return _gstreamerSrc;}
        
        /// \brief Accessor to set the private _gstreamerSrc variable
        ///       from the GnashAudio class.
        ///
        /// @param s A gchar* containing the type of the Gstreamer source
        ///         element type (e.g. pulsesrc)
        void setGstreamerSrc(gchar *s) {_gstreamerSrc = s;}
        
        /// \brief Accessor to get the private _productName variable
        ///       from the GnashAudio class.
        ///
        /// @return A gchar* containing the audio input's hardware name
        ///       (e.g. HDA Intel).
        gchar* getProductName() {return _productName;}
        
        /// \brief Accessor to set the private _productName variable
        ///       from the GnashAudio class.
        ///
        /// @param n A gchar* to the hardware input device's hardware name
        ///         (e.g. HDA Intel).
        void setProductName(gchar *n) {_productName = n;}

        /// Constructor for the GnashAudio class.
        GnashAudio();
        
    private:
        /// \var GnashAudio::_element
        /// \brief GstElement* which points to the audio source
        ///       element.
        GstElement* _element;
        
        /// \var GnashAudio::_devLocation
        /// \brief Contains the physical location of the audio input device
        gchar* _devLocation;
        
        /// \var GnashAudio::_gstreamerSrc
        /// \brief Contains a gchar* which describes the gstreamer source
        ///       type (e.g. pulseaudiosrc or jackaudiosrc).
        gchar* _gstreamerSrc;
        
        /// \var GnashAudio::_productName
        /// \brief Contains a gchar* which describes the name of the hardware
        ///      device (e.g. Built-In Microphone or HDA Intel).
        gchar* _productName;
};
 
/// \class GnashAudioPrivate
///
/// \brief This class is initialized once a hardware input device is chosen
///   it is more robust than GnashAudio because it has additional room to store
///   important Gstreamer information (pipelines, references to elements, etc.)
class GnashAudioPrivate {
    public:
        /// \var audioSource
        /// \brief A pointer to the Gstreamer element corresponding to the 
        ///   audio source (e.g. a built-in or usb microphone).
        GstElement *audioSource;
        
        /// \var audioEnc
        /// \brief A pointer to the audio encoder element of the Gstreamer
        ///   pipeline. The only format currently supported format is vorbis
        GstElement *audioEnc;
        
        /// Constructor for the GnashAudioPrivate class.
        GnashAudioPrivate();
        
        /// \brief This function sets the private _audioDevice element in
        ///   the GnashAudioPrivate class
        /// @param d A pointer to the GnashAudio class that you want to
        ///   use for audio input.
        void setAudioDevice(GnashAudio* d) {_audioDevice = d;}
        
        /// \brief This function returns the private _audioDevice element pointer
        ///   from the GnashAudioPrivate class.
        /// @return The GnashAudio* stored in the _audioDevice variable
        GnashAudio* getAudioDevice() {return _audioDevice;}
        
        /// \brief This function sets the private _deviceName element in the
        ///   GnashAudioPrivate class.
        /// @param n A gchar* describing the input hardware (e.g. HDA Intel)
        void setDeviceName(gchar* n) {_deviceName = n;}
        
        /// \brief This function returns the private _deviceName variable from the
        ///   GnashAudioPrivate class.
        /// @return The gchar* describing the physical device's name (e.g. HDA Intel)
        gchar* getDeviceName() {return _deviceName;}
    
    //FIXME: I can't figure out why this isn't working right. Since I made 
    // AudioInputGst inherit from GnashAudioPrivate it should be able to access
    // protected variables, but I can't get it to work!    
    //protected:
        /// \var _audioDevice
        /// \brief A pointer to the GnashAudio class of the selected hardware device
        ///   This info should be stored to the GnashAudioPrivate class in the
        ///   transferToPrivate function.
        GnashAudio* _audioDevice;
        
        /// \var _deviceName
        /// \brief A gchar* describing the physical input device's name
        ///   (e.g. HDA Intel or Built-In Microphone)
        gchar* _deviceName;
        
        /// \var _pipeline
        /// \brief A pointer to the main Gstreamer pipeline that all
        ///      created elements and bins will be dropped into.
        GstElement* _pipeline;
        
        /// \var _audioMainBin
        /// The main bin is set up to handle any number of connections to be made
        /// later in the program. The basic pipeline design is as follows:
        /// (sink ghostpad) tee ! audioPlaybackQueue
        /// (sink ghostpad) tee ! audioSaveQueue
        /// The source bin is dropped into the main bin and will eventually be
        /// fed into the tee element
        GstElement* _audioMainBin;
        
        /// \var _audioSourceBin
        /// The audio source bin contains the source device and a restriction on
        /// its capabilities. Currently a lot of stuff in here is hardcoded and
        /// will probably eventually be made into options that can be changed
        /// using setters. The basic pipeline design is as follows:
        /// <selected audio source> ! capsfiter
        /// The source bin is dropped into the _audioMainBin.
        GstElement* _audioSourceBin;
        
        /// \var _audioPlaybackBin
        /// The audio playback bin contains the elements necessary to playback
        /// the audio being captured by the selected device. Note that if you
        /// create the playback bin it will not automatically link up to the
        /// playback queue. To do that you need to call makeAudioSourcePlaybackLink()
        /// function. The basic pipeline design is as follows:
        /// autoaudiosink ! NULL
        GstElement* _audioPlaybackBin;
        
        /// \var _audioSaveBin
        /// The audio save bin contains the elements necessary to save the audio
        /// being captured to a file (currently just to an ogg file). Note that if
        /// you create the save bin it will not automatically link up to the
        /// save queue in the main bin. To do that you need to call the
        /// makeAudioSourceSaveLink() function. The basic pipeline structure is
        /// as follows:
        /// audioconvert ! vorbinenc ! oggmux ! filesink
        GstElement* _audioSaveBin;
        
        /// \var _mux
        /// \brief A direct link to the oggmux element in the _audioSaveBin for
        /// use with linking up to a video muxer so that audio and video are both
        /// muxed out to the same file.
        GstElement* _mux;
        
        /// \var _pipelineIsPlaying
        /// \brief A boolean value which stores whether or not the _pipeline
        /// element is currently in it's 'playing' state or not
        gboolean _pipelineIsPlaying;
};
 
/// \class AudioInputGst
/// \brief The main AudioInputGst class, which actually doesn't store too
/// much important information (most of that is stored in the GnashAudio
/// and GnashAudioPrivate classes)
//
/// The top part of this class implements the AudioInput interface, which
/// is more or less what is needed to implement the rest, though it lacks
/// any data-fetching methods.
//
/// I'm not sure what the rest of it does, but it's not anything useful.
/// Anyone implementing this class should start by implementing the
/// interface.
class AudioInputGst : public AudioInput, public GnashAudioPrivate
{
	
public:

    /// This part implements the interface

	AudioInputGst();

	virtual ~AudioInputGst();

    //setters and getters
    virtual void setActivityLevel(double a) {
        _activityLevel = a;
    }

    virtual double activityLevel() const {
        return _activityLevel;
    }
    
    /// Set the input's gain
    //
    /// Interface range is 0..100, gst range is -60 to 60
    /// TODO: shouldn't we set the value in the input rather than storing
    /// it here?
    virtual void setGain(double g) {
        assert (g >= 0 && g <= 100);
        _gain = g;
        audioChangeSourceBin(getGlobalAudio());
    }

    /// Get the input's gain
    //
    /// Interface range is 0..100, gst range is -60 to 60
    /// TODO: shouldn't we query the value from the input rather than storing
    /// it here?
    virtual double gain() const {
        return _gain;
    }
    
    virtual void setIndex(int i) {
        _index = i;
    }

    virtual int index() const {
        return _index; 
    }
    
    virtual bool muted() {
        return _muted;
    }
    
    virtual void setName(std::string name) {
        _name = name;
    }

    virtual const std::string& name() const { return _name; }
    
    /// Supported rates are (maybe hardware-dependent): 5, 8, 11, 16, 22, 44
    //
    /// TODO: store in device and query that.
    virtual void setRate(int r) {

        // Yes, this isn't pretty, but it is only designed for the 
        // testsuite to continue passing.
        if (r >= 44) {
            _rate = 44000;
            audioChangeSourceBin(getGlobalAudio());
            return;
        }
        static const int rates[] = { 5, 8, 11, 16, 22, 44 };
        const int* rate = rates;
        while (*rate < r) ++rate;
        _rate = *rate * 1000;
        audioChangeSourceBin(getGlobalAudio());
    }

    /// Supported rates are (maybe hardware-dependent): 5, 8, 11, 16, 22, 44
    //
    /// TODO: store in device and query that.
    virtual int rate() const {
        return _rate / 1000;
    }
    
    virtual void setSilenceLevel(double s) {
        _silenceLevel = s;
    }
    
    virtual double silenceLevel() const {
        return _silenceLevel;
    }
    
    virtual void setSilenceTimeout(int s) {
        _silenceTimeout = s;
    }
    
    virtual int silenceTimeout() const {
        return _silenceTimeout;
    }
    
    virtual void setUseEchoSuppression(bool e) {
        _useEchoSuppression = e;
    }

    virtual bool useEchoSuppression() const {
        return _useEchoSuppression;
    }

private:

    double _activityLevel;
    double _gain;
    int _index;
    bool _muted;
    std::string _name;
    int _rate;
    double _silenceLevel;
    int _silenceTimeout;
    bool _useEchoSuppression;

    /// End of interface implementation

    /// \brief This function enumerates information about the audio input devices
    /// attached to the machine and stores them in the _audioVect vector.
    /// @param Nothing.
    /// @return Nothing. All important information is now stored in the _audioVect
    ///   element.
    void findAudioDevs();
    
    /// \brief This function is currently the workhorse of this function. It
    /// looks in the gnashrc file and checks for a default microphone input device.
    /// If one is not selected, the audiotestsrc is used by default. This function
    /// also currently calls the functions to make the GstBins, pipelines and
    /// element connections.
    int makeAudioDevSelection();
    
    /// \brief This function grabs information about the selected audio input
    /// device. It also calls checkSupportedFormats to make sure that Gnash
    /// can handle the input formats supported by the source device.
    /// @param devselect The integer value describing the selected microphone
    ///  This should probably be changed eventually to a more robust selection
    ///  method (rather than a seemingly random integer value)
    void getSelectedCaps(int devselect);
    
    /// \brief This function checks the format information enumerated in
    ///  getSelectedCaps and makes sure that Gnash can handle said input.
    /// @param caps A pointer to the capabilities of the device as enumerated
    ///  in the getSelectedCaps function
    /// @return A boolean value (true means that the device has at least one 
    ///  supported format, false means that the device has no supported formats)
    bool checkSupportedFormats(GstCaps *caps);
    
    /// \brief This function transfers the selected audio device from a GnashAudio
    ///  class to a GnashAudioPrivate class. This function is called once the
    ///  device selection has been made.
    /// @param devselect The integer value describing the selected microphone.
    ///   This should probably be changed eventually to a more robust selection
    ///   method (rather than a seemingly random integer value)
    GnashAudioPrivate* transferToPrivate(int devselect);
    
    /// This function creates the main audio bin. A reference to this bin is
    /// stored in a GnashWebcamPrivate class structure under the _audioMainBin
    /// variable. See the description of _audioMainBin for a pipeline description.
    /// @param audio A pointer to the GnashAudioPrivate class structure of the
    ///  selected audio input device.
    /// @return True if the bin was created successfully, false otherwise.
    gboolean audioCreateMainBin (GnashAudioPrivate *audio);
    
    /// This function creates the audio source bin. A reference to this bin is
    /// stored in a GnashWebcamPrivate class structure under the _audioSourceBin
    /// variable. See the description of _audioSourceBin for a pipeline description.
    /// @param audio A pointer to the GnashAudioPrivate class structure of the
    ///   selected audio input device.
    /// @return True if the bin was created successfully, false otherwise.
    gboolean audioCreateSourceBin (GnashAudioPrivate *audio);
    
    /// This function creates the audio playback bin. A reference to this bin is
    /// stored in a GnashWebcamPrivate class structure under the _audioPlaybackBin
    /// variable. See the description of _audioPlaybackBin for a pipeline description.
    /// IMPORTANT: If you make the playback bin, it's not automatically linked up
    ///  and activated. You must call the makeAudioSourcePlaybackLink() function.
    /// @param audio A pointer to the GnashAudioPrivate class structure of the
    ///   selected audio input device.
    /// @return True if the bin was created successfully, false otherwise.
    gboolean audioCreatePlaybackBin (GnashAudioPrivate *audio);
    
    /// This function makes the link between the audio playback queue (which
    /// receives an audio stream from the source device) and the playback element.
    /// It's important to note that if you create the playback bin, you must
    /// make sure to call makeAudioSourcePlaybackLink() so that the links are
    /// all made properly.
    /// @param audio A pointer to the GnashAudioPrivate class structure of the
    ///   selected audio input device.
    /// @return True if the link was made successfully, false otherwise.
    gboolean makeAudioSourcePlaybackLink (GnashAudioPrivate *audio);
    
    /// This function breaks the link between the audio playback queue and the
    /// playback element.
    /// @param audio A pointer to the GnashAudioPrivate class strucutre of the
    ///   selected audio input device.
    /// @return True if the link was successfully broken, false otherwise.
    gboolean breakAudioSourcePlaybackLink (GnashAudioPrivate *audio);
    
    /// This function makes the link between the audio save queue (which receives
    /// an audio stream from the source device) and the respective save elements.
    /// It's important to note that if you create the save bin you must make sure
    /// to call makeAudioSourceSaveLink() so that the links are all made properly
    /// and it can successfully activate when the pipeline starts.
    /// @param audio A pointer to the GnashAudioPrivate class structure of the
    ///   selected audio input device.
    /// @return True if the link was made successfully, false otherwise.
    gboolean makeAudioSourceSaveLink (GnashAudioPrivate *audio);
    
    /// This function breaks the link between the audio save queue and the audio
    /// stream.
    /// @param audio A pointer to the GnashAudioPrivate class of the selected
    ///   audio input device.
    /// @return True if the link was successfully broken, false otherwise.
    gboolean breakAudioSourceSaveLink (GnashAudioPrivate *audio);
    
    /// This function creates the audio save bin. A reference to this bin is
    /// stored in a GnashWebcamPrivate class structure under the _audioSaveBin
    /// variable. See the description of _audioSaveBin for a pipeline description.
    /// IMPORTANT: If you make the save bin, it's not automatically linked up
    ///  and activated. You must call the makeAudioSourceSaveLink() function.
    /// @param audio A pointer to the GnashAudioPrivate class structure of the
    ///   selected audio input device.
    /// @return True if the bin was created successfully, false otherwise.
    gboolean audioCreateSaveBin (GnashAudioPrivate *audio);
    
    /// This function should be called when the desired pipeline is created. This
    /// will do the proper cleanup and activate the pipeline described in the audio
    /// parameter.
    /// @param audio A pointer to the GnashAudioPrivate class structure containing
    ///   the pipeline to start up.
    /// @return True if the pipeline started to play correctly, false otherwise.
    bool audioPlay(GnashAudioPrivate *audio);
    
    /// This function stops the audio pipeline created earlier in code execution.
    /// 
    /// @param audio A pointer tot he GnashAudioPrivate class structure containing
    ///  the pipeline to start up.
    /// @return True if the pipeline successfully stopped, false otherwise.
    bool audioStop(GnashAudioPrivate *audio);
    
    /// This function applies changes made to one of the following items:
    ///   Gstreamer source, device location, gain, rate
    /// When any of these above listed items are changed, you must call
    /// audioChangeSourceBin to delete the old source bin, recreate and link the
    /// source bin up with new values.
    /// @param audio A pointer to the GnashAudioPrivate structure containing
    ///  the information about the device with the changed values.
    /// @return True if the new changes were applied succesfully, false otherwise
    gboolean audioChangeSourceBin (GnashAudioPrivate *audio);
    
    /// \brief Function returns the total number of devices detected (useful in
    ///  iterating through the _audioVect vector.
    /// @return The _numdevs private variable from the AudioInputGst class.
    int getNumdevs() const { return _audioVect.size(); }
    
    /// \brief Function returns a pointer to the private _audioVect element from
    ///  AudioInputGst class.
    std::vector<GnashAudio*>* getAudioVect() {return &_audioVect;}
    
    /// \brief Accessor to get the reference to the GnashAudioPrivate data
    ///   structure currently being worked with.
    GnashAudioPrivate* getGlobalAudio() {return _globalAudio;}
    
    /// Interface range is 0..100, gst range is -60 to 60
    double gstgain() { return (gain() - 50) * 1.2; }

private:
    
    /// \var _audioVect
    /// \brief A vector of GnashAudio pointers. This is used when storing information
    ///  about attached devices. This vector is accessed using the integer value
    ///  from the gnashrc file (e.g. set microphoneDevice 2 means you're accessing
    ///  _audioVect[2])
    std::vector<GnashAudio*> _audioVect;
    
    /// \var _globalAudio
    /// \brief A global pointer to the GnashAudioPrivate class for the selected
    ///  device. This is useful so you don't have to keep relying on the _audioVect
    ///  vector.
    GnashAudioPrivate* _globalAudio;

};

} // gst namespace
} // gnash.media namespace 
} // gnash namespace

#endif
