do
local mysound = yoink.sound("Explosion")
+ --local mysound = yoink.sound()
+ --mysound:sample("Explosion")
local count = 0
function Event.Think()
if count % 300 == 0 then
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
-- 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
// 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_;
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");
#if !defined(__WIN32)
#include <termios.h>
#else
-int isatty(int dummy) { return 0; }
+inline int isatty(int dummy) { return 0; }
#endif
#include <stlplus/portability/file_system.hpp>
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);
int main(int argc, char* argv[])
{
- moof::backend backend;
+ //moof::backend backend;
if (argc > 1)
{
#if !defined(__WIN32)
#include <termios.h>
#else
-int isatty(int dummy) { return 0; }
+inline int isatty(int dummy) { return 0; }
#endif
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;
}
}
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];
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));
}
error = 1, ///< Log only errors.
warning = 2, ///< Log warnings and errors.
info = 3, ///< Log everything.
+ debug = 4, ///< Log absolutely everything.
};
extern class log log_error;
extern class log log_warning;
extern class log log_info;
+extern class log log_debug;
template <class T>
obj = obj->parent.lock();
}
}
- else
- {
- log_warning("UNKNOWN ATOM:", atom);
- }
}
while (stream);
-
- std::vector<object_ptr>::iterator meh;
- for (meh = objects_.begin(); meh != objects_.end(); ++meh)
- {
- object_ptr cow = *meh;
- log_info("OBJ: -", cow->name, cow->kids.size());
-
- std::vector<object_ptr>::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)
// 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;
}
*
**************************************************************************/
-#include <cstdio>
#include <deque>
-#include <list>
#include <stdexcept>
#include <boost/algorithm/string.hpp>
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
-#include "hash.hh"
#include "log.hh"
#include "sound.hh"
#include "resource.hh"
#ifndef BUF_SIZE
-#define BUF_SIZE (4096 * 64)
+#define BUF_SIZE (4096)
#endif
-#define NUM_BUFFERS (4)
+#define NUM_SAMPLE_BITS (16)
namespace moof {
MOOF_REGISTER_RESOURCE(sound_resource, ogg, sounds);
-/*] 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;
- }
-
- 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
*************************************************************************/
{
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)
{
~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))
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
{
}
}
- 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;
}
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_;
};
impl(const std::string& name)
{
init();
- enqueue(name);
+ sample(name);
}
void init()
is_looping_ = false;
sample_ = 0;
+ buffer_size_ = SCALAR(1.0);
alGenSources(1, &source_);
~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;
}
stream_timer_.invalidate();
- // TODO: clear buffers if streaming
+ sample_ = 0;
}
void pause()
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;
}
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();
+ //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
}
bool is_playing_;
bool is_looping_;
+ bool is_streaming_;
std::deque<sound_handle> queue_;
uint64_t sample_;
+ int num_buffers_;
+ scalar buffer_size_;
timer stream_timer_;
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()
impl_->pause();
}
-void sound::rewind()
-{
- // pass through
- impl_->rewind();
-}
-
-
void sound::toggle()
{
if (is_playing()) pause();
return impl_->is_playing();
}
+void sound::buffer_size(scalar seconds)
+{
+ impl_->buffer_size_ = seconds;
+}
+
void sound::position(const vector3& position)
{
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);
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<moof::sound>("sound").get(sound);
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<moof::sound>("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<moof::sound>("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<moof::sound>("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<moof::sound>("sound").get(sound);
- sound->rewind();
+ sound->pause();
return 0;
}
script::slot parent = script.push_table(nspace);
script::slot meta = script.push_class<sound>(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);
namespace moof {
-scalar timer::next_expiration_ = std::numeric_limits<scalar>::max();
+scalar timer::next_event_ = std::numeric_limits<scalar>::max();
hash<unsigned,timer*,hash_function> timer::timers_;
id_ = new_identifier();
timers_.insert(std::pair<unsigned,timer*>(id_, this));
- if (absolute_ < next_expiration_) next_expiration_ = absolute_;
+ if (absolute_ < next_event_) next_event_ = absolute_;
}
}
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();
}
}
}
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
}
-scalar timer::find_next_expiration()
+scalar timer::find_next_event()
{
scalar next_fire = std::numeric_limits<scalar>::max();
return absolute_ - ticks();
}
+scalar timer::next_expiration() const
+{
+ return absolute_;
+}
+
bool timer::is_expired() const
{
return seconds_remaining() < 0.0;
void timer::fire_expired_timers(scalar t)
{
- if (next_expiration_ > t) return;
+ if (t < next_event_) return;
hash<unsigned,timer*,hash_function>::iterator it;
for (it = timers_.begin(); it.valid(); ++it)
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);
}
// 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
#include <boost/bind.hpp>
#include <boost/function.hpp>
+#include <boost/noncopyable.hpp>
#include <moof/hash.hh>
#include <moof/math.hh>
* 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:
*/
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
* 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_;
}
private:
static unsigned new_identifier();
- static scalar find_next_expiration();
+ static scalar find_next_event();
function function_;
mode mode_;
scalar interval_;
unsigned id_;
- static scalar next_expiration_;
+ static scalar next_event_;
static hash<unsigned,timer*,hash_function> timers_;
};
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)
* 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();
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;
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.
}
}