@@ -161,6 +161,116 @@ bool LoadTGAFile(const std::vector<uint8>& buffer, TGAFILE *tgaFile)
161
161
return true ;
162
162
}
163
163
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
+
164
274
void LatteShaderCache_finish ()
165
275
{
166
276
if (g_renderer->GetType () == RendererAPI::Vulkan)
@@ -305,9 +415,8 @@ void LatteShaderCache_Load()
305
415
loadBackgroundTexture (true , g_shaderCacheLoaderState.textureTVId );
306
416
loadBackgroundTexture (false , g_shaderCacheLoaderState.textureDRCId );
307
417
308
- // initialise resources for playing bootup sound
309
418
if (GetConfig ().play_boot_sound )
310
- LatteShaderCache_InitBootSound ();
419
+ g_bootSndPlayer. StartSound ();
311
420
312
421
sint32 numLoadedShaders = 0 ;
313
422
uint32 loadIndex = 0 ;
@@ -376,8 +485,7 @@ void LatteShaderCache_Load()
376
485
if (g_shaderCacheLoaderState.textureDRCId )
377
486
g_renderer->DeleteTexture (g_shaderCacheLoaderState.textureDRCId );
378
487
379
- // free resources for playing boot sound
380
- LatteShaderCache_ShutdownBootSound ();
488
+ g_bootSndPlayer.StopSound ();
381
489
}
382
490
383
491
void LatteShaderCache_ShowProgress (const std::function <bool (void )>& loadUpdateFunc, bool isPipelines)
@@ -818,87 +926,4 @@ void LatteShaderCache_handleDeprecatedCacheFiles(fs::path pathGeneric, fs::path
818
926
fs::remove (pathGenericPre1_25_0, ec);
819
927
}
820
928
}
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