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