Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #define _CH_INFO(a,b) (((a)<<1)|(b))
- //assumes input AND output samples are f32 (intermediate type is determined by the given voice)
- int _kit_kmixerVoiceProc(void* data){ //(this is an SDL_ThreadFunction)
- _kit_kmixerVoice* voice=data;
- SDL_LockMutex(voice->lock);
- void *ibuffer=voice->bufferInput.v, *obuffer=voice->bufferOutput.v;
- void *ubuffer=voice->bufferUser.v, *cbuffer=voice->bufferConvert.v;
- void *userdata=voice->spec.userdata, *_stream=ubuffer;
- ///
- SDL_bool hasInput = voice->inputs!=NULL;
- SDL_bool ustereo=voice->spec.stereo;
- SDL_bool ostereo=voice->stereoOutput;
- ///
- SDL_AudioFormat uformat=voice->spec.format;
- Uint32 frames=voice->spec.samples, ubuffer_size=voice->spec._size;
- //convert (if necessary) input type (format) from f32
- int userTypeIsF32 = uformat==AUDIO_F32;
- if(userTypeIsF32) _stream=ibuffer; //input and user are identical; just reroute
- else _kit_kmixerVoiceProcToType(ibuffer, ubuffer, frames,uformat,ustereo);
- //the actual user callback
- voice->spec.callback(userdata, _stream, ubuffer_size, hasInput);
- //convert (if necessary) to f32 and channels, sending result to output
- int sameChannels = ustereo==ostereo;
- if(sameChannels){
- //convert type (or just copy contents if _stream is f32)
- _kit_kmixerVoiceProcFromType(_stream,obuffer, frames,uformat,ustereo);
- } else if(userTypeIsF32){ //convert channels
- _kit_kmixerVoiceProcChannels(_stream,obuffer, frames,_CH_INFO(ustereo,ostereo));
- } else { //convert type and channels
- _kit_kmixerVoiceProcFromType(_stream,cbuffer, frames,uformat,ustereo);
- _kit_kmixerVoiceProcChannels(cbuffer,obuffer, frames,_CH_INFO(ustereo,ostereo));
- }
- SDL_UnlockMutex(voice->lock);
- return 0;
- }
- //assumes all samples are f32
- //if nothing else works
- static inline void _kit_kmixerVoiceMixFallback(_kit_kmixerVoice* ovoice, kit_coreVector* _ivoices){
- if(ovoice->lock == NULL) return; //skip in case voice was removed
- SDL_LockMutex(ovoice->lock);
- Uint32 frames=ovoice->spec.samples;
- //bad things would happen if input buffer is NULL
- memset(ovoice->bufferInput.v, 0, (frames*sizeof(float))<<ovoice->spec.stereo);
- _stereo_samples_f32* osamples=ovoice->bufferInput.s;
- Uint32 samples_len=frames>>(!ovoice->spec.stereo);
- //do the mixing
- _kit_kmixerVoice* ivoices=_ivoices->ptr;
- Uint32 ivoices_len=_ivoices->x;
- for(Uint32 vi=0; vi<ivoices_len; ++vi){
- _kit_kmixerVoice* ivoice=&ivoices[vi];
- if(ivoice->lock == NULL) continue; //skip in case voice was removed
- SDL_LockMutex(ivoice->lock);
- _stereo_samples_f32* isamples=ivoice->bufferOutput.s;
- float volL=ivoice->volL, volR=ivoice->volR;
- volL=_MN(volL,1.0f); volR=_MN(volR, 1.0f);
- if(!ivoice->stereoOutput) volR=volL;
- if(volL==0 && volR==0) continue; //if volume is effectively muted, then skip the voice
- else if(volL==1.0f && volR==1.0f) goto _do_not_apply_volume; //because sample*1=sample
- else if((volL>=0)&&ivoice->applyVolume){
- if(volR<0) volR=volL;
- for(Uint32 i=0; i<samples_len; ++i){
- osamples[i].l += isamples[i].l*volL;
- osamples[i].r += isamples[i].r*volR;
- }
- } else { _do_not_apply_volume:
- for(Uint32 i=0; i<samples_len; ++i){
- osamples[i].l += isamples[i].l;
- osamples[i].r += isamples[i].r;
- }
- }
- SDL_UnlockMutex(ivoice->lock);
- }
- //hard clip output samples to between -1.0f and 1.0f
- for(Uint32 i=0; i<samples_len; ++i){
- float sampleL=osamples[i].l;
- osamples[i].l=_CLMP(sampleL, -1.0f,1.0f);
- float sampleR=osamples[i].r;
- osamples[i].r=_CLMP(sampleR, -1.0f,1.0f);
- }
- SDL_UnlockMutex(ovoice->lock);
- }
- void _kit_kmixerVoiceMix(_kit_kmixerVoice* ovoice, kit_coreVector* ivoices){
- _kit_kmixerVoiceMixFallback(ovoice, ivoices);
- }
- int _kit_kmixerVoiceRebuild(kit_kmixerDevice* device){
- _IF_GOTO_ERROR(kit_kmixerDeviceLock(device,SDL_TRUE),;)
- _kit_kmixerVoice* raw=device->_voices.raw->ptr;
- Uint32 raw_len=device->_voices.raw->x;
- //trim any empty space at the end of the raw voice list
- for(Uint32 i=raw_len-1; i>0; --i){
- if(raw[i].lock != NULL){
- _IF_GOTO_ERROR(kit_coreVectorSet(&device->_voices.raw,i+1,0,0),;)
- break;
- }
- }
- raw=device->_voices.raw->ptr; //update raw info
- raw_len=device->_voices.raw->x; //^^
- //probe current highest chain stage, to pre-set ord and chain's x to it
- //(so i don't need to call kit_coreVectorSet as much)
- Uint32 highestVoiceChain=0;
- for(Uint32 i=0; i<raw_len; ++i){
- if(raw[i].lock != NULL){
- if(raw[i].chainStage > highestVoiceChain)
- highestVoiceChain=raw[i].chainStage;
- }
- }
- //rebuild ord (and by extension chain)
- kit_coreVector** ord_p=&device->_voices.ord;
- _IF_GOTO_ERROR(kit_coreVectorSet(ord_p,1,1,0),;) //so new size inits to 0
- _IF_GOTO_ERROR(kit_coreVectorSet(ord_p,highestVoiceChain+1,1,0),;)
- _kit_kmixerVoice*** ord=(*ord_p)->ptr;
- kit_coreVector** chain_p=&device->_voices.chain;
- _IF_GOTO_ERROR(kit_coreVectorSet(chain_p,1,0,0),;) //so new size inits to 0
- _IF_GOTO_ERROR(kit_coreVectorSet(chain_p,highestVoiceChain+1,0,0),;)
- Uint32* chain=(*chain_p)->ptr; chain[0]=0; //so voice 0 will ++ back to 1
- for(Uint32 i=0; i<raw_len; ++i){
- if(raw[i].lock != NULL){
- Uint32 chainStage=raw[i].chainStage; //x position in ord
- Uint32 stageIndex=chain[chainStage]; //y position in ord
- //check if extension of ord's y is required
- if(++chain[chainStage] > (*ord_p)->y){
- _IF_GOTO_ERROR(kit_coreVectorAdd(ord_p,0,1,0),;)
- ord=(*ord_p)->ptr;
- }
- //finally, assign reference to proper place in ord
- ord[chainStage][stageIndex]=&raw[i];
- }
- }
- _IF_GOTO_ERROR(kit_kmixerDeviceLock(device,SDL_FALSE),;)
- return 0;
- _error_:
- return -1;
- }
- int kit_kmixerVoiceRemove(kit_kmixerDevice* device, Uint32 voiceID){
- _IF_SDLERR(device==NULL,;,"device cannot be NULL")
- _IF_SDLERR((device->_magic.num&U64_MSBC)!=_DEV_MAGIC_NUM,;,"invalid device struct")
- //if bit 31 is set, call is assumed to be the result of recursion
- SDL_bool rootCall=(!(voiceID&0x80000000)) ? SDL_TRUE : SDL_FALSE;
- voiceID&=0x7fffffff; //unset bit 31 now that rootCall is set
- //only allow voice 0 to be removed when device->_closing is set
- _IF_SDLERR(!voiceID && !device->_closing,;,"cannot remove device 0")
- _kit_kmixerVoice* raw=device->_voices.raw->ptr;
- _kit_kmixerVoice* voice=&raw[voiceID];
- if(voice->lock == NULL) return 0; //return normally if voice already removed
- _IF_GOTO_ERROR(SDL_LockMutex(voice->lock),;)
- //loop through and remove any inputs the voice might have (recursive)
- if(voice->inputs != NULL){
- Uint32* inputs=voice->inputs->ptr;
- Uint32 inputs_len=voice->inputs->x;
- for(Uint32 i=0; i<inputs_len; ++i){
- Uint32 index=inputs[i];
- if(raw[index].lock != NULL) //bit 31 is set to indicate recursion
- _IF_GOTO_ERROR(kit_kmixerVoiceRemove(device,0x80000000|index),;)
- }
- //(kit_coreVectorDestroy automatically sets pointer to NULL)
- _IF_GOTO_ERROR(kit_coreVectorDestroy(&voice->inputs),;)
- _IF_GOTO_ERROR(kit_coreVectorDestroy(&voice->inputRefs),;)
- }
- //free buffers
- if(voice->bufferInput.v != NULL) SDL_free(voice->bufferInput.v);
- if(voice->bufferUser.v != NULL) SDL_free(voice->bufferUser.v);
- if(voice->bufferConvert.v != NULL) SDL_free(voice->bufferConvert.v);
- if(voice->bufferOutput.v != NULL) SDL_free(voice->bufferOutput.v);
- //call the user's 'userdata removal' callback
- if(voice->spec.remove!=NULL) voice->spec.remove(voice->spec.userdata);
- _IF_GOTO_ERROR(SDL_UnlockMutex(voice->lock),;)
- SDL_DestroyMutex(voice->lock);
- voice->lock=NULL; //this indicates that it was removed successfully
- if(rootCall){ //if this call is not the result of recursion
- //trim raw, and rebuild ord and chain
- _IF_GOTO_ERROR(_kit_kmixerVoiceRebuild(device),;)
- //trim inputs of output voice, potentially destroying inputs if none are left
- _kit_kmixerVoice* voiceO=voice->output;
- _IF_SDLERR(voiceO==NULL,;,"voice->output == NULL"); //shouldn't be able to happen
- _IF_GOTO_ERROR(SDL_LockMutex(voiceO->lock),;)
- kit_coreVector** inputRefs_p=&voiceO->inputRefs;
- _kit_kmixerVoice** inputRefs=(*inputRefs_p)->ptr;
- Uint32 inputs_len=(*inputRefs_p)->x;
- ///
- Sint32 lastIndex=-1; //will remain -1 if inputs are completely empty
- for(Uint32 i=0; i<inputs_len; ++i){
- if(inputRefs[i]->lock != NULL) lastIndex=i;
- }
- if(lastIndex==-1){ //destroy
- kit_coreVectorDestroy(&voiceO->inputs);
- kit_coreVectorDestroy(inputRefs_p);
- } else if(++lastIndex < inputs_len){ //only trim
- _IF_GOTO_ERROR(kit_coreVectorSet(&voiceO->inputs, lastIndex,0,0),;)
- _IF_GOTO_ERROR(kit_coreVectorSet(inputRefs_p, lastIndex,0,0),;)
- }
- _IF_GOTO_ERROR(SDL_UnlockMutex(voiceO->lock),;)
- }
- return 0;
- _error_:
- return -1;
- }
- Uint32 kit_kmixerVoiceAdd(kit_kmixerDevice* device, kit_kmixerVoiceSpec* spec,
- Uint32 outputVoiceID)
- {
- //create voice and check device validity
- Sint32 newIndex=0; //0 for error by default
- _kit_kmixerVoice voice;
- SDL_memset(&voice, 0, sizeof(_kit_kmixerVoice)); //just in case
- _IF_SDLERR(device==NULL,;,"device cannot be NULL")
- _IF_SDLERR((device->_magic.num&U64_MSBC)!=_DEV_MAGIC_NUM,;,"invalid device struct")
- //raw voice list
- _kit_kmixerVoice* raw=device->_voices.raw->ptr;
- Uint32 raw_len=device->_voices.raw->x;
- _IF_SDLERR(outputVoiceID>=raw_len,;,"outputVoiceID > voice count")
- //lock is used to check if a voice is valid
- _IF_SDLERR(raw[outputVoiceID].lock==NULL,;,"output voice is nonexistent");
- //voice0 is a pseudo-voice that acts as a device proxy
- _kit_kmixerVoice* voice0=device->_voices.raw->ptr;
- //<<!stereo so size is always assumed to be stereo unless specified otherwise
- Uint32 voice0_size = voice0->spec._size<<(!voice0->spec.stereo);
- //set voice spec's device-oriented stuff
- //(.stereo is set by the voice, but .samples is set when opening the device)
- Uint32 voice_size = voice0->spec.samples<<spec->stereo;
- //set voice_size to size of user buffer
- switch(spec->format){
- case AUDIO_F32: SDL_FALLTHROUGH;
- case AUDIO_S32: voice_size*=2; SDL_FALLTHROUGH;
- case AUDIO_S16: voice_size*=2; SDL_FALLTHROUGH;
- case AUDIO_U8 : spec->_size=voice_size; break;
- default: _IS_SDLERR(;,"spec's format is invalid");
- }
- spec->freq=voice0->spec.freq;
- spec->samples=voice0->spec.samples;
- //fill in new voice
- _kit_kmixerVoice* voiceO=&raw[outputVoiceID]; //the voice to output to
- voice.lock=SDL_CreateMutex();
- _IF_GOTO_ERROR(voice.lock==NULL,;)
- //inputs & inputRefs is made once another voice is created,
- //that outputs to this voice
- voice.output=voiceO;
- voice.bufferInput.v =SDL_malloc(voice0_size>>(!spec->stereo));
- voice.bufferUser.v =SDL_malloc(voice_size);
- voice.bufferConvert.v=SDL_malloc(voice0_size);
- voice.bufferOutput.v =SDL_malloc(voice0_size>>(!voiceO->spec.stereo));
- voice.spec=*spec;
- //set chain processing stage one higher,
- //to ensure this voice is processed before the output
- voice.chainStage=voiceO->chainStage+1;
- //(voice.index is set later)
- voice.volL=1.0f;
- voice.volR=(voice.spec.stereo) ? 1.0f : -1.0f;
- voice.applyVolume=SDL_TRUE;
- voice.stereoOutput=voiceO->spec.stereo;
- //add this voice's id to voiceO's inputs
- //first, attempt to find an empty slot inside current raw list...
- _IF_GOTO_ERROR(kit_kmixerDeviceLock(device,SDL_TRUE),;)
- for(Uint32 i=0; i<raw_len; ++i){
- if(raw[i].lock==NULL){
- voice.index=newIndex = i;
- raw[i]=voice; break;
- }
- }
- //...and if no empty space was found, append the voice to raw
- if(!newIndex){
- newIndex=kit_coreVectorAppend(&device->_voices.raw, &voice, 0,0);
- //(kit_coreVectorAppend will return negative on error)
- _IF_GOTO_ERROR(newIndex<0,newIndex=0)
- }
- //update ord with new index (append only; don't call _kit_kmixerVoiceRebuild)
- kit_coreVector** ord_p=&device->_voices.ord;
- _kit_kmixerVoice*** ord=(*ord_p)->ptr;
- kit_coreVector** chain_p=&device->_voices.chain;
- Uint32* chain=(*chain_p)->ptr;
- ///
- Uint32 chainStage=voice.chainStage;
- _kit_kmixerVoice* voiceInAddress=&raw[newIndex];
- if(chainStage>=(*ord_p)->x){ //make sure ord->x and chain->x is long enough
- _IF_GOTO_ERROR(kit_coreVectorSet(ord_p,chainStage+1,0,0),;)
- _IF_GOTO_ERROR(kit_coreVectorSet(chain_p,chainStage+1,0,0),;)
- ord=(*ord_p)->ptr;
- chain=(*chain_p)->ptr;
- }
- if(ord[chainStage][0]!=NULL && ord[chainStage][0]->lock!=NULL){
- _IF_GOTO_ERROR(kit_coreVectorAppend(ord_p, &voiceInAddress, chainStage,0)<0,;)
- ++chain[chainStage]; //++ since a new index was added to ord[chainStage]
- } else { //first reference in ord[chainStage] is actually free
- ord[chainStage][0]=voiceInAddress;
- }
- _IF_GOTO_ERROR(kit_kmixerDeviceLock(device,SDL_FALSE),;)
- //finally, add the id, as well as the voice's reference to voiceO
- _IF_GOTO_ERROR(SDL_LockMutex(voiceO->lock),;)
- if(voiceO->inputs!=NULL){
- _IF_GOTO_ERROR(kit_coreVectorAppend(&voiceO->inputs,&newIndex,0,0)<0,;)
- _IF_GOTO_ERROR(kit_coreVectorAppend(&voiceO->inputRefs,&voiceInAddress,0,0)<0,;)
- } else {
- voiceO->inputs=kit_coreVectorCreate(1,0,0,sizeof(Uint32),U32_STR("U4 \x00"),NULL);
- _IF_GOTO_ERROR(voiceO->inputs==NULL,;)
- voiceO->inputRefs=kit_coreVectorCreate(1,0,0,sizeof(_kit_kmixerVoice*),U32_STR("kVp\x00"),NULL);
- _IF_GOTO_ERROR(voiceO->inputRefs==NULL,;)
- ((Uint32*)voiceO->inputs->ptr)[0]=newIndex;
- ((_kit_kmixerVoice**)voiceO->inputRefs->ptr)[0]=voiceInAddress;
- }
- _IF_GOTO_ERROR(SDL_UnlockMutex(voiceO->lock),;)
- _error_:
- if(!newIndex && voice.lock!=NULL){
- }
- return newIndex;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement