rlm@0: /** rlm@0: * OpenAL cross platform audio library rlm@0: * Copyright (C) 1999-2007 by authors. rlm@0: * This library is free software; you can redistribute it and/or rlm@0: * modify it under the terms of the GNU Library General Public rlm@0: * License as published by the Free Software Foundation; either rlm@0: * version 2 of the License, or (at your option) any later version. rlm@0: * rlm@0: * This library is distributed in the hope that it will be useful, rlm@0: * but WITHOUT ANY WARRANTY; without even the implied warranty of rlm@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU rlm@0: * Library General Public License for more details. rlm@0: * rlm@0: * You should have received a copy of the GNU Library General Public rlm@0: * License along with this library; if not, write to the rlm@0: * Free Software Foundation, Inc., 59 Temple Place - Suite 330, rlm@0: * Boston, MA 02111-1307, USA. rlm@0: * Or go to http://www.gnu.org/copyleft/lgpl.html rlm@0: */ rlm@0: rlm@0: #include "config.h" rlm@0: rlm@0: #include rlm@0: #include rlm@0: #include rlm@0: #include "alMain.h" rlm@0: #include "AL/al.h" rlm@0: #include "AL/alc.h" rlm@0: #include "alError.h" rlm@0: #include "alSource.h" rlm@0: #include "alBuffer.h" rlm@0: #include "alThunk.h" rlm@0: #include "alAuxEffectSlot.h" rlm@0: rlm@0: rlm@0: enum Resampler DefaultResampler; rlm@0: const ALsizei ResamplerPadding[RESAMPLER_MAX] = { rlm@0: 0, /* Point */ rlm@0: 1, /* Linear */ rlm@0: 2, /* Cubic */ rlm@0: }; rlm@0: const ALsizei ResamplerPrePadding[RESAMPLER_MAX] = { rlm@0: 0, /* Point */ rlm@0: 0, /* Linear */ rlm@0: 1, /* Cubic */ rlm@0: }; rlm@0: rlm@0: rlm@0: static ALvoid InitSourceParams(ALsource *Source); rlm@0: static ALvoid GetSourceOffset(ALsource *Source, ALenum eName, ALdouble *Offsets, ALdouble updateLen); rlm@0: static ALint GetByteOffset(ALsource *Source); rlm@0: rlm@0: #define LookupSource(m, k) ((ALsource*)LookupUIntMapKey(&(m), (k))) rlm@0: #define LookupBuffer(m, k) ((ALbuffer*)LookupUIntMapKey(&(m), (k))) rlm@0: #define LookupFilter(m, k) ((ALfilter*)LookupUIntMapKey(&(m), (k))) rlm@0: #define LookupEffectSlot(m, k) ((ALeffectslot*)LookupUIntMapKey(&(m), (k))) rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alGenSources(ALsizei n,ALuint *sources) rlm@0: { rlm@0: ALCcontext *Context; rlm@0: ALCdevice *Device; rlm@0: rlm@0: Context = GetLockedContext(); rlm@0: if(!Context) return; rlm@0: rlm@0: Device = Context->Device; rlm@0: if(n < 0 || IsBadWritePtr((void*)sources, n * sizeof(ALuint))) rlm@0: alSetError(Context, AL_INVALID_VALUE); rlm@0: else if((ALuint)n > Device->MaxNoOfSources - Context->SourceMap.size) rlm@0: alSetError(Context, AL_INVALID_VALUE); rlm@0: else rlm@0: { rlm@0: ALenum err; rlm@0: ALsizei i; rlm@0: rlm@0: // Add additional sources to the list rlm@0: i = 0; rlm@0: while(i < n) rlm@0: { rlm@0: ALsource *source = calloc(1, sizeof(ALsource)); rlm@0: if(!source) rlm@0: { rlm@0: alSetError(Context, AL_OUT_OF_MEMORY); rlm@0: alDeleteSources(i, sources); rlm@0: break; rlm@0: } rlm@0: rlm@0: err = NewThunkEntry(&source->source); rlm@0: if(err == AL_NO_ERROR) rlm@0: err = InsertUIntMapEntry(&Context->SourceMap, source->source, source); rlm@0: if(err != AL_NO_ERROR) rlm@0: { rlm@0: FreeThunkEntry(source->source); rlm@0: memset(source, 0, sizeof(ALsource)); rlm@0: free(source); rlm@0: rlm@0: alSetError(Context, err); rlm@0: alDeleteSources(i, sources); rlm@0: break; rlm@0: } rlm@0: rlm@0: sources[i++] = source->source; rlm@0: InitSourceParams(source); rlm@0: } rlm@0: } rlm@0: rlm@0: UnlockContext(Context); rlm@0: } rlm@0: rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) rlm@0: { rlm@0: ALCcontext *Context; rlm@0: ALsource *Source; rlm@0: ALsizei i, j; rlm@0: ALbufferlistitem *BufferList; rlm@0: ALboolean SourcesValid = AL_FALSE; rlm@0: rlm@0: Context = GetLockedContext(); rlm@0: if(!Context) return; rlm@0: rlm@0: if(n < 0) rlm@0: alSetError(Context, AL_INVALID_VALUE); rlm@0: else rlm@0: { rlm@0: SourcesValid = AL_TRUE; rlm@0: // Check that all Sources are valid (and can therefore be deleted) rlm@0: for(i = 0;i < n;i++) rlm@0: { rlm@0: if(LookupSource(Context->SourceMap, sources[i]) == NULL) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_NAME); rlm@0: SourcesValid = AL_FALSE; rlm@0: break; rlm@0: } rlm@0: } rlm@0: } rlm@0: rlm@0: if(SourcesValid) rlm@0: { rlm@0: // All Sources are valid, and can be deleted rlm@0: for(i = 0;i < n;i++) rlm@0: { rlm@0: // Recheck that the Source is valid, because there could be duplicated Source names rlm@0: if((Source=LookupSource(Context->SourceMap, sources[i])) == NULL) rlm@0: continue; rlm@0: rlm@0: for(j = 0;j < Context->ActiveSourceCount;j++) rlm@0: { rlm@0: if(Context->ActiveSources[j] == Source) rlm@0: { rlm@0: ALsizei end = --(Context->ActiveSourceCount); rlm@0: Context->ActiveSources[j] = Context->ActiveSources[end]; rlm@0: break; rlm@0: } rlm@0: } rlm@0: rlm@0: // For each buffer in the source's queue... rlm@0: while(Source->queue != NULL) rlm@0: { rlm@0: BufferList = Source->queue; rlm@0: Source->queue = BufferList->next; rlm@0: rlm@0: if(BufferList->buffer != NULL) rlm@0: BufferList->buffer->refcount--; rlm@0: free(BufferList); rlm@0: } rlm@0: rlm@0: for(j = 0;j < MAX_SENDS;++j) rlm@0: { rlm@0: if(Source->Send[j].Slot) rlm@0: Source->Send[j].Slot->refcount--; rlm@0: Source->Send[j].Slot = NULL; rlm@0: } rlm@0: rlm@0: // Remove Source from list of Sources rlm@0: RemoveUIntMapKey(&Context->SourceMap, Source->source); rlm@0: FreeThunkEntry(Source->source); rlm@0: rlm@0: memset(Source,0,sizeof(ALsource)); rlm@0: free(Source); rlm@0: } rlm@0: } rlm@0: rlm@0: UnlockContext(Context); rlm@0: } rlm@0: rlm@0: rlm@0: AL_API ALboolean AL_APIENTRY alIsSource(ALuint source) rlm@0: { rlm@0: ALCcontext *Context; rlm@0: ALboolean result; rlm@0: rlm@0: Context = GetLockedContext(); rlm@0: if(!Context) return AL_FALSE; rlm@0: rlm@0: result = (LookupSource(Context->SourceMap, source) ? AL_TRUE : AL_FALSE); rlm@0: rlm@0: UnlockContext(Context); rlm@0: rlm@0: return result; rlm@0: } rlm@0: rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alSourcef(ALuint source, ALenum eParam, ALfloat flValue) rlm@0: { rlm@0: ALCcontext *pContext; rlm@0: ALsource *Source; rlm@0: rlm@0: pContext = GetLockedContext(); rlm@0: if(!pContext) return; rlm@0: rlm@0: if((Source=LookupSource(pContext->SourceMap, source)) != NULL) rlm@0: { rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_PITCH: rlm@0: if(flValue >= 0.0f) rlm@0: { rlm@0: Source->flPitch = flValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_CONE_INNER_ANGLE: rlm@0: if(flValue >= 0.0f && flValue <= 360.0f) rlm@0: { rlm@0: Source->flInnerAngle = flValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_CONE_OUTER_ANGLE: rlm@0: if(flValue >= 0.0f && flValue <= 360.0f) rlm@0: { rlm@0: Source->flOuterAngle = flValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_GAIN: rlm@0: if(flValue >= 0.0f) rlm@0: { rlm@0: Source->flGain = flValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_MAX_DISTANCE: rlm@0: if(flValue >= 0.0f) rlm@0: { rlm@0: Source->flMaxDistance = flValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_ROLLOFF_FACTOR: rlm@0: if(flValue >= 0.0f) rlm@0: { rlm@0: Source->flRollOffFactor = flValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_REFERENCE_DISTANCE: rlm@0: if(flValue >= 0.0f) rlm@0: { rlm@0: Source->flRefDistance = flValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_MIN_GAIN: rlm@0: if(flValue >= 0.0f && flValue <= 1.0f) rlm@0: { rlm@0: Source->flMinGain = flValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_MAX_GAIN: rlm@0: if(flValue >= 0.0f && flValue <= 1.0f) rlm@0: { rlm@0: Source->flMaxGain = flValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_CONE_OUTER_GAIN: rlm@0: if(flValue >= 0.0f && flValue <= 1.0f) rlm@0: { rlm@0: Source->flOuterGain = flValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_CONE_OUTER_GAINHF: rlm@0: if(flValue >= 0.0f && flValue <= 1.0f) rlm@0: { rlm@0: Source->OuterGainHF = flValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_AIR_ABSORPTION_FACTOR: rlm@0: if(flValue >= 0.0f && flValue <= 10.0f) rlm@0: { rlm@0: Source->AirAbsorptionFactor = flValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_ROOM_ROLLOFF_FACTOR: rlm@0: if(flValue >= 0.0f && flValue <= 10.0f) rlm@0: { rlm@0: Source->RoomRolloffFactor = flValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_DOPPLER_FACTOR: rlm@0: if(flValue >= 0.0f && flValue <= 1.0f) rlm@0: { rlm@0: Source->DopplerFactor = flValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_SEC_OFFSET: rlm@0: case AL_SAMPLE_OFFSET: rlm@0: case AL_BYTE_OFFSET: rlm@0: if(flValue >= 0.0f) rlm@0: { rlm@0: Source->lOffsetType = eParam; rlm@0: rlm@0: // Store Offset (convert Seconds into Milliseconds) rlm@0: if(eParam == AL_SEC_OFFSET) rlm@0: Source->lOffset = (ALint)(flValue * 1000.0f); rlm@0: else rlm@0: Source->lOffset = (ALint)flValue; rlm@0: rlm@0: if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && rlm@0: !pContext->DeferUpdates) rlm@0: { rlm@0: if(ApplyOffset(Source) == AL_FALSE) rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: } rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: default: rlm@0: alSetError(pContext, AL_INVALID_ENUM); rlm@0: break; rlm@0: } rlm@0: } rlm@0: else rlm@0: { rlm@0: // Invalid Source Name rlm@0: alSetError(pContext, AL_INVALID_NAME); rlm@0: } rlm@0: rlm@0: UnlockContext(pContext); rlm@0: } rlm@0: rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alSource3f(ALuint source, ALenum eParam, ALfloat flValue1,ALfloat flValue2,ALfloat flValue3) rlm@0: { rlm@0: ALCcontext *pContext; rlm@0: ALsource *Source; rlm@0: rlm@0: pContext = GetLockedContext(); rlm@0: if(!pContext) return; rlm@0: rlm@0: if((Source=LookupSource(pContext->SourceMap, source)) != NULL) rlm@0: { rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_POSITION: rlm@0: if(isfinite(flValue1) && isfinite(flValue2) && isfinite(flValue3)) rlm@0: { rlm@0: Source->vPosition[0] = flValue1; rlm@0: Source->vPosition[1] = flValue2; rlm@0: Source->vPosition[2] = flValue3; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_VELOCITY: rlm@0: if(isfinite(flValue1) && isfinite(flValue2) && isfinite(flValue3)) rlm@0: { rlm@0: Source->vVelocity[0] = flValue1; rlm@0: Source->vVelocity[1] = flValue2; rlm@0: Source->vVelocity[2] = flValue3; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_DIRECTION: rlm@0: if(isfinite(flValue1) && isfinite(flValue2) && isfinite(flValue3)) rlm@0: { rlm@0: Source->vOrientation[0] = flValue1; rlm@0: Source->vOrientation[1] = flValue2; rlm@0: Source->vOrientation[2] = flValue3; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: default: rlm@0: alSetError(pContext, AL_INVALID_ENUM); rlm@0: break; rlm@0: } rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_NAME); rlm@0: rlm@0: UnlockContext(pContext); rlm@0: } rlm@0: rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alSourcefv(ALuint source, ALenum eParam, const ALfloat *pflValues) rlm@0: { rlm@0: ALCcontext *pContext; rlm@0: rlm@0: if(pflValues) rlm@0: { rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_PITCH: rlm@0: case AL_CONE_INNER_ANGLE: rlm@0: case AL_CONE_OUTER_ANGLE: rlm@0: case AL_GAIN: rlm@0: case AL_MAX_DISTANCE: rlm@0: case AL_ROLLOFF_FACTOR: rlm@0: case AL_REFERENCE_DISTANCE: rlm@0: case AL_MIN_GAIN: rlm@0: case AL_MAX_GAIN: rlm@0: case AL_CONE_OUTER_GAIN: rlm@0: case AL_CONE_OUTER_GAINHF: rlm@0: case AL_SEC_OFFSET: rlm@0: case AL_SAMPLE_OFFSET: rlm@0: case AL_BYTE_OFFSET: rlm@0: case AL_AIR_ABSORPTION_FACTOR: rlm@0: case AL_ROOM_ROLLOFF_FACTOR: rlm@0: alSourcef(source, eParam, pflValues[0]); rlm@0: return; rlm@0: rlm@0: case AL_POSITION: rlm@0: case AL_VELOCITY: rlm@0: case AL_DIRECTION: rlm@0: alSource3f(source, eParam, pflValues[0], pflValues[1], pflValues[2]); rlm@0: return; rlm@0: } rlm@0: } rlm@0: rlm@0: pContext = GetLockedContext(); rlm@0: if(!pContext) return; rlm@0: rlm@0: if(pflValues) rlm@0: { rlm@0: if(LookupSource(pContext->SourceMap, source) != NULL) rlm@0: { rlm@0: switch(eParam) rlm@0: { rlm@0: default: rlm@0: alSetError(pContext, AL_INVALID_ENUM); rlm@0: break; rlm@0: } rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_NAME); rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: rlm@0: UnlockContext(pContext); rlm@0: } rlm@0: rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alSourcei(ALuint source,ALenum eParam,ALint lValue) rlm@0: { rlm@0: ALCcontext *pContext; rlm@0: ALsource *Source; rlm@0: ALbufferlistitem *BufferListItem; rlm@0: rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_MAX_DISTANCE: rlm@0: case AL_ROLLOFF_FACTOR: rlm@0: case AL_CONE_INNER_ANGLE: rlm@0: case AL_CONE_OUTER_ANGLE: rlm@0: case AL_REFERENCE_DISTANCE: rlm@0: alSourcef(source, eParam, (ALfloat)lValue); rlm@0: return; rlm@0: } rlm@0: rlm@0: pContext = GetLockedContext(); rlm@0: if(!pContext) return; rlm@0: rlm@0: if((Source=LookupSource(pContext->SourceMap, source)) != NULL) rlm@0: { rlm@0: ALCdevice *device = pContext->Device; rlm@0: rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_SOURCE_RELATIVE: rlm@0: if(lValue == AL_FALSE || lValue == AL_TRUE) rlm@0: { rlm@0: Source->bHeadRelative = (ALboolean)lValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_LOOPING: rlm@0: if(lValue == AL_FALSE || lValue == AL_TRUE) rlm@0: Source->bLooping = (ALboolean)lValue; rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_BUFFER: rlm@0: if(Source->state == AL_STOPPED || Source->state == AL_INITIAL) rlm@0: { rlm@0: ALbuffer *buffer = NULL; rlm@0: rlm@0: if(lValue == 0 || rlm@0: (buffer=LookupBuffer(device->BufferMap, lValue)) != NULL) rlm@0: { rlm@0: // Remove all elements in the queue rlm@0: while(Source->queue != NULL) rlm@0: { rlm@0: BufferListItem = Source->queue; rlm@0: Source->queue = BufferListItem->next; rlm@0: rlm@0: if(BufferListItem->buffer) rlm@0: BufferListItem->buffer->refcount--; rlm@0: free(BufferListItem); rlm@0: } rlm@0: Source->BuffersInQueue = 0; rlm@0: rlm@0: // Add the buffer to the queue (as long as it is NOT the NULL buffer) rlm@0: if(buffer != NULL) rlm@0: { rlm@0: // Source is now in STATIC mode rlm@0: Source->lSourceType = AL_STATIC; rlm@0: rlm@0: // Add the selected buffer to the queue rlm@0: BufferListItem = malloc(sizeof(ALbufferlistitem)); rlm@0: BufferListItem->buffer = buffer; rlm@0: BufferListItem->next = NULL; rlm@0: BufferListItem->prev = NULL; rlm@0: rlm@0: Source->queue = BufferListItem; rlm@0: Source->BuffersInQueue = 1; rlm@0: rlm@0: Source->NumChannels = ChannelsFromFmt(buffer->FmtChannels); rlm@0: Source->SampleSize = BytesFromFmt(buffer->FmtType); rlm@0: if(buffer->FmtChannels == FmtMono) rlm@0: Source->Update = CalcSourceParams; rlm@0: else rlm@0: Source->Update = CalcNonAttnSourceParams; rlm@0: rlm@0: // Increment reference counter for buffer rlm@0: buffer->refcount++; rlm@0: } rlm@0: else rlm@0: { rlm@0: // Source is now in UNDETERMINED mode rlm@0: Source->lSourceType = AL_UNDETERMINED; rlm@0: } rlm@0: Source->BuffersPlayed = 0; rlm@0: rlm@0: // Update AL_BUFFER parameter rlm@0: Source->Buffer = buffer; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_OPERATION); rlm@0: break; rlm@0: rlm@0: case AL_SOURCE_STATE: rlm@0: // Query only rlm@0: alSetError(pContext, AL_INVALID_OPERATION); rlm@0: break; rlm@0: rlm@0: case AL_SEC_OFFSET: rlm@0: case AL_SAMPLE_OFFSET: rlm@0: case AL_BYTE_OFFSET: rlm@0: if(lValue >= 0) rlm@0: { rlm@0: Source->lOffsetType = eParam; rlm@0: rlm@0: // Store Offset (convert Seconds into Milliseconds) rlm@0: if(eParam == AL_SEC_OFFSET) rlm@0: Source->lOffset = lValue * 1000; rlm@0: else rlm@0: Source->lOffset = lValue; rlm@0: rlm@0: if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && rlm@0: !pContext->DeferUpdates) rlm@0: { rlm@0: if(ApplyOffset(Source) == AL_FALSE) rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: } rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_DIRECT_FILTER: { rlm@0: ALfilter *filter = NULL; rlm@0: rlm@0: if(lValue == 0 || rlm@0: (filter=LookupFilter(pContext->Device->FilterMap, lValue)) != NULL) rlm@0: { rlm@0: if(!filter) rlm@0: { rlm@0: Source->DirectFilter.type = AL_FILTER_NULL; rlm@0: Source->DirectFilter.filter = 0; rlm@0: } rlm@0: else rlm@0: memcpy(&Source->DirectFilter, filter, sizeof(*filter)); rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: } break; rlm@0: rlm@0: case AL_DIRECT_FILTER_GAINHF_AUTO: rlm@0: if(lValue == AL_TRUE || lValue == AL_FALSE) rlm@0: { rlm@0: Source->DryGainHFAuto = lValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: rlm@0: if(lValue == AL_TRUE || lValue == AL_FALSE) rlm@0: { rlm@0: Source->WetGainAuto = lValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: rlm@0: if(lValue == AL_TRUE || lValue == AL_FALSE) rlm@0: { rlm@0: Source->WetGainHFAuto = lValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_VIRTUAL_CHANNELS_SOFT: rlm@0: if(lValue == AL_TRUE || lValue == AL_FALSE) rlm@0: { rlm@0: Source->VirtualChannels = lValue; rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: case AL_DISTANCE_MODEL: rlm@0: if(lValue == AL_NONE || rlm@0: lValue == AL_INVERSE_DISTANCE || rlm@0: lValue == AL_INVERSE_DISTANCE_CLAMPED || rlm@0: lValue == AL_LINEAR_DISTANCE || rlm@0: lValue == AL_LINEAR_DISTANCE_CLAMPED || rlm@0: lValue == AL_EXPONENT_DISTANCE || rlm@0: lValue == AL_EXPONENT_DISTANCE_CLAMPED) rlm@0: { rlm@0: Source->DistanceModel = lValue; rlm@0: if(pContext->SourceDistanceModel) rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: break; rlm@0: rlm@0: default: rlm@0: alSetError(pContext, AL_INVALID_ENUM); rlm@0: break; rlm@0: } rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_NAME); rlm@0: rlm@0: UnlockContext(pContext); rlm@0: } rlm@0: rlm@0: rlm@0: AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum eParam, ALint lValue1, ALint lValue2, ALint lValue3) rlm@0: { rlm@0: ALCcontext *pContext; rlm@0: ALsource *Source; rlm@0: rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_POSITION: rlm@0: case AL_VELOCITY: rlm@0: case AL_DIRECTION: rlm@0: alSource3f(source, eParam, (ALfloat)lValue1, (ALfloat)lValue2, (ALfloat)lValue3); rlm@0: return; rlm@0: } rlm@0: rlm@0: pContext = GetLockedContext(); rlm@0: if(!pContext) return; rlm@0: rlm@0: if((Source=LookupSource(pContext->SourceMap, source)) != NULL) rlm@0: { rlm@0: ALCdevice *device = pContext->Device; rlm@0: rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_AUXILIARY_SEND_FILTER: { rlm@0: ALeffectslot *ALEffectSlot = NULL; rlm@0: ALfilter *ALFilter = NULL; rlm@0: rlm@0: if((ALuint)lValue2 < device->NumAuxSends && rlm@0: (lValue1 == 0 || rlm@0: (ALEffectSlot=LookupEffectSlot(pContext->EffectSlotMap, lValue1)) != NULL) && rlm@0: (lValue3 == 0 || rlm@0: (ALFilter=LookupFilter(device->FilterMap, lValue3)) != NULL)) rlm@0: { rlm@0: /* Release refcount on the previous slot, and add one for rlm@0: * the new slot */ rlm@0: if(Source->Send[lValue2].Slot) rlm@0: Source->Send[lValue2].Slot->refcount--; rlm@0: Source->Send[lValue2].Slot = ALEffectSlot; rlm@0: if(Source->Send[lValue2].Slot) rlm@0: Source->Send[lValue2].Slot->refcount++; rlm@0: rlm@0: if(!ALFilter) rlm@0: { rlm@0: /* Disable filter */ rlm@0: Source->Send[lValue2].WetFilter.type = 0; rlm@0: Source->Send[lValue2].WetFilter.filter = 0; rlm@0: } rlm@0: else rlm@0: memcpy(&Source->Send[lValue2].WetFilter, ALFilter, sizeof(*ALFilter)); rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: } break; rlm@0: rlm@0: default: rlm@0: alSetError(pContext, AL_INVALID_ENUM); rlm@0: break; rlm@0: } rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_NAME); rlm@0: rlm@0: UnlockContext(pContext); rlm@0: } rlm@0: rlm@0: rlm@0: AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum eParam, const ALint* plValues) rlm@0: { rlm@0: ALCcontext *pContext; rlm@0: rlm@0: if(plValues) rlm@0: { rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_SOURCE_RELATIVE: rlm@0: case AL_CONE_INNER_ANGLE: rlm@0: case AL_CONE_OUTER_ANGLE: rlm@0: case AL_LOOPING: rlm@0: case AL_BUFFER: rlm@0: case AL_SOURCE_STATE: rlm@0: case AL_SEC_OFFSET: rlm@0: case AL_SAMPLE_OFFSET: rlm@0: case AL_BYTE_OFFSET: rlm@0: case AL_MAX_DISTANCE: rlm@0: case AL_ROLLOFF_FACTOR: rlm@0: case AL_REFERENCE_DISTANCE: rlm@0: case AL_DIRECT_FILTER: rlm@0: case AL_DIRECT_FILTER_GAINHF_AUTO: rlm@0: case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: rlm@0: case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: rlm@0: case AL_DISTANCE_MODEL: rlm@0: case AL_VIRTUAL_CHANNELS_SOFT: rlm@0: alSourcei(source, eParam, plValues[0]); rlm@0: return; rlm@0: rlm@0: case AL_POSITION: rlm@0: case AL_VELOCITY: rlm@0: case AL_DIRECTION: rlm@0: case AL_AUXILIARY_SEND_FILTER: rlm@0: alSource3i(source, eParam, plValues[0], plValues[1], plValues[2]); rlm@0: return; rlm@0: } rlm@0: } rlm@0: rlm@0: pContext = GetLockedContext(); rlm@0: if(!pContext) return; rlm@0: rlm@0: if(plValues) rlm@0: { rlm@0: if(LookupSource(pContext->SourceMap, source) != NULL) rlm@0: { rlm@0: switch(eParam) rlm@0: { rlm@0: default: rlm@0: alSetError(pContext, AL_INVALID_ENUM); rlm@0: break; rlm@0: } rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_NAME); rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: rlm@0: UnlockContext(pContext); rlm@0: } rlm@0: rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alGetSourcef(ALuint source, ALenum eParam, ALfloat *pflValue) rlm@0: { rlm@0: ALCcontext *pContext; rlm@0: ALsource *Source; rlm@0: ALdouble Offsets[2]; rlm@0: ALdouble updateLen; rlm@0: rlm@0: pContext = GetLockedContext(); rlm@0: if(!pContext) return; rlm@0: rlm@0: if(pflValue) rlm@0: { rlm@0: if((Source=LookupSource(pContext->SourceMap, source)) != NULL) rlm@0: { rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_PITCH: rlm@0: *pflValue = Source->flPitch; rlm@0: break; rlm@0: rlm@0: case AL_GAIN: rlm@0: *pflValue = Source->flGain; rlm@0: break; rlm@0: rlm@0: case AL_MIN_GAIN: rlm@0: *pflValue = Source->flMinGain; rlm@0: break; rlm@0: rlm@0: case AL_MAX_GAIN: rlm@0: *pflValue = Source->flMaxGain; rlm@0: break; rlm@0: rlm@0: case AL_MAX_DISTANCE: rlm@0: *pflValue = Source->flMaxDistance; rlm@0: break; rlm@0: rlm@0: case AL_ROLLOFF_FACTOR: rlm@0: *pflValue = Source->flRollOffFactor; rlm@0: break; rlm@0: rlm@0: case AL_CONE_OUTER_GAIN: rlm@0: *pflValue = Source->flOuterGain; rlm@0: break; rlm@0: rlm@0: case AL_CONE_OUTER_GAINHF: rlm@0: *pflValue = Source->OuterGainHF; rlm@0: break; rlm@0: rlm@0: case AL_SEC_OFFSET: rlm@0: case AL_SAMPLE_OFFSET: rlm@0: case AL_BYTE_OFFSET: rlm@0: updateLen = (ALdouble)pContext->Device->UpdateSize / rlm@0: pContext->Device->Frequency; rlm@0: GetSourceOffset(Source, eParam, Offsets, updateLen); rlm@0: *pflValue = Offsets[0]; rlm@0: break; rlm@0: rlm@0: case AL_CONE_INNER_ANGLE: rlm@0: *pflValue = Source->flInnerAngle; rlm@0: break; rlm@0: rlm@0: case AL_CONE_OUTER_ANGLE: rlm@0: *pflValue = Source->flOuterAngle; rlm@0: break; rlm@0: rlm@0: case AL_REFERENCE_DISTANCE: rlm@0: *pflValue = Source->flRefDistance; rlm@0: break; rlm@0: rlm@0: case AL_AIR_ABSORPTION_FACTOR: rlm@0: *pflValue = Source->AirAbsorptionFactor; rlm@0: break; rlm@0: rlm@0: case AL_ROOM_ROLLOFF_FACTOR: rlm@0: *pflValue = Source->RoomRolloffFactor; rlm@0: break; rlm@0: rlm@0: case AL_DOPPLER_FACTOR: rlm@0: *pflValue = Source->DopplerFactor; rlm@0: break; rlm@0: rlm@0: default: rlm@0: alSetError(pContext, AL_INVALID_ENUM); rlm@0: break; rlm@0: } rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_NAME); rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: rlm@0: UnlockContext(pContext); rlm@0: } rlm@0: rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alGetSource3f(ALuint source, ALenum eParam, ALfloat* pflValue1, ALfloat* pflValue2, ALfloat* pflValue3) rlm@0: { rlm@0: ALCcontext *pContext; rlm@0: ALsource *Source; rlm@0: rlm@0: pContext = GetLockedContext(); rlm@0: if(!pContext) return; rlm@0: rlm@0: if(pflValue1 && pflValue2 && pflValue3) rlm@0: { rlm@0: if((Source=LookupSource(pContext->SourceMap, source)) != NULL) rlm@0: { rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_POSITION: rlm@0: *pflValue1 = Source->vPosition[0]; rlm@0: *pflValue2 = Source->vPosition[1]; rlm@0: *pflValue3 = Source->vPosition[2]; rlm@0: break; rlm@0: rlm@0: case AL_VELOCITY: rlm@0: *pflValue1 = Source->vVelocity[0]; rlm@0: *pflValue2 = Source->vVelocity[1]; rlm@0: *pflValue3 = Source->vVelocity[2]; rlm@0: break; rlm@0: rlm@0: case AL_DIRECTION: rlm@0: *pflValue1 = Source->vOrientation[0]; rlm@0: *pflValue2 = Source->vOrientation[1]; rlm@0: *pflValue3 = Source->vOrientation[2]; rlm@0: break; rlm@0: rlm@0: default: rlm@0: alSetError(pContext, AL_INVALID_ENUM); rlm@0: break; rlm@0: } rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_NAME); rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: rlm@0: UnlockContext(pContext); rlm@0: } rlm@0: rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alGetSourcefv(ALuint source, ALenum eParam, ALfloat *pflValues) rlm@0: { rlm@0: ALCcontext *pContext; rlm@0: ALsource *Source; rlm@0: ALdouble Offsets[2]; rlm@0: ALdouble updateLen; rlm@0: rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_PITCH: rlm@0: case AL_GAIN: rlm@0: case AL_MIN_GAIN: rlm@0: case AL_MAX_GAIN: rlm@0: case AL_MAX_DISTANCE: rlm@0: case AL_ROLLOFF_FACTOR: rlm@0: case AL_DOPPLER_FACTOR: rlm@0: case AL_CONE_OUTER_GAIN: rlm@0: case AL_SEC_OFFSET: rlm@0: case AL_SAMPLE_OFFSET: rlm@0: case AL_BYTE_OFFSET: rlm@0: case AL_CONE_INNER_ANGLE: rlm@0: case AL_CONE_OUTER_ANGLE: rlm@0: case AL_REFERENCE_DISTANCE: rlm@0: case AL_CONE_OUTER_GAINHF: rlm@0: case AL_AIR_ABSORPTION_FACTOR: rlm@0: case AL_ROOM_ROLLOFF_FACTOR: rlm@0: alGetSourcef(source, eParam, pflValues); rlm@0: return; rlm@0: rlm@0: case AL_POSITION: rlm@0: case AL_VELOCITY: rlm@0: case AL_DIRECTION: rlm@0: alGetSource3f(source, eParam, pflValues+0, pflValues+1, pflValues+2); rlm@0: return; rlm@0: } rlm@0: rlm@0: pContext = GetLockedContext(); rlm@0: if(!pContext) return; rlm@0: rlm@0: if(pflValues) rlm@0: { rlm@0: if((Source=LookupSource(pContext->SourceMap, source)) != NULL) rlm@0: { rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_SAMPLE_RW_OFFSETS_SOFT: rlm@0: case AL_BYTE_RW_OFFSETS_SOFT: rlm@0: updateLen = (ALdouble)pContext->Device->UpdateSize / rlm@0: pContext->Device->Frequency; rlm@0: GetSourceOffset(Source, eParam, Offsets, updateLen); rlm@0: pflValues[0] = Offsets[0]; rlm@0: pflValues[1] = Offsets[1]; rlm@0: break; rlm@0: rlm@0: default: rlm@0: alSetError(pContext, AL_INVALID_ENUM); rlm@0: break; rlm@0: } rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_NAME); rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: rlm@0: UnlockContext(pContext); rlm@0: } rlm@0: rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alGetSourcei(ALuint source, ALenum eParam, ALint *plValue) rlm@0: { rlm@0: ALCcontext *pContext; rlm@0: ALsource *Source; rlm@0: ALdouble Offsets[2]; rlm@0: ALdouble updateLen; rlm@0: rlm@0: pContext = GetLockedContext(); rlm@0: if(!pContext) return; rlm@0: rlm@0: if(plValue) rlm@0: { rlm@0: if((Source=LookupSource(pContext->SourceMap, source)) != NULL) rlm@0: { rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_MAX_DISTANCE: rlm@0: *plValue = (ALint)Source->flMaxDistance; rlm@0: break; rlm@0: rlm@0: case AL_ROLLOFF_FACTOR: rlm@0: *plValue = (ALint)Source->flRollOffFactor; rlm@0: break; rlm@0: rlm@0: case AL_REFERENCE_DISTANCE: rlm@0: *plValue = (ALint)Source->flRefDistance; rlm@0: break; rlm@0: rlm@0: case AL_SOURCE_RELATIVE: rlm@0: *plValue = Source->bHeadRelative; rlm@0: break; rlm@0: rlm@0: case AL_CONE_INNER_ANGLE: rlm@0: *plValue = (ALint)Source->flInnerAngle; rlm@0: break; rlm@0: rlm@0: case AL_CONE_OUTER_ANGLE: rlm@0: *plValue = (ALint)Source->flOuterAngle; rlm@0: break; rlm@0: rlm@0: case AL_LOOPING: rlm@0: *plValue = Source->bLooping; rlm@0: break; rlm@0: rlm@0: case AL_BUFFER: rlm@0: *plValue = (Source->Buffer ? Source->Buffer->buffer : 0); rlm@0: break; rlm@0: rlm@0: case AL_SOURCE_STATE: rlm@0: *plValue = Source->state; rlm@0: break; rlm@0: rlm@0: case AL_BUFFERS_QUEUED: rlm@0: *plValue = Source->BuffersInQueue; rlm@0: break; rlm@0: rlm@0: case AL_BUFFERS_PROCESSED: rlm@0: if(Source->bLooping || Source->lSourceType != AL_STREAMING) rlm@0: { rlm@0: /* Buffers on a looping source are in a perpetual state rlm@0: * of PENDING, so don't report any as PROCESSED */ rlm@0: *plValue = 0; rlm@0: } rlm@0: else rlm@0: *plValue = Source->BuffersPlayed; rlm@0: break; rlm@0: rlm@0: case AL_SOURCE_TYPE: rlm@0: *plValue = Source->lSourceType; rlm@0: break; rlm@0: rlm@0: case AL_SEC_OFFSET: rlm@0: case AL_SAMPLE_OFFSET: rlm@0: case AL_BYTE_OFFSET: rlm@0: updateLen = (ALdouble)pContext->Device->UpdateSize / rlm@0: pContext->Device->Frequency; rlm@0: GetSourceOffset(Source, eParam, Offsets, updateLen); rlm@0: *plValue = (ALint)Offsets[0]; rlm@0: break; rlm@0: rlm@0: case AL_DIRECT_FILTER: rlm@0: *plValue = Source->DirectFilter.filter; rlm@0: break; rlm@0: rlm@0: case AL_DIRECT_FILTER_GAINHF_AUTO: rlm@0: *plValue = Source->DryGainHFAuto; rlm@0: break; rlm@0: rlm@0: case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: rlm@0: *plValue = Source->WetGainAuto; rlm@0: break; rlm@0: rlm@0: case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: rlm@0: *plValue = Source->WetGainHFAuto; rlm@0: break; rlm@0: rlm@0: case AL_DOPPLER_FACTOR: rlm@0: *plValue = (ALint)Source->DopplerFactor; rlm@0: break; rlm@0: rlm@0: case AL_VIRTUAL_CHANNELS_SOFT: rlm@0: *plValue = Source->VirtualChannels; rlm@0: break; rlm@0: rlm@0: case AL_DISTANCE_MODEL: rlm@0: *plValue = Source->DistanceModel; rlm@0: break; rlm@0: rlm@0: default: rlm@0: alSetError(pContext, AL_INVALID_ENUM); rlm@0: break; rlm@0: } rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_NAME); rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: rlm@0: UnlockContext(pContext); rlm@0: } rlm@0: rlm@0: rlm@0: AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum eParam, ALint* plValue1, ALint* plValue2, ALint* plValue3) rlm@0: { rlm@0: ALCcontext *pContext; rlm@0: ALsource *Source; rlm@0: rlm@0: pContext = GetLockedContext(); rlm@0: if(!pContext) return; rlm@0: rlm@0: if(plValue1 && plValue2 && plValue3) rlm@0: { rlm@0: if((Source=LookupSource(pContext->SourceMap, source)) != NULL) rlm@0: { rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_POSITION: rlm@0: *plValue1 = (ALint)Source->vPosition[0]; rlm@0: *plValue2 = (ALint)Source->vPosition[1]; rlm@0: *plValue3 = (ALint)Source->vPosition[2]; rlm@0: break; rlm@0: rlm@0: case AL_VELOCITY: rlm@0: *plValue1 = (ALint)Source->vVelocity[0]; rlm@0: *plValue2 = (ALint)Source->vVelocity[1]; rlm@0: *plValue3 = (ALint)Source->vVelocity[2]; rlm@0: break; rlm@0: rlm@0: case AL_DIRECTION: rlm@0: *plValue1 = (ALint)Source->vOrientation[0]; rlm@0: *plValue2 = (ALint)Source->vOrientation[1]; rlm@0: *plValue3 = (ALint)Source->vOrientation[2]; rlm@0: break; rlm@0: rlm@0: default: rlm@0: alSetError(pContext, AL_INVALID_ENUM); rlm@0: break; rlm@0: } rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_NAME); rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: rlm@0: UnlockContext(pContext); rlm@0: } rlm@0: rlm@0: rlm@0: AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum eParam, ALint* plValues) rlm@0: { rlm@0: ALCcontext *pContext; rlm@0: ALsource *Source; rlm@0: ALdouble Offsets[2]; rlm@0: ALdouble updateLen; rlm@0: rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_SOURCE_RELATIVE: rlm@0: case AL_CONE_INNER_ANGLE: rlm@0: case AL_CONE_OUTER_ANGLE: rlm@0: case AL_LOOPING: rlm@0: case AL_BUFFER: rlm@0: case AL_SOURCE_STATE: rlm@0: case AL_BUFFERS_QUEUED: rlm@0: case AL_BUFFERS_PROCESSED: rlm@0: case AL_SEC_OFFSET: rlm@0: case AL_SAMPLE_OFFSET: rlm@0: case AL_BYTE_OFFSET: rlm@0: case AL_MAX_DISTANCE: rlm@0: case AL_ROLLOFF_FACTOR: rlm@0: case AL_DOPPLER_FACTOR: rlm@0: case AL_REFERENCE_DISTANCE: rlm@0: case AL_SOURCE_TYPE: rlm@0: case AL_DIRECT_FILTER: rlm@0: case AL_DIRECT_FILTER_GAINHF_AUTO: rlm@0: case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: rlm@0: case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: rlm@0: case AL_DISTANCE_MODEL: rlm@0: case AL_VIRTUAL_CHANNELS_SOFT: rlm@0: alGetSourcei(source, eParam, plValues); rlm@0: return; rlm@0: rlm@0: case AL_POSITION: rlm@0: case AL_VELOCITY: rlm@0: case AL_DIRECTION: rlm@0: alGetSource3i(source, eParam, plValues+0, plValues+1, plValues+2); rlm@0: return; rlm@0: } rlm@0: rlm@0: pContext = GetLockedContext(); rlm@0: if(!pContext) return; rlm@0: rlm@0: if(plValues) rlm@0: { rlm@0: if((Source=LookupSource(pContext->SourceMap, source)) != NULL) rlm@0: { rlm@0: switch(eParam) rlm@0: { rlm@0: case AL_SAMPLE_RW_OFFSETS_SOFT: rlm@0: case AL_BYTE_RW_OFFSETS_SOFT: rlm@0: updateLen = (ALdouble)pContext->Device->UpdateSize / rlm@0: pContext->Device->Frequency; rlm@0: GetSourceOffset(Source, eParam, Offsets, updateLen); rlm@0: plValues[0] = (ALint)Offsets[0]; rlm@0: plValues[1] = (ALint)Offsets[1]; rlm@0: break; rlm@0: rlm@0: default: rlm@0: alSetError(pContext, AL_INVALID_ENUM); rlm@0: break; rlm@0: } rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_NAME); rlm@0: } rlm@0: else rlm@0: alSetError(pContext, AL_INVALID_VALUE); rlm@0: rlm@0: UnlockContext(pContext); rlm@0: } rlm@0: rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alSourcePlay(ALuint source) rlm@0: { rlm@0: alSourcePlayv(1, &source); rlm@0: } rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) rlm@0: { rlm@0: ALCcontext *Context; rlm@0: ALsource *Source; rlm@0: ALsizei i; rlm@0: rlm@0: Context = GetLockedContext(); rlm@0: if(!Context) return; rlm@0: rlm@0: if(n < 0) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_VALUE); rlm@0: goto done; rlm@0: } rlm@0: if(n > 0 && !sources) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_VALUE); rlm@0: goto done; rlm@0: } rlm@0: rlm@0: // Check that all the Sources are valid rlm@0: for(i = 0;i < n;i++) rlm@0: { rlm@0: if(!LookupSource(Context->SourceMap, sources[i])) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_NAME); rlm@0: goto done; rlm@0: } rlm@0: } rlm@0: rlm@0: while(Context->MaxActiveSources-Context->ActiveSourceCount < n) rlm@0: { rlm@0: void *temp = NULL; rlm@0: ALsizei newcount; rlm@0: rlm@0: newcount = Context->MaxActiveSources << 1; rlm@0: if(newcount > 0) rlm@0: temp = realloc(Context->ActiveSources, rlm@0: sizeof(*Context->ActiveSources) * newcount); rlm@0: if(!temp) rlm@0: { rlm@0: alSetError(Context, AL_OUT_OF_MEMORY); rlm@0: goto done; rlm@0: } rlm@0: rlm@0: Context->ActiveSources = temp; rlm@0: Context->MaxActiveSources = newcount; rlm@0: } rlm@0: rlm@0: for(i = 0;i < n;i++) rlm@0: { rlm@0: Source = LookupSource(Context->SourceMap, sources[i]); rlm@0: if(Context->DeferUpdates) Source->new_state = AL_PLAYING; rlm@0: else SetSourceState(Source, Context, AL_PLAYING); rlm@0: } rlm@0: rlm@0: done: rlm@0: UnlockContext(Context); rlm@0: } rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alSourcePause(ALuint source) rlm@0: { rlm@0: alSourcePausev(1, &source); rlm@0: } rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources) rlm@0: { rlm@0: ALCcontext *Context; rlm@0: ALsource *Source; rlm@0: ALsizei i; rlm@0: rlm@0: Context = GetLockedContext(); rlm@0: if(!Context) return; rlm@0: rlm@0: if(n < 0) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_VALUE); rlm@0: goto done; rlm@0: } rlm@0: if(n > 0 && !sources) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_VALUE); rlm@0: goto done; rlm@0: } rlm@0: rlm@0: // Check all the Sources are valid rlm@0: for(i = 0;i < n;i++) rlm@0: { rlm@0: if(!LookupSource(Context->SourceMap, sources[i])) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_NAME); rlm@0: goto done; rlm@0: } rlm@0: } rlm@0: rlm@0: for(i = 0;i < n;i++) rlm@0: { rlm@0: Source = LookupSource(Context->SourceMap, sources[i]); rlm@0: if(Context->DeferUpdates) Source->new_state = AL_PAUSED; rlm@0: else SetSourceState(Source, Context, AL_PAUSED); rlm@0: } rlm@0: rlm@0: done: rlm@0: UnlockContext(Context); rlm@0: } rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alSourceStop(ALuint source) rlm@0: { rlm@0: alSourceStopv(1, &source); rlm@0: } rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources) rlm@0: { rlm@0: ALCcontext *Context; rlm@0: ALsource *Source; rlm@0: ALsizei i; rlm@0: rlm@0: Context = GetLockedContext(); rlm@0: if(!Context) return; rlm@0: rlm@0: if(n < 0) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_VALUE); rlm@0: goto done; rlm@0: } rlm@0: if(n > 0 && !sources) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_VALUE); rlm@0: goto done; rlm@0: } rlm@0: rlm@0: // Check all the Sources are valid rlm@0: for(i = 0;i < n;i++) rlm@0: { rlm@0: if(!LookupSource(Context->SourceMap, sources[i])) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_NAME); rlm@0: goto done; rlm@0: } rlm@0: } rlm@0: rlm@0: for(i = 0;i < n;i++) rlm@0: { rlm@0: Source = LookupSource(Context->SourceMap, sources[i]); rlm@0: if(Context->DeferUpdates) Source->new_state = AL_STOPPED; rlm@0: else SetSourceState(Source, Context, AL_STOPPED); rlm@0: } rlm@0: rlm@0: done: rlm@0: UnlockContext(Context); rlm@0: } rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alSourceRewind(ALuint source) rlm@0: { rlm@0: alSourceRewindv(1, &source); rlm@0: } rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources) rlm@0: { rlm@0: ALCcontext *Context; rlm@0: ALsource *Source; rlm@0: ALsizei i; rlm@0: rlm@0: Context = GetLockedContext(); rlm@0: if(!Context) return; rlm@0: rlm@0: if(n < 0) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_VALUE); rlm@0: goto done; rlm@0: } rlm@0: if(n > 0 && !sources) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_VALUE); rlm@0: goto done; rlm@0: } rlm@0: rlm@0: // Check all the Sources are valid rlm@0: for(i = 0;i < n;i++) rlm@0: { rlm@0: if(!LookupSource(Context->SourceMap, sources[i])) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_NAME); rlm@0: goto done; rlm@0: } rlm@0: } rlm@0: rlm@0: for(i = 0;i < n;i++) rlm@0: { rlm@0: Source = LookupSource(Context->SourceMap, sources[i]); rlm@0: if(Context->DeferUpdates) Source->new_state = AL_INITIAL; rlm@0: else SetSourceState(Source, Context, AL_INITIAL); rlm@0: } rlm@0: rlm@0: done: rlm@0: UnlockContext(Context); rlm@0: } rlm@0: rlm@0: rlm@0: AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei n, const ALuint *buffers) rlm@0: { rlm@0: ALCcontext *Context; rlm@0: ALCdevice *device; rlm@0: ALsource *Source; rlm@0: ALbuffer *buffer; rlm@0: ALsizei i; rlm@0: ALbufferlistitem *BufferListStart; rlm@0: ALbufferlistitem *BufferList; rlm@0: ALbuffer *BufferFmt; rlm@0: rlm@0: if(n == 0) rlm@0: return; rlm@0: rlm@0: Context = GetLockedContext(); rlm@0: if(!Context) return; rlm@0: rlm@0: if(n < 0) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_VALUE); rlm@0: goto done; rlm@0: } rlm@0: rlm@0: // Check that all buffers are valid or zero and that the source is valid rlm@0: rlm@0: // Check that this is a valid source rlm@0: if((Source=LookupSource(Context->SourceMap, source)) == NULL) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_NAME); rlm@0: goto done; rlm@0: } rlm@0: rlm@0: // Check that this is not a STATIC Source rlm@0: if(Source->lSourceType == AL_STATIC) rlm@0: { rlm@0: // Invalid Source Type (can't queue on a Static Source) rlm@0: alSetError(Context, AL_INVALID_OPERATION); rlm@0: goto done; rlm@0: } rlm@0: rlm@0: device = Context->Device; rlm@0: rlm@0: BufferFmt = NULL; rlm@0: rlm@0: // Check existing Queue (if any) for a valid Buffers and get its frequency and format rlm@0: BufferList = Source->queue; rlm@0: while(BufferList) rlm@0: { rlm@0: if(BufferList->buffer) rlm@0: { rlm@0: BufferFmt = BufferList->buffer; rlm@0: break; rlm@0: } rlm@0: BufferList = BufferList->next; rlm@0: } rlm@0: rlm@0: for(i = 0;i < n;i++) rlm@0: { rlm@0: if(!buffers[i]) rlm@0: continue; rlm@0: rlm@0: if((buffer=LookupBuffer(device->BufferMap, buffers[i])) == NULL) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_NAME); rlm@0: goto done; rlm@0: } rlm@0: rlm@0: if(BufferFmt == NULL) rlm@0: { rlm@0: BufferFmt = buffer; rlm@0: rlm@0: Source->NumChannels = ChannelsFromFmt(buffer->FmtChannels); rlm@0: Source->SampleSize = BytesFromFmt(buffer->FmtType); rlm@0: if(buffer->FmtChannels == FmtMono) rlm@0: Source->Update = CalcSourceParams; rlm@0: else rlm@0: Source->Update = CalcNonAttnSourceParams; rlm@0: rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: } rlm@0: else if(BufferFmt->Frequency != buffer->Frequency || rlm@0: BufferFmt->OriginalChannels != buffer->OriginalChannels || rlm@0: BufferFmt->OriginalType != buffer->OriginalType) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_OPERATION); rlm@0: goto done; rlm@0: } rlm@0: } rlm@0: rlm@0: // Change Source Type rlm@0: Source->lSourceType = AL_STREAMING; rlm@0: rlm@0: buffer = LookupBuffer(device->BufferMap, buffers[0]); rlm@0: rlm@0: // All buffers are valid - so add them to the list rlm@0: BufferListStart = malloc(sizeof(ALbufferlistitem)); rlm@0: BufferListStart->buffer = buffer; rlm@0: BufferListStart->next = NULL; rlm@0: BufferListStart->prev = NULL; rlm@0: rlm@0: // Increment reference counter for buffer rlm@0: if(buffer) buffer->refcount++; rlm@0: rlm@0: BufferList = BufferListStart; rlm@0: rlm@0: for(i = 1;i < n;i++) rlm@0: { rlm@0: buffer = LookupBuffer(device->BufferMap, buffers[i]); rlm@0: rlm@0: BufferList->next = malloc(sizeof(ALbufferlistitem)); rlm@0: BufferList->next->buffer = buffer; rlm@0: BufferList->next->next = NULL; rlm@0: BufferList->next->prev = BufferList; rlm@0: rlm@0: // Increment reference counter for buffer rlm@0: if(buffer) buffer->refcount++; rlm@0: rlm@0: BufferList = BufferList->next; rlm@0: } rlm@0: rlm@0: if(Source->queue == NULL) rlm@0: { rlm@0: Source->queue = BufferListStart; rlm@0: // Update Current Buffer rlm@0: Source->Buffer = BufferListStart->buffer; rlm@0: } rlm@0: else rlm@0: { rlm@0: // Find end of queue rlm@0: BufferList = Source->queue; rlm@0: while(BufferList->next != NULL) rlm@0: BufferList = BufferList->next; rlm@0: rlm@0: BufferList->next = BufferListStart; rlm@0: BufferList->next->prev = BufferList; rlm@0: } rlm@0: rlm@0: // Update number of buffers in queue rlm@0: Source->BuffersInQueue += n; rlm@0: rlm@0: done: rlm@0: UnlockContext(Context); rlm@0: } rlm@0: rlm@0: rlm@0: // Implementation assumes that n is the number of buffers to be removed from the queue and buffers is rlm@0: // an array of buffer IDs that are to be filled with the names of the buffers removed rlm@0: AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers( ALuint source, ALsizei n, ALuint* buffers ) rlm@0: { rlm@0: ALCcontext *Context; rlm@0: ALsource *Source; rlm@0: ALsizei i; rlm@0: ALbufferlistitem *BufferList; rlm@0: rlm@0: if(n == 0) rlm@0: return; rlm@0: rlm@0: Context = GetLockedContext(); rlm@0: if(!Context) return; rlm@0: rlm@0: if(n < 0) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_VALUE); rlm@0: goto done; rlm@0: } rlm@0: rlm@0: if((Source=LookupSource(Context->SourceMap, source)) == NULL) rlm@0: { rlm@0: alSetError(Context, AL_INVALID_NAME); rlm@0: goto done; rlm@0: } rlm@0: rlm@0: if(Source->bLooping || Source->lSourceType != AL_STREAMING || rlm@0: (ALuint)n > Source->BuffersPlayed) rlm@0: { rlm@0: // Some buffers can't be unqueue because they have not been processed rlm@0: alSetError(Context, AL_INVALID_VALUE); rlm@0: goto done; rlm@0: } rlm@0: rlm@0: for(i = 0;i < n;i++) rlm@0: { rlm@0: BufferList = Source->queue; rlm@0: Source->queue = BufferList->next; rlm@0: rlm@0: if(BufferList->buffer) rlm@0: { rlm@0: // Record name of buffer rlm@0: buffers[i] = BufferList->buffer->buffer; rlm@0: // Decrement buffer reference counter rlm@0: BufferList->buffer->refcount--; rlm@0: } rlm@0: else rlm@0: buffers[i] = 0; rlm@0: rlm@0: // Release memory for buffer list item rlm@0: free(BufferList); rlm@0: Source->BuffersInQueue--; rlm@0: } rlm@0: if(Source->queue) rlm@0: Source->queue->prev = NULL; rlm@0: rlm@0: if(Source->state != AL_PLAYING) rlm@0: { rlm@0: if(Source->queue) rlm@0: Source->Buffer = Source->queue->buffer; rlm@0: else rlm@0: Source->Buffer = NULL; rlm@0: } rlm@0: Source->BuffersPlayed -= n; rlm@0: rlm@0: done: rlm@0: UnlockContext(Context); rlm@0: } rlm@0: rlm@0: rlm@0: static ALvoid InitSourceParams(ALsource *Source) rlm@0: { rlm@0: Source->flInnerAngle = 360.0f; rlm@0: Source->flOuterAngle = 360.0f; rlm@0: Source->flPitch = 1.0f; rlm@0: Source->vPosition[0] = 0.0f; rlm@0: Source->vPosition[1] = 0.0f; rlm@0: Source->vPosition[2] = 0.0f; rlm@0: Source->vOrientation[0] = 0.0f; rlm@0: Source->vOrientation[1] = 0.0f; rlm@0: Source->vOrientation[2] = 0.0f; rlm@0: Source->vVelocity[0] = 0.0f; rlm@0: Source->vVelocity[1] = 0.0f; rlm@0: Source->vVelocity[2] = 0.0f; rlm@0: Source->flRefDistance = 1.0f; rlm@0: Source->flMaxDistance = FLT_MAX; rlm@0: Source->flRollOffFactor = 1.0f; rlm@0: Source->bLooping = AL_FALSE; rlm@0: Source->flGain = 1.0f; rlm@0: Source->flMinGain = 0.0f; rlm@0: Source->flMaxGain = 1.0f; rlm@0: Source->flOuterGain = 0.0f; rlm@0: Source->OuterGainHF = 1.0f; rlm@0: rlm@0: Source->DryGainHFAuto = AL_TRUE; rlm@0: Source->WetGainAuto = AL_TRUE; rlm@0: Source->WetGainHFAuto = AL_TRUE; rlm@0: Source->AirAbsorptionFactor = 0.0f; rlm@0: Source->RoomRolloffFactor = 0.0f; rlm@0: Source->DopplerFactor = 1.0f; rlm@0: Source->VirtualChannels = AL_TRUE; rlm@0: rlm@0: Source->DistanceModel = AL_INVERSE_DISTANCE_CLAMPED; rlm@0: rlm@0: Source->Resampler = DefaultResampler; rlm@0: rlm@0: Source->state = AL_INITIAL; rlm@0: Source->new_state = AL_NONE; rlm@0: Source->lSourceType = AL_UNDETERMINED; rlm@0: Source->lOffset = -1; rlm@0: rlm@0: Source->NeedsUpdate = AL_TRUE; rlm@0: rlm@0: Source->Buffer = NULL; rlm@0: rlm@0: Source->HrtfMoving = AL_FALSE; rlm@0: Source->HrtfCounter = 0; rlm@0: } rlm@0: rlm@0: rlm@0: /* rlm@0: * SetSourceState rlm@0: * rlm@0: * Sets the source's new play state given its current state rlm@0: */ rlm@0: ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state) rlm@0: { rlm@0: if(state == AL_PLAYING) rlm@0: { rlm@0: ALbufferlistitem *BufferList; rlm@0: ALsizei j, k; rlm@0: rlm@0: /* Check that there is a queue containing at least one non-null, non zero length AL Buffer */ rlm@0: BufferList = Source->queue; rlm@0: while(BufferList) rlm@0: { rlm@0: if(BufferList->buffer != NULL && BufferList->buffer->size) rlm@0: break; rlm@0: BufferList = BufferList->next; rlm@0: } rlm@0: rlm@0: /* If there's nothing to play, or device is disconnected, go right to rlm@0: * stopped */ rlm@0: if(!BufferList || !Context->Device->Connected) rlm@0: { rlm@0: SetSourceState(Source, Context, AL_STOPPED); rlm@0: return; rlm@0: } rlm@0: rlm@0: if(Source->state != AL_PLAYING) rlm@0: { rlm@0: for(j = 0;j < MAXCHANNELS;j++) rlm@0: { rlm@0: for(k = 0;k < SRC_HISTORY_LENGTH;k++) rlm@0: Source->HrtfHistory[j][k] = 0.0f; rlm@0: for(k = 0;k < HRIR_LENGTH;k++) rlm@0: { rlm@0: Source->HrtfValues[j][k][0] = 0.0f; rlm@0: Source->HrtfValues[j][k][1] = 0.0f; rlm@0: } rlm@0: } rlm@0: } rlm@0: rlm@0: if(Source->state != AL_PAUSED) rlm@0: { rlm@0: Source->state = AL_PLAYING; rlm@0: Source->position = 0; rlm@0: Source->position_fraction = 0; rlm@0: Source->BuffersPlayed = 0; rlm@0: rlm@0: Source->Buffer = Source->queue->buffer; rlm@0: } rlm@0: else rlm@0: Source->state = AL_PLAYING; rlm@0: rlm@0: // Check if an Offset has been set rlm@0: if(Source->lOffset != -1) rlm@0: ApplyOffset(Source); rlm@0: rlm@0: for(j = 0;j < Context->ActiveSourceCount;j++) rlm@0: { rlm@0: if(Context->ActiveSources[j] == Source) rlm@0: break; rlm@0: } rlm@0: if(j == Context->ActiveSourceCount) rlm@0: Context->ActiveSources[Context->ActiveSourceCount++] = Source; rlm@0: } rlm@0: else if(state == AL_PAUSED) rlm@0: { rlm@0: if(Source->state == AL_PLAYING) rlm@0: { rlm@0: Source->state = AL_PAUSED; rlm@0: Source->HrtfMoving = AL_FALSE; rlm@0: Source->HrtfCounter = 0; rlm@0: } rlm@0: } rlm@0: else if(state == AL_STOPPED) rlm@0: { rlm@0: if(Source->state != AL_INITIAL) rlm@0: { rlm@0: Source->state = AL_STOPPED; rlm@0: Source->BuffersPlayed = Source->BuffersInQueue; rlm@0: Source->HrtfMoving = AL_FALSE; rlm@0: Source->HrtfCounter = 0; rlm@0: } rlm@0: Source->lOffset = -1; rlm@0: } rlm@0: else if(state == AL_INITIAL) rlm@0: { rlm@0: if(Source->state != AL_INITIAL) rlm@0: { rlm@0: Source->state = AL_INITIAL; rlm@0: Source->position = 0; rlm@0: Source->position_fraction = 0; rlm@0: Source->BuffersPlayed = 0; rlm@0: if(Source->queue) rlm@0: Source->Buffer = Source->queue->buffer; rlm@0: Source->HrtfMoving = AL_FALSE; rlm@0: Source->HrtfCounter = 0; rlm@0: } rlm@0: Source->lOffset = -1; rlm@0: } rlm@0: } rlm@0: rlm@0: /* rlm@0: GetSourceOffset rlm@0: rlm@0: Gets the current playback position in the given Source, in the appropriate format (Bytes, Samples or MilliSeconds) rlm@0: The offset is relative to the start of the queue (not the start of the current buffer) rlm@0: */ rlm@0: static ALvoid GetSourceOffset(ALsource *Source, ALenum name, ALdouble *offset, ALdouble updateLen) rlm@0: { rlm@0: const ALbufferlistitem *BufferList; rlm@0: const ALbuffer *Buffer = NULL; rlm@0: enum UserFmtType OriginalType; rlm@0: ALsizei BufferFreq; rlm@0: ALint Channels, Bytes; rlm@0: ALuint readPos, writePos; rlm@0: ALuint TotalBufferDataSize; rlm@0: ALuint i; rlm@0: rlm@0: // Find the first non-NULL Buffer in the Queue rlm@0: BufferList = Source->queue; rlm@0: while(BufferList) rlm@0: { rlm@0: if(BufferList->buffer) rlm@0: { rlm@0: Buffer = BufferList->buffer; rlm@0: break; rlm@0: } rlm@0: BufferList = BufferList->next; rlm@0: } rlm@0: rlm@0: if((Source->state != AL_PLAYING && Source->state != AL_PAUSED) || !Buffer) rlm@0: { rlm@0: offset[0] = 0.0; rlm@0: offset[1] = 0.0; rlm@0: return; rlm@0: } rlm@0: rlm@0: // Get Current Buffer Size and frequency (in milliseconds) rlm@0: BufferFreq = Buffer->Frequency; rlm@0: OriginalType = Buffer->OriginalType; rlm@0: Channels = ChannelsFromFmt(Buffer->FmtChannels); rlm@0: Bytes = BytesFromFmt(Buffer->FmtType); rlm@0: rlm@0: // Get Current BytesPlayed (NOTE : This is the byte offset into the *current* buffer) rlm@0: readPos = Source->position * Channels * Bytes; rlm@0: // Add byte length of any processed buffers in the queue rlm@0: TotalBufferDataSize = 0; rlm@0: BufferList = Source->queue; rlm@0: for(i = 0;BufferList;i++) rlm@0: { rlm@0: if(BufferList->buffer) rlm@0: { rlm@0: if(i < Source->BuffersPlayed) rlm@0: readPos += BufferList->buffer->size; rlm@0: TotalBufferDataSize += BufferList->buffer->size; rlm@0: } rlm@0: BufferList = BufferList->next; rlm@0: } rlm@0: if(Source->state == AL_PLAYING) rlm@0: writePos = readPos + ((ALuint)(updateLen*BufferFreq) * Channels * Bytes); rlm@0: else rlm@0: writePos = readPos; rlm@0: rlm@0: if(Source->bLooping) rlm@0: { rlm@0: readPos %= TotalBufferDataSize; rlm@0: writePos %= TotalBufferDataSize; rlm@0: } rlm@0: else rlm@0: { rlm@0: // Wrap positions back to 0 rlm@0: if(readPos >= TotalBufferDataSize) rlm@0: readPos = 0; rlm@0: if(writePos >= TotalBufferDataSize) rlm@0: writePos = 0; rlm@0: } rlm@0: rlm@0: switch(name) rlm@0: { rlm@0: case AL_SEC_OFFSET: rlm@0: offset[0] = (ALdouble)readPos / (Channels * Bytes * BufferFreq); rlm@0: offset[1] = (ALdouble)writePos / (Channels * Bytes * BufferFreq); rlm@0: break; rlm@0: case AL_SAMPLE_OFFSET: rlm@0: case AL_SAMPLE_RW_OFFSETS_SOFT: rlm@0: offset[0] = (ALdouble)(readPos / (Channels * Bytes)); rlm@0: offset[1] = (ALdouble)(writePos / (Channels * Bytes)); rlm@0: break; rlm@0: case AL_BYTE_OFFSET: rlm@0: case AL_BYTE_RW_OFFSETS_SOFT: rlm@0: // Take into account the original format of the Buffer rlm@0: if(OriginalType == UserFmtIMA4) rlm@0: { rlm@0: ALuint FrameBlockSize = 65 * Bytes * Channels; rlm@0: ALuint BlockSize = 36 * Channels; rlm@0: rlm@0: // Round down to nearest ADPCM block rlm@0: offset[0] = (ALdouble)(readPos / FrameBlockSize * BlockSize); rlm@0: if(Source->state != AL_PLAYING) rlm@0: offset[1] = offset[0]; rlm@0: else rlm@0: { rlm@0: // Round up to nearest ADPCM block rlm@0: offset[1] = (ALdouble)((writePos+FrameBlockSize-1) / rlm@0: FrameBlockSize * BlockSize); rlm@0: } rlm@0: } rlm@0: else rlm@0: { rlm@0: ALuint OrigBytes = BytesFromUserFmt(OriginalType); rlm@0: offset[0] = (ALdouble)(readPos / Bytes * OrigBytes); rlm@0: offset[1] = (ALdouble)(writePos / Bytes * OrigBytes); rlm@0: } rlm@0: break; rlm@0: } rlm@0: } rlm@0: rlm@0: rlm@0: /* rlm@0: ApplyOffset rlm@0: rlm@0: Apply a playback offset to the Source. This function will update the queue (to correctly rlm@0: mark buffers as 'pending' or 'processed' depending upon the new offset. rlm@0: */ rlm@0: ALboolean ApplyOffset(ALsource *Source) rlm@0: { rlm@0: const ALbufferlistitem *BufferList; rlm@0: const ALbuffer *Buffer; rlm@0: ALint lBufferSize, lTotalBufferSize; rlm@0: ALint BuffersPlayed; rlm@0: ALint lByteOffset; rlm@0: rlm@0: // Get true byte offset rlm@0: lByteOffset = GetByteOffset(Source); rlm@0: rlm@0: // If the offset is invalid, don't apply it rlm@0: if(lByteOffset == -1) rlm@0: return AL_FALSE; rlm@0: rlm@0: // Sort out the queue (pending and processed states) rlm@0: BufferList = Source->queue; rlm@0: lTotalBufferSize = 0; rlm@0: BuffersPlayed = 0; rlm@0: rlm@0: while(BufferList) rlm@0: { rlm@0: Buffer = BufferList->buffer; rlm@0: lBufferSize = Buffer ? Buffer->size : 0; rlm@0: rlm@0: if(lBufferSize <= lByteOffset-lTotalBufferSize) rlm@0: { rlm@0: // Offset is past this buffer so increment BuffersPlayed rlm@0: BuffersPlayed++; rlm@0: } rlm@0: else if(lTotalBufferSize <= lByteOffset) rlm@0: { rlm@0: // Offset is within this buffer rlm@0: // Set Current Buffer rlm@0: Source->Buffer = BufferList->buffer; rlm@0: Source->BuffersPlayed = BuffersPlayed; rlm@0: rlm@0: // SW Mixer Positions are in Samples rlm@0: Source->position = (lByteOffset - lTotalBufferSize) / rlm@0: FrameSizeFromFmt(Buffer->FmtChannels, Buffer->FmtType); rlm@0: return AL_TRUE; rlm@0: } rlm@0: rlm@0: // Increment the TotalBufferSize rlm@0: lTotalBufferSize += lBufferSize; rlm@0: rlm@0: // Move on to next buffer in the Queue rlm@0: BufferList = BufferList->next; rlm@0: } rlm@0: // Offset is out of range of the buffer queue rlm@0: return AL_FALSE; rlm@0: } rlm@0: rlm@0: rlm@0: /* rlm@0: GetByteOffset rlm@0: rlm@0: Returns the 'true' byte offset into the Source's queue (from the Sample, Byte or Millisecond rlm@0: offset supplied by the application). This takes into account the fact that the buffer format rlm@0: may have been modifed by AL (e.g 8bit samples are converted to float) rlm@0: */ rlm@0: static ALint GetByteOffset(ALsource *Source) rlm@0: { rlm@0: const ALbuffer *Buffer = NULL; rlm@0: const ALbufferlistitem *BufferList; rlm@0: ALint ByteOffset = -1; rlm@0: rlm@0: // Find the first non-NULL Buffer in the Queue rlm@0: BufferList = Source->queue; rlm@0: while(BufferList) rlm@0: { rlm@0: if(BufferList->buffer) rlm@0: { rlm@0: Buffer = BufferList->buffer; rlm@0: break; rlm@0: } rlm@0: BufferList = BufferList->next; rlm@0: } rlm@0: rlm@0: if(!Buffer) rlm@0: { rlm@0: Source->lOffset = -1; rlm@0: return -1; rlm@0: } rlm@0: rlm@0: // Determine the ByteOffset (and ensure it is block aligned) rlm@0: switch(Source->lOffsetType) rlm@0: { rlm@0: case AL_BYTE_OFFSET: rlm@0: // Take into consideration the original format rlm@0: ByteOffset = Source->lOffset; rlm@0: if(Buffer->OriginalType == UserFmtIMA4) rlm@0: { rlm@0: // Round down to nearest ADPCM block rlm@0: ByteOffset /= 36 * ChannelsFromUserFmt(Buffer->OriginalChannels); rlm@0: // Multiply by compression rate (65 sample frames per block) rlm@0: ByteOffset *= 65; rlm@0: } rlm@0: else rlm@0: ByteOffset /= FrameSizeFromUserFmt(Buffer->OriginalChannels, Buffer->OriginalType); rlm@0: ByteOffset *= FrameSizeFromFmt(Buffer->FmtChannels, Buffer->FmtType); rlm@0: break; rlm@0: rlm@0: case AL_SAMPLE_OFFSET: rlm@0: ByteOffset = Source->lOffset * FrameSizeFromFmt(Buffer->FmtChannels, Buffer->FmtType); rlm@0: break; rlm@0: rlm@0: case AL_SEC_OFFSET: rlm@0: // Note - lOffset is internally stored as Milliseconds rlm@0: ByteOffset = (ALint)(Source->lOffset / 1000.0 * Buffer->Frequency); rlm@0: ByteOffset *= FrameSizeFromFmt(Buffer->FmtChannels, Buffer->FmtType); rlm@0: break; rlm@0: } rlm@0: // Clear Offset rlm@0: Source->lOffset = -1; rlm@0: rlm@0: return ByteOffset; rlm@0: } rlm@0: rlm@0: rlm@0: ALvoid ReleaseALSources(ALCcontext *Context) rlm@0: { rlm@0: ALsizei pos; rlm@0: ALuint j; rlm@0: for(pos = 0;pos < Context->SourceMap.size;pos++) rlm@0: { rlm@0: ALsource *temp = Context->SourceMap.array[pos].value; rlm@0: Context->SourceMap.array[pos].value = NULL; rlm@0: rlm@0: // For each buffer in the source's queue, decrement its reference counter and remove it rlm@0: while(temp->queue != NULL) rlm@0: { rlm@0: ALbufferlistitem *BufferList = temp->queue; rlm@0: temp->queue = BufferList->next; rlm@0: rlm@0: if(BufferList->buffer != NULL) rlm@0: BufferList->buffer->refcount--; rlm@0: free(BufferList); rlm@0: } rlm@0: rlm@0: for(j = 0;j < MAX_SENDS;++j) rlm@0: { rlm@0: if(temp->Send[j].Slot) rlm@0: temp->Send[j].Slot->refcount--; rlm@0: temp->Send[j].Slot = NULL; rlm@0: } rlm@0: rlm@0: // Release source structure rlm@0: FreeThunkEntry(temp->source); rlm@0: memset(temp, 0, sizeof(ALsource)); rlm@0: free(temp); rlm@0: } rlm@0: }