Advertisement
Kitomas

_sfx.hpp as of 2023-12-15

Dec 15th, 2023
722
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 7.23 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. union _every_sample_type {
  24.   void*     data;
  25.   Uint8*    u8;
  26.   Sint16*   i16;
  27.   float*    f32;
  28.   sfx_u8s*  u8s;
  29.   sfx_i16s* i16s;
  30.   sfx_f32s* f32s;
  31. };
  32.  
  33.  
  34.  
  35. #define _FORMAT_SWITCH(_channels, _format) ( ((_channels)<<16) | (_format) )
  36. enum _format_switch_enum {
  37.   fmt_u8   = _FORMAT_SWITCH(1,AUDIO_U8 ),
  38.   fmt_i16  = _FORMAT_SWITCH(1,AUDIO_S16),
  39.   fmt_f32  = _FORMAT_SWITCH(1,AUDIO_F32),
  40.   fmt_u8s  = _FORMAT_SWITCH(2,AUDIO_U8 ),
  41.   fmt_i16s = _FORMAT_SWITCH(2,AUDIO_S16),
  42.   fmt_f32s = _FORMAT_SWITCH(2,AUDIO_F32),
  43. };
  44.  
  45.  
  46.  
  47. static inline bool _format_invalid(SDL_AudioFormat format){
  48.   if(format == AUDIO_U8 ) return false;
  49.   if(format == AUDIO_S16) return false;
  50.   if(format == AUDIO_F32) return false;
  51.   return true; //any other format is invalid
  52. }
  53.  
  54.  
  55.  
  56.  
  57. #ifndef LERP2
  58. #define LERP2(_v0,_v1, _t) ( (_v0) + (_t)*((_v1)-(_v0)) )
  59. #endif
  60.  
  61. static inline sfx_f32s sfx_linearSample(std::vector<sfx_f32s>& src,
  62.                                         double& position,
  63.                                         Uint64 loopEnd = 0xffffffffffffffff)
  64. {
  65.   Uint64 intPosition = (Uint64)position;
  66.   double modPosition = position-intPosition;
  67.  
  68.   sfx_f32s smpA = src.at(   intPosition);
  69.   sfx_f32s smpB = src.at((++intPosition)%loopEnd);
  70.  
  71.   sfx_f32s smpOut(
  72.     LERP2(smpA.l, smpB.l, modPosition),
  73.     LERP2(smpA.r, smpB.r, modPosition)
  74.   );
  75.   return smpOut;
  76. }
  77.  
  78.  
  79.  
  80.  
  81. /****************/
  82. /* sfx_callback */
  83. /****************/
  84.  
  85. static inline sfx_f32s& _sfx_applyPan(sfx_f32s& sample, float pan){
  86.   //(i think this is how audacity does it...)
  87.   if(pan < 0){
  88.     sample.l += sample.r*(-pan);
  89.     sample.r *= 1.0f+pan;
  90.   } else if(pan > 0){
  91.     sample.r += sample.l*pan;
  92.     sample.l *= 1.0f-pan;
  93.   }
  94.   return sample;
  95. }
  96.  
  97.  
  98. static inline void _sfx_mixTrack(sfx_track& track, Uint64 sfxTimeStampEnd,
  99.                                  sfx_f32s* dst, int dst_len)
  100. {
  101.   sfx_pcm* pcm = track.pcm;
  102.   const sfx_f32s speed0VolumeDelta = -( (1000.0f/_speed0DeltaVolMS) / pcm->sampleRate );
  103.  
  104.   if(track.timeStamp > 0){
  105.     //calculate position based on difference between device and clip timestamps
  106.      //(timeStampEnd is used instead of Start, so the clip's time stamp
  107.      // can be compared to the last time the device callback exited)
  108.     double difference = (double)(track.timeStamp-sfxTimeStampEnd)/1000;
  109.     difference *= pcm->sampleRate; //convert seconds to samples
  110.     track.position = -(difference*track.speed); //starts playing when position reaches 0
  111.  
  112.     track.timeStamp = 0; //to ensure that this only occurs once per clip queued
  113.   }
  114.  
  115.  
  116.   //make copies of relevant track values
  117.   float  pan       = std::clamp(track.pan, -1.0f,1.0f);
  118.   Uint64 loopStart = pcm->loopStart;
  119.   Uint64 loopEnd   = pcm->loopEnd;
  120.   std::vector<sfx_f32s>& src = *pcm->samples;
  121.    //
  122.   double position = track.position;
  123.   double speed    = track.speed;
  124.   Uint16 loops    = track.loops;
  125.   bool   stopping = track.stopping;
  126.  
  127.   //make sure volume is clamped before applying it to the first sample,
  128.    //since volume is only clamped after volumeDelta is applied otherwise
  129.   track.volume.unit(); //clamp to 0.0 -> 1.0
  130.  
  131.  
  132.   for(int i=0; i<dst_len; ++i){
  133.     //loop handling (or end-of-clip handling i guess)
  134.     if(position >= loopEnd){ //clip finished loop
  135.       if(!loops){ stopping = true; break; } //mark as inactive and break
  136.       if(loops != 0xffff) --loops; //decrement loops (unless infinite)
  137.       position -= (Uint64)position; //basically position %= 1
  138.       position += loopStart; //now new_position = loopStart + old_position%1
  139.  
  140.     } else if(position < 0){ //clip has yet to start playing
  141.       if(position < -speed){
  142.         position += speed; //step forward by current speed
  143.         continue;
  144.       } else { //if position >= -speed, the clip should start next sample
  145.         position = 0; //make sure clip starts at 0
  146.       }
  147.  
  148.     }
  149.  
  150.     //get sample, apply volume, hard clip to -1.0f -> 1.0f
  151.     sfx_f32s sample = sfx_linearSample(src, position, loopEnd);
  152.     sample *= track.volume;
  153.     sample.clip();
  154.  
  155.     //apply pan and mix, apply volumeDelta, clamp volume to 0.0f -> 1.0f
  156.     dst[i] += _sfx_applyPan(sample,pan);
  157.     track.volume += track.volumeDelta;
  158.     track.volume.unit();
  159.  
  160.     //update position, apply speedDelta, start fade out if clip's speed <= 0
  161.     position += speed;
  162.     speed += track.speedDelta;
  163.     if(speed <= 0){
  164.       speed = 0;
  165.       track.volumeDelta = speed0VolumeDelta;
  166.     }
  167.   }
  168.  
  169.  
  170.   //update relevant values in track
  171.   track.position = position;
  172.   track.speed    = speed;
  173.   track.loops    = loops;
  174.   track.stopping = stopping;
  175.  
  176.   if(track.volume.l<=0 && track.volume.r<=0  &&  track.stopOnMute)
  177.     track.stopping = true;
  178.  
  179.   if(track.stopping) track.pcm = nullptr;
  180. }
  181.  
  182.  
  183.  
  184. extern int _sfx_pauseThread(void* data);
  185.  
  186. static inline void _sfx_globalFade(sfx_class* sfx, sfx_f32s* stream, int len){
  187.   sfx_f32s  fadeDelta      = sfx->_getFadeDelta();
  188.   sfx_f32s  fadeVolume     = sfx->_getFadeVolume();
  189.   sfx_f32s& volume         = sfx->_getVolume();
  190.   float&    pan            = sfx->_getPan();
  191.   Uint32&   fadeInDelayRef = sfx->_getFadeInDelay();
  192.   Uint32    fadeInDelay    = fadeInDelayRef;
  193.  
  194.   int i = 0; //this index is shared, as the loops can jump to others at will
  195.  
  196.  
  197.   //FADING OUT
  198.   if(sfx->isFadingOut()){ _fade_out_:;
  199.     for(; i<len; ++i){
  200.       if(!sfx->isFadingOut()) goto _fade_in_;
  201.       stream[i] *= fadeVolume;
  202.       fadeVolume -= fadeDelta;
  203.       if(fadeVolume.l < 0) fadeVolume = 0.0f;
  204.     }
  205.  
  206.     //trigger pause thread if fade out is complete
  207.     if(fadeVolume.l <= 0.0f){
  208.       SDL_Thread* pauseThread = SDL_CreateThread(_sfx_pauseThread,"_PauseTh", sfx);
  209.       //setting _fadeInDelay to -1 will cause further calls to the device callback
  210.        //to simply memset 0 until sfx_class::pause() is called again
  211.       if(pauseThread == NULL) fadeInDelayRef = 0xffffffff;
  212.       else SDL_DetachThread(pauseThread); //make sure thread cleans up when finished
  213.     }
  214.  
  215.  
  216.   //FADING IN
  217.   } else if(fadeVolume.l < 1.0f){
  218.     for(; (fadeInDelay)&&(i<len); ++i){ //let device warm up before fading in
  219.       stream[i] = 0.0f;  --fadeInDelay;
  220.     }
  221.     _fade_in_:;
  222.     for(; i<len; ++i){
  223.       if(sfx->isFadingOut()) goto _fade_out_;
  224.       else if(fadeVolume.l >= 1.0f){ fadeVolume = 1.0f; break; }
  225.       stream[i] *= fadeVolume;
  226.       fadeVolume += fadeDelta;
  227.     }
  228.  
  229.   }
  230.  
  231.  
  232.   //even if no fade is being done, output should be hard clipped no matter what
  233.    //(while we're at it, apply global volume and pan too i guess)
  234.   for(i=0; i<len; ++i){
  235.     stream[i] *= volume;
  236.     stream[i].clip();
  237.     stream[i] = _sfx_applyPan(stream[i], std::clamp(pan, -1.0f,1.0f));
  238.   }
  239.  
  240.  
  241.   //update any relevant new values for sfx
  242.   sfx->_getFadeVolume() = fadeVolume.l; //.l should equal .r anyway
  243.   if(fadeInDelayRef != 0xffffffff) fadeInDelayRef = fadeInDelay;
  244. }
  245.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement