From e9a16d767785b1a903a4466ff26265a5f7dae9e5 Mon Sep 17 00:00:00 2001 From: Charles McGarvey Date: Wed, 4 Aug 2010 20:56:19 -0600 Subject: [PATCH] testing improved runloop scheduling --- data/scenes/Classic.lua | 2 + data/yoinkrc | 21 +- src/Character.cc | 4 +- src/GameLayer.cc | 6 +- src/Main.cc | 10 +- src/moof/log.cc | 19 +- src/moof/log.hh | 2 + src/moof/mesh.cc | 17 -- src/moof/resource.cc | 4 +- src/moof/sound.cc | 461 ++++++++++++++++++------------------- src/moof/sound.hh | 5 +- src/moof/sound_bindings.cc | 50 ++-- src/moof/timer.cc | 46 ++-- src/moof/timer.hh | 13 +- src/moof/view.cc | 235 ++++++++++++++++--- 15 files changed, 530 insertions(+), 365 deletions(-) diff --git a/data/scenes/Classic.lua b/data/scenes/Classic.lua index 8476279..c2ca183 100644 --- a/data/scenes/Classic.lua +++ b/data/scenes/Classic.lua @@ -68,6 +68,8 @@ Event = {} do local mysound = yoink.sound("Explosion") + --local mysound = yoink.sound() + --mysound:sample("Explosion") local count = 0 function Event.Think() if count % 300 == 0 then diff --git a/data/yoinkrc b/data/yoinkrc index d26ac4d..af051b3 100644 --- a/data/yoinkrc +++ b/data/yoinkrc @@ -47,13 +47,13 @@ fullscreen = false resizable = true -- Set the screen resolution or size of the window. The value is an array --- with three number elements representing the width, height, and bits per --- pixel that make up the video mode. If the fullscreen option is set, the --- default behavior is to pick a native resolution. You can use the --- videomode to override the default resolution. If the fullscreen option --- is false, videomode will determine the size of the window. +-- with three number elements representing the width, height, and +-- (optionally) bits per pixel that make up the video mode. -if fullscreen == false then +if fullscreen then + -- In fullscreen mode, a native resolution will be picked. You could + -- still set videomode if you want to use a different resolution. +else videomode = {800, 600} end @@ -72,10 +72,11 @@ swapcontrol = true -- Set the level of log detail that will be output to the console. -- Possible values are: --- 0 print nothing --- 1 output errors only --- 2 include warnings --- 3 print everything, including debug messages +-- 0 nothing +-- 1 errors +-- 2 warnings +-- 3 info +-- 4 debug messages loglevel = 2 diff --git a/src/Character.cc b/src/Character.cc index c5d4838..3cae680 100644 --- a/src/Character.cc +++ b/src/Character.cc @@ -74,12 +74,12 @@ Character::Character(const std::string& name) : // forces state_.force = moof::vector2(0.0, 0.0); //state_.forces.push_back(SpringForce(moof::vector2(5.0, 4.0))); - state_.forces.push_back(ResistanceForce(2.0)); + //state_.forces.push_back(ResistanceForce(2.0)); //state_.forces.push_back(moof::linear_state<2>::gravity_force(-9.8)); // starting position state_.position = moof::vector2(5.0, 5.0); - state_.momentum = moof::vector2(1.0, 0.0); + state_.momentum = moof::vector2(3.0, 0.0); state_.recalculate(); prev_state_ = state_; diff --git a/src/GameLayer.cc b/src/GameLayer.cc index 8ff3a75..7d36f33 100644 --- a/src/GameLayer.cc +++ b/src/GameLayer.cc @@ -90,12 +90,12 @@ void GameLayer::advanceScene(moof::settings& settings) GameLayer::GameLayer() { - music_.sample("NightFusionIntro.ogg"); + music_.queue("NightFusionIntro"); music_.loop(true); - music_.enqueue("NightFusionLoop.ogg"); + music_.queue("NightFusionLoop"); music_.position(moof::vector3(10.0, 5.0, 0.0)); - punch_sound_.sample("RobotPunch"); + punch_sound_.sample("Thump"); state_.heroine = Heroine::alloc(); state_.heroine->animation.startSequence("FlyDiagonallyUp"); diff --git a/src/Main.cc b/src/Main.cc index 13681ee..ff2ce61 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -20,7 +20,7 @@ #if !defined(__WIN32) #include #else -int isatty(int dummy) { return 0; } +inline int isatty(int dummy) { return 0; } #endif #include @@ -163,9 +163,9 @@ void Main::setup_opengl() glEnable(GL_DEPTH_TEST); //glEnable(GL_CULL_FACE); - //glEnable(GL_POINT_SMOOTH); - //glEnable(GL_LINE_SMOOTH); - //glEnable(GL_POLYGON_SMOOTH); + glEnable(GL_POINT_SMOOTH); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_POLYGON_SMOOTH); glShadeModel(GL_SMOOTH); //glEnable(GL_BLEND); @@ -331,7 +331,7 @@ void goodbye() int main(int argc, char* argv[]) { - moof::backend backend; + //moof::backend backend; if (argc > 1) { diff --git a/src/moof/log.cc b/src/moof/log.cc index f9a3ad9..e59740d 100644 --- a/src/moof/log.cc +++ b/src/moof/log.cc @@ -14,7 +14,7 @@ #if !defined(__WIN32) #include #else -int isatty(int dummy) { return 0; } +inline int isatty(int dummy) { return 0; } #endif @@ -48,14 +48,16 @@ log::log(enum level level) : case log::error: prefix_ = " error: "; break; case log::warning: prefix_ = "warning: "; break; case log::info: prefix_ = " info: "; break; + case log::debug: prefix_ = " debug: "; break; case log::none: break; } else switch (level) { - case log::error: prefix_ = "\033[101m error: "; break; - case log::warning: prefix_ = "\033[103mwarning: "; break; - case log::info: prefix_ = " info: "; break; + case log::error: prefix_ = "\033[30;101m error: "; break; + case log::warning: prefix_ = "\033[30;103mwarning: "; break; + case log::info: prefix_ = " info: "; break; + case log::debug: prefix_ = "\033[2m debug: "; break; case log::none: break; } } @@ -79,14 +81,15 @@ std::ostream& log(std::cout); static std::ofstream null_log_; std::ostream& null_log(null_log_); -class log log_error( log::error); +class log log_error(log::error); class log log_warning(log::warning); -class log log_info( log::info); +class log log_info(log::info); +class log log_debug(log::debug); static int log_script(script& script, enum log::level level) { - static class log* logs[] = {0, &log_error, &log_warning, &log_info}; + static class log* logs[] = {0, &log_error, &log_warning, &log_info, &log_debug}; script::slot param = script[1]; @@ -109,6 +112,8 @@ void log::import(script& script) boost::bind(log_script, _1, log::info)); script.import_function("print", boost::bind(log_script, _1, log::info)); + script.import_function("LogDebug", + boost::bind(log_script, _1, log::debug)); } diff --git a/src/moof/log.hh b/src/moof/log.hh index e6e1a80..1e2a216 100644 --- a/src/moof/log.hh +++ b/src/moof/log.hh @@ -50,6 +50,7 @@ public: error = 1, ///< Log only errors. warning = 2, ///< Log warnings and errors. info = 3, ///< Log everything. + debug = 4, ///< Log absolutely everything. }; @@ -149,6 +150,7 @@ extern std::ostream& null_log; extern class log log_error; extern class log log_warning; extern class log log_info; +extern class log log_debug; template diff --git a/src/moof/mesh.cc b/src/moof/mesh.cc index 023138b..e380adf 100644 --- a/src/moof/mesh.cc +++ b/src/moof/mesh.cc @@ -356,25 +356,8 @@ void mesh::import(std::istream& stream) obj = obj->parent.lock(); } } - else - { - log_warning("UNKNOWN ATOM:", atom); - } } while (stream); - - std::vector::iterator meh; - for (meh = objects_.begin(); meh != objects_.end(); ++meh) - { - object_ptr cow = *meh; - log_info("OBJ: -", cow->name, cow->kids.size()); - - std::vector::iterator foo; - for (foo = cow->kids.begin(); foo != cow->kids.end(); ++foo) - { - log_info("OBJ: -", (*foo)->name, (*foo)->kids.size()); - } - } } //unsigned mesh::read_vertex_line(std::istream& stream) diff --git a/src/moof/resource.cc b/src/moof/resource.cc index 3cf901b..35cd663 100644 --- a/src/moof/resource.cc +++ b/src/moof/resource.cc @@ -63,12 +63,12 @@ std::string resource::find_file(const std::string& name) // try it with the prefix first path = stlplus::filespec_to_path(*it, prefix); path = stlplus::filespec_to_path(path, name); - log_info("looking for", name, "at", path); + log_debug("looking for", name, "at", path); if (stlplus::file_exists(path)) return path; } path = stlplus::filespec_to_path(*it, name); - log_info("looking for", name, "at", path); + log_debug("looking for", name, "at", path); if (stlplus::file_exists(path)) return path; } diff --git a/src/moof/sound.cc b/src/moof/sound.cc index 2c19d26..f306054 100644 --- a/src/moof/sound.cc +++ b/src/moof/sound.cc @@ -9,9 +9,7 @@ * **************************************************************************/ -#include #include -#include #include #include @@ -22,7 +20,6 @@ #include #include -#include "hash.hh" #include "log.hh" #include "sound.hh" #include "resource.hh" @@ -30,10 +27,10 @@ #ifndef BUF_SIZE -#define BUF_SIZE (4096 * 64) +#define BUF_SIZE (4096) #endif -#define NUM_BUFFERS (4) +#define NUM_SAMPLE_BITS (16) namespace moof { @@ -108,109 +105,6 @@ typedef resource_handle sound_handle; MOOF_REGISTER_RESOURCE(sound_resource, ogg, sounds); -/*] 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; - } - - 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(); - } - } - - static buffer unqueue(ALuint source) - { - ALuint buf = (ALuint)-1; - alSourceUnqueueBuffers(source, 1, &buf); - return buffer(buf); - } - - void set(ALuint source) const - { - 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); - } - } - - - ALuint buffer_; - sound_backend backend_; - - static retcount_lookup retain_counts_; -}; - -buffer::retcount_lookup buffer::retain_counts_; - - - /*] Sound resource *************************************************************************/ @@ -218,7 +112,9 @@ class sound_resource : public boost::noncopyable { public: - sound_resource(const std::string& path) + sound_resource(const std::string& path) : + buffer_(AL_NONE), + sample_(0) { if (ov_fopen((char*)path.c_str(), &file_) < 0) { @@ -229,24 +125,23 @@ public: ~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)) @@ -256,18 +151,21 @@ public: data + size, sizeof(data) - size, 0, 2, 1, §ion); - if (result > 0) + if (0 < result) { size += result; - continue; } else if (result == 0 && size > 0) { vorbis_info* info = ov_info(&file_, section); - buffer_ = buffer(data, size, - get_audio_format(info), info->rate); - buf = buffer_; - return true; + + ALuint buffer; + alGenBuffers(1, &buffer); + alBufferData(buffer, get_audio_format(info), + data, size, info->rate); + + buffer_ = buffer; + return buffer; } else { @@ -276,16 +174,14 @@ public: } } - 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) + if ((sample == sample_ && ov_pcm_seek_lap(&file_, sample) != 0) || + (sample != sample_ && ov_pcm_seek(&file_, sample) != 0)) { - log_warning("vorbis seek error"); return false; } @@ -293,30 +189,73 @@ public: int section; int result = ov_read(&file_, data, sizeof(data), 0, 2, 1, §ion); - if (result > 0) + 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; + } + + 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, §ion); + + if (0 < result) { vorbis_info* info = ov_info(&file_, section); - buf = buffer(data, result, get_audio_format(info), info->rate); - sample = ov_pcm_tell(&file_); + 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"); - 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_; }; @@ -335,7 +274,7 @@ public: impl(const std::string& name) { init(); - enqueue(name); + sample(name); } void init() @@ -344,6 +283,7 @@ public: is_looping_ = false; sample_ = 0; + buffer_size_ = SCALAR(1.0); alGenSources(1, &source_); @@ -357,126 +297,175 @@ public: ~impl() { stop(); + deplete_stream(); + alDeleteSources(1, &source_); } - void play() + void deplete_stream() { - if (queue_.empty()) return; - - sound_handle handle = queue_.front(); - buffer buf; - - if (handle->read(buf)) + if (is_streaming_) { - buf.set(source_); - alSourcei(source_, AL_LOOPING, is_looping_); - alSourcePlay(source_); + 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); } - void stream() + void play() { - if (queue_.empty()) return; + if (queue_.empty() || is_playing_) return; - if (!is_playing_) + ALenum state; + alGetSourcei(source_, AL_SOURCE_STATE, &state); + switch (state) { - alSourcei(source_, AL_LOOPING, false); + case AL_INITIAL: + case AL_STOPPED: - sound_handle handle = queue_.front(); + 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; - for (int i = 0; i < NUM_BUFFERS; ++i) - { - buffer buf; - if (handle->read(buf, sample_)) + case AL_PAUSED: + + if (is_streaming_) { - buf.queue(source_); + start_update_timer(); + is_playing_ = true; } + break; + } + + alSourcePlay(source_); + } + + void start_update_timer() + { + stream_timer_.init(boost::bind(&impl::stream_update, this, _1, _2), + SCALAR(0.1), timer::repeat); + } + + 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]; + + if (0 < target_diff) + { + if (queue_buffer(buf)) --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); } + else alDeleteBuffers(1, &buf); } - if (!stream_timer_.is_valid()) + if (stream_timer_.is_valid() && 0 < target_diff) { - stream_timer_.init(boost::bind(&impl::stream_update, this, _1, _2), - SCALAR(0.5), timer::repeat); + for (int i = 0; i < target_diff; ++i) + { + if (!queue_buffer(AL_NONE)) break; + } } - alSourcePlay(source_); - 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() + bool queue_buffer(ALuint buffer) { - ALint finished = 0; - - alGetSourcei(source_, AL_BUFFERS_PROCESSED, &finished); + if (buffer == AL_NONE) alGenBuffers(1, &buffer); - 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()) - { - // begin the next buffer in the queue - handle->read(buf, sample_); - buf.queue(source_); - } - else if (is_looping_) + if (0 < queue_.size()) { - // reload the same buffer - queue_.push_back(handle); - handle->read(buf, sample_); - buf.queue(source_); + 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 + else if (is_looping_ && !looped_once) { - // nothing more to play, stopping... - stop(); - queue_.push_back(handle); + queue_.push_back(sound); + looped_once = true; } + else 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; } @@ -487,7 +476,7 @@ public: stream_timer_.invalidate(); - // TODO: clear buffers if streaming + sample_ = 0; } void pause() @@ -498,27 +487,22 @@ public: stream_timer_.invalidate(); } - void rewind() - { - alSourceRewind(source_); - sample_ = 0; - } - void sample(const std::string& name) { stop(); - alSourcei(source_, AL_BUFFER, AL_NONE); + deplete_stream(); queue_.clear(); - - enqueue(name); + queue(name); + is_streaming_ = false; } - void enqueue(const std::string& name) + void queue(const std::string& name) { sound_handle handle = resource::load(name, "ogg"); - queue_.push_back(handle); + if (handle) queue_.push_back(handle); + is_streaming_ = true; } @@ -538,7 +522,6 @@ public: ALenum type; alGetSourcei(source_, AL_SOURCE_TYPE, &type); - if (type != AL_STREAMING) { alSourcei(source_, AL_LOOPING, is_looping_); @@ -548,7 +531,8 @@ public: void stream_update(timer& timer, scalar t) { - update(); + //log_error("blaaaaaaaaaaaaaaaaaaaaaaaaaaahhhhhhhhhhhhhhhhrrrrrg"); + fill_stream(); // TODO: might be nice to also allow using threads for streaming // rather than a timer, probably as a compile-time option } @@ -558,9 +542,12 @@ public: bool is_playing_; bool is_looping_; + bool is_streaming_; std::deque queue_; uint64_t sample_; + int num_buffers_; + scalar buffer_size_; timer stream_timer_; @@ -583,17 +570,17 @@ 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_->stream(); + impl_->play(); } void sound::stop() @@ -608,13 +595,6 @@ void sound::pause() impl_->pause(); } -void sound::rewind() -{ - // pass through - impl_->rewind(); -} - - void sound::toggle() { if (is_playing()) pause(); @@ -627,6 +607,11 @@ 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) { diff --git a/src/moof/sound.hh b/src/moof/sound.hh index f920f49..d4c1bcd 100644 --- a/src/moof/sound.hh +++ b/src/moof/sound.hh @@ -38,16 +38,17 @@ public: explicit sound(const std::string& name); void sample(const std::string& name); - void enqueue(const std::string& name); + void queue(const std::string& name); void play(); void stop(); void pause(); - void rewind(); void toggle(); bool is_playing() const; + void buffer_size(scalar seconds); + void position(const vector3& position); void velocity(const vector3& velocity); void gain(scalar gain); diff --git a/src/moof/sound_bindings.cc b/src/moof/sound_bindings.cc index 9e81aa3..6849d91 100644 --- a/src/moof/sound_bindings.cc +++ b/src/moof/sound_bindings.cc @@ -18,16 +18,26 @@ namespace moof { static int sound_new(script& script) { - script::slot name = script[2].require_string("sound name"); - std::string str; - name.get(str); - - script.push(sound(str)); + if (script[2].get(str)) + { + sound sound(str); + + for (int i = 3; script[i]; ++i) + { + if (script[i].get(str)) sound.queue(str); + else break; + } + + script.push(sound); + return 1; + } + + script.push(sound()); return 1; } -static int sound_enqueue(script& script) +static int sound_sample(script& script) { sound* sound; script[1].require_object("sound").get(sound); @@ -35,39 +45,45 @@ static int sound_enqueue(script& script) std::string name; script[2].require_string("sound name").get(name); - sound->enqueue(name); + sound->sample(name); return 0; } -static int sound_play(script& script) +static int sound_queue(script& script) { sound* sound; script[1].require_object("sound").get(sound); - sound->play(); + + std::string name; + for (int i = 2; script[i]; ++i) + { + if (script[i].get(name)) sound->queue(name); + else break; + } return 0; } -static int sound_stop(script& script) +static int sound_play(script& script) { sound* sound; script[1].require_object("sound").get(sound); - sound->stop(); + sound->play(); return 0; } -static int sound_pause(script& script) +static int sound_stop(script& script) { sound* sound; script[1].require_object("sound").get(sound); - sound->pause(); + sound->stop(); return 0; } -static int sound_rewind(script& script) +static int sound_pause(script& script) { sound* sound; script[1].require_object("sound").get(sound); - sound->rewind(); + sound->pause(); return 0; } @@ -95,11 +111,11 @@ void sound::import(script& script, const std::string& nspace) script::slot parent = script.push_table(nspace); script::slot meta = script.push_class(sound_new); - meta.set_field("enqueue", sound_enqueue); + meta.set_field("sample", sound_sample); + meta.set_field("queue", sound_queue); meta.set_field("play", sound_play); meta.set_field("stop", sound_stop); meta.set_field("pause", sound_pause); - meta.set_field("rewind", sound_rewind); meta.set_field("toggle", sound_toggle); meta.set_field("is_playing", sound_is_playing); diff --git a/src/moof/timer.cc b/src/moof/timer.cc index 74acc1c..f16a962 100644 --- a/src/moof/timer.cc +++ b/src/moof/timer.cc @@ -24,7 +24,7 @@ namespace moof { -scalar timer::next_expiration_ = std::numeric_limits::max(); +scalar timer::next_event_ = std::numeric_limits::max(); hash timer::timers_; @@ -58,7 +58,7 @@ void timer::init(const function& function, scalar seconds, mode mode) id_ = new_identifier(); timers_.insert(std::pair(id_, this)); - if (absolute_ < next_expiration_) next_expiration_ = absolute_; + if (absolute_ < next_event_) next_event_ = absolute_; } } @@ -75,9 +75,9 @@ void timer::invalidate() timers_.erase(id_); mode_ = invalid; - if (is_equal(absolute_, next_expiration_)) + if (is_equal(absolute_, next_event_)) { - next_expiration_ = find_next_expiration(); + next_event_ = find_next_event(); } } } @@ -96,9 +96,9 @@ void timer::fire() if (is_equal(absolute_, t, 1.0)) absolute_ += interval_; else absolute_ = interval_ + t; - if (is_equal(absolute, next_expiration_)) + if (is_equal(absolute, next_event_)) { - next_expiration_ = find_next_expiration(); + next_event_ = find_next_event(); } } else @@ -108,7 +108,7 @@ void timer::fire() } -scalar timer::find_next_expiration() +scalar timer::find_next_event() { scalar next_fire = std::numeric_limits::max(); @@ -128,6 +128,11 @@ scalar timer::seconds_remaining() const return absolute_ - ticks(); } +scalar timer::next_expiration() const +{ + return absolute_; +} + bool timer::is_expired() const { return seconds_remaining() < 0.0; @@ -141,7 +146,7 @@ bool timer::is_repeating() const void timer::fire_expired_timers(scalar t) { - if (next_expiration_ > t) return; + if (t < next_event_) return; hash::iterator it; for (it = timers_.begin(); it.valid(); ++it) @@ -191,18 +196,15 @@ scalar timer::ticks() void timer::sleep(scalar seconds, mode mode) { - struct timespec ts; - int ret; - if (mode == absolute) seconds -= ticks(); - ts.tv_sec = time_t(seconds); - ts.tv_nsec = long((seconds - scalar(ts.tv_sec)) * SCALAR(1000000000.0)); + if (seconds < SCALAR(0.0)) return; - do - { - ret = nanosleep(&ts, &ts); - } - while (ret == -1 && errno == EINTR); + struct timespec ts; + ts.tv_sec = seconds; + ts.tv_nsec = (seconds - scalar(ts.tv_sec)) * SCALAR(1000000000.0); + + int ret; + do ret = nanosleep(&ts, &ts); while (ret == -1 && errno == EINTR); } @@ -211,18 +213,18 @@ void timer::sleep(scalar seconds, mode mode) // If we don't have posix timers, we'll have to use a different timing // method. SDL only promises centisecond accuracy, but that's better than -// a kick in the pants. +// a kick in the pants. It could end up being just as good anyway. scalar timer::ticks() { - Uint32 ms = SDL_GetTicks(); - return scalar(ms / 1000) + scalar(ms % 1000) * SCALAR(0.001); + return scalar(SDL_GetTicks()) * SCALAR(0.001); } void timer::sleep(scalar seconds, mode mode) { if (mode == absolute) seconds -= ticks(); - SDL_Delay(Uint32(clamp(Uint32(seconds * SCALAR(1000.0)), 0, 1000))); + if (seconds < SCALAR(0.0)) return; + SDL_Delay(seconds * SCALAR(1000.0)); } #endif // USE_CLOCK_GETTIME diff --git a/src/moof/timer.hh b/src/moof/timer.hh index cb50c3b..f5c3583 100644 --- a/src/moof/timer.hh +++ b/src/moof/timer.hh @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -33,7 +34,7 @@ namespace moof { * runloop associated with the current running view will make an attempt to * fire any expired timers as close to their scheduled times as possible. */ -class timer +class timer : public boost::noncopyable { public: @@ -148,6 +149,8 @@ public: */ scalar seconds_remaining() const; + scalar next_expiration() const; + /** * Get whether or not the timer is expired. A timer on a repeating * schedule will never be expired since it will always have a scheduled @@ -191,9 +194,9 @@ public: * Get the absolute time when the next timer is scheduled to expire. * \return Absolute time, in seconds. */ - static scalar next_expiration() + static scalar next_event() { - return next_expiration_; + return next_event_; } @@ -216,7 +219,7 @@ public: private: static unsigned new_identifier(); - static scalar find_next_expiration(); + static scalar find_next_event(); function function_; mode mode_; @@ -224,7 +227,7 @@ private: scalar interval_; unsigned id_; - static scalar next_expiration_; + static scalar next_event_; static hash timers_; }; diff --git a/src/moof/view.cc b/src/moof/view.cc index 5c10378..ab0058b 100644 --- a/src/moof/view.cc +++ b/src/moof/view.cc @@ -30,6 +30,71 @@ namespace moof { +// Timestep Example Source Code +// Copyright (c) Glenn Fiedler 2004 +// http://www.gaffer.org/articles + +struct State +{ + float x; + float v; +}; + +struct Derivative +{ + float dx; + float dv; +}; + +State interpolate(const State &previous, const State ¤t, float alpha) +{ + State state; + state.x = current.x*alpha + previous.x*(1-alpha); + state.v = current.v*alpha + previous.v*(1-alpha); + return state; +} + +float acceleration(const State &state, float t) +{ + const float k = 10; + const float b = 1; + return - k*state.x - b*state.v; +} + +Derivative evaluate(const State &initial, float t) +{ + Derivative output; + output.dx = initial.v; + output.dv = acceleration(initial, t); + return output; +} + +Derivative evaluate(const State &initial, float t, float dt, const Derivative &d) +{ + State state; + state.x = initial.x + d.dx*dt; + state.v = initial.v + d.dv*dt; + Derivative output; + output.dx = state.v; + output.dv = acceleration(state, t+dt); + return output; +} + +void integrate(State &state, float t, float dt) +{ + Derivative a = evaluate(state, t); + Derivative b = evaluate(state, t, dt*0.5f, a); + Derivative c = evaluate(state, t, dt*0.5f, b); + Derivative d = evaluate(state, t, dt, c); + + const float dxdt = 1.0f/6.0f * (a.dx + 2.0f*(b.dx + c.dx) + d.dx); + const float dvdt = 1.0f/6.0f * (a.dv + 2.0f*(b.dv + c.dv) + d.dv); + + state.x = state.x + dxdt*dt; + state.v = state.v + dvdt*dt; +} + + class root_view : public view { void update(scalar t, scalar dt) @@ -93,10 +158,57 @@ public: * function will return the exit code used to stop the loop. */ + scalar nextUpdate; + scalar totalTime; + + void U(timer& T, scalar t) + { + const int MAX_FRAMESKIP = 15; + + log_info("update"); + + int i = 0; + while (nextUpdate < t && ++i < MAX_FRAMESKIP) + { + totalTime += timestep_; // 3. update state + view_.update(totalTime, timestep_); + + //previous = current; + //integrate(current, totalTime, timestep); + + nextUpdate += timestep_; + } + } + + void D(timer& T, scalar t) + { + const scalar inverseTimestep = SCALAR(1.0) / timestep_; + + log_info("draw"); + + scalar alpha = (t + timestep_ - nextUpdate) * inverseTimestep; + //scalar alpha = (nextUpdate - t) * inverseTimestep; + if (alpha < SCALAR(0.0)) log_error("UH OH!!!!! It's NEGATIVE", alpha); + if (alpha > SCALAR(1.0)) log_error("UH OH!!!!! It's POSITIVE", alpha); + log_info("alpha:", alpha); + + view_.draw(alpha); + video_->swap(); // 4. draw state + } + + timer utimer, dtimer; + void run() { ASSERT(video_ && "running without video set"); + utimer.init(boost::bind(&impl::U, this, _1, _2), timestep_, timer::repeat); + dtimer.init(boost::bind(&impl::D, this, _1, _2), framerate_, timer::repeat); + + totalTime = 0.0f; + nextUpdate = timer::ticks(); + + scalar totalTime = 0.0; scalar ticks = timer::ticks(); @@ -107,7 +219,7 @@ public: fps_ = 0; int frameCount = 0; - const scalar timestep = timestep_; + const scalar timestep = SCALAR(0.01);//timestep_; const scalar framerate = framerate_; const int MAX_FRAMESKIP = 15; @@ -119,44 +231,97 @@ public: timer::fire_expired_timers(); // 1. fire timers dispatch_events(); // 2. dispatch events - if (!is_running_) break; - - int i = 0; - while (nextUpdate < timer::ticks() && i < MAX_FRAMESKIP) - { - totalTime += timestep; // 3. update state - view_.update(totalTime, timestep); - - nextUpdate += timestep; - ++i; - - if (!is_running_) break; - } - - if (nextDraw < (ticks = timer::ticks())) - { - view_.draw( - (ticks + timestep - nextUpdate) * inverseTimestep); - video_->swap(); // 4. draw state - - nextDraw += framerate; - ++frameCount; - - if (nextSecond < timer::ticks()) - { - fps_ = frameCount; - frameCount = 0; + //if (!is_running_) break; + + //int i = 0; + //while (nextUpdate < timer::ticks() && i < MAX_FRAMESKIP) + //{ + //totalTime += timestep; // 3. update state + //view_.update(totalTime, timestep); + + ////previous = current; + ////integrate(current, totalTime, timestep); + + //nextUpdate += timestep; + //++i; + + //if (!is_running_) break; + //} + + ////const float newTime = timer::ticks(); + ////float deltaTime = newTime - currentTime; + ////currentTime = newTime; + + ////if (deltaTime>0.25f) + ////deltaTime = 0.25f; + + ////accumulator += deltaTime; + + ////while (accumulator>=dt) + ////{ + ////accumulator -= dt; + ////previous = current; + ////integrate(current, t, dt); + ////t += dt; + ////} + + ////if (nextDraw < (ticks = timer::ticks())) + ////{ + //ticks = timer::ticks(); + //scalar diff = ticks - nextDraw; + ////log_info("difference:", diff); + //scalar alpha = (ticks + timestep - nextUpdate) * inverseTimestep; + ////scalar alpha = (nextUpdate - ticks) * inverseTimestep; + ////float alpha = accumulator/dt; + //if (alpha < SCALAR(0.0)) log_error("UH OH!!!!! It's NEGATIVE", alpha); + //if (alpha > SCALAR(1.0)) log_error("UH OH!!!!! It's POSITIVE", alpha); + //log_info("alpha:", alpha); + + //view_.draw(alpha); + + //// TEMP + ////State state = interpolate(previous, current, alpha); + ////glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + ////glBegin(GL_POINTS); + ////glColor3f(1,1,1); + ////glVertex3f(state.x, 0, 0); + ////glEnd(); + + + + //video_->swap(); // 4. draw state + + //nextDraw += framerate; + //++frameCount; + + //if (nextSecond < ticks)//timer::ticks()) + //{ + //fps_ = frameCount; + //frameCount = 0; + + //if (show_fps_) log_info << fps_ << " fps" << std::endl; + + //nextSecond += SCALAR(1.0); + //} + ////} - if (show_fps_) log_info << fps_ << " fps" << std::endl; + if (!is_running_) break; - nextSecond += SCALAR(1.0); - } - } + //ticks = timer::ticks(); // 5. yield timeslice + //scalar next = std::min(nextUpdate, nextDraw); + //next = std::min(next, timer::next_event()); + //if (ticks < next) timer::sleep(next, timer::absolute); - if (!is_running_) break; + timer::sleep(timer::next_event(), timer::absolute); - ticks = timer::ticks(); // 5. yield timeslice - if (ticks < nextUpdate && ticks < nextDraw) timer::sleep(0.0); + // Animation is choppy... the sound timer makes the draw occur + // late. It's not usually enough to make the FPS drop, but it + // certainly is noticeably choppy. Maybe update and draw + // should both be scheduled like timers. That should reduce + // the number of times either update or draw occur late. It + // doesn't really matter if update is late, but it's ugly if + // draw is late. } } -- 2.45.2