Advertisement
Kitomas

kit_sdl2_kmixerDevice.c as of 2023-10-05

Oct 6th, 2023
784
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 9.95 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. //sdl audio artifacts seem to end at 10ms after unpausing,
  9.  //so i'll add one more millisecond just to be sure
  10. #define fadeInDelaySeconds 0.011
  11.  
  12.  
  13.  
  14.  
  15. int kit_kmixerDeviceLock(kit_kmixerDevice* device, SDL_bool lockState){
  16.   _CHECK_IF_DEVICE_IS_VALID_R(-1)
  17.  
  18.   if(lockState){ //lock
  19.     SDL_LockAudioDevice(device->_devID);
  20.     if(SDL_LockMutex(device->_lock)) return -2;
  21.     ++device->_lockCount;
  22.   } else if (device->_lockCount>0){ //unlock (only if actually locked)
  23.     --device->_lockCount;
  24.     if(SDL_UnlockMutex(device->_lock)) return -2;
  25.     SDL_UnlockAudioDevice(device->_devID);
  26.   } else { //lockstate is false, but _lockcount is also 0
  27.     //SDL_SetError("device is fully unlocked"); (this might overwrite actual errors!)
  28.     return 1; //warning, not technically an error
  29.   }
  30.  
  31.   return 0;
  32. }
  33.  
  34.  
  35.  
  36.  
  37. //workaround for having _kit_kmixerDeviceCallback pause the device,
  38.  //without having to call SDL_PauseAudioDevice inside the callback itself
  39. int _kit_kmixerDevicePauseThread(void* data){
  40.   kit_kmixerDevice* device = data;
  41.  
  42.   kit_kmixerDeviceLock(device,SDL_TRUE);
  43.  
  44.   SDL_PauseAudioDevice(device->_devID,1);
  45.   device->_playing = SDL_FALSE;
  46.  
  47.   kit_kmixerDeviceLock(device,SDL_FALSE);
  48.   return 0;
  49. }
  50.  
  51.  
  52.  
  53.  
  54. void _kit_kmixerDeviceCallback(void* userdata, Uint8* _stream, int len){
  55.   kit_kmixerDevice* device = userdata;
  56.   _IF_GOTO(device->_closing, _unlock_device, kit_coreMemset(_stream,0,len))
  57.   kit_kmixerDeviceLock(device,SDL_TRUE);
  58.   //if previous attempt to pause device failed, memset 0 the entire buffer
  59.   _IF_GOTO(device->_fadeInDelay==-1, _unlock_device, kit_coreMemset(_stream,0,len))
  60.  
  61.   //get values from voice 0 (device proxy)
  62.   kit_coreVector** raw_p = &device->_voices.raw;
  63.   _kit_kmixerVoice* voice0 = &VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, 0);
  64.    //(also memset 0 if there are literally no voices connected to the device)
  65.   _IF_GOTO(voice0->inputs==NULL,_unlock_device, kit_coreMemset(_stream,0,len))
  66.   SDL_bool stereo = voice0->spec.stereo;
  67.  
  68.   int size = len; //will stay as len's original value; the buffer size in bytes
  69.   len /= sizeof(float)<<stereo; // = number of sample frames
  70.  
  71.   /**/
  72.   _kit_kmixerVoice* voice1 = &VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, 1);
  73.   if(voice1->spec.callback != NULL)
  74.     voice1->spec.callback(voice1->spec.userdata,voice0->bufferInput.v,size,SDL_FALSE);
  75.   else
  76.     kit_coreMemset(voice0->bufferInput.v,0,size);
  77.   /**/
  78.  
  79.   //apply simple fade effect to reduce popping when pausing and unpausing the device
  80.    //as well as apply voice 0's volume
  81.   kit_coreMemcpy(_stream, voice0->bufferInput.v, size);
  82.   if(   stereo) _kit_kmixerDeviceStereoFade(device, (void*)_stream, len);
  83.   else /*mono*/ _kit_kmixerDeviceMonoFade(device, (void*)_stream, len);
  84.  
  85.   _unlock_device: kit_kmixerDeviceLock(device,SDL_FALSE);
  86. }
  87.  
  88.  
  89.  
  90.  
  91. int kit_kmixerDevicePlay(kit_kmixerDevice* device, SDL_bool playState){
  92.   _CHECK_IF_DEVICE_IS_VALID_R(-1)
  93.   _IF_GOTO(device->_closing,_unlock_device,;)
  94.   kit_kmixerDeviceLock(device,SDL_TRUE);
  95.  
  96.   //this should occur when the device callback
  97.    //fails to trigger the pause thread
  98.   if(device->_fadeInDelay == -1){
  99.     SDL_PauseAudioDevice(device->_devID,1);
  100.     device->_playing = SDL_FALSE;
  101.     device->_fadeInDelay = 0;
  102.   }
  103.  
  104.   device->_fadeOut = !(playState&=1);
  105.   if(playState && !device->_playing){
  106.     //the purpose of fadeInDelay is to mute for some samples
  107.      //to give the sdl audio device some time to warm up (about 10ms),
  108.      //otherwise artifacts start to occur
  109.     device->_fadeInDelay = device->_spec.freq*fadeInDelaySeconds;
  110.     SDL_PauseAudioDevice(device->_devID,0);
  111.   }
  112.  
  113.   _unlock_device: kit_kmixerDeviceLock(device,SDL_FALSE);
  114.   return 0;
  115. }
  116.  
  117.  
  118.  
  119. int kit_kmixerDeviceClose(kit_kmixerDevice** device_p){
  120.   _IF_SDLERR(device_p==NULL,;,"device_p cannot be NULL")
  121.   kit_kmixerDevice* device = *device_p;
  122.   _IF_GOTO(device==NULL,_noerr_,;)
  123.   //ignore MSB of struct ID when comparing here
  124.   _IF_SDLERR(device->_magic.num!=_DEV_MAGIC_NUM,;,"*device_p is invalid")
  125.  
  126.   device->_closing = SDL_TRUE;
  127.  
  128.   //this should pause this thread until _kit_kmixerDeviceCallback finishes
  129.   if(device->_devID != 0) SDL_CloseAudioDevice(device->_devID);
  130.  
  131.   if(device->_voices.raw != NULL){
  132.     //since voice removal is recursive, removing voice 0 will remove every
  133.      //other voice too, as every voice is connected to it in some way
  134.     if(kit_kmixerVoiceRemove(device,0)) return -4;
  135.   }
  136.  
  137.   kit_coreVectorDestroy(&device->_voices.ord);
  138.   kit_coreVectorDestroy(&device->_voices.raw);
  139.  
  140.   kit_coreVectorDestroy(&device->_thread.pool);
  141.   kit_coreVectorDestroy(&device->_thread.queue);
  142.   if(device->_thread.cond != NULL) SDL_DestroyCond(device->_thread.cond);
  143.  
  144.   if(device->_lock != NULL) SDL_DestroyMutex(device->_lock);
  145.  
  146.   SDL_free(device); *device_p = NULL;
  147.  
  148.   _noerr_: return  0; // 0 on success
  149.   _error_: return -1; //-1 on failure
  150. }
  151.  
  152.  
  153.  
  154. //(i don't know how to do exponential decay periods, so i'm using a lookup table)
  155. Sint32 _kit_kmixerDeviceRates[16] = {
  156.     1000,   2000,   4000,   8000,
  157.    11025,  16000,  22050,  32000,
  158.    44100,  48000,  88200,  96000,
  159.   176400, 192000, 352800, 384000
  160. };
  161. float _kit_kmixerDeviceFadeMuls[16] = {
  162.   0.021649449000, 0.147112140000, 0.383552000000, 0.619332447000,
  163.   0.706352736000, 0.786982133000, 0.840447939000, 0.887121599000,
  164.   0.916759476883, 0.923258340930, 0.957475575084, 0.960862072327,
  165.   0.978506808910, 0.980235906283, 0.989195030775, 0.990068556095
  166. };
  167. static inline float _kit_kmixerDeviceGetFadeMul(Sint32 sampleRate){
  168.   //range checks for sampleRate is done inside kit_kmixerDeviceOpen
  169.   Sint32 rangeLow,rangeHigh, i = 0;
  170.   for(; i<((sizeof(_kit_kmixerDeviceRates)/sizeof(Sint32))-1); ++i){
  171.     rangeLow  = _kit_kmixerDeviceRates[i  ];
  172.     rangeHigh = _kit_kmixerDeviceRates[i+1];
  173.     if(rangeLow<=sampleRate && sampleRate<=rangeHigh) break;
  174.   }
  175.   sampleRate -= rangeLow;  rangeHigh -= rangeLow;
  176.   return LERP(_kit_kmixerDeviceFadeMuls[i  ], //from
  177.               _kit_kmixerDeviceFadeMuls[i+1], //to
  178.               (float)sampleRate/rangeHigh);   //percentage
  179. }
  180.  
  181.  
  182. kit_kmixerDevice* kit_kmixerDeviceOpen(const char* deviceName, int allowedChanges,
  183.                                        const kit_kmixerVoiceSpec* voiceSpecDesired,
  184.                                        kit_kmixerVoiceSpec* voiceSpecObtained)
  185. {
  186.   kit_kmixerDevice* device = NULL;
  187.   SDL_bool success = SDL_FALSE;
  188.   _IF_SDLERR(!_kit_kmixerGlobals.init,;,"kmixer is not initialized")
  189.   _IF_SDLERR(voiceSpecDesired==NULL,;,"voiceSpecDesired cannot be NULL")
  190.   _IF_GOTO_ERROR(kit_coreRealloc(&device,0,sizeof(kit_kmixerDevice)),;)
  191.  
  192.   device->_magic.num = _DEV_MAGIC_NUM; // = "kmxrDev\x00"
  193.  
  194.  
  195.   //fill in sdl device specification
  196.   SDL_AudioSpec specWant, specHave;
  197.   specWant = _kit_kmixerDeviceVoiceToAudioSpec(device, voiceSpecDesired);
  198.   _IF_GOTO_ERROR(!specWant.format,;)
  199.  
  200.  
  201.   //open the sdl audio device itself
  202.   allowedChanges &= ~SDL_AUDIO_ALLOW_FORMAT_CHANGE;  //samples should always be f32 internally
  203.   allowedChanges &= ~SDL_AUDIO_ALLOW_SAMPLES_CHANGE; //i want to guarantee that samples is a power of 2
  204.   device->_devID = SDL_OpenAudioDevice(deviceName,0, &specWant,&specHave, allowedChanges);
  205.   _IF_GOTO_ERROR(!device->_devID,;)
  206.   _IF_SDLERR(!specHave.channels,;,"channels returned as 0") //this shouldn't be able to happen
  207.   _IF_SDLERR(specHave.channels>2,;,"channels returned as %i",specHave.channels)
  208.   _IF_SDLERR(specHave.freq<1000,;,"freq returned as <1kHz")
  209.   _IF_SDLERR(specHave.freq>384000,;,"freq returned as >384kHz")
  210.   device->_spec = specHave;
  211.  
  212.  
  213.   device->_lock = SDL_CreateMutex();
  214.   _IF_GOTO_ERROR(device->_lock==NULL,;)
  215.  
  216.  
  217.   //thread stuff
  218.   device->_thread.cond = SDL_CreateCond();
  219.   device->_thread.queue = kit_coreVectorCreate(1,1,1,sizeof(kit_coreThread),0);
  220.   device->_thread.pool = kit_coreVectorCreate(_kit_kmixerGlobals.threadPoolSize,1,1,
  221.                                               sizeof(kit_coreThread),0);
  222.   _IF_GOTO_ERROR(device->_thread.cond==NULL,;)
  223.   _IF_GOTO_ERROR(device->_thread.queue==NULL,;)
  224.   _IF_GOTO_ERROR(device->_thread.pool==NULL,;)
  225.  
  226.  
  227.   //voices lists-related
  228.   device->_voices.raw = kit_coreVectorCreate(1,1,1,sizeof(_kit_kmixerVoice),0);
  229.   device->_voices.ord = kit_coreVectorCreate(1,1,1,sizeof(_kit_kmixerVoice*),0);
  230.   _IF_GOTO_ERROR(device->_voices.raw==NULL,;)
  231.   _IF_GOTO_ERROR(device->_voices.ord==NULL,;)
  232.  
  233.  
  234.   //fill in obtained voice
  235.   voiceSpecObtained->remove   = voiceSpecDesired->remove;
  236.   voiceSpecObtained->callback = voiceSpecDesired->callback;
  237.   voiceSpecObtained->userdata = voiceSpecDesired->userdata;
  238.   voiceSpecObtained->freq     = specHave.freq;
  239.   voiceSpecObtained->_size    = 0; //(will be set inside kit_kmixerVoiceAdd)
  240.   voiceSpecObtained->stereo   = specHave.channels==2;
  241.   voiceSpecObtained->samples  = specHave.samples;
  242.   voiceSpecObtained->format   = voiceSpecDesired->format;
  243.  
  244.  
  245.   //fill in voice 0
  246.   _IF_GOTO_ERROR(_kit_kmixerDeviceFillVoice0(device, voiceSpecObtained),;)
  247.  
  248.  
  249.   device->_fadeMultiplier = _kit_kmixerDeviceGetFadeMul(specHave.freq);
  250.   //actual volume when fading in will be 1-_fadeVolume,
  251.    //before _fadeVolume *= _fadeMultiplier
  252.   device->_fadeVolume = 1.0f;
  253.  
  254.  
  255.   //redundant because of the memset, but whatever
  256.   device->_closing = SDL_FALSE;
  257.   device->_fadeOut = SDL_FALSE;
  258.  
  259.  
  260.   //create initial voice, only if format != 0
  261.   if(voiceSpecObtained->format){
  262.     _IF_GOTO_ERROR(!kit_kmixerVoiceAdd(device, voiceSpecObtained, 0),;)
  263.   }
  264.  
  265.  
  266.   success = SDL_TRUE;
  267.   _error_: //success will remain SDL_FALSE on failure
  268.   if(device!=NULL && !success) kit_kmixerDeviceClose(&device);
  269.   return device; //will be null if DeviceClose is called
  270. }
  271.  
  272.  
  273.  
  274.  
  275. #if defined(_KIT_KMIXER_DEBUG) || defined(_KIT_ALL_DEBUG)
  276. int kit_kmixerDevice_Test(){
  277.   SDL_SetError("not implemented");
  278.   return 999;
  279. }
  280. #else
  281. int kit_kmixerDevice_Test(){
  282.   SDL_SetError("!defined(_KIT_KMIXER_DEBUG)");
  283.   return 999;
  284. }
  285. #endif
  286.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement