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"
- //500ms
- #define _totalFadeDelay (0.500f)
- //linearly fade over the course of 10ms
- #define _fadeDeltaSeconds (0.010f)
- //the most common audio clipping ends at 10-11ms after unpausing,
- //but i've seen clipping as far as ~450ms after unpausing
- #define _fadeInDelaySeconds (_totalFadeDelay - _fadeDeltaSeconds)
- void _kit_kmixerDeviceDoTasks(SDL_ThreadFunction task, kit_coreVector* threads_v,
- kit_coreVector* threadData_v)
- {
- SDL_Thread** threads = threads_v->data;
- Uint32 threads_len = threads_v->x;
- void** threadData = threadData_v->data;
- Uint32 threadData_len = VECTOR_LENS_A(threadData_v, 0,0);
- Uint32 queueLen = 0;
- for(Uint32 tdi=0; tdi<threadData_len; ++tdi){
- threads[queueLen++] = SDL_CreateThread(task,NULL,threadData[tdi]);
- if(!( queueLen%threads_len )){ //queue is full; wait for everything in queue
- for(Uint32 ti=0; ti<queueLen; ++ti) SDL_WaitThread(threads[ti],NULL);
- queueLen = 0;
- }
- }
- if(queueLen){ //wait for any left over queued threads
- for(Uint32 ti=0; ti<queueLen; ++ti) SDL_WaitThread(threads[ti],NULL);
- }
- }
- //workaround for having _kit_kmixerDeviceCallback pause the device,
- //without having to call SDL_PauseAudioDevice inside the callback itself
- int _kit_kmixerDevicePauseThread(void* data){
- kit_kmixerDevice* device = data;
- kit_kmixerDeviceLock(device,SDL_TRUE);
- SDL_PauseAudioDevice(device->_devID,1);
- device->_playing = SDL_FALSE;
- kit_kmixerDeviceLock(device,SDL_FALSE);
- return 0;
- }
- void _kit_kmixerDeviceCallback(void* userdata, Uint8* _stream, int len){
- kit_coreMemset(_stream,0,len); //_stream must be completely filled no matter what
- kit_kmixerDevice* device = userdata;
- if(device->_closing) return; //exit early if device is currently being closed
- kit_kmixerDeviceLock(device,SDL_TRUE);
- //if previous attempt to pause device failed, memset 0 the entire buffer
- _IF_GOTO(device->_fadeInDelay==-1, _unlock_device,;)
- //get values from voice 0 (device proxy)
- kit_coreVector** raw_p = &device->_raw;
- _kit_kmixerVoice* voice0 = &VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, 0);
- //(also memset 0 if there are literally no voices connected to the device)
- _IF_GOTO(voice0->inputs==NULL,_unlock_device,;)
- SDL_bool stereo = voice0->spec.stereo;
- kit_coreVector* ord = device->_ord;
- Uint32 ord_x = ord->x;
- Uint32 ord_y = ord->y;
- //adjust thread data list to accomodate new width of ord
- kit_coreVectorSet(&device->_threadData, ord_x,1,1);
- int size = len; //will stay as len's original value; the buffer size in bytes
- len /= sizeof(float)<<stereo; // = number of sample frames
- /**/
- /* //single-threaded; simplest implementation
- for(Uint32 stage=ord_y-1; stage!=U32_MAX; --stage){
- for(Uint32 index=0; index<ord_x; ++index){
- _kit_kmixerVoice* voice = VECTOR_INDEX_A(_kit_kmixerVoice*, ord, index,stage,0);
- _kit_kmixerVoiceMix(voice);
- _kit_kmixerVoiceProc(voice);
- }
- } */
- //slightly more advanced multithreaded version (which should be faster)
- for(Uint32 stage=ord_y-1; stage!=U32_MAX; --stage){
- _kit_kmixerVoice** voiceRefs = &VECTOR_INDEX_A(_kit_kmixerVoice*, ord, 0,stage,0);
- Uint32 stageVoiceLen = VECTOR_LENS_A(ord, stage,0);
- _kit_kmixerDeviceMixThreads(device, voiceRefs, stageVoiceLen);
- _kit_kmixerDeviceProcThreads(device, voiceRefs, stageVoiceLen);
- }
- /**/
- //apply linear fade to reduce popping when pausing and unpausing the device
- kit_coreMemcpy(_stream, voice0->bufferInput.data, size);
- if( stereo) _kit_kmixerDeviceStereoFade(device, (void*)_stream, len);
- else /*mono*/ _kit_kmixerDeviceMonoFade(device, (void*)_stream, len);
- _unlock_device: kit_kmixerDeviceLock(device,SDL_FALSE);
- }
- int kit_kmixerDeviceLock(kit_kmixerDevice* device, SDL_bool lockState){
- _DEVICE_VALIDITY_CHECK(0)
- if(lockState){ //lock
- _IF_GOTO_ERROR(SDL_LockMutex(device->_lock),;)
- SDL_LockAudioDevice(device->_devID);
- ++device->_lockCount;
- } else if (device->_lockCount>0){ //unlock (only if actually locked)
- _IF_GOTO_ERROR(SDL_UnlockMutex(device->_lock),;)
- SDL_UnlockAudioDevice(device->_devID);
- --device->_lockCount;
- } else { //lockstate is false, but _lockcount is also 0
- return 1; //warning, technically not an error
- }
- _noerr_: return 0;
- _error_: return -1;
- }
- int kit_kmixerDevicePlay(kit_kmixerDevice* device, SDL_bool playState){
- _DEVICE_VALIDITY_CHECK(1)
- _IF_GOTO(device->_closing,_noerr_,;) //exit normally
- kit_kmixerDeviceLock(device,SDL_TRUE);
- //this should occur when the device callback
- //fails to trigger the pause thread
- if(device->_fadeInDelay == -1){
- SDL_PauseAudioDevice(device->_devID,1);
- device->_playing = SDL_FALSE;
- device->_fadeInDelay = 0;
- }
- device->_fadeOut = !(playState&=1);
- if(playState && !device->_playing){
- //the purpose of fadeInDelay is to mute for some samples
- //to give the sdl audio device some time to warm up,
- //otherwise artifacts start to occur
- device->_fadeInDelay = device->_spec.freq*_fadeInDelaySeconds;
- SDL_PauseAudioDevice(device->_devID,0);
- }
- kit_kmixerDeviceLock(device,SDL_FALSE);
- _noerr_: return 0;
- _error_: return -1;
- }
- int kit_kmixerDeviceClose(kit_kmixerDevice** device_p){
- _IF_SDLERR(device_p==NULL,;,"device_p cannot be NULL")
- kit_kmixerDevice* device = *device_p;
- _DEVICE_VALIDITY_CHECK(1)
- device->_closing = SDL_TRUE;
- //this should pause this thread until _kit_kmixerDeviceCallback finishes
- if(device->_devID != 0) SDL_CloseAudioDevice(device->_devID);
- if(device->_raw != NULL){
- //since voice removal is recursive, removing voice 0 will remove every
- //other voice too, as every voice is connected to it in some way
- _IF_GOTO_ERROR(kit_kmixerVoiceRemove(device,0),;)
- }
- kit_coreVectorDestroy(&device->_ord);
- kit_coreVectorDestroy(&device->_raw);
- kit_coreVectorDestroy(&device->_threadData);
- kit_coreVectorDestroy(&device->_threads);
- if(device->_lock != NULL) SDL_DestroyMutex(device->_lock);
- SDL_free(device); *device_p = NULL;
- _noerr_: return 0; // 0 on success
- _error_: return -1; //-1 on failure
- }
- //todo: maybe at some point make voiceSpecObtained to be optional
- kit_kmixerDevice* kit_kmixerDeviceOpen(const char* deviceName, int allowedChanges,
- const kit_kmixerVoiceSpec* voiceSpecDesired,
- kit_kmixerVoiceSpec* voiceSpecObtained)
- {
- kit_kmixerDevice* device = NULL;
- SDL_bool success = SDL_FALSE;
- _IF_SDLERR(!_kit_kmixerGlobals.init,;,"kmixer is not initialized")
- _IF_SDLERR(voiceSpecDesired==NULL,;,"voiceSpecDesired cannot be NULL")
- _IF_GOTO_ERROR(kit_coreRealloc(&device,0,sizeof(kit_kmixerDevice)),;)
- device->_magic.num = _DEV_MAGIC_NUM; // = "kmxrDev\x00"
- //fill in sdl device specification
- SDL_AudioSpec specWant, specHave;
- specWant = _kit_kmixerDeviceVoiceToAudioSpec(device, voiceSpecDesired);
- _IF_GOTO_ERROR(!specWant.format,;)
- //open the sdl audio device itself
- allowedChanges &= ~SDL_AUDIO_ALLOW_FORMAT_CHANGE; //samples should always be f32 internally
- allowedChanges &= ~SDL_AUDIO_ALLOW_SAMPLES_CHANGE; //i want to guarantee that samples is a power of 2
- device->_devID = SDL_OpenAudioDevice(deviceName,0, &specWant,&specHave, allowedChanges);
- _IF_GOTO_ERROR(!device->_devID,;)
- _IF_SDLERR(!specHave.channels,;,"channels returned as 0") //this shouldn't be able to happen
- _IF_SDLERR(specHave.channels>2,;,"channels returned as %i",specHave.channels)
- _IF_SDLERR(specHave.freq<1000,;,"freq returned as <1kHz")
- _IF_SDLERR(specHave.freq>384000,;,"freq returned as >384kHz")
- device->_spec = specHave;
- device->_lock = SDL_CreateMutex();
- _IF_GOTO_ERROR(device->_lock==NULL,;)
- //thread and thread data lists
- device->_threads = kit_coreVectorCreate(_kit_kmixerGlobals.numDeviceThreads,1,1, sizeof(SDL_ThreadFunction),0);
- device->_threadData = kit_coreVectorCreate(1,1,1, sizeof(void*),0);
- _IF_GOTO_ERROR(device->_threads==NULL,;)
- _IF_GOTO_ERROR(device->_threadData==NULL,;)
- //voices lists-related
- device->_raw = kit_coreVectorCreate(1,1,1,sizeof(_kit_kmixerVoice),0);
- device->_ord = kit_coreVectorCreate(1,1,1,sizeof(_kit_kmixerVoice*),0);
- _IF_GOTO_ERROR(device->_raw==NULL,;)
- _IF_GOTO_ERROR(device->_ord==NULL,;)
- //fill in obtained voice
- voiceSpecObtained->remove = voiceSpecDesired->remove; //note: might be called on abort!!!
- voiceSpecObtained->callback = voiceSpecDesired->callback;
- voiceSpecObtained->userdata = voiceSpecDesired->userdata;
- voiceSpecObtained->freq = specHave.freq;
- voiceSpecObtained->_size = 0; //(will be set inside kit_kmixerVoiceAdd)
- voiceSpecObtained->stereo = specHave.channels==2;
- voiceSpecObtained->samples = specHave.samples;
- voiceSpecObtained->format = voiceSpecDesired->format;
- //fill in voice 0
- _IF_GOTO_ERROR(_kit_kmixerDeviceFillVoice0(device, voiceSpecObtained),;)
- device->_fadeDelta = 1.0f/(((float)specHave.freq)*_fadeDeltaSeconds);
- device->_fadeVolume = 0.0f;
- //redundant because of the memset, but whatever
- device->_closing = SDL_FALSE;
- device->_fadeOut = SDL_FALSE;
- //create initial voice, only if format != 0
- if(voiceSpecObtained->format){
- _IF_GOTO_ERROR(!kit_kmixerVoiceAdd(device, voiceSpecObtained, 0),;)
- } else { //otherwise, just rebuild ord
- _IF_GOTO_ERROR(_kit_kmixerVoiceRebuildOrd(device),;)
- }
- success = SDL_TRUE;
- _error_: //success will remain SDL_FALSE on failure
- if(device!=NULL && !success) kit_kmixerDeviceClose(&device);
- return device; //will be null if DeviceClose is called
- }
- int kit_kmixerDeviceUnpauseAndWait(kit_kmixerDevice* device){
- Uint64 beforePlay = SDL_GetTicks64();
- SDL_bool wasPaused = !device->_playing;
- if(kit_kmixerDevicePlay(device,SDL_TRUE)<0) return -1;
- if(wasPaused){
- Uint32 totalDelayMS = _totalFadeDelay*1000;
- Uint32 timeDifference = SDL_GetTicks64()-beforePlay; //result might be 0, but w/e
- SDL_Delay( totalDelayMS - timeDifference );
- }
- return 0;
- }
- int kit_kmixerDevicePauseAndWait(kit_kmixerDevice* device){
- if(kit_kmixerDevicePlay(device,SDL_FALSE)<0) return -1;
- Uint32 fadeOutMS = _fadeDeltaSeconds*1000 * 4; //*4 seems to completely stop clipping
- do { SDL_Delay(fadeOutMS); } while(device->_playing);
- return 0;
- }
- #if defined(_KIT_KMIXER_DEBUG) || defined(_KIT_ALL_DEBUG)
- int kit_kmixerDevice_Test(){
- SDL_SetError("not implemented");
- return 999;
- }
- #else
- int kit_kmixerDevice_Test(){
- SDL_SetError("!defined(_KIT_KMIXER_DEBUG)");
- return 999;
- }
- #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement