[OpenAL] EAX issues (Lengthy technical question)

Started by Matzon, June 18, 2003, 20:26:27

Previous topic - Next topic

Matzon

Hi guys

Just need somekind of input on this tricky bit.
During the "conversion" I was looking at the EAX implementation. It's been some time since I made the initial implementation and comparing to the joal implementation, I went "whaaaat - why is my code *so* bloated compared to the "other" implementation???"

It all came down to this (the technical bit):
Eax has two calls:
ALenum EAXGet(const struct _GUID *propertySetID, ALuint property, ALuint source, ALvoid *value, ALuint size);
 ALenum EAXSet(const struct _GUID *propertySetID, ALuint property, ALuint source, ALvoid *value, ALuint size);

*propertySetID - A pointer to the property set GUID of the object being g/set (a listener or a source)
property - The property being g/set
source - The ID of the source to be g/set
*value - A pointer to the value being returned/set
size - The size of the data storage area pointed to by *value

One for reading eax values, and one for writing. The following is an example of reading the 'DSPROPERTY_EAXLISTENER_ENVIRONMENT', that is the environment the listener is positioned in (ie. EAX_ENVIRONMENT_CAVE or EAX_ENVIRONMENT_CITY).
int MyValue;
EAXGet(DSPROPSETID_EAX20_ListenerProperties, DSPROPERTY_EAXLISTENER_ENVIRONMENT, 0, &MyValue, sizeof(MyValue));


this is all fine and dandy. However the tricky bit is that there is also a property called 'DSPROPERTY_EAXLISTENER_ALLPARAMETERS'.
This property will read ALL the possible EAX values. Thus you would need a structure large enough to hold this, enter: 'EAXLISTENERPROPERTIES'
This struct has all the possible values to be set:

typedef struct _EAXLISTENERPROPERTIES
{
    long lRoom;                    // room effect level at low frequencies
    long lRoomHF;                  // room effect high-frequency level re. low frequency level
    float flRoomRolloffFactor;     // like DS3D flRolloffFactor but for room effect
    float flDecayTime;             // reverberation decay time at low frequencies
    float flDecayHFRatio;          // high-frequency to low-frequency decay time ratio
    long lReflections;             // early reflections level relative to room effect
    float flReflectionsDelay;      // initial reflection delay time
    long lReverb;                  // late reverberation level relative to room effect
    float flReverbDelay;           // late reverberation delay time relative to initial reflection
    unsigned long dwEnvironment;   // sets all listener properties
    float flEnvironmentSize;       // environment size in meters
    float flEnvironmentDiffusion;  // environment diffusion
    float flAirAbsorptionHF;       // change in level per meter at 5 kHz
    unsigned long dwFlags;         // modifies the behavior of properties
} EAXLISTENERPROPERTIES, *LPEAXLISTENERPROPERTIES;

Passing 'DSPROPERTY_EAXLISTENER_ALLPARAMETERS' to EAXGet instead of the environement property (DSPROPERTY_EAXLISTENER_ENVIRONMENT) would result in the instance of the above struct to get filled with all the current values.

So far so good.

So how do we implement this in Java?

Two possible solutions:
The simple solution (JOAL)
Pass a java.nio.Buffer to the eaxGet method. That property will then be written to the start of the Buffer. If you pass 'DSPROPERTY_EAXLISTENER_ALLPARAMETERS' all parameters will be written. Thus you need to be sure that the Buffer supplied is big enough.

I can already hear you all go, yeah - thats a neat solution - whats the problem with that?
This simple solution has one *huge* problem:
Suppose you do pass 'DSPROPERTY_EAXLISTENER_ALLPARAMETERS'. How would you then read the 'dwEnvironment' value?
The only way to do that, is to calculate the offset to it, that is (imaginary method in Java) sizeof(lRoom) + sizeof(lRoomHF) + + + and so forth. Right up untill 'flReverbDelay' at which you'd have the index. Now, as a programmer I would *very* much hate to do that shit every time!

The more advanced solution (LWJGL)
We have created an EAXBufferProperties and an EAXListenerProperties object that manages all of this (well most of it).
These classes have 'getEnvironment()' and 'setEnvironment(int environment)' which neatly calculates all of the offsets for you.
and when it comes time to actually reading/writing a value, you just pass the address of it's internal buffer to the eaxGet method (just like you would with joal). This works beatifully as long as you read and write using the 'ALLPARAMETERS' property EACH TIME. What I discovered today, is that if you do NOT pass the 'ALLPARAMETERS' flag, it will read/and write from the base offset (that is 'lRoom'). Thereby defeating the purpose of all the get/set methods on the EAXBufferProperties object.

So, now that I've got you all confused :), here's the actual question.
I want to fix this - either I do the JOAL method and add some public static int fields that hold the offset in the buffer:

ByteBuffer EAXListener = allocateByteBufferLargeEnough();
ByteBuffer Temp = ((ByteBuffer)EAXListener.position(EAX.POSITION_OF_ENVIRONMENT)).slice();
eaxGet(EAX.LISTENER_GUID, EAX.EAXLISTENER_ENVIRONMENT, source_id, Temp, EAX.SIZE_OF_ENVIRONMENT);
int value = Temp.getInt(0);

OR

I could add (I'd still retain the old eaxG/Set methods, to allow the retrieval of single values using a scratch buffer) a
eaxGet method that took the actual EAXBufferProperties or EAXListenerProperties object, and manipulated that directly. Thus you would do:
(caveat, this could become static...)
EAXListenerProperties EAXListener = new EAXListenerProperties();
eaxGetListenerProperty(EAXListener, EAXListenerProperties.EAXLISTENER_ENVIRONMENT, source_id);
int value = EAXListener.getEnvironment();

Without trying to influence what you guys think, I think the later solution is *much* better, however this (ironically) is very un-LWJGL-like, in that it actually hides some complexity from you :P
It also implies that we need to have the usual:
eaxGet
 eaxSet

along with
eaxGetListenerProperty
 eaxSetListenerProperty
 eaxGetBufferProperty
 eaxSetBufferProperty

- these could ofcourse be method overloaded (eaxGetProperty(Buffer/Listener))

I need/want some input on this before I proceed... I don't expect the huge turnup in this thread, since it is a wee bit technical and specific - but if *anyone* has *any* input I would gladly accept it...

ps. is there a lengthy post award?

princec

I like the way you do it now. There's no need for us to make the API a pain in the arse now is there :)

You could, of course, simply do the "unwrapping" of the buffer on the Java side though - ie. provide this convenience class as part of the LWJGL. It's more than we normally do but EAX is pretty specialist anyway.

Cas :)