Skip to content

Commit 92721a0

Browse files
committed
Add fadeout and handle thread lifetime with class
1 parent 372bbd0 commit 92721a0

File tree

1 file changed

+113
-88
lines changed

1 file changed

+113
-88
lines changed

src/Cafe/HW/Latte/Core/LatteShaderCache.cpp

Lines changed: 113 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,116 @@ bool LoadTGAFile(const std::vector<uint8>& buffer, TGAFILE *tgaFile)
161161
return true;
162162
}
163163

164+
class BootSoundPlayer
165+
{
166+
public:
167+
BootSoundPlayer() = default;
168+
~BootSoundPlayer()
169+
{
170+
StopSound();
171+
}
172+
173+
void StartSound()
174+
{
175+
if (!m_bootSndPlayThread.joinable())
176+
{
177+
m_stopRequested = false;
178+
m_volumeFactor = 1.0f;
179+
m_bootSndPlayThread = std::thread{[this]() {
180+
StreamBootSound();
181+
}};
182+
}
183+
}
184+
185+
void StopSound()
186+
{
187+
m_stopRequested = true;
188+
}
189+
190+
void ApplyFadeOutEffect(std::span<sint16> samples, float fadeStep)
191+
{
192+
for(auto& i : samples)
193+
{
194+
i *= m_volumeFactor;
195+
if(m_volumeFactor >= fadeStep)
196+
m_volumeFactor -= fadeStep;
197+
else
198+
m_volumeFactor = 0;
199+
}
200+
}
201+
202+
void StreamBootSound()
203+
{
204+
constexpr sint32 sampleRate = 48'000;
205+
constexpr sint32 bitsPerSample = 16;
206+
constexpr sint32 samplesPerBlock = sampleRate / 10; // block is 1/10th of a second
207+
constexpr sint32 nChannels = 2;
208+
static_assert(bitsPerSample % 8 == 0, "bits per sample is not a multiple of 8");
209+
210+
AudioAPIPtr bootSndAudioDev;
211+
212+
try
213+
{
214+
bootSndAudioDev = IAudioAPI::CreateDeviceFromConfig(true, sampleRate, nChannels, samplesPerBlock, bitsPerSample);
215+
if(!bootSndAudioDev)
216+
return;
217+
}
218+
catch (const std::runtime_error& ex)
219+
{
220+
cemuLog_log(LogType::Force, "Failed to initialise audio device for bootup sound");
221+
return;
222+
}
223+
bootSndAudioDev->SetAudioDelayOverride(4);
224+
bootSndAudioDev->Play();
225+
226+
std::string sndPath = fmt::format("{}/meta/{}", CafeSystem::GetMlcStoragePath(CafeSystem::GetForegroundTitleId()), "bootSound.btsnd");
227+
sint32 fscStatus = FSC_STATUS_UNDEFINED;
228+
229+
if(!fsc_doesFileExist(sndPath.c_str()))
230+
return;
231+
232+
FSCVirtualFile* bootSndFileHandle = fsc_open(sndPath.c_str(), FSC_ACCESS_FLAG::OPEN_FILE | FSC_ACCESS_FLAG::READ_PERMISSION, &fscStatus);
233+
if(!bootSndFileHandle)
234+
{
235+
cemuLog_log(LogType::Force, "failed to open bootSound.btsnd");
236+
return;
237+
}
238+
239+
constexpr sint32 audioBlockSize = samplesPerBlock * (bitsPerSample/8) * nChannels;
240+
BootSoundReader bootSndFileReader(bootSndFileHandle, audioBlockSize);
241+
242+
while(m_volumeFactor > 0)
243+
{
244+
while (bootSndAudioDev->NeedAdditionalBlocks())
245+
{
246+
sint16* data = bootSndFileReader.getSamples();
247+
if(data == nullptr)
248+
{
249+
// break outer loop
250+
m_volumeFactor = 0.0f;
251+
m_stopRequested = true;
252+
break;
253+
}
254+
if(m_stopRequested)
255+
ApplyFadeOutEffect({data, samplesPerBlock * 2}, 1.0f / (sampleRate * 10.0f));
256+
257+
bootSndAudioDev->FeedBlock(data);
258+
}
259+
// sleep for the duration of a single block
260+
std::this_thread::sleep_for(std::chrono::milliseconds(samplesPerBlock / (sampleRate/ 1'000)));
261+
}
262+
263+
if(bootSndFileHandle)
264+
fsc_close(bootSndFileHandle);
265+
}
266+
267+
private:
268+
std::thread m_bootSndPlayThread;
269+
std::atomic_bool m_stopRequested = false;
270+
float m_volumeFactor = 1.0f;
271+
};
272+
static BootSoundPlayer g_bootSndPlayer;
273+
164274
void LatteShaderCache_finish()
165275
{
166276
if (g_renderer->GetType() == RendererAPI::Vulkan)
@@ -305,9 +415,8 @@ void LatteShaderCache_Load()
305415
loadBackgroundTexture(true, g_shaderCacheLoaderState.textureTVId);
306416
loadBackgroundTexture(false, g_shaderCacheLoaderState.textureDRCId);
307417

308-
// initialise resources for playing bootup sound
309418
if(GetConfig().play_boot_sound)
310-
LatteShaderCache_InitBootSound();
419+
g_bootSndPlayer.StartSound();
311420

312421
sint32 numLoadedShaders = 0;
313422
uint32 loadIndex = 0;
@@ -376,8 +485,7 @@ void LatteShaderCache_Load()
376485
if (g_shaderCacheLoaderState.textureDRCId)
377486
g_renderer->DeleteTexture(g_shaderCacheLoaderState.textureDRCId);
378487

379-
// free resources for playing boot sound
380-
LatteShaderCache_ShutdownBootSound();
488+
g_bootSndPlayer.StopSound();
381489
}
382490

383491
void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateFunc, bool isPipelines)
@@ -818,87 +926,4 @@ void LatteShaderCache_handleDeprecatedCacheFiles(fs::path pathGeneric, fs::path
818926
fs::remove(pathGenericPre1_25_0, ec);
819927
}
820928
}
821-
}
822-
823-
static std::atomic<bool> audiothread_keeprunning = true;
824-
825-
void LatteShaderCache_StreamBootSound()
826-
{
827-
constexpr sint32 sampleRate = 48'000;
828-
constexpr sint32 bitsPerSample = 16;
829-
constexpr sint32 samplesPerBlock = sampleRate / 10; // block is 1/10th of a second
830-
constexpr sint32 nChannels = 2;
831-
static_assert(bitsPerSample % 8 == 0, "bits per sample is not a multiple of 8");
832-
833-
AudioAPIPtr bootSndAudioDev;
834-
std::unique_ptr<BootSoundReader> bootSndFileReader;
835-
FSCVirtualFile* bootSndFileHandle = 0;
836-
837-
try
838-
{
839-
bootSndAudioDev = IAudioAPI::CreateDeviceFromConfig(true, sampleRate, nChannels, samplesPerBlock, bitsPerSample);
840-
if(!bootSndAudioDev)
841-
return;
842-
}
843-
catch (const std::runtime_error& ex)
844-
{
845-
cemuLog_log(LogType::Force, "Failed to initialise audio device for bootup sound");
846-
return;
847-
}
848-
bootSndAudioDev->SetAudioDelayOverride(4);
849-
bootSndAudioDev->Play();
850-
851-
std::string sndPath = fmt::format("{}/meta/{}", CafeSystem::GetMlcStoragePath(CafeSystem::GetForegroundTitleId()), "bootSound.btsnd");
852-
sint32 fscStatus = FSC_STATUS_UNDEFINED;
853-
854-
if(!fsc_doesFileExist(sndPath.c_str()))
855-
return;
856-
857-
bootSndFileHandle = fsc_open(sndPath.c_str(), FSC_ACCESS_FLAG::OPEN_FILE | FSC_ACCESS_FLAG::READ_PERMISSION, &fscStatus);
858-
if(!bootSndFileHandle)
859-
{
860-
cemuLog_log(LogType::Force, "failed to open bootSound.btsnd");
861-
return;
862-
}
863-
864-
constexpr sint32 audioBlockSize = samplesPerBlock * (bitsPerSample/8) * nChannels;
865-
bootSndFileReader = std::make_unique<BootSoundReader>(bootSndFileHandle, audioBlockSize);
866-
867-
if(bootSndAudioDev && bootSndFileHandle && bootSndFileReader)
868-
{
869-
while(audiothread_keeprunning)
870-
{
871-
while (bootSndAudioDev->NeedAdditionalBlocks())
872-
{
873-
sint16* data = bootSndFileReader->getSamples();
874-
if(data == nullptr)
875-
{
876-
audiothread_keeprunning = false;
877-
break;
878-
}
879-
bootSndAudioDev->FeedBlock(data);
880-
}
881-
// sleep for the duration of a single block
882-
std::this_thread::sleep_for(std::chrono::milliseconds(samplesPerBlock / (sampleRate/ 1'000)));
883-
}
884-
}
885-
886-
if(bootSndFileHandle)
887-
fsc_close(bootSndFileHandle);
888-
}
889-
890-
static std::thread g_bootSndPlayThread;
891-
void LatteShaderCache_InitBootSound()
892-
{
893-
audiothread_keeprunning = true;
894-
if(!g_bootSndPlayThread.joinable())
895-
g_bootSndPlayThread = std::thread{LatteShaderCache_StreamBootSound};
896-
}
897-
898-
void LatteShaderCache_ShutdownBootSound()
899-
{
900-
audiothread_keeprunning = false;
901-
if(g_bootSndPlayThread.joinable())
902-
g_bootSndPlayThread.join();
903-
g_bootSndPlayThread = {};
904-
}
929+
}

0 commit comments

Comments
 (0)