X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2Fmoof%2Fsound.cc;h=a6fbf0310679faacc76175aa9d67b71079b1e9e5;hp=7482de7eb9eb22f862041cd6e92a70b2bed14198;hb=382626aad0a683ed8642a6a807eea743db45f7f8;hpb=831f04d4bc19a390415ac0bbac4331c7a65509bc diff --git a/src/moof/sound.cc b/src/moof/sound.cc index 7482de7..a6fbf03 100644 --- a/src/moof/sound.cc +++ b/src/moof/sound.cc @@ -16,210 +16,379 @@ #include #include - +#include +#include #include #include #include #include +#include "hash.hh" #include "log.hh" #include "manager.hh" #include "sound.hh" +#include "resource.hh" #include "timer.hh" -#define BUF_SIZE (64 * 1024) -//#define BUF_SIZE (5*2048) + +#ifndef BUF_SIZE +#define BUF_SIZE (4096) +#endif + +#define NUM_BUFFERS (16) + namespace moof { -class sound::impl +class sound_backend { public: - static ALenum getAudioFormat(const vorbis_info* audioInfo) + sound_backend() + { + if (retain_count++ == 0) + { + al_device = alcOpenDevice(0); + al_context = alcCreateContext(al_device, 0); + if (!al_device || !al_context) + { + const char* error = alcGetString(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; + } + } + } + + + sound_backend(const sound_backend& backend) { - if (audioInfo->channels == 1) return AL_FORMAT_MONO16; - else return AL_FORMAT_STEREO16; + ++retain_count; } - - class buffer; - typedef boost::shared_ptr buffer_ptr; - - class buffer : public manager + sound_backend& operator=(const sound_backend& backend) { - public: + ++retain_count; + return *this; + } - buffer() : - buffer_(-1) + ~sound_backend() + { + if (--retain_count == 0) { - mOggStream.datasource = 0; + alcMakeContextCurrent(0); + alcDestroyContext(al_context); + alcCloseDevice(al_device); + log_info("unloaded sound device ALSA"); } + } + + + static int retain_count; + static ALCdevice* al_device; + static ALCcontext* al_context; +}; + +int sound_backend::retain_count = 0; +ALCdevice* sound_backend::al_device; +ALCcontext* sound_backend::al_context; + + +class sound_resource; +typedef resource_handle sound_handle; + + +class sound_resource_loader +{ +public: + + sound_resource_loader() + { + resource::register_type("ogg"); + } + + ~sound_resource_loader() + { + resource::unregister_type("ogg"); + } +}; + +static sound_resource_loader loader; + + + +// SOUND BUFFER + +class buffer +{ +public: + + typedef hash 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(); + } - ~buffer() + + void queue(ALuint source) const + { + if (*this) { - if (mOggStream.datasource) - { - ov_clear(&mOggStream); - } - if (int(buffer_) != -1) alDeleteBuffers(1, &buffer_); + 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 init(const std::string& name) - { - if (mOggStream.datasource) - { - ov_clear(&mOggStream); - mOggStream.datasource = 0; - } + void set(ALuint source) const + { + log_warning("set buffer:", buffer_); + if (*this) alSourcei(source, AL_BUFFER, buffer_); + } - std::string path(name); - if (!sound::find_path(path)) - { - throw std::runtime_error("cannot find resource: " + name); - } + operator bool () const + { + return buffer_ != (ALuint)-1; + } - if (ov_fopen((char*)path.c_str(), &mOggStream) < 0) - { - throw std::runtime_error("problem reading audio: " + name); - } - vorbis_info* vorbisInfo = ov_info(&mOggStream, -1); - mFormat = getAudioFormat(vorbisInfo); - mFreq = vorbisInfo->rate; - } +private: + + explicit buffer(ALuint buf) : + buffer_(buf) {} - void load_all(ALuint source) + 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) { - if (!mOggStream.datasource) init(name()); - if (!mOggStream.datasource) return; + alDeleteBuffers(1, &buffer_); + retain_counts_.erase(it); + log_warning("kill buffer:", buffer_); + } + } - char data[BUF_SIZE]; - int size = 0; - for (;;) - { - int section; - int result = ov_read(&mOggStream, data + size, - BUF_SIZE - size, 0, 2, 1, §ion); + ALuint buffer_; + sound_backend backend_; - if (result > 0) - { - size += result; - } - else - { - if (result < 0) log_warning("vorbis playback error"); - break; - } - } - if (size == 0) - { - log_warning("decoded no bytes from", name()); - return; - } + static retcount_lookup retain_counts_; +}; + +buffer::retcount_lookup buffer::retain_counts_; + + + +// SOUND RESOURCE + +class sound_resource : public boost::noncopyable +{ +public: + + sound_resource(const std::string& path) + { + log_info("audio path is", path); + if (ov_fopen((char*)path.c_str(), &file_) < 0) + { + throw std::runtime_error("problem reading audio: " + path); + } + } - alGenBuffers(1, &buffer_); + ~sound_resource() + { + ov_clear(&file_); + } - alBufferData(buffer_, mFormat, data, size, mFreq); - alSourcei(source, AL_BUFFER, buffer_); - // don't need to keep this loaded - ov_clear(&mOggStream); - mOggStream.datasource = 0; + bool read(buffer& buf) const + { + if (buffer_) + { + buf = buffer_; + return true; } - bool stream(ALuint buffer) + if (ov_pcm_seek_lap(&file_, 0) != 0) { - char data[BUF_SIZE]; - int size = 0; + log_warning("vorbis seek error"); + return false; + } - while (size < BUF_SIZE) - { - int section; - int result = ov_read(&mOggStream, data + size, - BUF_SIZE - size, 0, 2, 1, §ion); + char data[64*BUF_SIZE]; + size_t size = 0; - if (result > 0) - { - size += result; - } - else - { - if (result < 0) log_warning("vorbis playback error"); - break; - } + while (size < sizeof(data)) + { + int section; + int result = ov_read(&file_, + data + size, sizeof(data) - size, + 0, 2, 1, §ion); + + if (result > 0) + { + 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; + } + else + { + log_warning("vorbis playback error"); + break; } + } - if (size == 0) return false; + if (size >= sizeof(data)) log_warning("sample is too big to play"); + return false; + } - alBufferData(buffer, mFormat, data, size, mFreq); - return true; + bool read(buffer& buf, uint64_t& sample) const + { + if (ov_pcm_seek_lap(&file_, sample) != 0) + { + log_warning("vorbis seek error"); + return false; } - void rewind() + char data[BUF_SIZE]; + int section; + int result = ov_read(&file_, data, sizeof(data), 0, 2, 1, §ion); + + if (result > 0) { - if (!mOggStream.datasource) init(name()); - else ov_raw_seek(&mOggStream, 0); + 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); + return true; } + if (result < 0) log_warning("vorbis playback error"); + return false; + } + + + static ALenum get_audio_format(const vorbis_info* info) + { + if (info->channels == 1) return AL_FORMAT_MONO16; + else return AL_FORMAT_STEREO16; + } + + +private: + + mutable OggVorbis_File file_; + mutable buffer buffer_; +}; - private: - OggVorbis_File mOggStream; - ALenum mFormat; - ALsizei mFreq; - ALuint buffer_; - }; +class sound::impl +{ +public: impl() { init(); } - impl(const std::string& name) + impl(const std::string& path) { + log_info("sound::impl constructor"); init(); - enqueue(name); + enqueue(path); } void init() { - retain_backend(); - - is_loaded_ = false; is_playing_ = false; is_looping_ = false; + sample_ = 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); - - alSourcei(source_, AL_LOOPING, is_looping_); + log_error("init:", alGetError()); } ~impl() { stop(); - alDeleteSources(1, &source_); - - while (!buffers_.empty()) - { - alDeleteBuffers(1, &buffers_.back()); - buffers_.pop_back(); - } - - release_backend(); } @@ -227,53 +396,60 @@ public: { if (queue_.empty()) return; - if (!is_loaded_) queue_.front()->load_all(source_); + sound_handle handle = queue_.front(); + buffer buf; - alSourcePlay(source_); - is_loaded_ = true; + if (handle->read(buf)) + { + log_info("playing source..."); + buf.set(source_); + alSourcei(source_, AL_LOOPING, is_looping_); + alSourcePlay(source_); + } } - - void play_stream() + void stream() { if (queue_.empty()) return; if (!is_playing_) { alSourcei(source_, AL_LOOPING, false); - buffer_stream(); + log_error("set not looping:", alGetError()); + + sound_handle handle = queue_.front(); + + for (int i = 0; i < NUM_BUFFERS; ++i) + { + buffer buf; + if (handle->read(buf, sample_)) + { + buf.queue(source_); + } + else + { + log_error("failed to start stream"); + break; + } + + ALint queued = 0; + alGetSourcei(source_, AL_BUFFERS_QUEUED, &queued); + log_info("buffers queued:", queued); + } } if (!stream_timer_.is_valid()) { stream_timer_.init(boost::bind(&impl::stream_update, this, _1, _2), - 1.0, timer::repeat); + 0.01, timer::repeat); } + log_info("streaming source..."); alSourcePlay(source_); + log_error("playing:", alGetError()); is_playing_ = true; } - void buffer_stream() - { - ALuint buffer; - for (int i = buffers_.size(); i <= 8; ++i) - { - alGenBuffers(1, &buffer); - - if (queue_.front()->stream(buffer)) - { - alSourceQueueBuffers(source_, 1, &buffer); - buffers_.push_back(buffer); - } - else - { - alDeleteBuffers(1, &buffer); - break; - } - } - } - void update() { @@ -283,47 +459,50 @@ public: while (finished-- > 0) { - ALuint bufferObj; - alSourceUnqueueBuffers(source_, 1, &bufferObj); - - buffer_ptr buffer = queue_.front(); - bool streamed = buffer->stream(bufferObj); + 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) { - alSourceQueueBuffers(source_, 1, &bufferObj); + buf.queue(source_); } else { // the buffer couldn't be streamed, so get rid of it queue_.pop_front(); + sample_ = 0; if (!queue_.empty()) { // begin the next buffer in the queue - queue_.front()->rewind(); - queue_.front()->stream(bufferObj); - alSourceQueueBuffers(source_, 1, &bufferObj); + handle->read(buf, sample_); + buf.queue(source_); log_info("loading new buffer"); - - // queue up any unused buffers - buffer_stream(); } else if (is_looping_) { // reload the same buffer - queue_.push_back(buffer); - buffer->rewind(); - buffer->stream(bufferObj); - alSourceQueueBuffers(source_, 1, &bufferObj); log_info("looping same buffer"); + + queue_.push_back(handle); + handle->read(buf, sample_); + buf.queue(source_); } else { // nothing more to play, stopping... - is_playing_ = false; - std::remove(buffers_.begin(), buffers_.end(), - bufferObj); + stop(); + queue_.push_back(handle); } } } @@ -346,6 +525,8 @@ public: is_playing_ = false; stream_timer_.invalidate(); + + // TODO: clear buffers if streaming } void pause() @@ -356,39 +537,37 @@ public: stream_timer_.invalidate(); } + void rewind() + { + alSourceRewind(source_); + sample_ = 0; + } + - void sample(const std::string& name) + void sample(const std::string& path) { stop(); alSourcei(source_, AL_BUFFER, AL_NONE); queue_.clear(); - is_loaded_ = false; - enqueue(name); - - while (!buffers_.empty()) - { - alDeleteBuffers(1, &buffers_.back()); - buffers_.pop_back(); - } + enqueue(path); } - void enqueue(const std::string& name) + void enqueue(const std::string& path) { - buffer_ptr buffer = buffer::instance(name); - queue_.push_back(buffer); + sound_handle handle = resource::load(path); + queue_.push_back(handle); } bool is_playing() const { - if (is_playing_) return true; - ALenum state; alGetSourcei(source_, AL_SOURCE_STATE, &state); - return state == AL_PLAYING; + if (state == AL_PLAYING) return true; + else return is_playing_; } @@ -414,74 +593,42 @@ public: } - static void retain_backend() - { - if (retain_count_++ == 0) - { - al_device_ = alcOpenDevice(0); - al_context_ = alcCreateContext(al_device_, 0); - if (!al_device_ || !al_context_) - { - const char* error = alcGetString(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; - } - } - } - - static void release_backend() - { - if (--retain_count_ == 0) - { - alcMakeContextCurrent(0); - alcDestroyContext(al_context_); - alcCloseDevice(al_device_); - } - } - - - ALuint source_; - std::list buffers_; + ALuint source_; - bool is_loaded_; - bool is_playing_; - bool is_looping_; + bool is_playing_; + bool is_looping_; - std::deque queue_; + std::deque queue_; + uint64_t sample_; - timer stream_timer_; + timer stream_timer_; - static unsigned retain_count_; - static ALCdevice* al_device_; - static ALCcontext* al_context_; + sound_backend backend_; }; -unsigned sound::impl::retain_count_ = 0; -ALCdevice* sound::impl::al_device_ = 0; -ALCcontext* sound::impl::al_context_ = 0; - sound::sound() : // pass through impl_(new sound::impl) {} -sound::sound(const std::string& name) : +sound::sound(const std::string& path) : // pass through - impl_(new sound::impl(name)) {} + impl_(new sound::impl(path)) +{ + log_info("sound constructor"); +} -void sound::sample(const std::string& name) +void sound::sample(const std::string& path) +{ + // pass through + impl_->sample(path); +} + +void sound::enqueue(const std::string& path) { // pass through - impl_->sample(name); + impl_->enqueue(path); } @@ -491,6 +638,12 @@ void sound::play() impl_->play(); } +void sound::stream() +{ + // pass through + impl_->stream(); +} + void sound::stop() { // pass through @@ -503,11 +656,18 @@ 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 @@ -559,7 +719,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]); @@ -572,28 +732,5 @@ void sound::listener_orientation(const vector3& forward, } -bool sound::find_path(std::string& name) -{ - return resource::find_path(name, "sounds/", "ogg"); -} - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -void sound_stream::enqueue(const std::string& name) -{ - // pass through - impl_->enqueue(name); -} - - -void sound_stream::play() -{ - // pass through - impl_->play_stream(); -} - - } // namespace moof