diff --git a/source/filelist.h b/source/filelist.h index 4d4ea37..df68257 100644 --- a/source/filelist.h +++ b/source/filelist.h @@ -130,6 +130,8 @@ extern const u8 icon_settings_network_png[]; extern const u32 icon_settings_network_png_size; extern const u8 icon_settings_video_png[]; extern const u32 icon_settings_video_png_size; +extern const u8 icon_settings_audio_png[]; +extern const u32 icon_settings_audio_png_size; extern const u8 icon_settings_screenshot_png[]; extern const u32 icon_settings_screenshot_png_size; diff --git a/source/images/icon_settings_audio.png b/source/images/icon_settings_audio.png new file mode 100644 index 0000000..b3b5b0a Binary files /dev/null and b/source/images/icon_settings_audio.png differ diff --git a/source/menu.cpp b/source/menu.cpp index 10d0756..a4b4422 100644 --- a/source/menu.cpp +++ b/source/menu.cpp @@ -41,6 +41,7 @@ #include "snes9x/snes9x.h" #include "snes9x/fxemu.h" #include "snes9x/memmap.h" +#include "snes9x/apu/apu.h" #include "snes9x/cheats.h" extern SCheatData Cheat; @@ -1886,6 +1887,7 @@ static int MenuGameSettings() GuiImageData btnLargeOutlineOver(button_large_over_png); GuiImageData iconMappings(icon_settings_mappings_png); GuiImageData iconVideo(icon_settings_video_png); + GuiImageData iconAudio(icon_settings_audio_png); GuiImageData iconController(icon_game_controllers_png); GuiImageData iconCheats(icon_game_cheats_png); GuiImageData iconScreenshot(icon_settings_screenshot_png); @@ -1902,7 +1904,7 @@ static int MenuGameSettings() GuiImage mappingBtnIcon(&iconMappings); GuiButton mappingBtn(btnLargeOutline.GetWidth(), btnLargeOutline.GetHeight()); mappingBtn.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - mappingBtn.SetPosition(-125, 120); + mappingBtn.SetPosition(-200, 120); mappingBtn.SetLabel(&mappingBtnTxt); mappingBtn.SetImage(&mappingBtnImg); mappingBtn.SetImageOver(&mappingBtnImgOver); @@ -1912,7 +1914,25 @@ static int MenuGameSettings() mappingBtn.SetTrigger(trigA); mappingBtn.SetTrigger(trig2); mappingBtn.SetEffectGrow(); - + + GuiText audioBtnTxt("Audio", 22, (GXColor){0, 0, 0, 255}); + audioBtnTxt.SetWrap(true, btnLargeOutline.GetWidth()-20); + GuiImage audioBtnImg(&btnLargeOutline); + GuiImage audioBtnImgOver(&btnLargeOutlineOver); + GuiImage audioBtnIcon(&iconAudio); + GuiButton audioBtn(btnLargeOutline.GetWidth(), btnLargeOutline.GetHeight()); + audioBtn.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + audioBtn.SetPosition(0, 120); + audioBtn.SetLabel(&audioBtnTxt); + audioBtn.SetImage(&audioBtnImg); + audioBtn.SetImageOver(&audioBtnImgOver); + audioBtn.SetIcon(&audioBtnIcon); + audioBtn.SetSoundOver(&btnSoundOver); + audioBtn.SetSoundClick(&btnSoundClick); + audioBtn.SetTrigger(trigA); + audioBtn.SetTrigger(trig2); + audioBtn.SetEffectGrow(); + GuiText videoBtnTxt("Video", 22, (GXColor){0, 0, 0, 255}); videoBtnTxt.SetWrap(true, btnLargeOutline.GetWidth()-20); GuiImage videoBtnImg(&btnLargeOutline); @@ -1920,7 +1940,7 @@ static int MenuGameSettings() GuiImage videoBtnIcon(&iconVideo); GuiButton videoBtn(btnLargeOutline.GetWidth(), btnLargeOutline.GetHeight()); videoBtn.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - videoBtn.SetPosition(125, 120); + videoBtn.SetPosition(200, 120); videoBtn.SetLabel(&videoBtnTxt); videoBtn.SetImage(&videoBtnImg); videoBtn.SetImageOver(&videoBtnImgOver); @@ -2018,6 +2038,7 @@ static int MenuGameSettings() w.Append(&titleTxt); w.Append(&mappingBtn); w.Append(&videoBtn); + w.Append(&audioBtn); w.Append(&controllerBtn); w.Append(&screenshotBtn); w.Append(&cheatsBtn); @@ -2040,6 +2061,10 @@ static int MenuGameSettings() { menu = MENU_GAMESETTINGS_VIDEO; } + else if(audioBtn.GetState() == STATE_CLICKED) + { + menu = MENU_GAMESETTINGS_AUDIO; + } else if(controllerBtn.GetState() == STATE_CLICKED) { ControllerWindow(); @@ -3333,6 +3358,102 @@ static int MenuSettingsVideo() return menu; } +/**************************************************************************** + * MenuSettingsAudio + ***************************************************************************/ +static int MenuSettingsAudio() +{ + int menu = MENU_NONE; + int ret; + int i = 0; + bool firstRun = true; + OptionList options; + sprintf(options.name[i++], "Interpolation"); + options.length = i; + for(i=0; i < options.length; i++) + options.value[i][0] = 0; + GuiText titleTxt("Game Settings - Audio", 26, (GXColor){255, 255, 255, 255}); + titleTxt.SetAlignment(ALIGN_LEFT, ALIGN_TOP); + titleTxt.SetPosition(50,50); + GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, SOUND_PCM); + GuiSound btnSoundClick(button_click_pcm, button_click_pcm_size, SOUND_PCM); + GuiImageData btnOutline(button_png); + GuiImageData btnOutlineOver(button_over_png); + GuiText backBtnTxt("Go Back", 22, (GXColor){0, 0, 0, 255}); + GuiImage backBtnImg(&btnOutline); + GuiImage backBtnImgOver(&btnOutlineOver); + GuiButton backBtn(btnOutline.GetWidth(), btnOutline.GetHeight()); + backBtn.SetAlignment(ALIGN_LEFT, ALIGN_BOTTOM); + backBtn.SetPosition(50, -35); + backBtn.SetLabel(&backBtnTxt); + backBtn.SetImage(&backBtnImg); + backBtn.SetImageOver(&backBtnImgOver); + backBtn.SetSoundOver(&btnSoundOver); + backBtn.SetSoundClick(&btnSoundClick); + backBtn.SetTrigger(trigA); + backBtn.SetTrigger(trig2); + backBtn.SetEffectGrow(); + GuiOptionBrowser optionBrowser(552, 248, &options); + optionBrowser.SetPosition(0, 108); + optionBrowser.SetCol2Position(200); + optionBrowser.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + HaltGui(); + GuiWindow w(screenwidth, screenheight); + w.Append(&backBtn); + mainWindow->Append(&optionBrowser); + mainWindow->Append(&w); + mainWindow->Append(&titleTxt); + ResumeGui(); + + while(menu == MENU_NONE) + { + usleep(THREAD_SLEEP); + ret = optionBrowser.GetClickedOption(); + + switch (ret) + { + case 0: + GCSettings.Interpolation++; + if (GCSettings.Interpolation > 2) { + GCSettings.Interpolation = 0; + } + switch(GCSettings.Interpolation) + { + case 0: Settings.InterpolationMethod = DSP_INTERPOLATION_GAUSSIAN; break; + case 1: Settings.InterpolationMethod = DSP_INTERPOLATION_LINEAR; break; + case 2: Settings.InterpolationMethod = DSP_INTERPOLATION_NONE; break; + } + break; + S9xReset(); + } + + if(ret >= 0 || firstRun) + { + firstRun = false; + + switch(GCSettings.Interpolation) + { + case 0: + sprintf (options.value[0], "Gaussian (Accurate)"); break; + case 1: + sprintf (options.value[0], "Linear"); break; + case 2: + sprintf (options.value[0], "None"); break; + } + optionBrowser.TriggerUpdate(); + } + if(backBtn.GetState() == STATE_CLICKED) + { + menu = MENU_GAMESETTINGS; + } + } + HaltGui(); + mainWindow->Remove(&optionBrowser); + mainWindow->Remove(&w); + mainWindow->Remove(&titleTxt); + return menu; +} + /**************************************************************************** * MenuSettings ***************************************************************************/ @@ -4158,6 +4279,9 @@ MainMenu (int menu) case MENU_GAMESETTINGS_VIDEO: currentMenu = MenuSettingsVideo(); break; + case MENU_GAMESETTINGS_AUDIO: + currentMenu = MenuSettingsAudio(); + break; case MENU_GAMESETTINGS_CHEATS: currentMenu = MenuGameCheats(); break; diff --git a/source/menu.h b/source/menu.h index 169cfbb..af3a85e 100644 --- a/source/menu.h +++ b/source/menu.h @@ -41,6 +41,7 @@ enum MENU_GAMESETTINGS_MAPPINGS_CTRL, MENU_GAMESETTINGS_MAPPINGS_MAP, MENU_GAMESETTINGS_VIDEO, + MENU_GAMESETTINGS_AUDIO, MENU_GAMESETTINGS_CHEATS }; diff --git a/source/preferences.cpp b/source/preferences.cpp index 799568b..cd416aa 100644 --- a/source/preferences.cpp +++ b/source/preferences.cpp @@ -23,6 +23,8 @@ #include "input.h" #include "button_mapping.h" +#include "snes9x/apu/apu.h" + struct SGCSettings GCSettings; /**************************************************************************** @@ -151,6 +153,7 @@ preparePrefsData () createXMLSetting("xshift", "Horizontal Video Shift", toStr(GCSettings.xshift)); createXMLSetting("yshift", "Vertical Video Shift", toStr(GCSettings.yshift)); createXMLSetting("sfxOverclock", "SuperFX Overclock", toStr(GCSettings.sfxOverclock)); + createXMLSetting("Interpolation", "Interpolation", toStr(GCSettings.Interpolation)); createXMLSection("Menu", "Menu Settings"); @@ -334,8 +337,12 @@ decodePrefsData () loadXMLSetting(&GCSettings.FilterMethod, "FilterMethod"); loadXMLSetting(&GCSettings.xshift, "xshift"); loadXMLSetting(&GCSettings.yshift, "yshift"); + + // Audio Settings + + loadXMLSetting(&GCSettings.Interpolation, "Interpolation"); - //Emulation Settings + // Emulation Settings loadXMLSetting(&GCSettings.sfxOverclock, "sfxOverclock"); @@ -488,6 +495,10 @@ DefaultSettings () Settings.SoundPlaybackRate = 48000; Settings.SoundInputRate = 31950; Settings.DynamicRateControl = true; + + // Interpolation Method + GCSettings.Interpolation = 0; + Settings.InterpolationMethod = DSP_INTERPOLATION_GAUSSIAN; // Graphics Settings.Transparency = true; diff --git a/source/snes9x/apu/SPC_DSP.cpp b/source/snes9x/apu/SPC_DSP.cpp index 8fad2f5..91599c8 100644 --- a/source/snes9x/apu/SPC_DSP.cpp +++ b/source/snes9x/apu/SPC_DSP.cpp @@ -1,5 +1,7 @@ // snes_spc 0.9.0. http://www.slack.net/~ant/ +#include "snes9x.h" + #include "SPC_DSP.h" #include "blargg_endian.h" @@ -127,25 +129,44 @@ static short const gauss [512] = inline int SPC_DSP::interpolate( voice_t const* v ) { - // Make pointers into gaussian based on fractional position between samples - int offset = v->interp_pos >> 4 & 0xFF; - short const* fwd = gauss + 255 - offset; - short const* rev = gauss + offset; // mirror left half of gaussian - - int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; - int out; - out = (fwd [ 0] * in [0]) >> 11; - out += (fwd [256] * in [1]) >> 11; - out += (rev [256] * in [2]) >> 11; - out = (int16_t) out; - out += (rev [ 0] * in [3]) >> 11; - - CLAMP16( out ); - out &= ~1; - return out; + int out; + int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; + switch (Settings.InterpolationMethod) + { + case 0: // raw + { + out = v->buf [(v->interp_pos >> 12) + v->buf_pos] & ~1; + break; + } + case 1: // linear interpolation + { + int fract = v->interp_pos & 0xFFF; + out = (0x1000 - fract) * in [0]; + out += fract * in [1]; + out >>= 12; + break; + } + default: + case 2: // Original gaussian filter + { + // Make pointers into gaussian based on fractional position between samples + int offset = v->interp_pos >> 4 & 0xFF; + short const* fwd = gauss + 255 - offset; + short const* rev = gauss + offset; // mirror left half of gaussian + int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; + out = (fwd [ 0] * in [0]) >> 11; + out += (fwd [256] * in [1]) >> 11; + out += (rev [256] * in [2]) >> 11; + out = (int16_t) out; + out += (rev [ 0] * in [3]) >> 11; + CLAMP16( out ); + out &= ~1; + break; + } + } + return out; } - //// Counters int const simple_counter_range = 2048 * 5 * 3; // 30720 diff --git a/source/snes9x/apu/apu.h b/source/snes9x/apu/apu.h index c90ab2b..91722d1 100644 --- a/source/snes9x/apu/apu.h +++ b/source/snes9x/apu/apu.h @@ -47,4 +47,8 @@ void S9xUpdateDynamicRate (double rate); extern SNES_SPC *spc_core; +#define DSP_INTERPOLATION_NONE 0 +#define DSP_INTERPOLATION_LINEAR 1 +#define DSP_INTERPOLATION_GAUSSIAN 2 + #endif diff --git a/source/snes9x/snes9x.h b/source/snes9x/snes9x.h index e965cfe..5756c71 100644 --- a/source/snes9x/snes9x.h +++ b/source/snes9x/snes9x.h @@ -242,6 +242,7 @@ struct SSettings bool8 ReverseStereo; bool8 Mute; bool8 DynamicRateControl; + int32 InterpolationMethod; bool8 SupportHiRes; bool8 Transparency; diff --git a/source/snes9xgx.cpp b/source/snes9xgx.cpp index a83cebc..9dee98b 100644 --- a/source/snes9xgx.cpp +++ b/source/snes9xgx.cpp @@ -485,6 +485,13 @@ int main(int argc, char *argv[]) case 2: Settings.SuperFXSpeedPerLine = 0.417 * 60.5e6; break; S9xResetSuperFX(); } + + switch (GCSettings.Interpolation) + { + case 0: Settings.InterpolationMethod = DSP_INTERPOLATION_GAUSSIAN; break; + case 1: Settings.InterpolationMethod = DSP_INTERPOLATION_LINEAR; break; + case 2: Settings.InterpolationMethod = DSP_INTERPOLATION_NONE; break; + } while (1) // main loop { diff --git a/source/snes9xgx.h b/source/snes9xgx.h index 469e0d8..9d56c7f 100644 --- a/source/snes9xgx.h +++ b/source/snes9xgx.h @@ -119,6 +119,8 @@ struct SGCSettings{ int PreviewImage; int sfxOverclock; + + int Interpolation; }; void ExitApp();