Advertisement
Kitomas

kit_sdl2_kmixerDevice.c as of 2023-10-12

Oct 12th, 2023
854
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 10.89 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. //500ms
  9. #define _totalFadeDelay (0.500f)
  10.  
  11. //linearly fade over the course of 10ms
  12. #define _fadeDeltaSeconds (0.010f)
  13.  
  14. //the most common audio clipping ends at 10-11ms after unpausing,
  15.  //but i've seen clipping as far as ~450ms after unpausing
  16. #define _fadeInDelaySeconds (_totalFadeDelay - _fadeDeltaSeconds)
  17.  
  18.  
  19.  
  20.  
  21. void _kit_kmixerDeviceDoTasks(SDL_ThreadFunction task, kit_coreVector* threads_v,
  22.                                                        kit_coreVector* threadData_v)
  23. {
  24.   SDL_Thread** threads        = threads_v->data;
  25.   Uint32       threads_len    = threads_v->x;
  26.   void**       threadData     = threadData_v->data;
  27.   Uint32       threadData_len = VECTOR_LENS_A(threadData_v, 0,0);
  28.  
  29.   Uint32 queueLen = 0;
  30.  
  31.  
  32.   for(Uint32 tdi=0; tdi<threadData_len; ++tdi){
  33.     threads[queueLen++] = SDL_CreateThread(task,NULL,threadData[tdi]);
  34.  
  35.     if(!( queueLen%threads_len )){ //queue is full; wait for everything in queue
  36.       for(Uint32 ti=0; ti<queueLen; ++ti) SDL_WaitThread(threads[ti],NULL);
  37.       queueLen = 0;
  38.     }
  39.  
  40.   }
  41.  
  42.   if(queueLen){ //wait for any left over queued threads
  43.     for(Uint32 ti=0; ti<queueLen; ++ti) SDL_WaitThread(threads[ti],NULL);
  44.   }
  45. }
  46.  
  47.  
  48.  
  49.  
  50. //workaround for having _kit_kmixerDeviceCallback pause the device,
  51.  //without having to call SDL_PauseAudioDevice inside the callback itself
  52. int _kit_kmixerDevicePauseThread(void* data){
  53.   kit_kmixerDevice* device = data;
  54.  
  55.   kit_kmixerDeviceLock(device,SDL_TRUE);
  56.  
  57.   SDL_PauseAudioDevice(device->_devID,1);
  58.   device->_playing = SDL_FALSE;
  59.  
  60.   kit_kmixerDeviceLock(device,SDL_FALSE);
  61.   return 0;
  62. }
  63.  
  64.  
  65.  
  66.  
  67. void _kit_kmixerDeviceCallback(void* userdata, Uint8* _stream, int len){
  68.   kit_coreMemset(_stream,0,len); //_stream must be completely filled no matter what
  69.   kit_kmixerDevice* device = userdata;
  70.   if(device->_closing) return; //exit early if device is currently being closed
  71.  
  72.   kit_kmixerDeviceLock(device,SDL_TRUE);
  73.   //if previous attempt to pause device failed, memset 0 the entire buffer
  74.   _IF_GOTO(device->_fadeInDelay==-1, _unlock_device,;)
  75.  
  76.  
  77.   //get values from voice 0 (device proxy)
  78.   kit_coreVector** raw_p = &device->_raw;
  79.   _kit_kmixerVoice* voice0 = &VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, 0);
  80.    //(also memset 0 if there are literally no voices connected to the device)
  81.   _IF_GOTO(voice0->inputs==NULL,_unlock_device,;)
  82.   SDL_bool stereo = voice0->spec.stereo;
  83.  
  84.   kit_coreVector* ord   = device->_ord;
  85.   Uint32          ord_x = ord->x;
  86.   Uint32          ord_y = ord->y;
  87.  
  88.   //adjust thread data list to accomodate new width of ord
  89.   kit_coreVectorSet(&device->_threadData, ord_x,1,1);
  90.  
  91.   int size = len; //will stay as len's original value; the buffer size in bytes
  92.   len /= sizeof(float)<<stereo; // = number of sample frames
  93.  
  94.  
  95.   /**/
  96.  
  97.   /* //single-threaded; simplest implementation
  98.   for(Uint32 stage=ord_y-1; stage!=U32_MAX; --stage){
  99.     for(Uint32 index=0; index<ord_x; ++index){
  100.       _kit_kmixerVoice* voice = VECTOR_INDEX_A(_kit_kmixerVoice*, ord, index,stage,0);
  101.  
  102.       _kit_kmixerVoiceMix(voice);
  103.       _kit_kmixerVoiceProc(voice);
  104.     }
  105.   } */
  106.  
  107.   //slightly more advanced multithreaded version (which should be faster)
  108.   for(Uint32 stage=ord_y-1; stage!=U32_MAX; --stage){
  109.     _kit_kmixerVoice** voiceRefs = &VECTOR_INDEX_A(_kit_kmixerVoice*, ord, 0,stage,0);
  110.     Uint32         stageVoiceLen = VECTOR_LENS_A(ord, stage,0);
  111.  
  112.     _kit_kmixerDeviceMixThreads(device, voiceRefs, stageVoiceLen);
  113.     _kit_kmixerDeviceProcThreads(device, voiceRefs, stageVoiceLen);
  114.   }
  115.  
  116.   /**/
  117.  
  118.  
  119.   //apply linear fade to reduce popping when pausing and unpausing the device
  120.   kit_coreMemcpy(_stream, voice0->bufferInput.data, size);
  121.   if(   stereo) _kit_kmixerDeviceStereoFade(device, (void*)_stream, len);
  122.   else /*mono*/ _kit_kmixerDeviceMonoFade(device, (void*)_stream, len);
  123.  
  124.  
  125.   _unlock_device: kit_kmixerDeviceLock(device,SDL_FALSE);
  126. }
  127.  
  128.  
  129.  
  130.  
  131. int kit_kmixerDeviceLock(kit_kmixerDevice* device, SDL_bool lockState){
  132.   _DEVICE_VALIDITY_CHECK(0)
  133.  
  134.   if(lockState){ //lock
  135.     _IF_GOTO_ERROR(SDL_LockMutex(device->_lock),;)
  136.     SDL_LockAudioDevice(device->_devID);
  137.     ++device->_lockCount;
  138.  
  139.   } else if (device->_lockCount>0){ //unlock (only if actually locked)
  140.     _IF_GOTO_ERROR(SDL_UnlockMutex(device->_lock),;)
  141.     SDL_UnlockAudioDevice(device->_devID);
  142.     --device->_lockCount;
  143.  
  144.   } else { //lockstate is false, but _lockcount is also 0
  145.     return 1; //warning, technically not an error
  146.   }
  147.  
  148.   _noerr_: return  0;
  149.   _error_: return -1;
  150. }
  151.  
  152.  
  153.  
  154.  
  155. int kit_kmixerDevicePlay(kit_kmixerDevice* device, SDL_bool playState){
  156.   _DEVICE_VALIDITY_CHECK(1)
  157.   _IF_GOTO(device->_closing,_noerr_,;) //exit normally
  158.   kit_kmixerDeviceLock(device,SDL_TRUE);
  159.  
  160.   //this should occur when the device callback
  161.    //fails to trigger the pause thread
  162.   if(device->_fadeInDelay == -1){
  163.     SDL_PauseAudioDevice(device->_devID,1);
  164.     device->_playing = SDL_FALSE;
  165.     device->_fadeInDelay = 0;
  166.   }
  167.  
  168.   device->_fadeOut = !(playState&=1);
  169.   if(playState && !device->_playing){
  170.     //the purpose of fadeInDelay is to mute for some samples
  171.      //to give the sdl audio device some time to warm up,
  172.      //otherwise artifacts start to occur
  173.     device->_fadeInDelay = device->_spec.freq*_fadeInDelaySeconds;
  174.     SDL_PauseAudioDevice(device->_devID,0);
  175.   }
  176.  
  177.   kit_kmixerDeviceLock(device,SDL_FALSE);
  178.   _noerr_: return  0;
  179.   _error_: return -1;
  180. }
  181.  
  182.  
  183.  
  184. int kit_kmixerDeviceClose(kit_kmixerDevice** device_p){
  185.   _IF_SDLERR(device_p==NULL,;,"device_p cannot be NULL")
  186.   kit_kmixerDevice* device = *device_p;
  187.   _DEVICE_VALIDITY_CHECK(1)
  188.  
  189.   device->_closing = SDL_TRUE;
  190.  
  191.   //this should pause this thread until _kit_kmixerDeviceCallback finishes
  192.   if(device->_devID != 0) SDL_CloseAudioDevice(device->_devID);
  193.  
  194.   if(device->_raw != NULL){
  195.     //since voice removal is recursive, removing voice 0 will remove every
  196.      //other voice too, as every voice is connected to it in some way
  197.     _IF_GOTO_ERROR(kit_kmixerVoiceRemove(device,0),;)
  198.   }
  199.  
  200.   kit_coreVectorDestroy(&device->_ord);
  201.   kit_coreVectorDestroy(&device->_raw);
  202.  
  203.   kit_coreVectorDestroy(&device->_threadData);
  204.   kit_coreVectorDestroy(&device->_threads);
  205.  
  206.   if(device->_lock != NULL) SDL_DestroyMutex(device->_lock);
  207.  
  208.   SDL_free(device); *device_p = NULL;
  209.  
  210.   _noerr_: return  0; // 0 on success
  211.   _error_: return -1; //-1 on failure
  212. }
  213.  
  214.  
  215.  
  216.  
  217. //todo: maybe at some point make voiceSpecObtained to be optional
  218. kit_kmixerDevice* kit_kmixerDeviceOpen(const char* deviceName, int allowedChanges,
  219.                                        const kit_kmixerVoiceSpec* voiceSpecDesired,
  220.                                        kit_kmixerVoiceSpec* voiceSpecObtained)
  221. {
  222.   kit_kmixerDevice* device = NULL;
  223.   SDL_bool success = SDL_FALSE;
  224.   _IF_SDLERR(!_kit_kmixerGlobals.init,;,"kmixer is not initialized")
  225.   _IF_SDLERR(voiceSpecDesired==NULL,;,"voiceSpecDesired cannot be NULL")
  226.   _IF_GOTO_ERROR(kit_coreRealloc(&device,0,sizeof(kit_kmixerDevice)),;)
  227.  
  228.   device->_magic.num = _DEV_MAGIC_NUM; // = "kmxrDev\x00"
  229.  
  230.  
  231.   //fill in sdl device specification
  232.   SDL_AudioSpec specWant, specHave;
  233.   specWant = _kit_kmixerDeviceVoiceToAudioSpec(device, voiceSpecDesired);
  234.   _IF_GOTO_ERROR(!specWant.format,;)
  235.  
  236.  
  237.   //open the sdl audio device itself
  238.   allowedChanges &= ~SDL_AUDIO_ALLOW_FORMAT_CHANGE;  //samples should always be f32 internally
  239.   allowedChanges &= ~SDL_AUDIO_ALLOW_SAMPLES_CHANGE; //i want to guarantee that samples is a power of 2
  240.   device->_devID = SDL_OpenAudioDevice(deviceName,0, &specWant,&specHave, allowedChanges);
  241.   _IF_GOTO_ERROR(!device->_devID,;)
  242.   _IF_SDLERR(!specHave.channels,;,"channels returned as 0") //this shouldn't be able to happen
  243.   _IF_SDLERR(specHave.channels>2,;,"channels returned as %i",specHave.channels)
  244.   _IF_SDLERR(specHave.freq<1000,;,"freq returned as <1kHz")
  245.   _IF_SDLERR(specHave.freq>384000,;,"freq returned as >384kHz")
  246.   device->_spec = specHave;
  247.  
  248.  
  249.   device->_lock = SDL_CreateMutex();
  250.   _IF_GOTO_ERROR(device->_lock==NULL,;)
  251.  
  252.  
  253.   //thread and thread data lists
  254.   device->_threads = kit_coreVectorCreate(_kit_kmixerGlobals.numDeviceThreads,1,1, sizeof(SDL_ThreadFunction),0);
  255.   device->_threadData = kit_coreVectorCreate(1,1,1, sizeof(void*),0);
  256.   _IF_GOTO_ERROR(device->_threads==NULL,;)
  257.   _IF_GOTO_ERROR(device->_threadData==NULL,;)
  258.  
  259.  
  260.   //voices lists-related
  261.   device->_raw = kit_coreVectorCreate(1,1,1,sizeof(_kit_kmixerVoice),0);
  262.   device->_ord = kit_coreVectorCreate(1,1,1,sizeof(_kit_kmixerVoice*),0);
  263.   _IF_GOTO_ERROR(device->_raw==NULL,;)
  264.   _IF_GOTO_ERROR(device->_ord==NULL,;)
  265.  
  266.  
  267.   //fill in obtained voice
  268.   voiceSpecObtained->remove   = voiceSpecDesired->remove; //note: might be called on abort!!!
  269.   voiceSpecObtained->callback = voiceSpecDesired->callback;
  270.   voiceSpecObtained->userdata = voiceSpecDesired->userdata;
  271.   voiceSpecObtained->freq     = specHave.freq;
  272.   voiceSpecObtained->_size    = 0; //(will be set inside kit_kmixerVoiceAdd)
  273.   voiceSpecObtained->stereo   = specHave.channels==2;
  274.   voiceSpecObtained->samples  = specHave.samples;
  275.   voiceSpecObtained->format   = voiceSpecDesired->format;
  276.  
  277.  
  278.   //fill in voice 0
  279.   _IF_GOTO_ERROR(_kit_kmixerDeviceFillVoice0(device, voiceSpecObtained),;)
  280.  
  281.  
  282.   device->_fadeDelta  = 1.0f/(((float)specHave.freq)*_fadeDeltaSeconds);
  283.   device->_fadeVolume = 0.0f;
  284.  
  285.  
  286.   //redundant because of the memset, but whatever
  287.   device->_closing = SDL_FALSE;
  288.   device->_fadeOut = SDL_FALSE;
  289.  
  290.  
  291.   //create initial voice, only if format != 0
  292.   if(voiceSpecObtained->format){
  293.     _IF_GOTO_ERROR(!kit_kmixerVoiceAdd(device, voiceSpecObtained, 0),;)
  294.   } else { //otherwise, just rebuild ord
  295.     _IF_GOTO_ERROR(_kit_kmixerVoiceRebuildOrd(device),;)
  296.   }
  297.  
  298.  
  299.   success = SDL_TRUE;
  300.   _error_: //success will remain SDL_FALSE on failure
  301.   if(device!=NULL && !success) kit_kmixerDeviceClose(&device);
  302.   return device; //will be null if DeviceClose is called
  303. }
  304.  
  305.  
  306.  
  307.  
  308. int kit_kmixerDeviceUnpauseAndWait(kit_kmixerDevice* device){
  309.   Uint64 beforePlay = SDL_GetTicks64();
  310.   SDL_bool wasPaused = !device->_playing;
  311.   if(kit_kmixerDevicePlay(device,SDL_TRUE)<0) return -1;
  312.  
  313.   if(wasPaused){
  314.     Uint32 totalDelayMS = _totalFadeDelay*1000;
  315.     Uint32 timeDifference = SDL_GetTicks64()-beforePlay; //result might be 0, but w/e
  316.     SDL_Delay( totalDelayMS - timeDifference );
  317.   }
  318.   return 0;
  319. }
  320.  
  321.  
  322.  
  323. int kit_kmixerDevicePauseAndWait(kit_kmixerDevice* device){
  324.   if(kit_kmixerDevicePlay(device,SDL_FALSE)<0) return -1;
  325.   Uint32 fadeOutMS = _fadeDeltaSeconds*1000 * 4; //*4 seems to completely stop clipping
  326.   do { SDL_Delay(fadeOutMS); } while(device->_playing);
  327.   return 0;
  328. }
  329.  
  330.  
  331.  
  332.  
  333. #if defined(_KIT_KMIXER_DEBUG) || defined(_KIT_ALL_DEBUG)
  334. int kit_kmixerDevice_Test(){
  335.   SDL_SetError("not implemented");
  336.   return 999;
  337. }
  338. #else
  339. int kit_kmixerDevice_Test(){
  340.   SDL_SetError("!defined(_KIT_KMIXER_DEBUG)");
  341.   return 999;
  342. }
  343. #endif
  344.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement