← Back to team overview

lucadestudios team mailing list archive

Re: OpenAL sound system for LuceneStudios project

 

2009/2/24 Luke Benstead <kazade@xxxxxxxxx>

> 2009/2/24 Sean <seanhodges@xxxxxxxxxxxxxx>:
> > Gents,
> >
> > I've written down a few notes on the sound system for the LucadeStudios
> project. I thought I'd post them up for review.
> >
> > Let me know if you have any queries or suggestions, this is just rough
> plan at this stage.
> >
> >
> > OBJECTIVE
> >
> > Sound library, providing a high-level interface to the OpenAL library
> with a focus on games development.
> >
> > Based on concepts from KALEngine, OGRE and Unity:
> >
> > http://techbase.kde.org/Development/Tutorials/Games/KALEngine
> > http://www.ogre3d.org/wiki/index.php/OpenAL
> > http://unity3d.com/support/documentation/Manual/Sound.html
> >
> >
> > COMPONENTS
> >
> >  === Manager ===
> >
> > High-level controller for the sound system. In normal circumstances only
> one instance is needed. Sources are added to this before they can be played,
> and are made accessible by a user-defined key (stored in a hash map).
> >
> > Future consideration: At a later stage it may be desirable to include a
> "addFromConfig()" call; to build the sound sources from declarations in the
> config_reader. This could be available in addition to the manual addSource()
> method.
> >
> >        init()
> >        shutdown()
> >        addSource(key, source)
> >        getSource(key)
> >        play(key)
> >        stop(key)
> >        pause(key)
> >
> >
>
> Yep cool, as long as the singleton pattern doesn't touch this I'm happy :)
>
> >  === Source Streams ===
> >
> > - ogg_stream
> > - wav_stream
> >
> > Sound sources, a source is initialised with a file name and passed to the
> manager with a key string. The associated file in a source stream is
> immutable, but other characteristics can be modified at any time (position,
> looping, volume, etc). As well as using the play() operation through the
> manager interface, you can also retrieve a reference to the stream and play
> it directly. By default, the sound data will be decompressed and loaded into
> a buffer on demand (when play() is invoked). This behaviour will be
> modifiable per sound stream, so that frequently triggered sounds can be
> available decompressed in memory.
> >
>
> Can you just clarify that this "decompression" of the stream happens
> to already loaded data? We can't afford disk hits during gameplay,
> unless you wanna make tea between frames ;)

Good point!

>
>
> > Future consideration: In the first pass, sound sources will be able to
> have a position, but will be static. We should work out a mechanism for
> setting a direction and velocity as a next phase.
> >
> >        play()
> >        stop()
> >        pause()
> >        storeInBuffer(bool)
> >        setLooping(bool)
> >        setPosition(float, float, float)
> >        dropPosition()
> >
> >
>
> Hmm.. I've got some niggles with this (sorry!). Firstly, the same
> sound needs to be played from many sources (locations), so
> initializing with a filename will end up in duplication of loaded data
> (unless there is some funky caching going on in the manager) I would
> instead recommend you divide this into two separate concepts...
> sources and streams. A source holds a (smart) pointer to a stream
> stored in the manager. The manager controls loading the streams from
> disk (think flyweight pattern). Of course, if a stream can only be
> played once at a time, we need to clone them to the sources before
> playing.
>
> Also, remember there is the case that (for example) a game object may
> be making one sound, and then swap to another sound. It would be nicer
> if the object held a "source" instance, and just called
> _source->setStream("somekey") which internally called
> manager->getStream("somekey")  (which may cause a stream to be
> cloned). We should also consider that a source may play more than one
> sound... (we may also want crossfades between sounds, this is beyond
> the scope for now, but we should bear it in mind).
>
> So OK to sum up my waffling:
>
> 1. a source should represent "something" that plays a sound
> 2. a stream should represent that sound, it should be playable by more
> than one source
> 3. Possibly the source interface should allow for extensions so we can
> have more than one sound either playing, or queued to be played.
>
Yeah and I think that's how OpenAL handles this anyway, having sound sources
which represent the real data, and sounds which are just referencing the
sounds, being placed somewhere with position, velocity and in its own
channel.
So we should go with that.

And there's another point which needs to be considered: If we want to play
sounds from different threads (eg collision sounds from the physics thread
and other gamplay sounds for instance when the player fired his weapon from
the main thread), we need to be sure that the sound system can handle that.

>
>
> >  === Sound API Service ===
> >
> > OpenAL will be abstracted from the manager, so we can support other API's
> in the future (e.g. to support a platform that is incompatible with OpenAL).
> Currently, it will be the default and only service available to the engine's
> sound system.
> >
>
> Yep nice.
>
> >
> > EXAMPLE USAGE
> >
> > // Initialise the sound system
> >
> > sound_manager mgr();
> > mgr.init();
> >
> > // Add some sources, the keys can be any string - format is down to the
> caller
> >
> > mgr.add_source("/global/menu/itemclick", new ogg_stream("beep.ogg"));
> > mgr.add_source("/enemy/actions/fireweapon", new ogg_stream("bang.ogg"));
> >
> > // The sounds can be triggered from the manager
> >
> > mgr.play("/global/menu/itemclick");
> >
> > // Or, alternatively, a source can be retrieved later on, to manipulate
> it and/or play it directly
> >
> > ogg_stream* _fire_weapon_sound =
> mgr.get_source("/enemy/actions/fireweapon");
> > _fire_weapon_sound->setPosition(50, 50, 0);
> > _fire_weapon_sound->play();
> >
> > // Shut down the sound system when you're finished
> > // We could possibly make this automatic by applying a scoped_ptr to the
> openal_service, I'll follow whatever the standard convention in the project
> is...
> >
> > mgr.shutdown();
> >
> >
>
> With my suggested adjustments this becomes:
>
> sound_manager mgr;
> mgr.init();
>
> //Add a stream
> mgr.add_stream("global/menu/itemclick", shared_ptr<stream> (new
> ogg_stream("beep.ogg")));
>
> //Create a source (the source gets an internal pointer to the manager
> - so it can find the stream)
> shared_ptr<source> new_source =
> mgr.add_source("global/menu/itemclick"); //calls manager->get_stream
> internally
> or
> shared_ptr<source> new_source = mgr.add_source(); //no sound, can't be
> played
> new_source->set_stream("global/menu/itemclick"); //Now it can be
> new_source->set_position(50, 50, 0);
> new_source->play();
>
> Let me know what you think,
>
> Luke.

Sounds good..

Cheers
Carsten

Follow ups

References