]> Dogcows Code - chaz/yoink/blobdiff - src/moof/sound.cc
pch support
[chaz/yoink] / src / moof / sound.cc
index a6fbf0310679faacc76175aa9d67b71079b1e9e5..bcd86670c302d4e6ff17d3bc0133eb73043cd9d9 100644 (file)
@@ -1,19 +1,14 @@
 
-/*]  Copyright (c) 2009-2010, Charles McGarvey  [**************************
+/*]  Copyright (c) 2009-2011, Charles McGarvey  [*****************************
 **]  All rights reserved.
 *
-* vi:ts=4 sw=4 tw=75
-*
 * Distributable under the terms and conditions of the 2-clause BSD license;
 * see the file COPYING for a complete text of the license.
 *
-**************************************************************************/
+*****************************************************************************/
 
-#include <cstdio>
 #include <deque>
-#include <list>
 #include <stdexcept>
-#include <string>
 
 #include <boost/algorithm/string.hpp>
 #include <boost/cstdint.hpp>
 #include <vorbis/codec.h>
 #include <vorbis/vorbisfile.h>
 
-#include "hash.hh"
-#include "log.hh"
-#include "manager.hh"
+#include "debug.hh"
 #include "sound.hh"
 #include "resource.hh"
+#include "runloop.hh"
+#include "thread.hh"
 #include "timer.hh"
 
 
 #ifndef BUF_SIZE
-#define BUF_SIZE       (4096)
+#define BUF_SIZE 8192
 #endif
 
-#define NUM_BUFFERS (16)
+#define NUM_SAMPLE_BITS        16
 
 
 namespace moof {
 
 
+thread stream_thread_;
+
+
 class sound_backend
 {
 public:
@@ -54,27 +52,25 @@ public:
                        if (!al_device || !al_context)
                        {
                                const char* error = alcGetString(al_device,
-                                                                                                alcGetError(al_device));
+                                               alcGetError(al_device));
                                log_error("audio subsystem initialization failure", error);
                        }
                        else
                        {
                                alcMakeContextCurrent(al_context);
-                               log_info << "opened sound device `"
-                                                << alcGetString(al_device,
-                                                                                ALC_DEFAULT_DEVICE_SPECIFIER)
-                                                << "'" << std::endl;
+                               log_info << "opened sound device `" <<
+                                       alcGetString(al_device, ALC_DEFAULT_DEVICE_SPECIFIER) <<
+                                       "'" << std::endl;
                        }
                }
        }
 
-
        sound_backend(const sound_backend& backend)
        {
                ++retain_count;
        }
 
-       sound_backend& operator=(const sound_backend& backend)
+       sound_backend& operator = (const sound_backend& backend)
        {
                ++retain_count;
                return *this;
@@ -87,17 +83,15 @@ public:
                        alcMakeContextCurrent(0);
                        alcDestroyContext(al_context);
                        alcCloseDevice(al_device);
-                       log_info("unloaded sound device ALSA");
                }
        }
 
-
-       static int                      retain_count;
+       static int              retain_count;
        static ALCdevice*       al_device;
        static ALCcontext*      al_context;
 };
 
-int                    sound_backend::retain_count = 0;
+int            sound_backend::retain_count = 0;
 ALCdevice*     sound_backend::al_device;
 ALCcontext*    sound_backend::al_context;
 
@@ -105,194 +99,63 @@ ALCcontext*       sound_backend::al_context;
 class sound_resource;
 typedef resource_handle<sound_resource> sound_handle;
 
-
-class sound_resource_loader
-{
-public:
-       
-       sound_resource_loader()
-       {
-               resource::register_type<sound_resource>("ogg");
-       }
-
-       ~sound_resource_loader()
-       {
-               resource::unregister_type("ogg");
-       }
-};
-
-static sound_resource_loader loader;
-
-
-
-// SOUND BUFFER
-
-class buffer
-{
-public:
-
-       typedef hash<ALuint,int,hash_function> retcount_lookup;
-
-
-       buffer() :
-               buffer_((ALuint)-1) {}
-
-       buffer(const void* data,
-                  ALsizei size,
-                  ALenum format,
-                  ALsizei freq)
-       {
-               alGenBuffers(1, &buffer_);
-               alBufferData(buffer_, format, data, size, freq);
-
-               retain_counts_[buffer_] = 1;
-               log_warning("ctor buffer:", buffer_);
-       }
-
-       buffer(const buffer& buf)
-       {
-               buffer_ = buf.buffer_;
-               retain();
-       }
-
-       buffer& operator = (const buffer& buf)
-       {
-               buffer_ = buf.buffer_;
-               retain();
-               return *this;
-       }
-
-       ~buffer()
-       {
-               release();
-       }
-
-
-       void queue(ALuint source) const
-       {
-               if (*this)
-               {
-                       alSourceQueueBuffers(source, 1, &buffer_);
-                       retain();
-                       log_warning("queued buffer:", buffer_);
-               }
-       }
-
-       static buffer unqueue(ALuint source)
-       {
-               ALuint buf = (ALuint)-1;
-               alSourceUnqueueBuffers(source, 1, &buf);
-               log_warning("unqueued buffer:", buf);
-               return buffer(buf);
-       }
-
-       void set(ALuint source) const
-       {
-               log_warning("set buffer:", buffer_);
-               if (*this) alSourcei(source, AL_BUFFER, buffer_);
-       }
-
-       operator bool () const
-       {
-               return buffer_ != (ALuint)-1;
-       }
-
-
-private:
-
-       explicit buffer(ALuint buf) :
-               buffer_(buf) {}
-
-
-       void retain() const
-       {
-               retcount_lookup::iterator it = retain_counts_.find(buffer_);
-               if (it.valid()) ++it->second;
-       }
-
-       void release() const
-       {
-               retcount_lookup::iterator it = retain_counts_.find(buffer_);
-               if (it.valid() && --it->second <= 0)
-               {
-                       alDeleteBuffers(1, &buffer_);
-                       retain_counts_.erase(it);
-                       log_warning("kill buffer:", buffer_);
-               }
-       }
-
-
-       ALuint                                  buffer_;
-       sound_backend                   backend_;
-
-       static retcount_lookup  retain_counts_;
-};
-
-buffer::retcount_lookup buffer::retain_counts_;
-
-
-
-// SOUND RESOURCE
+MOOF_REGISTER_RESOURCE(sound_resource, ogg, sounds);
 
 class sound_resource : public boost::noncopyable
 {
 public:
 
-       sound_resource(const std::string& path)
+       sound_resource(const std::string& path) :
+               buffer_(AL_NONE),
+               sample_(0)
        {
-               log_info("audio path is", path);
                if (ov_fopen((char*)path.c_str(), &file_) < 0)
-               {
-                       throw std::runtime_error("problem reading audio: " + path);
-               }
+                       throw std::runtime_error("problem reading audio: " +
+                                       path);
        }
 
        ~sound_resource()
        {
                ov_clear(&file_);
+               if (buffer_) alDeleteBuffers(1, &buffer_);
        }
 
-
-       bool read(buffer& buf) const
+       ALuint read_all() const
        {
-               if (buffer_)
-               {
-                       buf = buffer_;
-                       return true;
-               }
+               if (buffer_) return buffer_;
 
-               if (ov_pcm_seek_lap(&file_, 0) != 0)
+               if (ov_pcm_seek(&file_, 0) != 0)
                {
                        log_warning("vorbis seek error");
-                       return false;
+                       return AL_NONE;
                }
 
-               char    data[64*BUF_SIZE];
+               ogg_int64_t samples = ov_pcm_total(&file_, 0);
+
+               char    data[2 * samples * (NUM_SAMPLE_BITS / 8)];
                size_t  size = 0;
 
                while (size < sizeof(data))
                {
                        int     section;
-                       int     result = ov_read(&file_,
-                                                                data + size, sizeof(data) - size,
-                                                                0, 2, 1, &section);
+                       int     result = ov_read(&file_, data + size,
+                                       sizeof(data) - size, 0, 2, 1, &section);
 
-                       if (result > 0)
+                       if (0 < result)
                        {
                                size += result;
-                               continue;
                        }
                        else if (result == 0 && size > 0)
                        {
-                               log_info("loaded", size, "bytes from vorbis");
                                vorbis_info* info = ov_info(&file_, section);
-                               buffer_ = buffer(data, size,
-                                                                get_audio_format(info), info->rate);
-                               buf = buffer_;
-                               log_info("this section is", section);
-                               log_info("audio format is", get_audio_format(info));
-                               log_info("audio freq is", info->rate);
-                               return true;
+
+                               ALuint buffer;
+                               alGenBuffers(1, &buffer);
+                               alBufferData(buffer, get_audio_format(info),
+                                               data, size, info->rate);
+
+                               buffer_ = buffer;
+                               return buffer;
                        }
                        else
                        {
@@ -300,55 +163,92 @@ public:
                                break;
                        }
                }
-
-               if (size >= sizeof(data)) log_warning("sample is too big to play");
-               return false;
+               return AL_NONE;
        }
 
-
-       bool read(buffer& buf, uint64_t& sample) const
+       bool read(ALuint buffer, uint64_t& sample) const
        {
-               if (ov_pcm_seek_lap(&file_, sample) != 0)
-               {
-                       log_warning("vorbis seek error");
+               if ((sample == sample_ && ov_pcm_seek_lap(&file_, sample) != 0) ||
+                       (sample != sample_ && ov_pcm_seek(&file_, sample) != 0))
                        return false;
-               }
 
-               char data[BUF_SIZE];
+               char    data[BUF_SIZE];
                int     section;
-               int     result = ov_read(&file_, data, sizeof(data), 0, 2, 1, &section);
+               int     result = ov_read(&file_, data, sizeof(data),
+                               0, 2, 1, &section);
 
-               if (result > 0)
+               if (0 < result)
                {
-                       log_info("loaded", result, "bytes from vorbis");
                        vorbis_info* info = ov_info(&file_, section);
-                       buf = buffer(data, result, get_audio_format(info), info->rate);
-                       sample = ov_pcm_tell(&file_);
-                       log_info("this section is", section);
-                       log_info("next sample is", sample);
-                       log_info("audio format is", get_audio_format(info));
-                       log_info("audio freq is", info->rate);
+                       alBufferData(buffer, get_audio_format(info),
+                                                data, result, info->rate);
+                       sample_ = sample = ov_pcm_tell(&file_);
                        return true;
                }
+               else if (result < 0)
+               {
+                       log_warning("vorbis playback error");
+               }
+               return false;
+       }
 
-               if (result < 0) log_warning("vorbis playback error");
+       bool read(ALuint buffer, uint64_t& sample, sound_handle other) const
+       {
+               ov_crosslap(&other->file_, &file_);
+
+               char    data[BUF_SIZE];
+               int     section;
+               int     result = ov_read(&file_, data, sizeof(data),
+                               0, 2, 1, &section);
+
+               if (0 < result)
+               {
+                       vorbis_info* info = ov_info(&file_, section);
+                       alBufferData(buffer, get_audio_format(info),
+                                       data, result, info->rate);
+                       sample_ = sample = ov_pcm_tell(&file_);
+                       return true;
+               }
+               else if (result < 0)
+               {
+                       log_warning("vorbis playback error");
+               }
                return false;
        }
 
+       // determines the correct number of fixed-size buffers needed to hold
+       // a given number of seconds of PCM audio.
+       int num_buffers(scalar seconds) const
+       {
+               vorbis_info* info = ov_info(&file_, -1);
+               int count = scalar(info->rate) *                // sample rate
+                               scalar(info->channels) *        // channels
+                               scalar(NUM_SAMPLE_BITS) *       // sample size
+                               seconds *                       // time
+                               SCALAR(0.125) /                 // bits to bytes
+                               scalar(BUF_SIZE);               // individual buffer size
+               return count;
+       }
 
        static ALenum get_audio_format(const vorbis_info* info)
        {
-               if (info->channels == 1) return AL_FORMAT_MONO16;
-               else                                     return AL_FORMAT_STEREO16;
-       }
+               if (info->channels == 1)
+                       return AL_FORMAT_MONO16;
+               else if (info->channels == 2)
+                       return AL_FORMAT_STEREO16;
 
+               log_error("unsupported number of channels:", info->channels);
+               return 0;
+       }
 
 private:
 
        mutable OggVorbis_File  file_;
-       mutable buffer                  buffer_;
-};
+       mutable ALuint          buffer_;
+       mutable uint64_t        sample_;
 
+       sound_backend           backend_;
+};
 
 
 class sound::impl
@@ -360,11 +260,10 @@ public:
                init();
        }
 
-       impl(const std::string& path)
+       impl(const std::string& name)
        {
-               log_info("sound::impl constructor");
                init();
-               enqueue(path);
+               sample(name);
        }
 
        void init()
@@ -373,152 +272,210 @@ public:
                is_looping_ = false;
 
                sample_ = 0;
+               buffer_size_ = SCALAR(1.0);
 
                alGenSources(1, &source_);
-               log_error("alGenSources:", alGetError());
 
                ALfloat zero[] = {0.0f, 0.0f, 0.0f};
                alSourcef(source_,  AL_PITCH, 1.0f);
                alSourcef(source_,  AL_GAIN, 1.0f);
                alSourcefv(source_, AL_POSITION, zero);
                alSourcefv(source_, AL_VELOCITY, zero);
-               log_error("init:", alGetError());
        }
 
        ~impl()
        {
                stop();
+               deplete_stream();
+
                alDeleteSources(1, &source_);
        }
 
 
-       void play()
+       void deplete_stream()
        {
-               if (queue_.empty()) return;
+               if (is_streaming_)
+               {
+                       ALint queued = 0;
+                       alGetSourcei(source_, AL_BUFFERS_QUEUED, &queued);
+                       ALuint buffers[queued];
+                       alSourceUnqueueBuffers(source_, queued, buffers);
+                       alDeleteBuffers(queued, buffers);
+               }
+               else
+               {
+                       alSourcei(source_, AL_BUFFER, AL_NONE);
+               }
+       }
 
-               sound_handle    handle = queue_.front();
-               buffer                  buf;
+       void play()
+       {
+               if (queue_.empty() || is_playing_) return;
 
-               if (handle->read(buf))
+               ALenum state;
+               alGetSourcei(source_, AL_SOURCE_STATE, &state);
+               switch (state)
                {
-                       log_info("playing source...");
-                       buf.set(source_);
-                       alSourcei(source_, AL_LOOPING, is_looping_);
-                       alSourcePlay(source_);
+               case AL_INITIAL:
+               case AL_STOPPED:
+                       if (is_streaming_)
+                       {
+                               start_update_timer();
+                               num_buffers_ = queue_.front()->num_buffers(buffer_size_);
+                               fill_stream();
+                               is_playing_ = true;
+                               alSourcei(source_, AL_LOOPING, false);
+                       }
+                       else
+                       {
+                               ALuint buffer = queue_.front()->read_all();
+                               alSourcei(source_, AL_BUFFER, buffer);
+                               alSourcei(source_, AL_LOOPING, is_looping_);
+                       }
+                       break;
+
+               case AL_PAUSED:
+                       if (is_streaming_)
+                       {
+                               start_update_timer();
+                               is_playing_ = true;
+                       }
+                       break;
                }
+
+               alSourcePlay(source_);
        }
 
-       void stream()
+       void start_update_timer()
        {
-               if (queue_.empty()) return;
+               stream_timer_.init(boost::bind(&impl::stream_update,
+                                       this, _1, _2),
+                               SCALAR(0.1), timer::repeat);
 
-               if (!is_playing_)
-               {
-                       alSourcei(source_, AL_LOOPING, false);
-                       log_error("set not looping:", alGetError());
+               thread::main_runloop().add_timer(stream_timer_);
+
+               //thread thread;
+               //thread.runloop().add_timer(stream_timer_);
+               //if (!stream_thread_.is_valid())
+                       //stream_thread_ = thread::detach(stream_timer_);
+               // FIXME: proper locking needs to be done; this is causing crashes
+               // on shutdown when sound resources are destroyed yet the stream
+               // thread is neither shutdown or notified.
+       }
 
-                       sound_handle handle = queue_.front();
+       void fill_stream()
+       {
+               ALenum type;
+               alGetSourcei(source_, AL_SOURCE_TYPE, &type);
+               ASSERT(type != AL_STATIC && "source shouldn't be static");
+               
+               ALint   processed = 0;
+               ALint   queued = 0;
+               alGetSourcei(source_, AL_BUFFERS_QUEUED, &queued);
+               alGetSourcei(source_, AL_BUFFERS_PROCESSED, &processed);
+
+               int     target_diff = num_buffers_ - queued + processed;
+
+               ALuint  bufs[processed];
+               alSourceUnqueueBuffers(source_, processed, bufs);
+               for (int i = 0; i < processed; ++i)
+               {
+                       ALuint& buf = bufs[i];
 
-                       for (int i = 0; i < NUM_BUFFERS; ++i)
+                       if (0 < target_diff)
                        {
-                               buffer buf;
-                               if (handle->read(buf, sample_))
+                               if (queue_buffer(buf))
                                {
-                                       buf.queue(source_);
+                                       --target_diff;
                                }
                                else
                                {
-                                       log_error("failed to start stream");
+                                       alDeleteBuffers(processed - i - 1, &bufs[i+1]);
                                        break;
                                }
-
-                               ALint queued = 0;
-                               alGetSourcei(source_, AL_BUFFERS_QUEUED, &queued);
-                               log_info("buffers queued:", queued);
                        }
+                       else alDeleteBuffers(1, &buf);
                }
 
-               if (!stream_timer_.is_valid())
+               if (stream_timer_.mode() != timer::invalid && 0 < target_diff)
                {
-                       stream_timer_.init(boost::bind(&impl::stream_update, this, _1, _2),
-                                       0.01, timer::repeat);
+                       for (int i = 0; i < target_diff; ++i)
+                       {
+                               if (!queue_buffer(AL_NONE)) break;
+                       }
                }
 
-               log_info("streaming source...");
-               alSourcePlay(source_);
-               log_error("playing:", alGetError());
-               is_playing_ = true;
+               if (is_playing_)
+               {
+                       ALenum state;
+                       alGetSourcei(source_, AL_SOURCE_STATE, &state);
+                       if (state != AL_PLAYING)
+                       {
+                               // restart playing if we're stopped but supposed to be
+                               // playing...  this means we didn't queue enough...
+                               alSourcePlay(source_);
+                               log_warning("not enough audio buffered");
+                       }
+               }
        }
 
-
-       void update()
+       // TODO: this function is ugly
+       bool queue_buffer(ALuint buffer)
        {
-               ALint finished = 0;
+               if (buffer == AL_NONE) alGenBuffers(1, &buffer);
 
-               alGetSourcei(source_, AL_BUFFERS_PROCESSED, &finished);
-
-               while (finished-- > 0)
+               bool looped_once = false;
+               
+               while (0 < queue_.size())
                {
-                       buffer::unqueue(source_);
-                       bool streamed = false;
-                       //if (handle->is_loaded())
-                       //{
-                               //streamed = handle->read(buf);
-                       //}
-                       //else
-                       //{
-                               buffer buf;
-                               sound_handle handle = queue_.front();
-                               streamed = handle->read(buf, sample_);
-                       //}
-
-                       if (streamed)
+                       sound_handle sound = queue_.front();
+
+                       bool result = sound->read(buffer, sample_);
+                       if (result)
                        {
-                               buf.queue(source_);
+                               alSourceQueueBuffers(source_, 1, &buffer);
+                               return true;
                        }
                        else
                        {
-                               // the buffer couldn't be streamed, so get rid of it
-                               queue_.pop_front();
                                sample_ = 0;
+                               queue_.pop_front();
 
-                               if (!queue_.empty())
+                               if (0 < queue_.size())
                                {
-                                       // begin the next buffer in the queue
-                                       handle->read(buf, sample_);
-                                       buf.queue(source_);
-                                       log_info("loading new buffer");
+                                       sound_handle new_sound = queue_.front();
+                                       result = new_sound->read(buffer, sample_, sound);
+                                       if (result)
+                                       {
+                                               queue_.push_back(new_sound);
+                                               alSourceQueueBuffers(source_, 1, &buffer);
+                                               num_buffers_ = new_sound->num_buffers(buffer_size_);
+                                               return true;
+                                       }
+                                       else
+                                       {
+                                               queue_.pop_front();
+                                               queue_.push_front(sound);
+                                       }
                                }
-                               else if (is_looping_)
+                               else if (is_looping_ && !looped_once)
                                {
-                                       // reload the same buffer
-                                       log_info("looping same buffer");
-
-                                       queue_.push_back(handle);
-                                       handle->read(buf, sample_);
-                                       buf.queue(source_);
+                                       queue_.push_back(sound);
+                                       looped_once = true;
                                }
                                else
                                {
-                                       // nothing more to play, stopping...
-                                       stop();
-                                       queue_.push_back(handle);
+                                       break;
                                }
                        }
                }
 
-               ALenum state;
-               alGetSourcei(source_, AL_SOURCE_STATE, &state);
-
-               // restart playing if we're stopped but supposed to be playing...
-               // this means we didn't queue enough and the audio skipped :-(
-               if (is_playing_ && state != AL_PLAYING)
-               {
-                       alSourcePlay(source_);
-               }
+               alDeleteBuffers(1, &buffer);
+               is_playing_ = false;
+               stream_timer_.invalidate();
+               return false;
        }
 
-
        void stop()
        {
                alSourceStop(source_);
@@ -526,7 +483,7 @@ public:
 
                stream_timer_.invalidate();
 
-               // TODO: clear buffers if streaming
+               sample_ = 0;
        }
 
        void pause()
@@ -537,73 +494,62 @@ public:
                stream_timer_.invalidate();
        }
 
-       void rewind()
-       {
-               alSourceRewind(source_);
-               sample_ = 0;
-       }
-
-
-       void sample(const std::string& path)
+       void sample(const std::string& name)
        {
                stop();
-               alSourcei(source_, AL_BUFFER, AL_NONE);
+               deplete_stream();
 
                queue_.clear();
-
-               enqueue(path);
+               queue(name);
+               is_streaming_ = false;
        }
 
-       void enqueue(const std::string& path)
+       void queue(const std::string& name)
        {
-               sound_handle handle = resource::load(path);
-               queue_.push_back(handle);
+               sound_handle handle = resource::load(name, "ogg");
+               if (handle) queue_.push_back(handle);
+               is_streaming_ = true;
        }
 
-
        bool is_playing() const
        {
                ALenum state;
                alGetSourcei(source_, AL_SOURCE_STATE, &state);
 
                if (state == AL_PLAYING) return true;
-               else                     return is_playing_;
+               else return is_playing_;
        }
 
-
        void loop(bool looping)
        {
                is_looping_ = looping;
 
                ALenum type;
                alGetSourcei(source_, AL_SOURCE_TYPE, &type);
-
                if (type != AL_STREAMING)
-               {
                        alSourcei(source_, AL_LOOPING, is_looping_);
-               }
        }
 
-
        void stream_update(timer& timer, scalar t)
        {
-               update();
-               // TODO: might be nice to also allow using threads for streaming
-               // rather than a timer, probably as a compile-time option
+               log_debug("blaaaaaaaaaaaaaaaaaaaaaaaaaaahhhhhhhhhhhhhhhhrrrrrg");
+               fill_stream();
        }
 
+       ALuint                          source_;
 
-       ALuint                                          source_;
-
-       bool                                            is_playing_;
-       bool                                            is_looping_;
+       bool                            is_playing_;
+       bool                            is_looping_;
+       bool                            is_streaming_;
 
        std::deque<sound_handle>        queue_;
-       uint64_t                                        sample_;
+       uint64_t                        sample_;
+       int                             num_buffers_;
+       scalar                          buffer_size_;
 
-       timer                                           stream_timer_;
+       timer                           stream_timer_;
 
-       sound_backend                           backend_;
+       sound_backend                   backend_;
 };
 
 
@@ -613,11 +559,7 @@ sound::sound() :
 
 sound::sound(const std::string& path) :
        // pass through
-       impl_(new sound::impl(path))
-{
-       log_info("sound constructor");
-}
-
+       impl_(new sound::impl(path)) {}
 
 void sound::sample(const std::string& path)
 {
@@ -625,25 +567,18 @@ void sound::sample(const std::string& path)
        impl_->sample(path);
 }
 
-void sound::enqueue(const std::string& path)
+void sound::queue(const std::string& path)
 {
        // pass through
-       impl_->enqueue(path);
+       impl_->queue(path);
 }
 
-
 void sound::play()
 {
        // pass through
        impl_->play();
 }
 
-void sound::stream()
-{
-       // pass through
-       impl_->stream();
-}
-
 void sound::stop()
 {
        // pass through
@@ -656,18 +591,10 @@ void sound::pause()
        impl_->pause();
 }
 
-void sound::rewind()
-{
-       // pass through
-       impl_->rewind();
-}
-
-
 void sound::toggle()
 {
        if (is_playing()) pause();
        else play();
-       // TODO: what about streaming sources?
 }
 
 bool sound::is_playing() const
@@ -676,6 +603,10 @@ bool sound::is_playing() const
        return impl_->is_playing();
 }
 
+void sound::buffer_size(scalar seconds)
+{
+       impl_->buffer_size_ = seconds;
+}
 
 void sound::position(const vector3& position)
 {
@@ -705,7 +636,6 @@ void sound::loop(bool looping)
        impl_->loop(looping);
 }
 
-
 void sound::listener_position(const vector3& position)
 {
        float vec[] = {position[0], position[1], position[2]};
@@ -719,7 +649,7 @@ void sound::listener_velocity(const vector3& velocity)
 }
 
 void sound::listener_orientation(const vector3& forward,
-                                                                const vector3& up)
+               const vector3& up)
 {
        float vec[6];
        vec[0] = float(forward[0]);
This page took 0.040186 seconds and 4 git commands to generate.