Be ready if some clown decides to change core frequencies

This commit is contained in:
Ilari Liusvaara 2011-10-20 17:42:29 +03:00
parent 78ea0a9d88
commit 7e924d9c83
18 changed files with 141 additions and 61 deletions

View file

@ -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

View file

@ -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);
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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)
{
}

View file

@ -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())

View file

@ -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.
*

View file

@ -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;

View file

@ -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

View file

@ -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)

View file

@ -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";

View file

@ -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();
/**

View file

@ -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)

View file

@ -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();

View file

@ -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&);

View file

@ -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();