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"
- //sdl audio artifacts seem to end at 10ms after unpausing,
- //so i'll add one more millisecond just to be sure
- #define fadeInDelaySeconds 0.011
- int kit_kmixerDeviceLock(kit_kmixerDevice* device, SDL_bool lockState){
- _CHECK_IF_DEVICE_IS_VALID_R(-1)
- if(lockState){ //lock
- SDL_LockAudioDevice(device->_devID);
- if(SDL_LockMutex(device->_lock)) return -2;
- ++device->_lockCount;
- } else if (device->_lockCount>0){ //unlock (only if actually locked)
- --device->_lockCount;
- if(SDL_UnlockMutex(device->_lock)) return -2;
- SDL_UnlockAudioDevice(device->_devID);
- } else { //lockstate is false, but _lockcount is also 0
- //SDL_SetError("device is fully unlocked"); (this might overwrite actual errors!)
- return 1; //warning, not technically an error
- }
- return 0;
- }
- //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_kmixerDevice* device = userdata;
- _IF_GOTO(device->_closing, _unlock_device, kit_coreMemset(_stream,0,len))
- kit_kmixerDeviceLock(device,SDL_TRUE);
- //if previous attempt to pause device failed, memset 0 the entire buffer
- _IF_GOTO(device->_fadeInDelay==-1, _unlock_device, kit_coreMemset(_stream,0,len))
- //get values from voice 0 (device proxy)
- kit_coreVector** raw_p = &device->_voices.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, kit_coreMemset(_stream,0,len))
- SDL_bool stereo = voice0->spec.stereo;
- int size = len; //will stay as len's original value; the buffer size in bytes
- len /= sizeof(float)<<stereo; // = number of sample frames
- /**/
- _kit_kmixerVoice* voice1 = &VECTOR_INDEX_C(_kit_kmixerVoice, *raw_p, 1);
- if(voice1->spec.callback != NULL)
- voice1->spec.callback(voice1->spec.userdata,voice0->bufferInput.v,size,SDL_FALSE);
- else
- kit_coreMemset(voice0->bufferInput.v,0,size);
- /**/
- //apply simple fade effect to reduce popping when pausing and unpausing the device
- //as well as apply voice 0's volume
- kit_coreMemcpy(_stream, voice0->bufferInput.v, 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_kmixerDevicePlay(kit_kmixerDevice* device, SDL_bool playState){
- _CHECK_IF_DEVICE_IS_VALID_R(-1)
- _IF_GOTO(device->_closing,_unlock_device,;)
- 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 (about 10ms),
- //otherwise artifacts start to occur
- device->_fadeInDelay = device->_spec.freq*fadeInDelaySeconds;
- SDL_PauseAudioDevice(device->_devID,0);
- }
- _unlock_device: kit_kmixerDeviceLock(device,SDL_FALSE);
- return 0;
- }
- int kit_kmixerDeviceClose(kit_kmixerDevice** device_p){
- _IF_SDLERR(device_p==NULL,;,"device_p cannot be NULL")
- kit_kmixerDevice* device = *device_p;
- _IF_GOTO(device==NULL,_noerr_,;)
- //ignore MSB of struct ID when comparing here
- _IF_SDLERR(device->_magic.num!=_DEV_MAGIC_NUM,;,"*device_p is invalid")
- device->_closing = SDL_TRUE;
- //this should pause this thread until _kit_kmixerDeviceCallback finishes
- if(device->_devID != 0) SDL_CloseAudioDevice(device->_devID);
- if(device->_voices.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(kit_kmixerVoiceRemove(device,0)) return -4;
- }
- kit_coreVectorDestroy(&device->_voices.ord);
- kit_coreVectorDestroy(&device->_voices.raw);
- kit_coreVectorDestroy(&device->_thread.pool);
- kit_coreVectorDestroy(&device->_thread.queue);
- if(device->_thread.cond != NULL) SDL_DestroyCond(device->_thread.cond);
- 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
- }
- //(i don't know how to do exponential decay periods, so i'm using a lookup table)
- Sint32 _kit_kmixerDeviceRates[16] = {
- 1000, 2000, 4000, 8000,
- 11025, 16000, 22050, 32000,
- 44100, 48000, 88200, 96000,
- 176400, 192000, 352800, 384000
- };
- float _kit_kmixerDeviceFadeMuls[16] = {
- 0.021649449000, 0.147112140000, 0.383552000000, 0.619332447000,
- 0.706352736000, 0.786982133000, 0.840447939000, 0.887121599000,
- 0.916759476883, 0.923258340930, 0.957475575084, 0.960862072327,
- 0.978506808910, 0.980235906283, 0.989195030775, 0.990068556095
- };
- static inline float _kit_kmixerDeviceGetFadeMul(Sint32 sampleRate){
- //range checks for sampleRate is done inside kit_kmixerDeviceOpen
- Sint32 rangeLow,rangeHigh, i = 0;
- for(; i<((sizeof(_kit_kmixerDeviceRates)/sizeof(Sint32))-1); ++i){
- rangeLow = _kit_kmixerDeviceRates[i ];
- rangeHigh = _kit_kmixerDeviceRates[i+1];
- if(rangeLow<=sampleRate && sampleRate<=rangeHigh) break;
- }
- sampleRate -= rangeLow; rangeHigh -= rangeLow;
- return LERP(_kit_kmixerDeviceFadeMuls[i ], //from
- _kit_kmixerDeviceFadeMuls[i+1], //to
- (float)sampleRate/rangeHigh); //percentage
- }
- 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 stuff
- device->_thread.cond = SDL_CreateCond();
- device->_thread.queue = kit_coreVectorCreate(1,1,1,sizeof(kit_coreThread),0);
- device->_thread.pool = kit_coreVectorCreate(_kit_kmixerGlobals.threadPoolSize,1,1,
- sizeof(kit_coreThread),0);
- _IF_GOTO_ERROR(device->_thread.cond==NULL,;)
- _IF_GOTO_ERROR(device->_thread.queue==NULL,;)
- _IF_GOTO_ERROR(device->_thread.pool==NULL,;)
- //voices lists-related
- device->_voices.raw = kit_coreVectorCreate(1,1,1,sizeof(_kit_kmixerVoice),0);
- device->_voices.ord = kit_coreVectorCreate(1,1,1,sizeof(_kit_kmixerVoice*),0);
- _IF_GOTO_ERROR(device->_voices.raw==NULL,;)
- _IF_GOTO_ERROR(device->_voices.ord==NULL,;)
- //fill in obtained voice
- voiceSpecObtained->remove = voiceSpecDesired->remove;
- 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->_fadeMultiplier = _kit_kmixerDeviceGetFadeMul(specHave.freq);
- //actual volume when fading in will be 1-_fadeVolume,
- //before _fadeVolume *= _fadeMultiplier
- device->_fadeVolume = 1.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),;)
- }
- 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
- }
- #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