Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #pragma once
- #include <sfx.hpp>
- //500ms
- #define _totalFadeDelay (0.500f)
- //linearly fade over the course of 10ms
- #define _fadeDeltaSeconds (0.010f)
- //the most common audio clipping ends at 10-11ms after unpausing,
- //but i've seen clipping as far as ~450ms after unpausing
- #define _fadeInDelaySeconds (_totalFadeDelay - _fadeDeltaSeconds)
- //lower clip's volume to 0 within 10ms if speed reaches 0
- #define _speed0DeltaVolMS (10.0f)
- union _every_sample_type {
- void* data;
- Uint8* u8;
- Sint16* i16;
- float* f32;
- sfx_u8s* u8s;
- sfx_i16s* i16s;
- sfx_f32s* f32s;
- };
- #define _FORMAT_SWITCH(_channels, _format) ( ((_channels)<<16) | (_format) )
- enum _format_switch_enum {
- fmt_u8 = _FORMAT_SWITCH(1,AUDIO_U8 ),
- fmt_i16 = _FORMAT_SWITCH(1,AUDIO_S16),
- fmt_f32 = _FORMAT_SWITCH(1,AUDIO_F32),
- fmt_u8s = _FORMAT_SWITCH(2,AUDIO_U8 ),
- fmt_i16s = _FORMAT_SWITCH(2,AUDIO_S16),
- fmt_f32s = _FORMAT_SWITCH(2,AUDIO_F32),
- };
- static inline bool _format_invalid(SDL_AudioFormat format){
- if(format == AUDIO_U8 ) return false;
- if(format == AUDIO_S16) return false;
- if(format == AUDIO_F32) return false;
- return true; //any other format is invalid
- }
- #ifndef LERP2
- #define LERP2(_v0,_v1, _t) ( (_v0) + (_t)*((_v1)-(_v0)) )
- #endif
- static inline sfx_f32s sfx_linearSample(std::vector<sfx_f32s>& src,
- double& position,
- Uint64 loopEnd = 0xffffffffffffffff)
- {
- Uint64 intPosition = (Uint64)position;
- double modPosition = position-intPosition;
- sfx_f32s smpA = src.at( intPosition);
- sfx_f32s smpB = src.at((++intPosition)%loopEnd);
- sfx_f32s smpOut(
- LERP2(smpA.l, smpB.l, modPosition),
- LERP2(smpA.r, smpB.r, modPosition)
- );
- return smpOut;
- }
- /****************/
- /* sfx_callback */
- /****************/
- static inline sfx_f32s& _sfx_applyPan(sfx_f32s& sample, float pan){
- //(i think this is how audacity does it...)
- if(pan < 0){
- sample.l += sample.r*(-pan);
- sample.r *= 1.0f+pan;
- } else if(pan > 0){
- sample.r += sample.l*pan;
- sample.l *= 1.0f-pan;
- }
- return sample;
- }
- static inline void _sfx_mixTrack(sfx_track& track, Uint64 sfxTimeStampEnd,
- sfx_f32s* dst, int dst_len)
- {
- sfx_pcm* pcm = track.pcm;
- const sfx_f32s speed0VolumeDelta = -( (1000.0f/_speed0DeltaVolMS) / pcm->sampleRate );
- if(track.timeStamp > 0){
- //calculate position based on difference between device and clip timestamps
- //(timeStampEnd is used instead of Start, so the clip's time stamp
- // can be compared to the last time the device callback exited)
- double difference = (double)(track.timeStamp-sfxTimeStampEnd)/1000;
- difference *= pcm->sampleRate; //convert seconds to samples
- track.position = -(difference*track.speed); //starts playing when position reaches 0
- track.timeStamp = 0; //to ensure that this only occurs once per clip queued
- }
- //make copies of relevant track values
- float pan = std::clamp(track.pan, -1.0f,1.0f);
- Uint64 loopStart = pcm->loopStart;
- Uint64 loopEnd = pcm->loopEnd;
- std::vector<sfx_f32s>& src = *pcm->samples;
- //
- double position = track.position;
- double speed = track.speed;
- Uint16 loops = track.loops;
- bool stopping = track.stopping;
- //make sure volume is clamped before applying it to the first sample,
- //since volume is only clamped after volumeDelta is applied otherwise
- track.volume.unit(); //clamp to 0.0 -> 1.0
- for(int i=0; i<dst_len; ++i){
- //loop handling (or end-of-clip handling i guess)
- if(position >= loopEnd){ //clip finished loop
- if(!loops){ stopping = true; break; } //mark as inactive and break
- if(loops != 0xffff) --loops; //decrement loops (unless infinite)
- position -= (Uint64)position; //basically position %= 1
- position += loopStart; //now new_position = loopStart + old_position%1
- } else if(position < 0){ //clip has yet to start playing
- if(position < -speed){
- position += speed; //step forward by current speed
- continue;
- } else { //if position >= -speed, the clip should start next sample
- position = 0; //make sure clip starts at 0
- }
- }
- //get sample, apply volume, hard clip to -1.0f -> 1.0f
- sfx_f32s sample = sfx_linearSample(src, position, loopEnd);
- sample *= track.volume;
- sample.clip();
- //apply pan and mix, apply volumeDelta, clamp volume to 0.0f -> 1.0f
- dst[i] += _sfx_applyPan(sample,pan);
- track.volume += track.volumeDelta;
- track.volume.unit();
- //update position, apply speedDelta, start fade out if clip's speed <= 0
- position += speed;
- speed += track.speedDelta;
- if(speed <= 0){
- speed = 0;
- track.volumeDelta = speed0VolumeDelta;
- }
- }
- //update relevant values in track
- track.position = position;
- track.speed = speed;
- track.loops = loops;
- track.stopping = stopping;
- if(track.volume.l<=0 && track.volume.r<=0 && track.stopOnMute)
- track.stopping = true;
- if(track.stopping) track.pcm = nullptr;
- }
- extern int _sfx_pauseThread(void* data);
- static inline void _sfx_globalFade(sfx_class* sfx, sfx_f32s* stream, int len){
- sfx_f32s fadeDelta = sfx->_getFadeDelta();
- sfx_f32s fadeVolume = sfx->_getFadeVolume();
- sfx_f32s& volume = sfx->_getVolume();
- float& pan = sfx->_getPan();
- Uint32& fadeInDelayRef = sfx->_getFadeInDelay();
- Uint32 fadeInDelay = fadeInDelayRef;
- int i = 0; //this index is shared, as the loops can jump to others at will
- //FADING OUT
- if(sfx->isFadingOut()){ _fade_out_:;
- for(; i<len; ++i){
- if(!sfx->isFadingOut()) goto _fade_in_;
- stream[i] *= fadeVolume;
- fadeVolume -= fadeDelta;
- if(fadeVolume.l < 0) fadeVolume = 0.0f;
- }
- //trigger pause thread if fade out is complete
- if(fadeVolume.l <= 0.0f){
- SDL_Thread* pauseThread = SDL_CreateThread(_sfx_pauseThread,"_PauseTh", sfx);
- //setting _fadeInDelay to -1 will cause further calls to the device callback
- //to simply memset 0 until sfx_class::pause() is called again
- if(pauseThread == NULL) fadeInDelayRef = 0xffffffff;
- else SDL_DetachThread(pauseThread); //make sure thread cleans up when finished
- }
- //FADING IN
- } else if(fadeVolume.l < 1.0f){
- for(; (fadeInDelay)&&(i<len); ++i){ //let device warm up before fading in
- stream[i] = 0.0f; --fadeInDelay;
- }
- _fade_in_:;
- for(; i<len; ++i){
- if(sfx->isFadingOut()) goto _fade_out_;
- else if(fadeVolume.l >= 1.0f){ fadeVolume = 1.0f; break; }
- stream[i] *= fadeVolume;
- fadeVolume += fadeDelta;
- }
- }
- //even if no fade is being done, output should be hard clipped no matter what
- //(while we're at it, apply global volume and pan too i guess)
- for(i=0; i<len; ++i){
- stream[i] *= volume;
- stream[i].clip();
- stream[i] = _sfx_applyPan(stream[i], std::clamp(pan, -1.0f,1.0f));
- }
- //update any relevant new values for sfx
- sfx->_getFadeVolume() = fadeVolume.l; //.l should equal .r anyway
- if(fadeInDelayRef != 0xffffffff) fadeInDelayRef = fadeInDelay;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement