Push and wait on WorkQueueThread items using PushBlocking. Previously we
created a Common::Event sync_event on the caller's stack, called Wait on
it, then had the WorkQueueThread call Set on the sync_event once the
thread was done.
In addition to being simpler the new way avoids a use-after-free that
could happen in convoluted and unlikely yet possible thread scheduling
sequences.
One such case can be triggered as follows:
* Set your audio backend to Cubeb
* In CubebStream::SetVolume set a breakpoint at the call to Wait and at
the call to cubeb_stream_set_volume.
* Start a game.
* Continue until the Cubeb Worker thread hits the
cubeb_stream_set_volume breakpoint and Emuthread hits the Wait
breakpoint, freezing each thread when it hits its breakpoint.
* Unfreeze Cubeb Worker.
* In Event::Set set a breakpoint at the end of the scope containing the
lock_guard such that the guard has been constructed but not destructed
when the breakpoint is hit.
* Continue until that breakpoint is hit by Cubeb Worker. If other
threads hit it first keep going.
* Freeze Cubeb Worker.
* For convenience remove the breakpoint in Event::Set so other threads
don't trigger it.
* In CubebStream::SetRunning set a breakpoint at the call to Wait.
* Unfreeze Emuthread and continue until the breakpoint is hit.
* In Cubeb Worker go to Event::Set and examine the values of m_mutex's
member variables. In Visual Studio Debug these are locking_thread_id
== 0xcccccc01 and ownership_levels == 0xcccccccc. This is the result
of Visual Studio overwriting the memory used on the stack by
sync_event in CubebStream::SetVolume with cc bytes to represent
uninitialized memory on the stack (since that function already
returned), and then allocating enough memory on the stack when calling
AudioCommon::SetSoundStreamRunning and then CubebStream::SetRunning
that it overwrote one byte of the memory formerly occupied by
locking_thread_id.
* If you unfreeze Cubeb Worker at this point it will trigger the lock
guard's destructor which will then try to unlock m_mutex. Since
m_mutex is no longer in scope this is a use-after-free, and in VS
debug triggers a debug assert due to locking_thread_id not matching
the current thread id.
This can reduce audio latency according to
https://developer.android.com/ndk/guides/audio/opensl/opensl-prog-notes#perform.
Previously we were using the hardcoded values of 48000 Hz and 256 frames
per buffer. The sample rate we use with this change is 48000 Hz on all
devices I'm aware of, but the buffer size does vary across devices.
Terminology note: The old code used the term "sample" to refer to what
Android refers to as a "frame". "Frame" is a clearer term to use for
this, so I've changed OpenSLESStream's terminology. One frame consists
of one sample per channel.
Not sure if we're ever going to want to have more than one of these at
the same time, but these global variables are a code smell nonetheless.
I'm also deleting the existing member variables because they were
unused.
Based on @noahpistilli (Sketch) PR:
https://github.com/dolphin-emu/dolphin/pull/12567
Fixed the Windows support and the heisenbug caused by uninitialized
members.
Config system integration finalized.
Removed VolumeChanged signal, as ConfigChanged will trigger what is needed.
Only applies UpdateSoundStream to things that can change during emulation.
Settings::SetVolume might no longer be used, but left it in.
Cubeb logs a message at CUBEB_LOG_NORMAL verbosity every time you start
or stop a stream which can get a bit annoying when using frame advance
at Dolphin's default verbosity.