mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-06 15:01:18 +12:00
Add fadeout and handle thread lifetime with class
This commit is contained in:
parent
372bbd0037
commit
92721a023e
1 changed files with 113 additions and 88 deletions
|
@ -161,6 +161,116 @@ bool LoadTGAFile(const std::vector<uint8>& buffer, TGAFILE *tgaFile)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class BootSoundPlayer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BootSoundPlayer() = default;
|
||||||
|
~BootSoundPlayer()
|
||||||
|
{
|
||||||
|
StopSound();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartSound()
|
||||||
|
{
|
||||||
|
if (!m_bootSndPlayThread.joinable())
|
||||||
|
{
|
||||||
|
m_stopRequested = false;
|
||||||
|
m_volumeFactor = 1.0f;
|
||||||
|
m_bootSndPlayThread = std::thread{[this]() {
|
||||||
|
StreamBootSound();
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StopSound()
|
||||||
|
{
|
||||||
|
m_stopRequested = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyFadeOutEffect(std::span<sint16> samples, float fadeStep)
|
||||||
|
{
|
||||||
|
for(auto& i : samples)
|
||||||
|
{
|
||||||
|
i *= m_volumeFactor;
|
||||||
|
if(m_volumeFactor >= fadeStep)
|
||||||
|
m_volumeFactor -= fadeStep;
|
||||||
|
else
|
||||||
|
m_volumeFactor = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamBootSound()
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
while(m_volumeFactor > 0)
|
||||||
|
{
|
||||||
|
while (bootSndAudioDev->NeedAdditionalBlocks())
|
||||||
|
{
|
||||||
|
sint16* data = bootSndFileReader.getSamples();
|
||||||
|
if(data == nullptr)
|
||||||
|
{
|
||||||
|
// break outer loop
|
||||||
|
m_volumeFactor = 0.0f;
|
||||||
|
m_stopRequested = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(m_stopRequested)
|
||||||
|
ApplyFadeOutEffect({data, samplesPerBlock * 2}, 1.0f / (sampleRate * 10.0f));
|
||||||
|
|
||||||
|
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_stopRequested = false;
|
||||||
|
float m_volumeFactor = 1.0f;
|
||||||
|
};
|
||||||
|
static BootSoundPlayer g_bootSndPlayer;
|
||||||
|
|
||||||
void LatteShaderCache_finish()
|
void LatteShaderCache_finish()
|
||||||
{
|
{
|
||||||
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||||
|
@ -305,9 +415,8 @@ void LatteShaderCache_Load()
|
||||||
loadBackgroundTexture(true, g_shaderCacheLoaderState.textureTVId);
|
loadBackgroundTexture(true, g_shaderCacheLoaderState.textureTVId);
|
||||||
loadBackgroundTexture(false, g_shaderCacheLoaderState.textureDRCId);
|
loadBackgroundTexture(false, g_shaderCacheLoaderState.textureDRCId);
|
||||||
|
|
||||||
// initialise resources for playing bootup sound
|
|
||||||
if(GetConfig().play_boot_sound)
|
if(GetConfig().play_boot_sound)
|
||||||
LatteShaderCache_InitBootSound();
|
g_bootSndPlayer.StartSound();
|
||||||
|
|
||||||
sint32 numLoadedShaders = 0;
|
sint32 numLoadedShaders = 0;
|
||||||
uint32 loadIndex = 0;
|
uint32 loadIndex = 0;
|
||||||
|
@ -376,8 +485,7 @@ void LatteShaderCache_Load()
|
||||||
if (g_shaderCacheLoaderState.textureDRCId)
|
if (g_shaderCacheLoaderState.textureDRCId)
|
||||||
g_renderer->DeleteTexture(g_shaderCacheLoaderState.textureDRCId);
|
g_renderer->DeleteTexture(g_shaderCacheLoaderState.textureDRCId);
|
||||||
|
|
||||||
// free resources for playing boot sound
|
g_bootSndPlayer.StopSound();
|
||||||
LatteShaderCache_ShutdownBootSound();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateFunc, bool isPipelines)
|
void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateFunc, bool isPipelines)
|
||||||
|
@ -818,87 +926,4 @@ void LatteShaderCache_handleDeprecatedCacheFiles(fs::path pathGeneric, fs::path
|
||||||
fs::remove(pathGenericPre1_25_0, ec);
|
fs::remove(pathGenericPre1_25_0, ec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::atomic<bool> audiothread_keeprunning = true;
|
|
||||||
|
|
||||||
void LatteShaderCache_StreamBootSound()
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
std::unique_ptr<BootSoundReader> bootSndFileReader;
|
|
||||||
FSCVirtualFile* bootSndFileHandle = 0;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
bootSndFileReader = std::make_unique<BootSoundReader>(bootSndFileHandle, audioBlockSize);
|
|
||||||
|
|
||||||
if(bootSndAudioDev && bootSndFileHandle && bootSndFileReader)
|
|
||||||
{
|
|
||||||
while(audiothread_keeprunning)
|
|
||||||
{
|
|
||||||
while (bootSndAudioDev->NeedAdditionalBlocks())
|
|
||||||
{
|
|
||||||
sint16* data = bootSndFileReader->getSamples();
|
|
||||||
if(data == nullptr)
|
|
||||||
{
|
|
||||||
audiothread_keeprunning = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::thread g_bootSndPlayThread;
|
|
||||||
void LatteShaderCache_InitBootSound()
|
|
||||||
{
|
|
||||||
audiothread_keeprunning = true;
|
|
||||||
if(!g_bootSndPlayThread.joinable())
|
|
||||||
g_bootSndPlayThread = std::thread{LatteShaderCache_StreamBootSound};
|
|
||||||
}
|
|
||||||
|
|
||||||
void LatteShaderCache_ShutdownBootSound()
|
|
||||||
{
|
|
||||||
audiothread_keeprunning = false;
|
|
||||||
if(g_bootSndPlayThread.joinable())
|
|
||||||
g_bootSndPlayThread.join();
|
|
||||||
g_bootSndPlayThread = {};
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue