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) SDL_free(userdata); }
- void _kit_kmixerAsyncTrackCallback(void* userdata, void* _stream, int size, SDL_bool hasInput){
- _kit_kmixerAsyncTrack* track = userdata;
- kit_acodecPCM* pcm = track->pcm;
- 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) return;
- kit_kmixerVoiceSpec vspec = track->voice->spec; //make copy of voice spec
- //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->deviceTimeStamp; // = 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;
- 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);
- 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; //will either set track->pcm to itself, or NULL
- track->position = position;
- track->speed = speed;
- track->loops = loops;
- //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->pcm = NULL;
- }
- void _kit_kmixerAsyncVoiceCallback(void* _userdata, void* _stream, int size, SDL_bool hasInput){
- _kit_kmixerAsyncUserdata* userdata = _userdata;
- float pan = userdata->pan;
- SDL_bool stereo = userdata->voice->spec.stereo;
- Uint16 dst_len = size / (sizeof(float)<<stereo);
- if(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);
- }
- }
- Uint32 numTracks = userdata->numTracks;
- _kit_kmixerAsyncTrack* tracks = userdata->tracks;
- //deactivate all active tracks if main voice stopOnMute condition is met
- if(!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);
- }
- //otherwise, just deactivate the tracks that have finished playing its queued clip
- } else {
- for(Uint32 ti=0; ti<numTracks; ++ti){
- _kit_kmixerAsyncTrack* track = &tracks[ti];
- _kit_kmixerVoice* voice = track->voice;
- if(voice == NULL) return;
- if(voice->lock == NULL) return;
- _deactivate_track_if(track->pcm==NULL && voice->active, track);
- }
- }
- }
- 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(outputVoiceID>=device->_raw->x,;,"outputVoiceID out of bounds")
- _IF_SDLERR(numTracks==0,;,"!numTracks")
- kit_coreVector** raw_p = &device->_raw;
- size_t userdataSize = sizeof(_kit_kmixerAsyncUserdata);
- userdataSize += sizeof(_kit_kmixerAsyncTrack)*numTracks;
- //create main voice
- kit_kmixerVoiceSpec vspec = VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, 0).spec;
- vspec.remove = _kit_kmixerAsyncRemoveCallback;
- vspec.callback = _kit_kmixerAsyncVoiceCallback;
- _IF_GOTO_ERROR(kit_coreRealloc(&vspec.userdata,0,userdataSize),;)
- vspec.stereo = stereo&1;
- mainVoiceID = kit_kmixerVoiceAdd(device,&vspec,outputVoiceID);
- _IF_GOTO_ERROR(!mainVoiceID, SDL_free(vspec.userdata) )
- VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, mainVoiceID).active = SDL_FALSE;
- //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->numTracks = numTracks;
- userdata->voice = &VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, mainVoiceID);
- userdata->tracks = (void*)userdata+sizeof(_kit_kmixerAsyncUserdata);
- //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->linear = linearInterpolation&1;
- track->deviceTimeStamp = &device->_timeStampEnd;
- track->rawIndex = kit_kmixerVoiceAdd(device,&vspec,mainVoiceID);
- if(!track->rawIndex){ kit_kmixerVoiceRemove(device, mainVoiceID); return 0; }
- track->voice = &VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, track->rawIndex);
- kit_kmixerVoiceSetActive(device, track->rawIndex, SDL_FALSE);
- }
- _noerr_:
- _error_:
- return mainVoiceID;
- }
- _kit_kmixerAsyncUserdata* _kit_kmixerAsyncGetUserdata(kit_kmixerDevice* device, Uint32 voiceID){
- _DEVICE_VALIDITY_CHECK(0)
- _IF_SDLERR(voiceID>=device->_raw->x,;,"voiceID out of bounds")
- kit_coreVector** raw_p = &device->_raw;
- _kit_kmixerVoice* voice = &VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, voiceID);
- _IF_SDLERR(voice->lock==NULL,;,"voice does not exist")
- _kit_kmixerAsyncUserdata* userdata = voice->spec.userdata;
- _IF_SDLERR(userdata==NULL,;,"invalid async voice")
- _IF_SDLERR(userdata->type!=type_UDAT,;,"invalid async voice")
- _noerr_: return userdata;
- _error_: return NULL;
- }
- _kit_kmixerAsyncTrack* _kit_kmixerAsyncGetTrack(_kit_kmixerAsyncUserdata* userdata, Uint32 trackNum){
- _IF_SDLERR(trackNum>=userdata->numTracks,;,"trackNum out of bounds")
- _kit_kmixerAsyncTrack* track = &userdata->tracks[trackNum];
- _IF_SDLERR(track->pcm==NULL,;,"no clip queued in track")
- /*!err*/ return track;
- _error_: return NULL;
- }
- 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
- _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_kmixerAsyncUserdata* userdata = _kit_kmixerAsyncGetUserdata(device,voiceID);
- _IF_GOTO_ERROR(userdata==NULL,;)
- kit_kmixerVoiceSpec* vspec = &userdata->voice->spec;
- _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
- Uint32 numTracks = userdata->numTracks;
- _kit_kmixerAsyncTrack* tracks = userdata->tracks;
- for(Uint32 i=0; i<numTracks; ++i){
- if(tracks[i].pcm == NULL){ trackNum = i; break; }
- }
- _IF_GOTO(trackNum==-1,_noerr_,;) //exit early if no free track was found
- _kit_kmixerAsyncTrack* track = &tracks[trackNum];
- 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_: return trackNum;
- }
- int kit_kmixerAsyncStopTrack(kit_kmixerDevice* device, Uint32 voiceID,
- Uint32 trackNum)
- {
- _kit_kmixerAsyncUserdata* userdata = _kit_kmixerAsyncGetUserdata(device,voiceID);
- _IF_GOTO_ERROR(userdata==NULL,;)
- _IF_SDLERR(trackNum>=userdata->numTracks,;,"trackNum out of bounds")
- _kit_kmixerAsyncTrack* track = &userdata->tracks[trackNum];
- if(track->pcm == NULL) return 1;
- else track->pcm = NULL;
- _kit_kmixerVoice* trackVoice = track->voice;
- _IF_SDLERR(trackVoice->lock==NULL,;,"track's voice doesn't exist")
- _IF_GOTO_ERROR(SDL_LockMutex(trackVoice->lock),;)
- trackVoice->active = SDL_FALSE;
- _IF_GOTO_ERROR(SDL_UnlockMutex(trackVoice->lock),;)
- /*!err*/ return 0;
- _error_: return -1;
- }
- int kit_kmixerAsyncStopAllTracks(kit_kmixerDevice* device, Uint32 voiceID){
- int returnStatus = 0;
- _kit_kmixerAsyncUserdata* userdata = _kit_kmixerAsyncGetUserdata(device,voiceID);
- _IF_GOTO_ERROR(userdata==NULL, returnStatus=-1)
- Uint32 numTracks = userdata->numTracks;
- _kit_kmixerAsyncTrack* tracks = userdata->tracks;
- for(Uint32 i=0; i<numTracks; ++i){
- tracks[i].pcm = NULL;
- _kit_kmixerVoice* trackVoice = tracks[i].voice;
- _IF_SDLERR(trackVoice->lock==NULL, returnStatus=-1,
- "track %u's voice doesn't exist",i)
- if(SDL_LockMutex(trackVoice->lock)){ returnStatus = -1; continue; }
- trackVoice->active = SDL_FALSE;
- if(SDL_UnlockMutex(trackVoice->lock)) returnStatus = -1;
- }
- _error_:
- 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==NULL,;)
- 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==NULL) 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==NULL,;)
- _IF_SDLERR(trackNum>=userdata->numTracks,;,"trackNum out of bounds")
- /*!err*/ return userdata->tracks[trackNum].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==NULL,;)
- _kit_kmixerAsyncTrack* track = _kit_kmixerAsyncGetTrack(userdata, trackNum);
- _IF_GOTO_ERROR(track==NULL,;)
- _kit_kmixerVoice* trackVoice = track->voice;
- _IF_SDLERR(trackVoice->lock==NULL,;,"track's voice doesn't exist")
- Sint32 freq = trackVoice->spec.freq; //sample rate
- position = track->position*freq; //position, in seconds
- _error_:
- return position;
- }
- int kit_kmixerAsyncSetTrackDeltaS(kit_kmixerDevice* device, Uint32 voiceID,
- Uint32 trackNum, double deltaS)
- {
- _kit_kmixerAsyncUserdata* userdata = _kit_kmixerAsyncGetUserdata(device,voiceID);
- _IF_GOTO_ERROR(userdata==NULL,;)
- _kit_kmixerAsyncTrack* track = _kit_kmixerAsyncGetTrack(userdata, trackNum);
- _IF_GOTO_ERROR(track==NULL,;)
- 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