1 #ifndef STK_FORMSWEP_H
2 #define STK_FORMSWEP_H
3 
4 #include "Filter.h"
5 
6 namespace stk {
7 
8 /***************************************************/
9 /*! \class FormSwep
10     \brief STK sweepable formant filter class.
11 
12     This class implements a formant (resonance) which can be "swept"
13     over time from one frequency setting to another.  It provides
14     methods for controlling the sweep rate and target frequency.
15 
16     by Perry R. Cook and Gary P. Scavone, 1995--2021.
17 */
18 /***************************************************/
19 
20 class FormSwep : public Filter
21 {
22  public:
23 
24   //! Default constructor creates a second-order pass-through filter.
25   FormSwep( void );
26 
27   //! Class destructor.
28   ~FormSwep();
29 
30   //! A function to enable/disable the automatic updating of class data when the STK sample rate changes.
31   void ignoreSampleRateChange( bool ignore = true ) { ignoreSampleRateChange_ = ignore; };
32 
33   //! Sets the filter coefficients for a resonance at \e frequency (in Hz).
34   /*!
35     This method determines the filter coefficients corresponding to
36     two complex-conjugate poles with the given \e frequency (in Hz)
37     and \e radius from the z-plane origin.  The filter zeros are
38     placed at z = 1, z = -1, and the coefficients are then normalized
39     to produce a constant unity gain (independent of the filter \e
40     gain parameter).  The resulting filter frequency response has a
41     resonance at the given \e frequency.  The closer the poles are to
42     the unit-circle (\e radius close to one), the narrower the
43     resulting resonance width.  An unstable filter will result for \e
44     radius >= 1.0.  The \e frequency value should be between zero and
45     half the sample rate.
46   */
47   void setResonance( StkFloat frequency, StkFloat radius );
48 
49   //! Set both the current and target resonance parameters.
50   void setStates( StkFloat frequency, StkFloat radius, StkFloat gain = 1.0 );
51 
52   //! Set target resonance parameters.
53   void setTargets( StkFloat frequency, StkFloat radius, StkFloat gain = 1.0 );
54 
55   //! Set the sweep rate (between 0.0 - 1.0).
56   /*!
57     The formant parameters are varied in increments of the
58     sweep rate between their current and target values.
59     A sweep rate of 1.0 will produce an immediate change in
60     resonance parameters from their current values to the
61     target values.  A sweep rate of 0.0 will produce no
62     change in resonance parameters.
63   */
64   void setSweepRate( StkFloat rate );
65 
66   //! Set the sweep rate in terms of a time value in seconds.
67   /*!
68     This method adjusts the sweep rate based on a
69     given time for the formant parameters to reach
70     their target values.
71   */
72   void setSweepTime( StkFloat time );
73 
74   //! Return the last computed output value.
lastOut(void)75   StkFloat lastOut( void ) const { return lastFrame_[0]; };
76 
77   //! Input one sample to the filter and return a reference to one output.
78   StkFloat tick( StkFloat input );
79 
80   //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs.
81   /*!
82     The StkFrames argument reference is returned.  The \c channel
83     argument must be less than the number of channels in the
84     StkFrames argument (the first channel is specified by 0).
85     However, range checking is only performed if _STK_DEBUG_ is
86     defined during compilation, in which case an out-of-range value
87     will trigger an StkError exception.
88   */
89   StkFrames& tick( StkFrames& frames, unsigned int channel = 0 );
90 
91   //! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object.
92   /*!
93     The \c iFrames object reference is returned.  Each channel
94     argument must be less than the number of channels in the
95     corresponding StkFrames argument (the first channel is specified
96     by 0).  However, range checking is only performed if _STK_DEBUG_
97     is defined during compilation, in which case an out-of-range value
98     will trigger an StkError exception.
99   */
100   StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 );
101 
102  protected:
103 
104   virtual void sampleRateChanged( StkFloat newRate, StkFloat oldRate );
105 
106   bool dirty_;
107   StkFloat frequency_;
108   StkFloat radius_;
109   StkFloat startFrequency_;
110   StkFloat startRadius_;
111   StkFloat startGain_;
112   StkFloat targetFrequency_;
113   StkFloat targetRadius_;
114   StkFloat targetGain_;
115   StkFloat deltaFrequency_;
116   StkFloat deltaRadius_;
117   StkFloat deltaGain_;
118   StkFloat sweepState_;
119   StkFloat sweepRate_;
120 
121 };
122 
tick(StkFloat input)123 inline StkFloat FormSwep :: tick( StkFloat input )
124 {
125   if ( dirty_ )  {
126     sweepState_ += sweepRate_;
127     if ( sweepState_ >= 1.0 )   {
128       sweepState_ = 1.0;
129       dirty_ = false;
130       radius_ = targetRadius_;
131       frequency_ = targetFrequency_;
132       gain_ = targetGain_;
133     }
134     else {
135       radius_ = startRadius_ + (deltaRadius_ * sweepState_);
136       frequency_ = startFrequency_ + (deltaFrequency_ * sweepState_);
137       gain_ = startGain_ + (deltaGain_ * sweepState_);
138     }
139     this->setResonance( frequency_, radius_ );
140   }
141 
142   inputs_[0] = gain_ * input;
143   lastFrame_[0] = b_[0] * inputs_[0] + b_[1] * inputs_[1] + b_[2] * inputs_[2];
144   lastFrame_[0] -= a_[2] * outputs_[2] + a_[1] * outputs_[1];
145   inputs_[2] = inputs_[1];
146   inputs_[1] = inputs_[0];
147   outputs_[2] = outputs_[1];
148   outputs_[1] = lastFrame_[0];
149 
150   return lastFrame_[0];
151 }
152 
tick(StkFrames & frames,unsigned int channel)153 inline StkFrames& FormSwep :: tick( StkFrames& frames, unsigned int channel )
154 {
155 #if defined(_STK_DEBUG_)
156   if ( channel >= frames.channels() ) {
157     oStream_ << "FormSwep::tick(): channel and StkFrames arguments are incompatible!";
158     handleError( StkError::FUNCTION_ARGUMENT );
159   }
160 #endif
161 
162   StkFloat *samples = &frames[channel];
163   unsigned int hop = frames.channels();
164   for ( unsigned int i=0; i<frames.frames(); i++, samples += hop )
165     *samples = tick( *samples );
166 
167   return frames;
168 }
169 
tick(StkFrames & iFrames,StkFrames & oFrames,unsigned int iChannel,unsigned int oChannel)170 inline StkFrames& FormSwep :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel )
171 {
172 #if defined(_STK_DEBUG_)
173   if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() ) {
174     oStream_ << "FormSwep::tick(): channel and StkFrames arguments are incompatible!";
175     handleError( StkError::FUNCTION_ARGUMENT );
176   }
177 #endif
178 
179   StkFloat *iSamples = &iFrames[iChannel];
180   StkFloat *oSamples = &oFrames[oChannel];
181   unsigned int iHop = iFrames.channels(), oHop = oFrames.channels();
182   for ( unsigned int i=0; i<iFrames.frames(); i++, iSamples += iHop, oSamples += oHop )
183     *oSamples = tick( *iSamples );
184 
185   return iFrames;
186 }
187 
188 } // stk namespace
189 
190 #endif
191