Advertisement
Kitomas

_sfx.hpp 2023-12-08

Dec 8th, 2023
831
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 6.25 KB | None | 0 0
  1. #pragma once
  2.  
  3. #include <sfx.hpp>
  4.  
  5.  
  6. //500ms
  7. #define _totalFadeDelay (0.500f)
  8.  
  9. //linearly fade over the course of 10ms
  10. #define _fadeDeltaSeconds (0.010f)
  11.  
  12. //the most common audio clipping ends at 10-11ms after unpausing,
  13.  //but i've seen clipping as far as ~450ms after unpausing
  14. #define _fadeInDelaySeconds (_totalFadeDelay - _fadeDeltaSeconds)
  15.  
  16.  
  17. //lower clip's volume to 0 within 10ms if speed reaches 0
  18. #define _speed0DeltaVolMS (10.0f)
  19.  
  20.  
  21.  
  22.  
  23. #ifndef LERP2
  24. #define LERP2(_v0,_v1, _t) ( (_v0) + (_t)*((_v1)-(_v0)) )
  25. #endif
  26.  
  27. static inline sfx_f32s sfx_linearSample(std::vector<sfx_f32s>& src,
  28.                                         double& position)
  29. {
  30.   Uint64 intPosition = (Uint64)position;
  31.   double modPosition = position-intPosition;
  32.  
  33.   sfx_f32s smpA = src.at(  intPosition);
  34.   sfx_f32s smpB = src.at(++intPosition);
  35.  
  36.   sfx_f32s smpOut(
  37.     LERP2(smpA.l, smpB.l, modPosition),
  38.     LERP2(smpA.r, smpB.r, modPosition)
  39.   );
  40.   return smpOut;
  41. }
  42.  
  43.  
  44.  
  45.  
  46. /****************/
  47. /* sfx_callback */
  48. /****************/
  49.  
  50. static inline sfx_f32s& _sfx_applyPan(sfx_f32s& sample, float pan){
  51.   //(i think this is how audacity does it...)
  52.   if(pan < 0){
  53.     sample.l += sample.r*(-pan);
  54.     sample.r *= 1.0f+pan;
  55.   } else if(pan > 0){
  56.     sample.r += sample.l*pan;
  57.     sample.l *= 1.0f-pan;
  58.   }
  59.   return sample;
  60. }
  61.  
  62.  
  63. static inline void _sfx_mixTrack(sfx_track& track, Uint64 sfxTimeStampEnd,
  64.                                  sfx_f32s* dst, int dst_len)
  65. {
  66.   sfx_pcm* pcm = track.pcm;
  67.   const sfx_f32s speed0VolumeDelta = -( (1000.0f/_speed0DeltaVolMS) / pcm->sampleRate );
  68.  
  69.   if(track.timeStamp > 0){
  70.  
  71.  
  72.     //calculate position based on difference between device and clip timestamps
  73.      //(timeStampEnd is used instead of Start, so the clip's time stamp
  74.      // can be compared to the last time the device callback exited)
  75.     double difference = (double)(track.timeStamp-sfxTimeStampEnd)/1000;
  76.     difference *= pcm->sampleRate; //convert seconds to samples
  77.     track.position = -(difference*track.speed); //starts playing when position reaches 0
  78.  
  79.     track.timeStamp = 0; //to ensure that this only occurs once per clip queued
  80.   }
  81.  
  82.  
  83.   //make copies of relevant track values
  84.   float  pan       = std::clamp(track.pan, -1.0f,1.0f);
  85.   Uint64 loopStart = pcm->loopStart;
  86.   Uint64 loopEnd   = pcm->loopEnd-1; //might remove the -1 if needed
  87.   std::vector<sfx_f32s>& src = *pcm->samples;
  88.    //
  89.   double position = track.position;
  90.   double speed    = track.speed;
  91.   Uint16 loops    = track.loops;
  92.   bool   stopping = track.stopping;
  93.  
  94.   //make sure volume is clamped before applying it to the first sample,
  95.    //since volume is only clamped after volumeDelta is applied otherwise
  96.   track.volume.unit(); //clamp to 0.0 -> 1.0
  97.  
  98.  
  99.   for(int i=0; i<dst_len; ++i){
  100.     //loop handling (or end-of-clip handling i guess)
  101.     if(stopping){ dst[i] = 0.0f; continue; } //write silence if clip is stopping
  102.     if(position >= loopEnd){ //clip finished loop
  103.       if(!loops){ stopping = true; dst[i] = 0.0f; continue; } //mark as inactive
  104.       if(loops != 0xffff) --loops; //decrement loops (unless infinite)
  105.       position -= (Uint64)position; //basically position %= 1
  106.       position += loopStart; //now new_position = loopStart + old_position%1
  107.  
  108.     } else if(position < 0){ //clip has yet to start playing
  109.       if(position < -speed){
  110.         position += speed; //step forward by current speed
  111.         dst[i] = 0.0f; continue; //0 for silent sample
  112.       } else { //if position >= -speed, the clip should start next sample
  113.         position = 0; //make sure clip starts at 0
  114.       }
  115.  
  116.     }
  117.  
  118.     //get sample, apply volume, hard clip
  119.     sfx_f32s sample = sfx_linearSample(src,position);
  120.     sample *= track.volume;
  121.     sample.clip();
  122.  
  123.     //apply pan, apply volumeDelta, clamp volume to 0.0f -> 1.0f
  124.     dst[i] += _sfx_applyPan(sample,pan);
  125.     track.volume += track.volumeDelta;
  126.     track.volume.unit();
  127.  
  128.     //update position, apply speedDelta, start fade out if clip's speed <= 0
  129.     position += speed;
  130.     speed += track.speedDelta;
  131.     if(speed <= 0){
  132.       speed = 0;
  133.       track.volumeDelta = speed0VolumeDelta;
  134.     }
  135.   }
  136.  
  137.  
  138.   //update relevant values in track
  139.   track.position = position;
  140.   track.speed    = speed;
  141.   track.loops    = loops;
  142.   track.stopping = stopping;
  143.  
  144.   if(track.volume.l<=0 && track.volume.r<=0  &&  track.stopOnMute)
  145.     track.stopping = true;
  146.  
  147.   if(track.stopping) track.pcm = nullptr;
  148. }
  149.  
  150.  
  151.  
  152. extern int _sfx_pauseThread(void* data);
  153.  
  154. static inline void _sfx_globalFade(sfx_class* sfx, sfx_f32s* stream, int len){
  155.   sfx_f32s fadeDelta      = sfx->_getFadeDelta();
  156.   sfx_f32s fadeVolume     = sfx->_getFadeVolume();
  157.   Uint32&  fadeInDelayRef = sfx->_getFadeInDelay();
  158.   Uint32   fadeInDelay    = fadeInDelayRef;
  159.  
  160.   int i = 0; //this index is shared, as the loops can jump to others at will
  161.  
  162.  
  163.   //FADING OUT
  164.   if(sfx->isFadingOut()){ _fade_out_:;
  165.     for(; i<len; ++i){
  166.       if(!sfx->isFadingOut()) goto _fade_in_;
  167.       stream[i] *= fadeVolume;
  168.       fadeVolume -= fadeDelta;
  169.       if(fadeVolume.l < 0) fadeVolume = 0.0f;
  170.     }
  171.  
  172.     //trigger pause thread if fade out is complete
  173.     if(fadeVolume.l <= 0.0f){
  174.       SDL_Thread* pauseThread = SDL_CreateThread(_sfx_pauseThread,"_PauseTh", sfx);
  175.       //setting _fadeInDelay to -1 will cause further calls to the device callback
  176.        //to simply memset 0 until sfx_class::pause() is called again
  177.       if(pauseThread == NULL) fadeInDelayRef = 0xffffffff;
  178.       else SDL_DetachThread(pauseThread); //make sure thread cleans up when finished
  179.     }
  180.  
  181.  
  182.   //FADING IN
  183.   } else if(fadeVolume.l < 1.0f){
  184.     //let device warm up before fading in
  185.     for(; (fadeInDelay)&&(i<len); ++i){
  186.       stream[i] = 0.0f;  --fadeInDelay;
  187.     }
  188.  
  189.     _fade_in_:;
  190.     for(; i<len; ++i){
  191.       if(sfx->isFadingOut()) goto _fade_out_;
  192.       else if(fadeVolume.l >= 1.0f){ fadeVolume = 1.0f; break; }
  193.       stream[i] *= fadeVolume;
  194.       fadeVolume += fadeDelta;
  195.     }
  196.  
  197.   }
  198.  
  199.  
  200.   //even if no fade is being done, output should be hard clipped no matter what
  201.   for(i=0; i<len; ++i) stream[i].clip();
  202.  
  203.  
  204.   //update any relevant new values for sfx
  205.   sfx->_getFadeVolume() = fadeVolume.l; //.l should equal .r anyway
  206.   if(fadeInDelayRef != 0xffffffff) fadeInDelayRef = fadeInDelay;
  207. }
  208.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement