Skip to content

Commit

Permalink
Add fadeout and handle thread lifetime with class
Browse files Browse the repository at this point in the history
  • Loading branch information
goeiecool9999 committed Dec 9, 2024
1 parent 372bbd0 commit 92721a0
Showing 1 changed file with 113 additions and 88 deletions.
201 changes: 113 additions & 88 deletions src/Cafe/HW/Latte/Core/LatteShaderCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,116 @@ bool LoadTGAFile(const std::vector<uint8>& buffer, TGAFILE *tgaFile)
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()
{
if (g_renderer->GetType() == RendererAPI::Vulkan)
Expand Down Expand Up @@ -305,9 +415,8 @@ void LatteShaderCache_Load()
loadBackgroundTexture(true, g_shaderCacheLoaderState.textureTVId);
loadBackgroundTexture(false, g_shaderCacheLoaderState.textureDRCId);

// initialise resources for playing bootup sound
if(GetConfig().play_boot_sound)
LatteShaderCache_InitBootSound();
g_bootSndPlayer.StartSound();

sint32 numLoadedShaders = 0;
uint32 loadIndex = 0;
Expand Down Expand Up @@ -376,8 +485,7 @@ void LatteShaderCache_Load()
if (g_shaderCacheLoaderState.textureDRCId)
g_renderer->DeleteTexture(g_shaderCacheLoaderState.textureDRCId);

// free resources for playing boot sound
LatteShaderCache_ShutdownBootSound();
g_bootSndPlayer.StopSound();
}

void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateFunc, bool isPipelines)
Expand Down Expand Up @@ -818,87 +926,4 @@ void LatteShaderCache_handleDeprecatedCacheFiles(fs::path pathGeneric, fs::path
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 = {};
}
}

0 comments on commit 92721a0

Please sign in to comment.