1 #ifndef STK_SAXOFONY_H
2 #define STK_SAXOFONY_H
3 
4 #include "Instrmnt.h"
5 #include "DelayL.h"
6 #include "ReedTable.h"
7 #include "OneZero.h"
8 #include "Envelope.h"
9 #include "Noise.h"
10 #include "SineWave.h"
11 
12 namespace stk {
13 
14 /***************************************************/
15 /*! \class Saxofony
16     \brief STK faux conical bore reed instrument class.
17 
18     This class implements a "hybrid" digital
19     waveguide instrument that can generate a
20     variety of wind-like sounds.  It has also been
21     referred to as the "blowed string" model.  The
22     waveguide section is essentially that of a
23     string, with one rigid and one lossy
24     termination.  The non-linear function is a
25     reed table.  The string can be "blown" at any
26     point between the terminations, though just as
27     with strings, it is impossible to excite the
28     system at either end.  If the excitation is
29     placed at the string mid-point, the sound is
30     that of a clarinet.  At points closer to the
31     "bridge", the sound is closer to that of a
32     saxophone.  See Scavone (2002) for more details.
33 
34     This is a digital waveguide model, making its
35     use possibly subject to patents held by Stanford
36     University, Yamaha, and others.
37 
38     Control Change Numbers:
39        - Reed Stiffness = 2
40        - Reed Aperture = 26
41        - Noise Gain = 4
42        - Blow Position = 11
43        - Vibrato Frequency = 29
44        - Vibrato Gain = 1
45        - Breath Pressure = 128
46 
47     by Perry R. Cook and Gary P. Scavone, 1995--2021.
48 */
49 /***************************************************/
50 
51 class Saxofony : public Instrmnt
52 {
53  public:
54   //! Class constructor, taking the lowest desired playing frequency.
55   /*!
56     An StkError will be thrown if the rawwave path is incorrectly set.
57   */
58   Saxofony( StkFloat lowestFrequency );
59 
60   //! Class destructor.
61   ~Saxofony( void );
62 
63   //! Reset and clear all internal state.
64   void clear( void );
65 
66   //! Set instrument parameters for a particular frequency.
67   void setFrequency( StkFloat frequency );
68 
69   //! Set the "blowing" position between the air column terminations (0.0 - 1.0).
70   void setBlowPosition( StkFloat aPosition );
71 
72   //! Apply breath pressure to instrument with given amplitude and rate of increase.
73   void startBlowing( StkFloat amplitude, StkFloat rate );
74 
75   //! Decrease breath pressure with given rate of decrease.
76   void stopBlowing( StkFloat rate );
77 
78   //! Start a note with the given frequency and amplitude.
79   void noteOn( StkFloat frequency, StkFloat amplitude );
80 
81   //! Stop a note with the given amplitude (speed of decay).
82   void noteOff( StkFloat amplitude );
83 
84   //! Perform the control change specified by \e number and \e value (0.0 - 128.0).
85   void controlChange( int number, StkFloat value );
86 
87   //! Compute and return one output sample.
88   StkFloat tick( unsigned int channel = 0 );
89 
90   //! Fill a channel of the StkFrames object with computed outputs.
91   /*!
92     The \c channel argument must be less than the number of
93     channels in the StkFrames argument (the first channel is specified
94     by 0).  However, range checking is only performed if _STK_DEBUG_
95     is defined during compilation, in which case an out-of-range value
96     will trigger an StkError exception.
97   */
98   StkFrames& tick( StkFrames& frames, unsigned int channel = 0 );
99 
100  protected:
101 
102   DelayL    delays_[2];
103   ReedTable reedTable_;
104   OneZero   filter_;
105   Envelope  envelope_;
106   Noise     noise_;
107   SineWave vibrato_;
108 
109   StkFloat outputGain_;
110   StkFloat noiseGain_;
111   StkFloat vibratoGain_;
112   StkFloat position_;
113 
114 };
115 
tick(unsigned int)116 inline StkFloat Saxofony :: tick( unsigned int )
117 {
118   StkFloat pressureDiff;
119   StkFloat breathPressure;
120   StkFloat temp;
121 
122   // Calculate the breath pressure (envelope + noise + vibrato)
123   breathPressure = envelope_.tick();
124   breathPressure += breathPressure * noiseGain_ * noise_.tick();
125   breathPressure += breathPressure * vibratoGain_ * vibrato_.tick();
126 
127   temp = -0.95 * filter_.tick( delays_[0].lastOut() );
128   lastFrame_[0] = temp - delays_[1].lastOut();
129   pressureDiff = breathPressure - lastFrame_[0];
130   delays_[1].tick( temp );
131   delays_[0].tick( breathPressure - (pressureDiff * reedTable_.tick(pressureDiff)) - temp );
132 
133   lastFrame_[0] *= outputGain_;
134   return lastFrame_[0];
135 }
136 
tick(StkFrames & frames,unsigned int channel)137 inline StkFrames& Saxofony :: tick( StkFrames& frames, unsigned int channel )
138 {
139   unsigned int nChannels = lastFrame_.channels();
140 #if defined(_STK_DEBUG_)
141   if ( channel > frames.channels() - nChannels ) {
142     oStream_ << "Saxofony::tick(): channel and StkFrames arguments are incompatible!";
143     handleError( StkError::FUNCTION_ARGUMENT );
144   }
145 #endif
146 
147   StkFloat *samples = &frames[channel];
148   unsigned int j, hop = frames.channels() - nChannels;
149   if ( nChannels == 1 ) {
150     for ( unsigned int i=0; i<frames.frames(); i++, samples += hop )
151       *samples++ = tick();
152   }
153   else {
154     for ( unsigned int i=0; i<frames.frames(); i++, samples += hop ) {
155       *samples++ = tick();
156       for ( j=1; j<nChannels; j++ )
157         *samples++ = lastFrame_[j];
158     }
159   }
160 
161   return frames;
162 }
163 
164 } // stk namespace
165 
166 #endif
167