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_kmixerPrivate.h"
- #include "../_private/include/_kit_privmacro.h"
- #define fadeCutoff 0.0002
- //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(-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");
- 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;
- }
- //remember, ignore input if index is 0
- void _kit_kmixerDeviceCallback(void* userdata, Uint8* _stream, int len){
- kit_kmixerDevice* device=userdata;
- SDL_LockMutex(device->_lock);
- _IF_GOTO(device->_closing,_unlock_device,;)
- //if previous attempt to pause device failed, memset 0 the entire buffer
- _IF_GOTO(device->_fadeInDelay==-1,_unlock_device, memset(_stream,0,len))
- //get values from device
- float fadeMultiplier=device->_fadeMuliplier;
- float fadeVolume=device->_fadeVolume;
- Uint32 fadeInDelay=device->_fadeInDelay;
- //get values from voice 0 (device proxy)
- //(input is for voice 0 is always set to _stream here)
- _kit_kmixerVoice* raw=device->_voices.raw->ptr;
- //(also memset 0 if there are literally no voices connected to the device)
- _IF_GOTO(raw[0].inputs==NULL,_unlock_device, memset(_stream,0,len))
- _kit_kmixerVoice* voice0=&raw[0];
- SDL_bool stereo=voice0->spec.stereo;
- float volL=voice0->volL; volL=(volL>=0)?volL:1.0f;
- float volR=voice0->volR; volR=(volR>=0)?volR:volL;
- int size=len; //will stay as len's original value; the buffer size in bytes
- len/=sizeof(float)<<stereo; // = number of sample frames
- /**/
- if(voice0->spec.callback!=NULL)
- voice0->spec.callback(voice0->spec.userdata,voice0->bufferInput.v,size,SDL_FALSE);
- //_kit_kmixerVoice* voice=&raw[1];
- //memset(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
- memcpy(_stream, voice0->bufferInput.v, size);
- Uint32 i=0; //this index is shared, as the loops can jump to others at will
- //stereo samples
- if(stereo){ _stereo_samples_f32* stream=(void*)_stream;
- if(device->_fadeOut){
- _do_fade_out_stereo: //SDL_Log("fade out stereo");
- for(; i<len; ++i){
- if(!device->_fadeOut){ fadeVolume=1-fadeVolume; goto _do_fade_in_stereo; }
- stream[i].l*=fadeVolume*volL;
- stream[i].r*=fadeVolume*volR;
- fadeVolume*=fadeMultiplier;
- }
- if(fadeVolume<fadeCutoff){
- SDL_Thread* t=SDL_CreateThread(_kit_kmixerDevicePauseThread,"_PauseTh", device);
- //setting _fadeInDelay to -1 will make further calls to the device callback
- //to simply memset 0 until kit_kmixerDevicePlay is called again
- if(t==NULL) device->_fadeInDelay=-1; //0xffffffff
- }
- } else if(!fadeInDelay){
- _do_normal_stereo: //SDL_Log("normal stereo");
- for(; i<len; ++i){
- if(device->_fadeOut){ goto _do_fade_out_stereo; }
- stream[i].l*=volL;
- stream[i].r*=volR;
- }
- } else { //let device warm up before fading in
- for(; (fadeInDelay)&&(i<len); ++i){ //write 0s for fadeInDelaySeconds
- stream[i].l=stream[i].r = 0; --fadeInDelay;
- }
- _do_fade_in_stereo: //SDL_Log("fade in stereo");
- for(; i<len; ++i){
- if(device->_fadeOut){ fadeVolume=1-fadeVolume; goto _do_fade_out_stereo; }
- else if(fadeVolume<fadeCutoff){ fadeVolume=1; goto _do_normal_stereo; }
- stream[i].l*=(1-fadeVolume)*volL;
- stream[i].r*=(1-fadeVolume)*volR;
- fadeVolume*=fadeMultiplier;
- }
- }
- //mono samples
- } else { float* stream=(void*)_stream;
- if(device->_fadeOut){
- _do_fade_out_mono: //SDL_Log("fade out mono");
- for(; i<len; ++i){
- if(!device->_fadeOut){ fadeVolume=1-fadeVolume; goto _do_fade_in_mono; }
- stream[i]*=fadeVolume*volL;
- fadeVolume*=fadeMultiplier;
- }
- if(fadeVolume<fadeCutoff){
- SDL_Thread* t=SDL_CreateThread(_kit_kmixerDevicePauseThread,"_PauseTh", device);
- if(t==NULL) device->_fadeInDelay=-1; //0xffffffff
- }
- } else if(!fadeInDelay){
- _do_normal_mono: //SDL_Log("normal mono");
- for(; i<len; ++i){
- if(device->_fadeOut){ goto _do_fade_out_mono; }
- stream[i]*=volL;
- }
- } else { //let device warm up before fading in
- for(; (fadeInDelay)&&(i<len); ++i){ //write 0s for fadeInDelaySeconds
- stream[i]=0; --fadeInDelay;
- }
- _do_fade_in_mono: //SDL_Log("fade in mono");
- for(; i<len; ++i){
- if(device->_fadeOut){ fadeVolume=1-fadeVolume; goto _do_fade_out_mono; }
- else if(fadeVolume<fadeCutoff){ fadeVolume=1; goto _do_normal_mono; }
- stream[i]*=(1-fadeVolume)*volL;
- fadeVolume*=fadeMultiplier;
- }
- }
- }
- //update device struct to any relevant new values
- device->_fadeVolume=fadeVolume; //update fade volume
- device->_fadeInDelay=fadeInDelay; //update fade in delay
- _unlock_device: SDL_UnlockMutex(device->_lock);
- }
- int kit_kmixerDevicePlay(kit_kmixerDevice* device, SDL_bool playState){
- _CHECK_IF_DEVICE_IS_VALID(-1)
- kit_kmixerDeviceLock(device,SDL_TRUE);
- _IF_GOTO(device->_closing,_unlock_device,;)
- //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
- 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_R(device_p==NULL,-1,;,"device_p cannot be NULL")
- kit_kmixerDevice* device=*device_p;
- _IF_SDLERR_R(device==NULL,-2,;,"*device_p cannot be NULL")
- //ignore MSB of struct ID when comparing here
- _IF_SDLERR_R((device->_magic.num&U64_MSBC)!=_DEV_MAGIC_NUM,-3,;,"*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;
- }
- if(device->_voices.chain != NULL) kit_coreVectorDestroy(&device->_voices.chain);
- if(device->_voices.ord != NULL) kit_coreVectorDestroy(&device->_voices.ord);
- if(device->_voices.raw != NULL) kit_coreVectorDestroy(&device->_voices.raw);
- if(device->_thread.pool != NULL) kit_coreVectorDestroy(&device->_thread.pool);
- if(device->_thread.queue != NULL) 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;
- return 0;
- }
- //(i don't know how to do exponential decay, 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
- };
- #define _LERP(_v0,_v1, _t) ((1-(_t))*(_v0) + (_t)*(_v1))
- 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;
- _IF_SDLERR(!_kit_kmixerGlobals.init,;,"kmixer is not initialized")
- _IF_SDLERR(voiceSpecDesired==NULL,;,"voiceSpecDesired cannot be NULL")
- device=SDL_malloc(sizeof(kit_kmixerDevice));
- _IF_GOTO_ERROR(device==NULL,;)
- memset(device, 0, sizeof(kit_kmixerDevice));
- //the high byte will be set to \x00 on success,
- //because _magic.str could then be used as a null-terminated string ("kmxrDev\x00")
- device->_magic.num=U64_MSB|_DEV_MAGIC_NUM; //="kmxrDev\xff"
- SDL_AudioSpec specWant, specHave;
- specWant.freq=voiceSpecDesired->freq;
- specWant.format=AUDIO_F32;
- specWant.channels=1+(voiceSpecDesired->stereo&1);
- specWant.samples=voiceSpecDesired->samples;
- specWant.callback=_kit_kmixerDeviceCallback;
- specWant.userdata=device;
- //assuming 44.1kHz, <=16 sample frames would be well under 1ms lol
- //(even at 8kHz, it'd only be 2ms)
- _IF_SDLERR(specWant.samples<32,;,"samples < 32");
- _IF_SDLERR(count_bits(specWant.samples)>1,;,"samples not a power of 2");
- //the sdl audio device itself
- allowedChanges&=~SDL_AUDIO_ALLOW_FORMAT_CHANGE; //samples are always 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;
- //fill in obtained voice
- voiceSpecObtained->remove =voiceSpecDesired->remove;
- voiceSpecObtained->callback=voiceSpecDesired->callback;
- voiceSpecObtained->userdata=voiceSpecDesired->userdata;
- voiceSpecObtained->freq =specHave.freq;
- voiceSpecObtained->_size =0; //filled in during call to kit_kmixerVoiceAdd
- voiceSpecObtained->stereo =specHave.channels==2;
- voiceSpecObtained->samples =specHave.samples;
- voiceSpecObtained->format =voiceSpecDesired->format;
- device->_lock=SDL_CreateMutex();
- _IF_GOTO_ERROR(device->_lock==NULL,;)
- //thread stuff
- device->_thread.cond=SDL_CreateCond();
- _IF_GOTO_ERROR(device->_thread.cond==NULL,;)
- device->_thread.queue=kit_coreVectorCreate(1,0,0,sizeof(kit_coreThread),U32_STR("cT \x00"),NULL);
- _IF_GOTO_ERROR(device->_thread.queue==NULL,;)
- device->_thread.pool=kit_coreVectorCreate(_kit_kmixerGlobals.threadPoolSize,0,0,
- sizeof(kit_coreThread),U32_STR("cT \x00"),NULL);
- _IF_GOTO_ERROR(device->_thread.pool==NULL,;)
- //voices lists-related
- device->_voices.raw=kit_coreVectorCreate(1,0,0,sizeof(_kit_kmixerVoice),U32_STR("kV \x00"),NULL);
- _IF_GOTO_ERROR(device->_voices.raw==NULL,;)
- device->_voices.ord=kit_coreVectorCreate(1,1,0,sizeof(_kit_kmixerVoice),U32_STR("kV \x00"),NULL);
- _IF_GOTO_ERROR(device->_voices.ord==NULL,;)
- device->_voices.chain=kit_coreVectorCreate(1,0,0,sizeof(Uint32),U32_STR("U4 \x00"),NULL);
- _IF_GOTO_ERROR(device->_voices.chain==NULL,;)
- //fill in voice 0
- _kit_kmixerVoice* raw=device->_voices.raw->ptr;
- raw->lock=SDL_CreateMutex();
- _IF_GOTO_ERROR(raw->lock==NULL,;)
- raw->inputs =NULL; //created when a voice gets added
- raw->inputRefs=NULL; //also created when a voice gets added
- raw->output =NULL; //unused for voice 0
- //(voice 0's bufferInput is copied to device callback's _stream)
- raw->bufferInput.v =SDL_malloc(specHave.size);
- raw->bufferUser.v =NULL; //unused for voice 0
- raw->bufferConvert.v=NULL; //unused for voice 0
- raw->bufferOutput.v =NULL; //unused for voice 0
- raw->spec.callback=voiceSpecObtained->callback;
- raw->spec.userdata=voiceSpecObtained->userdata;
- raw->spec.freq =specHave.freq;
- raw->spec._size =specHave.size;
- raw->spec.stereo =specHave.channels==2;
- raw->spec.samples =specHave.samples;
- raw->spec.format =specHave.format;
- raw->chainStage=0;
- raw->index=0;
- raw->volL=1.0f;
- raw->volR=(raw->spec.stereo) ? 1.0f : -1.0f;
- raw->applyVolume=SDL_TRUE;
- raw->stereoOutput=raw->spec.stereo;
- //copy reference of voice 0 to ord...
- ((_kit_kmixerVoice***)device->_voices.ord->ptr)[0][0]=&raw[0];
- //...then set first element of chain to 1
- ((Uint32*)device->_voices.chain->ptr)[0]=1;
- device->_fadeMuliplier=_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),;)
- }
- device->_magic.num&=U64_MSBC; //set high byte to 0 to indicate success
- _error_: //high byte will remain 0xFF on failure
- if(device!=NULL && device->_magic.num&U64_MSB) kit_kmixerDeviceClose(&device);
- return device; //will be null if DeviceClose is called
- }
- #if defined(_KIT_KMIXER_DEBUG) || defined(_KIT_ALL_DEBUG)
- int kit_kmixerDeviceTest(){
- SDL_SetError("not implemented");
- return 0;
- }
- #else
- int kit_kmixerDeviceTest(){
- SDL_SetError("!defined(_KIT_KMIXER_DEBUG)");
- return 999;
- }
- #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement