Be ready if some clown decides to change core frequencies
This commit is contained in:
parent
78ea0a9d88
commit
7e924d9c83
18 changed files with 141 additions and 61 deletions
2
Makefile
2
Makefile
|
@ -88,4 +88,4 @@ fonts/parsehexfont.$(EXECUTABLE_SUFFIX): fonts/parsehexfont.cpp
|
|||
$(HOSTCC) -std=gnu++0x $(HOSTCCFLAGS) -o $@ $^
|
||||
|
||||
clean:
|
||||
rm -f $(PROGRAMS) $(patsubst %.$(EXECUTABLE_SUFFIX),%.$(OBJECT_SUFFIX),$(PROGRAMS)) SDL/*.$(OBJECT_SUFFIX) avidump/*.$(OBJECT_SUFFIX) generic/*.$(OBJECT_SUFFIX) fonts/font.o fonts/font.cpp
|
||||
rm -f $(PROGRAMS) $(patsubst %.$(EXECUTABLE_SUFFIX),%.$(OBJECT_SUFFIX),$(PROGRAMS)) SDL/*.$(OBJECT_SUFFIX) avidump/*.$(OBJECT_SUFFIX) generic/*.$(OBJECT_SUFFIX) lua/*.$(OBJECT_SUFFIX) fonts/font.o fonts/font.cpp
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
uint32_t audio_playback_freq = 0;
|
||||
bool wait_canceled;
|
||||
SDL_TimerID tid;
|
||||
|
||||
|
@ -517,11 +518,18 @@ namespace
|
|||
bool stereo = true;
|
||||
bool sound_enabled = true;
|
||||
|
||||
void calculate_sampledup(uint32_t real_rate)
|
||||
void calculate_sampledup(uint32_t rate_n, uint32_t rate_d)
|
||||
{
|
||||
sampledup_ctr = 0;
|
||||
sampledup_inc = 64081;
|
||||
sampledup_mod = 2 * real_rate + 64081;
|
||||
if(!audio_playback_freq) {
|
||||
//Sound disabled.
|
||||
sampledup_ctr = 0;
|
||||
sampledup_inc = 0;
|
||||
sampledup_mod = 0;
|
||||
} else {
|
||||
sampledup_ctr = 0;
|
||||
sampledup_inc = rate_n;
|
||||
sampledup_mod = rate_d * audio_playback_freq + rate_n;
|
||||
}
|
||||
}
|
||||
|
||||
void audiocb(void* dummy, Uint8* stream, int len)
|
||||
|
@ -1117,14 +1125,14 @@ void window::init()
|
|||
if(SDL_OpenAudio(desired, obtained) < 0) {
|
||||
message("Audio can't be initialized, audio playback disabled");
|
||||
//Disable audio.
|
||||
sampledup_ctr = 0;
|
||||
sampledup_inc = 0;
|
||||
sampledup_mod = 0;
|
||||
audio_playback_freq = 0;
|
||||
calculate_sampledup(32000, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
//Fill the parameters.
|
||||
calculate_sampledup(obtained->freq);
|
||||
audio_playback_freq = obtained->freq;
|
||||
calculate_sampledup(32000, 1);
|
||||
format = obtained->format;
|
||||
stereo = (obtained->channels == 2);
|
||||
//GO!!!
|
||||
|
@ -1605,3 +1613,9 @@ void window::set_window_compensation(uint32_t xoffset, uint32_t yoffset, uint32_
|
|||
vc_hscl = hscl;
|
||||
vc_vscl = vscl;
|
||||
}
|
||||
|
||||
void window::set_sound_rate(uint32_t rate_n, uint32_t rate_d)
|
||||
{
|
||||
uint32_t g = gcd(rate_n, rate_d);
|
||||
calculate_sampledup(rate_n / g, rate_d / g);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,10 @@ namespace
|
|||
sp.deflate_level = parameters.compression_level;
|
||||
sp.max_segment_frames = parameters.max_frames_per_segment;
|
||||
vid_dumper = new avi_cscd_dumper(prefix, gp, sp);
|
||||
soxdumper = new sox_dumper(prefix + ".sox", 32040.5, 2);
|
||||
soundrate = av_snooper::get_sound_rate();
|
||||
audio_record_rate = parameters.audio_sampling_rate;
|
||||
soxdumper = new sox_dumper(prefix + ".sox", static_cast<double>(soundrate.first) /
|
||||
soundrate.second, 2);
|
||||
dcounter = 0;
|
||||
have_dumped_frame = false;
|
||||
}
|
||||
|
@ -89,9 +92,9 @@ namespace
|
|||
sp.fps_d = fps_d;
|
||||
uint32_t x = 0x18100800;
|
||||
if(*reinterpret_cast<const uint8_t*>(&x) == 0x18)
|
||||
sp.dataformat = avi_cscd_dumper::PIXFMT_XBGR;
|
||||
sp.dataformat = avi_cscd_dumper::PIXFMT_XRGB;
|
||||
else
|
||||
sp.dataformat = avi_cscd_dumper::PIXFMT_RGBX;
|
||||
sp.dataformat = avi_cscd_dumper::PIXFMT_BGRX;
|
||||
sp.width = lrc.left_gap + hscl * _frame.width + lrc.right_gap;
|
||||
sp.height = lrc.top_gap + vscl * _frame.height + lrc.bottom_gap;
|
||||
sp.default_stride = true;
|
||||
|
@ -111,13 +114,15 @@ namespace
|
|||
|
||||
void sample(short l, short r) throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
dcounter += 81;
|
||||
if(dcounter < 64081) {
|
||||
dcounter += soundrate.first;
|
||||
while(dcounter < soundrate.second * audio_record_rate + soundrate.first) {
|
||||
if(have_dumped_frame)
|
||||
vid_dumper->audio(&l, &r, 1, avi_cscd_dumper::SNDFMT_SIGNED_16NE);
|
||||
} else
|
||||
dcounter -= 64081;
|
||||
soxdumper->sample(l, r);
|
||||
dcounter += soundrate.first;
|
||||
}
|
||||
dcounter -= (soundrate.second * audio_record_rate + soundrate.first);
|
||||
if(have_dumped_frame)
|
||||
soxdumper->sample(l, r);
|
||||
}
|
||||
|
||||
void end() throw(std::bad_alloc, std::runtime_error)
|
||||
|
@ -139,6 +144,8 @@ namespace
|
|||
unsigned dcounter;
|
||||
struct avi_info _parameters;
|
||||
bool have_dumped_frame;
|
||||
std::pair<uint32_t, uint32_t> soundrate;
|
||||
uint32_t audio_record_rate;
|
||||
};
|
||||
|
||||
avi_avsnoop* vid_dumper;
|
||||
|
|
|
@ -1037,7 +1037,7 @@ size_t avi_cscd_dumper::samples_for_next_frame()
|
|||
struct buffered_frame& f = *frame_buffer.begin();
|
||||
unsigned long critical = static_cast<Uint64>(gp_sampling_rate) * f.fps_d % f.fps_n;
|
||||
unsigned long ret = static_cast<Uint64>(gp_sampling_rate) * f.fps_d / f.fps_n;
|
||||
if(frame_period_counter % f.fps_n < critical)
|
||||
if(static_cast<Uint64>(frame_period_counter) * critical % f.fps_n < critical)
|
||||
ret++;
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace
|
|||
video_w = 0;
|
||||
video_n = 0;
|
||||
maxtc = 0;
|
||||
soundrate = av_snooper::get_sound_rate();
|
||||
}
|
||||
|
||||
~jmd_avsnoop() throw()
|
||||
|
@ -89,10 +90,10 @@ namespace
|
|||
uint64_t get_next_audio_ts()
|
||||
{
|
||||
uint64_t ret = audio_w;
|
||||
audio_w += 31210;
|
||||
audio_n += 31990;
|
||||
if(audio_n >= 64081) {
|
||||
audio_n -= 64081;
|
||||
audio_w += (1000000000ULL * soundrate.second) / soundrate.first;
|
||||
audio_n += (1000000000ULL * soundrate.second) % soundrate.first;
|
||||
if(audio_n >= soundrate.first) {
|
||||
audio_n -= soundrate.first;
|
||||
audio_w++;
|
||||
}
|
||||
maxtc = (ret > maxtc) ? ret : maxtc;
|
||||
|
@ -108,6 +109,7 @@ namespace
|
|||
uint64_t video_w;
|
||||
uint64_t video_n;
|
||||
uint64_t maxtc;
|
||||
std::pair<uint32_t, uint32_t> soundrate;
|
||||
};
|
||||
|
||||
jmd_avsnoop* vid_dumper;
|
||||
|
|
|
@ -56,6 +56,7 @@ sox_dumper::sox_dumper(const std::string& filename, double samplerate, uint32_t
|
|||
throw std::runtime_error("Can't write audio header");
|
||||
samplebuffer.resize(channels);
|
||||
databuf.resize(channels << 2);
|
||||
samples_dumped = 0;
|
||||
} catch(...) {
|
||||
sox_file.close();
|
||||
throw;
|
||||
|
|
|
@ -49,3 +49,7 @@ uint64_t get_ticks_msec() throw()
|
|||
static uint64_t c = 0;
|
||||
return c++;
|
||||
}
|
||||
|
||||
void window::set_sound_rate(uint32_t rate_n, uint32_t rate_d)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ namespace
|
|||
double s_gametime;
|
||||
std::list<std::pair<std::string, std::string>> s_authors;
|
||||
bool gameinfo_set;
|
||||
uint32_t srate_n;
|
||||
uint32_t srate_d;
|
||||
}
|
||||
|
||||
av_snooper::av_snooper() throw(std::bad_alloc)
|
||||
|
@ -31,6 +33,20 @@ av_snooper::~av_snooper() throw()
|
|||
i->dump_ending();
|
||||
}
|
||||
|
||||
void av_snooper::set_sound_rate(uint32_t rate_n, uint32_t rate_d)
|
||||
{
|
||||
srate_n = rate_n;
|
||||
srate_d = rate_d;
|
||||
uint32_t g = gcd(srate_n, srate_d);
|
||||
srate_n /= g;
|
||||
srate_d /= g;
|
||||
}
|
||||
|
||||
std::pair<uint32_t, uint32_t> av_snooper::get_sound_rate()
|
||||
{
|
||||
return std::make_pair(srate_n, srate_d);
|
||||
}
|
||||
|
||||
void av_snooper::frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d, bool dummy) throw(std::bad_alloc)
|
||||
{
|
||||
for(auto i : snoopers())
|
||||
|
|
|
@ -75,6 +75,10 @@ public:
|
|||
*/
|
||||
virtual void end() throw(std::bad_alloc, std::runtime_error) = 0;
|
||||
|
||||
/**
|
||||
* Set sound rate.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Call end() on all known A/V snoopers.
|
||||
*
|
||||
|
@ -82,6 +86,19 @@ public:
|
|||
*/
|
||||
static void end(bool dummy) throw(std::bad_alloc);
|
||||
|
||||
/**
|
||||
* Set sound sampling rate.
|
||||
*
|
||||
* parameter rate_n: Numerator of sampling rate.
|
||||
* parameter rate_d: Denomerator of sampling rate.
|
||||
*/
|
||||
static void set_sound_rate(uint32_t rate_n, uint32_t rate_d);
|
||||
|
||||
/**
|
||||
* Get sound sampling rate.
|
||||
*/
|
||||
std::pair<uint32_t, uint32_t> get_sound_rate();
|
||||
|
||||
/**
|
||||
* Notify game information.
|
||||
*
|
||||
|
|
|
@ -8,15 +8,17 @@
|
|||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define DEFAULT_NOMINAL_RATE 60
|
||||
|
||||
namespace
|
||||
{
|
||||
double nominal_rate = 60;
|
||||
double nominal_rate = DEFAULT_NOMINAL_RATE;
|
||||
double fps_value = 0;
|
||||
const double exp_factor = 0.97;
|
||||
uint64_t last_frame_usec = 0;
|
||||
bool last_frame_usec_valid = false;
|
||||
bool target_nominal = true;
|
||||
double target_fps = 60;
|
||||
double target_fps = DEFAULT_NOMINAL_RATE;
|
||||
bool target_infinite = false;
|
||||
uint64_t wait_duration = 0;
|
||||
|
||||
|
|
|
@ -4,13 +4,12 @@
|
|||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* Nominal framerate of NTSC SNES.
|
||||
* Number clocks per field/frame on NTSC/PAL
|
||||
*/
|
||||
#define FRAMERATE_SNES_NTSC (10738636.0/178683.0)
|
||||
/**
|
||||
* Nominal framerate of PAL SNES.
|
||||
*/
|
||||
#define FRAMERATE_SNES_PAL (322445.0/6448.0)
|
||||
#define DURATION_NTSC_FRAME 357366
|
||||
#define DURATION_NTSC_FIELD 357368
|
||||
#define DURATION_PAL_FRAME 425568
|
||||
#define DURATION_PAL_FIELD 425568
|
||||
|
||||
/**
|
||||
* Sets the nominal frame rate. Framerate limiting tries to maintain the nominal framerate when there is no other
|
||||
|
|
|
@ -33,12 +33,6 @@
|
|||
#define SPECIAL_SAVEPOINT 2
|
||||
#define SPECIAL_NONE 3
|
||||
|
||||
#define RTC_SUBSECOND_INCREMENT_PAL 69242724928ULL
|
||||
#define RTC_SUBSECOND_INCREMENT_NTSC 57615439935ULL
|
||||
#define RTC_SUBSECOND_INCREMENT_NTSC_I 57615762380ULL
|
||||
#define RTC_SUBSECONDS_PER_SECOND 3462619485020ULL
|
||||
|
||||
|
||||
void update_movie_state();
|
||||
|
||||
namespace
|
||||
|
@ -182,8 +176,6 @@ controls_t movie_logic::update_controls(bool subframe) throw(std::bad_alloc, std
|
|||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
//Do pending load (automatically unpauses).
|
||||
void mark_pending_load(const std::string& filename, int lmode)
|
||||
{
|
||||
|
@ -382,24 +374,16 @@ class my_interface : public SNES::Interface
|
|||
update_movie_state();
|
||||
redraw_framebuffer();
|
||||
uint32_t fps_n, fps_d;
|
||||
if(region) {
|
||||
fps_n = 322445;
|
||||
fps_d = 6448;
|
||||
our_movie.rtc_subsecond += RTC_SUBSECOND_INCREMENT_PAL;
|
||||
} else if(!interlace) {
|
||||
fps_n = 10738636;
|
||||
fps_d = 178683;
|
||||
our_movie.rtc_subsecond += RTC_SUBSECOND_INCREMENT_NTSC;
|
||||
} else {
|
||||
//Yes, interlace makes difference with NTSC but not on PAL.
|
||||
fps_n = 2684659;
|
||||
fps_d = 44671;
|
||||
our_movie.rtc_subsecond += RTC_SUBSECOND_INCREMENT_NTSC_I;
|
||||
}
|
||||
if(our_movie.rtc_subsecond >= RTC_SUBSECONDS_PER_SECOND) {
|
||||
our_movie.rtc_second++;
|
||||
our_movie.rtc_subsecond -= RTC_SUBSECONDS_PER_SECOND;
|
||||
}
|
||||
uint32_t fclocks;
|
||||
if(region)
|
||||
fclocks = interlace ? DURATION_PAL_FIELD : DURATION_PAL_FRAME;
|
||||
else
|
||||
fclocks = interlace ? DURATION_NTSC_FIELD : DURATION_NTSC_FRAME;
|
||||
fps_n = SNES::system.cpu_frequency();
|
||||
fps_d = fclocks;
|
||||
uint32_t g = gcd(fps_n, fps_d);
|
||||
fps_n /= g;
|
||||
fps_d /= g;
|
||||
av_snooper::frame(ls, fps_n, fps_d, true);
|
||||
}
|
||||
|
||||
|
@ -409,6 +393,12 @@ class my_interface : public SNES::Interface
|
|||
uint16_t _r = r_sample;
|
||||
window::play_audio_sample(_l + 32768, _r + 32768);
|
||||
av_snooper::sample(_l, _r, true);
|
||||
//The SMP emits a sample every 768 ticks of its clock. Use this in order to keep track of time.
|
||||
our_movie.rtc_subsecond += 768;
|
||||
while(our_movie.rtc_subsecond >= SNES::system.apu_frequency()) {
|
||||
our_movie.rtc_second++;
|
||||
our_movie.rtc_subsecond -= SNES::system.apu_frequency();
|
||||
}
|
||||
}
|
||||
|
||||
int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id)
|
||||
|
|
|
@ -295,6 +295,13 @@ std::ostream& _messages()
|
|||
return window::out();
|
||||
}
|
||||
|
||||
uint32_t gcd(uint32_t a, uint32_t b) throw()
|
||||
{
|
||||
if(b == 0)
|
||||
return a;
|
||||
else
|
||||
return gcd(b, a % b);
|
||||
}
|
||||
|
||||
std::string bsnes_core_version;
|
||||
std::string lsnes_version = "0-β16";
|
||||
|
|
|
@ -108,6 +108,8 @@ template<> inline std::string parse_value(const std::string& value) throw(std::b
|
|||
return value;
|
||||
}
|
||||
|
||||
uint32_t gcd(uint32_t a, uint32_t b) throw();
|
||||
|
||||
void create_lsnesrc();
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <boost/iostreams/copy.hpp>
|
||||
#include <boost/iostreams/device/back_inserter.hpp>
|
||||
|
||||
#define MAX_RTC_SUBSECOND 3462619485019ULL
|
||||
#define DEFAULT_RTC_SECOND 1000000000ULL
|
||||
#define DEFAULT_RTC_SUBSECOND 0ULL
|
||||
|
||||
|
@ -291,8 +290,7 @@ moviefile::moviefile(const std::string& movie) throw(std::bad_alloc, std::runtim
|
|||
read_numeric_file(r, "savetime.second", rtc_second, true);
|
||||
read_numeric_file(r, "savetime.subsecond", rtc_subsecond, true);
|
||||
}
|
||||
if(rtc_subsecond < 0 || rtc_subsecond > MAX_RTC_SUBSECOND || movie_rtc_subsecond < 0 ||
|
||||
movie_rtc_subsecond >= MAX_RTC_SUBSECOND)
|
||||
if(rtc_subsecond < 0 || movie_rtc_subsecond < 0)
|
||||
throw std::runtime_error("Invalid RTC subsecond value");
|
||||
std::string name = r.find_first();
|
||||
for(auto name : r)
|
||||
|
|
|
@ -16,6 +16,9 @@ using SNES::cartridge;
|
|||
#include <boost/iostreams/device/back_inserter.hpp>
|
||||
#include "rom.hpp"
|
||||
#include "command.hpp"
|
||||
#include "framerate.hpp"
|
||||
#include "window.hpp"
|
||||
#include "avsnoop.hpp"
|
||||
#include "zip.hpp"
|
||||
#include "misc.hpp"
|
||||
#include "memorymanip.hpp"
|
||||
|
@ -500,6 +503,12 @@ void loaded_rom::load() throw(std::bad_alloc, std::runtime_error)
|
|||
if(region == REGION_AUTO)
|
||||
region = snes_get_region() ? REGION_PAL : REGION_NTSC;
|
||||
snes_power();
|
||||
if(region == REGION_PAL)
|
||||
set_nominal_framerate(SNES::system.cpu_frequency() / DURATION_PAL_FRAME);
|
||||
else
|
||||
set_nominal_framerate(SNES::system.cpu_frequency() / DURATION_NTSC_FRAME);
|
||||
window::set_sound_rate(SNES::system.apu_frequency(), 768);
|
||||
av_snooper::set_sound_rate(SNES::system.apu_frequency(), 768);
|
||||
current_rom_type = rtype;
|
||||
current_region = region;
|
||||
refresh_cart_mappings();
|
||||
|
|
|
@ -168,6 +168,14 @@ public:
|
|||
* parameter vscl: Vertical scaling factor.
|
||||
*/
|
||||
static void set_window_compensation(uint32_t xoffset, uint32_t yoffset, uint32_t hscl, uint32_t vscl);
|
||||
|
||||
/**
|
||||
* Set sound sampling rate.
|
||||
*
|
||||
* parameter rate_n: Numerator of sampling rate.
|
||||
* parameter rate_d: Denomerator of sampling rate.
|
||||
*/
|
||||
static void set_sound_rate(uint32_t rate_n, uint32_t rate_d);
|
||||
private:
|
||||
window(const window&);
|
||||
window& operator==(const window&);
|
||||
|
|
|
@ -183,8 +183,6 @@ int main(int argc, char** argv)
|
|||
dump_region_map();
|
||||
messages << "--- End of Startup --- " << std::endl;
|
||||
|
||||
dumper_startup(cmdline);
|
||||
startup_lua_scripts(cmdline);
|
||||
|
||||
moviefile movie;
|
||||
try {
|
||||
|
@ -206,6 +204,12 @@ int main(int argc, char** argv)
|
|||
throw std::runtime_error("Specifying movie is required");
|
||||
if(!loaded)
|
||||
throw std::runtime_error("Can't load any of the movies specified");
|
||||
//Load ROM before starting the dumper.
|
||||
our_rom = &r;
|
||||
our_rom->region = gtype::toromregion(movie.gametype);
|
||||
our_rom->load();
|
||||
dumper_startup(cmdline);
|
||||
startup_lua_scripts(cmdline);
|
||||
main_loop(r, movie, true);
|
||||
} catch(std::bad_alloc& e) {
|
||||
OOM_panic();
|
||||
|
|
Loading…
Add table
Reference in a new issue