You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

257 lines
7.2KB

  1. /***************************************************/
  2. /*! \Class Whistle
  3. \brief STK police/referee whistle instrument class.
  4. This class implements a hybrid physical/spectral
  5. model of a police whistle (a la Cook).
  6. Control Change Numbers:
  7. - Noise Gain = 4
  8. - Fipple Modulation Frequency = 11
  9. - Fipple Modulation Gain = 1
  10. - Blowing Frequency Modulation = 2
  11. - Volume = 128
  12. by Perry R. Cook 1995--2017.
  13. */
  14. /***************************************************/
  15. #include "Whistle.h"
  16. #include "SKINImsg.h"
  17. #include <cmath>
  18. namespace stk {
  19. const int CAN_RADIUS = 100;
  20. const int PEA_RADIUS = 30;
  21. const int BUMP_RADIUS = 5;
  22. const StkFloat NORM_CAN_LOSS = 0.97;
  23. //const StkFloat SLOW_CAN_LOSS = 0.90;
  24. const StkFloat GRAVITY = 20.0;
  25. const StkFloat NORM_TICK_SIZE = 0.004;
  26. //const StkFloat SLOW_TICK_SIZE = 0.0001;
  27. const StkFloat ENV_RATE = 0.001;
  28. Whistle :: Whistle( void )
  29. {
  30. sine_.setFrequency( 2800.0 );
  31. can_.setRadius( CAN_RADIUS );
  32. can_.setPosition(0, 0, 0); // set can location
  33. can_.setVelocity(0, 0, 0); // and the velocity
  34. onepole_.setPole(0.95); // 0.99
  35. bumper_.setRadius( BUMP_RADIUS );
  36. bumper_.setPosition(0.0, CAN_RADIUS-BUMP_RADIUS, 0);
  37. bumper_.setPosition(0.0, CAN_RADIUS-BUMP_RADIUS, 0);
  38. pea_.setRadius( PEA_RADIUS );
  39. pea_.setPosition(0, CAN_RADIUS/2, 0);
  40. pea_.setVelocity(35, 15, 0);
  41. envelope_.setRate( ENV_RATE );
  42. envelope_.keyOn();
  43. fippleFreqMod_ = 0.5;
  44. fippleGainMod_ = 0.5;
  45. blowFreqMod_ = 0.25;
  46. noiseGain_ = 0.125;
  47. baseFrequency_ = 2000;
  48. tickSize_ = NORM_TICK_SIZE;
  49. canLoss_ = NORM_CAN_LOSS;
  50. subSample_ = 1;
  51. subSampCount_ = subSample_;
  52. }
  53. Whistle :: ~Whistle( void )
  54. {
  55. #ifdef WHISTLE_ANIMATION
  56. printf("Exit, Whistle bye bye!!\n");
  57. #endif
  58. }
  59. void Whistle :: clear( void )
  60. {
  61. }
  62. void Whistle :: setFrequency( StkFloat frequency )
  63. {
  64. #if defined(_STK_DEBUG_)
  65. if ( frequency <= 0.0 ) {
  66. oStream_ << "Whistle::setFrequency: parameter is less than or equal to zero!";
  67. handleError( StkError::WARNING ); return;
  68. }
  69. #endif
  70. baseFrequency_ = frequency * 4; // the whistle is a transposing instrument
  71. }
  72. void Whistle :: startBlowing( StkFloat amplitude, StkFloat rate )
  73. {
  74. if ( amplitude <= 0.0 || rate <= 0.0 ) {
  75. oStream_ << "Whistle::startBlowing: one or more arguments is less than or equal to zero!";
  76. handleError( StkError::WARNING ); return;
  77. }
  78. envelope_.setRate( ENV_RATE );
  79. envelope_.setTarget( amplitude );
  80. }
  81. void Whistle :: stopBlowing( StkFloat rate )
  82. {
  83. if ( rate <= 0.0 ) {
  84. oStream_ << "Whistle::stopBlowing: argument is less than or equal to zero!";
  85. handleError( StkError::WARNING ); return;
  86. }
  87. envelope_.setRate( rate );
  88. envelope_.keyOff();
  89. }
  90. void Whistle :: noteOn( StkFloat frequency, StkFloat amplitude )
  91. {
  92. this->setFrequency( frequency );
  93. this->startBlowing( amplitude*2.0 ,amplitude * 0.2 );
  94. }
  95. void Whistle :: noteOff( StkFloat amplitude )
  96. {
  97. this->stopBlowing( amplitude * 0.02 );
  98. }
  99. int frameCount = 0;
  100. StkFloat Whistle :: tick( unsigned int )
  101. {
  102. StkFloat soundMix, tempFreq;
  103. StkFloat envOut = 0, temp, temp1, temp2, tempX, tempY;
  104. double phi, cosphi, sinphi;
  105. double gain = 0.5, mod = 0.0;
  106. if ( --subSampCount_ <= 0 ) {
  107. tempVectorP_ = pea_.getPosition();
  108. subSampCount_ = subSample_;
  109. temp = bumper_.isInside( tempVectorP_ );
  110. #ifdef WHISTLE_ANIMATION
  111. frameCount += 1;
  112. if ( frameCount >= (1470 / subSample_) ) {
  113. frameCount = 0;
  114. printf("%f %f %f\n",tempVectorP_->getX(),tempVectorP_->getY(),envOut);
  115. fflush(stdout);
  116. }
  117. #endif
  118. envOut = envelope_.tick();
  119. if (temp < (BUMP_RADIUS + PEA_RADIUS)) {
  120. tempX = envOut * tickSize_ * 2000 * noise_.tick();
  121. tempY = -envOut * tickSize_ * 1000 * (1.0 + noise_.tick());
  122. pea_.addVelocity( tempX, tempY, 0 );
  123. pea_.tick( tickSize_ );
  124. }
  125. mod = exp(-temp * 0.01); // exp. distance falloff of fipple/pea effect
  126. temp = onepole_.tick(mod); // smooth it a little
  127. gain = (1.0 - (fippleGainMod_*0.5)) + (2.0 * fippleGainMod_ * temp);
  128. gain *= gain; // squared distance/gain
  129. // tempFreq = 1.0 // Normalized Base Freq
  130. // + (fippleFreqMod_ * 0.25) - (fippleFreqMod_ * temp) // fippleModulation
  131. // - (blowFreqMod_) + (blowFreqMod_ * envOut); // blowingModulation
  132. // short form of above
  133. tempFreq = 1.0 + fippleFreqMod_*(0.25-temp) + blowFreqMod_*(envOut-1.0);
  134. tempFreq *= baseFrequency_;
  135. sine_.setFrequency(tempFreq);
  136. tempVectorP_ = pea_.getPosition();
  137. temp = can_.isInside(tempVectorP_);
  138. temp = -temp; // We know (hope) it's inside, just how much??
  139. if (temp < (PEA_RADIUS * 1.25)) {
  140. pea_.getVelocity( &tempVector_ ); // This is the can/pea collision
  141. tempX = tempVectorP_->getX(); // calculation. Could probably
  142. tempY = tempVectorP_->getY(); // simplify using tables, etc.
  143. phi = -atan2(tempY,tempX);
  144. cosphi = cos(phi);
  145. sinphi = sin(phi);
  146. temp1 = (cosphi*tempVector_.getX()) - (sinphi*tempVector_.getY());
  147. temp2 = (sinphi*tempVector_.getX()) + (cosphi*tempVector_.getY());
  148. temp1 = -temp1;
  149. tempX = (cosphi*temp1) + (sinphi*temp2);
  150. tempY = (-sinphi*temp1) + (cosphi*temp2);
  151. pea_.setVelocity(tempX, tempY, 0);
  152. pea_.tick(tickSize_);
  153. pea_.setVelocity( tempX*canLoss_, tempY*canLoss_, 0 );
  154. pea_.tick(tickSize_);
  155. }
  156. temp = tempVectorP_->getLength();
  157. if (temp > 0.01) {
  158. tempX = tempVectorP_->getX();
  159. tempY = tempVectorP_->getY();
  160. phi = atan2( tempY, tempX );
  161. phi += 0.3 * temp / CAN_RADIUS;
  162. cosphi = cos(phi);
  163. sinphi = sin(phi);
  164. tempX = 3.0 * temp * cosphi;
  165. tempY = 3.0 * temp * sinphi;
  166. }
  167. else {
  168. tempX = 0.0;
  169. tempY = 0.0;
  170. }
  171. temp = (0.9 + 0.1*subSample_*noise_.tick()) * envOut * 0.6 * tickSize_;
  172. pea_.addVelocity( temp * tempX, (temp*tempY) - (GRAVITY*tickSize_), 0 );
  173. pea_.tick( tickSize_ );
  174. // bumper_.tick(0.0);
  175. }
  176. temp = envOut * envOut * gain / 2;
  177. soundMix = temp * ( sine_.tick() + ( noiseGain_*noise_.tick() ) );
  178. lastFrame_[0] = 0.20 * soundMix; // should probably do one-zero filter here
  179. return lastFrame_[0];
  180. }
  181. void Whistle :: controlChange( int number, StkFloat value )
  182. {
  183. #if defined(_STK_DEBUG_)
  184. if ( Stk::inRange( value, 0.0, 128.0 ) == false ) {
  185. oStream_ << "Whistle::controlChange: value (" << value << ") is out of range!";
  186. handleError( StkError::WARNING ); return;
  187. }
  188. #endif
  189. StkFloat normalizedValue = value * ONE_OVER_128;
  190. if ( number == __SK_NoiseLevel_ ) // 4
  191. noiseGain_ = 0.25 * normalizedValue;
  192. else if ( number == __SK_ModFrequency_ ) // 11
  193. fippleFreqMod_ = normalizedValue;
  194. else if ( number == __SK_ModWheel_ ) // 1
  195. fippleGainMod_ = normalizedValue;
  196. else if ( number == __SK_AfterTouch_Cont_ ) // 128
  197. envelope_.setTarget( normalizedValue * 2.0 );
  198. else if ( number == __SK_Breath_ ) // 2
  199. blowFreqMod_ = normalizedValue * 0.5;
  200. else if ( number == __SK_Sustain_ ) { // 64
  201. subSample_ = (int) value;
  202. if ( subSample_ < 1.0 ) subSample_ = 1;
  203. envelope_.setRate( ENV_RATE / subSample_ );
  204. }
  205. #if defined(_STK_DEBUG_)
  206. else {
  207. oStream_ << "Whistle::controlChange: undefined control number (" << number << ")!";
  208. handleError( StkError::WARNING );
  209. }
  210. #endif
  211. }
  212. } // stk namespace