Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "../include/kit_sdl2/kit_kmixer.h"
- #include "../_private/include/_kit_privmacro.h"
- #include "../_private/include/_kit_kmixerAllPrivate.h"
- void _kit_kmixerAsyncRemoveCallback(void* _userdata){
- if(_userdata != NULL){
- _kit_kmixerAsyncUserdata* userdata = _userdata;
- //destroy all of the track mutexes
- _kit_kmixerAsyncTrack* tracks = userdata->tracks;
- Uint32 numTracks = userdata->numTracks;
- for(Uint32 i=0; i<numTracks; ++i){
- if(tracks[i].lock != NULL){
- SDL_DestroyMutex(tracks[i].lock);
- tracks[i].lock = NULL;
- }
- }
- //destroy main userdata mutex
- if(userdata->lock != NULL){
- SDL_DestroyMutex(userdata->lock);
- userdata->lock = NULL;
- }
- SDL_free(userdata);
- }
- }
- void _kit_kmixerAsyncTrackCallback(void* userdata, void* _stream, int size, SDL_bool hasInput){
- _kit_kmixerAsyncTrack* track = userdata;
- kit_acodecPCM* pcm = track->pcm;
- SDL_LockMutex(track->lock);
- kit_coreMemset(_stream,0,size);
- //if pcm is NULL, the clip has finished playing, and will be deactivated by the main voice
- if(pcm == NULL) goto _exit_early;
- kit_kmixerVoiceSpec* vspec = track->vspec;
- //lower volume to 0 within 10ms if speed reaches 0
- #define speed0DeltaVolMS 10.0f
- const float speed0DeltaVol = -( (1000.0f/speed0DeltaVolMS) / vspec->freq);
- if(track->timeStamp){
- //loops will count down to 0, whereupon the clip will finish (-1 for endless loop)
- track->loops = pcm->loopCount;
- //calculate position based on difference between device and clip timestamps
- Uint64 trackTimeStamp = track->timeStamp;
- Uint64 deviceTimeStamp = track->device->_timeStampEnd;
- //device->_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)(trackTimeStamp-deviceTimeStamp)/1000;
- difference *= vspec->freq; //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
- }
- float pan = track->pan;
- double position = track->position;
- float speed = track->speed;
- Uint16 loops = track->loops;
- Uint16 stopping = track->stopping;
- Uint64 loopStart = pcm->loopStart;
- Uint64 loopEnd = pcm->loopEnd-1;
- //Uint64 src_len = pcm->numSamples; (unnecessary, as loopEnd is used instead)
- Uint16 dst_len = size / (sizeof(float)<<vspec->stereo);
- //make sure volume is clamped before applying it to the first sample,
- //since volume is only clamped after deltaVol is applied otherwise
- track->volume.l = CLAMP(track->volume.l, 0.0f,1.0f);
- track->volume.r = CLAMP(track->volume.r, 0.0f,1.0f);
- if(vspec->stereo){
- kit_acodecPCM_F32S* dst = _stream, *src = pcm->f32s;
- if(track->linear){ //linear resampling
- for(Uint32 i=0; i<dst_len; ++i) _stereo_iteration(_stereo_linear)
- } else { //nearest-neighbor resampling
- for(Uint32 i=0; i<dst_len; ++i) _stereo_iteration(_stereo_nearest)
- }
- } else { //mono
- float* dst = _stream, *src = pcm->f32;
- if(track->linear){ //linear resampling
- for(Uint32 i=0; i<dst_len; ++i) _mono_iteration(_mono_linear)
- } else { //nearest-neighbor resampling
- for(Uint32 i=0; i<dst_len; ++i) _mono_iteration(_mono_nearest)
- }
- }
- //update some values
- //track->pcm = pcm; (unnecessary)
- track->position = position;
- track->speed = speed;
- track->loops = loops;
- track->stopping = stopping;
- //if the track's stopOnMute condition is met, mark track as finished,
- //so it'll be deactivated until a new clip is queued
- if(!vspec->stereo) track->volume.r = track->volume.l;
- if(track->volume.l<=0 && track->volume.r<=0 && track->stopOnMute)
- track->stopping = SDL_TRUE;
- _exit_early:
- SDL_UnlockMutex(track->lock);
- }
- void _kit_kmixerAsyncVoiceCallback(void* _userdata, void* _stream, int size, SDL_bool hasInput){
- _kit_kmixerAsyncUserdata* userdata = _userdata;
- SDL_LockMutex(userdata->lock);
- if(!kit_kmixerAsyncGetActiveTracks(userdata->device, userdata->rawIndex)){
- kit_coreMemset(_stream,0,size);
- goto _exit_early;
- }
- float pan = userdata->pan;
- kit_kmixerVoiceSpec* vspec = &userdata->vspec;
- Uint16 dst_len = size / (sizeof(float)<<vspec->stereo);
- userdata->volume.l = CLAMP(userdata->volume.l, 0.0f,1.0f);
- userdata->volume.r = CLAMP(userdata->volume.r, 0.0f,1.0f);
- if(vspec->stereo){
- kit_acodecPCM_F32S* dst = _stream;
- for(Uint32 i=0; i<dst_len; ++i){
- kit_acodecPCM_F32S sample = _stereo_volume(dst[i], (void*)userdata);
- dst[i] = _apply_pan(_stereo_clamp(sample), pan);
- _stereo_deltaVol((void*)userdata);
- }
- } else { //mono
- float* dst = _stream;
- for(Uint32 i=0; i<dst_len; ++i){
- dst[i] = _mono_clamp(_mono_volume(dst[i], (void*)userdata));
- _mono_deltaVol((void*)userdata);
- }
- }
- SDL_bool* main_active_p = kit_kmixerVoiceGetActiveRef(userdata->device, userdata->rawIndex);
- _kit_kmixerAsyncTrack* tracks = userdata->tracks;
- Uint32 numTracks = userdata->numTracks;
- //force all tracks to deactivate if main voice's stopOnMute condition is met
- if(!vspec->stereo) userdata->volume.r = userdata->volume.l;
- if(userdata->volume.l<=0 && userdata->volume.r<=0 && userdata->stopOnMute){
- for(Uint32 ti=0; ti<numTracks; ++ti){
- _kit_kmixerAsyncTrack* track = &tracks[ti];
- _deactivate_track_if(track->pcm!=NULL, track);
- }
- //setting active to false here directly should be safe,
- //since voices are always locked during their callback
- //*main_active_p = SDL_FALSE;
- //otherwise, just deactivate the tracks that have finished playing its queued clip
- } else {
- //Uint32 inactiveTracks = 0;
- for(Uint32 ti=0; ti<numTracks; ++ti){
- _kit_kmixerAsyncTrack* track = &tracks[ti];
- if(kit_kmixerVoiceExists(track->device, track->rawIndex)<1) continue;
- SDL_bool track_active = kit_kmixerVoiceGetActive(track->device, track->rawIndex);
- _deactivate_track_if(track->stopping, track);
- //if(!track_active) ++inactiveTracks;
- }
- //deactivate main voice if all of its tracks are inactive
- //if(inactiveTracks == numTracks) *main_active_p = SDL_FALSE;
- }
- _exit_early:
- SDL_UnlockMutex(userdata->lock);
- }
- Uint32 kit_kmixerAsyncAdd(kit_kmixerDevice* device,
- SDL_bool linearInterpolation, SDL_bool stereo,
- Uint32 outputVoiceID, Uint32 numTracks)
- {
- Uint32 mainVoiceID = 0; //0 for error by default
- _DEVICE_VALIDITY_CHECK(0)
- _IF_SDLERR(numTracks==0,;,"!numTracks")
- int outputExists = kit_kmixerVoiceExists(device, outputVoiceID);
- _IF_SDLERR(outputExists<0,;,"outputVoiceID out of bounds")
- _IF_SDLERR(!outputExists,;,"output voice doesn't exist")
- //create main voice
- //(userdata is contiguous with the track data)
- size_t userdataSize = sizeof(_kit_kmixerAsyncUserdata);
- userdataSize += sizeof(_kit_kmixerAsyncTrack)*numTracks;
- kit_kmixerVoiceSpec vspec = kit_kmixerVoiceGetSpec(device,0);
- _IF_GOTO_ERROR(vspec.format==0,;)
- vspec.remove = _kit_kmixerAsyncRemoveCallback;
- vspec.callback = _kit_kmixerAsyncVoiceCallback;
- _IF_GOTO_ERROR(kit_coreRealloc(&vspec.userdata,0,userdataSize),;)
- vspec.stereo = stereo&1; //stereo is the only non-pointer member that's different
- mainVoiceID = kit_kmixerVoiceAdd(device,&vspec,outputVoiceID);
- _IF_GOTO_ERROR(!mainVoiceID, SDL_free(vspec.userdata) )
- kit_kmixerVoiceSetActive(device, mainVoiceID, SDL_TRUE);
- //fill in userdata
- _kit_kmixerAsyncUserdata* userdata = vspec.userdata;
- userdata->type = type_UDAT; //U[SER]DAT[A]
- userdata->volume.l = 1.0f;
- userdata->volume.r = 1.0f;
- userdata->stopOnMute = SDL_FALSE; //unlike track, main's stopOnMute is false by default
- userdata->rawIndex = mainVoiceID;
- userdata->device = device;
- userdata->vspec = vspec;
- userdata->tracks = (void*)userdata+sizeof(_kit_kmixerAsyncUserdata);
- userdata->numTracks = numTracks;
- userdata->lock = SDL_CreateMutex();
- _IF_GOTO_ERROR(userdata->lock==NULL, kit_kmixerVoiceRemove(device, mainVoiceID) )
- //create tracks
- vspec.remove = NULL; //only the main voice should be doing the free()'ing
- vspec.callback = _kit_kmixerAsyncTrackCallback;
- _kit_kmixerAsyncTrack* tracks = userdata->tracks;
- for(Uint32 ti=0; ti<numTracks; ++ti){
- _kit_kmixerAsyncTrack* track = vspec.userdata = &tracks[ti];
- track->type = type_TRCK; //TR[A]CK
- track->volume.l = 1.0f;
- track->volume.r = 1.0f;
- track->stopOnMute = SDL_TRUE;
- track->rawIndex = kit_kmixerVoiceAdd(device,&vspec,mainVoiceID);
- track->device = device;
- track->lock = SDL_CreateMutex();
- track->vspec = &userdata->vspec;
- track->linear = linearInterpolation&1;
- if(!track->rawIndex || track->lock==NULL){
- kit_kmixerVoiceRemove(device, mainVoiceID); return 0;
- }
- kit_kmixerVoiceSetActive(device, track->rawIndex, SDL_TRUE);
- }
- _noerr_:
- _error_:
- return mainVoiceID;
- }
- Uint32 kit_kmixerAsyncPlayPVS(kit_kmixerDevice* device, Uint32 voiceID, kit_acodecPCM* pcm,
- float pan, float volumeL, float volumeR, double speedMultiplier)
- {
- Uint64 trackTimeStamp = SDL_GetTicks64();
- Uint32 trackNum = -1; //set to -1 for error by default
- SDL_bool udat_lock = SDL_FALSE, trck_lock = SDL_FALSE;
- _kit_kmixerAsyncUserdata* userdata = NULL;
- _kit_kmixerAsyncTrack* track = NULL;
- _IF_SDLERR(pcm==NULL,;,"!pcm")
- _IF_SDLERR(!NORMALIZED(pan),;,"pan must be -1.0f -> 1.0f")
- _IF_SDLERR(volumeL<0,;,"volumeL < 0")
- _IF_SDLERR(volumeR<0,;,"volumeR < 0")
- _IF_SDLERR(speedMultiplier<=0,;,"speedMultiplier <= 0")
- kit_kmixerVoiceSpec vspec = kit_kmixerVoiceGetSpec(device, voiceID);
- _IF_GOTO_ERROR(!vspec.format,;)
- userdata = vspec.userdata;
- _IF_SDLERR(userdata==NULL,;,"invalid async voice")
- _IF_SDLERR(userdata->type!=type_UDAT,;,"invalid async voice")
- _IF_SDLERR(pcm->magic!=KPCM_MAGIC,;,"invalid pcm struct")
- _IF_SDLERR(pcm->format!=AUDIO_F32,;,"pcm->format != AUDIO_F32")
- _IF_SDLERR(pcm->loopStart>=pcm->loopEnd,;,"pcm->loopStart >= pcm->loopEnd")
- _IF_SDLERR(pcm->loopEnd>pcm->numSamples,;,"pcm->loopEnd > pcm->numSamples")
- _IF_SDLERR(pcm->numSamples<2,;,"pcm->numSamples < 2")
- _IF_SDLERR(pcm->sampleRate!=vspec.freq,;,"pcm->sampleRate != device freq")
- _IF_SDLERR((pcm->channels-1)!=vspec.stereo,;,"pcm->channels is invalid")
- //look for empty track to queue pcm clip with
- kit_kmixerDeviceLock(device, SDL_TRUE);
- SDL_LockMutex(userdata->lock); udat_lock = SDL_TRUE;
- Uint32 numTracks = userdata->numTracks;
- _kit_kmixerAsyncTrack* tracks = userdata->tracks;
- for(Uint32 i=0; i<numTracks; ++i){
- track = &tracks[i];
- SDL_LockMutex(track->lock); trck_lock = SDL_TRUE;
- while(device->_timeStampStart > device->_timeStampEnd) SDL_Delay(1);
- if(!track->stopping && track->pcm==NULL){
- int exists = kit_kmixerVoiceExists(device, track->rawIndex);
- _IF_SDLERR(!exists,;,"track %u's voice doesn't exist",i)
- trackNum = i; break;
- }
- SDL_UnlockMutex(track->lock); trck_lock = SDL_FALSE;
- }
- kit_kmixerDeviceLock(device, SDL_FALSE);
- SDL_UnlockMutex(userdata->lock); udat_lock = SDL_FALSE;
- _IF_GOTO(trackNum==-1,_noerr_,;) //exit early if no free track was found
- //track = &tracks[trackNum]; (track was already set inside the loop)
- track->pan = pan;
- track->volume.l = volumeL;
- track->volume.r = volumeR;
- track->delta.l = 0.0f;
- track->delta.r = 0.0f;
- track->stopOnMute = SDL_TRUE;
- track->pcm = pcm;
- track->timeStamp = trackTimeStamp;
- track->speed = speedMultiplier;
- track->deltaS = 0.0;
- //kit_kmixerVoiceSetActive(device, track->rawIndex, SDL_TRUE);
- //if(!kit_kmixerVoiceGetActive(device, voiceID)) kit_kmixerVoiceSetActive(device, voiceID, SDL_TRUE);
- _noerr_: if(trackNum == -1) trackNum = -2; //-2 for 'no available track found; no error'
- _error_:
- if(udat_lock) SDL_UnlockMutex(userdata->lock);
- if(trck_lock) SDL_UnlockMutex(track->lock);
- return trackNum;
- }
- //returns -1 (U64_MAX) pointer on error
- #define UDAT_ERROR ((void*)U64_MAX) //(also used for GetTrack's error)
- _kit_kmixerAsyncUserdata* _kit_kmixerAsyncGetUserdata(kit_kmixerDevice* device, Uint32 voiceID){
- kit_kmixerVoiceSpec vspec = kit_kmixerVoiceGetSpec(device, voiceID);
- _IF_GOTO_ERROR(!vspec.format,;)
- _kit_kmixerAsyncUserdata* userdata = vspec.userdata;
- _IF_SDLERR(userdata==NULL,;,"invalid async voice")
- _IF_SDLERR(userdata->type!=type_UDAT,;,"invalid async voice")
- /*!err*/ return userdata;
- _error_: return UDAT_ERROR; // = (void*)0xffffffffffffffff
- }
- _kit_kmixerAsyncTrack* _kit_kmixerAsyncGetTrack(kit_kmixerDevice* device,
- _kit_kmixerAsyncUserdata* userdata, Uint32 trackNum)
- {
- _IF_SDLERR(trackNum>=userdata->numTracks,;,"trackNum out of bounds")
- _kit_kmixerAsyncTrack* track = &userdata->tracks[trackNum];
- int exists = kit_kmixerVoiceExists(device, track->rawIndex);
- _IF_SDLERR(!exists,;,"track %u's voice nonexistent",trackNum)
- /*!err*/ return track; //will not error even if no clip is queued (track->pcm = NULL)
- _error_: return UDAT_ERROR; // = (void*)0xffffffffffffffff
- }
- int kit_kmixerAsyncStopTrack(kit_kmixerDevice* device, Uint32 voiceID,
- Uint32 trackNum)
- {
- _kit_kmixerAsyncUserdata* userdata = _kit_kmixerAsyncGetUserdata(device,voiceID);
- _IF_GOTO_ERROR(userdata==UDAT_ERROR,;)
- _kit_kmixerAsyncTrack* track = _kit_kmixerAsyncGetTrack(device, userdata, trackNum);
- _IF_GOTO_ERROR(track==UDAT_ERROR,;)
- if(track->pcm == NULL) return 1; //already stopped; return a 1
- else track->pcm = NULL;
- int exists = kit_kmixerVoiceExists(device, track->rawIndex);
- _IF_SDLERR(!exists,;,"track %u's voice nonexistent",trackNum)
- //_IF_GOTO_ERROR(kit_kmixerVoiceSetActive(device, track->rawIndex, SDL_FALSE),;)
- /*!err*/ return 0;
- _error_: return -1;
- }
- int kit_kmixerAsyncStopAllTracks(kit_kmixerDevice* device, Uint32 voiceID){
- int returnStatus = -1;
- SDL_bool locked = SDL_FALSE;
- _kit_kmixerAsyncUserdata* userdata = _kit_kmixerAsyncGetUserdata(device,voiceID);
- _IF_GOTO_ERROR(userdata==UDAT_ERROR,;)
- _IF_GOTO_ERROR(kit_kmixerVoiceLock(device, voiceID)<0,;)
- locked = SDL_TRUE;
- Uint32 numTracks = userdata->numTracks;
- _kit_kmixerAsyncTrack* tracks = userdata->tracks;
- SDL_bool tracksAlreadyStopped = SDL_TRUE; //set to false if any track was halted
- for(Uint32 i=0; i<numTracks; ++i){
- if(tracks[i].pcm != NULL) tracksAlreadyStopped = SDL_FALSE;
- tracks[i].pcm = NULL;
- Uint32 rawIndex = tracks[i].rawIndex;
- int exists = kit_kmixerVoiceExists(device, rawIndex);
- _IF_SDLERR(!exists,;,"track %u's voice nonexistent",rawIndex)
- //_IF_GOTO_ERROR(kit_kmixerVoiceSetActive(device, rawIndex, SDL_FALSE),;)
- }
- returnStatus = tracksAlreadyStopped;
- _error_:
- if(locked && kit_kmixerVoiceUnlock(device, voiceID)<0) returnStatus = -1;
- return returnStatus;
- }
- Uint32 kit_kmixerAsyncGetActiveTracks(kit_kmixerDevice* device, Uint32 voiceID){
- Uint32 activeTracks = -1; //-1 for error by default
- _kit_kmixerAsyncUserdata* userdata = _kit_kmixerAsyncGetUserdata(device,voiceID);
- _IF_GOTO_ERROR(userdata==UDAT_ERROR,;)
- activeTracks = 0; //there are no errors past this, so setting to 0 is safe here
- Uint32 numTracks = userdata->numTracks;
- _kit_kmixerAsyncTrack* tracks = userdata->tracks;
- for(Uint32 i=0; i<numTracks; ++i){
- if(tracks[i].pcm != NULL) ++activeTracks;
- }
- _error_: return activeTracks;
- }
- Uint32 kit_kmixerAsyncGetNumTracks(kit_kmixerDevice* device, Uint32 voiceID){
- _kit_kmixerAsyncUserdata* userdata = _kit_kmixerAsyncGetUserdata(device,voiceID);
- if(userdata==UDAT_ERROR) return -1;
- else return userdata->numTracks;
- }
- int kit_kmixerAsyncGetTrackPlayState(kit_kmixerDevice* device, Uint32 voiceID,
- Uint32 trackNum)
- {
- _kit_kmixerAsyncUserdata* userdata = _kit_kmixerAsyncGetUserdata(device,voiceID);
- _IF_GOTO_ERROR(userdata==UDAT_ERROR,;)
- _kit_kmixerAsyncTrack* track = _kit_kmixerAsyncGetTrack(device, userdata, trackNum);
- _IF_GOTO_ERROR(track==UDAT_ERROR,;)
- /*!err*/ return track->pcm != NULL;
- _error_: return -1;
- }
- double kit_kmixerAsyncGetTrackPosition(kit_kmixerDevice* device, Uint32 voiceID,
- Uint32 trackNum)
- {
- double position = NAN; //NaN for error by default
- _kit_kmixerAsyncUserdata* userdata = _kit_kmixerAsyncGetUserdata(device,voiceID);
- _IF_GOTO_ERROR(userdata==UDAT_ERROR,;)
- _kit_kmixerAsyncTrack* track = _kit_kmixerAsyncGetTrack(device, userdata, trackNum);
- _IF_GOTO_ERROR(track==UDAT_ERROR,;)
- Sint32 freq = userdata->vspec.freq; //sample rate
- position = track->position*freq; //position, in seconds
- _error_:
- return position;
- }
- int kit_kmixerAsyncSetTrackStopOnMute(kit_kmixerDevice* device, Uint32 voiceID,
- Uint32 trackNum, SDL_bool stopOnMute)
- {
- _kit_kmixerAsyncUserdata* userdata = _kit_kmixerAsyncGetUserdata(device,voiceID);
- _IF_GOTO_ERROR(userdata==UDAT_ERROR,;)
- _kit_kmixerAsyncTrack* track = _kit_kmixerAsyncGetTrack(device, userdata, trackNum);
- _IF_GOTO_ERROR(track==UDAT_ERROR,;)
- track->stopOnMute = stopOnMute; //changing stopOnMute doesn't require a lock
- /*!err*/ return 0;
- _error_: return -1;
- }
- int kit_kmixerAsyncSetTrackDeltaS(kit_kmixerDevice* device, Uint32 voiceID,
- Uint32 trackNum, double deltaS)
- {
- _kit_kmixerAsyncUserdata* userdata = _kit_kmixerAsyncGetUserdata(device,voiceID);
- _IF_GOTO_ERROR(userdata==UDAT_ERROR,;)
- _kit_kmixerAsyncTrack* track = _kit_kmixerAsyncGetTrack(device, userdata, trackNum);
- _IF_GOTO_ERROR(track==UDAT_ERROR,;)
- track->deltaS = deltaS; //changing deltaS doesn't require a lock
- /*!err*/ return 0;
- _error_: return -1;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement