diff --git a/libcross2d b/libcross2d index c9bec0e0..0147b647 160000 --- a/libcross2d +++ b/libcross2d @@ -1 +1 @@ -Subproject commit c9bec0e0467795443d5e67c2d0e5ea7dcd2df65a +Subproject commit 0147b6472100ac0f2fd793a03df00f54b5bf7a62 diff --git a/pfbneo/sources/fbneo/retro_input_wrapper.h b/pfbneo/sources/fbneo/retro_input_wrapper.h index 916ada60..68cb39dd 100644 --- a/pfbneo/sources/fbneo/retro_input_wrapper.h +++ b/pfbneo/sources/fbneo/retro_input_wrapper.h @@ -30,7 +30,7 @@ void poll_cb(); #define RETRO_GAME_TYPE_NEOCD 13 #define HARDWARE_SEGA_SYSTEM32 (HARDWARE_PREFIX_SEGA | 0x000c0000) -#define HARDWARE_NVS (HARDWARE_PREFIX_NES | 0x00010000) +//#define HARDWARE_NVS (HARDWARE_PREFIX_NES | 0x00010000) #define RETRO_DEVICE_TYPE_SHIFT 8 #define RETRO_DEVICE_MASK ((1 << RETRO_DEVICE_TYPE_SHIFT) - 1) diff --git a/pfbneo/sources/uiEmu.cpp b/pfbneo/sources/uiEmu.cpp index fc5ac6f3..d01e2aed 100644 --- a/pfbneo/sources/uiEmu.cpp +++ b/pfbneo/sources/uiEmu.cpp @@ -205,9 +205,8 @@ int PFBAUiEmu::load(const ss_api::Game &game) { pBurnSoundOut = (INT16 *) malloc(audio->getSamplesSize()); } audio_sync = !bForce60Hz; - targetFps = (float) nBurnFPS / 100; - printf("PFBAUiEmu::load: FORCE_60HZ: %i, AUDIO_SYNC: %i, FPS: %f (BURNFPS: %f)\n", - bForce60Hz, audio_sync, (float) nBurnFPS / 100.0f, targetFps); + targetFps = (float) nBurnFPS / 100.0f; + printf("PFBAUiEmu::load: FORCE_60HZ: %i, AUDIO_SYNC: %i, FPS: %f\n", bForce60Hz, audio_sync, targetFps); /////////// // AUDIO ////////// @@ -375,8 +374,17 @@ void PFBAUiEmu::onUpdate() { frameskip = 0; } - if (audio && audio->isAvailable()) { - audio->play(pBurnSoundOut, audio->getSamples(), audio_sync); + if (audio) { +#if 0 + int queued = audio->getSampleBufferQueued(); + int capacity = audio->getSampleBufferCapacity(); + if (audio->getSamples() + queued > capacity) { + printf("WARNING: samples: %i, queued: %i, capacity: %i (fps: %f)\n", + audio->getSamples(), queued, capacity, targetFps); + } +#endif + audio->play(pBurnSoundOut, audio->getSamples(), + audio_sync ? Audio::SyncMode::LowLatency : Audio::SyncMode::None); } UiEmu::onUpdate(); diff --git a/pgen/sources/pgen_ui_emu.cpp b/pgen/sources/pgen_ui_emu.cpp index 3d5943b7..a9092a7c 100644 --- a/pgen/sources/pgen_ui_emu.cpp +++ b/pgen/sources/pgen_ui_emu.cpp @@ -79,8 +79,7 @@ int PGENUiEmu::load(const ss_api::Game &game) { // audio init targetFps = vdp_pal ? 50 : 60; - int samples = 48000 / (int) targetFps; - addAudio(48000, samples); + addAudio(48000, vdp_pal ? 966 : 801); getUi()->getUiProgressBox()->setProgress(1); getUi()->flip(); @@ -166,7 +165,16 @@ void PGENUiEmu::onUpdate() { // audio int samples = audio_update(sound_buffer); - getAudio()->play(sound_buffer, samples, vdp_pal); +#if 0 + int queued = audio->getSampleBufferQueued(); + int capacity = audio->getSampleBufferCapacity(); + if (samples + queued > capacity) { + printf("WARNING: samples: %i, queued: %i, capacity: %i (fps: %i)\n", + samples, queued, capacity, vdp_pal ? 50 : 60); + } +#endif + getAudio()->play(sound_buffer, samples, + vdp_pal ? Audio::SyncMode::Safe : Audio::SyncMode::None); } return UiEmu::onUpdate(); diff --git a/pnes/sources/uiEmu.cpp b/pnes/sources/uiEmu.cpp index 7b31a396..accf87c1 100644 --- a/pnes/sources/uiEmu.cpp +++ b/pnes/sources/uiEmu.cpp @@ -145,14 +145,19 @@ void audio_deinit() { } void audio_queue() { - if (uiEmu->getAudio()) { + Audio *audio = uiEmu->getAudio(); + if (audio) { #if 0 - printf("play: samples: %i, queued: %i, capacity: %i\n", - uiEmu->getAudio()->getSamples(), - uiEmu->getAudio()->getSampleBufferQueued(), - uiEmu->getAudio()->getSampleBufferCapacity()); + int samples = uiEmu->getAudio()->getSamples(); + int queued = audio->getSampleBufferQueued(); + int capacity = audio->getSampleBufferCapacity(); + if (samples + queued > capacity) { + printf("WARNING: samples: %i, queued: %i, capacity: %i (fps: %i)\n", + samples, queued, capacity, nst_pal() ? 50 : 60); + } #endif - uiEmu->getAudio()->play(audio_buffer, uiEmu->getAudio()->getSamples(), nst_pal()); + uiEmu->getAudio()->play(audio_buffer, uiEmu->getAudio()->getSamples(), + nst_pal() ? Audio::SyncMode::LowLatency : Audio::SyncMode::None); } } diff --git a/psnes/sources/config.cpp b/psnes/sources/config.cpp index 10fb0840..1e65ba67 100644 --- a/psnes/sources/config.cpp +++ b/psnes/sources/config.cpp @@ -9,10 +9,13 @@ using namespace c2d; using namespace c2dui; PSNESConfig::PSNESConfig(c2d::Io *io, int version) : Config(io, version) { - printf("PSNESConfig(%s, v%i)\n", getConfigPath().c_str(), version); - add(Option::Id::ROM_SHOW_FPS, "CHEATS", {"OFF", "ON"}, 1, + add(Option::Id::ROM_SHOW_FPS, "AUDIO_SYNC", {"OFF", "ON"}, 0, + Option::Id::ROM_AUDIO_SYNC, Option::Flags::BOOLEAN); + get(Option::Id::ROM_AUDIO_SYNC)->setInfo("ON: PERFECT AUDIO - OFF: MINOR AUDIO STUTTERING (FAVOR FPS)"); + + add(Option::Id::ROM_AUDIO_SYNC, "CHEATS", {"OFF", "ON"}, 1, Option::Id::ROM_PSNES_CHEATS, Option::Flags::BOOLEAN); add(Option::Id::ROM_PSNES_CHEATS, "BLOCk_INVALID_VRAM", {"OFF", "ON"}, 1, diff --git a/psnes/sources/uiEmu.cpp b/psnes/sources/uiEmu.cpp index 731a518b..9c65fa11 100644 --- a/psnes/sources/uiEmu.cpp +++ b/psnes/sources/uiEmu.cpp @@ -25,6 +25,8 @@ using namespace c2dui; static UiMain *m_ui; +static Option *optionAudioSync = nullptr; + static void *audio_buffer = nullptr; static const char *s9x_base_dir = nullptr; @@ -47,7 +49,7 @@ static const char dirNames[13][32] = { "" }; -static int make_snes9x_dirs(); +static int S9xCreateDirectory(); static void S9xEndScreenRefreshCallback(void *); @@ -84,7 +86,7 @@ int PSNESUiEmu::load(const ss_api::Game &game) { // audio Settings.SixteenBitSound = TRUE; Settings.Stereo = TRUE; - Settings.SoundSync = FALSE; + Settings.SoundSync = TRUE; Settings.SoundInputRate = 31950; Settings.SoundPlaybackRate = 48000; @@ -123,7 +125,7 @@ int PSNESUiEmu::load(const ss_api::Game &game) { CPU.Flags = 0; - make_snes9x_dirs(); + S9xCreateDirectory(); if (!Memory.Init() || !S9xInitAPU()) { Memory.Deinit(); @@ -182,16 +184,20 @@ int PSNESUiEmu::load(const ss_api::Game &game) { CPU.Flags = saved_flags; Settings.StopEmulation = FALSE; + // audio + int samples = Audio::toSamples((int) Settings.SoundPlaybackRate, + (float) Memory.ROMFramesPerSecond); + addAudio((int) Settings.SoundPlaybackRate, samples * 2); + audio_buffer = malloc(getAudio()->getSamplesSize() * getAudio()->getChannels() * 5); + optionAudioSync = ui->getConfig()->get(Option::Id::ROM_AUDIO_SYNC, true); + // video S9xGraphicsInit(); - S9xSetEndScreenRefreshCallback(S9xEndScreenRefreshCallback, nullptr); + S9xSetEndScreenRefreshCallback(S9xEndScreenRefreshCallback, getAudio()); + addVideo((uint8_t **) &GFX.Screen, (int *) &GFX.Pitch, {MAX_SNES_WIDTH, MAX_SNES_HEIGHT}); targetFps = (float) Memory.ROMFramesPerSecond; - // audio - addAudio((int) Settings.SoundPlaybackRate, targetFps < 60 ? 1916 : 1600); - audio_buffer = malloc(getAudio()->getSamplesSize() * getAudio()->getChannels() * 5); - ui->getUiProgressBox()->setProgress(1); ui->flip(); ui->delay(500); @@ -250,6 +256,7 @@ void PSNESUiEmu::onUpdate() { void PSNESUiEmu::pause() { S9xSetSoundMute(TRUE); + S9xClearSamples(); UiEmu::pause(); } @@ -259,41 +266,35 @@ void PSNESUiEmu::resume() { } /////////////////////////////////////////////////////////////////////////////// -// Functions called by Snes9x below +// Functions called by snes9x below /////////////////////////////////////////////////////////////////////////////// -static void S9xEndScreenRefreshCallback(void *) { +static void S9xEndScreenRefreshCallback(void *data) { if (Settings.Mute) { S9xClearSamples(); return; } + auto audio = (Audio *) data; int samples = S9xGetSampleCount(); S9xMixSamples((uint8 *) audio_buffer, samples); #if 0 - printf("play: samples: %i, queued: %i, capacity: %i (fps: %i)\n", - samples, - m_ui->getUiEmu()->getAudio()->getSampleBufferQueued(), - m_ui->getUiEmu()->getAudio()->getSampleBufferCapacity(), - Memory.ROMFramesPerSecond); + int queued = audio->getSampleBufferQueued(); + int capacity = audio->getSampleBufferCapacity(); + if (samples + queued > capacity) { + printf("WARNING: samples: %i, queued: %i, capacity: %i (fps: %i)\n", + samples, queued, capacity, Memory.ROMFramesPerSecond); + } #endif - m_ui->getUiEmu()->getAudio()->play(audio_buffer, samples >> 1, true); + Audio::SyncMode mode = Memory.ROMFramesPerSecond < 60 || optionAudioSync->getValueBool() ? + Audio::SyncMode::Safe : Audio::SyncMode::None; + audio->play(audio_buffer, samples >> 1, mode); } -/** - * Called just before Snes9x begins to render an SNES screen. - * Use this function if you should prepare before drawing, otherwise let it empty. - */ bool8 S9xInitUpdate() { return TRUE; } -/** - * Called once a complete SNES screen has been rendered into the GFX.Screen memory buffer, - * now is your chance to copy the SNES rendered screen to the host computer's screen memory. - * The problem is that you have to cope with different sized SNES rendered screens: - * 256*224, 256*239, 512*224, 512*239, 512*448 and 512*478. - */ bool8 S9xDeinitUpdate(int width, int height) { //printf("S9xDeinitUpdate(%i, %i\n", width, height); C2DUIVideo *video = m_ui->getUiEmu()->getVideo(); @@ -310,35 +311,12 @@ bool8 S9xDeinitUpdate(int width, int height) { return TRUE; } -/** - * Called at the end of screen refresh if GFX.DoInterlace && GFX.InterlaceFrame == 0 is true (?). - */ bool8 S9xContinueUpdate(int width, int height) { - //printf("S9xContinueUpdate\n"); S9xDeinitUpdate(width, height); return TRUE; } -/** - * Display port-specific usage information - */ -void S9xExtraUsage() { -} - -/** - * Parse port-specific arguments - */ -void S9xParseArg(char **argv, int &i, int argc) { -} - -/** - * Parse port-specific config - */ -void S9xParsePortConfig(ConfigFile &conf, int pass) { -} - -static int make_snes9x_dirs() { - +static int S9xCreateDirectory() { if (strlen(s9x_base_dir) + 1 + sizeof(dirNames[0]) > PATH_MAX + 1) return (-1); @@ -355,11 +333,7 @@ static int make_snes9x_dirs() { return (0); } -/** - * Called when Snes9x wants to know the directory dirtype. - */ const char *S9xGetDirectory(s9x_getdirtype dirtype) { - static char s[PATH_MAX + 1]; if (dirNames[dirtype][0]) @@ -398,13 +372,7 @@ const char *S9xGetDirectory(s9x_getdirtype dirtype) { return (s); } -/** - * When Snes9x needs to know the name of the cheat/IPS file and so on, this function is called. - * Check extension and dirtype, and return the appropriate filename. - * The current ports return the ROM file path with the given extension. - */ const char *S9xGetFilename(const char *ex, s9x_getdirtype dirtype) { - static char s[PATH_MAX + 1]; char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1]; @@ -414,13 +382,7 @@ const char *S9xGetFilename(const char *ex, s9x_getdirtype dirtype) { return (s); } -/** - * Almost the same as S9xGetFilename function, but used for saving SPC files etc. - * So you have to take care not to delete the previously saved file, by increasing - * the number of the filename; romname.000.spc, romname.001.spc, ... - */ const char *S9xGetFilenameInc(const char *ex, s9x_getdirtype dirtype) { - static char s[PATH_MAX + 1]; char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1]; @@ -439,12 +401,7 @@ const char *S9xGetFilenameInc(const char *ex, s9x_getdirtype dirtype) { } -/** - * Called when Snes9x wants to know the name of the ROM image. - * Typically, extract the filename from path and return it. - */ const char *S9xBasename(const char *f) { - const char *p; if ((p = strrchr(f, '/')) != NULL || (p = strrchr(f, '\\')) != NULL) @@ -453,104 +410,23 @@ const char *S9xBasename(const char *f) { return (f); } -/** - * If your port can match Snes9x's built-in LoadFreezeFile/SaveFreezeFile command - * (see controls.cpp), you may choose to use this function. Otherwise return NULL. - */ -const char *S9xChooseFilename(bool8 read_only) { - return nullptr; -} - -/** - * If your port can match Snes9x's built-in BeginRecordingMovie/LoadMovie command - * (see controls.cpp), you may choose to use this function. Otherwise return NULL. - */ -const char *S9xChooseMovieFilename(bool8 read_only) { - return nullptr; -} - -/** - * This function opens a freeze-game file. STREAM is defined as a gzFile if ZLIB is - * defined else it's defined as FILE *. The read_only parameter is set to true when - * reading a freeze-game file and false when writing a freeze-game file. - * Open the file filepath and return its pointer file. - */ bool8 S9xOpenSnapshotFile(const char *filename, bool8 read_only, STREAM*file) { - - if ((*file = OPEN_STREAM(filename, read_only ? "rb" : "wb"))) - return (TRUE); - - printf("S9xOpenSnapshotFile: %s (open failed, read only=%i)\n", filename, read_only); - + if ((*file = OPEN_STREAM(filename, read_only ? "rb" : "wb"))) return (TRUE); return (FALSE); } -/** - * This function closes the freeze-game file opened by S9xOpenSnapshotFile function. - */ void S9xCloseSnapshotFile(STREAM file) { - - printf("S9xCloseSnapshotFile\n"); CLOSE_STREAM(file); } -/** - * If Settings.AutoSaveDelay is not zero, Snes9x calls this function when the contents of - * the S-RAM has been changed. Typically, call Memory.SaveSRAM function from this function. - */ void S9xAutoSaveSRAM() { - Memory.SaveSRAM(S9xGetFilename(".srm", SRAM_DIR)); } -/** - * Called at the end of S9xMainLoop function, when emulating one frame has been done. - * You should adjust the frame rate in this function - */ -void S9xSyncSpeed() { -} - -/** - * Called by Snes9x to poll for buttons that should be polled. - */ -bool S9xPollButton(uint32 id, bool *pressed) { - return false; -} - -/** - * Called by Snes9x to poll for axises that should be polled. - */ -bool S9xPollAxis(uint32 id, int16 *value) { - return false; -} - -/** - * Called by Snes9x to poll for poiters that should be polled. - */ -bool S9xPollPointer(uint32 id, int16 *x, int16 *y) { - return false; -} - -/** - * Handle port-specific commands (?). - */ -void S9xHandlePortCommand(s9xcommand_t cmd, int16 data1, int16 data2) { -} - -/** - * Called when some fatal error situation arises or when the “q” debugger command is used. - */ void S9xExit() { m_ui->getUiEmu()->stop(); } -/** - * When Snes9x wants to display an error, information or warning message, it calls this function. - * Check in messages.h for the types and individual message numbers that Snes9x currently passes as parameters. - * The idea is display the message string so the user can see it, but you choose not to display anything at all, - * or change the message based on the message number or message type. - * Eventually all debug output will also go via this function, trace information already does. - */ void S9xMessage(int type, int number, const char *message) { const int max = 36 * 3; static char buffer[max + 1]; @@ -560,22 +436,30 @@ void S9xMessage(int type, int number, const char *message) { S9xSetInfoString(buffer); } -/** - * Used by Snes9x to ask the user for input. - */ -const char *S9xStringInput(const char *message) { - return nullptr; -} +const char *S9xStringInput(const char *message) { return nullptr; } -/** - * Called when the SNES color palette has changed. - * Use this function if your system should change its color palette to match the SNES's. - * Otherwise let it empty. - */ void S9xSetPalette() {} -bool8 S9xOpenSoundDevice() { - return TRUE; -} +bool8 S9xOpenSoundDevice() { return TRUE; } void S9xToggleSoundChannel(int c) {} + +void S9xHandlePortCommand(s9xcommand_t cmd, int16 data1, int16 data2) {} + +void S9xSyncSpeed() {} + +void S9xExtraUsage() {} + +void S9xParseArg(char **argv, int &i, int argc) {} + +void S9xParsePortConfig(ConfigFile &conf, int pass) {} + +bool S9xPollButton(uint32 id, bool *pressed) { return false; } + +bool S9xPollAxis(uint32 id, int16 *value) { return false; } + +bool S9xPollPointer(uint32 id, int16 *x, int16 *y) { return false; } + +const char *S9xChooseFilename(bool8 read_only) { return nullptr; } + +const char *S9xChooseMovieFilename(bool8 read_only) { return nullptr; } diff --git a/ui/c2dui_option.h b/ui/c2dui_option.h index b3900659..c0a01eef 100644 --- a/ui/c2dui_option.h +++ b/ui/c2dui_option.h @@ -54,6 +54,7 @@ namespace c2dui { ROM_WAIT_RENDERING, #endif ROM_FORCE_60HZ, + ROM_AUDIO_SYNC, ROM_AUDIO_FREQ, ROM_AUDIO_INTERPOLATION, ROM_AUDIO_FMINTERPOLATION, diff --git a/ui/c2dui_ui_emu.cpp b/ui/c2dui_ui_emu.cpp index 29150ff2..6deb91db 100644 --- a/ui/c2dui_ui_emu.cpp +++ b/ui/c2dui_ui_emu.cpp @@ -152,7 +152,7 @@ void UiEmu::onUpdate() { if (!fpsText->isVisible()) { fpsText->setVisibility(c2d::Visibility::Visible); } - sprintf(fpsString, "FPS: %.0f/%.0f", ui->getFps(), targetFps); + sprintf(fpsString, "FPS: %.1f/%.1f", ui->getFps(), targetFps); fpsText->setString(fpsString); } else { if (fpsText->isVisible()) { diff --git a/ui/c2dui_ui_emu.h b/ui/c2dui_ui_emu.h index 55d1277b..fc399aa1 100644 --- a/ui/c2dui_ui_emu.h +++ b/ui/c2dui_ui_emu.h @@ -59,7 +59,6 @@ namespace c2dui { char fpsString[32]; float targetFps = 60; bool paused = true; - float frameDuration = 0; }; }