2014-08-24 21:17:22

This isn't in New Releases because I will metaphorically murder anyone who manages to miss the several warnings about how it's not ready for production.  Also, it's only useful to programmers.
Anyhow: here's the blog post.  I'll entertain comments and whatever via here as well as the other mentioned sources, and this is probably one of the two places (the other being my Twitter) that I will poke when the roadmap for 1.0 is ready.

My Blog
Twitter: @ajhicks1992

2014-08-25 07:12:26

Yeah Yeah!
Downloading the binaries! smile
*does a little happy dance!*

2014-08-25 07:54:43

note,
enum is a dependency that requires you install
pip install enum34
enum by itself is not the right thing!


Also, how do you play sounds? none of them play past the importing of the sound. So for example, on sim3d.py, I type in "sounds/sword.ogg" it plays and tells me to type a toople. I type (1,1,1) and hit enter and nothing happens.
In the file I looked and there is no "play" anywhere or anything that looked like play. I copied
sim.output_object = world
right under the last line
    source.position = vect
and nothing happened. I'm going to fiddle with it some more, but the only example that really works 100% is the sine.py.

2014-08-25 09:11:26 (edited by frastlin 2014-08-25 10:02:11)

Here is the fixed sim3d (what are the value limits?):


#demonstrates how to use the 3d simulation.
import libaudioverse
import collections

sim = libaudioverse.Simulation(device_index = -1)
world = libaudioverse.WorldObject(sim, "mit.hrtf")
source = libaudioverse.SourceObject(sim, world)
print """Enter a path to a sound file.
For best results, this should be mono.  If not, only the first (usually left) channel will be used."""
filepath = raw_input()

print """Enter python expressions that evaluate to 3-tuples (x, y, z).
this would look like:
1,0,0
Positive x is to your right, positive y is above you, and positive z is
behind you.
Enter quit to quit."""

a = True
vect = (0,0,0)

while a:
    z = raw_input("give a command")
    if z == "quit":
        a = False
    elif z.strip() != "":
        vect = eval(z)

    f = libaudioverse.FileObject(sim, filepath)
    source.inputs[0] = f, 0
    source.position = vect
    sim.output_object = world
print "done"



#here is a little scene that will show the sound playing at different locations without you doing anything


#demonstrates how to use the 3d simulation.
import libaudioverse, time
import collections

t = time.sleep

sim = libaudioverse.Simulation(device_index = -1)
world = libaudioverse.WorldObject(sim, "mit.hrtf")
source = libaudioverse.SourceObject(sim, world)

filepath = "sounds/sword.ogg"

def s(vect=(0,0,0)):
    """Is the script to play the sound. The sound needs time to play, so that is why there is sleeping at the end. the sleeping at the start is for your screen reader to say what the sound is. Pass in the sound location as a tupal like the default"""
    t(1)
    f = libaudioverse.FileObject(sim, filepath)
    source.inputs[0] = f, 0
    source.position = vect
    sim.output_object = world
    t(1.5)

print "listen to the sword in front of you"
s((0,0,0))
print "now hear it move to your left"
s((-1,0,0))
print"now it will move behind you"
s((-1,0,1))
print"now it will continue moving behind you"
s((0,0,1))
print "You hear it behind you to your right"
s((1,0,1))
print "Now it will move to your right, in front"
s((1,0,0))
print "and now we are back to the front"
s((0,0,0))

2014-08-25 15:27:58

Sounds which are short stop playing after the first repetition.  The examples do work, but you need to run them on longer files like music.  If it still doesn't work at that point, then it is indeed a bug.  I'm kind of thinking there will need to be a preview 2 at some point, but perhaps I'll just move from here to alpha.
Re-creating the file object as you are here is just clobbering your hard disk; if you want to modify it in this manner, place file.position = 0 at the end of the while loop.  While there will be a variant of the FileObject that caches for you, the vanilla one here is actually throwing out and re-reading the entire file every time you create it.  Still, good to know the garbage collector isn't causing crashes; this was one of the major problems in Camlorn_audio.
An object starts playing, but only when needed.  To not be overly technical, the device detects that it needs the world, the world detects that it needs the source, and the source detects that it needs the file.  If the file were never connected, time would not advance for it (read: it would be paused).  This is an incredibly useful thing, in that some objects are very expensive.  A prime example is the HRTF panner, which requires 44100*128*4=22579200 mathematical operations per second.  This number is scary and cannot be reduced.  It also pales in comparison to what a high-quality reverb is going to need, but you probably won't ever need to run more than one reverb at a time.
At the moment, setting obj.suspended to True on any object makes it stop advancing, even when needed.  This is equivalent to pausing.  I am going to be reworking this portion of the library to have an obj.state with a few different state values on everything: playing, playing silently, playing even when not needed, and paused.  The model here is that you are asserting that a thing is true, not calling a method to make it so.
Things like looping are missing because they're trivial, and I have bigger fish to fry still.  Looping is probably one of the next things I'll conquer (it will take maybe 10 minutes), but I've got an even bigger problem yet: Portaudio sucks beyond all telling and i may have to rewrite it.
And yeah, I'm an idiot for forgetting to put a note about enum34 in the readme. I'll go fix that now.  It's been so long since I installed it that I forgot I installed it.

My Blog
Twitter: @ajhicks1992

2014-08-25 18:08:44

When will it be possible to try it in C/C++ ?

C/C++ users know all about why I can't just give out a pre-built binary already, so there's not too much that needs
to be said about that.

I'm C/C++ user, and I don't know why. Could you explain ?

There are 10 kinds of people : those who know binary, and those who don't.

2014-08-25 19:01:43 (edited by camlorn 2014-08-25 23:48:50)

I'm not going to build the C/C++ version for you.  As far as I know it works now, but I've not yet written the build instructions.  There are actually more examples in C++ than in Python.
At the moment, it's only a C library.  I may use my bindings generator to expose C++ classes on top of it (so C++ classes forced into a C API that's then wrapped...).  Probably not, though.  I just don't see the need.
The reason I can't just build it is because of ABI and runtime disagreements.  Some people will want a static library because they're using a compiler that supports C++11.  Some will want just the DLL.  Some will want both the DLL and the export library.  I'm not going to figure this out for you and, given that you're choosing the complexity of C++ anyway, I feel this is completely fair.  Building it, once I write up the directions, should be fine.  If it's not, I rate this as a bug, at least so long as it's my fault.
Edit:
Not to mention the 32-bit vs. 64-bit battle, the whether I want to have more performance and not run on older computers battle...all of these things are controlled with compiler switches and CMake options, and they're part of the decisions a C/C++ project needs to make.

My Blog
Twitter: @ajhicks1992

2014-08-28 04:53:12

Is there any way currently to make the panning more or less than 1? I tried both (1,0.5,0) and (-0.5,0,0) and it still came out the same location, not in between. Also, what are you doing to make the "behind sound?" Can we make it stronger and weeker by increasing from 1 to 10? It doesn't work now, but could it?

2014-08-28 06:01:23

Is this with the build-in example?  If so, which one?  It could be a bug, but I need more information to determine one way or another.
if you're not using headphones, given that those are outputting HRTF, it's not going to sound right.
In the case of not using headphones, you need to use an amplitude panner.  I have one built-in, but no Python example for it.  It's identical to the HRTF panner, save that you don't have to pass it an HRTF file and it can be configured to work on more than 2 speakers (warning: doesn't actually work on more than 2 speakers, I've got to change the algorithm to use dot products and get surround sound maps working, the latter of which is going to take something like 1000 lines or a godsend).  Anyhow, replacing the HRTF panner with an amplitude panner in the HRTF example will work fine; the class name is libaudioverse.AmplitudePannerObject.  The case of sim3d.py is a bit more complex and needs some internal work.
The behind sound is done with a technique called convolution, using some data from MIT.  Increasing the behind sound with a number is not something easily done in HRTF configurations, but other panning strategies (which I will implement for imbedded devices, eventually) do allow for such.  The point here is realism, and alternative strategies lose that and the ability to pan vertically, at all.  Anything that isn't headphones can't provide this configuration either: either you're using stereo speakers and I can't do anything, or you're using a surround sound setup and the "behind sound" is whether or not I play through the speakers positioned behind you.  I'm not going to make guarantees either way, but the best headphone strategy available is HRTF and anything I do with it beyond the traditional stuff is basically original and publishable research (I.e. I don't know, because no one has come before besides trade secret companies and people who aren't talking).  A good deal of this is also sound design: the techniques I am using work on sounds with what is called a wide frequency spectrum, so something like a piece of flute music is going to have a lot less differentiation of when it's behind you than, say, a footstep.
Now, on to your problem.  If you mean that the sound isn't moving at all, then this is a bug and we need to talk.  But I think this is a misunderstanding of what the numbers are and that you're saying it's only either coming directly out the left or the right speaker.  The sim3d example is demonstrating how one makes a 3D world, and you're not supposed to use it like you are.  Those are coordinates in 3d space, not panning values.  Pretend they're in meters, though really this is configurable.  A value like (-0.5, 0, 0) means half a meter to the left of your head; (0.5, 0, 0) is half a meter to the right.  While traditional audiogame programmers are going to kill me for this, y is vertical and z is forward/behind (I'm not going to break conventions of every single math and science book as well as OpenGL, OpenAL, and properly configured DirectX without a better reason).  The effect you're aiming for is more simply achieved by panning an HRTF or amplitude panner from -90 to 90.  To see sounds "move in front of you", try z values of -10, i.e. (2, 0, -10).  yes, everyone who isn't in this community uses negative Z to mean in front, and the reasons seem to do with compass directions and trigonometry (and to really screw your brain, it's sometimes easiest or even almost necessary to make the positive X axis point north).  This will also eventually be configurable, but actually configuring it requires knowledge of 3D transformation matrices (so I might publish a table of "if you want this then use that" values).  Nevertheless, when using Libaudioverse worlds you work in world coordinates; you feed it object positions directly from wherever they are in your game, set up configuration values for whether or not it's feet or meters or millimeters or inches or whatever, and let it go.  In addition, you can move and turn the listener around; you don't move the sources when the player walks, you move the player and Libaudioverse does what it has to.  I'm going to make tutorials for all of this.  It is much easier than it looks from this short post and also kills huge chunks of code that everyone making a first-person game writes.
For games like side-scrollers, the world/source interface is cumbersome and unnecessary.  This is why the amplitude panner and hrtf panners exist.  An amplitude panner ignores verticall elevation (we can't do anything with it and it's only provided so you can use the same code with both) and pans evenly for azimuths specified as a clockwise angle from straight in front of you.  I.e 90 is out the right speaker, -90 is out the left speaker, and values above this range specify things behind you (it does nothing special for stereo).  If you go from 90 to 180 with stereo, it'll center itself again; with surround sound, when I code that, it'll play out the rear speakers.
Hope I helped some.  I am currently working on the manual, but this will take a bit of time.  I've also already added looping, and reworking the stupid suspended property is the next thing on the list.  Then it's time for the "fun" of improving the audio backends.

My Blog
Twitter: @ajhicks1992

2014-08-28 08:51:27

Wow, you made the range 0-50.
That is what through me, I'm used to 0.001 or 0-99 or something like that.
So y is up and down, so If I wished to make falling objects I'd use y (although my headphones almost make it sound like z).
And z is forward and back. So (10,0,10) is behind me and to the right 10 squares? Then if I changed the z to -10, so I had 10,0,-10, it would be forward and to the right?
It is really hard to hear this which is why I would like the muffle to be a little stronger. Either that, or somehow create a strict mono track and place it, with no blead, in a location, but that just sounds like a nightmare to do on the engine.

2014-08-28 16:48:00

Okay. Let me try this again.
You do have the coordinate system right.
But the values are not panning ranges.  The default distance model configuration is set up such that, when a source is more than 50 meters away from you, it's silent.  The 3 values are a position, not a panning value.  If you have a monster at (5, 3) in a first-person game (assuming x is west/east and y is north/south), you translate it to (5, 0, -3).  (40, 0, -40) will also produce silence, because (by the Pythagorean theorem) it's more than 50 meters away.  As the player moves, you update the listener's position through properties on the world object.  It's literally as though you'd suddenly put a real monster in the real room with you.  Moving the listener simply represents you walking, and turning the listener represents you turning.   If you wanted everything to be in millimeters, you can change the max distance to 50000; if it's in kilometers, you can change the max distance to 0.05.  If the environment is a massively padded room, you can lower them.  If it's an outdoor field, you can raise them.  But the point is that this is coordinates taken directly from positions of objects in your game, not some sort of panning values you're supposed to calculate.  There is no in-built maximums or minimums.  If you put the object at (100, 0, -100) (100 units east, 100 units north) and then bump the listener to (80 0, -80), the sound becomes audible as though you teleported; if you move the listener slowly towards it, the sound becomes audible as though you're walking towards it in a big open field or something.  I'm not demonstrating this because I need to bring in 4 or 5 more dependencies to do it properly, all of which must be installed by someone writing the examples.  Perhaps I'll add more commands to sim3d.py, but that doesn't do it justice and I was trying to keep things simple.
I'm not sure what placing a mono track with no blead in a location means.  I'm almost sure it's possible anyway.  The 3D simulation is built using other Libaudioverse components, all of which are publicly exposed and consequently usable by you.  If you wanted to do something custom, you can feed it through an attenuator (so you can simulate distance) and a panner (so you can move it between the speakers), and then do whatever.  This is exactly what the 3D simulation does behind the scenes, only Libaudioverse is providing the equations to control the attenuator and panner instead of you.  You can use this at the same time as one or more 3d worlds by running all the outputs through a mixer.  I'm not sure what your goal is, however.
As for the y values, it's subtle and a bit tricky.  If you try only coordinates like (0, 10, 0) you won't really hear it, and it doesn't work well with some sounds.  I3d had to add the radar because of this but, in my experience, it's working about as well as the real world.  Part of the problem with this is that you're driving the simulation incorrectly and part of the problem is that it's slight anyway.  Run the HRTF example and try values like (30, 30), (30, -30), etc.  Putting it right in front of you doesn't work so well when trying to demonstrate it.  Also note that this part of the library isn't quite finished: if you sweep a sound from (30, 0) to (30, 90) in the HRTF example by anything less than 10-degree increments, it'll jump up really noticeably.
Two things to note about this:  First, examples like this don't really demonstrate it properly, as there's a ton of queues missing due to the fact that it's not being driven "naturally".  Whether or not something is behind you is actually partially determined by movement, so it's a lot more obvious when things are moved around smoothy and you're actually in control of a virtual character.  Second, most of the audio libraries worked with in this community have been intentionally watered down either because the authors didn't know enough math to do it properly or because the creators did not feel the need or desire to explain it to you (I place BGT in this latter category, because he obviously knows the math to have done better).  Other libraries (Pygame) are simple because you, as the game designer, are supposed to write a higher level framework.  I'm not sure why this isn't provided in Pygame, but always found it mildly stupid that they didn't.

My Blog
Twitter: @ajhicks1992

2014-08-28 21:01:47

So, if I wished to write a map like in paladin of the Sky has, I would use a reversed z for going forward and back and x and y to go right and left? Then if I wished to place street lamps or something, I place them on the y so that they sound above me?
Do you also have an equation that will turn my location, so I can say what direction the character is facing? So I can say that 0 is north, 90 is east, 180 is south and 270 is west?

2014-08-28 21:29:46

You are indeed correct.
The equations for facing are a bit complicated and need a full tutorial, but I can quickly sketch out the basics of how that works in general.  I can never recall the specific equation off the top of my head, and don't want to spout off something that's wrong.  You can actually turn to any arbitrary angle and, if you're not, it's perhaps better to use some other features directly.  If you can't hit the angles in between with aiming/turning controls, you will lose a lot and things will sound...a bit odd.  The specific values for north/south/east/west are as follows:
north: (0, 0, -1, 0, 1, 0) which is the default.
East: (1, 0, 0, 0, 1, 0)
south: (0, 0, 1, 0, 1, 0)
west: (-1, 0, 0, 0, 1, 0)
Set them on the orientation property of the world.
So, a qualitative explanation of why I'm using 6 numbers.
The orientation of any object anywhere at all can be defined uniquely by 4 things.  You must define the top of the object, the front of the object, the direction a rod sticking out of the top of the object would be pointing, and the direction a rod stuck out of the front of an object would be pointing.  We usually specify these two values as the forward and up vectors, and specify that the front of the viewer is towards the monitor and the top of the viewer is towards the top of the monitor.  These rods must be kept perpendicular at all times or the equations that make them work fail hard.
To put it more simply, pretend you've got a rod glued to your back that runs up to just above your head and another one that's poking straight out of your nose.  Libaudioverse (and every other library for graphics and sound) makes this assumption and does not let you configure it.  The first 3 numbers in the 6-tuple specify where the one pointing out of your nose is pointing, and the last 3 numbers specify where the one running up your back and out the top of your head is pointing.
Now, the at and up properties could be separate, but suppose that the following sequence happens: you set at (which makes it temporarily not perpendicular to up), Libaudioverse does a block of mixing in the background, you set up, and another block of mixing happens.  We've got one block where they're "wrong", and everything randomly decides to sound very strange.  To that end, we make them into a list of 6 numbers rather than two separate properties on a world, which forces your game to update it either completely or not at all (this actually ties into our threading discussion and is an example of something called atomicity.  We say that orientation updates must be atomic).
Notice that, for 2D games, the second set is always 0, 1, 0.  This is because you're not doing tricks with looking up and down.  Audiogames need the functionality very rarely, but it is there and can make some interesting effects like falling over happen.  This is not intuitive, so I suggest actually fiddling with a physical object you can rotate to understand it better.
And to give what I *think* the equation is (I can't test it at the moment): (sin(theta), 0, -cos(theta), 0, 1, 0).  theta may be in radians or degrees, depending on your programming language and the configuration of your math libraries (Python is radians).  Positive angles turn clockwise, so 90 is east and 180 is south.  The conversion formula for degrees to radians is as follows: radians=(degrees/180.0)*pi.  I believe Python provides a function for this, but mathematicians and similar all just use radians directly anyway.  There is a version of this formula that allows for phi, the angle between the ground and the direction you want to look in, but I have to re derive it before I can post it and I suspect you don't need or even want it at this point anyway.

My Blog
Twitter: @ajhicks1992

2014-09-02 20:54:41

A C style interface would be perfectly fine for me. It wouldn't be the only C style API inside C++ code, I have the impression that there are often still many.

And yes, I remember now that, effectively, there are ABI problems when tryhing to use DLLs compiled with different compilers, because of 32 vs 64 or C++03 vs C++11, while there is none for plain C. I already have had problems of that kind because I'm not using MSVC.

Question: why not release the source code directly ? The building problem would be solved, as we could build it ourselves as we wish.

We could certainly imagine a licensing scheme which would fit both open source and commercial, if you mind releasing the source code :
* GPL for products that are GPL
* LGPL for products that are open or closed source but completely free / not sold / non commercial; in which case you are only allowed to build a dynamically loaded version (and not a statically linked one
* Full licenses for products that wish to be both closed source and paid

I think that this kind of scheme is the best of all world. Contributors can participate to make the library better, and as the same time nobody can make money on your work.

There are 10 kinds of people : those who know binary, and those who don't.

2014-09-02 21:05:48

It's up on Github and has been from day 1.  See here.
The license is GPL.  When 1.0 is ready, I'm going to offer commercial licenses, probably through 3 Mouse Technology, a cooperative of which I am a member.  I have been absurdly paranoid about copyright and no one but myself has contributed any code; to that end, I am still legally in the clear to do this.
Building is a bit complicated and the readme is way, way out of date.  Further documentation efforts are happening and are about 1/3rd done as of an hour ago.  The readme is not the current state of development; the docs branch has a manual in asciidoc that includes build instructions.  The TLDR version is this: install CMake, Python 2.7, Jinja2, enum34, pyyaml, pycparser, windows binaries for Libsndfile.  Run cmake as usual.
I have a binding generator that uses Jinja2 and Pycparser as well as a master Yaml file; the net result is that I can probably do C++ classes on top of it.  I just don't see the point at this time; I certainly won't be using them, not even for testing.  Extensive C++ examples exist, as mentioned above.
if anyone is both sufficiently adventurous and capable of attempting to build and can't get it to do so, either e-mail me or open issues on the Github repository.  It should build on most windows systems, though there is at least one known issue with Libsndfile paths maybe not being quite right (see the docs branch, subdirectory documentation).  Building is currently windows-only; Linux and Mac take some amount of work (probably 4 or 5 days all told, but really 2 weeks because of classes) and won't happen until I iron out the rest.  iOS and Android are much more significant and are not on the table because of performance issues until after 1.0.

My Blog
Twitter: @ajhicks1992

2014-09-08 21:09:52

Are you really obliged to install all python related stuff if you don't want to build at all the python binding ?

There are 10 kinds of people : those who know binary, and those who don't.

2014-09-08 22:59:52

All but Enum34, yes.  Enum34 is only needed to run the Python bindings.  Get pip working, and you can just "pip install <thing>" for all of them.  For anyone with a Python setup, installing those takes maybe a minute.
A big part of why Libaudioverse is able to be everywhere instead of just Python is that I'm actually autogenerating Python bindings from the headers and a yaml file.  In order to decrease the code duplication and because I do not particularly want to maintain a 2000+-line function in C++ that sets up properties, the cmake build systems call into Python with those packages to generate it as well.  This moves what would usually be something upwards of 3000 lines of code into one Yaml file and some helper scripts; each binding that I write can share much of these scripts and would otherwise also be upwards of 700 lines.  The upshot is that my bindings literally maintain themselves, the downside is that you have to have Python on your path and with those packages installed.
Also, as I write this, I'm realizing that you'll need to build it from a visual studio 2013 developer command prompt, which would be a good thing to document.  It also calls into the c++ compiler to preprocess the header at one point.

My Blog
Twitter: @ajhicks1992