#include <AL/al.h>
#include <AL/alc.h>
#include <AL/alu.h>
#include <AL/alure.h>
#include <AL/efx.h>
#include <al/EFX-Util.h>
#include <al/efx-creative.h>
//#include <al/xram.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
// TODO EFX/EAX troubleshooting, stream, capture, network transceive, examine timing (add firing time, etc)
#include <sndfile.h>
#define RANGE(a, b, c) ((b) < (a) ? (a) : ((b) > (c) ? (c) : (b)))
// EFX help (where we got the .h): Dmytry Lavrov (Public Domain code samples)
//EAX
//const GUID DSPROPSETID_EAX20_ListenerProperties = { 0x306a6a8, 0xb224, 0x11d2, { 0x99, 0xe5, 0x0, 0x0, 0xe8, 0xd8, 0xc7, 0x22 } };
//const GUID DSPROPSETID_EAX20_BufferProperties = { 0x306a6a7, 0xb224, 0x11d2, { 0x99, 0xe5, 0x0, 0x0, 0xe8, 0xd8, 0xc7, 0x22 } };
//#ifndef(AL_FORMAT_VORBIS_EXT)
//#define AL_FORMAT_VORBIS_EXT 0x10003
//#endif
#ifndef ALC_EXT_EFX
#define AL_FILTER_TYPE 0x8001
#define AL_EFFECT_TYPE 0x8001
#define AL_FILTER_NULL 0x0000
#define AL_FILTER_LOWPASS 0x0001
#define AL_FILTER_HIGHPASS 0x0002
#define AL_FILTER_BANDPASS 0x0003
#define AL_EFFECT_NULL 0x0000
#define AL_EFFECT_EAXREVERB 0x8000
#define AL_EFFECT_REVERB 0x0001
#define AL_EFFECT_CHORUS 0x0002
#define AL_EFFECT_DISTORTION 0x0003
#define AL_EFFECT_ECHO 0x0004
#define AL_EFFECT_FLANGER 0x0005
#define AL_EFFECT_FREQUENCY_SHIFTER 0x0006
#define AL_EFFECT_VOCAL_MORPHER 0x0007
#define AL_EFFECT_PITCH_SHIFTER 0x0008
#define AL_EFFECT_RING_MODULATOR 0x0009
#define AL_EFFECT_AUTOWAH 0x000A
#define AL_EFFECT_COMPRESSOR 0x000B
#define AL_EFFECT_EQUALIZER 0x000C
#define ALC_EFX_MAJOR_VERSION 0x20001
#define ALC_EFX_MINOR_VERSION 0x20002
#define ALC_MAX_AUXILIARY_SENDS 0x20003
#endif
#ifndef ALC_MONO_SOURCES
#define ALC_MONO_SOURCES 0x1010
#define ALC_STEREO_SOURCES 0x1011
#endif
#ifndef AL_EFFECT_EAXREVERB
#define AL_EFFECT_EAXREVERB 0x8000
#endif
#ifndef ALC_CAPTURE_DEVICE_SPECIFIER
/**
* The Specifier string for default device
*/
#define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004
#define ALC_DEVICE_SPECIFIER 0x1005
#define ALC_EXTENSIONS 0x1006
#define ALC_MAJOR_VERSION 0x1000
#define ALC_MINOR_VERSION 0x1001
#define ALC_ATTRIBUTES_SIZE 0x1002
#define ALC_ALL_ATTRIBUTES 0x1003
/**
* Capture extension
*/
#define ALC_CAPTURE_DEVICE_SPECIFIER 0x310
#define ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER 0x311
#define ALC_CAPTURE_SAMPLES 0x312
#endif
import "ecere"
// Be very careful with these two parameters
// It is very dependant on the audio hardware your
// user is using. It you get too large, it may work
// on one persons system but not on another.
// TODO Write a hardware test !
#define MAX_AUDIO_BUFFERS 64
#define MAX_AUDIO_SOURCES 16
enum ALState { empty=0, stopped=1, paused=2, playing=3 };
enum ALSoundFormat { mono8=AL_FORMAT_MONO8, mono16=AL_FORMAT_MONO16, stereo8=AL_FORMAT_STEREO8, stereo16=AL_FORMAT_STEREO16 };
/*
* Compare strings, case insensitive.
*/
#define LOWER(c) ((c) >= 'A' && (c) <= 'Z' ? (c)+'a'-'A' : (c))
bool contains( const char *astr, const char *bstr )
{
if ( astr == null ) { printf( "Str_cmp: null astr.\n" ); return false; }
if ( bstr == null ) { printf( "Str_cmp: null bstr.\n" ); return false; }
for ( ; *astr || *bstr; astr++, bstr++ ) if ( LOWER(*astr) != LOWER(*bstr) ) return false;
return true;
}
// Used during Init(), maybe it shouldn't die() here...
static void checkForErrors(void)
{
ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
ALCenum ALCerror = alcGetError(device);
ALenum ALerror;
if(ALCerror != ALC_NO_ERROR) printf( "ALC %s\n", alcGetString(device, ALCerror));
ALerror = alGetError();
if(ALerror != AL_NO_ERROR) printf("AL %s\n", alGetString(ALerror));
}
static const int indentation = 4;
static const int maxmimumWidth = 79;
static void printChar(int c, int *width) { putchar(c); *width = ((c == '\n') ? 0 : ((*width) + 1)); }
static void indent(int *width) { int i; for(i = 0; i < indentation; i++) printChar(' ', width); }
static void printList(const char *header, char separator, const char *list)
{
int width = 0, start = 0, end = 0;
printf("%s:\n", header);
if(list == NULL || list[0] == '\0') return;
indent(&width);
while(1) {
if(list[end] == separator || list[end] == '\0') {
if(width + end - start + 2 > maxmimumWidth) {
printChar('\n', &width);
indent(&width);
}
while(start < end) {
printChar(list[start], &width);
start++;
}
if(list[end] == '\0') break;
start++;
end++;
if(list[end] == '\0') break;
printChar(',', &width);
printChar(' ', &width);
}
end++;
}
printChar('\n', &width);
}
struct SoundVector
{
float x,y,z,a,b,c;
//SoundVector() { y=1.0f; }
float *asFloat3( ) { return &x; }
float *asFloat6( ) { return &x; }
};
//int unusedBuffers, unusedSources;
void GetALCErrorString(ALenum err){
switch(err) {
case ALC_NO_ERROR: printf("AL_NO_ERROR"); break;
case ALC_INVALID_DEVICE: printf("ALC_INVALID_DEVICE"); break;
case ALC_INVALID_CONTEXT: printf("ALC_INVALID_CONTEXT"); break;
case ALC_INVALID_ENUM: printf("ALC_INVALID_ENUM"); break;
case ALC_INVALID_VALUE: printf("ALC_INVALID_VALUE"); break;
case ALC_OUT_OF_MEMORY: printf("ALC_OUT_OF_MEMORY"); break;
}
}
bool ALError( char *tb ) {
ALenum errCode;
if ( ( errCode = alGetError() ) != AL_NO_ERROR ) {
printf( "Audio %s Error #%d %s\n", tb, errCode, (char *) alGetString( errCode ) );
return true;
}
return false;
}
class ALBuffer : ListItem {
ALuint id;
inline bool valid() { if ( alIsBuffer( id ) ) return true; }
float duration() {
ALint bits, channels, freq, size;
alGetBufferi(id, AL_BITS, &bits);
alGetBufferi(id, AL_CHANNELS, &channels);
alGetBufferi(id, AL_FREQUENCY, &freq);
alGetBufferi(id, AL_SIZE, &size);
if(ALError("ALBuffer:duration()")) { return 0.0f; }
return (float) ((size / channels * 8 / bits ) / freq);
}
bool Close() {
if ( valid() ) alDeleteBuffers(1, &(id)); if ( ALError( "ALBuffer:Close:alDeleteBuffers") ) return false; //unusedBuffers++;
}
~ALBuffer() { Close(); }
}
class ALBuffers : LinkList<ALBuffer> {
void ClearInvalid() {
for ( b:this ) { if ( !b.valid() ) this.Delete(b); }
}
ALBuffer New() {
ALBuffer b { };
alGetError();
//if ( unusedBuffers <= 0 ) { printf( "No unused buffers available when requesting New() buffer\n" ); delete b; return null; }
b= ALBuffer { };
alGenBuffers(1,&(b.id)); if(ALError("ALBuffer:alGenBuffers")) { delete b; return null; } //unusedBuffers--;
//printf( "#%d buffer created with ID of %d\n", unusedBuffers, b.id );
this.Add(b);
return b;
}
};
class ALSource : ListItem {
ALuint id;
SoundVector position, velocity;
float pitch,gain;
float rolloff;
ALboolean loop,relative;
ALState state;
ALSource() { position = SoundVector { }; velocity = SoundVector { }; pitch=1.0f; gain=1.0f; loop=AL_FALSE; relative=AL_FALSE; }
inline bool valid() { if ( alIsSource( id ) ) return true; }
void bindBuffer( ALBuffer buffer ) {
alGetError();
alSourcei (id, AL_BUFFER, buffer.id );
alSourcef (id, AL_PITCH, pitch );
alSourcef (id, AL_GAIN, gain );
alSourcefv(id, AL_POSITION, position.asFloat3() );
alSourcefv(id, AL_VELOCITY, velocity.asFloat3() );
alSourcef (id, AL_ROLLOFF_FACTOR, rolloff );
alSourcei (id, AL_LOOPING, loop );
alSourcei (id, AL_SOURCE_RELATIVE, relative );
ALError("ALSource:bindBuffer() had an error");
state=stopped;
}
void bindSound( ALSound sound ) {
alGetError();
alSourcei (id, AL_BUFFER, sound.waveform.buffer.id );
alSourcef (id, AL_PITCH, sound.pitch );
alSourcef (id, AL_GAIN, sound.gain );
alSourcefv(id, AL_POSITION, sound.position.asFloat3() );
alSourcefv(id, AL_VELOCITY, sound.velocity.asFloat3() );
alSourcef (id, AL_ROLLOFF_FACTOR, rolloff );
alSourcei (id, AL_LOOPING, sound.loop );
alSourcei (id, AL_SOURCE_RELATIVE, sound.relative );
ALError("ALSource:bindSound() had an error");
state=stopped;
}
void bindStream( ALUREStream stream ) {
alGetError();
alSourcef (id, AL_PITCH, pitch );
alSourcef (id, AL_GAIN, gain );
alSourcefv(id, AL_POSITION, position.asFloat3() );
alSourcefv(id, AL_VELOCITY, velocity.asFloat3() );
alSourcef (id, AL_ROLLOFF_FACTOR, rolloff );
alSourcei (id, AL_LOOPING, AL_FALSE ); // handled elsewhere
alSourcei (id, AL_SOURCE_RELATIVE, relative );
stream.source=this;
ALError("ALSource:bindStream() had an error");
state=stopped;
}
void Detach() { if ( state == empty ) { printf( "ALSource:Detach:Empty source attempted. (Cannot dump a buffer from an empty source)\n" ); return; }
else if ( state != stopped ) { Stop(); }
if ( valid() ) { alSourcei(id,AL_BUFFER,0); state=empty; alGetError(); }
}
inline void Stop() { if ( valid() && state == playing ) { alGetError(); alSourceStop(id); alGetError(); state=stopped; } }
inline void Pause() { if ( valid() && state == playing ) { alGetError(); alSourcePause(id); alGetError(); state=paused; } }
inline void Play() { if ( valid() && state != playing ) { ALError("ALSound:Pre-Play()"); alSourcePlay(id); ALError("ALSound:Play() had an error."); alGetError(); state=playing; } }
inline void Resume() { Play(); }
// void PauseAll() { if ( valid() && state == playing ) Pause(); if ( next ) next.PauseAll(); }
// void ResumeAll() { if ( valid() && state == paused ) Resume(); if ( next ) next.ResumeAll(); }
bool Close( ) {
if ( valid() ) {
if ( state != empty ) this.Detach();
alDeleteSources(1, &(id)); if ( ALError( "ALSource:Close:alDeleteSources") ) return false;
//unusedSources++;
return true;
}
return false;
}
// void rehead( ALSource a ) { ALSource b=this; for ( ; b; b=b.next ) b.head=a; }
~ALSource() { Close(); }
}
class ALSources : LinkList<ALSource>
{
// List functions for managing a multi-source list.
ALSource AddNew()
{
ALSource s = null;
// if( unusedSources > 0 )
{
uint id;
alGenSources(1,&id);
if(!ALError("ALSource:WAV:alGenSources")) {
//unusedSources--;
Insert(null, s = { id = id });
}
}
return s;
}
}
/*
ALURE stream
*/
class ALUREStream {
public union { LinkElement<thisclass> link; struct { thisclass prev, next; }; };
ALSoundManager manager;
alureStream *stream;
char *name;
int bufferSize;
int usesBuffers;
uint samples;
float duration;
float refreshRate;
// ALuint *buf;
ALSource source;
bool created;
bool loop;
bool badError;
SoundVector position, velocity;
float pitch,gain;
float threadDelay;
bool terminated;
bufferSize=128*1024;
usesBuffers=3;//3;
threadDelay=0.01f;
~ALUREStream()
{
alureDestroyStream(stream, 0, null);// usesBuffers, buf);
delete source;
//unusedBuffers+=usesBuffers;
delete name;
}
inline bool Playing() { return ( source && source.state == playing ); }
// uint Main() { Sleep(threadDelay); DelayExpired(); return 0; }
void Stop() { if(created) { alureStopSource(source.id,AL_FALSE); } } //terminated = true; ((GuiApplication)__thisModule).Unlock(); Wait(); ((GuiApplication)__thisModule).Lock(); } }
bool LoadIntoSource(char *fn, ALSource s, bool CalcSize ) {
name=CopyString(fn);
if ( !source ) { printf("ALUREStream:Create complains the required source was not provided.\n" ); return false; }
//if ( usesBuffers > unusedBuffers ) { printf("ALUREStream:Create Not enough buffers available. Requested: %d Available: %d\n", usesBuffers, unusedBuffers ); return false; }
alGetError();
source=s;
// if ( buf ) { printf( "ALUREStream:Create warns, the buffers were already created, was .Create() mistakenly called twice?\n" ); delete buf; }
//buf=new ALuint[usesBuffers];
//unusedBuffers-=usesBuffers;
stream=alureCreateStreamFromFile((ALchar *) fn,bufferSize,0/*usesBuffers*/,null);//buf);
if ( !stream ) { printf( "ALUREStream:Create could not load the file: %s\n", fn, alureGetErrorString()); return false; }
if ( CalcSize ) CalculateSize(fn);
return (created=true);
}
virtual void OnComplete() { }
bool Play() {
if ( !this ) { printf( "ALUREStream:Play:Called on a null object.\n" ); return false; }
if ( !source ) { printf( "ALUREStream:Play:No source defined for the stream '%s'. Did it load properly?\n", name ); return false; }
if ( source.state == stopped ) {
printf ( "Play() was called on a stream.\n" );
alGetError();
// alSourceQueueBuffers(source.id,usesBuffers,buf);
// if ( ALError("ALUREStream:Play:alSourceQueueBuffers") ) { printf( "ALUREStream:Play failed to queue buffers.\n" ); return false; }
// alSourcePlay(source.id);
alurePlaySourceStream(source.id,stream,usesBuffers,0/*# of loops*/,null,null); // add eos_callback tie-in here
source.state=playing;
// if ( ALError("ALUREStream:Play:alSourcePlay") ) { printf( "ALUREStream:Play failed to start source.\n" ); return false; }
// this.timer.delay = (1.0f/refreshRate);
// this.timer.Start();
// Create();
} else if ( source.state == paused || source.state==stopped ) { source.Resume(); return true; }
else if ( source.state == empty ) { printf( "ALUREStream:Play called on an empty source (no buffer selected).\n" ); return false; }
else if ( source.state == playing ) { printf( "ALUREStream:Play() called on a playing stream.\n" ); return true; }
return true;
}
virtual void BetweenFrames() { }
virtual void OnError() { }
// Slightly inefficient, but precise.
int CalculateSize( char * fn ) {
ALuint total = 0;
ALuint b;
ALint freq = 0;
alureStream *s;
s = alureCreateStreamFromFile( (ALchar *) fn, 19200, 1, &b);
if(s) {
do {
ALint size, bits, channels;
alGetBufferi(b, AL_SIZE, &size);
alGetBufferi(b, AL_BITS, &bits);
alGetBufferi(b, AL_CHANNELS, &channels);
if(!freq) alGetBufferi(b, AL_FREQUENCY, &freq);
total += bits / channels * 8 / bits;
} while(alureBufferDataFromStream(s, 1, &b) > 0);
alureDestroyStream(s,1,&b);
}
duration=(float)total/(float)freq;
samples=(uint) total;
}
}
class ALUREStreams : LinkList<ALUREStream, link=ALUREStream::link> {};
class ALCaptureStream {
ALSoundFormat format;
int sampleFactor;
public:
property ALSoundFormat format { get { return format; }
set { format=value;
switch ( (ALSoundFormat) value ) {
case mono8: sampleFactor=1; break;
case mono16: case stereo8: sampleFactor=2; break;
case stereo16: sampleFactor=4; break;
}
}
}
uint frequency;
uint bufferSize;
int samples;
ALuint available;
ALCdevice *captureDevice;
char *deviceName;
bool capturing;
byte *target;
int ofs;
ALCaptureStream() { format=stereo16; sampleFactor=4; }
void Start() { if ( captureDevice ) { alcCaptureStart(captureDevice); capturing=true; } }
int *Acquire() {
if ( capturing ) {
int *buffer;
alcGetIntegerv(captureDevice, ALC_CAPTURE_SAMPLES, 1, &available);
buffer = new byte[available*sampleFactor];
memset(buffer,0,available);
alcCaptureSamples(captureDevice,buffer,(ALuint) available);
return buffer;
}
return null;
}
void AcquireTo( byte **buf ) {
if ( capturing ) {
if ( *buf ) delete *buf;
alcGetIntegerv(captureDevice, ALC_CAPTURE_SAMPLES, 1, &available);
*buf = new byte[available*sampleFactor];
target = *buf;
alcCaptureSamples(captureDevice,*buf,(ALuint) available);
} else {
delete *buf; *buf=null;
}
}
void AcquireAppend( byte *pre ) {
if ( capturing ) {
int *post=&(pre[ofs]);
alcGetIntegerv(captureDevice, ALC_CAPTURE_SAMPLES, 1, &available);
ofs+=available;
alcCaptureSamples(captureDevice,post,(ALuint) available);
}
}
void Stop() {
if ( captureDevice ) { alcCaptureStop(captureDevice); capturing=false; OnCaptureComplete(); }
}
virtual void OnCaptureComplete() { }
bool Init( char *name ) {
deviceName=CopyString(name);
captureDevice = alcCaptureOpenDevice( (ALCchar *) name, frequency, format, (ALCsizei) bufferSize );
if ( !captureDevice ) {
printf( "ALCaptureStream:Init:alcCaptureOpenDevice -> unable to open a device\n" );
return false;
}
return true;
}
~ALCaptureStream() {
}
}
class ALSound {
public union { LinkElement<thisclass> link; struct { thisclass prev, next; }; };
ALWaveForm waveform;
char *name;
uint id;
float duration;
bool deleteMe;
ALenum format;
ALvoid *data;
ALsizei size,freq;
ALboolean loop, relative;
ALSource source; // sources are points of emitting sound.
SoundVector position, velocity, direction;
float pitch,gain,min_gain,max_gain,distance,rolloff;
bool boundToSource;
bool lastFramePlayState;
bool Restart;
bool Release() { alSourceStop( id ); }
int NextAvailableID( int max ) {
ALSound s;
int i;
bool *notavailable= new bool[max];
for ( s=this; s; s=s.next ) notavailable[s.id-1]=true;
for ( i=0; i<max; i++ ) if ( notavailable[i] == false ) { delete notavailable; return i+1; }
delete notavailable;
return -1;
}
bool Play( )
{
int sourceAudioState = 0;
WhenPlayed();
if ( !source ) { printf( "Audio %s had no source.\n", waveform.filename ); return false; }
alGetError();
alGetSourcei( source.id, AL_SOURCE_STATE, &sourceAudioState );
if ( sourceAudioState == AL_PLAYING ) {
if ( Restart ) Stop( id ); else return false;
}
alSourcePlay( source.id );
if ( ALError( "ALSound:Play:alSourcePlay") ) return false;
lastFramePlayState=true;
source.state=playing;
alGetError();
return true;
}
bool Pause( ) {
WhenPaused();
alGetError();
alSourcePause( source.id );
if ( ALError( "ALSound:Pause:alSourcePause") ) return false;
lastFramePlayState=false;
source.state=paused;
alGetError();
return true;
}
bool Playing() {
int sourceAudioState = 0;
if ( !source ) return false;
alGetSourcei( source.id, AL_SOURCE_STATE, &sourceAudioState );
if ( sourceAudioState == AL_PLAYING ) source.state=playing;
if ( sourceAudioState == AL_PAUSED ) source.state=paused;
return ( source.state == playing );
}
// If the sound was paused the sound will resume, else it will play from the beginning.
bool Resume( )
{
int sourceAudioState = 0;
WhenResumed();
alGetError();
alGetSourcei( source.id, AL_SOURCE_STATE, &sourceAudioState );
if ( sourceAudioState == AL_PLAYING ) return true;
if ( sourceAudioState == AL_PAUSED ) { alSourcePlay( source.id ); if ( ALError( "Resume:alSourcePlay") ) return false; }
if ( ALError( "ALSound:Resume:alSourcePlay") ) return false;
lastFramePlayState=true;
source.state=playing;
alGetError();
return true;
}
// Make sure the audio source ident is valid and usable
bool Stop( )
{
alGetError();
alSourceStop( source.id );
if ( ALError( "ALSound:Stop:alSourceStop") ) return false;
lastFramePlayState=false;
source.state=stopped;
alGetError();
return true;
}
bool position( )
{
alGetError();
alSourcefv(source.id, AL_POSITION, position.asFloat3() ); if ( ALError( "ALSound:position:alSourcefv:AL_POSITION" ) ) return false;
return true;
}
bool SetPVD( )
{
if ( !Playing() ) return true;
alGetError();
alSourcefv(source.id, AL_POSITION, position.asFloat3() ); if ( ALError( "ALSound:SetPVD:alSourcefv:AL_POSITION" ) ) return false;
alSourcefv(source.id, AL_VELOCITY, velocity.asFloat3() ); if ( ALError( "ALSound:SetPVD:alSourcefv:AL_VELOCITY" ) ) return false;
alSourcefv(source.id, AL_DIRECTION, direction.asFloat3() ); if ( ALError( "ALSound:SetPVD:alSourcefv:AL_DIRECTION" ) ) return false;
alGetError();
return true;
}
bool SetSound( float maxDistance, float minGain, float maxGain, float rollOff )
{
alGetError();
alSourcef(source.id, AL_MAX_DISTANCE, distance=maxDistance ); if ( ALError( "ALSound:SetSound:alSourcef:AL_MAX_DISTANCE" ) ) return false;
alSourcef(source.id, AL_MIN_GAIN, min_gain=minGain ); if ( ALError( "ALSound:SetSound:alSourcef:AL_MIN_GAIN" ) ) return false;
alSourcef(source.id, AL_MAX_GAIN, max_gain=maxGain ); if ( ALError( "ALSound:SetSound:alSourcef:AL_MAX_GAIN" ) ) return false;
alSourcef(source.id, AL_ROLLOFF_FACTOR, rolloff =rollOff ); if ( ALError( "ALSound:SetSound:alSourcef:AL_ROLLOFF_FACTOR" ) ) return false;
return true;
}
ALSource UpdateSource()
{
alGetError();
// alSourcei (source.id, AL_BUFFER, buffer.id );
alSourcef (source.id, AL_PITCH, pitch );
alSourcef (source.id, AL_GAIN, gain );
alSourcef (source.id, AL_MAX_DISTANCE, distance ); if ( ALError( "ALSound:AddSource:AL_MAX_DISTANCE" ) ) { delete source; return null; }
alSourcef (source.id, AL_MIN_GAIN, min_gain ); if ( ALError( "ALSound:AddSource:AL_MIN_GAIN" ) ) { delete source; return null; }
alSourcef (source.id, AL_MAX_GAIN, max_gain ); if ( ALError( "ALSound:AddSource:AL_MAX_GAIN" ) ) { delete source; return null; }
alSourcef (source.id, AL_ROLLOFF_FACTOR, rolloff ); if ( ALError( "ALSound:AddSource:AL_ROLLOFF_FACTOR" ) ) { delete source; return null; }
alSourcefv(source.id, AL_POSITION, position.asFloat3() ); if ( ALError( "ALSound:AddSource:AL_POSITION" ) ) { delete source; return null; }
alSourcefv(source.id, AL_VELOCITY, velocity.asFloat3() ); if ( ALError( "ALSound:AddSource:AL_VELOCITY" ) ) { delete source; return null; }
alSourcefv(source.id, AL_DIRECTION, direction.asFloat3() ); if ( ALError( "ALSound:AddSource:AL_DIRECTION" ) ) { delete source; return null; }
alSourcei (source.id, AL_LOOPING, (ALboolean) loop );
alSourcePlay(source.id);
return source;
}
void AssignSource( ALSource s ) {
alGetError();
if ( source ) { printf( "ALSound:AssignSource:Error! Attempted to assign a source when there already was one.\n" ); return; }
source=s;
alSourcei (source.id, AL_BUFFER, waveform.buffer.id );
UpdateSource();
}
ALSource AddSource()
{
if ( !source ) {
ALSource s { };
alGetError();
alGenSources(1, &(s.id));
if (alGetError() != AL_NO_ERROR) { printf("ALSound:AddSource(): Error generating audio source."); return; }
alSourcei (s.id, AL_BUFFER, waveform.buffer.id );
alSourcef (s.id, AL_PITCH, pitch );
alSourcef (s.id, AL_GAIN, gain );
alSourcef (s.id, AL_MAX_DISTANCE, distance ); if ( ALError( "ALSound:AddSource:AL_MAX_DISTANCE" ) ) { delete source; return null; }
alSourcef (s.id, AL_MIN_GAIN, min_gain ); if ( ALError( "ALSound:AddSource:AL_MIN_GAIN" ) ) { delete source; return null; }
alSourcef (s.id, AL_MAX_GAIN, max_gain ); if ( ALError( "ALSound:AddSource:AL_MAX_GAIN" ) ) { delete source; return null; }
alSourcef (s.id, AL_ROLLOFF_FACTOR, rolloff ); if ( ALError( "ALSound:AddSource:AL_ROLLOFF_FACTOR" ) ) { delete source; return null; }
alSourcefv(s.id, AL_POSITION, position.asFloat3() ); if ( ALError( "ALSound:AddSource:AL_POSITION" ) ) { delete source; return null; }
alSourcefv(s.id, AL_VELOCITY, velocity.asFloat3() ); if ( ALError( "ALSound:AddSource:AL_VELOCITY" ) ) { delete source; return null; }
alSourcefv(s.id, AL_DIRECTION, direction.asFloat3() ); if ( ALError( "ALSound:AddSource:AL_DIRECTION" ) ) { delete source; return null; }
alSourcei (s.id, AL_LOOPING, (ALboolean) loop );
alSourcePlay(s.id);
this.source=s;
return s;
}
printf( "ALSound:AddSource() - source Already Created; File: %s\n", waveform.filename );
return null;
}
void PrintInfo( ) {
}
bool *ThrowASwitch;
virtual void WhenPlayed ( ){ }
virtual void WhenPaused ( ){ }
virtual void WhenResumed ( ){ }
virtual void WhenComplete ( ){ if ( ThrowASwitch ) *ThrowASwitch=true; }
virtual void BetweenFrames( ){ }
void _BetweenFrames() {
int state;
alGetSourcei(source.id, AL_SOURCE_STATE, &state);
if ( AL_PLAYING == state ) BetweenFrames();
if ( !(AL_PLAYING == state) && lastFramePlayState ) {
WhenComplete();
this.Release();
this.deleteMe=true; //printf( "%s sound cleared for deletion.\n", name );
}
lastFramePlayState=(AL_PLAYING == state);
}
~ALSound() {
// printf( "Deleting sound %s\n", name );
delete source;
delete name;
}
}
class ALSounds : LinkList<ALSound, link=ALSound::link>
{
bool inList( char *fn ) { for ( s : this; contains(fn,s.name)) return true; return false; }
ALSound findInList( char *fn ) { for ( s : this; contains(fn,s.name) ) return s; return null; }
void DeleteDone() { ALSound s,n; for ( s=this.first; s; s=n ) { n=s.next; if ( s.deleteMe ) Delete((void *)s); } }
}
class ALCDeviceContext : ListItem {
ALCcontext* context;
ALCdevice* device;
char *deviceName;
bool closed;
void PickDefault() {
device=alcOpenDevice(null);
context=alcCreateContext(device,null);
alcMakeContextCurrent(context);
closed=false;
}
void Init( char *context, void *variables ) {
device = alcOpenDevice(context);
this.context = alcCreateContext(device, variables);
alcMakeContextCurrent(this.context);
closed=false;
}
void Close() {
context = alcGetCurrentContext();
device = alcGetContextsDevice(context);
alcMakeContextCurrent(null);
alcDestroyContext(context);
alcCloseDevice(device);
closed=true;
}
void DeviceInfo( ) {
printf( "Sound devices available:\n" );
if ( alcIsExtensionPresent( null, "ALC_ENUMERATION_EXT" ) == AL_TRUE ) {
printf( "ALC_DEVICE_SPECIFIER: %s\n", (char*) alcGetString( device, ALC_DEVICE_SPECIFIER ) );
printf( "ALC_DEFAULT_DEVICE_SPECIFIER: %s\n", (char*) alcGetString( null, ALC_DEFAULT_DEVICE_SPECIFIER ) );
printf( "ALC_EXTENSIONS: %s\n", (char*) alcGetString( null, ALC_EXTENSIONS ) );
}
else printf( " ... enumeration error.\n" );
}
~ALCDeviceContext() { if (!closed) Close(); }
}
class ALCDeviceContexts : LinkList<ALCDeviceContext>;
enum EFXType {
null=0,
reverb=1,
chorus=2,
distortion=3,
echo=4,
flanger=5,
frequencyShifter=6,
vocalMorpher=7,
pitchShifter=8,
ringModulator=9,
autoWah=10,
compressor=11,
equalizer=12
};
enum EFXWaveForm { sinusoid=0, triangle=1 };
enum EFXDirection { down=0, up=1, off=2 };
enum EFXPhoneme { A=0, E=1, I=2, O=3, U=4, AA=5, AE=6, AH=7, AO=8, EH=9, ER=10, IH=11, IY=12, UH=13, AW=14,
B=15, D=16, F=17, G=18, J=19, K=20, L=21, M=22, N=23, P=24, R=25, S=26, T=27, V=28, Z=29 };
enum EFXWaveForm3a { sinusoid=0, triangle=1, sawtooth=2 };
enum EFXWaveForm3b { sinusoid=0, sawtooth=1, square=2 };
class EFXAuxSlot : ListItem {
EFX efx;
ALuint id;
bool Create() {
alGetError();
efx.alGenAuxiliaryEffectSlots(1,&id);
if ( ALError("EFXAuxSlot:Create:alGenAuxiliaryEffectSlots") ) return false;
return true;
}
bool Destroy() {
alGetError();
efx.alDeleteAuxiliaryEffectSlots(1,&id);
if ( ALError("EFXAuxSlot:Destroy:alDeleteAuxiliaryEffectSlots") ) return false;
return true;
}
}
class EFXAuxSlots : LinkList<EFXAuxSlot>;
class EFXEffect {
EFX efx;
EFXAuxSlot aux;
uint id;
uint type;
uint firstParameter,lastParameter;
float directFilter,
auxSendFilter,
airAbsorption,
roomRolloff,
coneOuterGainHF,
directFilterGainHF,
auxSendFilterGainAuto,
auxSendFilterGainHFAuto;
bool Create() {
alGetError();
if ( aux ) if ( !aux.Destroy() ) { return false; }
delete aux; aux = EFXAuxSlot { }; if ( !aux.Create() ) { return false; }
efx.alGenEffects(1,&id);
if ( ALError("EFXEffect:Create:alGenEffects") ) return false;
efx.alEffecti(id,AL_EFFECT_TYPE,type);
if ( ALError("EFXEffect:Create:alEffecti") ) { efx.alDeleteEffects(1,&id); return false; }
efx.alAuxiliaryEffectSloti(aux.id, AL_EFFECTSLOT_EFFECT, id);
}
bool Destroy() {
alGetError();
efx.alAuxiliaryEffectSloti(aux.id, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
efx.alDeleteEffects(1,&id);
if ( ALError("EFXEffect:Destroy:alDeleteEffects") ) return false;
}
void ApplySharedEffectParameters() {
}
}
enum EAXReverbPresets { hanger=2,bathroom=1 };
class EAXReverb : EFXEffect {
float environmentSize,
environmentDiffusion,
density,
diffusion,
gain,
gainHF,
gainLF,
decayTime,
decayHFRatio,
decayLFRatio,
reflectionsGain,
reflectionsDelay,
lateGain,
lateDelay,
reverbDelay,
echoTime,
echoDepth,
modulationTime,
modulationDepth,
airAbsorptionGainHF,
referenceHF,
referenceLF,
roomRolloffFactor;
SoundVector reflectionsPan, latePan;
int room, roomHF, roomLF, reverb, flags, reflections, environment;
bool decayHFLimit;
EAXReverb() { // Defaulted to bathroom effect
reflectionsPan = SoundVector { };
latePan = SoundVector { };
}
bool Apply() {
alGetError();
efx.alEffectf(id, AL_EAXREVERB_DENSITY, density);
efx.alEffectf(id, AL_EAXREVERB_DIFFUSION, diffusion);
efx.alEffectf(id, AL_EAXREVERB_GAIN, gain);
efx.alEffectf(id, AL_EAXREVERB_GAINHF, gainHF);
efx.alEffectf(id, AL_EAXREVERB_GAINLF, gainLF);
efx.alEffectf(id, AL_EAXREVERB_DECAY_TIME, decayTime);
efx.alEffectf(id, AL_EAXREVERB_DECAY_HFRATIO, decayHFRatio);
efx.alEffectf(id, AL_EAXREVERB_DECAY_LFRATIO, decayLFRatio);
efx.alEffectf(id, AL_EAXREVERB_REFLECTIONS_GAIN, reflectionsGain);
efx.alEffectf(id, AL_EAXREVERB_REFLECTIONS_DELAY, reflectionsDelay);
efx.alEffectfv(id, AL_EAXREVERB_REFLECTIONS_PAN, reflectionsPan.asFloat3());
efx.alEffectf(id, AL_EAXREVERB_LATE_REVERB_GAIN, lateGain);
efx.alEffectf(id, AL_EAXREVERB_LATE_REVERB_DELAY, lateDelay);
efx.alEffectfv(id, AL_EAXREVERB_LATE_REVERB_PAN, latePan.asFloat3());
efx.alEffectf(id, AL_EAXREVERB_ECHO_TIME, echoTime);
efx.alEffectf(id, AL_EAXREVERB_ECHO_DEPTH, echoDepth);
efx.alEffectf(id, AL_EAXREVERB_MODULATION_TIME, modulationTime);
efx.alEffectf(id, AL_EAXREVERB_MODULATION_DEPTH, modulationDepth);
efx.alEffectf(id, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, airAbsorptionGainHF);
efx.alEffectf(id, AL_EAXREVERB_HFREFERENCE, referenceHF);
efx.alEffectf(id, AL_EAXREVERB_LFREFERENCE, referenceLF);
efx.alEffectf(id, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, roomRolloffFactor);
efx.alEffecti(id, AL_EAXREVERB_DECAY_HFLIMIT, (ALboolean) decayHFLimit);
if ( ALError("EAXReverb:Apply") ) return false;
return true;
}
void Preset( EAXReverbPresets preset ) {
switch ( preset ) {
default:
case bathroom:
environment=3; environmentSize=1.4f; environmentDiffusion=1.000f;
room=-1000; roomHF=-1200; roomLF=0;
decayTime=1.49f; decayHFRatio=0.54f; decayLFRatio=1.00f;
reflections=-370; reflectionsDelay=0.007f; reflectionsPan.x=0.0f; reflectionsPan.y=0.0f; reflectionsPan.z=0.0f;
reverb=1030; reverbDelay=0.011f; latePan.x=0.00f; latePan.y=0.00f; latePan.z=0.00f;
echoTime=0.250f; echoDepth=0.000f;
modulationTime=0.250f; modulationDepth=0.000f;
airAbsorptionGainHF=-5.0f;
referenceHF=5000.0f; referenceLF=250.0f;
roomRolloffFactor=0.00f;
flags=0x3f;
break;
}
}
}
class EFXReverb : EFXEffect {
float density,
diffusion,
gain,
gainHF,
decayTime,
decayHFRatio,
reflectionsGain,
reflectionsDelay,
lateGain,
lateDelay,
airAbsorptionGainHF,
roomRolloffFactor;
bool decayHFLimit;
EFXReverb() {
density = AL_REVERB_DEFAULT_DENSITY;
diffusion = AL_REVERB_DEFAULT_DIFFUSION;
gain = AL_REVERB_DEFAULT_GAIN;
gainHF = AL_REVERB_DEFAULT_GAINHF;
decayTime = AL_REVERB_DEFAULT_DECAY_TIME;
decayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO;
reflectionsGain = AL_REVERB_DEFAULT_REFLECTIONS_GAIN;
reflectionsDelay = AL_REVERB_DEFAULT_REFLECTIONS_DELAY;
lateGain = AL_REVERB_DEFAULT_LATE_REVERB_GAIN;
lateDelay = AL_REVERB_DEFAULT_LATE_REVERB_DELAY;
airAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF;
roomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR;
decayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT;
}
public:
property float density { get { return density; } set { density =RANGE(AL_REVERB_MIN_DENSITY, value, AL_REVERB_MAX_DENSITY); } }
property float diffusion { get { return diffusion; } set { diffusion =RANGE(AL_REVERB_MIN_DIFFUSION, value, AL_REVERB_MAX_DIFFUSION); } }
property float gain { get { return gain; } set { gain =RANGE(AL_REVERB_MIN_GAIN, value, AL_REVERB_MAX_GAIN); } }
property float gainHF { get { return gainHF; } set { gainHF =RANGE(AL_REVERB_MIN_GAINHF, value, AL_REVERB_MAX_GAINHF); } }
property float decayTime { get { return decayTime; } set { decayTime =RANGE(AL_REVERB_MIN_DECAY_TIME, value, AL_REVERB_MAX_DECAY_TIME); } }
property float decayHFRatio { get { return decayHFRatio; } set { decayHFRatio =RANGE(AL_REVERB_MIN_DECAY_HFRATIO, value, AL_REVERB_MAX_DECAY_HFRATIO); } }
property float reflectionsGain { get { return reflectionsGain; } set { reflectionsGain =RANGE(AL_REVERB_MIN_REFLECTIONS_GAIN, value, AL_REVERB_MAX_REFLECTIONS_GAIN); } }
property float reflectionsDelay { get { return reflectionsDelay; } set { reflectionsDelay =RANGE(AL_REVERB_MIN_LATE_REVERB_DELAY, value, AL_REVERB_MAX_REFLECTIONS_DELAY); } }
property float lateGain { get { return lateGain; } set { lateGain =RANGE(AL_REVERB_MIN_LATE_REVERB_GAIN, value, AL_REVERB_MAX_LATE_REVERB_GAIN); } }
property float lateDelay { get { return lateDelay; } set { lateDelay =RANGE(AL_REVERB_MIN_LATE_REVERB_DELAY, value, AL_REVERB_MAX_LATE_REVERB_DELAY); } }
property float airAbsorptionGainHF { get { return airAbsorptionGainHF; } set { airAbsorptionGainHF =RANGE(AL_REVERB_MIN_AIR_ABSORPTION_GAINHF, value, AL_REVERB_MAX_AIR_ABSORPTION_GAINHF); } }
property float roomRolloffFactor { get { return roomRolloffFactor; } set { roomRolloffFactor =RANGE(AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR, value, AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR); } }
property bool decayHFLimit { get { return decayHFLimit; } set { decayHFLimit =RANGE(AL_REVERB_MIN_DECAY_HFLIMIT, value, AL_REVERB_MAX_DECAY_HFLIMIT); } }
void Apply() {
}
void Update() {
}
void Print() {
}
}
class EFXChorus : EFXEffect {
EFXWaveForm waveform;
int phase;
float rate, depth, feedback, delay;
EFXChorus() {
waveform = (EFXWaveForm) AL_CHORUS_DEFAULT_WAVEFORM;
phase = AL_CHORUS_DEFAULT_PHASE;
rate = AL_CHORUS_DEFAULT_RATE;
depth = AL_CHORUS_DEFAULT_DEPTH;
feedback = AL_CHORUS_DEFAULT_FEEDBACK;
delay = AL_CHORUS_DEFAULT_DELAY;
}
public:
property float phase { get { return phase; } set { phase =RANGE(AL_CHORUS_MIN_PHASE, value, AL_CHORUS_MAX_PHASE); } }
property float rate { get { return rate; } set { rate =RANGE(AL_CHORUS_MIN_RATE, value, AL_CHORUS_MAX_RATE); } }
property float depth { get { return depth; } set { depth =RANGE(AL_CHORUS_MIN_DEPTH, value, AL_CHORUS_MAX_DEPTH); } }
property float feedback { get { return feedback; } set { feedback =RANGE(AL_CHORUS_MIN_FEEDBACK, value, AL_CHORUS_MAX_FEEDBACK); } }
property float delay { get { return delay; } set { delay =RANGE(AL_CHORUS_MIN_DELAY, value, AL_CHORUS_MAX_DELAY); } }
void Apply() {
}
void Update() {
}
void Print() {
}
}
class EFXDistortion : EFXEffect {
float edge, gain, cutoff, eqCenter, eqBandwidth;
EFXDistortion() {
edge = AL_DISTORTION_DEFAULT_EDGE;
gain = AL_DISTORTION_DEFAULT_GAIN;
cutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
eqCenter = AL_DISTORTION_DEFAULT_EQCENTER;
eqBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
}
public:
property float edge { get { return edge; } set { edge =RANGE(AL_DISTORTION_MIN_EDGE, value, AL_DISTORTION_MAX_EDGE); } }
property float gain { get { return gain; } set { gain =RANGE(AL_DISTORTION_MIN_GAIN, value, AL_DISTORTION_MAX_GAIN); } }
property float cutoff { get { return cutoff; } set { cutoff =RANGE(AL_DISTORTION_MIN_LOWPASS_CUTOFF, value, AL_DISTORTION_MAX_LOWPASS_CUTOFF); } }
property float eqCenter { get { return eqCenter; } set { eqCenter =RANGE(AL_DISTORTION_MIN_EQCENTER, value, AL_DISTORTION_MAX_EQCENTER); } }
property float eqBandwidth { get { return eqBandwidth; } set { eqBandwidth =RANGE(AL_DISTORTION_MIN_EQBANDWIDTH, value, AL_DISTORTION_MAX_EQBANDWIDTH); } }
void Apply() {
}
void Update() {
}
void Print() {
}
}
class EFXEcho : EFXEffect {
float delay, delayLR, damping, feedback, spread;
EFXEcho() {
delay = AL_ECHO_DEFAULT_DELAY;
delayLR = AL_ECHO_DEFAULT_LRDELAY;
damping = AL_ECHO_DEFAULT_DAMPING;
feedback = AL_ECHO_DEFAULT_FEEDBACK;
spread = AL_ECHO_DEFAULT_SPREAD;
}
public:
property float delay { get { return delay; } set { delay =RANGE(AL_ECHO_MIN_DELAY, value, AL_ECHO_MAX_DELAY); } }
property float delayLR { get { return delayLR; } set { delayLR =RANGE(AL_ECHO_MIN_LRDELAY, value, AL_ECHO_MAX_LRDELAY); } }
property float damping { get { return damping; } set { damping =RANGE(AL_ECHO_MIN_DAMPING, value, AL_ECHO_MAX_DAMPING); } }
property float feedback { get { return feedback; } set { feedback =RANGE(AL_ECHO_MIN_FEEDBACK, value, AL_ECHO_MAX_FEEDBACK); } }
property float spread { get { return spread; } set { spread =RANGE(AL_ECHO_MIN_SPREAD, value, AL_ECHO_MAX_SPREAD); } }
void Apply() {
}
void Update() {
}
void Print() {
}
}
class EFXFlanger : EFXEffect {
EFXWaveForm waveform;
int phase;
float rate, depth, feedback, delay;
EFXFlanger() {
waveform = (EFXWaveForm) AL_FLANGER_DEFAULT_WAVEFORM;
phase = AL_FLANGER_DEFAULT_PHASE;
rate = AL_FLANGER_DEFAULT_RATE;
depth = AL_FLANGER_DEFAULT_DEPTH;
feedback = AL_FLANGER_DEFAULT_FEEDBACK;
delay = AL_FLANGER_DEFAULT_DELAY;
}
public:
property float phase { get { return phase; } set { phase =RANGE(AL_FLANGER_MIN_PHASE, value, AL_FLANGER_MAX_PHASE); } }
property float rate { get { return rate; } set { rate =RANGE(AL_FLANGER_MIN_RATE, value, AL_FLANGER_MAX_RATE); } }
property float depth { get { return depth; } set { depth =RANGE(AL_FLANGER_MIN_DEPTH, value, AL_FLANGER_MAX_DEPTH); } }
property float feedback { get { return feedback; } set { feedback =RANGE(AL_FLANGER_MIN_FEEDBACK, value, AL_FLANGER_MAX_FEEDBACK); } }
property float delay { get { return delay; } set { delay =RANGE(AL_FLANGER_MIN_DELAY, value, AL_FLANGER_MAX_DELAY); } }
void Apply() {
}
void Update() {
}
void Print() {
}
}
class EFXFrequencyShift : EFXEffect {
float freq;
EFXDirection L,R;
EFXFrequencyShift() {
freq = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
L = (EFXDirection) AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION;
R = (EFXDirection) AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION;
}
public:
property float freq { get { return freq; } set { freq =RANGE(AL_FREQUENCY_SHIFTER_MIN_FREQUENCY, value, AL_FREQUENCY_SHIFTER_MAX_FREQUENCY); } }
void Apply() {
}
void Update() {
}
void Print() {
}
}
class EFXVocalMorph : EFXEffect {
EFXPhoneme phonemeA, phonemeB;
int phonemeACoarseTuning, phonemeBCoarseTuning;
EFXWaveForm3a waveform;
float rate;
EFXReverb() {
phonemeA = (EFXPhoneme) AL_VOCAL_MORPHER_DEFAULT_PHONEMEA;
phonemeB = (EFXPhoneme) AL_VOCAL_MORPHER_DEFAULT_PHONEMEB;
phonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING;
phonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING;
waveform = (EFXWaveForm3a) AL_VOCAL_MORPHER_DEFAULT_WAVEFORM;
rate = AL_VOCAL_MORPHER_DEFAULT_RATE;
}
public:
property float phonemeACoarseTuning { get { return phonemeACoarseTuning; } set { phonemeACoarseTuning =RANGE(AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING, value, AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING); } }
property float phonemeBCoarseTuning { get { return phonemeBCoarseTuning; } set { phonemeBCoarseTuning =RANGE(AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING, value, AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING); } }
property float rate { get { return rate; } set { rate =RANGE(AL_VOCAL_MORPHER_MIN_RATE, value, AL_VOCAL_MORPHER_MAX_RATE); } }
void Apply() {
}
void Update() {
}
void Print() {
}
}
class EFXPitchShift : EFXEffect {
int coarse, fine;
EFXReverb() {
coarse = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE;
fine = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE;
}
public:
property float coarse { get { return coarse; } set { coarse =RANGE(AL_PITCH_SHIFTER_MIN_COARSE_TUNE, value, AL_PITCH_SHIFTER_MAX_COARSE_TUNE); } }
property float fine { get { return fine; } set { fine =RANGE(AL_PITCH_SHIFTER_MIN_FINE_TUNE, value, AL_PITCH_SHIFTER_MAX_FINE_TUNE); } }
void Apply() {
}
void Update() {
}
void Print() {
}
}
class EFXRingMod : EFXEffect {
float frequency, cutoff;
EFXWaveForm3b waveform;
EFXReverb() {
frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
cutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
waveform = (EFXWaveForm3b) AL_RING_MODULATOR_DEFAULT_WAVEFORM;
}
public:
property float frequency { get { return frequency; } set { frequency =RANGE(AL_RING_MODULATOR_MIN_FREQUENCY, value, AL_RING_MODULATOR_MAX_FREQUENCY); } }
property float cutoff { get { return cutoff; } set { cutoff =RANGE(AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF, value, AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF); } }
void Apply() {
}
void Update() {
}
void Print() {
}
}
class EFXAutoWah : EFXEffect {
float attack, release, resonance, gain;
EFXReverb() {
attack = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
release = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
gain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
}
public:
property float attack { get { return attack; } set { attack =RANGE(AL_AUTOWAH_MIN_ATTACK_TIME, value, AL_AUTOWAH_MAX_ATTACK_TIME); } }
property float release { get { return release; } set { release =RANGE(AL_AUTOWAH_MIN_RELEASE_TIME, value, AL_AUTOWAH_MAX_RELEASE_TIME); } }
property float resonance { get { return resonance; } set { resonance =RANGE(AL_AUTOWAH_MIN_RESONANCE, value, AL_AUTOWAH_MAX_RESONANCE); } }
property float gain { get { return gain; } set { gain =RANGE(AL_AUTOWAH_MIN_PEAK_GAIN, value, AL_AUTOWAH_MAX_PEAK_GAIN); } }
void Apply() {
}
void Update() {
}
void Print() {
}
}
class EFXCompressor {
bool on;
EFXReverb() {
on = (bool) AL_COMPRESSOR_DEFAULT_ONOFF;
}
public:
void Apply() {
}
void Update() {
}
void Print() {
}
}
class EFXEqualizer {
float lowGain, lowCutoff, mid1Gain, mid1Center, mid1Width, mid2Gain, mid2Center, mid2Width, highGain, highCutoff;
EFXReverb() {
lowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN;
lowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN;
mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER;
mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH;
mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN;
mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER;
mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
highGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
highCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
}
public:
property float lowGain { get { return lowGain; } set { lowGain =RANGE(AL_EQUALIZER_MIN_LOW_GAIN, value, AL_EQUALIZER_MAX_LOW_GAIN); } }
property float lowCutoff { get { return lowCutoff; } set { lowCutoff =RANGE(AL_EQUALIZER_MIN_LOW_CUTOFF, value, AL_EQUALIZER_MAX_LOW_CUTOFF); } }
property float mid1Gain { get { return mid1Gain; } set { mid1Gain =RANGE(AL_EQUALIZER_MIN_MID1_GAIN, value, AL_EQUALIZER_MAX_MID1_GAIN); } }
property float mid1Center { get { return mid1Center; } set { mid1Center =RANGE(AL_EQUALIZER_MIN_MID1_CENTER, value, AL_EQUALIZER_MAX_MID1_CENTER); } }
property float mid1Width { get { return mid1Width; } set { mid1Width =RANGE(AL_EQUALIZER_MIN_MID1_WIDTH, value, AL_EQUALIZER_MAX_MID1_WIDTH); } }
property float mid2Gain { get { return mid2Gain; } set { mid2Gain =RANGE(AL_EQUALIZER_MIN_MID2_GAIN, value, AL_EQUALIZER_MAX_MID2_GAIN); } }
property float mid2Center { get { return mid2Center; } set { mid2Center =RANGE(AL_EQUALIZER_MIN_MID2_CENTER, value, AL_EQUALIZER_MAX_MID2_CENTER); } }
property float mid2Width { get { return mid2Width; } set { mid2Width =RANGE(AL_EQUALIZER_MIN_MID2_WIDTH, value, AL_EQUALIZER_MAX_MID2_WIDTH); } }
property float highGain { get { return highGain; } set { highGain =RANGE(AL_EQUALIZER_MIN_HIGH_GAIN, value, AL_EQUALIZER_MAX_HIGH_GAIN); } }
property float highCutoff { get { return highCutoff; } set { highCutoff =RANGE(AL_EQUALIZER_MIN_HIGH_CUTOFF, value, AL_EQUALIZER_MAX_HIGH_CUTOFF); } }
void Apply() {
}
void Update() {
}
void Print() {
}
}
class EFXFilter {
}
class EFX {
EFXEffect effects;
int version_major,version_minor,max_aux_sends,meters_per_unit;
LPALGENEFFECTS alGenEffects;
LPALDELETEEFFECTS alDeleteEffects;
LPALISEFFECT alIsEffect;
LPALEFFECTI alEffecti;
LPALEFFECTIV alEffectiv;
LPALEFFECTF alEffectf;
LPALEFFECTFV alEffectfv;
LPALGETEFFECTI alGetEffecti;
LPALGETEFFECTIV alGetEffectiv;
LPALGETEFFECTF alGetEffectf;
LPALGETEFFECTFV alGetEffectfv;
LPALGENFILTERS alGenFilters;
LPALDELETEFILTERS alDeleteFilters;
LPALISFILTER alIsFilter;
LPALFILTERI alFilteri;
LPALFILTERIV alFilteriv;
LPALFILTERF alFilterf;
LPALFILTERFV alFilterfv;
LPALGETFILTERI alGetFilteri;
LPALGETFILTERIV alGetFilteriv;
LPALGETFILTERF alGetFilterf;
LPALGETFILTERFV alGetFilterfv;
LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots;
LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots;
LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot;
LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti;
LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv;
LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf;
LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv;
LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti;
LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv;
LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf;
LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv;
bool Init()
{
bool hasEFX=false;
ALCdevice *device;
ALuint obj;
int i;
const ALenum effects[] = {
AL_EFFECT_EAXREVERB, AL_EFFECT_REVERB, AL_EFFECT_CHORUS,
AL_EFFECT_DISTORTION, AL_EFFECT_ECHO, AL_EFFECT_FLANGER,
AL_EFFECT_FREQUENCY_SHIFTER, AL_EFFECT_VOCAL_MORPHER,
AL_EFFECT_PITCH_SHIFTER, AL_EFFECT_RING_MODULATOR, AL_EFFECT_AUTOWAH,
AL_EFFECT_COMPRESSOR, AL_EFFECT_EQUALIZER, AL_EFFECT_NULL
};
char effectNames[] = "EAX Reverb,Reverb,Chorus,Distortion,Echo,Flanger,Frequency Shifter,Vocal Morpher,pitch Shifter,Ring Modulator,Autowah,Compressor,Equalizer,";
const ALenum filters[] = {
AL_FILTER_LOWPASS, AL_FILTER_HIGHPASS, AL_FILTER_BANDPASS,
AL_FILTER_NULL
};
char filterNames[] = "Low-pass,High-pass,Band-pass,";
char *current;
device = alcGetContextsDevice(alcGetCurrentContext());
if(alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_FALSE) { printf("EFX not available.\n"); return; }
alcGetIntegerv(device, ALC_EFX_MAJOR_VERSION, 1, &version_major);
alcGetIntegerv(device, ALC_EFX_MINOR_VERSION, 1, &version_minor);
checkForErrors();
printf("EFX version: %d.%d\n", (int)version_major, (int)version_minor);
alcGetIntegerv(device, ALC_MAX_AUXILIARY_SENDS, 1, &max_aux_sends);
checkForErrors();
printf("Max auxiliary sends: %d\n", (int)max_aux_sends);
hasEFX = alcIsExtensionPresent(null, ALC_EXT_EFX_NAME );
if ( hasEFX ) {
printf( "EFX extension available: \n" );
alGenEffects = (LPALGENEFFECTS) alGetProcAddress( "alGenEffects" ); if ( alGenEffects == null ) hasEFX = false;
alDeleteEffects = (LPALDELETEEFFECTS) alGetProcAddress( "alDeleteEffects" ); if ( alDeleteEffects == null ) hasEFX = false;
alIsEffect = (LPALISEFFECT) alGetProcAddress( "alIsEffect" ); if ( alIsEffect == null ) hasEFX = false;
alEffecti = (LPALEFFECTI) alGetProcAddress( "alEffecti" ); if ( alEffecti == null ) hasEFX = false;
alEffectiv = (LPALEFFECTIV) alGetProcAddress( "alEffectiv" ); if ( alEffectiv == null ) hasEFX = false;
alEffectf = (LPALEFFECTF) alGetProcAddress( "alEffectf" ); if ( alEffectf == null ) hasEFX = false;
alEffectfv = (LPALEFFECTFV) alGetProcAddress( "alEffectfv" ); if ( alEffectfv == null ) hasEFX = false;
alGetEffecti = (LPALGETEFFECTI) alGetProcAddress( "alGetEffecti" ); if ( alGetEffecti == null ) hasEFX = false;
alGetEffectiv = (LPALGETEFFECTIV) alGetProcAddress( "alGetEffectiv" ); if ( alGetEffectiv == null ) hasEFX = false;
alGetEffectf = (LPALGETEFFECTF) alGetProcAddress( "alGetEffectf" ); if ( alGetEffectf == null ) hasEFX = false;
alGetEffectfv = (LPALGETEFFECTFV) alGetProcAddress( "alGetEffectfv" ); if ( alGetEffectfv == null ) hasEFX = false;
alGenFilters = (LPALGENFILTERS) alGetProcAddress( "alGenFilters" ); if ( alGenFilters == null ) hasEFX = false;
alDeleteFilters = (LPALDELETEFILTERS) alGetProcAddress( "alDeleteFilters" ); if ( alDeleteFilters == null ) hasEFX = false;
alIsFilter = (LPALISFILTER) alGetProcAddress( "alIsFilter" ); if ( alIsFilter == null ) hasEFX = false;
alFilteri = (LPALFILTERI) alGetProcAddress( "alFilteri" ); if ( alFilteri == null ) hasEFX = false;
alFilteriv = (LPALFILTERIV) alGetProcAddress( "alFilteriv" ); if ( alFilteriv == null ) hasEFX = false;
alFilterf = (LPALFILTERF) alGetProcAddress( "alFilterf" ); if ( alFilterf == null ) hasEFX = false;
alFilterfv = (LPALFILTERFV) alGetProcAddress( "alFilterfv" ); if ( alFilterfv == null ) hasEFX = false;
alGetFilteri = (LPALGETFILTERI) alGetProcAddress( "alGetFilteri" ); if ( alGetFilteri == null ) hasEFX = false;
alGetFilteriv = (LPALGETFILTERIV) alGetProcAddress( "alGetFilteriv" ); if ( alGetFilteriv == null ) hasEFX = false;
alGetFilterf = (LPALGETFILTERF) alGetProcAddress( "alGetFilterf" ); if ( alGetFilterf == null ) hasEFX = false;
alGetFilterfv = (LPALGETFILTERFV) alGetProcAddress( "alGetFilterfv" ); if ( alGetFilterfv == null ) hasEFX = false;
alGenAuxiliaryEffectSlots = (LPALGENAUXILIARYEFFECTSLOTS) alGetProcAddress( "alGenAuxiliaryEffectSlots" ); if ( alGenAuxiliaryEffectSlots == null ) hasEFX = false;
alDeleteAuxiliaryEffectSlots = (LPALDELETEAUXILIARYEFFECTSLOTS) alGetProcAddress( "alDeleteAuxiliaryEffectSlots" ); if ( alDeleteAuxiliaryEffectSlots == null ) hasEFX = false;
alIsAuxiliaryEffectSlot = (LPALISAUXILIARYEFFECTSLOT) alGetProcAddress( "alIsAuxiliaryEffectSlot" ); if ( alIsAuxiliaryEffectSlot == null ) hasEFX = false;
alAuxiliaryEffectSloti = (LPALAUXILIARYEFFECTSLOTI) alGetProcAddress( "alAuxiliaryEffectSloti" ); if ( alAuxiliaryEffectSloti == null ) hasEFX = false;
alAuxiliaryEffectSlotiv = (LPALAUXILIARYEFFECTSLOTIV) alGetProcAddress( "alAuxiliaryEffectSlotiv" ); if ( alAuxiliaryEffectSlotiv == null ) hasEFX = false;
alAuxiliaryEffectSlotf = (LPALAUXILIARYEFFECTSLOTF) alGetProcAddress( "alAuxiliaryEffectSlotf" ); if ( alAuxiliaryEffectSlotf == null ) hasEFX = false;
alAuxiliaryEffectSlotfv = (LPALAUXILIARYEFFECTSLOTFV) alGetProcAddress( "alAuxiliaryEffectSlotfv" ); if ( alAuxiliaryEffectSlotfv == null ) hasEFX = false;
alGetAuxiliaryEffectSloti = (LPALGETAUXILIARYEFFECTSLOTI) alGetProcAddress( "alGetAuxiliaryEffectSloti" ); if ( alGetAuxiliaryEffectSloti == null ) hasEFX = false;
alGetAuxiliaryEffectSlotiv = (LPALGETAUXILIARYEFFECTSLOTIV) alGetProcAddress( "alGetAuxiliaryEffectSlotiv" ); if ( alGetAuxiliaryEffectSlotiv == null ) hasEFX = false;
alGetAuxiliaryEffectSlotf = (LPALGETAUXILIARYEFFECTSLOTF) alGetProcAddress( "alGetAuxiliaryEffectSlotf" ); if ( alGetAuxiliaryEffectSlotf == null ) hasEFX = false;
alGetAuxiliaryEffectSlotfv = (LPALGETAUXILIARYEFFECTSLOTFV) alGetProcAddress( "alGetAuxiliaryEffectSlotfv" ); if ( alGetAuxiliaryEffectSlotfv == null ) hasEFX = false;
}
alGetError();
if ( !hasEFX ) { ALError( "Failed to retreive EFX extension functions.\n" ); return; }
alGenFilters(1, &obj);
checkForErrors();
current = filterNames;
for(i = 0;filters[i] != AL_FILTER_NULL;i++)
{
char *next = strchr(current, ',');
alFilteri(obj, AL_FILTER_TYPE, filters[i]);
if(alGetError() == AL_NO_ERROR) current = next+1;
else memmove(current, next+1, strlen(next));
}
alDeleteFilters(1, &obj);
checkForErrors();
printList("Supported filters", ',', filterNames);
alGenEffects(1, &obj);
checkForErrors();
current = effectNames;
for(i = 0;effects[i] != AL_EFFECT_NULL;i++) {
char *next = strchr(current, ',');
alEffecti(obj, AL_EFFECT_TYPE, effects[i]);
if(alGetError() == AL_NO_ERROR)
current = next+1;
else
memmove(current, next+1, strlen(next));
}
alDeleteEffects(1, &obj);
checkForErrors();
printList("Supported effects", ',', effectNames);
return hasEFX=true;
}
}
class ALDeviceDescription : ListItem {
String name;
property String name { set { delete name; name = CopyString(value); } };
~ALDeviceDescription() { delete name; }
}
class ALDeviceDescriptions : LinkList<ALDeviceDescription>
{
bool inList(char *fn) { for (a : this; contains(a.name, fn)) return true; return false; }
ALDeviceDescription find(char *fn) { for (a : this; contains(a.name, fn)) return a; return null; }
}
// Simple Sound Manager
class ALSoundManager {
ALWaveForms waveforms { };
ALUREStreams streams { };
ALSounds sounds { };
ALCDeviceContexts deviceContexts { };
bool error,initialized,muted,hasOggExtension,hasEAX;
float distance, gain, max_gain, min_gain, dopplerFactor, dopplerVelocity;
int version_major, version_minor, refreshRate;
// uint bufferClamp, sourceClamp;
uint monoSources, stereoSources;
ALDeviceDescriptions outputs { };
ALDeviceDescriptions inputs { };
Timer timer { this, delay=(1.0f/refreshRate); started=false;
bool DelayExpired()
{
alureUpdate(); //printf( "alureUpdate()\n" ); ALError("Update: ");
for ( s : sounds) s._BetweenFrames();
sounds.DeleteDone();
timer.delay = (1.0f/refreshRate);
timer.Start();
return true;
}
};
// EAX 2.0 GUIDs
EAXSet eaxSet; // EAXSet function, retrieved if EAX Extension is supported
EAXGet eaxGet; // EAXGet function, retrieved if EAX Extension is supported
EFX efx {}; // EFX function bindings
SoundVector position, velocity, orientation;
// ^orientation of the Listener. (first 3 elements are "at", second 3 are "up")
// Also note that these should be units of '1'.
//bufferClamp=MAX_AUDIO_BUFFERS;
//sourceClamp=MAX_AUDIO_SOURCES;
refreshRate=60;
distance = 10000.0f; gain=1.0f; max_gain=1.0f; min_gain=0.0f;
position = SoundVector { x=0.0f, y=0.0f, z=0.0f };
velocity = SoundVector { x=0.0f, y=0.0f, z=0.0f };
orientation = SoundVector { x=0.0f, y=0.0f, z=-1.0f, a=0.0f, b=1.0f, c=0.0f };
ALSoundManager() {
this.timer.delay = (1.0f/refreshRate);
this.timer.Start();
}
~ALSoundManager()
{
for (s : sounds) { s.Release(); }
delete sounds;
delete efx;
deviceContexts.Free();
for(s : streams) s.Stop();
streams.Free();
delete waveforms;
}
inline void ClearError() { alGetError(); }
ALCdevice *currentDevice() { return alcGetContextsDevice(alcGetCurrentContext()); }
ALCcontext *currentContext() { return alcGetCurrentContext(); }
// Forces initialization defaulting to software when available.
// You would use this to support EFX on most contemporary sound devices.
// The device context is returned, or null if none was initialized.
ALCDeviceContext InitSoftware() {
ALCDeviceContext alc { };
alc.Init("Generic Software",0);
if ( !Init(alc) ) { delete alc; return null; }
return alc;
}
ALCDeviceContext InitDirectSound() {
ALCDeviceContext alc { };
alc.Init("DirectSound",0);
if ( !Init(alc) ) { delete alc; return null; }
return alc;
}
ALCDeviceContext InitContext( char * context ) {
ALCDeviceContext alc { };
alc.Init(context,0);
if ( !Init(alc) ) { delete alc; return null; }
return alc;
}
// Initialize OpenAL and clear the error bit.
bool Init( ALCDeviceContext alc ) {
const char *str;
if ( initialized ) return;
if ( alc ) { deviceContexts.Insert(null, alc); }
else { alc = ALCDeviceContext { }; alc.PickDefault(); deviceContexts.Insert(null, alc); }
str = alcGetString(alc.device, ALC_DEVICE_SPECIFIER);
printf( "OpenAL initialized: %s\n", str );
//unusedBuffers=bufferClamp;
//unusedSources=sourceClamp;
alGetError();
if ( alGetError() != AL_NO_ERROR ) {
printf( "Init: Error initializing audio!\n" );
initialized=false;
return false;
}
printf( "ALSound: Initializing.\n" );
{ ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
alcGetIntegerv(alc ? alc.device : device, ALC_MONO_SOURCES, 1, &monoSources);
alcGetIntegerv(alc ? alc.device : device, ALC_STEREO_SOURCES, 1, &stereoSources);
}
printf( "Detected %d hinted mono source%s, and %d hinted stereo source%s.\n",
monoSources, monoSources != 1 ? "s" : "", stereoSources, stereoSources != 1 ? "s" : "" );
if ( alIsExtensionPresent( "AL_EXT_vorbis" ) != AL_TRUE ) {
printf( "Ogg Vorbis extension not available! This is probably not Linux.\n" );
hasOggExtension = false;
} else hasOggExtension = true;
// Check for EAX 2.0 support and
// Retrieves function entry addresses to API ARB extensions, in this case,
// for the EAX extension. See Appendix 1 (Extensions) of
// http://www.openal.org/openal_webstf/specs/OpenAL1-1Spec_html/al11spec7.html
hasEAX = alIsExtensionPresent( "EAX2.0" );
if ( hasEAX ) {
printf( "EAX 2.0 extension is available.\n" );
eaxSet = (EAXSet) alGetProcAddress( "EAXSet" ); if ( eaxSet == null ) hasEAX = false;
eaxGet = (EAXGet) alGetProcAddress( "EAXGet" ); if ( eaxGet == null ) hasEAX = false;
if ( !hasEAX ) ALError( "Failed to retreive EAX extension functions.\n" );
}
if ( !efx.Init() ) { delete efx; efx=null; printf( "EFX did not initialize properly, and therefore is unavailable.\n" ); }
DeviceInfo( );
alGetError();
return true;
}
ALSound Cue( char *filename, float pitch, float gain, bool looped, bool createSource )
{
ALSound sound;
int newid;
if ( !filename ) return null;
alGetError();
// if ( unusedBuffers <= 0 ) { printf( "ALSound:Cue() No more buffers available (%d used)\n", bufferClamp-unusedBuffers ); return null; }
sound = ALSound { pitch=pitch, gain=gain, loop=(ALboolean) looped, name=CopyString(filename) };
sound.waveform=waveforms.findOrLoad(filename);
sound.format=sound.waveform.sfinfo.format;
sound.size=sound.waveform.sampleCount * sound.waveform.sfinfo.channels *2;
sound.freq=(uint) sound.waveform.sfinfo.samplerate;
//sound.loop = looped ? AL_LOOP : 0;
sound.duration=(float) sound.waveform.sampleCount / (float) sound.freq;
//sound.printInfo();
sound.source = ALSource { };
// sources.Insert(null, sound.source);
//unusedSources--;
alGenSources(1, &(sound.source.id)); if(ALError("ALSoundManager:Cue:alGenSources")) { delete sound; return null; }
sound.source.bindSound(sound);
sounds.Insert(null, sound);
return sound;
}
ALSound CueAndPlay( char *filename, float pitch, float gain, bool looped, bool createSource ) {
ALSound s = Cue( filename, pitch, gain, looped, createSource );
// printf( "Attempting to play %s\n", filename );
// if ( !s ) printf( "Sound was null, so it couldn't have been played.\n" );
// else {
// printf ( "Playing sound '%s' buffer %d source %d\n", s.name, (int) s.buffer.id, (int) s.source.id );
if ( s ) s.Play();
return s;
}
ALUREStream AddStream( char *fn, ALSource source, bool loop, bool calcLength ) {
ALUREStream stream { };
if ( !source ) { // Bind the buffer with the source.
stream.source = ALSource { };
//unusedSources--;
alGenSources(1, &(stream.source.id)); if(ALError("ALSoundManager:WAV:alGenSources")) { return stream; }
stream.source.bindStream(stream);
// Do another error check and return.
if(ALError("ALSoundManager:AddStream:alSource")) { return stream; }
}
else stream.source=source;
streams.Insert(null, stream);
incref stream;
stream.manager=this;
stream.loop=loop;
if ( !stream.LoadIntoSource(fn,stream.source,calcLength) ) { printf("ALSoundManager:AddStream:stream.Create() had an error.\n" ); return stream; }
return stream;
}
void SetListenerValues()
{
alListenerfv(AL_POSITION, position.asFloat3() );
alListenerfv(AL_VELOCITY, velocity.asFloat3() );
alListenerfv(AL_ORIENTATION, orientation.asFloat6() );
}
void SetGain( float f ) { gain=f; alListenerf( AL_GAIN, gain ); }
// Detaches all sources from buffers. This is done before all sounds are discarded.
void Clear() { }
bool ResumeAll( void )
{
int sourceAudioState = 0;
alGetError();
for ( s : sounds) { // Are we currently playing the audio source?
alGetSourcei( s.source.id, AL_SOURCE_STATE, &sourceAudioState );
if ( sourceAudioState == AL_PAUSED )
{
alSourcePlay( s.source.id );
if ( ALError( "ALSoundManager:ResumeAll:alSourcePlay") ) return false;
s.source.state=playing;
}
}
muted=false;
return true;
}
bool PauseAll() {
int sourceAudioState = 0;
alGetError();
for ( s : sounds ) {
if ( s.Playing() ) {
alGetSourcei( s.source.id, AL_SOURCE_STATE, &sourceAudioState );
if ( sourceAudioState != AL_PAUSED ) {
alSourcePause( s.source.id );
if ( ALError( "ALSoundManager:PauseAll:alSourcePa