added everything
This commit is contained in:
2503
engine/audio/audio.cc
Executable file
2503
engine/audio/audio.cc
Executable file
File diff suppressed because it is too large
Load Diff
16
engine/audio/audio.h
Executable file
16
engine/audio/audio.h
Executable file
@ -0,0 +1,16 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AUDIO_H_
|
||||
#define _AUDIO_H_
|
||||
|
||||
#ifndef _PLATFORMAUDIO_H_
|
||||
#include "platform/platformAudio.h"
|
||||
#endif
|
||||
#ifndef _AUDIODATABLOCK_H_
|
||||
#include "audio/audioDataBlock.h"
|
||||
#endif
|
||||
|
||||
#endif // _H_AUDIO_
|
429
engine/audio/audioBuffer.cc
Executable file
429
engine/audio/audioBuffer.cc
Executable file
@ -0,0 +1,429 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platformAL.h"
|
||||
#include "audio/audioBuffer.h"
|
||||
#include "core/stream.h"
|
||||
#include "console/console.h"
|
||||
#include "core/frameAllocator.h"
|
||||
|
||||
#ifndef TORQUE_NO_OGGVORBIS
|
||||
#include "vorbis/codec.h"
|
||||
#endif
|
||||
|
||||
|
||||
//#define LOG_SOUND_LOADS
|
||||
|
||||
/// WAV File-header
|
||||
struct WAVFileHdr
|
||||
{
|
||||
ALubyte id[4];
|
||||
ALsizei size;
|
||||
ALubyte type[4];
|
||||
};
|
||||
|
||||
//// WAV Fmt-header
|
||||
struct WAVFmtHdr
|
||||
{
|
||||
ALushort format;
|
||||
ALushort channels;
|
||||
ALuint samplesPerSec;
|
||||
ALuint bytesPerSec;
|
||||
ALushort blockAlign;
|
||||
ALushort bitsPerSample;
|
||||
};
|
||||
|
||||
/// WAV FmtEx-header
|
||||
struct WAVFmtExHdr
|
||||
{
|
||||
ALushort size;
|
||||
ALushort samplesPerBlock;
|
||||
};
|
||||
|
||||
/// WAV Smpl-header
|
||||
struct WAVSmplHdr
|
||||
{
|
||||
ALuint manufacturer;
|
||||
ALuint product;
|
||||
ALuint samplePeriod;
|
||||
ALuint note;
|
||||
ALuint fineTune;
|
||||
ALuint SMPTEFormat;
|
||||
ALuint SMPTEOffest;
|
||||
ALuint loops;
|
||||
ALuint samplerData;
|
||||
struct
|
||||
{
|
||||
ALuint identifier;
|
||||
ALuint type;
|
||||
ALuint start;
|
||||
ALuint end;
|
||||
ALuint fraction;
|
||||
ALuint count;
|
||||
} loop[1];
|
||||
};
|
||||
|
||||
/// WAV Chunk-header
|
||||
struct WAVChunkHdr
|
||||
{
|
||||
ALubyte id[4];
|
||||
ALuint size;
|
||||
};
|
||||
|
||||
#define CHUNKSIZE 4096
|
||||
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
AudioBuffer::AudioBuffer(StringTableEntry filename)
|
||||
{
|
||||
AssertFatal(StringTable->lookup(filename), "AudioBuffer:: filename is not a string table entry");
|
||||
|
||||
mFilename = filename;
|
||||
mLoading = false;
|
||||
malBuffer = 0;
|
||||
}
|
||||
|
||||
AudioBuffer::~AudioBuffer()
|
||||
{
|
||||
if( malBuffer != 0 ) {
|
||||
alDeleteBuffers( 1, &malBuffer );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
Resource<AudioBuffer> AudioBuffer::find(const char *filename)
|
||||
{
|
||||
U32 mark = FrameAllocator::getWaterMark();
|
||||
char * f2 = NULL;
|
||||
|
||||
Resource<AudioBuffer> buffer = ResourceManager->load(filename);
|
||||
if (bool(buffer) == false)
|
||||
{
|
||||
// wav file doesn't exist, try ogg file instead
|
||||
S32 len = dStrlen(filename);
|
||||
if (len>3 && !dStricmp(filename+len-4,".wav"))
|
||||
{
|
||||
f2 = (char*)FrameAllocator::alloc(len+1);
|
||||
dStrcpy(f2,filename);
|
||||
f2[len-3] = 'o';
|
||||
f2[len-2] = 'g';
|
||||
f2[len-1] = 'g';
|
||||
buffer = ResourceManager->load(f2);
|
||||
}
|
||||
}
|
||||
|
||||
// if resource still not there, try to create it if file exists
|
||||
if (bool(buffer) == false)
|
||||
{
|
||||
// see if the file exists -- first try default, then try ogg
|
||||
if (ResourceManager->getPathOf(filename))
|
||||
{
|
||||
AudioBuffer *temp = new AudioBuffer(StringTable->insert(filename));
|
||||
ResourceManager->add(filename, temp);
|
||||
buffer = ResourceManager->load(filename);
|
||||
}
|
||||
else if (f2 && ResourceManager->getPathOf(f2))
|
||||
{
|
||||
AudioBuffer *temp = new AudioBuffer(StringTable->insert(f2));
|
||||
ResourceManager->add(f2, temp);
|
||||
buffer = ResourceManager->load(f2);
|
||||
}
|
||||
}
|
||||
|
||||
FrameAllocator::setWaterMark(mark);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ResourceInstance* AudioBuffer::construct(Stream &)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
ALuint AudioBuffer::getALBuffer()
|
||||
{
|
||||
if (!alcGetCurrentContext())
|
||||
return 0;
|
||||
|
||||
// clear the error state
|
||||
alGetError();
|
||||
|
||||
// Intangir> fix for newest openAL from creative (it returns true, yea right 0 is not a valid buffer)
|
||||
// it MIGHT not work at all for all i know.
|
||||
if (malBuffer && alIsBuffer(malBuffer))
|
||||
return malBuffer;
|
||||
|
||||
alGenBuffers(1, &malBuffer);
|
||||
if(alGetError() != AL_NO_ERROR)
|
||||
return 0;
|
||||
|
||||
ResourceObject * obj = ResourceManager->find(mFilename);
|
||||
if(obj)
|
||||
{
|
||||
bool readSuccess = false;
|
||||
S32 len = dStrlen(mFilename);
|
||||
|
||||
if(len > 3 && !dStricmp(mFilename + len - 4, ".wav"))
|
||||
{
|
||||
#ifdef LOG_SOUND_LOADS
|
||||
Con::printf("Reading WAV: %s\n", mFilename);
|
||||
#endif
|
||||
readSuccess = readWAV(obj);
|
||||
}
|
||||
#ifndef TORQUE_NO_OGGVORBIS
|
||||
else if(len > 3 && !dStricmp(mFilename + len - 4, ".ogg"))
|
||||
{
|
||||
# ifdef LOG_SOUND_LOADS
|
||||
Con::printf("Reading Ogg: %s\n", mFilename);
|
||||
# endif
|
||||
readSuccess = readOgg(obj);
|
||||
}
|
||||
#endif
|
||||
if(readSuccess)
|
||||
return(malBuffer);
|
||||
}
|
||||
|
||||
alDeleteBuffers(1, &malBuffer);
|
||||
malBuffer = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! The Read a WAV file from the given ResourceObject and initialize
|
||||
an alBuffer with it.
|
||||
*/
|
||||
bool AudioBuffer::readWAV(ResourceObject *obj)
|
||||
{
|
||||
WAVChunkHdr chunkHdr;
|
||||
WAVFmtExHdr fmtExHdr;
|
||||
WAVFileHdr fileHdr;
|
||||
WAVSmplHdr smplHdr;
|
||||
WAVFmtHdr fmtHdr;
|
||||
|
||||
ALenum format = AL_FORMAT_MONO16;
|
||||
char *data = NULL;
|
||||
ALsizei size = 0;
|
||||
ALsizei freq = 22050;
|
||||
ALboolean loop = AL_FALSE;
|
||||
|
||||
Stream *stream = ResourceManager->openStream(obj);
|
||||
if (!stream)
|
||||
return false;
|
||||
|
||||
stream->read(4, &fileHdr.id[0]);
|
||||
stream->read(&fileHdr.size);
|
||||
stream->read(4, &fileHdr.type[0]);
|
||||
|
||||
fileHdr.size=((fileHdr.size+1)&~1)-4;
|
||||
|
||||
stream->read(4, &chunkHdr.id[0]);
|
||||
stream->read(&chunkHdr.size);
|
||||
// unread chunk data rounded up to nearest WORD
|
||||
S32 chunkRemaining = chunkHdr.size + (chunkHdr.size&1);
|
||||
|
||||
while ((fileHdr.size!=0) && (stream->getStatus() != Stream::EOS))
|
||||
{
|
||||
// WAV Format header
|
||||
if (!dStrncmp((const char*)chunkHdr.id,"fmt ",4))
|
||||
{
|
||||
stream->read(&fmtHdr.format);
|
||||
stream->read(&fmtHdr.channels);
|
||||
stream->read(&fmtHdr.samplesPerSec);
|
||||
stream->read(&fmtHdr.bytesPerSec);
|
||||
stream->read(&fmtHdr.blockAlign);
|
||||
stream->read(&fmtHdr.bitsPerSample);
|
||||
|
||||
if (fmtHdr.format==0x0001)
|
||||
{
|
||||
format=(fmtHdr.channels==1?
|
||||
(fmtHdr.bitsPerSample==8?AL_FORMAT_MONO8:AL_FORMAT_MONO16):
|
||||
(fmtHdr.bitsPerSample==8?AL_FORMAT_STEREO8:AL_FORMAT_STEREO16));
|
||||
freq=fmtHdr.samplesPerSec;
|
||||
chunkRemaining -= sizeof(WAVFmtHdr);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream->read(sizeof(WAVFmtExHdr), &fmtExHdr);
|
||||
chunkRemaining -= sizeof(WAVFmtExHdr);
|
||||
}
|
||||
}
|
||||
// WAV Format header
|
||||
else if (!dStrncmp((const char*)chunkHdr.id,"data",4))
|
||||
{
|
||||
if (fmtHdr.format==0x0001)
|
||||
{
|
||||
size=chunkHdr.size;
|
||||
data=new char[chunkHdr.size];
|
||||
if (data)
|
||||
{
|
||||
stream->read(chunkHdr.size, data);
|
||||
#if defined(TORQUE_OS_MAC)
|
||||
// need to endian-flip the 16-bit data.
|
||||
if (fmtHdr.bitsPerSample==16) // !!!TBD we don't handle stereo, so may be RL flipped.
|
||||
{
|
||||
U16 *ds = (U16*)data;
|
||||
U16 *de = (U16*)(data+size);
|
||||
while (ds<de)
|
||||
{
|
||||
#if defined(TORQUE_BIG_ENDIAN)
|
||||
*ds = convertLEndianToHost(*ds);
|
||||
#else
|
||||
*ds = convertBEndianToHost(*ds);
|
||||
#endif
|
||||
ds++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
chunkRemaining -= chunkHdr.size;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else if (fmtHdr.format==0x0011)
|
||||
{
|
||||
//IMA ADPCM
|
||||
}
|
||||
else if (fmtHdr.format==0x0055)
|
||||
{
|
||||
//MP3 WAVE
|
||||
}
|
||||
}
|
||||
// WAV Loop header
|
||||
else if (!dStrncmp((const char*)chunkHdr.id,"smpl",4))
|
||||
{
|
||||
// this struct read is NOT endian safe but it is ok because
|
||||
// we are only testing the loops field against ZERO
|
||||
stream->read(sizeof(WAVSmplHdr), &smplHdr);
|
||||
loop = (smplHdr.loops ? AL_TRUE : AL_FALSE);
|
||||
chunkRemaining -= sizeof(WAVSmplHdr);
|
||||
}
|
||||
|
||||
// either we have unread chunk data or we found an unknown chunk type
|
||||
// loop and read up to 1K bytes at a time until we have
|
||||
// read to the end of this chunk
|
||||
char buffer[1024];
|
||||
AssertFatal(chunkRemaining >= 0, "AudioBuffer::readWAV: remaining chunk data should never be less than zero.");
|
||||
while (chunkRemaining > 0)
|
||||
{
|
||||
S32 readSize = getMin(1024, chunkRemaining);
|
||||
stream->read(readSize, buffer);
|
||||
chunkRemaining -= readSize;
|
||||
}
|
||||
|
||||
fileHdr.size-=(((chunkHdr.size+1)&~1)+8);
|
||||
|
||||
// read next chunk header...
|
||||
stream->read(4, &chunkHdr.id[0]);
|
||||
stream->read(&chunkHdr.size);
|
||||
// unread chunk data rounded up to nearest WORD
|
||||
chunkRemaining = chunkHdr.size + (chunkHdr.size&1);
|
||||
}
|
||||
|
||||
ResourceManager->closeStream(stream);
|
||||
if (data)
|
||||
{
|
||||
alBufferData(malBuffer, format, data, size, freq);
|
||||
delete [] data;
|
||||
return (alGetError() == AL_NO_ERROR);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef TORQUE_NO_OGGVORBIS
|
||||
/*! The Read an Ogg Vorbis file from the given ResourceObject and initialize
|
||||
an alBuffer with it.
|
||||
*/
|
||||
bool AudioBuffer::readOgg(ResourceObject *obj)
|
||||
{
|
||||
OggVorbisFile vf;
|
||||
vorbis_info *vi;
|
||||
|
||||
ALenum format = AL_FORMAT_MONO16;
|
||||
char *data = NULL;
|
||||
ALsizei size = 0;
|
||||
ALsizei freq = 22050;
|
||||
ALboolean loop = AL_FALSE;
|
||||
int current_section = 0;
|
||||
|
||||
#if defined(TORQUE_BIG_ENDIAN)
|
||||
int endian = 1;
|
||||
#else
|
||||
int endian = 0;
|
||||
#endif
|
||||
|
||||
int eof = 0;
|
||||
|
||||
Stream *stream = ResourceManager->openStream(obj);
|
||||
if (!stream)
|
||||
return false;
|
||||
|
||||
if(vf.ov_open(stream, NULL, 0) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//Read Vorbis File Info
|
||||
vi = vf.ov_info(-1);
|
||||
freq = vi->rate;
|
||||
|
||||
long samples = (long)vf.ov_pcm_total(-1);
|
||||
|
||||
if(vi->channels == 1) {
|
||||
format = AL_FORMAT_MONO16;
|
||||
size = 2 * samples;
|
||||
} else {
|
||||
format = AL_FORMAT_STEREO16;
|
||||
size = 4 * samples;
|
||||
}
|
||||
|
||||
|
||||
data=new char[size];
|
||||
|
||||
if (data)
|
||||
{
|
||||
long ret = oggRead(&vf, data, size, endian, ¤t_section);
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
vf.ov_clear();
|
||||
|
||||
ResourceManager->closeStream(stream);
|
||||
if (data)
|
||||
{
|
||||
alBufferData(malBuffer, format, data, size, freq);
|
||||
delete [] data;
|
||||
return (alGetError() == AL_NO_ERROR);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ov_read() only returns a maximum of one page worth of data
|
||||
// this helper function will repeat the read until buffer is full
|
||||
long AudioBuffer::oggRead(OggVorbisFile* vf, char *buffer,int length,
|
||||
int bigendianp,int *bitstream)
|
||||
{
|
||||
long bytesRead = 0;
|
||||
long totalBytes = 0;
|
||||
long offset = 0;
|
||||
long bytesToRead = 0;
|
||||
//while((offset + CHUNKSIZE) < length) {
|
||||
while((offset) < length)
|
||||
{
|
||||
if((length - offset) < CHUNKSIZE)
|
||||
bytesToRead = length - offset;
|
||||
else
|
||||
bytesToRead = CHUNKSIZE;
|
||||
|
||||
bytesRead = vf->ov_read(buffer, bytesToRead, bigendianp, bitstream);
|
||||
if(bytesRead <= 0)
|
||||
break;
|
||||
offset += bytesRead;
|
||||
buffer += bytesRead;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
#endif
|
56
engine/audio/audioBuffer.h
Executable file
56
engine/audio/audioBuffer.h
Executable file
@ -0,0 +1,56 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AUDIOBUFFER_H_
|
||||
#define _AUDIOBUFFER_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _PLATFORMAL_H_
|
||||
#include "platform/platformAL.h"
|
||||
#endif
|
||||
#ifndef _RESMANAGER_H_
|
||||
#include "core/resManager.h"
|
||||
#endif
|
||||
|
||||
// MLH - don't need oggbvorbis in tools
|
||||
#ifndef TORQUE_NO_OGGVORBIS
|
||||
#include "audio/vorbisStream.h"
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class AudioBuffer: public ResourceInstance
|
||||
{
|
||||
friend class AudioThread;
|
||||
|
||||
private:
|
||||
StringTableEntry mFilename;
|
||||
bool mLoading;
|
||||
ALuint malBuffer;
|
||||
|
||||
bool readRIFFchunk(Stream &s, const char *seekLabel, U32 *size);
|
||||
bool readWAV(ResourceObject *obj);
|
||||
|
||||
#ifndef TORQUE_NO_OGGVORBIS
|
||||
bool readOgg(ResourceObject *obj);
|
||||
long oggRead(OggVorbisFile* vf, char *buffer,int length,
|
||||
int bigendianp,int *bitstream);
|
||||
#endif
|
||||
|
||||
public:
|
||||
AudioBuffer(StringTableEntry filename);
|
||||
~AudioBuffer();
|
||||
ALuint getALBuffer();
|
||||
bool isLoading() {return(mLoading);}
|
||||
|
||||
static Resource<AudioBuffer> find(const char *filename);
|
||||
static ResourceInstance* construct(Stream& stream);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // _H_AUDIOBUFFER_
|
532
engine/audio/audioDataBlock.cc
Executable file
532
engine/audio/audioDataBlock.cc
Executable file
@ -0,0 +1,532 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "audio/audioDataBlock.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "platform/platformAL.h"
|
||||
#include "sim/netConnection.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
namespace
|
||||
{
|
||||
void writeRangedF32(BitStream * bstream, F32 val, F32 min, F32 max, U32 numBits)
|
||||
{
|
||||
val = (mClampF(val, min, max) - min) / (max - min);
|
||||
bstream->writeInt(val * ((1 << numBits) - 1), numBits);
|
||||
}
|
||||
|
||||
F32 readRangedF32(BitStream * bstream, F32 min, F32 max, U32 numBits)
|
||||
{
|
||||
return(min + (F32(bstream->readInt(numBits)) / F32((1 << numBits) - 1)) * (max - min));
|
||||
}
|
||||
|
||||
void writeRangedS32(BitStream * bstream, S32 val, S32 min, S32 max)
|
||||
{
|
||||
bstream->writeRangedU32((val - min), 0, (max - min));
|
||||
}
|
||||
|
||||
S32 readRangedS32(BitStream * bstream, S32 min, S32 max)
|
||||
{
|
||||
return(bstream->readRangedU32(0, (max - min)) + min);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Class AudioEnvironment:
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CO_DATABLOCK_V1(AudioEnvironment);
|
||||
|
||||
AudioEnvironment::AudioEnvironment()
|
||||
{
|
||||
mUseRoom = true;
|
||||
mRoom = EAX_ENVIRONMENT_GENERIC;
|
||||
mRoomHF = 0;
|
||||
mReflections = 0;
|
||||
mReverb = 0;
|
||||
mRoomRolloffFactor = 0.1f;
|
||||
mDecayTime = 0.1f;
|
||||
mDecayHFRatio = 0.1f;
|
||||
mReflectionsDelay = 0.f;
|
||||
mReverbDelay = 0.f;
|
||||
mRoomVolume = 0;
|
||||
mEffectVolume = 0.f;
|
||||
mDamping = 0.f;
|
||||
mEnvironmentSize = 10.f;
|
||||
mEnvironmentDiffusion = 1.f;
|
||||
mAirAbsorption = 0.f;
|
||||
mFlags = 0;
|
||||
}
|
||||
|
||||
static EnumTable::Enums roomEnums[] =
|
||||
{
|
||||
{ EAX_ENVIRONMENT_GENERIC, "GENERIC" }, // 0
|
||||
{ EAX_ENVIRONMENT_PADDEDCELL, "PADDEDCELL" },
|
||||
{ EAX_ENVIRONMENT_ROOM, "ROOM" },
|
||||
{ EAX_ENVIRONMENT_BATHROOM, "BATHROOM" },
|
||||
{ EAX_ENVIRONMENT_LIVINGROOM, "LIVINGROOM" },
|
||||
{ EAX_ENVIRONMENT_STONEROOM, "STONEROOM" }, // 5
|
||||
{ EAX_ENVIRONMENT_AUDITORIUM, "AUDITORIUM" },
|
||||
{ EAX_ENVIRONMENT_CONCERTHALL, "CONCERTHALL" },
|
||||
{ EAX_ENVIRONMENT_CAVE, "CAVE" },
|
||||
{ EAX_ENVIRONMENT_ARENA, "ARENA" },
|
||||
{ EAX_ENVIRONMENT_HANGAR, "HANGAR" }, // 10
|
||||
{ EAX_ENVIRONMENT_CARPETEDHALLWAY, "CARPETEDHALLWAY" },
|
||||
{ EAX_ENVIRONMENT_HALLWAY, "HALLWAY" },
|
||||
{ EAX_ENVIRONMENT_STONECORRIDOR, "STONECORRIDOR" },
|
||||
{ EAX_ENVIRONMENT_ALLEY, "ALLEY" },
|
||||
{ EAX_ENVIRONMENT_FOREST, "FOREST" }, // 15
|
||||
{ EAX_ENVIRONMENT_CITY, "CITY" },
|
||||
{ EAX_ENVIRONMENT_MOUNTAINS, "MOUNTAINS" },
|
||||
{ EAX_ENVIRONMENT_QUARRY, "QUARRY" },
|
||||
{ EAX_ENVIRONMENT_PLAIN, "PLAIN" },
|
||||
{ EAX_ENVIRONMENT_PARKINGLOT, "PARKINGLOT" }, // 20
|
||||
{ EAX_ENVIRONMENT_SEWERPIPE, "SEWERPIPE" },
|
||||
{ EAX_ENVIRONMENT_UNDERWATER, "UNDERWATER" },
|
||||
{ EAX_ENVIRONMENT_DRUGGED, "DRUGGED" },
|
||||
{ EAX_ENVIRONMENT_DIZZY, "DIZZY" },
|
||||
{ EAX_ENVIRONMENT_PSYCHOTIC, "PSYCHOTIC" } // 25
|
||||
};
|
||||
static EnumTable gAudioEnvironmentRoomTypes(sizeof(roomEnums) / sizeof(roomEnums[0]), &roomEnums[0]);
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CONSOLETYPE(AudioEnvironment)
|
||||
IMPLEMENT_GETDATATYPE(AudioEnvironment)
|
||||
IMPLEMENT_SETDATATYPE(AudioEnvironment)
|
||||
|
||||
void AudioEnvironment::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("useRoom", TypeBool, Offset(mUseRoom, AudioEnvironment));
|
||||
addField("room", TypeEnum, Offset(mRoom, AudioEnvironment), 1, &gAudioEnvironmentRoomTypes);
|
||||
addField("roomHF", TypeS32, Offset(mRoomHF, AudioEnvironment));
|
||||
addField("reflections", TypeS32, Offset(mReflections, AudioEnvironment));
|
||||
addField("reverb", TypeS32, Offset(mReverb, AudioEnvironment));
|
||||
addField("roomRolloffFactor", TypeF32, Offset(mRoomRolloffFactor, AudioEnvironment));
|
||||
addField("decayTime", TypeF32, Offset(mDecayTime, AudioEnvironment));
|
||||
addField("decayHFRatio", TypeF32, Offset(mDecayHFRatio, AudioEnvironment));
|
||||
addField("reflectionsDelay", TypeF32, Offset(mReflectionsDelay, AudioEnvironment));
|
||||
addField("reverbDelay", TypeF32, Offset(mReverbDelay, AudioEnvironment));
|
||||
addField("roomVolume", TypeS32, Offset(mRoomVolume, AudioEnvironment));
|
||||
addField("effectVolume", TypeF32, Offset(mEffectVolume, AudioEnvironment));
|
||||
addField("damping", TypeF32, Offset(mDamping, AudioEnvironment));
|
||||
addField("environmentSize", TypeF32, Offset(mEnvironmentSize, AudioEnvironment));
|
||||
addField("environmentDiffusion", TypeF32, Offset(mEnvironmentDiffusion, AudioEnvironment));
|
||||
addField("airAbsorption", TypeF32, Offset(mAirAbsorption, AudioEnvironment));
|
||||
addField("flags", TypeS32, Offset(mFlags, AudioEnvironment));
|
||||
}
|
||||
|
||||
|
||||
void AudioEnvironment::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
if(stream->writeFlag(mUseRoom))
|
||||
stream->writeRangedU32(mRoom, EAX_ENVIRONMENT_GENERIC, EAX_ENVIRONMENT_COUNT);
|
||||
else
|
||||
{
|
||||
writeRangedS32(stream, mRoomHF, -10000, 0);
|
||||
writeRangedS32(stream, mReflections, -10000, 10000);
|
||||
writeRangedS32(stream, mReverb, -10000, 2000);
|
||||
writeRangedF32(stream, mRoomRolloffFactor, 0.1f, 10.f, 8);
|
||||
writeRangedF32(stream, mDecayTime, 0.1f, 20.f, 8);
|
||||
writeRangedF32(stream, mDecayHFRatio, 0.1f, 20.f, 8);
|
||||
writeRangedF32(stream, mReflectionsDelay, 0.f, 0.3f, 9);
|
||||
writeRangedF32(stream, mReverbDelay, 0.f, 0.1f, 7);
|
||||
writeRangedS32(stream, mRoomVolume, -10000, 0);
|
||||
writeRangedF32(stream, mEffectVolume, 0.f, 1.f, 8);
|
||||
writeRangedF32(stream, mDamping, 0.f, 2.f, 9);
|
||||
writeRangedF32(stream, mEnvironmentSize, 1.f, 100.f, 10);
|
||||
writeRangedF32(stream, mEnvironmentDiffusion, 0.f, 1.f, 8);
|
||||
writeRangedF32(stream, mAirAbsorption, -100.f, 0.f, 10);
|
||||
stream->writeInt(mFlags, 6);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioEnvironment::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
mUseRoom = stream->readFlag();
|
||||
if(mUseRoom)
|
||||
mRoom = stream->readRangedU32(EAX_ENVIRONMENT_GENERIC, EAX_ENVIRONMENT_COUNT);
|
||||
else
|
||||
{
|
||||
mRoomHF = readRangedS32(stream, -10000, 0);
|
||||
mReflections = readRangedS32(stream, -10000, 10000);
|
||||
mReverb = readRangedS32(stream, -10000, 2000);
|
||||
mRoomRolloffFactor = readRangedF32(stream, 0.1f, 10.f, 8);
|
||||
mDecayTime = readRangedF32(stream, 0.1f, 20.f, 8);
|
||||
mDecayHFRatio = readRangedF32(stream, 0.1f, 20.f, 8);
|
||||
mReflectionsDelay = readRangedF32(stream, 0.f, 0.3f, 9);
|
||||
mReverbDelay = readRangedF32(stream, 0.f, 0.1f, 7);
|
||||
mRoomVolume = readRangedS32(stream, -10000, 0);
|
||||
mEffectVolume = readRangedF32(stream, 0.f, 1.f, 8);
|
||||
mDamping = readRangedF32(stream, 0.f, 2.f, 9);
|
||||
mEnvironmentSize = readRangedF32(stream, 1.f, 100.f, 10);
|
||||
mEnvironmentDiffusion = readRangedF32(stream, 0.f, 1.f, 8);
|
||||
mAirAbsorption = readRangedF32(stream, -100.f, 0.f, 10);
|
||||
mFlags = stream->readInt(6);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Class AudioEnvironmentProfile:
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CO_DATABLOCK_V1(AudioSampleEnvironment);
|
||||
|
||||
AudioSampleEnvironment::AudioSampleEnvironment()
|
||||
{
|
||||
mDirect = 0;
|
||||
mDirectHF = 0;
|
||||
mRoom = 0;
|
||||
mRoomHF = 0;
|
||||
mObstruction = 0.f;
|
||||
mObstructionLFRatio = 0.f;
|
||||
mOcclusion = 0.f;
|
||||
mOcclusionLFRatio = 0.f;
|
||||
mOcclusionRoomRatio = 0.f;
|
||||
mRoomRolloff = 0.f;
|
||||
mAirAbsorption = 0.f;
|
||||
mOutsideVolumeHF = 0.f;
|
||||
mFlags = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CONSOLETYPE(AudioSampleEnvironment)
|
||||
IMPLEMENT_GETDATATYPE(AudioSampleEnvironment)
|
||||
IMPLEMENT_SETDATATYPE(AudioSampleEnvironment)
|
||||
|
||||
void AudioSampleEnvironment::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("direct", TypeS32, Offset(mDirect, AudioSampleEnvironment));
|
||||
addField("directHF", TypeS32, Offset(mDirectHF, AudioSampleEnvironment));
|
||||
addField("room", TypeS32, Offset(mRoom, AudioSampleEnvironment));
|
||||
addField("obstruction", TypeF32, Offset(mObstruction, AudioSampleEnvironment));
|
||||
addField("obstructionLFRatio", TypeF32, Offset(mObstructionLFRatio, AudioSampleEnvironment));
|
||||
addField("occlusion", TypeF32, Offset(mOcclusion, AudioSampleEnvironment));
|
||||
addField("occlusionLFRatio", TypeF32, Offset(mOcclusionLFRatio, AudioSampleEnvironment));
|
||||
addField("occlusionRoomRatio", TypeF32, Offset(mOcclusionRoomRatio, AudioSampleEnvironment));
|
||||
addField("roomRolloff", TypeF32, Offset(mRoomRolloff, AudioSampleEnvironment));
|
||||
addField("airAbsorption", TypeF32, Offset(mAirAbsorption, AudioSampleEnvironment));
|
||||
addField("outsideVolumeHF", TypeS32, Offset(mOutsideVolumeHF, AudioSampleEnvironment));
|
||||
addField("flags", TypeS32, Offset(mFlags, AudioSampleEnvironment));
|
||||
}
|
||||
|
||||
void AudioSampleEnvironment::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
writeRangedS32(stream, mDirect, -10000, 1000);
|
||||
writeRangedS32(stream, mDirectHF, -10000, 0);
|
||||
writeRangedS32(stream, mRoom, -10000, 1000);
|
||||
writeRangedS32(stream, mRoomHF, -10000, 0);
|
||||
writeRangedF32(stream, mObstruction, 0.f, 1.f, 9);
|
||||
writeRangedF32(stream, mObstructionLFRatio, 0.f, 1.f, 8);
|
||||
writeRangedF32(stream, mOcclusion, 0.f, 1.f, 9);
|
||||
writeRangedF32(stream, mOcclusionLFRatio, 0.f, 1.f, 8);
|
||||
writeRangedF32(stream, mOcclusionRoomRatio, 0.f, 10.f, 9);
|
||||
writeRangedF32(stream, mRoomRolloff, 0.f, 10.f, 9);
|
||||
writeRangedF32(stream, mAirAbsorption, 0.f, 10.f, 9);
|
||||
writeRangedS32(stream, mOutsideVolumeHF, -10000, 0);
|
||||
stream->writeInt(mFlags, 3);
|
||||
}
|
||||
|
||||
void AudioSampleEnvironment::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
mDirect = readRangedS32(stream, -10000, 1000);
|
||||
mDirectHF = readRangedS32(stream, -10000, 0);
|
||||
mRoom = readRangedS32(stream, -10000, 1000);
|
||||
mRoomHF = readRangedS32(stream, -10000, 0);
|
||||
mObstruction = readRangedF32(stream, 0.f, 1.f, 9);
|
||||
mObstructionLFRatio = readRangedF32(stream, 0.f, 1.f, 8);
|
||||
mOcclusion = readRangedF32(stream, 0.f, 1.f, 9);
|
||||
mOcclusionLFRatio = readRangedF32(stream, 0.f, 1.f, 8);
|
||||
mOcclusionRoomRatio = readRangedF32(stream, 0.f, 10.f, 9);
|
||||
mRoomRolloff = readRangedF32(stream, 0.f, 10.f, 9);
|
||||
mAirAbsorption = readRangedF32(stream, 0.f, 10.f, 9);
|
||||
mOutsideVolumeHF = readRangedS32(stream, -10000, 0);
|
||||
mFlags = stream->readInt(3);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Class AudioDescription:
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CO_DATABLOCK_V1(AudioDescription);
|
||||
|
||||
AudioDescription::AudioDescription()
|
||||
{
|
||||
mDescription.mVolume = 1.0f;
|
||||
mDescription.mIsLooping = false;
|
||||
mDescription.mIsStreaming = false;
|
||||
mDescription.mIs3D = false;
|
||||
mDescription.mReferenceDistance = 1.0f;
|
||||
mDescription.mMaxDistance = 100.0f;
|
||||
mDescription.mConeInsideAngle = 360;
|
||||
mDescription.mConeOutsideAngle = 360;
|
||||
mDescription.mConeOutsideVolume = 1.0f;
|
||||
mDescription.mConeVector.set(0, 0, 1);
|
||||
mDescription.mEnvironmentLevel = 0.f;
|
||||
mDescription.mLoopCount = -1;
|
||||
mDescription.mMinLoopGap = 0;
|
||||
mDescription.mMaxLoopGap = 0;
|
||||
mDescription.mType = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CONSOLETYPE(AudioDescription)
|
||||
IMPLEMENT_GETDATATYPE(AudioDescription)
|
||||
IMPLEMENT_SETDATATYPE(AudioDescription)
|
||||
|
||||
void AudioDescription::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("volume", TypeF32, Offset(mDescription.mVolume, AudioDescription));
|
||||
addField("isLooping", TypeBool, Offset(mDescription.mIsLooping, AudioDescription));
|
||||
addField("isStreaming", TypeBool, Offset(mDescription.mIsStreaming, AudioDescription));
|
||||
addField("is3D", TypeBool, Offset(mDescription.mIs3D, AudioDescription));
|
||||
addField("referenceDistance", TypeF32, Offset(mDescription.mReferenceDistance, AudioDescription));
|
||||
addField("maxDistance", TypeF32, Offset(mDescription.mMaxDistance, AudioDescription));
|
||||
addField("coneInsideAngle", TypeS32, Offset(mDescription.mConeInsideAngle, AudioDescription));
|
||||
addField("coneOutsideAngle", TypeS32, Offset(mDescription.mConeOutsideAngle, AudioDescription));
|
||||
addField("coneOutsideVolume", TypeF32, Offset(mDescription.mConeOutsideVolume, AudioDescription));
|
||||
addField("coneVector", TypePoint3F, Offset(mDescription.mConeVector, AudioDescription));
|
||||
addField("environmentLevel", TypeF32, Offset(mDescription.mEnvironmentLevel, AudioDescription));
|
||||
addField("loopCount", TypeS32, Offset(mDescription.mLoopCount, AudioDescription));
|
||||
addField("minLoopGap", TypeS32, Offset(mDescription.mMinLoopGap, AudioDescription));
|
||||
addField("maxLoopGap", TypeS32, Offset(mDescription.mMaxLoopGap, AudioDescription));
|
||||
addField("type", TypeS32, Offset(mDescription.mType, AudioDescription));
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool AudioDescription::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
// validate the data
|
||||
mDescription.mVolume = mClampF(mDescription.mVolume, 0.0f, 1.0f);
|
||||
mDescription.mLoopCount = mClamp(mDescription.mLoopCount, -1, mDescription.mLoopCount);
|
||||
mDescription.mMaxLoopGap = mClamp(mDescription.mMaxLoopGap, mDescription.mMinLoopGap, mDescription.mMaxLoopGap);
|
||||
mDescription.mMinLoopGap = mClamp(mDescription.mMinLoopGap, 0, mDescription.mMaxLoopGap);
|
||||
|
||||
if (mDescription.mIs3D)
|
||||
{
|
||||
// validate the data
|
||||
mDescription.mReferenceDistance = mClampF(mDescription.mReferenceDistance, 0.f, mDescription.mReferenceDistance);
|
||||
mDescription.mMaxDistance = (mDescription.mMaxDistance > mDescription.mReferenceDistance) ? mDescription.mMaxDistance : (mDescription.mReferenceDistance+0.01f);
|
||||
mDescription.mConeInsideAngle = mClamp(mDescription.mConeInsideAngle, 0, 360);
|
||||
mDescription.mConeOutsideAngle = mClamp(mDescription.mConeOutsideAngle, mDescription.mConeInsideAngle, 360);
|
||||
mDescription.mConeOutsideVolume = mClampF(mDescription.mConeOutsideVolume, 0.0f, 1.0f);
|
||||
mDescription.mConeVector.normalize();
|
||||
mDescription.mEnvironmentLevel = mClampF(mDescription.mEnvironmentLevel, 0.f, 1.f);
|
||||
}
|
||||
|
||||
if(mDescription.mType >= Audio::NumAudioTypes)
|
||||
mDescription.mType = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void AudioDescription::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
stream->writeFloat(mDescription.mVolume, 6);
|
||||
if(stream->writeFlag(mDescription.mIsLooping))
|
||||
{
|
||||
stream->write(mDescription.mLoopCount);
|
||||
stream->write(mDescription.mMinLoopGap);
|
||||
stream->write(mDescription.mMaxLoopGap);
|
||||
}
|
||||
|
||||
stream->writeFlag(mDescription.mIsStreaming);
|
||||
stream->writeFlag(mDescription.mIs3D);
|
||||
if (mDescription.mIs3D)
|
||||
{
|
||||
stream->write(mDescription.mReferenceDistance);
|
||||
stream->write(mDescription.mMaxDistance);
|
||||
stream->writeInt(mDescription.mConeInsideAngle, 9);
|
||||
stream->writeInt(mDescription.mConeOutsideAngle, 9);
|
||||
stream->writeInt(mDescription.mConeOutsideVolume, 6);
|
||||
stream->writeNormalVector(mDescription.mConeVector, 8);
|
||||
stream->write(mDescription.mEnvironmentLevel);
|
||||
}
|
||||
stream->writeInt(mDescription.mType, 3);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void AudioDescription::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
mDescription.mVolume = stream->readFloat(6);
|
||||
mDescription.mIsLooping = stream->readFlag();
|
||||
if(mDescription.mIsLooping)
|
||||
{
|
||||
stream->read(&mDescription.mLoopCount);
|
||||
stream->read(&mDescription.mMinLoopGap);
|
||||
stream->read(&mDescription.mMaxLoopGap);
|
||||
}
|
||||
|
||||
mDescription.mIsStreaming = stream->readFlag();
|
||||
mDescription.mIs3D = stream->readFlag();
|
||||
if ( mDescription.mIs3D )
|
||||
{
|
||||
stream->read(&mDescription.mReferenceDistance);
|
||||
stream->read(&mDescription.mMaxDistance);
|
||||
mDescription.mConeInsideAngle = stream->readInt(9);
|
||||
mDescription.mConeOutsideAngle = stream->readInt(9);
|
||||
mDescription.mConeOutsideVolume = stream->readFloat(6);
|
||||
stream->readNormalVector(&mDescription.mConeVector, 8);
|
||||
stream->read(&mDescription.mEnvironmentLevel);
|
||||
}
|
||||
mDescription.mType = stream->readInt(3);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Class AudioProfile:
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CO_DATABLOCK_V1(AudioProfile);
|
||||
|
||||
AudioProfile::AudioProfile()
|
||||
{
|
||||
mFilename = NULL;
|
||||
mDescriptionObjectID = 0;
|
||||
mDescriptionObject = NULL;
|
||||
mSampleEnvironment = 0;
|
||||
mPreload = false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CONSOLETYPE(AudioProfile)
|
||||
IMPLEMENT_GETDATATYPE(AudioProfile)
|
||||
IMPLEMENT_SETDATATYPE(AudioProfile)
|
||||
|
||||
void AudioProfile::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("filename", TypeFilename, Offset(mFilename, AudioProfile));
|
||||
addField("description", TypeAudioDescriptionPtr, Offset(mDescriptionObject, AudioProfile));
|
||||
addField("environment", TypeAudioSampleEnvironmentPtr, Offset(mSampleEnvironment, AudioProfile));
|
||||
addField("preload", TypeBool, Offset(mPreload, AudioProfile));
|
||||
}
|
||||
|
||||
bool AudioProfile::preload(bool server, char errorBuffer[256])
|
||||
{
|
||||
if(!Parent::preload(server, errorBuffer))
|
||||
return false;
|
||||
|
||||
if(!server && NetConnection::filesWereDownloaded() && !bool(AudioBuffer::find(mFilename)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool AudioProfile::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
if (!mDescriptionObject && mDescriptionObjectID)
|
||||
Sim::findObject(mDescriptionObjectID , mDescriptionObject);
|
||||
|
||||
// if this is client side, make sure that description is as well
|
||||
if(mDescriptionObject)
|
||||
{ // client side dataBlock id's are not in the dataBlock id range
|
||||
if (getId() >= DataBlockObjectIdFirst && getId() <= DataBlockObjectIdLast)
|
||||
{
|
||||
SimObjectId pid = mDescriptionObject->getId();
|
||||
if (pid < DataBlockObjectIdFirst || pid > DataBlockObjectIdLast)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General,"AudioProfile: data dataBlock not networkable (use datablock to create).");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(mPreload && mFilename != NULL && alcGetCurrentContext())
|
||||
{
|
||||
mBuffer = AudioBuffer::find(mFilename);
|
||||
if(bool(mBuffer))
|
||||
{
|
||||
ALuint bufferId = mBuffer->getALBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void AudioProfile::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
|
||||
// audio description:
|
||||
if (stream->writeFlag(mDescriptionObject))
|
||||
stream->writeRangedU32(mDescriptionObject->getId(), DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
|
||||
// environmental info:
|
||||
if (stream->writeFlag(mSampleEnvironment))
|
||||
stream->writeRangedU32(mSampleEnvironment->getId(), DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
|
||||
//
|
||||
char buffer[256];
|
||||
if(!mFilename)
|
||||
buffer[0] = 0;
|
||||
else
|
||||
dStrcpy(buffer, mFilename);
|
||||
// S32 len = dStrlen(buffer);
|
||||
// if(len > 3 && !dStricmp(buffer + len - 4, ".wav"))
|
||||
// buffer[len-4] = 0;
|
||||
stream->writeString(buffer);
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void AudioProfile::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
|
||||
// audio datablock:
|
||||
if (stream->readFlag()) {
|
||||
SimObjectId id = stream->readRangedU32(DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
|
||||
mDescriptionObjectID = id;
|
||||
Sim::findObject(id, mDescriptionObject);
|
||||
}
|
||||
|
||||
// sample environment:
|
||||
if (stream->readFlag()) {
|
||||
SimObjectId id = stream->readRangedU32(DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
Sim::findObject(id, mSampleEnvironment);
|
||||
}
|
||||
|
||||
char buffer[256];
|
||||
stream->readString(buffer);
|
||||
// dStrcat(buffer, ".wav");
|
||||
|
||||
mFilename = StringTable->insert(buffer);
|
||||
|
||||
// Doh! Something missing from the unpackData...don't want to break
|
||||
// network protocol, so set it to true always here. This is good for
|
||||
// ThinkTanks only. In the future, simply send the preload bit.
|
||||
mPreload = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
140
engine/audio/audioDataBlock.h
Executable file
140
engine/audio/audioDataBlock.h
Executable file
@ -0,0 +1,140 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AUDIODATABLOCK_H_
|
||||
#define _AUDIODATABLOCK_H_
|
||||
|
||||
#ifndef _PLATFORMAUDIO_H_
|
||||
#include "platform/platformAudio.h"
|
||||
#endif
|
||||
#ifndef _AUDIOBUFFER_H_
|
||||
#include "audio/audioBuffer.h"
|
||||
#endif
|
||||
#ifndef _BITSTREAM_H_
|
||||
#include "core/bitStream.h"
|
||||
#endif
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class AudioEnvironment : public SimDataBlock
|
||||
{
|
||||
typedef SimDataBlock Parent;
|
||||
|
||||
public:
|
||||
|
||||
bool mUseRoom;
|
||||
S32 mRoom;
|
||||
S32 mRoomHF;
|
||||
S32 mReflections;
|
||||
S32 mReverb;
|
||||
F32 mRoomRolloffFactor;
|
||||
F32 mDecayTime;
|
||||
F32 mDecayHFRatio;
|
||||
F32 mReflectionsDelay;
|
||||
F32 mReverbDelay;
|
||||
S32 mRoomVolume;
|
||||
F32 mEffectVolume;
|
||||
F32 mDamping;
|
||||
F32 mEnvironmentSize;
|
||||
F32 mEnvironmentDiffusion;
|
||||
F32 mAirAbsorption;
|
||||
S32 mFlags;
|
||||
|
||||
AudioEnvironment();
|
||||
|
||||
static void initPersistFields();
|
||||
void packData(BitStream* stream);
|
||||
void unpackData(BitStream* stream);
|
||||
|
||||
DECLARE_CONOBJECT(AudioEnvironment);
|
||||
};
|
||||
DECLARE_CONSOLETYPE(AudioEnvironment)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class AudioSampleEnvironment : public SimDataBlock
|
||||
{
|
||||
typedef SimDataBlock Parent;
|
||||
|
||||
public:
|
||||
|
||||
S32 mDirect;
|
||||
S32 mDirectHF;
|
||||
S32 mRoom;
|
||||
S32 mRoomHF;
|
||||
F32 mObstruction;
|
||||
F32 mObstructionLFRatio;
|
||||
F32 mOcclusion;
|
||||
F32 mOcclusionLFRatio;
|
||||
F32 mOcclusionRoomRatio;
|
||||
F32 mRoomRolloff;
|
||||
F32 mAirAbsorption;
|
||||
S32 mOutsideVolumeHF;
|
||||
S32 mFlags;
|
||||
|
||||
AudioSampleEnvironment();
|
||||
static void initPersistFields();
|
||||
|
||||
void packData(BitStream* stream);
|
||||
void unpackData(BitStream* stream);
|
||||
|
||||
DECLARE_CONOBJECT(AudioSampleEnvironment);
|
||||
};
|
||||
DECLARE_CONSOLETYPE(AudioSampleEnvironment)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class AudioDescription: public SimDataBlock
|
||||
{
|
||||
private:
|
||||
typedef SimDataBlock Parent;
|
||||
|
||||
public:
|
||||
// field info
|
||||
Audio::Description mDescription;
|
||||
|
||||
AudioDescription();
|
||||
DECLARE_CONOBJECT(AudioDescription);
|
||||
static void initPersistFields();
|
||||
virtual bool onAdd();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
|
||||
const Audio::Description* getDescription() const { return &mDescription; }
|
||||
};
|
||||
DECLARE_CONSOLETYPE(AudioDescription)
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
class AudioProfile: public SimDataBlock
|
||||
{
|
||||
private:
|
||||
typedef SimDataBlock Parent;
|
||||
|
||||
Resource<AudioBuffer> mBuffer;
|
||||
|
||||
public:
|
||||
// field info
|
||||
U32 mDescriptionObjectID;
|
||||
AudioDescription *mDescriptionObject;
|
||||
AudioSampleEnvironment *mSampleEnvironment;
|
||||
|
||||
StringTableEntry mFilename;
|
||||
bool mPreload;
|
||||
|
||||
AudioProfile();
|
||||
DECLARE_CONOBJECT(AudioProfile);
|
||||
static void initPersistFields();
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
virtual bool preload(bool server, char errorBuffer[256]);
|
||||
|
||||
const Audio::Description* getDescription() const { return mDescriptionObject ? mDescriptionObject->getDescription() : NULL; }
|
||||
bool isPreload() { return mPreload; }
|
||||
};
|
||||
DECLARE_CONSOLETYPE(AudioProfile)
|
||||
|
||||
#endif // _H_AUDIODATABLOCK_
|
554
engine/audio/audioFunctions.cc
Executable file
554
engine/audio/audioFunctions.cc
Executable file
@ -0,0 +1,554 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/platformAudio.h"
|
||||
#include "console/simBase.h"
|
||||
#include "audio/audioDataBlock.h"
|
||||
|
||||
|
||||
extern ALuint alxGetWaveLen(ALuint buffer);
|
||||
|
||||
extern F32 mAudioTypeVolume[Audio::NumAudioTypes];
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Expose all al get/set methods...
|
||||
//--------------------------------------------------------------------------
|
||||
enum AL_GetSetBits{
|
||||
Source = BIT(0),
|
||||
Listener = BIT(1),
|
||||
Context = BIT(2),
|
||||
Environment = BIT(3),
|
||||
Get = BIT(4),
|
||||
Set = BIT(5),
|
||||
Int = BIT(6),
|
||||
Float = BIT(7),
|
||||
Float3 = BIT(8),
|
||||
Float6 = BIT(9)
|
||||
};
|
||||
|
||||
static ALenum getEnum(const char * name, U32 flags)
|
||||
{
|
||||
AssertFatal(name, "Audio getEnum: bad param");
|
||||
|
||||
static struct {
|
||||
char * mName;
|
||||
ALenum mAlenum;
|
||||
U32 mFlags;
|
||||
} table[] = {
|
||||
//-----------------------------------------------------------------------------------------------------------------
|
||||
// "name" ENUM Flags
|
||||
//-----------------------------------------------------------------------------------------------------------------
|
||||
{ "AL_GAIN", AL_GAIN, (Source|Listener|Get|Set|Float) },
|
||||
{ "AL_GAIN_LINEAR", AL_GAIN_LINEAR, (Source|Listener|Get|Set|Float) },
|
||||
{ "AL_PITCH", AL_PITCH, (Source|Get|Set|Float) },
|
||||
{ "AL_REFERENCE_DISTANCE", AL_REFERENCE_DISTANCE, (Source|Get|Set|Float) },
|
||||
{ "AL_MAX_DISTANCE", AL_MAX_DISTANCE, (Source|Get|Set|Float) },
|
||||
{ "AL_CONE_OUTER_GAIN", AL_CONE_OUTER_GAIN, (Source|Get|Set|Float) },
|
||||
{ "AL_POSITION", AL_POSITION, (Source|Listener|Get|Set|Float3) },
|
||||
{ "AL_DIRECTION", AL_DIRECTION, (Source|Get|Set|Float3) },
|
||||
{ "AL_VELOCITY", AL_VELOCITY, (Source|Listener|Get|Set|Float3) },
|
||||
{ "AL_ORIENTATION", AL_ORIENTATION, (Listener|Set|Float6) },
|
||||
{ "AL_CONE_INNER_ANGLE", AL_CONE_INNER_ANGLE, (Source|Get|Set|Int) },
|
||||
{ "AL_CONE_OUTER_ANGLE", AL_CONE_OUTER_ANGLE, (Source|Get|Set|Int) },
|
||||
{ "AL_LOOPING", AL_LOOPING, (Source|Get|Set|Int) },
|
||||
//{ "AL_STREAMING", AL_STREAMING, (Source|Get|Set|Int) },
|
||||
//{ "AL_BUFFER", AL_BUFFER, (Source|Get|Set|Int) },
|
||||
|
||||
{ "AL_VENDOR", AL_VENDOR, (Context|Get) },
|
||||
{ "AL_VERSION", AL_VERSION, (Context|Get) },
|
||||
{ "AL_RENDERER", AL_RENDERER, (Context|Get) },
|
||||
{ "AL_EXTENSIONS", AL_EXTENSIONS, (Context|Get) },
|
||||
|
||||
/*
|
||||
// environment
|
||||
{ "AL_ENV_ROOM_IASIG", AL_ENV_ROOM_IASIG, (Environment|Get|Set|Int) },
|
||||
{ "AL_ENV_ROOM_HIGH_FREQUENCY_IASIG", AL_ENV_ROOM_HIGH_FREQUENCY_IASIG, (Environment|Get|Set|Int) },
|
||||
{ "AL_ENV_REFLECTIONS_IASIG", AL_ENV_REFLECTIONS_IASIG, (Environment|Get|Set|Int) },
|
||||
{ "AL_ENV_REVERB_IASIG", AL_ENV_REVERB_IASIG, (Environment|Get|Set|Int) },
|
||||
{ "AL_ENV_ROOM_ROLLOFF_FACTOR_IASIG", AL_ENV_ROOM_ROLLOFF_FACTOR_IASIG, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_DECAY_TIME_IASIG", AL_ENV_DECAY_TIME_IASIG, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_DECAY_HIGH_FREQUENCY_RATIO_IASIG", AL_ENV_DECAY_HIGH_FREQUENCY_RATIO_IASIG, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_REFLECTIONS_DELAY_IASIG", AL_ENV_REFLECTIONS_DELAY_IASIG, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_REVERB_DELAY_IASIG", AL_ENV_REVERB_DELAY_IASIG, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_DIFFUSION_IASIG", AL_ENV_DIFFUSION_IASIG, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_DENSITY_IASIG", AL_ENV_DENSITY_IASIG, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_HIGH_FREQUENCY_REFERENCE_IASIG", AL_ENV_HIGH_FREQUENCY_REFERENCE_IASIG, (Environment|Get|Set|Float) },
|
||||
|
||||
{ "AL_ENV_ROOM_VOLUME_EXT", AL_ENV_ROOM_VOLUME_EXT, (Environment|Get|Set|Int) },
|
||||
{ "AL_ENV_FLAGS_EXT", AL_ENV_FLAGS_EXT, (Environment|Get|Set|Int) },
|
||||
{ "AL_ENV_EFFECT_VOLUME_EXT", AL_ENV_EFFECT_VOLUME_EXT, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_DAMPING_EXT", AL_ENV_DAMPING_EXT, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_ENVIRONMENT_SIZE_EXT", AL_ENV_ENVIRONMENT_SIZE_EXT, (Environment|Get|Set|Float) },
|
||||
|
||||
// sample environment
|
||||
{ "AL_ENV_SAMPLE_DIRECT_EXT", AL_ENV_SAMPLE_DIRECT_EXT, (Source|Get|Set|Int) },
|
||||
{ "AL_ENV_SAMPLE_DIRECT_HF_EXT", AL_ENV_SAMPLE_DIRECT_HF_EXT, (Source|Get|Set|Int) },
|
||||
{ "AL_ENV_SAMPLE_ROOM_EXT", AL_ENV_SAMPLE_ROOM_EXT, (Source|Get|Set|Int) },
|
||||
{ "AL_ENV_SAMPLE_ROOM_HF_EXT", AL_ENV_SAMPLE_ROOM_HF_EXT, (Source|Get|Set|Int) },
|
||||
{ "AL_ENV_SAMPLE_OUTSIDE_VOLUME_HF_EXT", AL_ENV_SAMPLE_OUTSIDE_VOLUME_HF_EXT, (Source|Get|Set|Int) },
|
||||
{ "AL_ENV_SAMPLE_FLAGS_EXT", AL_ENV_SAMPLE_FLAGS_EXT, (Source|Get|Set|Int) },
|
||||
|
||||
{ "AL_ENV_SAMPLE_REVERB_MIX_EXT", AL_ENV_SAMPLE_REVERB_MIX_EXT, (Source|Get|Set|Float) },
|
||||
{ "AL_ENV_SAMPLE_OBSTRUCTION_EXT", AL_ENV_SAMPLE_OBSTRUCTION_EXT, (Source|Get|Set|Float) },
|
||||
{ "AL_ENV_SAMPLE_OBSTRUCTION_LF_RATIO_EXT", AL_ENV_SAMPLE_OBSTRUCTION_LF_RATIO_EXT, (Source|Get|Set|Float) },
|
||||
{ "AL_ENV_SAMPLE_OCCLUSION_EXT", AL_ENV_SAMPLE_OCCLUSION_EXT, (Source|Get|Set|Float) },
|
||||
{ "AL_ENV_SAMPLE_OCCLUSION_LF_RATIO_EXT", AL_ENV_SAMPLE_OCCLUSION_LF_RATIO_EXT, (Source|Get|Set|Float) },
|
||||
{ "AL_ENV_SAMPLE_OCCLUSION_ROOM_RATIO_EXT", AL_ENV_SAMPLE_OCCLUSION_ROOM_RATIO_EXT, (Source|Get|Set|Float) },
|
||||
{ "AL_ENV_SAMPLE_ROOM_ROLLOFF_EXT", AL_ENV_SAMPLE_ROOM_ROLLOFF_EXT, (Source|Get|Set|Float) },
|
||||
{ "AL_ENV_SAMPLE_AIR_ABSORPTION_EXT", AL_ENV_SAMPLE_AIR_ABSORPTION_EXT, (Source|Get|Set|Float) },
|
||||
*/
|
||||
};
|
||||
for(U32 i = 0; i < (sizeof(table) / sizeof(table[0])); i++)
|
||||
{
|
||||
if((table[i].mFlags & flags) != flags)
|
||||
continue;
|
||||
|
||||
if(dStricmp(table[i].mName, name) == 0)
|
||||
return(table[i].mAlenum);
|
||||
}
|
||||
|
||||
return(AL_INVALID);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunctionGroupBegin(Audio, "Functions dealing with the OpenAL audio layer.\n\n"
|
||||
"@see www.OpenAL.org for what these functions do. Variances from posted"
|
||||
" behaviour is described below.");
|
||||
|
||||
ConsoleFunction(OpenALInitDriver, bool, 1, 1, "Initializes the OpenAL driver.\n\n"
|
||||
"@note You must call this before any sounds will work!")
|
||||
{
|
||||
if (Audio::OpenALInit())
|
||||
{
|
||||
static bool registered = false;
|
||||
if (!registered) {
|
||||
ResourceManager->registerExtension(".wav", AudioBuffer::construct);
|
||||
ResourceManager->registerExtension(".ogg", AudioBuffer::construct);
|
||||
}
|
||||
registered = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(OpenALShutdownDriver, void, 1, 1, "OpenALShutdownDriver()")
|
||||
{
|
||||
Audio::OpenALShutdown();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(OpenALRegisterExtensions, void, 1, 1, "OpenALRegisterExtensions()")
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alGetString, const char *, 2, 2, "(string item)\n\n"
|
||||
"This wraps alGetString().")
|
||||
{
|
||||
argc;
|
||||
ALenum e = getEnum(argv[1], (Context|Get));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alGetString: invalid enum name '%s'", argv[1]);
|
||||
return "";
|
||||
}
|
||||
|
||||
return (const char*)alGetString(e);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Soundfile
|
||||
//--------------------------------------------------------------------------
|
||||
ConsoleFunction(alxGetWaveLen, S32, 2, 2, "(string filename)\n\n"
|
||||
"@param filename File to determine length of.\n"
|
||||
"@returns Length in milliseconds.")
|
||||
{
|
||||
Resource<AudioBuffer> buffer = AudioBuffer::find(argv[1]);
|
||||
if (bool(buffer)) {
|
||||
ALuint alBuffer = buffer->getALBuffer();
|
||||
return alxGetWaveLen(alBuffer);
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Source
|
||||
//--------------------------------------------------------------------------
|
||||
ConsoleFunction(alxCreateSource, S32, 2, 6,
|
||||
"(profile) or "
|
||||
"(profile, x,y,z) or "
|
||||
"(description, filename) or "
|
||||
"(description, filename, x,y,z)")
|
||||
{
|
||||
AudioDescription *description = NULL;
|
||||
AudioProfile *profile = dynamic_cast<AudioProfile*>( Sim::findObject( argv[1] ) );
|
||||
if (profile == NULL)
|
||||
{
|
||||
description = dynamic_cast<AudioDescription*>( Sim::findObject( argv[1] ) );
|
||||
if (description == NULL)
|
||||
{
|
||||
Con::printf("Unable to locate audio profile/description '%s'", argv[1]);
|
||||
return NULL_AUDIOHANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (profile)
|
||||
{
|
||||
if (argc == 2)
|
||||
return alxCreateSource(profile);
|
||||
|
||||
MatrixF transform;
|
||||
transform.set(EulerF(0,0,0), Point3F( dAtof(argv[2]),dAtof(argv[3]),dAtof(argv[4]) ));
|
||||
return alxCreateSource(profile, &transform);
|
||||
}
|
||||
|
||||
if (description)
|
||||
{
|
||||
|
||||
if (argc == 3)
|
||||
return alxCreateSource(description, argv[2]);
|
||||
|
||||
MatrixF transform;
|
||||
transform.set(EulerF(0,0,0), Point3F( dAtof(argv[3]),dAtof(argv[4]),dAtof(argv[5]) ));
|
||||
return alxCreateSource(description, argv[2], &transform);
|
||||
}
|
||||
|
||||
return NULL_AUDIOHANDLE;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxSourcef, void, 4, 4, "(handle, ALenum, value)")
|
||||
{
|
||||
ALenum e = getEnum(argv[2], (Source|Set|Float));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "cAudio_alxSourcef: invalid enum name '%s'", argv[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
alxSourcef(dAtoi(argv[1]), e, dAtof(argv[3]));
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxSource3f, void, 3, 6, "(handle, ALenum, x, y, z)\n\n"
|
||||
"@note You can replace the last three parameters with a string, "
|
||||
"\"x y z\"")
|
||||
{
|
||||
ALenum e = getEnum(argv[2], (Source|Set|Float3));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "cAudio_alxSource3f: invalid enum name '%s'", argv[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
if((argc != 4 && argc != 6))
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "cAudio_alxSource3f: wrong number of args");
|
||||
return;
|
||||
}
|
||||
|
||||
Point3F pos;
|
||||
if(argc == 4)
|
||||
dSscanf(argv[3], "%g %g %g", &pos.x, &pos.y, &pos.z);
|
||||
else
|
||||
{
|
||||
pos.x = dAtof(argv[3]);
|
||||
pos.y = dAtof(argv[4]);
|
||||
pos.z = dAtof(argv[5]);
|
||||
}
|
||||
|
||||
alxSource3f(dAtoi(argv[1]), e, pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxSourcei, void, 4, 4, "(handle, ALenum, value)")
|
||||
{
|
||||
ALenum e = getEnum(argv[2], (Source|Set|Int));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "cAudio_alxSourcei: invalid enum name '%s'", argv[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
alxSourcei(dAtoi(argv[1]), e, dAtoi(argv[3]));
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxGetSourcef, F32, 3, 3, "(handle, ALenum)")
|
||||
{
|
||||
ALenum e = getEnum(argv[2], (Source|Get|Float));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "cAudio_alxGetSourcef: invalid enum name '%s'", argv[2]);
|
||||
return(0.f);
|
||||
}
|
||||
|
||||
F32 value;
|
||||
alxGetSourcef(dAtoi(argv[1]), e, &value);
|
||||
return(value);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxGetSource3f, const char *, 3, 3, "(handle, ALenum)" )
|
||||
{
|
||||
ALenum e = getEnum(argv[2], (Source|Get|Float));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "cAudio_alxGetSource3f: invalid enum name '%s'", argv[2]);
|
||||
return("0 0 0");
|
||||
}
|
||||
|
||||
F32 value1, value2, value3;
|
||||
alxGetSource3f(dAtoi(argv[1]), e, &value1, &value2, &value3);
|
||||
|
||||
char * ret = Con::getReturnBuffer(64);
|
||||
dSprintf(ret, 64, "%7.3f %7.3 %7.3", value1, value2, value3);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxGetSourcei, S32, 3, 3, "(handle, ALenum)")
|
||||
{
|
||||
ALenum e = getEnum(argv[2], (Source|Get|Int));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "cAudio_alxGetSourcei: invalid enum name '%s'", argv[2]);
|
||||
return(0);
|
||||
}
|
||||
|
||||
S32 value;
|
||||
alxGetSourcei(dAtoi(argv[1]), e, &value);
|
||||
return(value);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxPlay, S32, 2, 5, "alxPlay(handle) or "
|
||||
"alxPlay(profile) or "
|
||||
"alxPlay(profile, x,y,z)")
|
||||
{
|
||||
if (argc == 2)
|
||||
{
|
||||
AUDIOHANDLE handle = dAtoi(argv[1]);
|
||||
if (handle != 0)
|
||||
return alxPlay(handle);
|
||||
}
|
||||
|
||||
AudioProfile *profile = dynamic_cast<AudioProfile*>( Sim::findObject( argv[1] ) );
|
||||
if (profile == NULL)
|
||||
{
|
||||
Con::printf("Unable to locate audio profile '%s'", argv[1]);
|
||||
return NULL_AUDIOHANDLE;
|
||||
}
|
||||
|
||||
Point3F pos(0.f, 0.f, 0.f);
|
||||
if(argc == 3)
|
||||
dSscanf(argv[2], "%g %g %g", &pos.x, &pos.y, &pos.z);
|
||||
else if(argc == 5)
|
||||
pos.set(dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]));
|
||||
|
||||
MatrixF transform;
|
||||
transform.set(EulerF(0,0,0), pos);
|
||||
|
||||
return alxPlay(profile, &transform, NULL);
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxStop, void, 2, 2, "(int handle)")
|
||||
{
|
||||
AUDIOHANDLE handle = dAtoi(argv[1]);
|
||||
if(handle == NULL_AUDIOHANDLE)
|
||||
return;
|
||||
alxStop(handle);
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxStopAll, void, 1, 1, "()")
|
||||
{
|
||||
alxStopAll();
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxIsPlaying, bool, 2, 5, "alxIsPlaying(handle)")
|
||||
{
|
||||
AUDIOHANDLE handle = dAtoi(argv[1]);
|
||||
if(handle == NULL_AUDIOHANDLE)
|
||||
return false;
|
||||
return alxIsPlaying(handle);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Listener
|
||||
//--------------------------------------------------------------------------
|
||||
ConsoleFunction(alxListenerf, void, 3, 3, "alxListener(ALenum, value)")
|
||||
{
|
||||
ALenum e = getEnum(argv[1], (Listener|Set|Float));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alxListenerf: invalid enum name '%s'", argv[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
alxListenerf(e, dAtof(argv[2]));
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alListener3f, void, 3, 5, "alListener3f(ALenum, \"x y z\") or "
|
||||
"alListener3f(ALenum, x, y, z)")
|
||||
{
|
||||
ALenum e = getEnum(argv[1], (Listener|Set|Float3));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alListener3f: invalid enum name '%s'", argv[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(argc != 3 && argc != 5)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alListener3f: wrong number of arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
Point3F pos;
|
||||
if(argc == 3)
|
||||
dSscanf(argv[2], "%g %g %g", &pos.x, &pos.y, &pos.z);
|
||||
else
|
||||
{
|
||||
pos.x = dAtof(argv[2]);
|
||||
pos.y = dAtof(argv[3]);
|
||||
pos.z = dAtof(argv[4]);
|
||||
}
|
||||
|
||||
alListener3f(e, pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxGetListenerf, F32, 2, 2, "alxGetListenerf(Alenum)")
|
||||
{
|
||||
ALenum e = getEnum(argv[1], (Source|Get|Float));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alxGetListenerf: invalid enum name '%s'", argv[1]);
|
||||
return(0.f);
|
||||
}
|
||||
|
||||
F32 value;
|
||||
alxGetListenerf(e, &value);
|
||||
return(value);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alGetListener3f, const char *, 2, 2, "alGetListener3f(Alenum)")
|
||||
{
|
||||
ALenum e = getEnum(argv[2], (Source|Get|Float));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alGetListener3f: invalid enum name '%s'", argv[1]);
|
||||
return("0 0 0");
|
||||
}
|
||||
|
||||
F32 value1, value2, value3;
|
||||
alGetListener3f(e, &value1, &value2, &value3);
|
||||
|
||||
char * ret = Con::getReturnBuffer(64);
|
||||
dSprintf(ret, 64, "%7.3f %7.3 %7.3", value1, value2, value3);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alGetListeneri, S32, 2, 2, "alGetListeneri(Alenum)")
|
||||
{
|
||||
ALenum e = getEnum(argv[1], (Source|Get|Int));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alGetListeneri: invalid enum name '%s'", argv[1]);
|
||||
return(0);
|
||||
}
|
||||
|
||||
S32 value;
|
||||
alGetListeneri(e, &value);
|
||||
return(value);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Channel Volumes
|
||||
//--------------------------------------------------------------------------
|
||||
ConsoleFunction(alxGetChannelVolume, F32, 2, 2, "(int channel_id)\n\n"
|
||||
"@param channel_id ID of channel to fetch volume from.\n"
|
||||
"@return Volume of channel.")
|
||||
{
|
||||
U32 type = dAtoi(argv[1]);
|
||||
if(type >= Audio::NumAudioTypes)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alxGetChannelVolume: invalid channel '%d'", dAtoi(argv[1]));
|
||||
return(0.f);
|
||||
}
|
||||
|
||||
return(mAudioTypeVolume[type]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxSetChannelVolume, bool, 3, 3, "(int channel_id, float volume)\n\n"
|
||||
"@param channel_id ID of channel to set volume on.\n"
|
||||
"@param volume New volume of channel, from 0.0f-1.0f"
|
||||
)
|
||||
{
|
||||
U32 type = dAtoi(argv[1]);
|
||||
F32 volume = mClampF(dAtof(argv[2]), 0.f, 1.f);
|
||||
|
||||
if(type >= Audio::NumAudioTypes)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alxSetChannelVolume: channel '%d' out of range [0, %d]", dAtoi(argv[1]), Audio::NumAudioTypes);
|
||||
return false;
|
||||
}
|
||||
|
||||
mAudioTypeVolume[type] = volume;
|
||||
alxUpdateTypeGain(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxGetStreamPosition, F32, 2, 2, "alxGetStreamPosition(handle)" )
|
||||
{
|
||||
AUDIOHANDLE handle = dAtoi(argv[1]);
|
||||
|
||||
if(handle == NULL_AUDIOHANDLE)
|
||||
return -1;
|
||||
|
||||
return alxGetStreamPosition( handle );
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxGetStreamDuration, F32, 2, 2, "alxGetStreamDuration(handle)" )
|
||||
{
|
||||
AUDIOHANDLE handle = dAtoi(argv[1]);
|
||||
|
||||
if(handle == NULL_AUDIOHANDLE)
|
||||
return -1;
|
||||
|
||||
return alxGetStreamDuration( handle );
|
||||
}
|
||||
|
||||
|
||||
ConsoleFunctionGroupEnd(Audio);
|
64
engine/audio/audioStreamSource.h
Executable file
64
engine/audio/audioStreamSource.h
Executable file
@ -0,0 +1,64 @@
|
||||
//--------------------------------------------
|
||||
// audioStreamSource.h
|
||||
// header for streaming audio source
|
||||
//
|
||||
// Kurtis Seebaldt
|
||||
//--------------------------------------------
|
||||
|
||||
#ifndef _AUDIOSTREAMSOURCE_H_
|
||||
#define _AUDIOSTREAMSOURCE_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _PLATFORMAUDIO_H_
|
||||
#include "platform/platformAudio.h"
|
||||
#endif
|
||||
#ifndef _PLATFORMAL_H_
|
||||
#include "platform/platformAL.h"
|
||||
#endif
|
||||
#ifndef _AUDIOBUFFER_H_
|
||||
#include "audio/audioBuffer.h"
|
||||
#endif
|
||||
#ifndef _RESMANAGER_H_
|
||||
#include "core/resManager.h"
|
||||
#endif
|
||||
|
||||
#define NUMBUFFERS 16
|
||||
|
||||
class AudioStreamSource
|
||||
{
|
||||
public:
|
||||
//need this because subclasses are deleted through base pointer
|
||||
virtual ~AudioStreamSource() {}
|
||||
virtual bool initStream() = 0;
|
||||
virtual bool updateBuffers() = 0;
|
||||
virtual void freeStream() = 0;
|
||||
virtual F32 getElapsedTime() = 0;
|
||||
virtual F32 getTotalTime() = 0;
|
||||
//void clear();
|
||||
|
||||
AUDIOHANDLE mHandle;
|
||||
ALuint mSource;
|
||||
|
||||
Audio::Description mDescription;
|
||||
AudioSampleEnvironment *mEnvironment;
|
||||
|
||||
Point3F mPosition;
|
||||
Point3F mDirection;
|
||||
F32 mPitch;
|
||||
F32 mScore;
|
||||
U32 mCullTime;
|
||||
|
||||
bool bFinishedPlaying;
|
||||
bool bIsValid;
|
||||
|
||||
#ifdef TORQUE_OS_LINUX
|
||||
void checkPosition();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
const char* mFilename;
|
||||
};
|
||||
|
||||
#endif // _AUDIOSTREAMSOURCE_H_
|
33
engine/audio/audioStreamSourceFactory.cc
Executable file
33
engine/audio/audioStreamSourceFactory.cc
Executable file
@ -0,0 +1,33 @@
|
||||
//--------------------------------------
|
||||
// audioStreamSource.cc
|
||||
// implementation of streaming audio source
|
||||
//
|
||||
// Kurtis Seebaldt
|
||||
//--------------------------------------
|
||||
|
||||
#include "audio/audioStreamSourceFactory.h"
|
||||
|
||||
#include "audio/wavStreamSource.h"
|
||||
#ifndef TORQUE_NO_OGGVORBIS
|
||||
#include "audio/vorbisStreamSource.h"
|
||||
#include "audio/oggMixedStreamSource.h"
|
||||
#endif
|
||||
|
||||
AudioStreamSource* AudioStreamSourceFactory::getNewInstance(const char *filename)
|
||||
{
|
||||
#ifndef TORQUE_NO_OGGVORBIS
|
||||
|
||||
if(!dStricmp(filename, "oggMixedStream"))
|
||||
return new OggMixedStreamSource(filename);
|
||||
|
||||
S32 len = dStrlen(filename);
|
||||
|
||||
// Check filename extension and guess filetype from that
|
||||
|
||||
if(len > 3 && !dStricmp(filename + len - 4, ".wav"))
|
||||
return new WavStreamSource(filename);
|
||||
if(len > 3 && !dStricmp(filename + len - 4, ".ogg"))
|
||||
return new VorbisStreamSource(filename);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
30
engine/audio/audioStreamSourceFactory.h
Executable file
30
engine/audio/audioStreamSourceFactory.h
Executable file
@ -0,0 +1,30 @@
|
||||
//--------------------------------------------
|
||||
// audioStreamSource.h
|
||||
// header for streaming audio source
|
||||
//
|
||||
// Kurtis Seebaldt
|
||||
//--------------------------------------------
|
||||
|
||||
#ifndef _AUDIOSTREAMSOURCEFACTORY_H_
|
||||
#define _AUDIOSTREAMSOURCEFACTORY_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _PLATFORMAUDIO_H_
|
||||
#include "platform/platformAudio.h"
|
||||
#endif
|
||||
#ifndef _PLATFORMAL_H_
|
||||
#include "platform/platformAL.h"
|
||||
#endif
|
||||
#ifndef _AUDIOSTREAMSOURCE_H_
|
||||
#include "audio/audioStreamSource.h"
|
||||
#endif
|
||||
|
||||
class AudioStreamSourceFactory
|
||||
{
|
||||
public:
|
||||
static AudioStreamSource* getNewInstance(const char* filename);
|
||||
};
|
||||
|
||||
#endif // _AUDIOSTREAMSOURCEFACTORY_H_
|
133
engine/audio/oggMixedStreamSource.cc
Executable file
133
engine/audio/oggMixedStreamSource.cc
Executable file
@ -0,0 +1,133 @@
|
||||
//--------------------------------------
|
||||
//
|
||||
// This class is basically a buffer that is filled from
|
||||
// the ogg stream the theoraplayer has open
|
||||
//
|
||||
//--------------------------------------
|
||||
|
||||
#include "audio/oggMixedStreamSource.h"
|
||||
|
||||
OggMixedStreamSource::OggMixedStreamSource(const char *filename)
|
||||
{
|
||||
bIsValid = false;
|
||||
bBuffersAllocated = false;
|
||||
for(int i = 0; i < BUFFERCNT; i++)
|
||||
{
|
||||
mBufferList[i] = 0;
|
||||
m_fBufferInUse[i] = false;
|
||||
}
|
||||
|
||||
|
||||
mHandle = NULL_AUDIOHANDLE;
|
||||
mSource = NULL;
|
||||
|
||||
mFilename = filename;
|
||||
mPosition = Point3F(0.f,0.f,0.f);
|
||||
|
||||
dMemset(&mDescription, 0, sizeof(Audio::Description));
|
||||
mEnvironment = 0;
|
||||
mPosition.set(0.f,0.f,0.f);
|
||||
mDirection.set(0.f,1.f,0.f);
|
||||
mPitch = 1.f;
|
||||
mScore = 0.f;
|
||||
mCullTime = 0;
|
||||
|
||||
bFinishedPlaying = false;
|
||||
bIsValid = false;
|
||||
bBuffersAllocated = false;
|
||||
}
|
||||
|
||||
OggMixedStreamSource::~OggMixedStreamSource()
|
||||
{
|
||||
if(bIsValid)
|
||||
freeStream();
|
||||
}
|
||||
|
||||
bool OggMixedStreamSource::initStream()
|
||||
{
|
||||
alSourceStop(mSource);
|
||||
alSourcei(mSource, AL_BUFFER, 0);
|
||||
|
||||
// Clear Error Code
|
||||
alGetError();
|
||||
|
||||
alGenBuffers(BUFFERCNT, mBufferList);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
bBuffersAllocated = true;
|
||||
|
||||
alSourcei(mSource, AL_LOOPING, AL_FALSE);
|
||||
|
||||
bIsValid = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OggMixedStreamSource::updateBuffers()
|
||||
{
|
||||
// buffers are updated from theora player
|
||||
return true;
|
||||
}
|
||||
|
||||
void OggMixedStreamSource::freeStream()
|
||||
{
|
||||
// free the al buffers
|
||||
if(bBuffersAllocated)
|
||||
{
|
||||
alSourceStop(mSource);
|
||||
alSourcei(mSource, AL_BUFFER, 0);
|
||||
alDeleteBuffers(BUFFERCNT, mBufferList);
|
||||
|
||||
alGetError();
|
||||
|
||||
for(int i = 0; i < BUFFERCNT; i++)
|
||||
{
|
||||
mBufferList[i] = 0;
|
||||
m_fBufferInUse[i] = false;
|
||||
}
|
||||
|
||||
bBuffersAllocated = false;
|
||||
}
|
||||
}
|
||||
|
||||
ALuint OggMixedStreamSource::GetAvailableBuffer()
|
||||
{
|
||||
if(!bBuffersAllocated)
|
||||
return 0;
|
||||
|
||||
// test for unused buffers
|
||||
for(int i = 0; i < BUFFERCNT; i++)
|
||||
{
|
||||
if(!m_fBufferInUse[i])
|
||||
{
|
||||
m_fBufferInUse[i] = true;
|
||||
return mBufferList[i];
|
||||
}
|
||||
}
|
||||
|
||||
alGetError();
|
||||
|
||||
// test for processed buffers
|
||||
ALint processed;
|
||||
alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);
|
||||
|
||||
if(!processed)
|
||||
return 0; // no available buffers
|
||||
|
||||
ALuint BufferID;
|
||||
alSourceUnqueueBuffers(mSource, 1, &BufferID);
|
||||
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
return 0; // something went wrong..
|
||||
|
||||
return BufferID;
|
||||
}
|
||||
|
||||
bool OggMixedStreamSource::QueueBuffer(ALuint BufferID)
|
||||
{
|
||||
alSourceQueueBuffers(mSource, 1, &BufferID);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
return false;
|
||||
return true;
|
||||
}
|
50
engine/audio/oggMixedStreamSource.h
Executable file
50
engine/audio/oggMixedStreamSource.h
Executable file
@ -0,0 +1,50 @@
|
||||
//--------------------------------------------
|
||||
// oggMixedStreamSource.h
|
||||
// header for audio stream dummy class, basicly
|
||||
// an audio buffer filled remotely
|
||||
//
|
||||
//--------------------------------------------
|
||||
|
||||
#ifndef _OGGMIXEDSTREAMSOURCE_H_
|
||||
#define _OGGMIXEDSTREAMSOURCE_H_
|
||||
|
||||
#ifndef _AUDIOSTREAMSOURCE_H_
|
||||
#include "audio/audioStreamSource.h"
|
||||
#endif
|
||||
|
||||
#define BUFFERCNT 128
|
||||
|
||||
class OggMixedStreamSource: public AudioStreamSource
|
||||
{
|
||||
public:
|
||||
OggMixedStreamSource(const char *filename);
|
||||
virtual ~OggMixedStreamSource();
|
||||
|
||||
virtual bool initStream();
|
||||
virtual bool updateBuffers();
|
||||
virtual void freeStream();
|
||||
|
||||
ALuint GetAvailableBuffer();
|
||||
bool QueueBuffer(ALuint BufferID);
|
||||
void PlayStream();
|
||||
|
||||
virtual F32 getElapsedTime()
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
virtual F32 getTotalTime()
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
private:
|
||||
ALuint mBufferList[BUFFERCNT];
|
||||
bool m_fBufferInUse[BUFFERCNT];
|
||||
|
||||
bool bBuffersAllocated;
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
#endif // _OGGMIXEDSTREAMSOURCE_H_
|
1541
engine/audio/vorbisStream.cc
Executable file
1541
engine/audio/vorbisStream.cc
Executable file
File diff suppressed because it is too large
Load Diff
157
engine/audio/vorbisStream.h
Executable file
157
engine/audio/vorbisStream.h
Executable file
@ -0,0 +1,157 @@
|
||||
/********************************************************************
|
||||
* *
|
||||
* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
|
||||
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
|
||||
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
|
||||
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
|
||||
* *
|
||||
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 *
|
||||
* by the XIPHOPHORUS Company http://www.xiph.org/ *
|
||||
* *
|
||||
********************************************************************
|
||||
|
||||
function: stdio-based convenience library for opening/seeking/decoding
|
||||
|
||||
********************************************************************/
|
||||
|
||||
// modified vorbisfile to use Torque Streams
|
||||
// Kurtis Seebaldt
|
||||
|
||||
|
||||
#ifndef _OV_FILE_H_
|
||||
#define _OV_FILE_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include "vorbis/codec.h"
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _RESMANAGER_H_
|
||||
#include "core/resManager.h"
|
||||
#endif
|
||||
|
||||
/* The function prototypes for the callbacks are basically the same as for
|
||||
* the stdio functions fread, fseek, fclose, ftell.
|
||||
* The one difference is that the FILE * arguments have been replaced with
|
||||
* a void * - this is to be used as a pointer to whatever internal data these
|
||||
* functions might need. In the stdio case, it's just a FILE * cast to a void *
|
||||
*
|
||||
* If you use other functions, check the docs for these functions and return
|
||||
* the right values. For seek_func(), you *MUST* return -1 if the stream is
|
||||
* unseekable
|
||||
*/
|
||||
//typedef struct {
|
||||
// size_t (*read_func) (Stream *ptr, size_t size, size_t nmemb, void *datasource);
|
||||
// int (*seek_func) (Stream *datasource, ogg_int64_t offset, int whence);
|
||||
// int (*close_func) (void *datasource);
|
||||
// long (*tell_func) (void *datasource);
|
||||
//} ov_callbacks;
|
||||
|
||||
#define NOTOPEN 0
|
||||
#define PARTOPEN 1
|
||||
#define OPENED 2
|
||||
#define STREAMSET 3
|
||||
#define INITSET 4
|
||||
|
||||
class OggVorbis_File {
|
||||
public:
|
||||
Stream *datasource; /* Pointer to a FILE *, etc. */
|
||||
int seekable;
|
||||
ogg_int64_t offset;
|
||||
ogg_int64_t end;
|
||||
ogg_sync_state oy;
|
||||
|
||||
/* If the FILE handle isn't seekable (eg, a pipe), only the current
|
||||
stream appears */
|
||||
int links;
|
||||
ogg_int64_t *offsets;
|
||||
ogg_int64_t *dataoffsets;
|
||||
long *serialnos;
|
||||
ogg_int64_t *pcmlengths;
|
||||
vorbis_info *vi;
|
||||
vorbis_comment *vc;
|
||||
|
||||
/* Decoding working state local storage */
|
||||
ogg_int64_t pcm_offset;
|
||||
int ready_state;
|
||||
long current_serialno;
|
||||
int current_link;
|
||||
|
||||
double bittrack;
|
||||
double samptrack;
|
||||
|
||||
ogg_stream_state os; /* take physical pages, weld into a logical
|
||||
stream of packets */
|
||||
vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
|
||||
vorbis_block vb; /* local working space for packet->PCM decode */
|
||||
|
||||
// ov_callbacks callbacks;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class OggVorbisFile {
|
||||
|
||||
public:
|
||||
OggVorbisFile();
|
||||
~OggVorbisFile();
|
||||
int ov_clear();
|
||||
int ov_open(Stream *stream,char *initial,long ibytes);
|
||||
int ov_open_callbacks(Stream *datasource,
|
||||
char *initial, long ibytes);
|
||||
|
||||
int ov_test(Stream *stream,char *initial,long ibytes);
|
||||
int ov_test_callbacks(Stream *datasource,
|
||||
char *initial, long ibytes);
|
||||
int ov_test_open();
|
||||
|
||||
long ov_bitrate(int i);
|
||||
long ov_bitrate_instant();
|
||||
long ov_streams();
|
||||
long ov_seekable();
|
||||
long ov_serialnumber(int i);
|
||||
|
||||
ogg_int64_t ov_raw_total(int i);
|
||||
ogg_int64_t ov_pcm_total(int i);
|
||||
double ov_time_total(int i);
|
||||
|
||||
int ov_raw_seek(long pos);
|
||||
int ov_pcm_seek(ogg_int64_t pos);
|
||||
int ov_pcm_seek_page(ogg_int64_t pos);
|
||||
int ov_time_seek(double pos);
|
||||
int ov_time_seek_page(double pos);
|
||||
|
||||
ogg_int64_t ov_raw_tell();
|
||||
ogg_int64_t ov_pcm_tell();
|
||||
double ov_time_tell();
|
||||
|
||||
vorbis_info *ov_info(int link);
|
||||
vorbis_comment *ov_comment(int link);
|
||||
|
||||
long ov_read_float(float ***pcm_channels,
|
||||
int *bitstream);
|
||||
long ov_read(char *buffer,int length,
|
||||
int bigendianp,int *bitstream);
|
||||
|
||||
private:
|
||||
OggVorbis_File *vf;
|
||||
|
||||
long _get_data();
|
||||
void _seek_helper(long offset);
|
||||
long _get_next_page(ogg_page *og,int boundary);
|
||||
long _get_prev_page(ogg_page *og);
|
||||
int _bisect_forward_serialno(long begin,long searched,long end,long currentno,long m);
|
||||
int _fetch_headers(vorbis_info *vi,vorbis_comment *vc,long *serialno,ogg_page *og_ptr);
|
||||
void _prefetch_all_headers(long dataoffset);
|
||||
void _make_decode_ready();
|
||||
int _open_seekable2();
|
||||
void _decode_clear();
|
||||
int _process_packet(int readp);
|
||||
int _fseek64_wrap(Stream *stream,ogg_int64_t off,int whence);
|
||||
int _ov_open1(Stream *stream,char *initial,long ibytes);
|
||||
int _ov_open2();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
442
engine/audio/vorbisStreamSource.cc
Executable file
442
engine/audio/vorbisStreamSource.cc
Executable file
@ -0,0 +1,442 @@
|
||||
//--------------------------------------
|
||||
// vorbisStreamSource.cc
|
||||
// implementation of streaming audio source
|
||||
//
|
||||
// Kurtis Seebaldt
|
||||
//--------------------------------------
|
||||
|
||||
#include "audio/vorbisStreamSource.h"
|
||||
#include "vorbis/codec.h"
|
||||
|
||||
#define BUFFERSIZE 32768
|
||||
#define CHUNKSIZE 4096
|
||||
|
||||
#if defined(TORQUE_BIG_ENDIAN)
|
||||
#define ENDIAN 1
|
||||
#else
|
||||
#define ENDIAN 0
|
||||
#endif
|
||||
|
||||
extern const char * MusicPlayerStreamingHook(const AUDIOHANDLE & handle);
|
||||
|
||||
VorbisStreamSource::VorbisStreamSource(const char *filename)
|
||||
{
|
||||
stream = NULL;
|
||||
bIsValid = false;
|
||||
bBuffersAllocated = false;
|
||||
bVorbisFileInitialized = false;
|
||||
mBufferList[0] = 0;
|
||||
clear();
|
||||
|
||||
mFilename = filename;
|
||||
mPosition = Point3F(0.f,0.f,0.f);
|
||||
}
|
||||
|
||||
VorbisStreamSource::~VorbisStreamSource()
|
||||
{
|
||||
if(bReady && bIsValid)
|
||||
freeStream();
|
||||
}
|
||||
|
||||
void VorbisStreamSource::clear()
|
||||
{
|
||||
if(stream)
|
||||
freeStream();
|
||||
|
||||
mHandle = NULL_AUDIOHANDLE;
|
||||
mSource = NULL;
|
||||
|
||||
if(mBufferList[0] != 0)
|
||||
alDeleteBuffers(NUMBUFFERS, mBufferList);
|
||||
for(int i = 0; i < NUMBUFFERS; i++)
|
||||
mBufferList[i] = 0;
|
||||
|
||||
dMemset(&mDescription, 0, sizeof(Audio::Description));
|
||||
mEnvironment = 0;
|
||||
mPosition.set(0.f,0.f,0.f);
|
||||
mDirection.set(0.f,1.f,0.f);
|
||||
mPitch = 1.f;
|
||||
mScore = 0.f;
|
||||
mCullTime = 0;
|
||||
|
||||
bReady = false;
|
||||
bFinishedPlaying = false;
|
||||
bIsValid = false;
|
||||
bBuffersAllocated = false;
|
||||
bVorbisFileInitialized = false;
|
||||
}
|
||||
|
||||
bool VorbisStreamSource::initStream()
|
||||
{
|
||||
vorbis_info *vi;
|
||||
|
||||
ALint error;
|
||||
|
||||
bFinished = false;
|
||||
|
||||
// JMQ: changed buffer to static and doubled size. workaround for
|
||||
// https://206.163.64.242/mantis/view_bug_page.php?f_id=0000242
|
||||
static char data[BUFFERSIZE*2];
|
||||
|
||||
alSourceStop(mSource);
|
||||
alSourcei(mSource, AL_BUFFER, 0);
|
||||
|
||||
stream = ResourceManager->openStream(mFilename);
|
||||
if(stream != NULL)
|
||||
{
|
||||
if(vf.ov_open(stream, NULL, 0) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bVorbisFileInitialized = true;
|
||||
|
||||
//Read Vorbis File Info
|
||||
vi = vf.ov_info(-1);
|
||||
freq = vi->rate;
|
||||
|
||||
long samples = (long)vf.ov_pcm_total(-1);
|
||||
|
||||
if(vi->channels == 1)
|
||||
{
|
||||
format = AL_FORMAT_MONO16;
|
||||
DataSize = 2 * samples;
|
||||
}
|
||||
else
|
||||
{
|
||||
format = AL_FORMAT_STEREO16;
|
||||
DataSize = 4 * samples;
|
||||
}
|
||||
DataLeft = DataSize;
|
||||
|
||||
// Clear Error Code
|
||||
alGetError();
|
||||
|
||||
alGenBuffers(NUMBUFFERS, mBufferList);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
bBuffersAllocated = true;
|
||||
|
||||
int numBuffers = 0;
|
||||
for(int loop = 0; loop < NUMBUFFERS; loop++)
|
||||
{
|
||||
ALuint DataToRead = (DataLeft > BUFFERSIZE) ? BUFFERSIZE : DataLeft;
|
||||
if (DataToRead == DataLeft)
|
||||
bFinished = AL_TRUE;
|
||||
|
||||
long ret = oggRead(data, BUFFERSIZE, ENDIAN, ¤t_section);
|
||||
if(ret <= 0)
|
||||
{
|
||||
bFinished = AL_TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
DataLeft -= ret;
|
||||
alBufferData(mBufferList[loop], format, data, ret, freq);
|
||||
++numBuffers;
|
||||
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
if(bFinished)
|
||||
break;
|
||||
}
|
||||
|
||||
// Queue the buffers on the source
|
||||
alSourceQueueBuffers(mSource, NUMBUFFERS, mBufferList);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
alSourcei(mSource, AL_LOOPING, AL_FALSE);
|
||||
bReady = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bIsValid = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VorbisStreamSource::updateBuffers()
|
||||
{
|
||||
ALint processed;
|
||||
ALuint BufferID;
|
||||
ALint error;
|
||||
// JMQ: changed buffer to static and doubled size. workaround for
|
||||
// https://206.163.64.242/mantis/view_bug_page.php?f_id=0000242
|
||||
static char data[BUFFERSIZE*2];
|
||||
|
||||
// don't do anything if stream not loaded properly
|
||||
if(!bIsValid)
|
||||
return false;
|
||||
|
||||
if(bFinished && mDescription.mIsLooping)
|
||||
resetStream();
|
||||
|
||||
// reset AL error code
|
||||
alGetError();
|
||||
|
||||
#if 1 //def TORQUE_OS_LINUX
|
||||
// JMQ: this doesn't really help on linux. it may make things worse.
|
||||
// if it doesn't help on mac/win either, could disable it.
|
||||
ALint state;
|
||||
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
|
||||
if (state == AL_STOPPED)
|
||||
{
|
||||
// um, yeah. you should be playing
|
||||
// restart
|
||||
alSourcePlay(mSource);
|
||||
//#ifdef TORQUE_DEBUG
|
||||
//Con::errorf(">><<>><< THE MUSIC STOPPED >><<>><<");
|
||||
//#endif
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TORQUE_OS_LINUX
|
||||
checkPosition();
|
||||
#endif
|
||||
|
||||
// Get status
|
||||
alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);
|
||||
|
||||
// If some buffers have been played, unqueue them and load new audio into them, then add them to the queue
|
||||
if (processed > 0)
|
||||
{
|
||||
while (processed)
|
||||
{
|
||||
alSourceUnqueueBuffers(mSource, 1, &BufferID);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
if (!bFinished)
|
||||
{
|
||||
ALuint DataToRead = (DataLeft > BUFFERSIZE) ? BUFFERSIZE : DataLeft;
|
||||
|
||||
if (DataToRead == DataLeft)
|
||||
bFinished = AL_TRUE;
|
||||
|
||||
long ret = oggRead(data, BUFFERSIZE, ENDIAN, ¤t_section);
|
||||
if(ret > 0)
|
||||
{
|
||||
DataLeft -= ret;
|
||||
|
||||
alBufferData(BufferID, format, data, ret, freq);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
// Queue buffer
|
||||
alSourceQueueBuffers(mSource, 1, &BufferID);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
}
|
||||
|
||||
processed--;
|
||||
|
||||
if(bFinished && mDescription.mIsLooping)
|
||||
{
|
||||
resetStream();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffersinqueue--;
|
||||
processed--;
|
||||
|
||||
if (buffersinqueue == 0)
|
||||
{
|
||||
bFinishedPlaying = AL_TRUE;
|
||||
return AL_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VorbisStreamSource::freeStream()
|
||||
{
|
||||
bReady = false;
|
||||
|
||||
if(stream != NULL)
|
||||
ResourceManager->closeStream(stream);
|
||||
|
||||
stream = NULL;
|
||||
|
||||
if(bBuffersAllocated)
|
||||
{
|
||||
if(mBufferList[0] != 0)
|
||||
alDeleteBuffers(NUMBUFFERS, mBufferList);
|
||||
for(int i = 0; i < NUMBUFFERS; i++)
|
||||
mBufferList[i] = 0;
|
||||
|
||||
bBuffersAllocated = false;
|
||||
}
|
||||
|
||||
if(bVorbisFileInitialized)
|
||||
{
|
||||
vf.ov_clear();
|
||||
bVorbisFileInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
void VorbisStreamSource::resetStream()
|
||||
{
|
||||
// MusicPlayerStreamingHook allow you to create a handler
|
||||
// where you can rotate through streaming files
|
||||
// Comment in and provide hook if desired.
|
||||
//
|
||||
//const char * newFile = MusicPlayerStreamingHook(mHandle);
|
||||
//if (newFile)
|
||||
//{
|
||||
// setNewFile(newFile);
|
||||
// return;
|
||||
//}
|
||||
//else
|
||||
{
|
||||
vf.ov_pcm_seek(0);
|
||||
DataLeft = DataSize;
|
||||
bFinished = AL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void VorbisStreamSource::setNewFile(const char * file)
|
||||
{
|
||||
//---------------------
|
||||
// close down old file...
|
||||
//---------------------
|
||||
|
||||
if(stream != NULL)
|
||||
{
|
||||
ResourceManager->closeStream(stream);
|
||||
stream = NULL;
|
||||
}
|
||||
|
||||
if(bVorbisFileInitialized)
|
||||
{
|
||||
vf.ov_clear();
|
||||
bVorbisFileInitialized = false;
|
||||
}
|
||||
|
||||
//---------------------
|
||||
// open up new file...
|
||||
//---------------------
|
||||
|
||||
mFilename = file;
|
||||
stream = ResourceManager->openStream(mFilename);
|
||||
if(stream != NULL)
|
||||
{
|
||||
if(vf.ov_open(stream, NULL, 0) < 0)
|
||||
{
|
||||
bFinished = AL_TRUE;
|
||||
bVorbisFileInitialized = false;
|
||||
bIsValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//Read Vorbis File Info
|
||||
vorbis_info * vi = vf.ov_info(-1);
|
||||
freq = vi->rate;
|
||||
|
||||
long samples = (long)vf.ov_pcm_total(-1);
|
||||
|
||||
if(vi->channels == 1)
|
||||
{
|
||||
format = AL_FORMAT_MONO16;
|
||||
DataSize = 2 * samples;
|
||||
}
|
||||
else
|
||||
{
|
||||
format = AL_FORMAT_STEREO16;
|
||||
DataSize = 4 * samples;
|
||||
}
|
||||
DataLeft = DataSize;
|
||||
|
||||
// Clear Error Code
|
||||
alGetError();
|
||||
|
||||
bFinished = AL_FALSE;
|
||||
bVorbisFileInitialized = true;
|
||||
bIsValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ov_read() only returns a maximum of one page worth of data
|
||||
// this helper function will repeat the read until buffer is full
|
||||
long VorbisStreamSource::oggRead(char *buffer,int length,
|
||||
int bigendianp,int *bitstream)
|
||||
{
|
||||
long bytesRead = 0;
|
||||
long totalBytes = 0;
|
||||
long offset = 0;
|
||||
long bytesToRead = 0;
|
||||
|
||||
while((offset) < length)
|
||||
{
|
||||
if((length - offset) < CHUNKSIZE)
|
||||
bytesToRead = length - offset;
|
||||
else
|
||||
bytesToRead = CHUNKSIZE;
|
||||
|
||||
bytesRead = vf.ov_read(buffer, bytesToRead, bigendianp, bitstream);
|
||||
//#ifdef TORQUE_OS_LINUX
|
||||
#if 1 // Might fix mac audio issue and possibly others...based on references, this looks like correct action
|
||||
// linux ver will hang on exit after a stream loop if we don't
|
||||
// do this
|
||||
if (bytesRead == OV_HOLE)
|
||||
// retry, see:
|
||||
// http://www.xiph.org/archives/vorbis-dev/200102/0163.html
|
||||
// http://www.mit.edu/afs/sipb/user/xiphmont/ogg-sandbox/vorbis/doc/vorbis-errors.txt
|
||||
continue;
|
||||
#endif
|
||||
|
||||
if(bytesRead <= 0)
|
||||
break;
|
||||
offset += bytesRead;
|
||||
buffer += bytesRead;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
#ifdef TORQUE_OS_LINUX
|
||||
|
||||
// JMQ: OpenAL sometimes replaces the stream source's position with its own
|
||||
// nifty value, causing the music to pan around the listener. how nice.
|
||||
// this function checks to see if the current source position in openal
|
||||
// is near the initial position, and slams it to the correct value if it
|
||||
// is wrong.
|
||||
|
||||
// This is a bad place to put this, but I don't feel like adding a new
|
||||
// .cc file. And since this is an incredibly lame hack to
|
||||
// workaround a stupid OpenAL bug, I see no point in overengineering it.
|
||||
|
||||
void AudioStreamSource::checkPosition()
|
||||
{
|
||||
ALfloat pos[3];
|
||||
alGetSourcefv(mSource, AL_POSITION, pos);
|
||||
|
||||
// just compute the difference between the openal pos and the
|
||||
// correct pos. it better be pretty friggin small.
|
||||
Point3F openalPos(pos[0], pos[1], pos[2]);
|
||||
F32 slopDist = 0.0001f;
|
||||
|
||||
F32 dist = mFabs((openalPos - mPosition).len());
|
||||
if (dist > slopDist)
|
||||
// slam!
|
||||
alSource3f(mSource, AL_POSITION, mPosition.x, mPosition.y, mPosition.z);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
F32 VorbisStreamSource::getElapsedTime()
|
||||
{
|
||||
return vf.ov_time_tell();
|
||||
}
|
||||
|
||||
F32 VorbisStreamSource::getTotalTime()
|
||||
{
|
||||
return vf.ov_time_total(-1);
|
||||
}
|
58
engine/audio/vorbisStreamSource.h
Executable file
58
engine/audio/vorbisStreamSource.h
Executable file
@ -0,0 +1,58 @@
|
||||
//--------------------------------------------
|
||||
// vorbisStreamSource.h
|
||||
// header for streaming audio source for Ogg Vorbis
|
||||
//
|
||||
// Kurtis Seebaldt
|
||||
//--------------------------------------------
|
||||
|
||||
#ifndef _VORBISSTREAMSOURCE_H_
|
||||
#define _VORBISSTREAMSOURCE_H_
|
||||
|
||||
#ifndef _AUDIOSTREAMSOURCE_H_
|
||||
#include "audio/audioStreamSource.h"
|
||||
#endif
|
||||
|
||||
#include "audio/vorbisStream.h"
|
||||
|
||||
class VorbisStreamSource: public AudioStreamSource
|
||||
{
|
||||
public:
|
||||
VorbisStreamSource(const char *filename);
|
||||
virtual ~VorbisStreamSource();
|
||||
|
||||
virtual bool initStream();
|
||||
virtual bool updateBuffers();
|
||||
virtual void freeStream();
|
||||
virtual F32 getElapsedTime();
|
||||
virtual F32 getTotalTime();
|
||||
|
||||
private:
|
||||
ALuint mBufferList[NUMBUFFERS];
|
||||
S32 mNumBuffers;
|
||||
S32 mBufferSize;
|
||||
Stream *stream;
|
||||
|
||||
bool bReady;
|
||||
bool bFinished;
|
||||
|
||||
ALenum format;
|
||||
ALsizei size;
|
||||
ALsizei freq;
|
||||
|
||||
ALuint DataSize;
|
||||
ALuint DataLeft;
|
||||
ALuint buffersinqueue;
|
||||
|
||||
bool bBuffersAllocated;
|
||||
bool bVorbisFileInitialized;
|
||||
|
||||
int current_section;
|
||||
OggVorbisFile vf;
|
||||
|
||||
void clear();
|
||||
long oggRead(char *buffer,int length, int bigendianp,int *bitstream);
|
||||
void resetStream();
|
||||
void setNewFile(const char * file);
|
||||
};
|
||||
|
||||
#endif // _VORBISSTREAMSOURCE_H_
|
334
engine/audio/wavStreamSource.cc
Executable file
334
engine/audio/wavStreamSource.cc
Executable file
@ -0,0 +1,334 @@
|
||||
//--------------------------------------
|
||||
// audioStreamSource.cc
|
||||
// implementation of streaming audio source
|
||||
//
|
||||
// Kurtis Seebaldt
|
||||
//--------------------------------------
|
||||
|
||||
#include "audio/wavStreamSource.h"
|
||||
|
||||
#define BUFFERSIZE 32768
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ALubyte riff[4]; // 'RIFF'
|
||||
ALsizei riffSize;
|
||||
ALubyte wave[4]; // 'WAVE'
|
||||
ALubyte fmt[4]; // 'fmt '
|
||||
ALuint fmtSize;
|
||||
ALushort Format;
|
||||
ALushort Channels;
|
||||
ALuint SamplesPerSec;
|
||||
ALuint BytesPerSec;
|
||||
ALushort BlockAlign;
|
||||
ALushort BitsPerSample;
|
||||
ALubyte data[4]; // 'data'
|
||||
ALuint dataSize;
|
||||
} WAVE_Struct;
|
||||
|
||||
/// WAV File-header
|
||||
struct WAVFileHdr
|
||||
{
|
||||
ALubyte id[4];
|
||||
ALsizei size;
|
||||
ALubyte type[4];
|
||||
};
|
||||
|
||||
//// WAV Fmt-header
|
||||
struct WAVFmtHdr
|
||||
{
|
||||
ALushort format;
|
||||
ALushort channels;
|
||||
ALuint samplesPerSec;
|
||||
ALuint bytesPerSec;
|
||||
ALushort blockAlign;
|
||||
ALushort bitsPerSample;
|
||||
};
|
||||
|
||||
/// WAV FmtEx-header
|
||||
struct WAVFmtExHdr
|
||||
{
|
||||
ALushort size;
|
||||
ALushort samplesPerBlock;
|
||||
};
|
||||
|
||||
/// WAV Smpl-header
|
||||
struct WAVSmplHdr
|
||||
{
|
||||
ALuint manufacturer;
|
||||
ALuint product;
|
||||
ALuint samplePeriod;
|
||||
ALuint note;
|
||||
ALuint fineTune;
|
||||
ALuint SMPTEFormat;
|
||||
ALuint SMPTEOffest;
|
||||
ALuint loops;
|
||||
ALuint samplerData;
|
||||
struct
|
||||
{
|
||||
ALuint identifier;
|
||||
ALuint type;
|
||||
ALuint start;
|
||||
ALuint end;
|
||||
ALuint fraction;
|
||||
ALuint count;
|
||||
} loop[1];
|
||||
};
|
||||
|
||||
/// WAV Chunk-header
|
||||
struct WAVChunkHdr
|
||||
{
|
||||
ALubyte id[4];
|
||||
ALuint size;
|
||||
};
|
||||
|
||||
|
||||
|
||||
WavStreamSource::WavStreamSource(const char *filename) {
|
||||
stream = NULL;
|
||||
bIsValid = false;
|
||||
bBuffersAllocated = false;
|
||||
mBufferList[0] = 0;
|
||||
clear();
|
||||
|
||||
mFilename = filename;
|
||||
mPosition = Point3F(0.f,0.f,0.f);
|
||||
}
|
||||
|
||||
WavStreamSource::~WavStreamSource() {
|
||||
if(bReady && bIsValid)
|
||||
freeStream();
|
||||
}
|
||||
|
||||
void WavStreamSource::clear()
|
||||
{
|
||||
if(stream)
|
||||
freeStream();
|
||||
|
||||
mHandle = NULL_AUDIOHANDLE;
|
||||
mSource = NULL;
|
||||
|
||||
if(mBufferList[0] != 0)
|
||||
alDeleteBuffers(NUMBUFFERS, mBufferList);
|
||||
for(int i = 0; i < NUMBUFFERS; i++)
|
||||
mBufferList[i] = 0;
|
||||
|
||||
dMemset(&mDescription, 0, sizeof(Audio::Description));
|
||||
mEnvironment = 0;
|
||||
mPosition.set(0.f,0.f,0.f);
|
||||
mDirection.set(0.f,1.f,0.f);
|
||||
mPitch = 1.f;
|
||||
mScore = 0.f;
|
||||
mCullTime = 0;
|
||||
|
||||
bReady = false;
|
||||
bFinishedPlaying = false;
|
||||
bIsValid = false;
|
||||
bBuffersAllocated = false;
|
||||
}
|
||||
|
||||
bool WavStreamSource::initStream() {
|
||||
WAVChunkHdr chunkHdr;
|
||||
WAVFmtExHdr fmtExHdr;
|
||||
WAVFileHdr fileHdr;
|
||||
WAVSmplHdr smplHdr;
|
||||
WAVFmtHdr fmtHdr;
|
||||
|
||||
WAVE_Struct wave;
|
||||
|
||||
ALint error;
|
||||
|
||||
bFinished = false;
|
||||
|
||||
char data[BUFFERSIZE];
|
||||
|
||||
alSourceStop(mSource);
|
||||
alSourcei(mSource, AL_BUFFER, 0);
|
||||
|
||||
stream = ResourceManager->openStream(mFilename);
|
||||
if(stream != NULL) {
|
||||
stream->read(4, &fileHdr.id[0]);
|
||||
stream->read(&fileHdr.size);
|
||||
stream->read(4, &fileHdr.type[0]);
|
||||
|
||||
stream->read(4, &chunkHdr.id[0]);
|
||||
stream->read(&chunkHdr.size);
|
||||
|
||||
// WAV Format header
|
||||
stream->read(&fmtHdr.format);
|
||||
stream->read(&fmtHdr.channels);
|
||||
stream->read(&fmtHdr.samplesPerSec);
|
||||
stream->read(&fmtHdr.bytesPerSec);
|
||||
stream->read(&fmtHdr.blockAlign);
|
||||
stream->read(&fmtHdr.bitsPerSample);
|
||||
|
||||
format=(fmtHdr.channels==1?
|
||||
(fmtHdr.bitsPerSample==8?AL_FORMAT_MONO8:AL_FORMAT_MONO16):
|
||||
(fmtHdr.bitsPerSample==8?AL_FORMAT_STEREO8:AL_FORMAT_STEREO16));
|
||||
freq=fmtHdr.samplesPerSec;
|
||||
|
||||
stream->read(4, &chunkHdr.id[0]);
|
||||
stream->read(&chunkHdr.size);
|
||||
|
||||
DataSize = chunkHdr.size;
|
||||
DataLeft = DataSize;
|
||||
dataStart = stream->getPosition();
|
||||
|
||||
// Clear Error Code
|
||||
alGetError();
|
||||
|
||||
alGenBuffers(NUMBUFFERS, mBufferList);
|
||||
if ((error = alGetError()) != AL_NO_ERROR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bBuffersAllocated = true;
|
||||
|
||||
int numBuffers = 0;
|
||||
for(int loop = 0; loop < NUMBUFFERS; loop++)
|
||||
{
|
||||
ALuint DataToRead = (DataLeft > BUFFERSIZE) ? BUFFERSIZE : DataLeft;
|
||||
|
||||
if (DataToRead == DataLeft)
|
||||
bFinished = AL_TRUE;
|
||||
stream->read(DataToRead, data);
|
||||
DataLeft -= DataToRead;
|
||||
alBufferData(mBufferList[loop], format, data, DataToRead, freq);
|
||||
if ((error = alGetError()) != AL_NO_ERROR) {
|
||||
return false;
|
||||
}
|
||||
++numBuffers;
|
||||
if(bFinished)
|
||||
break;
|
||||
}
|
||||
|
||||
// Queue the buffers on the source
|
||||
alSourceQueueBuffers(mSource, numBuffers, mBufferList);
|
||||
if ((error = alGetError()) != AL_NO_ERROR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
buffersinqueue = numBuffers;
|
||||
alSourcei(mSource, AL_LOOPING, AL_FALSE);
|
||||
bReady = true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
bIsValid = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WavStreamSource::updateBuffers() {
|
||||
|
||||
ALint processed;
|
||||
ALuint BufferID;
|
||||
ALint error;
|
||||
char data[BUFFERSIZE];
|
||||
|
||||
// don't do anything if buffer isn't initialized
|
||||
if(!bIsValid)
|
||||
return false;
|
||||
|
||||
if(bFinished && mDescription.mIsLooping) {
|
||||
resetStream();
|
||||
}
|
||||
|
||||
// reset AL error code
|
||||
alGetError();
|
||||
|
||||
// Get status
|
||||
alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);
|
||||
|
||||
// If some buffers have been played, unqueue them and load new audio into them, then add them to the queue
|
||||
if (processed > 0)
|
||||
{
|
||||
|
||||
while (processed)
|
||||
{
|
||||
alSourceUnqueueBuffers(mSource, 1, &BufferID);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
if (!bFinished)
|
||||
{
|
||||
ALuint DataToRead = (DataLeft > BUFFERSIZE) ? BUFFERSIZE : DataLeft;
|
||||
|
||||
if (DataToRead == DataLeft) {
|
||||
bFinished = AL_TRUE;
|
||||
}
|
||||
|
||||
stream->read(DataToRead, data);
|
||||
DataLeft -= DataToRead;
|
||||
|
||||
alBufferData(BufferID, format, data, DataToRead, freq);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
// Queue buffer
|
||||
alSourceQueueBuffers(mSource, 1, &BufferID);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
processed--;
|
||||
|
||||
if(bFinished && mDescription.mIsLooping) {
|
||||
resetStream();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffersinqueue--;
|
||||
processed--;
|
||||
|
||||
if (buffersinqueue == 0)
|
||||
{
|
||||
bFinishedPlaying = AL_TRUE;
|
||||
return AL_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
void WavStreamSource::freeStream() {
|
||||
bReady = false;
|
||||
|
||||
|
||||
if(stream != NULL)
|
||||
ResourceManager->closeStream(stream);
|
||||
stream = NULL;
|
||||
|
||||
if(bBuffersAllocated) {
|
||||
if(mBufferList[0] != 0)
|
||||
alDeleteBuffers(NUMBUFFERS, mBufferList);
|
||||
for(int i = 0; i < NUMBUFFERS; i++)
|
||||
mBufferList[i] = 0;
|
||||
|
||||
bBuffersAllocated = false;
|
||||
}
|
||||
}
|
||||
|
||||
void WavStreamSource::resetStream() {
|
||||
stream->setPosition(dataStart);
|
||||
DataLeft = DataSize;
|
||||
bFinished = AL_FALSE;
|
||||
}
|
||||
|
||||
#include "console/console.h"
|
||||
|
||||
F32 WavStreamSource::getElapsedTime()
|
||||
{
|
||||
Con::warnf( "GetElapsedTime not implemented in WaveStreams yet" );
|
||||
return -1.f;
|
||||
}
|
||||
|
||||
F32 WavStreamSource::getTotalTime()
|
||||
{
|
||||
Con::warnf( "GetTotalTime not implemented in WaveStreams yet" );
|
||||
return -1.f;
|
||||
}
|
49
engine/audio/wavStreamSource.h
Executable file
49
engine/audio/wavStreamSource.h
Executable file
@ -0,0 +1,49 @@
|
||||
//--------------------------------------------
|
||||
// wavStreamSource.h
|
||||
// header for streaming audio source for WAV
|
||||
//--------------------------------------------
|
||||
|
||||
#ifndef _WAVSTREAMSOURCE_H_
|
||||
#define _WAVSTREAMSOURCE_H_
|
||||
|
||||
#ifndef _AUDIOSTREAMSOURCE_H_
|
||||
#include "audio/audioStreamSource.h"
|
||||
#endif
|
||||
|
||||
class WavStreamSource: public AudioStreamSource
|
||||
{
|
||||
public:
|
||||
WavStreamSource(const char *filename);
|
||||
virtual ~WavStreamSource();
|
||||
|
||||
virtual bool initStream();
|
||||
virtual bool updateBuffers();
|
||||
virtual void freeStream();
|
||||
virtual F32 getElapsedTime();
|
||||
virtual F32 getTotalTime();
|
||||
|
||||
private:
|
||||
ALuint mBufferList[NUMBUFFERS];
|
||||
S32 mNumBuffers;
|
||||
S32 mBufferSize;
|
||||
Stream *stream;
|
||||
|
||||
bool bReady;
|
||||
bool bFinished;
|
||||
|
||||
ALenum format;
|
||||
ALsizei size;
|
||||
ALsizei freq;
|
||||
|
||||
ALuint DataSize;
|
||||
ALuint DataLeft;
|
||||
ALuint dataStart;
|
||||
ALuint buffersinqueue;
|
||||
|
||||
bool bBuffersAllocated;
|
||||
|
||||
void clear();
|
||||
void resetStream();
|
||||
};
|
||||
|
||||
#endif // _AUDIOSTREAMSOURCE_H_
|
Reference in New Issue
Block a user