From 7e924d9c835a8b7736924ba729a5081ca94e6a8c Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Thu, 20 Oct 2011 17:42:29 +0300 Subject: [PATCH] Be ready if some clown decides to change core frequencies --- Makefile | 2 +- SDL/window-sdl.cpp | 30 +++++++++++++++++++------- avidump/avidump-control.cpp | 23 +++++++++++++------- avidump/cscd.cpp | 2 +- avidump/jmd-control.cpp | 10 +++++---- avidump/sox.cpp | 1 + dummy/window-dummy.cpp | 4 ++++ generic/avsnoop.cpp | 16 ++++++++++++++ generic/avsnoop.hpp | 17 +++++++++++++++ generic/framerate.cpp | 6 ++++-- generic/framerate.hpp | 11 +++++----- generic/mainloop.cpp | 42 ++++++++++++++----------------------- generic/misc.cpp | 7 +++++++ generic/misc.hpp | 2 ++ generic/moviefile.cpp | 4 +--- generic/rom.cpp | 9 ++++++++ generic/window.hpp | 8 +++++++ lsnes-dumpavi.cpp | 8 +++++-- 18 files changed, 141 insertions(+), 61 deletions(-) diff --git a/Makefile b/Makefile index c442abd0..7add476d 100644 --- a/Makefile +++ b/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 diff --git a/SDL/window-sdl.cpp b/SDL/window-sdl.cpp index e19a4f7b..35c1fade 100644 --- a/SDL/window-sdl.cpp +++ b/SDL/window-sdl.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); +} diff --git a/avidump/avidump-control.cpp b/avidump/avidump-control.cpp index 6053f47a..4d7e3b8d 100644 --- a/avidump/avidump-control.cpp +++ b/avidump/avidump-control.cpp @@ -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(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(&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 soundrate; + uint32_t audio_record_rate; }; avi_avsnoop* vid_dumper; diff --git a/avidump/cscd.cpp b/avidump/cscd.cpp index 992acb15..07815668 100644 --- a/avidump/cscd.cpp +++ b/avidump/cscd.cpp @@ -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(gp_sampling_rate) * f.fps_d % f.fps_n; unsigned long ret = static_cast(gp_sampling_rate) * f.fps_d / f.fps_n; - if(frame_period_counter % f.fps_n < critical) + if(static_cast(frame_period_counter) * critical % f.fps_n < critical) ret++; return ret; } diff --git a/avidump/jmd-control.cpp b/avidump/jmd-control.cpp index 2b11ece1..921d92a1 100644 --- a/avidump/jmd-control.cpp +++ b/avidump/jmd-control.cpp @@ -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 soundrate; }; jmd_avsnoop* vid_dumper; diff --git a/avidump/sox.cpp b/avidump/sox.cpp index 802a396b..6ac4f2d6 100644 --- a/avidump/sox.cpp +++ b/avidump/sox.cpp @@ -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; diff --git a/dummy/window-dummy.cpp b/dummy/window-dummy.cpp index d7ca9ea6..263d84ed 100644 --- a/dummy/window-dummy.cpp +++ b/dummy/window-dummy.cpp @@ -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) +{ +} diff --git a/generic/avsnoop.cpp b/generic/avsnoop.cpp index 6828e732..529a8fd1 100644 --- a/generic/avsnoop.cpp +++ b/generic/avsnoop.cpp @@ -11,6 +11,8 @@ namespace double s_gametime; std::list> 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 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()) diff --git a/generic/avsnoop.hpp b/generic/avsnoop.hpp index 579fdaa9..039a6caf 100644 --- a/generic/avsnoop.hpp +++ b/generic/avsnoop.hpp @@ -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 get_sound_rate(); + /** * Notify game information. * diff --git a/generic/framerate.cpp b/generic/framerate.cpp index 5e7697f5..867a028b 100644 --- a/generic/framerate.cpp +++ b/generic/framerate.cpp @@ -8,15 +8,17 @@ #include #include +#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; diff --git a/generic/framerate.hpp b/generic/framerate.hpp index 8442cb75..1fef5e04 100644 --- a/generic/framerate.hpp +++ b/generic/framerate.hpp @@ -4,13 +4,12 @@ #include /** - * 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 diff --git a/generic/mainloop.cpp b/generic/mainloop.cpp index 8ed017a6..3cd7a065 100644 --- a/generic/mainloop.cpp +++ b/generic/mainloop.cpp @@ -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) diff --git a/generic/misc.cpp b/generic/misc.cpp index 99aa65fb..a7e963fa 100644 --- a/generic/misc.cpp +++ b/generic/misc.cpp @@ -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"; diff --git a/generic/misc.hpp b/generic/misc.hpp index 5377b54e..4a93e21d 100644 --- a/generic/misc.hpp +++ b/generic/misc.hpp @@ -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(); /** diff --git a/generic/moviefile.cpp b/generic/moviefile.cpp index b5b2b11e..d5fb1e03 100644 --- a/generic/moviefile.cpp +++ b/generic/moviefile.cpp @@ -10,7 +10,6 @@ #include #include -#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) diff --git a/generic/rom.cpp b/generic/rom.cpp index 30d41672..485a1641 100644 --- a/generic/rom.cpp +++ b/generic/rom.cpp @@ -16,6 +16,9 @@ using SNES::cartridge; #include #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(); diff --git a/generic/window.hpp b/generic/window.hpp index 0dacebee..637df32c 100644 --- a/generic/window.hpp +++ b/generic/window.hpp @@ -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&); diff --git a/lsnes-dumpavi.cpp b/lsnes-dumpavi.cpp index 213f242a..2cc3e789 100644 --- a/lsnes-dumpavi.cpp +++ b/lsnes-dumpavi.cpp @@ -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();