2019-03-15 21:52:33

I'm working on a wrap-around Asteroids-style shooter. I wanted to mark the edge of the field with sound, since the player probably wants to be a bit more careful flying near it since asteroids may suddenly wrap into their play area. Initially I modeled this by essentially creating audio fence posts--sound sources every few units to create an audio border. This worked a bit, but when I bumped up the density of my posts (I.e. one every 4 border units instead if 5) I got an "out of memory" error. I assume that's memory on the sound hardware and not the computer, since this box has 32 GB of RAM and hasn't come anywhere close to anything I'd worry about.

Needless to say I scrapped that implementation idea and am now trying something much more complex with raycasting, and field borders that only collide with rays extending from the player's front/left/right/back. I'll then only position 4 audio sources at the nearest point of intersection between these rays and the field borders such that the player is in the center of 4 moving sound sources representing the nearest contact with the border. But I'm a bit worried that OpenAL hit a limit at less than 100 sounds. I mean, I know sound hardware has practical limits, but I'd hoped for some intelligent mixing/culling to identify quieter sounds and either trim them from the mix, or combine them in some way that at least acknowledged they're there without allocating a distinct source for each.

Is there some OpenAL limit I need to be aware of when rendering sound? I'm not familiar with OpenAL extensions--are there any I can enable to fix this? I don't know what the source limit is, but between music, bullets, player sounds, and lots of fragmenting asteroids, I'd hate for a player to hit it and crash the game. smile Or is this limit something I don't have to worry about unless I do something else stupid, like create 80-90 audio fence posts for my field?

And as an aside, is OpenAL the only game in town for open source/reasonably priced cross-platform audio? I'm targetting at least Linux and Windows (and maybe MacOS if I can get a Mac at some point), so cross-platform is essential. OpenAL isn't bad, but jokes about my stupidity aside, I didn't think I placed too many demands on my sound hardware, and I really thought OpenAL would handle the excess in some way other than completely crashing the game.

Thanks.

2019-03-15 23:23:55

Assuming your using OpenAL-Soft, how many sounds are you using and playing at once? Its hard to pin down exactly what the limits of OpenAL may be, outside of the systems available memory and CPU power, though numbers from a "safe" 32 up to 256 have been tossed around. It might be useful to profile your system to see what the CPU and memory load is, and there are ways of catching errors both in OpenAL and python without crashing the program, such as using try/except. There's also a [config file] you can configure to change some of OpenALs default parameters, for example the source limit:

## sources:
#  Sets the maximum number of allocatable sources. Lower values may help for
#  systems with apps that try to play more sounds than the CPU can handle.
#sources = 256

As for alternatives, for the most part OpenAL-Soft is a fairly dependable and feature rich library, I'm not sure if libaudioverse is maintained anymore, otherwise it would be a strong competitor  There's also SDL, PortAudio, pybass or bass4py, rtaudio, and possibly others.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2019-03-16 00:04:58

Hmm, now that I crunch the numbers, that'd have been more than 256 sources. A square arena of width 300 with a source every 4 units is 75 per side, which is already 300. I guess that explains why a source per 5 units didn't crash the game, since that'd have been 240 sources for the border plus 1 for the player rumble. Guess I created more than I'd originally thought, though I still hoped there'd be some sort of intelligent mixing to cull out more distant sources, and to prefer not playing quiet sounds over crashing the app.

FWIW, I'm using Rust, so I can interface with any audio system with a Rust-native interface or a C-compatible ABI, in which case I could write my own FFI wrapper. I'd prefer something where spatial audio is a first-class citizen since I'm not smart enough to do my own spatial mixing. Pretty sure SDL doesn't provide that, though I'm not sure about Portaudio.

But if the OpenAL default is 256, I'm probably OK with that for now. I do eventually want to do more complex games where there may be over 200 active entities, even if some of them are out of the player's view, and it'd be nice if the audio system I was using just culled out distant sources rather than throwing up its hands when an arbitrary limit is hit. But I'll cross that bridge if I get there. smile

Thanks for pointing me to the config file.

2019-03-16 00:25:47

This is where I'd use one or both of something like tiling, or something like a sound_pool. The former would keep a smaller number of border sounds—the maximum that the player can hear at any given time,—and when one goes out of earshot, move it to where the one that comes into earshot should be.
Or you could try something that's not too realistic, and position sounds that need to cover a wide area as the player moves. Ex, if the sound is a plane at x = -100, you'd position that border sound at (-100, listener.y, listener.z).
The sound_pool would basically be writing your own datastructure that manages what sounds should and shouldn't play when. Could also include giving them bounding boxes this way.

For this case, specifically, I'd lean toward tiling.

看過來!
"If you want utopia but reality gives you Lovecraft, you don't give up, you carve your utopia out of the corpses of dead gods."
MaxAngor wrote:
    George... Don't do that.

2019-03-16 01:31:44

Can you please explain what you mean by "tiling?"

My current strategy essentially involves casting a ray ahead, behind, left and right of the player. I then have 4 audio sources positioned wherever that ray intersects the arena border. So for instance, if the arena is 50X50 and the player is dead center, there is an audio source representing the border equidistant from the player in every direction. If the player moves forward 25 units, the source behind fades because it is 25 units more distant, but the source ahead becomes louder because it is 25 units closer. If the character rotates 45 degrees right, the coordinates where the rays intersect the border shift such that the player has some sense of the nearest borders. The forward ray intersects the border slightly left, and the rest of the sources shift such that the player knows that the loudest border is ahead and left, so they should probably navigate back and right to move toward the center of the field. You couldn't necessarily position yourself exactly within the field, but you'd at least be able to know you're near a border, or to fly along one and turn when one is upcoming by listening to the changes in the sound sources.

But I'm interested in other approaches as well. I'm trying to do audio game development on top of a full 3-D engine which, while great from the perspective of giving me every feature I might possibly want, is painful because I have to invent the mechanisms by which I see the world.

2019-03-16 02:26:25

Another way of putting it would be sector or proximity based sound management. If your using a 10 by 10 2D grid, you'd in essence have 100 "tiles" or sectors, so when a sound moves out of your specified range of the listener, you deallocate that sound, and when one moves into range you allocate a sound. This can be effective for recycling OpenAL sources, and is somewhat similar to the kinds of optimization rendering engines use with Render Distances and such.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2019-03-16 16:36:37

Ah, got it, so distance-based culling then. I'll definitely consider that if I need it. I just suspected that more advanced audio libraries like FMod do some sort of smart processing based on how they describe their features, and hoped OpenAL had something similar hidden in its extensions.

Thanks for the pointers.

2019-03-17 01:54:29 (edited by Ian Reed 2019-03-17 02:00:53)

Going back to the original issue of getting an out of memory error:
You were probably creating a fresh buffer for every one of your 300 sources and reloading the same sound data into it.
Instead you should be using a single buffer for the fence post sound data, then associating it with all your 300 sources.
In that way you only need to load the sound data into memory once, not 300 times.
Consider a 10 second fence post mono sound with 16 bit depth at 48000Hz.
That would be 10 seconds * 2 bytes * 48000 = 960000 bytes, or lets just round it to 1 megabyte.
So you could either use 1MB for all 300 sources, or more likely you used 300MB to cover those 300 sources.
Imagine that your fence post sound was 30 seconds long, then you would quickly approach a gigabyte of memory usage.
C# programs will crash with an out of memory exception around 1GB, regardless of you having 32GB of ram on your system, unless you specify different configuration options.
Similar default constraints probably exist with other programming languages, such as Rust.

Though as you and others have said, there are much better solutions than creating 300 fence post sources, such as your ray casting solution where you move 4 sources around the listener.
I used something similar to handle river, stream, and pit sounds in A Hero's Call.

In addition to reusing the same buffer on multiple sources, you can deallocate the buffers and sources yourself for sounds that are very distant, as others have already suggested with the tiling/sector/proximity based/distance culling solutions.
Or another great solution is to switch from loading the entire sound file into a buffer and instead use streaming buffers so that only a portion of the sound is ever loaded at once.
For my next project, I am currently using OpenALSoft, with 5 streaming buffers per sound, each of which holds up to 20ms of stereo data, which is 3840 bytes per buffer, or 19200 for all 5 buffers, basically just under 20KB.
This keeps sounds playing well and avoids buffer underruns.
It also has the benefit that each sound only consumes about 20KB of memory, including long sounds, such as music files that can be several minutes long.
And long sounds get loaded very quickly because from the time your code decides to start playing the sound, it only has to wait for the first 20KB to be uncompressed into memory, while loading the entire music file might take multiple seconds and be very noticeable to your users.
You can also combine streaming with the distance culling solutions if you have really huge maps.
Worth noting that keeping your streaming buffers filled is usually done with an additional thread that can wake up every 10ms or so and refill them as needed.

The FMOD Studio and FMOD low level APIs can handle streaming for you, which does make your life easier.
OpenAL expects you to handle streaming yourself, which is more work for you, but also gives you a bit more control.
You probably don't need this control right now, so maybe FMOD would provide a better experience.
FMOD will also handle more audio file formats without you needing to manually include libsndfile, libopus, or others.
I also believe FMOD "virtualizes" sounds that are too far away to be heard, which saves some CPU, but that wasn't your issue, running out of memory was your issue.
I don't consider OpenAL to have big limitations, in fact we were using it for A Hero's Call for much of the development, and only switched to FMOD near the end primarily to allow our sound designer to use the FMOD Studio tool.
I prefer OpenALSoft's license over FMOD's, but FMOD's is pretty reasonable for most people.
I'll be using OpenALSoft on my next project, along with libopus, so I consider them to be pretty reasonable libraries, but I've had a lot of experience with OpenALSoft at this point and I actually have a reason to handle the streaming manually.

Hope my ramblings are a bit helpful.

~ Ian Reed
Visit BlindGamers.com to rate blind accessible games and see how others have rated them.
Try my free JGT addon, the easy way to play Japanese games in English.
Or try the free games I've created.

2019-03-18 00:39:39

Thanks for this detailed response. Glad to know that OpenAL was up to the task of rendering something like AHC.

I'm positive I'm not creating a new buffer for every source. I spent several days battling with making Amethyst's asset-loader return audio assets as OpenAL buffers. My suspicion is that I'm hitting the source limit, and the message about running out of memory is a red herring that could be made clearer.

Anyhow, I'll look into streaming, as I'll certainly need that for music. Thanks again.