You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
799 lines
22 KiB
799 lines
22 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
Additional permissions under GNU GPL version 3 section 7 |
|
|
|
If you modify this Program, or any covered work, by linking or |
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
|
(or a modified version of those libraries), |
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
|
licensors of this Program grant you additional |
|
permission to convey the resulting work. Corresponding Source for a |
|
non-source form of such a combination shall include the source code for |
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
|
work. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
////////////////////////////////////////////////////////////////////////////// |
|
// // |
|
// plDSoundBuffer - Simple wrapper class for a DirectSound buffer. // |
|
// Allows us to simplify all the work done behind the // |
|
// scenes in plWin32BufferThread. // |
|
// // |
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
#include "hsTypes.h" |
|
#include "hsThread.h" |
|
#include "plDSoundBuffer.h" |
|
#include "al.h" |
|
|
|
#include "plgDispatch.h" |
|
#include "plAudioSystem.h" |
|
#include "../plAudioCore/plAudioCore.h" |
|
#include "../plAudioCore/plAudioFileReader.h" |
|
#include "plEAXEffects.h" |
|
|
|
#include "plProfile.h" |
|
|
|
#include "../plStatusLog/plStatusLog.h" |
|
|
|
#include <dsound.h> |
|
|
|
UInt32 plDSoundBuffer::fNumBuffers = 0; |
|
plProfile_CreateCounterNoReset( "Playing", "Sound", SoundPlaying ); |
|
plProfile_CreateCounterNoReset( "Allocated", "Sound", NumAllocated ); |
|
|
|
|
|
//// Constructor/Destructor ////////////////////////////////////////////////// |
|
|
|
plDSoundBuffer::plDSoundBuffer( UInt32 size, plWAVHeader &bufferDesc, hsBool enable3D, hsBool isLooping, hsBool tryStatic, bool streaming ) |
|
{ |
|
fLooping = isLooping; |
|
fValid = false; |
|
fBufferDesc = nil; |
|
|
|
fLockPtr = nil; |
|
fLockLength = 0; |
|
|
|
fStreaming = streaming; |
|
|
|
buffer = 0; |
|
source = 0; |
|
for(int i = 0; i < STREAMING_BUFFERS; ++i) |
|
{ |
|
streamingBuffers[i] = 0; |
|
} |
|
|
|
IAllocate( size, bufferDesc, enable3D, tryStatic ); |
|
fNumBuffers++; |
|
} |
|
|
|
plDSoundBuffer::~plDSoundBuffer() |
|
{ |
|
IRelease(); |
|
fNumBuffers--; |
|
|
|
} |
|
|
|
//// IAllocate /////////////////////////////////////////////////////////////// |
|
|
|
void plDSoundBuffer::IAllocate( UInt32 size, plWAVHeader &bufferDesc, hsBool enable3D, hsBool tryStatic ) |
|
{ |
|
// Create a DSound buffer description |
|
fBufferDesc = TRACKED_NEW DSBUFFERDESC; |
|
fBufferDesc->dwSize = sizeof( DSBUFFERDESC ); |
|
|
|
fBufferDesc->dwBufferBytes = size; |
|
fBufferDesc->dwReserved = 0; |
|
|
|
fBufferDesc->lpwfxFormat = TRACKED_NEW WAVEFORMATEX; |
|
fBufferDesc->lpwfxFormat->cbSize = 0; |
|
fBufferDesc->lpwfxFormat->nAvgBytesPerSec = bufferDesc.fAvgBytesPerSec; |
|
fBufferDesc->lpwfxFormat->nBlockAlign = bufferDesc.fBlockAlign; |
|
fBufferDesc->lpwfxFormat->nChannels = bufferDesc.fNumChannels; |
|
fBufferDesc->lpwfxFormat->nSamplesPerSec = bufferDesc.fNumSamplesPerSec; |
|
fBufferDesc->lpwfxFormat->wBitsPerSample = bufferDesc.fBitsPerSample; |
|
fBufferDesc->lpwfxFormat->wFormatTag = bufferDesc.fFormatTag; |
|
|
|
// Do we want to try EAX? |
|
if( plgAudioSys::UsingEAX() ) |
|
fEAXSource.Init( this ); |
|
|
|
fValid = true; |
|
plProfile_Inc( NumAllocated ); |
|
} |
|
|
|
//// IRelease //////////////////////////////////////////////////////////////// |
|
|
|
void plDSoundBuffer::IRelease( void ) |
|
{ |
|
if( IsPlaying() ) |
|
Stop(); |
|
|
|
// Release stuff |
|
fEAXSource.Release(); |
|
alSourcei(source, AL_BUFFER, nil); |
|
alDeleteSources(1, &source); |
|
if(buffer) |
|
alDeleteBuffers( 1, &buffer ); |
|
else |
|
alDeleteBuffers(STREAMING_BUFFERS, streamingBuffers); |
|
source = 0; |
|
buffer = 0; |
|
|
|
alGetError(); |
|
|
|
memset(streamingBuffers, 0, STREAMING_BUFFERS * sizeof(unsigned)); |
|
if( fBufferDesc != nil ) |
|
{ |
|
delete fBufferDesc->lpwfxFormat; |
|
fBufferDesc->lpwfxFormat = nil; |
|
} |
|
|
|
delete fBufferDesc; |
|
fBufferDesc = nil; |
|
|
|
fValid = false; |
|
plProfile_Dec( NumAllocated ); |
|
} |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* OpenAL |
|
* |
|
***/ |
|
|
|
int plDSoundBuffer::IGetALFormat(unsigned bitsPerSample, unsigned int numChannels) |
|
{ |
|
int format = 0; |
|
switch(bitsPerSample) |
|
{ |
|
case 8: |
|
format = (numChannels == 1) ? AL_FORMAT_MONO8 : AL_FORMAT_STEREO8; |
|
break; |
|
case 16: |
|
format = (numChannels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; |
|
break; |
|
} |
|
|
|
return format; |
|
} |
|
|
|
bool plDSoundBuffer::FillBuffer(void *data, unsigned bytes, plWAVHeader *header) |
|
{ |
|
if(source) |
|
{ |
|
alSourcei(source, AL_BUFFER, nil); |
|
alDeleteSources(1, &source); |
|
} |
|
if(buffer) |
|
alDeleteBuffers(1, &buffer); |
|
source = 0; |
|
buffer = 0; |
|
|
|
ALenum format = IGetALFormat(fBufferDesc->lpwfxFormat->wBitsPerSample, fBufferDesc->lpwfxFormat->nChannels); |
|
ALenum error = alGetError(); |
|
alGenBuffers(1, &buffer); |
|
error = alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error); |
|
return false; |
|
} |
|
|
|
alBufferData(buffer, format, data, bytes, header->fNumSamplesPerSec ); |
|
error = alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to fill sound buffer %d", error); |
|
return false; |
|
} |
|
alGenSources(1, &source); |
|
error = alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source); |
|
return false; |
|
} |
|
|
|
// Just make it quiet for now |
|
SetScalarVolume(0); |
|
|
|
alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048); |
|
alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
return false; |
|
} |
|
|
|
alSourcei(source, AL_BUFFER, buffer); |
|
error = alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to attach buffer to source %d", error); |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
|
|
//============================================================================ |
|
// OpenAL Streaming functions |
|
//============================================================================ |
|
|
|
// this function is used when restarting the audio system. It is needed to restart a streaming source from where it left off |
|
bool plDSoundBuffer::SetupStreamingSource(plAudioFileReader *stream) |
|
{ |
|
unsigned char data[STREAM_BUFFER_SIZE]; |
|
unsigned int size; |
|
ALenum error; |
|
|
|
alGetError(); |
|
int numBuffersToQueue = 0; |
|
|
|
// fill buffers with data |
|
for( int i = 0; i < STREAMING_BUFFERS; i++ ) |
|
{ |
|
size = stream->NumBytesLeft() < STREAM_BUFFER_SIZE ? stream->NumBytesLeft() : STREAM_BUFFER_SIZE; |
|
if(!size) |
|
{ |
|
if(IsLooping()) |
|
{ |
|
stream->SetPosition(0); |
|
} |
|
} |
|
|
|
stream->Read(size, data); |
|
numBuffersToQueue++; |
|
|
|
alGenBuffers( 1, &streamingBuffers[i] ); |
|
error = alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error); |
|
return false; |
|
} |
|
|
|
ALenum format = IGetALFormat(fBufferDesc->lpwfxFormat->wBitsPerSample, fBufferDesc->lpwfxFormat->nChannels); |
|
alBufferData( streamingBuffers[i], format, data, size, fBufferDesc->lpwfxFormat->nSamplesPerSec ); |
|
if( (error = alGetError()) != AL_NO_ERROR ) |
|
plStatusLog::AddLineS("audio.log", "alBufferData"); |
|
} |
|
|
|
// Generate AL Source |
|
alGenSources( 1, &source ); |
|
error = alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source); |
|
return false; |
|
} |
|
alSourcei(source, AL_BUFFER, nil); |
|
SetScalarVolume(0); |
|
|
|
|
|
alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048); |
|
alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
return false; |
|
} |
|
|
|
alSourceQueueBuffers( source, numBuffersToQueue, streamingBuffers ); |
|
error = alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to queue buffers %d", error); |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
// this function is used when starting up a streaming sound, as opposed to restarting it due to an audio system restart. |
|
bool plDSoundBuffer::SetupStreamingSource(void *data, unsigned bytes) |
|
{ |
|
unsigned char bufferData[STREAM_BUFFER_SIZE]; |
|
unsigned int size; |
|
ALenum error; |
|
char *pData = (char *)data; |
|
|
|
alGetError(); |
|
int numBuffersToQueue = 0; |
|
|
|
// fill buffers with data |
|
for( int i = 0; i < STREAMING_BUFFERS; i++ ) |
|
{ |
|
size = bytes < STREAM_BUFFER_SIZE ? bytes : STREAM_BUFFER_SIZE; |
|
if(!size) |
|
break; |
|
|
|
MemCopy(bufferData, pData, size); |
|
pData += size; |
|
bytes-= size; |
|
numBuffersToQueue++; |
|
|
|
alGenBuffers( 1, &streamingBuffers[i] ); |
|
error = alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error); |
|
return false; |
|
} |
|
|
|
ALenum format = IGetALFormat(fBufferDesc->lpwfxFormat->wBitsPerSample, fBufferDesc->lpwfxFormat->nChannels); |
|
alBufferData( streamingBuffers[i], format, bufferData, size, fBufferDesc->lpwfxFormat->nSamplesPerSec ); |
|
if( (error = alGetError()) != AL_NO_ERROR ) |
|
plStatusLog::AddLineS("audio.log", "alBufferData"); |
|
} |
|
|
|
// Generate AL Source |
|
alGenSources( 1, &source ); |
|
error = alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source); |
|
return false; |
|
} |
|
alSourcei(source, AL_BUFFER, nil); |
|
SetScalarVolume(0); |
|
|
|
alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048); |
|
alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
return false; |
|
} |
|
|
|
alSourceQueueBuffers( source, numBuffersToQueue, streamingBuffers ); |
|
error = alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to queue buffers %d", error); |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
//============================================================================ |
|
int plDSoundBuffer::BuffersProcessed() |
|
{ |
|
if(alIsSource(source)==AL_FALSE) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "BuffersProcessed, source invalid"); |
|
return 0; |
|
} |
|
ALint processed = 0; |
|
alGetSourcei( source, AL_BUFFERS_PROCESSED, &processed ); |
|
if(alGetError() != AL_NO_ERROR) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "alGetSourcei failed"); |
|
} |
|
|
|
return processed; |
|
} |
|
|
|
//============================================================================ |
|
int plDSoundBuffer::BuffersQueued() |
|
{ |
|
if(alIsSource(source)==AL_FALSE) return 0; |
|
ALint queued = 0; |
|
alGetSourcei( source, AL_BUFFERS_QUEUED, &queued ); |
|
alGetError(); |
|
|
|
return queued; |
|
|
|
} |
|
|
|
//============================================================================ |
|
bool plDSoundBuffer::StreamingFillBuffer(plAudioFileReader *stream) |
|
{ |
|
if(!source) |
|
return false; |
|
|
|
ALenum error; |
|
ALuint bufferId; |
|
unsigned char data[STREAM_BUFFER_SIZE]; |
|
int buffersProcessed = BuffersProcessed(); |
|
hsBool finished = false; |
|
|
|
for(int i = 0; i < buffersProcessed; i++) |
|
{ |
|
alSourceUnqueueBuffers( source, 1, &bufferId ); |
|
if( (error = alGetError()) != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to unqueue buffer %d", error); |
|
return false; |
|
} |
|
|
|
if(!finished) |
|
{ |
|
if(stream->NumBytesLeft() == 0) |
|
{ |
|
// if at anytime we run out of data, and we are looping, reset the data stream and continue to fill buffers |
|
if(IsLooping()) |
|
{ |
|
stream->SetPosition(0); // we are looping, so reset data stream, and keep filling buffers |
|
} |
|
else |
|
{ |
|
finished = true; // no more data, but we could still be playing, so we don't want to stop the sound yet |
|
} |
|
} |
|
|
|
if(!finished) |
|
{ unsigned int size = stream->NumBytesLeft() < STREAM_BUFFER_SIZE ? stream->NumBytesLeft() : STREAM_BUFFER_SIZE; |
|
stream->Read(size, data); |
|
|
|
ALenum format = IGetALFormat(fBufferDesc->lpwfxFormat->wBitsPerSample, fBufferDesc->lpwfxFormat->nChannels); |
|
alBufferData( bufferId, format, data, size, fBufferDesc->lpwfxFormat->nSamplesPerSec ); |
|
if( (error = alGetError()) != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to copy data to sound buffer %d", error); |
|
return false; |
|
} |
|
|
|
alSourceQueueBuffers( source, 1, &bufferId ); |
|
if( (error = alGetError()) != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to queue buffer %d", error); |
|
return false; |
|
} |
|
} |
|
} |
|
} |
|
if(!IsPlaying() && !finished) |
|
{ |
|
alSourcePlay(source); |
|
} |
|
alGetError(); |
|
return true; |
|
} |
|
|
|
/***************************************************************************** |
|
* |
|
* Voice playback functions |
|
* |
|
***/ |
|
|
|
bool plDSoundBuffer::GetAvailableBufferId(unsigned *bufferId) |
|
{ |
|
if(mAvailableBuffers.empty()) |
|
{ |
|
return false; |
|
} |
|
*bufferId = mAvailableBuffers.front(); |
|
mAvailableBuffers.pop_front(); |
|
return true; |
|
} |
|
|
|
bool plDSoundBuffer::SetupVoiceSource() |
|
{ |
|
ALenum error; |
|
alGetError(); |
|
|
|
// Generate AL Buffers |
|
alGenBuffers( STREAMING_BUFFERS, streamingBuffers ); |
|
error = alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error); |
|
return false; |
|
} |
|
|
|
for( int i = 0; i < STREAMING_BUFFERS; i++ ) |
|
{ |
|
mAvailableBuffers.push_back(streamingBuffers[i]); |
|
} |
|
|
|
// Generate AL Source |
|
alGenSources( 1, &source ); |
|
error = alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source); |
|
return false; |
|
} |
|
|
|
SetScalarVolume(0); |
|
|
|
alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048); |
|
alGetError(); |
|
if( error != AL_NO_ERROR ) |
|
{ |
|
return false; |
|
} |
|
alSourcei(source, AL_BUFFER, nil); |
|
alGetError(); |
|
//alSourcei(source, AL_PITCH, 0); |
|
|
|
// dont queue any buffers here |
|
return true; |
|
} |
|
|
|
//============================================================================ |
|
void plDSoundBuffer::UnQueueVoiceBuffers() |
|
{ |
|
unsigned buffersProcessed = BuffersProcessed(); |
|
if(buffersProcessed) |
|
plStatusLog::AddLineS("audio.log", "unqueuing buffers %d", buffersProcessed); |
|
for(int i = 0; i < buffersProcessed; i++) |
|
{ |
|
ALuint unQueued; |
|
alSourceUnqueueBuffers( source, 1, &unQueued ); |
|
if(alGetError() == AL_NO_ERROR) |
|
{ |
|
mAvailableBuffers.push_back(unQueued); |
|
} |
|
else |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to unqueue buffer"); |
|
} |
|
} |
|
} |
|
|
|
//============================================================================ |
|
bool plDSoundBuffer::VoiceFillBuffer(void *data, unsigned bytes, unsigned bufferId) |
|
{ |
|
if(!source) |
|
return false; |
|
|
|
ALenum error; |
|
unsigned int size = bytes < STREAM_BUFFER_SIZE ? bytes : STREAM_BUFFER_SIZE; |
|
|
|
ALenum format = IGetALFormat(fBufferDesc->lpwfxFormat->wBitsPerSample, fBufferDesc->lpwfxFormat->nChannels); |
|
alBufferData( bufferId, format, data, size, fBufferDesc->lpwfxFormat->nSamplesPerSec ); |
|
if( (error = alGetError()) != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to copy data to sound buffer %d", error); |
|
return false; |
|
} |
|
alSourceQueueBuffers( source, 1, &bufferId ); |
|
if( (error = alGetError()) != AL_NO_ERROR ) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Failed to queue buffer %d", error); |
|
return false; |
|
} |
|
if(!IsPlaying()) |
|
{ |
|
alSourcePlay(source); |
|
} |
|
alGetError(); |
|
|
|
return true; |
|
} |
|
|
|
//// SetLooping ////////////////////////////////////////////////////////////// |
|
|
|
void plDSoundBuffer::SetLooping( hsBool loop ) |
|
{ |
|
fLooping = loop; |
|
} |
|
|
|
void plDSoundBuffer::SetMinDistance( int dist ) |
|
{ |
|
alSourcei(source, AL_REFERENCE_DISTANCE, dist); |
|
ALenum error; |
|
if((error = alGetError()) != AL_NO_ERROR) |
|
plStatusLog::AddLineS("audio.log", "Failed to set min distance"); |
|
} |
|
|
|
void plDSoundBuffer::SetMaxDistance( int dist ) |
|
{ |
|
alSourcei(source, AL_MAX_DISTANCE, dist); |
|
ALenum error; |
|
if((error = alGetError()) != AL_NO_ERROR) |
|
plStatusLog::AddLineS("audio.log", "Failed to set min distance"); |
|
} |
|
|
|
//// Play //////////////////////////////////////////////////////////////////// |
|
|
|
void plDSoundBuffer::Play( void ) |
|
{ |
|
if(!source) |
|
return; |
|
ALenum error = alGetError(); // clear error |
|
|
|
// we dont want openal to loop our streaming buffers, or the buffer will loop back on itself. We will handle looping in the streaming sound |
|
if(fLooping && !fStreaming) |
|
alSourcei(source, AL_LOOPING, AL_TRUE); |
|
else |
|
alSourcei(source, AL_LOOPING, AL_FALSE); |
|
|
|
error = alGetError(); |
|
alSourcePlay(source); |
|
error = alGetError(); |
|
if(error != AL_NO_ERROR) |
|
plStatusLog::AddLineS("voice.log", "Play failed"); |
|
|
|
plProfile_Inc( SoundPlaying ); |
|
|
|
} |
|
|
|
//// Stop //////////////////////////////////////////////////////////////////// |
|
|
|
void plDSoundBuffer::Stop( void ) |
|
{ |
|
if(!source) |
|
return; |
|
alSourceStop(source); |
|
alGetError(); |
|
plProfile_Dec( SoundPlaying ); |
|
} |
|
|
|
//============================================================================ |
|
void plDSoundBuffer::SetPosition(float x, float y, float z) |
|
{ |
|
alSource3f(source, AL_POSITION, x, y, -z); // negate z coord, since openal uses opposite handedness |
|
alGetError(); |
|
} |
|
|
|
//============================================================================ |
|
void plDSoundBuffer::SetOrientation(float x, float y, float z) |
|
{ |
|
alSource3f(source, AL_ORIENTATION, x, y, -z); // negate z coord, since openal uses opposite handedness |
|
alGetError(); |
|
} |
|
|
|
//============================================================================ |
|
void plDSoundBuffer::SetVelocity(float x, float y, float z) |
|
{ |
|
alSource3f(source, AL_VELOCITY, 0, 0, 0); // no doppler shift |
|
alGetError(); |
|
} |
|
|
|
//============================================================================ |
|
void plDSoundBuffer::SetConeAngles(int inner, int outer) |
|
{ |
|
alSourcei(source, AL_CONE_INNER_ANGLE, inner); |
|
alSourcei(source, AL_CONE_OUTER_ANGLE, outer); |
|
alGetError(); |
|
} |
|
|
|
//============================================================================ |
|
void plDSoundBuffer::SetConeOrientation(float x, float y, float z) |
|
{ |
|
alSource3f(source, AL_DIRECTION, x, y, -z); // negate z coord, since openal uses opposite handedness |
|
alGetError(); |
|
} |
|
|
|
//============================================================================ |
|
// vol range: -5000 - 0 |
|
void plDSoundBuffer::SetConeOutsideVolume(int vol) |
|
{ |
|
float volume = (float)vol / 5000.0f + 1.0f; // mb to scalar |
|
alSourcef(source, AL_CONE_OUTER_GAIN, volume); |
|
alGetError(); |
|
} |
|
|
|
//============================================================================ |
|
void plDSoundBuffer::Rewind() |
|
{ |
|
alSourceRewind(source); |
|
alGetError(); |
|
} |
|
|
|
//// IsPlaying /////////////////////////////////////////////////////////////// |
|
|
|
hsBool plDSoundBuffer::IsPlaying( void ) |
|
{ |
|
ALint state = AL_STOPPED; |
|
alGetSourcei(source, AL_SOURCE_STATE, &state); |
|
alGetError(); |
|
return state == AL_PLAYING; |
|
} |
|
|
|
//// IsEAXAccelerated //////////////////////////////////////////////////////// |
|
|
|
hsBool plDSoundBuffer::IsEAXAccelerated( void ) const |
|
{ |
|
return fEAXSource.IsValid(); |
|
} |
|
|
|
//// BytePosToMSecs ////////////////////////////////////////////////////////// |
|
|
|
UInt32 plDSoundBuffer::BytePosToMSecs( UInt32 bytePos ) const |
|
{ |
|
return (UInt32)(bytePos * 1000 / (hsScalar)fBufferDesc->lpwfxFormat->nAvgBytesPerSec); |
|
} |
|
|
|
//// GetBufferBytePos //////////////////////////////////////////////////////// |
|
|
|
UInt32 plDSoundBuffer::GetBufferBytePos( hsScalar timeInSecs ) const |
|
{ |
|
hsAssert( fBufferDesc != nil && fBufferDesc->lpwfxFormat != nil, "Nil buffer description when calling GetBufferBytePos()" ); |
|
|
|
UInt32 byte = (UInt32)( timeInSecs * (hsScalar)fBufferDesc->lpwfxFormat->nSamplesPerSec ); |
|
byte *= fBufferDesc->lpwfxFormat->nBlockAlign; |
|
|
|
return byte; |
|
} |
|
|
|
//// GetLengthInBytes //////////////////////////////////////////////////////// |
|
|
|
UInt32 plDSoundBuffer::GetLengthInBytes( void ) const |
|
{ |
|
return (UInt32)fBufferDesc->dwBufferBytes; |
|
} |
|
|
|
//// SetEAXSettings ////////////////////////////////////////////////////////// |
|
|
|
void plDSoundBuffer::SetEAXSettings( plEAXSourceSettings *settings, hsBool force ) |
|
{ |
|
fEAXSource.SetFrom( settings, source, force ); |
|
} |
|
|
|
//// GetBlockAlign /////////////////////////////////////////////////////////// |
|
|
|
UInt8 plDSoundBuffer::GetBlockAlign( void ) const |
|
{ |
|
return ( fBufferDesc != nil && fBufferDesc->lpwfxFormat != nil ) ? fBufferDesc->lpwfxFormat->nBlockAlign : 0; |
|
} |
|
|
|
//// SetScalarVolume ///////////////////////////////////////////////////////// |
|
// Sets the volume, but on a range from 0 to 1 |
|
|
|
void plDSoundBuffer::SetScalarVolume( hsScalar volume ) |
|
{ |
|
if(source) |
|
{ |
|
ALenum error; |
|
alSourcef(source, AL_GAIN, volume); |
|
if((error = alGetError()) != AL_NO_ERROR) |
|
plStatusLog::AddLineS("audio.log", "failed to set volume on source %d", error); |
|
} |
|
} |
|
|
|
unsigned plDSoundBuffer::GetByteOffset() |
|
{ |
|
ALint bytes; |
|
alGetSourcei(source, AL_BYTE_OFFSET, &bytes); |
|
ALenum error = alGetError(); |
|
return bytes; |
|
} |
|
|
|
float plDSoundBuffer::GetTimeOffsetSec() |
|
{ |
|
float time; |
|
alGetSourcef(source, AL_SEC_OFFSET, &time); |
|
ALenum error = alGetError(); |
|
return time; |
|
} |
|
|
|
void plDSoundBuffer::SetTimeOffsetSec(float seconds) |
|
{ |
|
alSourcef(source, AL_SEC_OFFSET, seconds); |
|
ALenum error = alGetError(); |
|
} |
|
|
|
void plDSoundBuffer::SetTimeOffsetBytes(unsigned bytes) |
|
{ |
|
alSourcef(source, AL_BYTE_OFFSET, bytes); |
|
ALenum error = alGetError(); |
|
} |
|
|