Advertisement
Kitomas

kit_sdl2_kmixerAsync.c as of 2023-10-20

Oct 20th, 2023
717
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 11.02 KB | None | 0 0
  1. #include "../include/kit_sdl2/kit_kmixer.h"
  2. #include "../_private/include/_kit_privmacro.h"
  3. #include "../_private/include/_kit_kmixerAllPrivate.h"
  4.  
  5.  
  6.  
  7.  
  8. void _kit_kmixerAsyncRemoveCallback(void* userdata){  if(userdata != NULL) SDL_free(userdata);  }
  9.  
  10.  
  11.  
  12. void _kit_kmixerAsyncTrackCallback(void* userdata, void* _stream, int size, SDL_bool hasInput){
  13.   _kit_kmixerAsyncTrack* track = userdata;
  14.   kit_acodecPCM*           pcm = track->pcm;
  15.  
  16.   //if pcm is NULL, the clip has finished playing, and will be deactivated by the main voice
  17.   if(pcm == NULL){ kit_coreMemset(_stream,0,size); return; }
  18.   kit_kmixerVoiceSpec vspec = track->voice->spec; //make copy of voice spec
  19.  
  20.  
  21.   if(track->timeStamp){
  22.     //loops will count down to 0, whereupon the clip will finish (-1 for endless loop)
  23.     track->loops = pcm->loopCount;
  24.     //initialize previous sample to NAN, to indicate that it should be set later
  25.     union { Uint32 u32; float f32; } not_a_number = { .u32 = 0x7FC00000 };
  26.     track->prevSample.l = not_a_number.f32;
  27.  
  28.     //calculate position based on difference between device and clip timestamps
  29.     Uint64  trackTimeStamp = track->timeStamp;
  30.     Uint64 deviceTimeStamp = *track->deviceTimeStamp; // = device->_timeStampEnd
  31.  
  32.     //device->_timeStampEnd is used instead of Start, so the clip's time stamp
  33.      //can be compared to the last time the device callback exited
  34.     double difference = (double)(trackTimeStamp-deviceTimeStamp)/1000;
  35.     difference *= vspec.freq; //convert seconds to samples
  36.     track->position = -(difference*track->speed); //starts playing when position reaches 0
  37.  
  38.     track->timeStamp = 0; //to ensure that this only occurs once per clip queued
  39.   }
  40.  
  41.  
  42.   float        pan = track->pan;
  43.   double  position = track->position;
  44.   float      speed = track->speed;
  45.   Uint16     loops = track->loops;
  46.   Uint64 loopStart = pcm->loopStart;
  47.   Uint64   loopEnd = pcm->loopEnd;
  48.   //Uint64 src_len = pcm->numSamples; (unnecessary, as loopEnd is used instead)
  49.   Uint16   dst_len = size / (sizeof(float)<<vspec.stereo);
  50.  
  51.  
  52.   if(vspec.stereo){
  53.     kit_acodecPCM_F32S* dst = _stream,  *src = pcm->f32s;
  54.     kit_acodecPCM_F32S prevSample = track->prevSample;
  55.     //(set prevSample to src[0] if track's prevSample is NaN)
  56.     if(track->prevSample.l != track->prevSample.l) prevSample = src[0];
  57.  
  58.     if(track->linear){ //linear resampling
  59.       for(Uint32 i=0; i<dst_len; ++i) _stereo_iteration(_stereo_linear)
  60.     } else { //nearest-neighbor resampling
  61.       for(Uint32 i=0; i<dst_len; ++i) _stereo_iteration(_stereo_nearest)
  62.     }
  63.  
  64.     track->prevSample = prevSample;
  65.  
  66.   } else { //mono
  67.     float* dst = _stream,  *src = pcm->f32;
  68.     float prevSample = track->prevSample.l;
  69.     //(set prevSample to src[0] if track's prevSample is NaN)
  70.     if(track->prevSample.l != track->prevSample.l) prevSample = src[0];
  71.  
  72.     if(track->linear){ //linear resampling
  73.       for(Uint32 i=0; i<dst_len; ++i) _mono_iteration(_mono_linear)
  74.     } else { //nearest-neighbor resampling
  75.       for(Uint32 i=0; i<dst_len; ++i) _mono_iteration(_mono_nearest)
  76.     }
  77.  
  78.     track->prevSample.l = prevSample;
  79.  
  80.   }
  81.  
  82.  
  83.   //update some values
  84.   track->pcm       = pcm; //will either set track->pcm to itself, or NULL
  85.   track->position  = position;
  86.   track->speed     = speed;
  87.   track->loops     = loops;
  88.  
  89.   //if the track's stopOnMute condition is met, mark track as finished,
  90.    //so it'll be deactivated until a new clip is queued
  91.   if(!vspec.stereo) track->volume.r = track->volume.l;
  92.   if((track->volume.l<=0 && track->volume.r<=0) || track->speed<=0){
  93.     if(track->stopOnMute) track->pcm = NULL;
  94.   }
  95. }
  96.  
  97.  
  98.  
  99. void _kit_kmixerAsyncVoiceCallback(void* _userdata, void* _stream, int size, SDL_bool hasInput){
  100.   _kit_kmixerAsyncUserdata* userdata = _userdata;
  101.  
  102.   float       pan = userdata->pan;
  103.   SDL_bool stereo = userdata->voice->spec.stereo;
  104.   Uint16  dst_len = size / (sizeof(float)<<stereo);
  105.  
  106.  
  107.   if(stereo){
  108.     kit_acodecPCM_F32S* dst = _stream;
  109.     for(Uint32 i=0; i<dst_len; ++i){
  110.       kit_acodecPCM_F32S sample = _stereo_volume(dst[i], (void*)userdata);
  111.       dst[i] = _apply_pan(_stereo_clamp(sample), pan);
  112.       _stereo_deltaVol((void*)userdata);
  113.     }
  114.  
  115.   } else { //mono
  116.     float* dst = _stream;
  117.     for(Uint32 i=0; i<dst_len; ++i){
  118.       dst[i] = _mono_clamp(_mono_volume(dst[i],(void*)userdata));
  119.       _mono_deltaVol((void*)userdata);
  120.     }
  121.  
  122.   }
  123.  
  124.  
  125.   Uint32              numTracks = userdata->numTracks;
  126.   _kit_kmixerAsyncTrack* tracks = userdata->tracks;
  127.   //deactivate all active tracks if main voice stopOnMute condition is met
  128.   if(!stereo) userdata->volume.r = userdata->volume.l;
  129.   if(userdata->volume.l<=0 && userdata->volume.r<=0  &&  userdata->stopOnMute){
  130.     for(Uint32 ti=0; ti<numTracks; ++ti){
  131.       _kit_kmixerAsyncTrack* track = &tracks[ti];
  132.       _deactivate_track_if(track->pcm!=NULL, track);
  133.     }
  134.  
  135.   //otherwise, just deactivate the tracks that have finished playing its queued clip
  136.   } else {
  137.     for(Uint32 ti=0; ti<numTracks; ++ti){
  138.       _kit_kmixerAsyncTrack* track = &tracks[ti];
  139.       _kit_kmixerVoice* voice = track->voice;
  140.       if(voice == NULL) return;
  141.       if(voice->lock == NULL) return;
  142.       _deactivate_track_if(track->pcm==NULL && voice->active, track);
  143.     }
  144.  
  145.   }
  146. }
  147.  
  148.  
  149.  
  150.  
  151. Uint32 kit_kmixerAsyncAdd(kit_kmixerDevice* device,
  152.                           SDL_bool linearInterpolation, SDL_bool stereo,
  153.                           Uint32 outputVoiceID, Uint32 numTracks)
  154. {
  155.   Uint32 mainVoiceID = 0; //0 for error by default
  156.   _DEVICE_VALIDITY_CHECK(0)
  157.   _IF_SDLERR(outputVoiceID>=device->_raw->x,;,"outputVoiceID out of bounds")
  158.   _IF_SDLERR(numTracks==0,;,"!numTracks")
  159.   kit_coreVector** raw_p = &device->_raw;
  160.  
  161.   size_t userdataSize = sizeof(_kit_kmixerAsyncUserdata);
  162.   userdataSize += sizeof(_kit_kmixerAsyncTrack)*numTracks;
  163.  
  164.   //create main voice
  165.   kit_kmixerVoiceSpec vspec = VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, 0).spec;
  166.   vspec.remove   = _kit_kmixerAsyncRemoveCallback;
  167.   vspec.callback = _kit_kmixerAsyncVoiceCallback;
  168.   _IF_GOTO_ERROR(kit_coreRealloc(&vspec.userdata,0,userdataSize),;)
  169.   vspec.stereo   = stereo&1;
  170.  
  171.   mainVoiceID = kit_kmixerVoiceAdd(device,&vspec,outputVoiceID);
  172.   _IF_GOTO_ERROR(!mainVoiceID, SDL_free(vspec.userdata) )
  173.   VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, mainVoiceID).active = SDL_FALSE;
  174.  
  175.  
  176.   //fill in userdata
  177.   _kit_kmixerAsyncUserdata* userdata = vspec.userdata;
  178.   userdata->type      = type_UDAT; //U[SER]DAT[A]
  179.   userdata->volume.l  = 1.0f;
  180.   userdata->volume.r  = 1.0f;
  181.   userdata->numTracks = numTracks;
  182.   userdata->voice     = &VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, mainVoiceID);
  183.   userdata->tracks    = (void*)userdata+sizeof(_kit_kmixerAsyncUserdata);
  184.  
  185.  
  186.   //create tracks
  187.   vspec.remove   = NULL; //only the main voice should be doing the free()'ing
  188.   vspec.callback = _kit_kmixerAsyncTrackCallback;
  189.   _kit_kmixerAsyncTrack* tracks = userdata->tracks;
  190.   for(Uint32 ti=0; ti<numTracks; ++ti){
  191.     _kit_kmixerAsyncTrack* track = vspec.userdata = &tracks[ti];
  192.     track->type            = type_TRCK; //TR[A]CK
  193.     track->volume.l        = 1.0f;
  194.     track->volume.r        = 1.0f;
  195.     track->linear          = linearInterpolation&1;
  196.     track->deviceTimeStamp = &device->_timeStampEnd;
  197.     track->rawIndex        = kit_kmixerVoiceAdd(device,&vspec,mainVoiceID);
  198.     if(!track->rawIndex){ kit_kmixerVoiceRemove(device, mainVoiceID); return 0; }
  199.     track->voice = &VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, track->rawIndex);
  200.     kit_kmixerVoiceSetActive(device, track->rawIndex, SDL_FALSE);
  201.   }
  202.  
  203.  
  204.   _noerr_:
  205.   _error_:
  206.   return mainVoiceID;
  207. }
  208.  
  209.  
  210.  
  211.  
  212. Uint32 kit_kmixerAsyncPlayPVS(kit_kmixerDevice* device, Uint32 voiceID, kit_acodecPCM* pcm,
  213.                               float pan, float volumeL, float volumeR, float speedMultiplier)
  214. {
  215.   Uint64 trackTimeStamp = SDL_GetTicks64();
  216.   Uint32 trackNum = -1; //set to -1 for error by default
  217.   _DEVICE_VALIDITY_CHECK(0)
  218.   _IF_SDLERR(voiceID>=device->_raw->x,;,"voiceID out of bounds")
  219.   _IF_SDLERR(pcm==NULL,;,"!pcm")
  220.   _IF_SDLERR(!NORMALIZED(pan),;,"pan must be -1.0f -> 1.0f")
  221.   _IF_SDLERR(volumeL<0,;,"volumeL < 0")
  222.   _IF_SDLERR(volumeR<0,;,"volumeR < 0")
  223.   _IF_SDLERR(speedMultiplier<=0,;,"speedMultiplier <= 0")
  224.  
  225.   kit_coreVector** raw_p = &device->_raw;
  226.   _kit_kmixerVoice* voice = &VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, voiceID);
  227.   _IF_SDLERR(voice->lock==NULL,;,"voice does not exist")
  228.   kit_kmixerVoiceSpec vspec = voice->spec;
  229.   _kit_kmixerAsyncUserdata* userdata = vspec.userdata;
  230.   _IF_SDLERR(userdata==NULL,;,"invalid async voice")
  231.   _IF_SDLERR(userdata->type!=type_UDAT,;,"invalid async voice")
  232.  
  233.   _IF_SDLERR(pcm->magic!=KPCM_MAGIC,;,"invalid pcm struct")
  234.   _IF_SDLERR(pcm->format!=AUDIO_F32,;,"pcm->format != AUDIO_F32")
  235.   _IF_SDLERR(pcm->loopStart>=pcm->loopEnd,;,"pcm->loopStart >= pcm->loopEnd")
  236.   _IF_SDLERR(pcm->loopEnd>pcm->numSamples,;,"pcm->loopEnd > pcm->numSamples")
  237.   _IF_SDLERR(pcm->numSamples<2,;,"pcm->numSamples < 2")
  238.   _IF_SDLERR(pcm->sampleRate!=vspec.freq,;,"pcm->sampleRate != device freq")
  239.   _IF_SDLERR((pcm->channels-1)!=vspec.stereo,;,"pcm->channels is invalid")
  240.  
  241.  
  242.   //look for empty track to queue pcm clip with
  243.   Uint32              numTracks = userdata->numTracks;
  244.   _kit_kmixerAsyncTrack* tracks = userdata->tracks;
  245.   for(Uint32 i=0; i<numTracks; ++i){
  246.     if(tracks[i].pcm == NULL){ trackNum = i; break; }
  247.   }
  248.   _IF_GOTO(trackNum==-1,_noerr_,;) //exit early if no free track was found
  249.  
  250.  
  251.   _kit_kmixerAsyncTrack* track = &tracks[trackNum];
  252.   track->pan        = pan;
  253.   track->volume.l   = volumeL;
  254.   track->volume.r   = volumeR;
  255.   track->delta.l    = 0.0f;
  256.   track->delta.r    = 0.0f;
  257.   track->stopOnMute = SDL_TRUE;
  258.  
  259.   track->pcm        = pcm;
  260.   track->timeStamp  = trackTimeStamp;
  261.   track->speed      = speedMultiplier;
  262.   track->deltaS     = 0.0f;
  263.  
  264.  
  265.   track->voice->active = SDL_TRUE;
  266.   kit_kmixerVoiceSetActive(device, track->rawIndex, SDL_TRUE);
  267.   if(!kit_kmixerVoiceGetActive(device, voiceID)) kit_kmixerVoiceSetActive(device, voiceID, SDL_TRUE);
  268.   _noerr_: if(trackNum == -1) trackNum = -2; //-2 for 'no available track found; no error'
  269.   _error_: return trackNum;
  270. }
  271.  
  272.  
  273.  
  274.  
  275. Uint32 kit_kmixerAsyncGetActiveTracks(kit_kmixerDevice* device, Uint32 voiceID){
  276.   Uint32 activeTracks = -1; //-1 for error by default
  277.   _DEVICE_VALIDITY_CHECK(0)
  278.   _IF_SDLERR(voiceID>=device->_raw->x,;,"voiceID out of bounds")
  279.  
  280.   kit_coreVector** raw_p = &device->_raw;
  281.   _kit_kmixerVoice* voice = &VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, voiceID);
  282.   _IF_SDLERR(voice->lock==NULL,;,"voice does not exist")
  283.   kit_kmixerVoiceSpec vspec = voice->spec;
  284.   _kit_kmixerAsyncUserdata* userdata = vspec.userdata;
  285.   _IF_SDLERR(userdata==NULL,;,"invalid async voice")
  286.   _IF_SDLERR(userdata->type!=type_UDAT,;,"invalid async voice")
  287.  
  288.  
  289.   activeTracks = 0; //there are no errors past this, so setting to 0 is safe here
  290.   Uint32              numTracks = userdata->numTracks;
  291.   _kit_kmixerAsyncTrack* tracks = userdata->tracks;
  292.   for(Uint32 i=0; i<numTracks; ++i){
  293.     if(tracks[i].pcm != NULL) ++activeTracks;
  294.   }
  295.  
  296.  
  297.   _noerr_:
  298.   _error_: return activeTracks;
  299. }
  300.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement