Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "_sfx.hpp"
- char const _sfx_err_invalid[] = "invalid sfx_class instance";
- static inline int numBitsU32(Uint32 n){
- int active = 0;
- while(n != 0){
- if(n&1) ++active;
- n >>= 1;
- }
- return active;
- }
- extern void _sfx_callback(void* userdata, Uint8* _stream, int size);
- sfx_class::sfx_class(const Uint32 numTracks,
- const int sampleRate,
- const std::string& deviceName,
- const Uint32 bufferLength)
- {
- if(numTracks == 0) throw "numTracks = 0";
- if(numTracks > 1024) throw "numTracks > 1024";
- if(sampleRate < 1000) throw "sampleRate < 1000";
- if(sampleRate > 384000) throw "sampleRate > 384000";
- if(bufferLength < 32) throw "bufferLength < 32";
- if(bufferLength > 65535) throw "bufferLength > 65535";
- if(numBitsU32(bufferLength) != 1) throw "bufferLength is not a power of 2";
- const char* deviceNameRaw = nullptr;
- if(deviceName != "") deviceNameRaw = deviceName.c_str();
- SDL_AudioSpec specWant, specHave;
- specWant.freq = sampleRate;
- specWant.format = AUDIO_F32;
- specWant.channels = 2;
- specWant.samples = bufferLength;
- specWant.callback = _sfx_callback;
- specWant.userdata = this;
- _deviceID = SDL_OpenAudioDevice(deviceNameRaw,0,&specWant,&specHave,0);
- if(_deviceID == 0) throw SDL_GetError();
- _tracks = new std::vector<sfx_track>(numTracks);
- if(_tracks == nullptr){
- SDL_CloseAudioDevice(_deviceID);
- throw "failed to make tracks vector";
- }
- _lock = SDL_CreateMutex();
- if(_lock == nullptr){
- SDL_CloseAudioDevice(_deviceID);
- delete _tracks;
- throw SDL_GetError();
- }
- _volume = 1.0f;
- _pan = 0.0f;
- _fadeDelta = 1.0f / ( ((float)sampleRate)*_fadeDeltaSeconds );
- _fadeVolume = 0.0f;
- _sampleRate = sampleRate;
- _bufferLength = bufferLength;
- _deviceName = SDL_GetAudioDeviceName(_deviceID,0);
- _valid = true; //set valid to true to indicate success
- }
- sfx_class::~sfx_class(){
- lock(true);
- _closing = true;
- _fadeOut = true; //probably redundant
- if(_deviceID > 0){
- SDL_CloseAudioDevice(_deviceID);
- _playing = false;
- _deviceID = 0;
- }
- delete _auxStream;
- _auxStream = nullptr;
- delete _tracks;
- _tracks = nullptr;
- lock(false); //will throw if _valid is set to false before this
- _valid = false;
- if(_lock != nullptr){
- SDL_mutex* lock = _lock;
- _lock = nullptr;
- SDL_DestroyMutex(lock);
- }
- }
- void sfx_class::setVolume(sfx_f32s volume){
- if(!_valid) throw _sfx_err_invalid;
- _volume = volume.unit();
- }
- void sfx_class::setPan(float pan){
- if(!_valid) throw _sfx_err_invalid;
- _pan = std::clamp(pan, -1.0f,1.0f);
- }
- void sfx_class::lock(bool lockState){
- if(!_valid) throw _sfx_err_invalid;
- int failure;
- if(lockState) failure = (SDL_LockMutex(_lock)<0);
- else failure = (SDL_UnlockMutex(_lock)<0);
- if(failure) throw SDL_GetError();
- }
- void sfx_class::lockDevice(bool lockState){
- if(!_valid) throw _sfx_err_invalid;
- if(lockState) SDL_LockAudioDevice(_deviceID);
- else SDL_UnlockAudioDevice(_deviceID);
- }
- void sfx_class::pauseDevice(bool pauseState){
- if(!_valid) throw _sfx_err_invalid;
- if(_closing) return; //return early if closing
- lock(true);
- //this should occur when _sfx_callback's pause thread fails,
- //or if the callback itself does
- if(_fadeInDelay == 0xffffffff){ //if equal to -1
- SDL_PauseAudioDevice(_deviceID,1);
- _playing = false;
- _fadeInDelay = 0;
- }
- _fadeOut = pauseState;
- if(!pauseState && !_playing){
- //the purpose of fadeInDelay is to mute for some samples
- //to give the sdl audio device some time to warm up,
- //otherwise artifacts start to occur (for me, anyway)
- _fadeInDelay = _sampleRate*_fadeInDelaySeconds;
- if(!_timeStampStart) _timeStampStart = SDL_GetTicks64();
- if(!_timeStampEnd ) _timeStampEnd = SDL_GetTicks64();
- SDL_PauseAudioDevice(_deviceID,0);
- _playing = true;
- }
- lock(false);
- }
- void sfx_class::pauseDeviceAndWait(bool pauseState){
- Uint64 startTimeMS = SDL_GetTicks64();
- bool wasPaused = !_playing;
- pauseDevice(pauseState); //should throw if sfx is invalid
- if(_closing) return;
- if(pauseState){
- int timeoutCount = 0;
- do {
- SDL_Delay(_fadeDeltaSeconds*1000);
- timeoutCount += _fadeDeltaSeconds*1000;
- if(timeoutCount >= 2000) throw "device pause timed out";
- } while(_playing);
- } else if(wasPaused){ //delay only if previously paused
- Uint32 totalDelayMS = _totalFadeDelay*1000;
- Uint32 timeDifferenceMS = SDL_GetTicks64()-startTimeMS;
- SDL_Delay( std::max(totalDelayMS-timeDifferenceMS,0u)+1 ); //+1 just in case
- }
- }
- void sfx_class::waitForTracks(Uint64 timeoutMS){
- Uint64 timeStamp = SDL_GetTicks64();
- if(timeoutMS == 0) timeoutMS = 0xffffffffffffffff; //wait forever if 0
- while(getNumActiveTracks()){
- SDL_Delay(10);
- if((SDL_GetTicks64()-timeStamp)>timeoutMS) throw "waitForTracks() timed out!";
- }
- }
- void sfx_class::waitForTrack(int track, Uint64 timeoutMS){
- Uint64 timeStamp = SDL_GetTicks64();
- if(timeoutMS == 0) timeoutMS = 0xffffffffffffffff; //wait forever if 0
- while(isTrackPlaying(track)){
- SDL_Delay(10);
- if((SDL_GetTicks64()-timeStamp)>timeoutMS) throw "waitForTracks() timed out!";
- }
- }
- int sfx_class::play(const sfx_pcm* pcm,
- sfx_f32s volume,
- double speed,
- float pan)
- {
- Uint64 timeStamp = SDL_GetTicks64();
- if(!_valid) throw _sfx_err_invalid;
- if(pcm == nullptr) throw "pcm = nullptr";
- if(pcm->magic != SFX_PCM_MAGIC) throw "pcm is invalid";
- lock(true);
- //will return -1 if no available track was found
- int queuedTrackID = -1;
- std::vector<sfx_track>& tracksRef = *_tracks;
- size_t numTracks = tracksRef.size();
- for(size_t i=0; i<numTracks; ++i){
- sfx_track& track = tracksRef.at(i);
- if(track.pcm == nullptr){
- track.timeStamp = timeStamp;
- track.speed = speed;
- track.speedDelta = 0.0;
- track.volume = volume;
- track.volumeDelta = 0.0f;
- track.pan = pan;
- track.loops = pcm->loopCount;
- track.stopOnMute = true;
- track.stopping = false;
- track.pcm = (sfx_pcm*)pcm;
- queuedTrackID = i; break;
- }
- }
- lock(false);
- return queuedTrackID;
- }
- void sfx_class::stop(int track){
- if(!_valid) throw _sfx_err_invalid;
- setTrackVolumeDelta(track,-_fadeDeltaSeconds);
- }
- void sfx_class::stopAll(){
- if(!_valid) throw _sfx_err_invalid;
- size_t numTracks = _tracks->size();
- for(size_t i=0; i<numTracks; ++i) setTrackVolumeDelta(i,-_fadeDeltaSeconds);
- }
- Uint32 sfx_class::getNumActiveTracks(){
- if(!_valid) throw _sfx_err_invalid;
- lock(true);
- if(_closing) return 0;
- if(_tracks == nullptr) return 0;
- Uint32 amount = 0;
- std::vector<sfx_track>& tracksRef = *_tracks;
- size_t numTracks = tracksRef.size();
- for(size_t i=0; i<numTracks; ++i)
- if(tracksRef.at(i).pcm != nullptr) ++amount;
- lock(false);
- return amount;
- }
- bool sfx_class::isTrackPlaying(int track){
- if(!_valid) throw _sfx_err_invalid;
- if(track < 0) return false;
- return _tracks->at(track).pcm != nullptr;
- }
- sfx_track* sfx_class::getTrackPtr(int track){
- if(!_valid) throw _sfx_err_invalid;
- if(track < 0) return nullptr;
- return &_tracks->at(track);
- }
- void sfx_class::setTrackVolumeDelta(int track, sfx_f32s volumeDeltaSeconds){
- if(!_valid) throw _sfx_err_invalid;
- if(track < 0) return;
- sfx_track* _track = &_tracks->at(track);
- _track->volumeDelta.l = (1.0f/volumeDeltaSeconds.l)/_sampleRate;
- _track->volumeDelta.r = (1.0f/volumeDeltaSeconds.r)/_sampleRate;
- }
- void sfx_class::setTrackSpeedDelta(int track, double speedDeltaSeconds){
- if(!_valid) throw _sfx_err_invalid;
- if(track < 0) return;
- _tracks->at(track).speedDelta = (1.0f/speedDeltaSeconds)/_sampleRate;
- }
- void sfx_class::setAux(sfx_auxCallback callback, void* userdata,
- SDL_AudioFormat format, Uint16 channels)
- {
- if(callback == nullptr) throw "callback = nullptr";
- if(_format_invalid(format)) throw "format is invalid";
- if(channels!=1 && channels!=2) throw "channels must be either 1 or 2";
- size_t auxStream_len = _bufferLength * (SDL_AUDIO_BITSIZE(format)/8);
- delete _auxStream;
- _auxStream = new std::vector<char>(auxStream_len);
- if(_auxStream == nullptr) throw "failed to create _auxStream";
- _auxCallback = callback;
- _auxUserdata = userdata;
- _auxFormat = format;
- _auxChannels = channels;
- }
Add Comment
Please, Sign In to add comment