Fix zero luma corner case
[lsnes.git] / src / core / framerate.cpp
bloba2457f5ba8b70fd081cd1d9a73136d9ccfe5353c
1 #include "core/framerate.hpp"
2 #include "core/settings.hpp"
4 #include <cstdlib>
5 #include <string>
6 #include <sstream>
7 #include <iostream>
8 #include <stdexcept>
9 #include <sys/time.h>
10 #include <unistd.h>
12 #define DEFAULT_NOMINAL_RATE 60
14 namespace
16 double nominal_rate = DEFAULT_NOMINAL_RATE;
17 double fps_value = 0;
18 const double exp_factor = 0.97;
19 uint64_t last_frame_usec = 0;
20 bool last_frame_usec_valid = false;
21 bool target_nominal = true;
22 double target_fps = DEFAULT_NOMINAL_RATE;
23 bool target_infinite = false;
24 uint64_t wait_duration = 0;
26 struct setting_targetfps : public setting
28 setting_targetfps() throw(std::bad_alloc)
29 : setting("targetfps")
33 void blank() throw(std::bad_alloc, std::runtime_error)
35 target_nominal = true;
36 target_infinite = false;
37 target_fps = nominal_rate;
40 bool is_set() throw()
42 return !target_nominal;
45 virtual void set(const std::string& value) throw(std::bad_alloc, std::runtime_error)
47 double tmp;
48 const char* s;
49 char* e;
50 if(value == "infinite") {
51 target_infinite = true;
52 target_nominal = false;
53 return;
55 s = value.c_str();
56 tmp = strtod(s, &e);
57 if(*e)
58 throw std::runtime_error("Invalid frame rate");
59 if(tmp < 0.001)
60 throw std::runtime_error("Target frame rate must be at least 0.001fps");
61 target_fps = tmp;
62 target_infinite = false;
63 target_nominal = false;
66 virtual std::string get() throw(std::bad_alloc)
68 if(target_nominal)
69 return "";
70 else {
71 std::ostringstream o;
72 o << target_fps;
73 return o.str();
77 } targetfps;
80 void set_nominal_framerate(double fps) throw()
82 nominal_rate = fps;
83 if(target_nominal) {
84 target_fps = nominal_rate;
85 target_infinite = false;
89 double get_framerate() throw()
91 return fps_value;
94 void ack_frame_tick(uint64_t usec) throw()
96 if(!last_frame_usec_valid) {
97 last_frame_usec = usec;
98 last_frame_usec_valid = true;
99 return;
101 uint64_t frame_usec = usec - last_frame_usec;
102 fps_value = exp_factor * fps_value + (1 - exp_factor) * (1000000.0 / frame_usec);
103 last_frame_usec = usec;
106 uint64_t to_wait_frame(uint64_t usec) throw()
108 //Very simple algorithm. TODO: Make better one.
109 if(!last_frame_usec_valid || target_infinite)
110 return 0;
111 if(get_framerate() < target_fps && wait_duration > 0)
112 wait_duration -= 1000;
113 else if(get_framerate() > target_fps)
114 wait_duration += 1000;
115 return wait_duration;
119 uint64_t get_utime()
121 struct timeval tv;
122 gettimeofday(&tv, NULL);
123 return static_cast<uint64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
126 #define MAXSLEEP 500000
128 void wait_usec(uint64_t usec)
130 uint64_t sleep_end = get_utime() + usec;
131 while(1) {
132 uint64_t time_now = get_utime();
133 if(time_now >= sleep_end)
134 break;
135 if(sleep_end < time_now + MAXSLEEP)
136 usleep(sleep_end - time_now);
137 else
138 usleep(MAXSLEEP);