Some OpenAL reflections

Hi there!

I tried out the new AL API of OpenTK tonight, it stopped when downloading alut.dll was not enough to kickstart a simple Alut.Init()/OpenTK0.9 windows forms application.

I'm new to OpenAL, so I'm approaching the AL API with a "C#/OpenTK"-mindset.

Sorry if I sound rant-ish! I just want to express my opinion. Here we go:

1. AL.GenBuffers(out uint bufferid): maybe GenBuffer without 's' is better name..? Same with GenSources(out uint sourceid) and DeleteSources(..)
2. IntelliSense-documentation refers to "ALuint"'s and not uint, which are used in reality. In general,
the IntelliSense seems more "c-ish" than "OpenTK-ish" to me :)
3. Is it really necessary to error-check everything all the time..? Could we not have some kind of exception system? I think error-polling is so tedious, it gets really tempting to either
a) skip it altogether (not good)
b) write a thin AL-wrapper around the OpenTK-AL class (not nice either)
4. Alut.Init() returns a strange enum type called "AL.Bool" -- why not use .NET's bool type instead?
5. Why does AL.DeleteSources() take a ref to an uint instead of just an uint? If I want to set the id to 0, I'll rather do it myself.
6. I would like GenSource() to take no out parameter, just return a uint.
7. I would like GenSources(int) to take the number of source id's to generate, and return a uint[].
8. 6-7 applies to Buffers too. This is more C#-idiomatic, out and ref feel very C-idiomatic.


Comments

Re: Some OpenAL reflections
posted by Inertia

1. Used to be like that in alpha stage, changed into an overload to help finding functions through intellisense (would bloat it, while the GenBuffer and GenBuffers basically do the same. Bad practice having 2 different named functions accomplishing the same task)
2. well, it's a binding to a C api ...
3. Fiddler had a bold idea regarding this: different debug and release versions of OpenTK.dll. While release builds will be unchanged, debug builds imply *L.GetError() after each and every function call and throw if the returned error != NoError. Until some details are sorted out (e.g. how do you copy Debug.dll to bin/Debug/OpenTK.dll, but Release.dll to bin/Release/OpenTK.dll when building from the IDE?) I'd recommend checking for errors only after context init and after each loading of sound data, and assign one key to print you the error on request while the app is running.
4. It will return bool in 0.9.1 (or for a couple of weeks already if you build from svn). AL.Bool is deprecated and was created due to some problem, which could be worked around with Fiddler's help.
5. because there's no 'in'?
6-8. Np, will add it. For Efx aswell.

Re: Some OpenAL reflections
posted by the Fiddler

2) Yep, should be changed to reflect the actual parameters. Docs are being improved from version to version.

3) The idea is to have a safety net while developing the app, to help you pinpoint and fix problems as soon as they come up. You should still manually check for errors in critical parts of the code, since the automatic error checking will not occur in release builds.

OpenTK will do this in (almost) all constructors in 0.9.1 (AudioContext, GraphicsContext, GameWindow, GLControl, DisplayDevice and so on)

5) Becuase the C it is expecting a pointer (this function may delete 1 or more sources I think?)

Inertia, if we are going to add GenSource() and GenSources(), maybe do the same for DeleteSource() and DeleteSources() for symmetry?

I don't know how far to take this, though, as changes like these aren't feasible on a large scale (e.g. OpenGL 2). It's still a good idea to streamline the API wherever we can though.

Re: Some OpenAL reflections
posted by Inertia

Wtf you convinced me to use overloads instead of 2 functions, now separate them again? :P

AL.GenSource(out uint bid) is _not_ a native OpenAL function .. it's basically calling AL.GenSources(1, out bid) internally. I don't think this is a good idea, because it will add 10 new functions to OpenAL, which have the same effect. If we start with separating functions like this, AL.SourcePlay's overloads must be split aswell (one overload plays only 1 source, the other plays an array of sources).

Imo the current solution using overloads isn't very error-prone, because the amount of parameters differs.

Re: Some OpenAL reflections
posted by objarni

In my humble opinion, the "i want to create an array of id's" is not so useful to begin with.

Since the syntax for generating such an array is cumbersome, and the alternative is really simple, is there really any reason to not just skip the incredibly ugly

GenBuffers(int, some-kind-of-quasi-pointer-thingie-here)

or, if there are performance reasons for having this array-allocation-way-of-doing-things, using the more C#-idiomatic

uint[] GenBuffers(int)

..? (same for GenSources and DeleteSources/Buffers).

Re: Some OpenAL reflections
posted by Inertia

Mhmm are you going to change GL.GenTextures() too? I think this should be consistent if changed. Would also make it harder to use C manuals/reference.

Re: Some OpenAL reflections
posted by objarni

GenTextures is another example, yes.

About making it harder to humanly parse c manuals/references - yes a little little harder. If you know how to read c, and are used to the C#/OpenTK way/idioms, I don't think it's any real difference in practice. I do think using ref/out/pointer/whatnot is harder for a C# developer, however.

my 2 cc as always :)

Re: Some OpenAL reflections
posted by Inertia

We really should leave it as is, be it for the sole reason not to change the API now, and change it again when generating IL codes directly comes into reach. uint[] DeleteBuffers(int n); makes little sense, it's like trying to make a women's shoe fit a man's foot with force, because it looks prettier. We already had this discussion iirc, and I'm not going to sacrifice performance just so you can save those 3 letters 'ref' which are right next to each other on the keyboard :P (the IDE will remind you kindly that the parameter requires a keyword, if it's missing)

Re: Some OpenAL reflections
posted by objarni

uint[] DeleteBuffers(int n);

Hehe! You're right, that makes no sense.


.. and I'm not going to sacrifice performance just so you can save those 3 letters 'ref' which are right next to each other on the keyboard :P

It's not my fingers I'm worried about, it's my eyes! And OpenTK's reputation as being elegant. Seriously, you can't claim performance reasons in this case:

DeleteBuffer(ref int);
DeleteBuffer(int);  // this should actually be faster :)

I'm getting the feeling you're just a little bit lazy Inertia hehe :)

Which of course is great when you're a programmer, but you really have to give me stronger arguments than "we can't change the oh-so-stable API of OpenTK". That really isn't a good argument! I believe the versions before v1.0 should be adressing issues of naming, idioms and such. This is the chance to fix the interface, which admittedly gets to be a more sore thing to do after v1.0!

Re: Some OpenAL reflections
posted by the Fiddler

Heh, why don't you give this a try yourself?

It's nothing difficult, just go over the 1528 OpenGL functions and write overloads like the above wherever it makes sense. Or limit yourself to the core - only 570 different functions to check then!

I'd really welcome such a patch. :)

Re: Some OpenAL reflections
posted by objarni

1. Why overloads ? Is there a technical reason the old ones should be left ?
2. What else besides checking out the SVN version of OpenTK do I need to do? (Install Ant?)
3. Which Visual Studio should I test-compile in? Is Express Edition 2005 OK?

I might do that, if that is what it takes (I'm quite sensitive maybe it's because I'm into literature also).

Edit: I might do that when it comes to name allocation for Textures/Buffers/Sources. What more names are there in GL/AL?

Re: Some OpenAL reflections
posted by the Fiddler

1. Why overloads ? Is there a technical reason the old ones should be left ?

The core GL.cs is generated by a tool. You'd place your hand-written functions in GLHelper.cs, so they'd technically be overloads until/unless I update the generator to suppress these functions.

Unfortunately it's not possible to update the generator to create these functions automatically... (it would have been easy otherwise)

2. Nothing. Just execute "Build.exe vs" from the commandline to create a Visual Studio project (Build.exe can be found in the Build/ folder).

3. VS2005 is fine.

For OpenGL I can think lists, textures, buffers, shaders and programs on the top of my head. For OpenAL it's Buffers, Sources and (I think) Effects.

Edit: Actually it's be best if the overloads were left in. There are programs that rely on those, and it's the most common API (identical between Tao, OpenTK, CsGL etc). You'd never see them anyway, if you don't want them (intellisense displays the functions with the fewest parameters first) :)

Re: Some OpenAL reflections
posted by Inertia

AL: Sources, Buffers
Efx: Effects, AuxiliaryEffectSlots, Filters
GL: Buffers, Lists, Queries, Textures

It's not so much the problem of doing it, more the insight why it's necessary. I'll keep using ref/out, because they are the way I understand telling the API about locations in memory.

Edit: Shaders only return 1 Handle I think. GL.Arb.GenPrograms is missing tho

DeleteBuffer(ref int);
DeleteBuffer(int);  // this should actually be faster :)

Why should it be faster? Because you typed less?

Re: Some OpenAL reflections
posted by the Fiddler

The argument for these, is that they'd fit the managed world better - there's this strange aversion regarding memory locations and pointers and the ref/out overloads remind people of these :p

Edit: arguable the first is a little cleaner/nicer to read:

int[] buffers = GL.GenBuffers(10);
// -- vs --
int[] buffers = new int[10];
GL.GenBuffers(buffers.Length, buffers);

Even if the first just calls the latter internally:

public static int[] GenBuffers(int n)
{
    int[] buffers = new int[n];
    GL.GenBuffers(buffers.Length, buffers);
    return buffers;
}

Re: Some OpenAL reflections
posted by Inertia

Mhmm actually I think the ref/out keywords are one of the best things about C#. At least the 'out' makes it very clear in which direction the data is used. I'd like to see an 'in' keyword too (compiler could check if you make any modification inside the method to it and complain), because it is very expressive about the direction.

Vector3.Add( in V1, in V2, out V3 );
V3=Vector3.Add( V1, V2 );

Regardless of performance, the 1st version is more like the machine understands the operation, while the 2nd is more human-readable. The 2nd version can easily lead to bugs with just a small slip:

V3-=Vector3.Add( V1, V2 );

Are you sure you would have found this slip in a larger mathematical calculation on 1st sight? Compiler will not complain about this, while any usage error to the 1st mentioned function will.

Edit:
int[] buffers = GL.GenBuffers(10);

does not allow:

int[] buffers = new int[10000];
GL.GenBuffers(5, out buffers[1234]);

Re: Some OpenAL reflections
posted by the Fiddler

A reason to keep both :)

Although this example isn't really useful for name generation (which is probably why they made the shader stuff generate only one name, and not an array).

I read recently a rather interesting post, which claimed that you do not need to generate a name before using it. According to the poster, this is not an error as the driver will allocate it as soon as you bind it. He was using this to define his own naming algorithm, which would allow him to avoid keeping a map of allocated resources.

Slightly off-topic, but interesting nonetheless.

Re: Some OpenAL reflections
posted by objarni

Fiddler/Inertia: I guess we have to agree that we disagree here, then.

I find ref/out just barely better than pointers, because pointers' is basically what they are, even if I agree separating the intensions are better than just passing a pointer. It's software engineering experience leaking through, I guess.

Also, there's less to parse for the brain the less letters there are on screen!

Regarding the syntax

V3 = Vector3.Add( V1, V2 );

.. I would really like to write it the way I write it on paper / read it in mathematics books:

V3 = V1 + V2; // I know we've been through this discussion already, just wanted to express the ideal

That is for me the cleanest syntax.

As a wise man said, "90% of a programmers life is reading code, 10% is typing".

int[] buffers = new int[10000];
GL.GenBuffers(5, out buffers[1234]);

Inertia: this example seems a little constructed to me ;)

Re: Some OpenAL reflections
posted by Inertia

My point was, you cannot enforce your preference on other users, no matter how much more elegant it might read. Someone might have use for generating a larger array than he required handles. (if the array is 10000 or 10 handles is just a detail here, if you only want to generate 5 at this point.)

I believe this is pretty much about how much you are willing to bend your mind to fit into the machine's way of understanding, or the other way around. Once you have taken a look at assembly language you will see that it's only an illusion trying to bend the machine's 'thinking', so you might aswell express your commands in a way it can understand efficiently.

Re: Some OpenAL reflections
posted by objarni

Inertia: please don't lecture me on assembly language! :)

I've done assembly from the Commodore 64 to Amiga to PC.. I've even built a couple of emulators!

When you've done enough programming, you recognise and appreciate languages and syntaxes that are suitable for humans, not machines.

If you like low-level stuff, why don't you program in C/Assembly..? C#/OpenTK is a different animal, IMHO.

Maybe you have a lot of code using Name-generation the way it looks right now in OpenTK, and don't want the additional work of changing this..? So let there be overrides, both convenient and inconvenient, at least for awhile. They could be marked "deprecated" and removed until OpenTK1.0, or just kept all along.

I'm trying to make the best of OpenTK, I hope you are too. I see no reason why the APIs need to be harder/uglier to use than is possible.

Is there no one else having an opinion about the Gen/Delete methods and their ref/out usage? Compare these two ways:

1.

uint name = AL.GenBuffer();
uint[] names = AL.GenBuffers(10);
AL.DeleteBuffer(name);
AL.DeleteBuffers(names);

2.

uint name;
AL.GenBuffers(out name);
uint[] names = new uint[10];
AL.GenBuffers(names.Length, out names[0])
AL.DeleteBuffers(ref name);
AL.DeleteBuffers(name.Length, ref names);

Re: Some OpenAL reflections
posted by Mincus

I'm quite happy with how the GL stuff currently works. I've not looked at the AL things too much yet. I'm waiting on the 0.9.1 release first.

Re: Some OpenAL reflections
posted by Inertia

I've changed the source code for 2. I'd like to point out that you're basically doing nothing else but merging declaration and initialization. This is not a common use case, you typically want your arrays to survive the exit of the current method.

>If you like low-level stuff, why don't you program in C/Assembly..?

Productivity. You spend far less time in the debugger with C#

>...don't want the additional work of changing this..?

Important is the fact that removing current GL.GenTextures overloads in favor of compacting 2 lines into 1 will break pretty much every app written in OpenTK so far. I don't see removal as an option, no matter how much you keep nagging. I see no problem adding your suggestion as an additional overload though.

>I see no reason why the APIs need to be harder/uglier to use than is possible.

It already is damn pretty compared to the alternatives.

Re: Some OpenAL reflections
posted by objarni


I've changed the source code for 2. I'd like to point out that you're basically doing nothing else but merging declaration and initialization. This is not a common use case, you typically want your arrays to survive the exit of the current method.

I guess we come from different backgrounds. To me this is the most basic use case:

uint id = AL.GenSource();
// use id

It just feels very intuitive to me.


Important is the fact that removing current GL.GenTextures overloads in favor of compacting 2 lines into 1 will break pretty much every app written in OpenTK so far. I don't see removal as an option, no matter how much you keep nagging. I see no problem adding your suggestion as an additional overload though.

Well then please Inertia, could you add these kind of overloads, if only for me? Or at least not be religiously opposed to it if I do the hard work myself?

Re: Some OpenAL reflections
posted by teichgraf

I agree with objarni that OpenTK would benefit from such overloads. Isn't one of the OpenTK goals to provide a nice interface?
But these overloads should be an extra gimmick. Removing the original methods would involve a too big interface change. And I think this is not acceptabel for most users.
To make things clear, the current API is already really comfortable and intuitive. And this was the main reason why I decided to change from Tao to OpenTK.

Anyhow, it's a lot of work to do. And who should do this? :) Nevertheless, I think some of the overloads could also be generated automatically by a tool. Especially the simple stuff with one out keyword in the method siganture could be parsed and generated.

void Foo(out var p)
{
   // ...
}

// generates...

var Foo()
{
   var res;
   //...
   return res;
}

Re: Some OpenAL reflections
posted by the Fiddler

So we all agree that these functions would be nice to have. :)

Such overloads fall into the "nice feature" category: if someone is irked by their lack and implements them, I'll be happy to apply the patch; if noone bothers, they may be added in the future.

Edit: about automatically generating these, it's not as simple as it sounds.

Changing the generator may have unintended side-effects - how are you going to check the whole API to make sure it didn't change a function it shouldn't have?

Far easier to add them by hand, where you control exactly what you change.

Re: Some OpenAL reflections
posted by Inertia

Sure, go ahead and add them, here is a template:

public static void DeleteBuffers( uint[] buffers )
{
  AL.DeleteBuffers( buffers.Length, ref buffers );
}

public static uint[] GenBuffers( int n )
{
  uint[] result = new uint[n];
  AL.GenBuffers( result.Length, out result[0] );
  return result;
}

Still fail to see the greatness in this, but whatever makes you happy ...

Re: Some OpenAL reflections
posted by the Fiddler

To whomever tries to do this: provide int overloads (either forego uints, or add both). CLS-compliance rules:

public static void DeleteBuffers( int[] buffers )
{
  AL.DeleteBuffers( buffers.Length, ref buffers );
}

public static int[] GenBuffers( int n )
{
  int[] result = new int[n];
  AL.GenBuffers( result.Length, out result[0] );
  return result;
}

Re: Some OpenAL reflections
posted by Inertia

*Argh* Just managed to dispel that compliance nightmare. Thanks for the reminder. :p

P.S. Accurate inline docu would be nice too. Thanks :)

Re: Some OpenAL reflections
posted by objarni

Should both int and uint overloads exist?

Re: Some OpenAL reflections
posted by the Fiddler

Uints are not strictly necessary, but would be nice for reasons of symmetry.

Edit: Uints should be marked as [CLSCompliant(false)], too.

Re: Some OpenAL reflections
posted by objarni

When I get some free time during the next few days, I plan to make an effort to write "managed friendly versions" of Gen/Delete for the named objects OpenTK uses in GL and AL.

That is, I will write overloads with the following signatures, for example AL Buffers:

uint GenBuffer()
int GenBuffer()
uint[] GenBuffers(int n)
int[] GenBuffers(int n)
void DeleteBuffer(uint buffer)
void DeleteBuffer(int buffer)
void DeleteBuffers(uint[] buffers)
void DeleteBuffers(int[] buffers)

Same eight methods will be created for each of the following object types:

AL: Sources, Buffers
Efx: Effects, AuxiliaryEffectSlots, Filters
GL: Buffers, Lists, Queries, Textures

I will skip the array-versions in cases where there exist no corresponding "ref/out" array version (if any).

GLHelpers.cs has been mentioned as an apropriate target source file in the GL case; is there any such file for AL/Efx?

For me, the simplest thing would be to just post the result in this thread. Is this OK with you?

Re: Some OpenAL reflections
posted by objarni

Having both of these isn't allowed (compile time error):

    [CLSCompliant(true)]
    public static int GenTexture()
    {
      int texture;
      GL.GenTextures(out texture);
      return texture;
    }
    [CLSCompliant(false)]
    public static uint GenTexture()
    {
      uint texture;
      GL.GenTextures(out texture);
      return texture;
    }
// Error        1       Type 'GL' already defines a member called 'GenTexture' with the same parameter types

I guess it's because methods with equal parameter lists but different return type count as having the same signature in C#.

Should I ditch the CLSCompliant version of the uint version?