#ifndef INCLUDE_SIEVE_H
#define INCLUDE_SIEVE_H
/*
 * This is the Loris C++ Class Library, implementing analysis, 
 * manipulation, and synthesis of digitized sounds using the Reassigned 
 * Bandwidth-Enhanced Additive Sound Model.
 *
 * Loris is Copyright (c) 1999-2010 by Kelly Fitz and Lippold Haken
 *
 * 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 2 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 * Sieve.h
 *
 * Definition of class Sieve.
 *
 * Lippold Haken, 20 Jan 2001
 * loris@cerlsoundgroup.org
 *
 * http://www.cerlsoundgroup.org/Loris/
 *
 */
 
#include "Distiller.h"	//	for default fade time and silent time
 
#if defined(NO_TEMPLATE_MEMBERS)
#include "PartialList.h"
#endif

#include "PartialPtrs.h"

//  begin namespace
namespace Loris {

// ---------------------------------------------------------------------------
//  class Sieve
//
//! A Sieve eliminating temporal overlap among Partials.
//!
//! Class Sieve represents an algorithm for identifying channelized (see also 
//! Channelizer) Partials that overlap in time, and selecting the longer
//! one to represent the channel. The identification of overlap includes 
//! the time needed for Partials to fade to and from zero amplitude in 
//!  synthesis (see also  Synthesizer) or distillation. (see also Distiller)
//! 
//! In some cases, the energy redistribution effected by the distiller
//! (see also Distiller) is undesirable. In such cases, the partials can be
//! sifted before distillation. The sifting process in Loris identifies
//! all the partials that would be rejected (and converted to noise
//! energy) by the distiller and assigns them a label of 0. These sifted
//! partials can then be identified and treated sepearately or removed
//! altogether, or they can be passed through the distiller unlabeled, and
//! crossfaded in the morphing process (see also Morpher).
//!
//!   \sa Channelizer, Distiller, Morpher, Synthesizer
//
class Sieve
{
//  -- instance variables --

    double _fadeTime; //! extra time (in seconds) added to each end of 
                      //! a Partial when determining overlap, to accomodate 
                      //! the fade to and from zero amplitude.
    
//  -- public interface --
public:

//  -- global defaults and constants --

	enum 
	{
	
		//! Default time in milliseconds over which Partials joined by
		//! distillation fade to and from zero amplitude. Divide by 
		//!	1000 to use as a member function parameter. This parameter
		//!	should be the same in Distiller, Sieve, and Collator.
		DefaultFadeTimeMs = Distiller::DefaultFadeTimeMs,
		
		//! Default minimum duration in milliseconds of the silent 
		//! (zero-amplitude) gap between two Partials joined by 
		//!	distillation. Divide by 1000 to use as a member function 
		//!	parameter. This parameter should be the same in Distiller, 
		//!	Sieve, and Collator.
		DefaultSilentTimeMs = Distiller::DefaultSilentTimeMs
    };
    
//  -- construction --

    //! Construct a new Sieve using the specified partial fade
    //! time. If unspecified, the default fade time (same as the   
    //! default fade time for the Distiller) is used.
    //!
    //!   \param   partialFadeTime is the extra time (in seconds)  
    //!            added to each end of a Partial to accomodate 
    //!            the fade to and from zero amplitude. Fade time
    //!            must be non-negative. A default value is used
    //!			   if unspecified.
    //!   \throw  InvalidArgument if partialFadeTime is negative.
    explicit Sieve( double partialFadeTime = Sieve::DefaultFadeTimeMs/1000.0 );
     
    //  Use compiler-generated copy, assign, and destroy.
    
//  -- sifting --

    //! Sift labeled Partials on the specified half-open (STL-style)
    //! range. If any two Partials having same label overlap in time, keep
    //! only the longer of the two Partials. Set the label of the shorter
    //! duration partial to zero. No Partials are removed from the
    //! sequence and the sequence order is unaltered. 
    //!
    //! \param sift_begin is the beginning of the range of Partials to sift
    //! \param sift_end is (one-past) the end of the range of Partials to sift
    //! 
    //! If compiled with NO_TEMPLATE_MEMBERS defined, then sift_begin and 
    //! sift_end must be PartialList::iterators, otherwise they can be any type
    //! of iterators over a sequence of Partials.
#if ! defined(NO_TEMPLATE_MEMBERS)
    template<typename Iter>
    void sift( Iter sift_begin, Iter sift_end  );
#else
   inline
    void sift( PartialList::iterator sift_begin, PartialList::iterator sift_end  );
#endif

    //! Sift labeled Partials in the specified container
    //! If any two Partials having same label overlap in time, keep
    //! only the longer of the two Partials. Set the label of the shorter
    //! duration partial to zero. No Partials are removed from the
    //! container and the container order is unaltered. 
    //!
    //! \param  partials is the collection of Partials to sift in-place
    //! 
    //! If compiled with NO_TEMPLATE_MEMBERS defined, then partials
    //! must be a PartialList, otherwise it can be any container type
    //! storing Partials that supports at least bidirectional iterators.
#if ! defined(NO_TEMPLATE_MEMBERS)
    template< typename Container >
    void sift( Container & partials  )
#else
   inline
    void sift( PartialList & partials )
#endif
    {
        sift( partials.begin(), partials.end() );
    }
         
// -- static members --

    //! Static member that constructs an instance and applies
    //! it to a sequence of Partials. 
    //! Construct a Sieve using the specified Partial
    //! fade time (in seconds), and use it to sift a
    //! sequence of Partials. 
    //!
    //! \param  sift_begin is the beginning of the range of Partials to sift
    //! \param  sift_end is (one-past) the end of the range of Partials to sift
    //! \param  partialFadeTime is the extra time (in seconds)  
    //!         added to each end of a Partial to accomodate 
    //!         the fade to and from zero amplitude. The Partial fade time
    //!         must be non-negative.
    //! \throw  InvalidArgument if partialFadeTime is negative.
    //! 
    //! If compiled with NO_TEMPLATE_MEMBERS defined, then begin and end
    //! must be PartialList::iterators, otherwise they can be any type
    //! of iterators over a sequence of Partials.
#if ! defined(NO_TEMPLATE_MEMBERS)
    template< typename Iter >
    static 
    void sift( Iter sift_begin, Iter sift_end, 
              double partialFadeTime );
#else
    static inline 
    void sift( PartialList::iterator sift_begin, PartialList::iterator sift_end,
              double partialFadeTime );
#endif   

//  -- helper --
private:

   //!   Sift labeled Partials. If any two Partials having the same (non-zero)
   //!   label overlap in time (where overlap includes the fade time at both 
   //!   ends of each Partial), then set the label of the Partial having the
   //!   shorter duration to zero. Sifting is performed on a collection of 
   //!   pointers to Partials so that the it can be performed without changing 
   //!   the order of the Partials in the sequence.
   //!
   //!   \param   ptrs is a collection of pointers to the Partials in the
   //!            sequence to be sifted.
    void sift_ptrs( PartialPtrs & ptrs );

};  //  end of class Sieve

// ---------------------------------------------------------------------------
//  sift
// ---------------------------------------------------------------------------
//! Sift labeled Partials on the specified half-open (STL-style)
//! range. If any two Partials having same label overlap in time, keep
//! only the longer of the two Partials. Set the label of the shorter
//! duration partial to zero. No Partials are removed from the
//! sequence and the sequence order is unaltered. 
//!
//! \param sift_begin is the beginning of the range of Partials to sift
//! \param sift_end is (one-past) the end of the range of Partials to sift
//! 
//! If compiled with NO_TEMPLATE_MEMBERS defined, then sift_begin and 
//! sift_end must be PartialList::iterators, otherwise they can be any type
//! of iterators over a sequence of Partials.
#if ! defined(NO_TEMPLATE_MEMBERS)
template< typename Iter >
void Sieve::sift( Iter sift_begin, Iter sift_end  )
#else
inline
void Sieve::sift( PartialList::iterator sift_begin, PartialList::iterator sift_end  )
#endif
{
    PartialPtrs ptrs;
    fillPartialPtrs( sift_begin, sift_end, ptrs );
    sift_ptrs( ptrs );
}

// ---------------------------------------------------------------------------
//  sift (static)
// ---------------------------------------------------------------------------
//! Static member that constructs an instance and applies
//! it to a sequence of Partials. 
//! Construct a Sieve using the specified Partial
//! fade time (in seconds), and use it to sift a
//! sequence of Partials. 
//!
//! \param  sift_begin is the beginning of the range of Partials to sift
//! \param  sift_end is (one-past) the end of the range of Partials to sift
//! \param  partialFadeTime is the extra time (in seconds)  
//!         added to each end of a Partial to accomodate 
//!         the fade to and from zero amplitude. The Partial fade time
//!         must be non-negative.
//! \throw  InvalidArgument if partialFadeTime is negative.
//! 
//! If compiled with NO_TEMPLATE_MEMBERS defined, then begin and end
//! must be PartialList::iterators, otherwise they can be any type
//! of iterators over a sequence of Partials.
#if ! defined(NO_TEMPLATE_MEMBERS)
template< typename Iter >
void Sieve::sift( Iter sift_begin, Iter sift_end, 
                  double partialFadeTime )
#else
inline 
void Sieve::sift( PartialList::iterator sift_begin, PartialList::iterator sift_end,
                  double partialFadeTime )
#endif   
{
   Sieve instance( partialFadeTime );
   instance.sift( sift_begin, sift_end );
}

}   //  end of namespace Loris

#endif /* ndef INCLUDE_SIEVE_H */