mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-12-23 16:21:53 +01:00
Play bootSound.btsnd while shaders/pipelines are compiling (#1047)
This commit is contained in:
parent
b53b223ba9
commit
3738ccd2e6
@ -25,6 +25,9 @@
|
||||
#include "util/helpers/Serializer.h"
|
||||
|
||||
#include <wx/msgdlg.h>
|
||||
#include <audio/IAudioAPI.h>
|
||||
#include <util/bootSound/BootSoundReader.h>
|
||||
#include <thread>
|
||||
|
||||
#if BOOST_OS_WINDOWS
|
||||
#include <psapi.h>
|
||||
@ -155,6 +158,118 @@ bool LoadTGAFile(const std::vector<uint8>& buffer, TGAFILE *tgaFile)
|
||||
return true;
|
||||
}
|
||||
|
||||
class BootSoundPlayer
|
||||
{
|
||||
public:
|
||||
BootSoundPlayer() = default;
|
||||
~BootSoundPlayer()
|
||||
{
|
||||
m_stopRequested = true;
|
||||
}
|
||||
|
||||
void StartSound()
|
||||
{
|
||||
if (!m_bootSndPlayThread.joinable())
|
||||
{
|
||||
m_fadeOutRequested = false;
|
||||
m_stopRequested = false;
|
||||
m_bootSndPlayThread = std::thread{[this]() {
|
||||
StreamBootSound();
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
void FadeOutSound()
|
||||
{
|
||||
m_fadeOutRequested = true;
|
||||
}
|
||||
|
||||
void ApplyFadeOutEffect(std::span<sint16> samples, uint64& fadeOutSample, uint64 fadeOutDuration)
|
||||
{
|
||||
for (size_t i = 0; i < samples.size(); i += 2)
|
||||
{
|
||||
const float decibel = (float)fadeOutSample / fadeOutDuration * -60.0f;
|
||||
const float volumeFactor = pow(10, decibel / 20);
|
||||
samples[i] *= volumeFactor;
|
||||
samples[i + 1] *= volumeFactor;
|
||||
fadeOutSample++;
|
||||
}
|
||||
}
|
||||
|
||||
void StreamBootSound()
|
||||
{
|
||||
SetThreadName("bootsnd");
|
||||
constexpr sint32 sampleRate = 48'000;
|
||||
constexpr sint32 bitsPerSample = 16;
|
||||
constexpr sint32 samplesPerBlock = sampleRate / 10; // block is 1/10th of a second
|
||||
constexpr sint32 nChannels = 2;
|
||||
static_assert(bitsPerSample % 8 == 0, "bits per sample is not a multiple of 8");
|
||||
|
||||
AudioAPIPtr bootSndAudioDev;
|
||||
|
||||
try
|
||||
{
|
||||
bootSndAudioDev = IAudioAPI::CreateDeviceFromConfig(true, sampleRate, nChannels, samplesPerBlock, bitsPerSample);
|
||||
if(!bootSndAudioDev)
|
||||
return;
|
||||
}
|
||||
catch (const std::runtime_error& ex)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Failed to initialise audio device for bootup sound");
|
||||
return;
|
||||
}
|
||||
bootSndAudioDev->SetAudioDelayOverride(4);
|
||||
bootSndAudioDev->Play();
|
||||
|
||||
std::string sndPath = fmt::format("{}/meta/{}", CafeSystem::GetMlcStoragePath(CafeSystem::GetForegroundTitleId()), "bootSound.btsnd");
|
||||
sint32 fscStatus = FSC_STATUS_UNDEFINED;
|
||||
|
||||
if(!fsc_doesFileExist(sndPath.c_str()))
|
||||
return;
|
||||
|
||||
FSCVirtualFile* bootSndFileHandle = fsc_open(sndPath.c_str(), FSC_ACCESS_FLAG::OPEN_FILE | FSC_ACCESS_FLAG::READ_PERMISSION, &fscStatus);
|
||||
if(!bootSndFileHandle)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "failed to open bootSound.btsnd");
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr sint32 audioBlockSize = samplesPerBlock * (bitsPerSample/8) * nChannels;
|
||||
BootSoundReader bootSndFileReader(bootSndFileHandle, audioBlockSize);
|
||||
|
||||
uint64 fadeOutSample = 0; // track how far into the fadeout
|
||||
constexpr uint64 fadeOutDuration = sampleRate * 2; // fadeout should last 2 seconds
|
||||
while(fadeOutSample < fadeOutDuration && !m_stopRequested)
|
||||
{
|
||||
while (bootSndAudioDev->NeedAdditionalBlocks())
|
||||
{
|
||||
sint16* data = bootSndFileReader.getSamples();
|
||||
if(data == nullptr)
|
||||
{
|
||||
// break outer loop
|
||||
m_stopRequested = true;
|
||||
break;
|
||||
}
|
||||
if(m_fadeOutRequested)
|
||||
ApplyFadeOutEffect({data, samplesPerBlock * nChannels}, fadeOutSample, fadeOutDuration);
|
||||
|
||||
bootSndAudioDev->FeedBlock(data);
|
||||
}
|
||||
// sleep for the duration of a single block
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(samplesPerBlock / (sampleRate/ 1'000)));
|
||||
}
|
||||
|
||||
if(bootSndFileHandle)
|
||||
fsc_close(bootSndFileHandle);
|
||||
}
|
||||
|
||||
private:
|
||||
std::thread m_bootSndPlayThread;
|
||||
std::atomic_bool m_fadeOutRequested = false;
|
||||
std::atomic_bool m_stopRequested = false;
|
||||
};
|
||||
static BootSoundPlayer g_bootSndPlayer;
|
||||
|
||||
void LatteShaderCache_finish()
|
||||
{
|
||||
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
@ -299,6 +414,9 @@ void LatteShaderCache_Load()
|
||||
loadBackgroundTexture(true, g_shaderCacheLoaderState.textureTVId);
|
||||
loadBackgroundTexture(false, g_shaderCacheLoaderState.textureDRCId);
|
||||
|
||||
if(GetConfig().play_boot_sound)
|
||||
g_bootSndPlayer.StartSound();
|
||||
|
||||
sint32 numLoadedShaders = 0;
|
||||
uint32 loadIndex = 0;
|
||||
|
||||
@ -365,6 +483,8 @@ void LatteShaderCache_Load()
|
||||
g_renderer->DeleteTexture(g_shaderCacheLoaderState.textureTVId);
|
||||
if (g_shaderCacheLoaderState.textureDRCId)
|
||||
g_renderer->DeleteTexture(g_shaderCacheLoaderState.textureDRCId);
|
||||
|
||||
g_bootSndPlayer.FadeOutSound();
|
||||
}
|
||||
|
||||
void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateFunc, bool isPipelines)
|
||||
@ -805,4 +925,4 @@ void LatteShaderCache_handleDeprecatedCacheFiles(fs::path pathGeneric, fs::path
|
||||
fs::remove(pathGenericPre1_25_0, ec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -396,90 +396,35 @@ namespace snd_core
|
||||
|
||||
void AXOut_init()
|
||||
{
|
||||
auto& config = GetConfig();
|
||||
const auto audio_api = (IAudioAPI::AudioAPI)config.audio_api;
|
||||
|
||||
numQueuedFramesSndGeneric = 0;
|
||||
|
||||
std::unique_lock lock(g_audioMutex);
|
||||
if (!g_tvAudio)
|
||||
{
|
||||
sint32 channels;
|
||||
switch (config.tv_channels)
|
||||
try
|
||||
{
|
||||
case 0:
|
||||
channels = 1; // will mix mono sound on both output channels
|
||||
break;
|
||||
case 2:
|
||||
channels = 6;
|
||||
break;
|
||||
default: // stereo
|
||||
channels = 2;
|
||||
break;
|
||||
g_tvAudio = IAudioAPI::CreateDeviceFromConfig(true, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
|
||||
}
|
||||
|
||||
IAudioAPI::DeviceDescriptionPtr device_description;
|
||||
if (IAudioAPI::IsAudioAPIAvailable(audio_api))
|
||||
catch (std::runtime_error& ex)
|
||||
{
|
||||
auto devices = IAudioAPI::GetDevices(audio_api);
|
||||
const auto it = std::find_if(devices.begin(), devices.end(), [&config](const auto& d) {return d->GetIdentifier() == config.tv_device; });
|
||||
if (it != devices.end())
|
||||
device_description = *it;
|
||||
}
|
||||
|
||||
if (device_description)
|
||||
{
|
||||
try
|
||||
{
|
||||
g_tvAudio = IAudioAPI::CreateDevice((IAudioAPI::AudioAPI)config.audio_api, device_description, 48000, channels, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
|
||||
g_tvAudio->SetVolume(config.tv_volume);
|
||||
}
|
||||
catch (std::runtime_error& ex)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "can't initialize tv audio: {}", ex.what());
|
||||
exit(0);
|
||||
}
|
||||
cemuLog_log(LogType::Force, "can't initialize tv audio: {}", ex.what());
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!g_padAudio)
|
||||
{
|
||||
sint32 channels;
|
||||
switch (config.pad_channels)
|
||||
try
|
||||
{
|
||||
case 0:
|
||||
channels = 1; // will mix mono sound on both output channels
|
||||
break;
|
||||
case 2:
|
||||
channels = 6;
|
||||
break;
|
||||
default: // stereo
|
||||
channels = 2;
|
||||
break;
|
||||
g_padAudio = IAudioAPI::CreateDeviceFromConfig(false, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
|
||||
if(g_padAudio)
|
||||
g_padVolume = g_padAudio->GetVolume();
|
||||
}
|
||||
|
||||
IAudioAPI::DeviceDescriptionPtr device_description;
|
||||
if (IAudioAPI::IsAudioAPIAvailable(audio_api))
|
||||
catch (std::runtime_error& ex)
|
||||
{
|
||||
auto devices = IAudioAPI::GetDevices(audio_api);
|
||||
const auto it = std::find_if(devices.begin(), devices.end(), [&config](const auto& d) {return d->GetIdentifier() == config.pad_device; });
|
||||
if (it != devices.end())
|
||||
device_description = *it;
|
||||
}
|
||||
|
||||
if (device_description)
|
||||
{
|
||||
try
|
||||
{
|
||||
g_padAudio = IAudioAPI::CreateDevice((IAudioAPI::AudioAPI)config.audio_api, device_description, 48000, channels, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
|
||||
g_padAudio->SetVolume(config.pad_volume);
|
||||
g_padVolume = config.pad_volume;
|
||||
}
|
||||
catch (std::runtime_error& ex)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "can't initialize pad audio: {}", ex.what());
|
||||
exit(0);
|
||||
}
|
||||
cemuLog_log(LogType::Force, "can't initialize pad audio: {}", ex.what());
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ CubebAPI::~CubebAPI()
|
||||
bool CubebAPI::NeedAdditionalBlocks() const
|
||||
{
|
||||
std::shared_lock lock(m_mutex);
|
||||
return m_buffer.size() < s_audioDelay * m_bytesPerBlock;
|
||||
return m_buffer.size() < GetAudioDelay() * m_bytesPerBlock;
|
||||
}
|
||||
|
||||
bool CubebAPI::FeedBlock(sint16* data)
|
||||
|
@ -210,7 +210,7 @@ void DirectSoundAPI::SetVolume(sint32 volume)
|
||||
bool DirectSoundAPI::NeedAdditionalBlocks() const
|
||||
{
|
||||
std::shared_lock lock(m_mutex);
|
||||
return m_buffer.size() < s_audioDelay;
|
||||
return m_buffer.size() < GetAudioDelay();
|
||||
}
|
||||
|
||||
std::vector<DirectSoundAPI::DeviceDescriptionPtr> DirectSoundAPI::GetDevices()
|
||||
|
@ -97,7 +97,40 @@ bool IAudioAPI::IsAudioAPIAvailable(AudioAPI api)
|
||||
return false;
|
||||
}
|
||||
|
||||
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(bool TV, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample)
|
||||
{
|
||||
auto& config = GetConfig();
|
||||
sint32 channels = CemuConfig::AudioChannelsToNChannels(TV ? config.tv_channels : config.pad_channels);
|
||||
return CreateDeviceFromConfig(TV, rate, channels, samples_per_block, bits_per_sample);
|
||||
}
|
||||
|
||||
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(bool TV, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample)
|
||||
{
|
||||
AudioAPIPtr audioAPIDev;
|
||||
|
||||
auto& config = GetConfig();
|
||||
|
||||
const auto audio_api = (IAudioAPI::AudioAPI)config.audio_api;
|
||||
auto& selectedDevice = TV ? config.tv_device : config.pad_device;
|
||||
|
||||
if(selectedDevice.empty())
|
||||
return {};
|
||||
|
||||
IAudioAPI::DeviceDescriptionPtr device_description;
|
||||
if (IAudioAPI::IsAudioAPIAvailable(audio_api))
|
||||
{
|
||||
auto devices = IAudioAPI::GetDevices(audio_api);
|
||||
const auto it = std::find_if(devices.begin(), devices.end(), [&selectedDevice](const auto& d) {return d->GetIdentifier() == selectedDevice; });
|
||||
if (it != devices.end())
|
||||
device_description = *it;
|
||||
}
|
||||
if (!device_description)
|
||||
throw std::runtime_error("failed to find selected device while trying to create audio device");
|
||||
|
||||
audioAPIDev = CreateDevice(audio_api, device_description, rate, channels, samples_per_block, bits_per_sample);
|
||||
audioAPIDev->SetVolume(TV ? config.tv_volume : config.pad_volume);
|
||||
return audioAPIDev;
|
||||
}
|
||||
|
||||
AudioAPIPtr IAudioAPI::CreateDevice(AudioAPI api, const DeviceDescriptionPtr& device, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample)
|
||||
{
|
||||
@ -167,3 +200,12 @@ std::vector<IAudioAPI::DeviceDescriptionPtr> IAudioAPI::GetDevices(AudioAPI api)
|
||||
}
|
||||
}
|
||||
|
||||
void IAudioAPI::SetAudioDelayOverride(uint32 delay)
|
||||
{
|
||||
m_audioDelayOverride = delay;
|
||||
}
|
||||
|
||||
uint32 IAudioAPI::GetAudioDelay() const
|
||||
{
|
||||
return m_audioDelayOverride > 0 ? m_audioDelayOverride : s_audioDelay;
|
||||
}
|
||||
|
@ -55,11 +55,15 @@ public:
|
||||
virtual bool FeedBlock(sint16* data) = 0;
|
||||
virtual bool Play() = 0;
|
||||
virtual bool Stop() = 0;
|
||||
void SetAudioDelayOverride(uint32 delay);
|
||||
uint32 GetAudioDelay() const;
|
||||
|
||||
static void PrintLogging();
|
||||
static void InitializeStatic();
|
||||
static bool IsAudioAPIAvailable(AudioAPI api);
|
||||
|
||||
|
||||
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(bool TV, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample);
|
||||
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(bool TV, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
|
||||
static std::unique_ptr<IAudioAPI> CreateDevice(AudioAPI api, const DeviceDescriptionPtr& device, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
|
||||
static std::vector<DeviceDescriptionPtr> GetDevices(AudioAPI api);
|
||||
|
||||
@ -75,9 +79,10 @@ protected:
|
||||
bool m_playing = false;
|
||||
|
||||
static std::array<bool, AudioAPIEnd> s_availableApis;
|
||||
static uint32 s_audioDelay;
|
||||
uint32 m_audioDelayOverride = 0;
|
||||
|
||||
private:
|
||||
static uint32 s_audioDelay;
|
||||
void InitWFX(sint32 samplerate, sint32 channels, sint32 bits_per_sample);
|
||||
|
||||
};
|
||||
|
@ -33,8 +33,8 @@ XAudio27API::XAudio27API(uint32 device_id, uint32 samplerate, uint32 channels, u
|
||||
m_wfx.Format.nChannels = channels;
|
||||
m_wfx.Format.nSamplesPerSec = samplerate;
|
||||
m_wfx.Format.wBitsPerSample = bits_per_sample;
|
||||
m_wfx.Format.nBlockAlign = (m_wfx.Format.nChannels * m_wfx.Format.wBitsPerSample) / 8; // must equal (nChannels × wBitsPerSample) / 8
|
||||
m_wfx.Format.nAvgBytesPerSec = m_wfx.Format.nSamplesPerSec * m_wfx.Format.nBlockAlign; // must equal nSamplesPerSec × nBlockAlign.
|
||||
m_wfx.Format.nBlockAlign = (m_wfx.Format.nChannels * m_wfx.Format.wBitsPerSample) / 8; // must equal (nChannels × wBitsPerSample) / 8
|
||||
m_wfx.Format.nAvgBytesPerSec = m_wfx.Format.nSamplesPerSec * m_wfx.Format.nBlockAlign; // must equal nSamplesPerSec × nBlockAlign.
|
||||
m_wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
|
||||
m_wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
@ -199,9 +199,7 @@ bool XAudio27API::FeedBlock(sint16* data)
|
||||
// check if we queued too many blocks
|
||||
if(m_blocks_queued >= kBlockCount)
|
||||
{
|
||||
XAUDIO2_VOICE_STATE state{};
|
||||
m_source_voice->GetState(&state);
|
||||
m_blocks_queued = state.BuffersQueued;
|
||||
m_blocks_queued = GetQueuedBuffers();
|
||||
|
||||
if (m_blocks_queued >= kBlockCount)
|
||||
{
|
||||
@ -222,7 +220,14 @@ bool XAudio27API::FeedBlock(sint16* data)
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 XAudio27API::GetQueuedBuffers() const
|
||||
{
|
||||
XAUDIO2_VOICE_STATE state{};
|
||||
m_source_voice->GetState(&state);
|
||||
return state.BuffersQueued;
|
||||
}
|
||||
|
||||
bool XAudio27API::NeedAdditionalBlocks() const
|
||||
{
|
||||
return m_blocks_queued < s_audioDelay;
|
||||
return GetQueuedBuffers() < GetAudioDelay();
|
||||
}
|
||||
|
@ -47,6 +47,8 @@ public:
|
||||
static std::vector<DeviceDescriptionPtr> GetDevices();
|
||||
|
||||
private:
|
||||
uint32 GetQueuedBuffers() const;
|
||||
|
||||
struct XAudioDeleter
|
||||
{
|
||||
void operator()(IXAudio2* ptr) const;
|
||||
|
@ -270,9 +270,7 @@ bool XAudio2API::FeedBlock(sint16* data)
|
||||
// check if we queued too many blocks
|
||||
if (m_blocks_queued >= kBlockCount)
|
||||
{
|
||||
XAUDIO2_VOICE_STATE state{};
|
||||
m_source_voice->GetState(&state);
|
||||
m_blocks_queued = state.BuffersQueued;
|
||||
m_blocks_queued = GetQueuedBuffers();
|
||||
|
||||
if (m_blocks_queued >= kBlockCount)
|
||||
{
|
||||
@ -293,7 +291,14 @@ bool XAudio2API::FeedBlock(sint16* data)
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 XAudio2API::GetQueuedBuffers() const
|
||||
{
|
||||
XAUDIO2_VOICE_STATE state{};
|
||||
m_source_voice->GetState(&state);
|
||||
return state.BuffersQueued;
|
||||
}
|
||||
|
||||
bool XAudio2API::NeedAdditionalBlocks() const
|
||||
{
|
||||
return m_blocks_queued < s_audioDelay;
|
||||
return GetQueuedBuffers() < GetAudioDelay();
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ public:
|
||||
static const std::vector<DeviceDescriptionPtr>& GetDevices() { return s_devices; }
|
||||
|
||||
private:
|
||||
uint32 GetQueuedBuffers() const;
|
||||
|
||||
static const std::vector<DeviceDescriptionPtr>& RefreshDevices();
|
||||
|
||||
struct XAudioDeleter
|
||||
|
@ -46,6 +46,7 @@ void CemuConfig::Load(XMLConfigParser& parser)
|
||||
fullscreen = parser.get("fullscreen", fullscreen);
|
||||
proxy_server = parser.get("proxy_server", "");
|
||||
disable_screensaver = parser.get("disable_screensaver", disable_screensaver);
|
||||
play_boot_sound = parser.get("play_boot_sound", play_boot_sound);
|
||||
console_language = parser.get("console_language", console_language.GetInitValue());
|
||||
|
||||
window_position.x = parser.get("window_position").get("x", -1);
|
||||
@ -370,6 +371,7 @@ void CemuConfig::Save(XMLConfigParser& parser)
|
||||
config.set<bool>("fullscreen", fullscreen);
|
||||
config.set("proxy_server", proxy_server.GetValue().c_str());
|
||||
config.set<bool>("disable_screensaver", disable_screensaver);
|
||||
config.set<bool>("play_boot_sound", play_boot_sound);
|
||||
|
||||
// config.set("cpu_mode", cpu_mode.GetValue());
|
||||
//config.set("console_region", console_region.GetValue());
|
||||
|
@ -380,6 +380,7 @@ struct CemuConfig
|
||||
#endif
|
||||
ConfigValue<bool> disable_screensaver{DISABLE_SCREENSAVER_DEFAULT};
|
||||
#undef DISABLE_SCREENSAVER_DEFAULT
|
||||
ConfigValue<bool> play_boot_sound{false};
|
||||
|
||||
std::vector<std::string> game_paths;
|
||||
std::mutex game_cache_entries_mutex;
|
||||
@ -524,7 +525,20 @@ struct CemuConfig
|
||||
ConfigValue<bool> emulate_dimensions_toypad{false};
|
||||
}emulated_usb_devices{};
|
||||
|
||||
private:
|
||||
static int AudioChannelsToNChannels(AudioChannels kStereo)
|
||||
{
|
||||
switch (kStereo)
|
||||
{
|
||||
case 0:
|
||||
return 1; // will mix mono sound on both output channels
|
||||
case 2:
|
||||
return 6;
|
||||
default: // stereo
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
GameEntry* GetGameEntryByTitleId(uint64 titleId);
|
||||
GameEntry* CreateGameEntry(uint64 titleId);
|
||||
};
|
||||
|
@ -207,8 +207,10 @@ wxPanel* GeneralSettings2::AddGeneralPage(wxNotebook* notebook)
|
||||
#if BOOST_OS_MACOS
|
||||
m_disable_screensaver->Enable(false);
|
||||
#endif
|
||||
|
||||
// InsertEmptyRow();
|
||||
m_play_boot_sound = new wxCheckBox(box, wxID_ANY, _("Enable intro sound"));
|
||||
m_play_boot_sound->SetToolTip(_("Play bootSound file while compiling shaders/pipelines."));
|
||||
second_row->Add(m_play_boot_sound, 0, botflag, 5);
|
||||
CountRowElement();
|
||||
|
||||
m_auto_update = new wxCheckBox(box, wxID_ANY, _("Automatically check for updates"));
|
||||
m_auto_update->SetToolTip(_("Automatically checks for new cemu versions on startup"));
|
||||
@ -936,13 +938,15 @@ void GeneralSettings2::StoreConfig()
|
||||
#if BOOST_OS_LINUX && defined(ENABLE_FERAL_GAMEMODE)
|
||||
config.feral_gamemode = m_feral_gamemode->IsChecked();
|
||||
#endif
|
||||
config.play_boot_sound = m_play_boot_sound->IsChecked();
|
||||
config.disable_screensaver = m_disable_screensaver->IsChecked();
|
||||
// Toggle while a game is running
|
||||
if (CafeSystem::IsTitleRunning())
|
||||
{
|
||||
ScreenSaver::SetInhibit(config.disable_screensaver);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -1 is default wx widget value -> set to dummy 0 so mainwindow and padwindow will update it
|
||||
config.window_position = m_save_window_position_size->IsChecked() ? Vector2i{ 0,0 } : Vector2i{-1,-1};
|
||||
config.window_size = m_save_window_position_size->IsChecked() ? Vector2i{ 0,0 } : Vector2i{-1,-1};
|
||||
@ -1574,6 +1578,7 @@ void GeneralSettings2::ApplyConfig()
|
||||
m_save_screenshot->SetValue(config.save_screenshot);
|
||||
|
||||
m_disable_screensaver->SetValue(config.disable_screensaver);
|
||||
m_play_boot_sound->SetValue(config.play_boot_sound);
|
||||
#if BOOST_OS_LINUX && defined(ENABLE_FERAL_GAMEMODE)
|
||||
m_feral_gamemode->SetValue(config.feral_gamemode);
|
||||
#endif
|
||||
@ -1776,20 +1781,7 @@ void GeneralSettings2::UpdateAudioDevice()
|
||||
if (m_game_launched && g_tvAudio)
|
||||
channels = g_tvAudio->GetChannels();
|
||||
else
|
||||
{
|
||||
switch (config.tv_channels)
|
||||
{
|
||||
case 0:
|
||||
channels = 1;
|
||||
break;
|
||||
case 2:
|
||||
channels = 6;
|
||||
break;
|
||||
default: // stereo
|
||||
channels = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
channels = CemuConfig::AudioChannelsToNChannels(config.tv_channels);
|
||||
|
||||
try
|
||||
{
|
||||
@ -1824,20 +1816,7 @@ void GeneralSettings2::UpdateAudioDevice()
|
||||
if (m_game_launched && g_padAudio)
|
||||
channels = g_padAudio->GetChannels();
|
||||
else
|
||||
{
|
||||
switch (config.pad_channels)
|
||||
{
|
||||
case 0:
|
||||
channels = 1;
|
||||
break;
|
||||
case 2:
|
||||
channels = 6;
|
||||
break;
|
||||
default: // stereo
|
||||
channels = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
channels = CemuConfig::AudioChannelsToNChannels(config.pad_channels);
|
||||
|
||||
try
|
||||
{
|
||||
@ -1873,20 +1852,7 @@ void GeneralSettings2::UpdateAudioDevice()
|
||||
if (m_game_launched && g_inputAudio)
|
||||
channels = g_inputAudio->GetChannels();
|
||||
else
|
||||
{
|
||||
switch (config.input_channels)
|
||||
{
|
||||
case 0:
|
||||
channels = 1;
|
||||
break;
|
||||
case 2:
|
||||
channels = 6;
|
||||
break;
|
||||
default: // stereo
|
||||
channels = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
channels = CemuConfig::AudioChannelsToNChannels(config.input_channels);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -43,6 +43,7 @@ private:
|
||||
wxCheckBox* m_discord_presence, *m_fullscreen_menubar;
|
||||
wxCheckBox* m_auto_update, *m_receive_untested_releases, *m_save_screenshot;
|
||||
wxCheckBox* m_disable_screensaver;
|
||||
wxCheckBox* m_play_boot_sound;
|
||||
#if BOOST_OS_LINUX && defined(ENABLE_FERAL_GAMEMODE)
|
||||
wxCheckBox* m_feral_gamemode;
|
||||
#endif
|
||||
|
@ -1,5 +1,7 @@
|
||||
add_library(CemuUtil
|
||||
boost/bluetooth.h
|
||||
bootSound/BootSoundReader.cpp
|
||||
bootSound/BootSoundReader.h
|
||||
ChunkedHeap/ChunkedHeap.h
|
||||
containers/flat_hash_map.hpp
|
||||
containers/IntervalBucketContainer.h
|
||||
|
51
src/util/bootSound/BootSoundReader.cpp
Normal file
51
src/util/bootSound/BootSoundReader.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include "BootSoundReader.h"
|
||||
#include "Cafe/CafeSystem.h"
|
||||
|
||||
BootSoundReader::BootSoundReader(FSCVirtualFile* bootsndFile, sint32 blockSize) : bootsndFile(bootsndFile), blockSize(blockSize)
|
||||
{
|
||||
// crash if this constructor is invoked with a blockSize that has a different number of samples per channel
|
||||
cemu_assert(blockSize % (sizeof(sint16be) * 2) == 0);
|
||||
|
||||
fsc_setFileSeek(bootsndFile, 0);
|
||||
fsc_readFile(bootsndFile, &muteBits, 4);
|
||||
fsc_readFile(bootsndFile, &loopPoint, 4);
|
||||
|
||||
buffer.resize(blockSize / sizeof(sint16));
|
||||
bufferBE.resize(blockSize / sizeof(sint16be));
|
||||
|
||||
// workaround: SM3DW has incorrect loop point
|
||||
const auto titleId = CafeSystem::GetForegroundTitleId();
|
||||
if(titleId == 0x0005000010145D00 || titleId == 0x0005000010145C00 || titleId == 0x0005000010106100)
|
||||
loopPoint = 113074;
|
||||
}
|
||||
|
||||
sint16* BootSoundReader::getSamples()
|
||||
{
|
||||
size_t totalRead = 0;
|
||||
const size_t loopPointOffset = 8 + loopPoint * 4;
|
||||
while (totalRead < blockSize)
|
||||
{
|
||||
auto read = fsc_readFile(bootsndFile, bufferBE.data(), blockSize - totalRead);
|
||||
if (read == 0)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "failed to read PCM samples from bootSound.btsnd");
|
||||
return nullptr;
|
||||
}
|
||||
if (read % (sizeof(sint16be) * 2) != 0)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "failed to play bootSound.btsnd: reading PCM data stopped at an odd number of samples (is the file corrupt?)");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::copy_n(bufferBE.begin(), read / sizeof(sint16be), buffer.begin() + (totalRead / sizeof(sint16)));
|
||||
totalRead += read;
|
||||
if (totalRead < blockSize)
|
||||
fsc_setFileSeek(bootsndFile, loopPointOffset);
|
||||
}
|
||||
|
||||
// handle case where the end of a block of samples lines up with the end of the file
|
||||
if(fsc_getFileSeek(bootsndFile) == fsc_getFileSize(bootsndFile))
|
||||
fsc_setFileSeek(bootsndFile, loopPointOffset);
|
||||
|
||||
return buffer.data();
|
||||
}
|
20
src/util/bootSound/BootSoundReader.h
Normal file
20
src/util/bootSound/BootSoundReader.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "Cafe/Filesystem/fsc.h"
|
||||
|
||||
class BootSoundReader
|
||||
{
|
||||
public:
|
||||
BootSoundReader() = delete;
|
||||
BootSoundReader(FSCVirtualFile* bootsndFile, sint32 blockSize);
|
||||
|
||||
sint16* getSamples();
|
||||
|
||||
private:
|
||||
FSCVirtualFile* bootsndFile{};
|
||||
sint32 blockSize{};
|
||||
|
||||
uint32be muteBits{};
|
||||
uint32be loopPoint{};
|
||||
std::vector<sint16> buffer{};
|
||||
std::vector<sint16be> bufferBE{};
|
||||
};
|
Loading…
Reference in New Issue
Block a user