Tutorial

Overview

Hello there! Here's quick explanation of the clunk library concepts and usage scenarios.

Typical scenario

Let's initialize context with typical values: 22kHz sample rate, 2 channels and 1024 bytes period:
    Context context; 
    context.init(22050, 2, 1024);
    //main code
    context.deinit();
If you choose greater sample rate such as 44kHz or even 48kHz, you will need more CPU power to mix sounds and it could hurt overall game performance. You could raise period value to avoid clicks, but you get more latency for that. Latency could be calculated with the following formula:
 latency (in seconds) = period_size / channels / byte per sample (2 for 16 bit sound) / sample_rate 
in this example latency is only 12ms. Such small delays are almost invisible even for perfect ears :)

Then application should load some samples to the library. Clunk itself does not provide code to decode audio formats, or load raw wave files. Check ogg/vorbis library for a free production-quality audio codec. Samples allocates within context internally with clunk::Context::create_sample() method.

    clunk::Buffer data; //placeholder for a memory chunk
    //decode ogg sample into data
    clunk::Sample *sample = Context->create_sample();
    sample->init(data, ogg_rate, AUDIO_S16LSB, ogg_channels);

So all audio data were loaded and initialized. Next step is to allocate objects. Clunk was designed to be easily integrated into programs. The most useful object is clunk::Object. It could hold several playing sources . You could use two different approaches here:

You wont ever need to track objects and/or manage its destruction, clunk will do it itself. Example allowing sound to play after your object's death:
    clunk::Object *clunk_object; 
    
    GameObject::~GameObject() {
        if (clunk_object != NULL) {
            clunk_object->autodelete(); //destroy me! 
            clunk_object = NULL; //leave destruction to the clunk::Context
        }
    }

So the next step is source management. It's the most easiest part. Each source connects to its audio sample. Source holds data about actual playing sound: position in wave data, pitch, gain and distance. It processes audio data and simulate 3d sound positioning with hrtf function.

Creating source and adding it to the object : (the most easiest part)

        clunk_object->play("voice", new Source(yeti_sound_sample)); // no loop, no pitch, no gain adjustments. 
Sources are automatically purged from the object when they are not needed anymore. So, you don't need to worry about its deletion or any management. Anyway, you could cancel any playing source:
        clunk_object->cancel("voice"); 

Or cancel all sounds from this object at once:

        clunk_object->cancel_all(true);

Object positioning

Usually objects are positioning the some sort of ticking function called every frame or from the on_object_update callback. Positioning is really simple:
        clunk_object->update(clunk::v3<float>(x, y, z), clunk::v3<float>(velocity_x, velocity_y, velocity_z), clunk::v3<float>(direction_x, direction_y, direction_z));
Moving listener is easy too, listener is regular clunk::Object, but it's stored in clunk::Context and holds information about your position
        context.get_listener()->update(clunk::v3<float>(x, y, z), clunk::v3<float>(velocity_x, velocity_y, velocity_z), clunk::v3<float>(direction_x, direction_y, direction_z));

Playing music and ambient sounds

Clunk is able to mix as many music streams as you want (or your CPU could handle :) ). First of all you need to implement your stream class derived from the clunk::Stream. Don't worry, you need to implement just 2(!) clunk-related methods to make the music play.
        class FooStream : public clunk::Stream {
        public: 
            void FooStream(const std::string &file) {
                //open music file. 
                //store music parameters into members : 
                sample_rate = music_rate;
                channels = music_channels;
                format = AUDIO_S16LSB; 
                //this values here are only for educational purpose. Don't forget to fill it with actual values from the music file!
            }
            
            void rewind() {
                //rewind your stream here
            }
            
            bool read(clunk::Buffer &data, unsigned hint) {
                //read as many data as you want, but it'd better to read around 'hint' bytes to avoid memory queue overhead. 
            }

            virtual ~FooStream() {
                //don't forget to close your stream here. Leaks are unwanted guests here. 
            }
        };

So, the most complicated part passed by. Let the party begin !

        context.play(0, new FooStream("data/background_music.ogg"), false); //do not loop music, look below for details.
        context.play(1, new FooStream("data/ambience_city.ogg"), true); //loops ambient

There's no magic numbers here. I've chosen 0 and 1 just for fun. You could use any integer id. 42 for example. Why don't I use loop == true for music ? We need it to change various tunes. Let's periodically test if music ends and restart with new tune:

        if (!context.playing(0)) {
            context.play(0, new FooStream(next_song));
        }

Final words from author

I've covered almost all major topics of the clunk here in this tutorial. If you have suggestion - feel free to contact me directly. Hope all this code will be useful for someone. Good luck! We're waiting for your feedback!

Generated on Fri Oct 10 16:06:16 2008 for Clunk by  doxygen 1.5.5