mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-04-24 22:34:54 +00:00
Merge 4de290dc6f
into 879a8889aa
This commit is contained in:
commit
d2092ccedd
19 changed files with 235 additions and 164 deletions
|
@ -77,9 +77,21 @@ void AlsaSound::SoundLoop()
|
||||||
m_thread_status.store(ALSAThreadStatus::STOPPED);
|
m_thread_status.store(ALSAThreadStatus::STOPPED);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AlsaSound::SetRunning(bool running)
|
bool AlsaSound::Start()
|
||||||
{
|
{
|
||||||
m_thread_status.store(running ? ALSAThreadStatus::RUNNING : ALSAThreadStatus::PAUSED);
|
m_thread_status.store(ALSAThreadStatus::RUNNING);
|
||||||
|
|
||||||
|
// Immediately lock and unlock mutex to prevent cv race.
|
||||||
|
std::unique_lock<std::mutex>{cv_m}.unlock();
|
||||||
|
|
||||||
|
// Notify thread that status has changed
|
||||||
|
cv.notify_one();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AlsaSound::Stop()
|
||||||
|
{
|
||||||
|
m_thread_status.store(ALSAThreadStatus::PAUSED);
|
||||||
|
|
||||||
// Immediately lock and unlock mutex to prevent cv race.
|
// Immediately lock and unlock mutex to prevent cv race.
|
||||||
std::unique_lock<std::mutex>{cv_m}.unlock();
|
std::unique_lock<std::mutex>{cv_m}.unlock();
|
||||||
|
|
|
@ -23,7 +23,8 @@ public:
|
||||||
~AlsaSound() override;
|
~AlsaSound() override;
|
||||||
|
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
bool SetRunning(bool running) override;
|
bool Start() override;
|
||||||
|
bool Stop() override;
|
||||||
|
|
||||||
static bool IsValid() { return true; }
|
static bool IsValid() { return true; }
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ void PostInitSoundStream(Core::System& system)
|
||||||
// This needs to be called after AudioInterface::Init and SerialInterface::Init (for GBA devices)
|
// This needs to be called after AudioInterface::Init and SerialInterface::Init (for GBA devices)
|
||||||
// where input sample rates are set
|
// where input sample rates are set
|
||||||
UpdateSoundStream(system);
|
UpdateSoundStream(system);
|
||||||
SetSoundStreamRunning(system, true);
|
StartSoundStream(system);
|
||||||
|
|
||||||
if (Config::Get(Config::MAIN_DUMP_AUDIO) && !system.IsAudioDumpStarted())
|
if (Config::Get(Config::MAIN_DUMP_AUDIO) && !system.IsAudioDumpStarted())
|
||||||
StartAudioDump(system);
|
StartAudioDump(system);
|
||||||
|
@ -85,7 +85,7 @@ void ShutdownSoundStream(Core::System& system)
|
||||||
if (Config::Get(Config::MAIN_DUMP_AUDIO) && system.IsAudioDumpStarted())
|
if (Config::Get(Config::MAIN_DUMP_AUDIO) && system.IsAudioDumpStarted())
|
||||||
StopAudioDump(system);
|
StopAudioDump(system);
|
||||||
|
|
||||||
SetSoundStreamRunning(system, false);
|
StopSoundStream(system);
|
||||||
system.SetSoundStream(nullptr);
|
system.SetSoundStream(nullptr);
|
||||||
|
|
||||||
INFO_LOG_FMT(AUDIO, "Done shutting down sound stream");
|
INFO_LOG_FMT(AUDIO, "Done shutting down sound stream");
|
||||||
|
@ -171,23 +171,40 @@ void UpdateSoundStream(Core::System& system)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetSoundStreamRunning(Core::System& system, bool running)
|
void StartSoundStream(Core::System& system)
|
||||||
{
|
{
|
||||||
SoundStream* sound_stream = system.GetSoundStream();
|
SoundStream* sound_stream = system.GetSoundStream();
|
||||||
|
|
||||||
if (!sound_stream)
|
if (!sound_stream)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (system.IsSoundStreamRunning() == running)
|
if (system.IsSoundStreamRunning())
|
||||||
return;
|
return;
|
||||||
system.SetSoundStreamRunning(running);
|
|
||||||
|
|
||||||
if (sound_stream->SetRunning(running))
|
system.StartSoundStream();
|
||||||
|
|
||||||
|
if (sound_stream->Start())
|
||||||
return;
|
return;
|
||||||
if (running)
|
|
||||||
ERROR_LOG_FMT(AUDIO, "Error starting stream.");
|
ERROR_LOG_FMT(AUDIO, "Error starting stream.");
|
||||||
else
|
}
|
||||||
ERROR_LOG_FMT(AUDIO, "Error stopping stream.");
|
|
||||||
|
void StopSoundStream(Core::System& system)
|
||||||
|
{
|
||||||
|
SoundStream* sound_stream = system.GetSoundStream();
|
||||||
|
|
||||||
|
if (!sound_stream)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!system.IsSoundStreamRunning())
|
||||||
|
return;
|
||||||
|
|
||||||
|
system.StopSoundStream();
|
||||||
|
|
||||||
|
if (sound_stream->Stop())
|
||||||
|
return;
|
||||||
|
|
||||||
|
ERROR_LOG_FMT(AUDIO, "Error stopping stream.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendAIBuffer(Core::System& system, const short* samples, unsigned int num_samples)
|
void SendAIBuffer(Core::System& system, const short* samples, unsigned int num_samples)
|
||||||
|
|
|
@ -30,7 +30,8 @@ bool SupportsDPL2Decoder(std::string_view backend);
|
||||||
bool SupportsLatencyControl(std::string_view backend);
|
bool SupportsLatencyControl(std::string_view backend);
|
||||||
bool SupportsVolumeChanges(std::string_view backend);
|
bool SupportsVolumeChanges(std::string_view backend);
|
||||||
void UpdateSoundStream(Core::System& system);
|
void UpdateSoundStream(Core::System& system);
|
||||||
void SetSoundStreamRunning(Core::System& system, bool running);
|
void StartSoundStream(Core::System& system);
|
||||||
|
void StopSoundStream(Core::System& system);
|
||||||
void SendAIBuffer(Core::System& system, const short* samples, unsigned int num_samples);
|
void SendAIBuffer(Core::System& system, const short* samples, unsigned int num_samples);
|
||||||
void StartAudioDump(Core::System& system);
|
void StartAudioDump(Core::System& system);
|
||||||
void StopAudioDump(Core::System& system);
|
void StopAudioDump(Core::System& system);
|
||||||
|
|
|
@ -105,8 +105,9 @@ bool CubebStream::Init()
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CubebStream::SetRunning(bool running)
|
bool CubebStream::Start()
|
||||||
{
|
{
|
||||||
|
bool running = true;
|
||||||
bool return_value = false;
|
bool return_value = false;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -116,10 +117,28 @@ bool CubebStream::SetRunning(bool running)
|
||||||
m_work_queue.EmplaceItem([this, running, &return_value, &sync_event] {
|
m_work_queue.EmplaceItem([this, running, &return_value, &sync_event] {
|
||||||
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
|
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
|
||||||
#endif
|
#endif
|
||||||
if (running)
|
return_value = cubeb_stream_start(m_stream) == CUBEB_OK;
|
||||||
return_value = cubeb_stream_start(m_stream) == CUBEB_OK;
|
#ifdef _WIN32
|
||||||
else
|
});
|
||||||
return_value = cubeb_stream_stop(m_stream) == CUBEB_OK;
|
sync_event.Wait();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CubebStream::Stop()
|
||||||
|
{
|
||||||
|
bool running = false;
|
||||||
|
bool return_value = false;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (!m_coinit_success)
|
||||||
|
return false;
|
||||||
|
Common::Event sync_event;
|
||||||
|
m_work_queue.EmplaceItem([this, running, &return_value, &sync_event] {
|
||||||
|
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
|
||||||
|
#endif
|
||||||
|
return_value = cubeb_stream_stop(m_stream) == CUBEB_OK;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
});
|
});
|
||||||
sync_event.Wait();
|
sync_event.Wait();
|
||||||
|
|
|
@ -26,7 +26,8 @@ public:
|
||||||
CubebStream& operator=(CubebStream&& other) = delete;
|
CubebStream& operator=(CubebStream&& other) = delete;
|
||||||
~CubebStream() override;
|
~CubebStream() override;
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
bool SetRunning(bool running) override;
|
bool Start() override;
|
||||||
|
bool Stop() override;
|
||||||
void SetVolume(int) override;
|
void SetVolume(int) override;
|
||||||
static bool IsValid() { return true; }
|
static bool IsValid() { return true; }
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,12 @@ bool NullSound::Init()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NullSound::SetRunning(bool running)
|
bool NullSound::Start()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NullSound::Stop()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@ class NullSound final : public SoundStream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
bool SetRunning(bool running) override;
|
bool Start() override;
|
||||||
|
bool Stop() override;
|
||||||
void SetVolume(int volume) override;
|
void SetVolume(int volume) override;
|
||||||
|
|
||||||
static bool IsValid() { return true; }
|
static bool IsValid() { return true; }
|
||||||
|
|
|
@ -153,16 +153,15 @@ void OpenALStream::SetVolume(int volume)
|
||||||
palSourcef(m_source, AL_GAIN, m_volume);
|
palSourcef(m_source, AL_GAIN, m_volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenALStream::SetRunning(bool running)
|
bool OpenALStream::Start()
|
||||||
{
|
{
|
||||||
if (running)
|
palSourcePlay(m_source);
|
||||||
{
|
return true;
|
||||||
palSourcePlay(m_source);
|
}
|
||||||
}
|
|
||||||
else
|
bool OpenALStream::Stop()
|
||||||
{
|
{
|
||||||
palSourceStop(m_source);
|
palSourceStop(m_source);
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,8 @@ public:
|
||||||
~OpenALStream() override;
|
~OpenALStream() override;
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
void SetVolume(int volume) override;
|
void SetVolume(int volume) override;
|
||||||
bool SetRunning(bool running) override;
|
bool Start() override;
|
||||||
|
bool Stop() override;
|
||||||
|
|
||||||
static bool IsValid();
|
static bool IsValid();
|
||||||
|
|
||||||
|
|
|
@ -137,10 +137,14 @@ OpenSLESStream::~OpenSLESStream()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenSLESStream::SetRunning(bool running)
|
bool OpenSLESStream::Start()
|
||||||
{
|
{
|
||||||
SLuint32 new_state = running ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_PAUSED;
|
return (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING) == SL_RESULT_SUCCESS;
|
||||||
return (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, new_state) == SL_RESULT_SUCCESS;
|
}
|
||||||
|
|
||||||
|
bool OpenSLESStream::Stop()
|
||||||
|
{
|
||||||
|
return (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED) == SL_RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenSLESStream::SetVolume(int volume)
|
void OpenSLESStream::SetVolume(int volume)
|
||||||
|
|
|
@ -14,7 +14,8 @@ class OpenSLESStream final : public SoundStream
|
||||||
public:
|
public:
|
||||||
~OpenSLESStream() override;
|
~OpenSLESStream() override;
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
bool SetRunning(bool running) override;
|
bool Start() override;
|
||||||
|
bool Stop() override;
|
||||||
void SetVolume(int volume) override;
|
void SetVolume(int volume) override;
|
||||||
static bool IsValid() { return true; }
|
static bool IsValid() { return true; }
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,8 @@ public:
|
||||||
~PulseAudio() override;
|
~PulseAudio() override;
|
||||||
|
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
bool SetRunning(bool running) override { return true; }
|
bool Start() override { return true; }
|
||||||
|
bool Stop() override { return true; }
|
||||||
static bool IsValid() { return true; }
|
static bool IsValid() { return true; }
|
||||||
void StateCallback(pa_context* c);
|
void StateCallback(pa_context* c);
|
||||||
void WriteCallback(pa_stream* s, size_t length);
|
void WriteCallback(pa_stream* s, size_t length);
|
||||||
|
|
|
@ -21,5 +21,6 @@ public:
|
||||||
virtual bool Init() { return false; }
|
virtual bool Init() { return false; }
|
||||||
virtual void SetVolume(int) {}
|
virtual void SetVolume(int) {}
|
||||||
// Returns true if successful.
|
// Returns true if successful.
|
||||||
virtual bool SetRunning(bool running) { return false; }
|
virtual bool Start() { return false; }
|
||||||
|
virtual bool Stop() { return false; }
|
||||||
};
|
};
|
||||||
|
|
|
@ -168,150 +168,150 @@ bool WASAPIStream::Init()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WASAPIStream::SetRunning(bool running)
|
bool WASAPIStream::Start()
|
||||||
{
|
{
|
||||||
if (running)
|
ComPtr<IMMDevice> device;
|
||||||
|
|
||||||
|
HRESULT result;
|
||||||
|
|
||||||
|
if (Config::Get(Config::MAIN_WASAPI_DEVICE) == "default")
|
||||||
{
|
{
|
||||||
ComPtr<IMMDevice> device;
|
result = m_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, device.GetAddressOf());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = S_OK;
|
||||||
|
device = GetDeviceByName(Config::Get(Config::MAIN_WASAPI_DEVICE));
|
||||||
|
|
||||||
HRESULT result;
|
if (!device)
|
||||||
|
|
||||||
if (Config::Get(Config::MAIN_WASAPI_DEVICE) == "default")
|
|
||||||
{
|
{
|
||||||
|
ERROR_LOG_FMT(AUDIO, "Can't find device '{}', falling back to default",
|
||||||
|
Config::Get(Config::MAIN_WASAPI_DEVICE));
|
||||||
result = m_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, device.GetAddressOf());
|
result = m_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, device.GetAddressOf());
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
|
||||||
result = S_OK;
|
|
||||||
device = GetDeviceByName(Config::Get(Config::MAIN_WASAPI_DEVICE));
|
|
||||||
|
|
||||||
if (!device)
|
if (!HandleWinAPI("Failed to obtain default endpoint", result))
|
||||||
{
|
return false;
|
||||||
ERROR_LOG_FMT(AUDIO, "Can't find device '{}', falling back to default",
|
|
||||||
Config::Get(Config::MAIN_WASAPI_DEVICE));
|
|
||||||
result = m_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, device.GetAddressOf());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!HandleWinAPI("Failed to obtain default endpoint", result))
|
// Show a friendly name in the log
|
||||||
|
ComPtr<IPropertyStore> device_properties;
|
||||||
|
|
||||||
|
result = device->OpenPropertyStore(STGM_READ, device_properties.GetAddressOf());
|
||||||
|
|
||||||
|
if (!HandleWinAPI("Failed to initialize IPropertyStore", result))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
wil::unique_prop_variant device_name;
|
||||||
|
device_properties->GetValue(PKEY_Device_FriendlyName, device_name.addressof());
|
||||||
|
|
||||||
|
INFO_LOG_FMT(AUDIO, "Using audio endpoint '{}'", TStrToUTF8(device_name.pwszVal));
|
||||||
|
|
||||||
|
ComPtr<IAudioClient> audio_client;
|
||||||
|
|
||||||
|
// Get IAudioDevice
|
||||||
|
result = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, nullptr,
|
||||||
|
reinterpret_cast<LPVOID*>(audio_client.GetAddressOf()));
|
||||||
|
|
||||||
|
if (!HandleWinAPI("Failed to activate IAudioClient", result))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
REFERENCE_TIME device_period = 0;
|
||||||
|
|
||||||
|
result = audio_client->GetDevicePeriod(nullptr, &device_period);
|
||||||
|
|
||||||
|
device_period += Config::Get(Config::MAIN_AUDIO_LATENCY) * (10000 / m_format.Format.nChannels);
|
||||||
|
INFO_LOG_FMT(AUDIO, "Audio period set to {}", device_period);
|
||||||
|
|
||||||
|
if (!HandleWinAPI("Failed to obtain device period", result))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
result = audio_client->Initialize(
|
||||||
|
AUDCLNT_SHAREMODE_EXCLUSIVE,
|
||||||
|
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, device_period,
|
||||||
|
device_period, reinterpret_cast<WAVEFORMATEX*>(&m_format), nullptr);
|
||||||
|
|
||||||
|
if (result == AUDCLNT_E_UNSUPPORTED_FORMAT)
|
||||||
|
{
|
||||||
|
OSD::AddMessage("Your current audio device doesn't support 16-bit 48000 hz PCM audio. WASAPI "
|
||||||
|
"exclusive mode won't work.",
|
||||||
|
6000U);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
|
||||||
|
{
|
||||||
|
result = audio_client->GetBufferSize(&m_frames_in_buffer);
|
||||||
|
|
||||||
|
if (!HandleWinAPI("Failed to get aligned buffer size", result))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Show a friendly name in the log
|
|
||||||
ComPtr<IPropertyStore> device_properties;
|
|
||||||
|
|
||||||
result = device->OpenPropertyStore(STGM_READ, device_properties.GetAddressOf());
|
|
||||||
|
|
||||||
if (!HandleWinAPI("Failed to initialize IPropertyStore", result))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
wil::unique_prop_variant device_name;
|
|
||||||
device_properties->GetValue(PKEY_Device_FriendlyName, device_name.addressof());
|
|
||||||
|
|
||||||
INFO_LOG_FMT(AUDIO, "Using audio endpoint '{}'", TStrToUTF8(device_name.pwszVal));
|
|
||||||
|
|
||||||
ComPtr<IAudioClient> audio_client;
|
|
||||||
|
|
||||||
// Get IAudioDevice
|
// Get IAudioDevice
|
||||||
result = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, nullptr,
|
result = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, nullptr,
|
||||||
reinterpret_cast<LPVOID*>(audio_client.GetAddressOf()));
|
reinterpret_cast<LPVOID*>(audio_client.ReleaseAndGetAddressOf()));
|
||||||
|
|
||||||
if (!HandleWinAPI("Failed to activate IAudioClient", result))
|
if (!HandleWinAPI("Failed to reactivate IAudioClient", result))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
REFERENCE_TIME device_period = 0;
|
device_period =
|
||||||
|
static_cast<REFERENCE_TIME>(
|
||||||
result = audio_client->GetDevicePeriod(nullptr, &device_period);
|
10000.0 * 1000 * m_frames_in_buffer / m_format.Format.nSamplesPerSec + 0.5) +
|
||||||
|
Config::Get(Config::MAIN_AUDIO_LATENCY) * 10000;
|
||||||
device_period += Config::Get(Config::MAIN_AUDIO_LATENCY) * (10000 / m_format.Format.nChannels);
|
|
||||||
INFO_LOG_FMT(AUDIO, "Audio period set to {}", device_period);
|
|
||||||
|
|
||||||
if (!HandleWinAPI("Failed to obtain device period", result))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
result = audio_client->Initialize(
|
result = audio_client->Initialize(
|
||||||
AUDCLNT_SHAREMODE_EXCLUSIVE,
|
AUDCLNT_SHAREMODE_EXCLUSIVE,
|
||||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, device_period,
|
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, device_period,
|
||||||
device_period, reinterpret_cast<WAVEFORMATEX*>(&m_format), nullptr);
|
device_period, reinterpret_cast<WAVEFORMATEX*>(&m_format), nullptr);
|
||||||
|
|
||||||
if (result == AUDCLNT_E_UNSUPPORTED_FORMAT)
|
|
||||||
{
|
|
||||||
OSD::AddMessage("Your current audio device doesn't support 16-bit 48000 hz PCM audio. WASAPI "
|
|
||||||
"exclusive mode won't work.",
|
|
||||||
6000U);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
|
|
||||||
{
|
|
||||||
result = audio_client->GetBufferSize(&m_frames_in_buffer);
|
|
||||||
|
|
||||||
if (!HandleWinAPI("Failed to get aligned buffer size", result))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Get IAudioDevice
|
|
||||||
result = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, nullptr,
|
|
||||||
reinterpret_cast<LPVOID*>(audio_client.ReleaseAndGetAddressOf()));
|
|
||||||
|
|
||||||
if (!HandleWinAPI("Failed to reactivate IAudioClient", result))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
device_period =
|
|
||||||
static_cast<REFERENCE_TIME>(
|
|
||||||
10000.0 * 1000 * m_frames_in_buffer / m_format.Format.nSamplesPerSec + 0.5) +
|
|
||||||
Config::Get(Config::MAIN_AUDIO_LATENCY) * 10000;
|
|
||||||
|
|
||||||
result = audio_client->Initialize(
|
|
||||||
AUDCLNT_SHAREMODE_EXCLUSIVE,
|
|
||||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, device_period,
|
|
||||||
device_period, reinterpret_cast<WAVEFORMATEX*>(&m_format), nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!HandleWinAPI("Failed to initialize IAudioClient", result))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
result = audio_client->GetBufferSize(&m_frames_in_buffer);
|
|
||||||
|
|
||||||
if (!HandleWinAPI("Failed to get buffer size from IAudioClient", result))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IAudioRenderClient> audio_renderer;
|
|
||||||
|
|
||||||
result = audio_client->GetService(IID_PPV_ARGS(audio_renderer.GetAddressOf()));
|
|
||||||
|
|
||||||
if (!HandleWinAPI("Failed to get IAudioRenderClient from IAudioClient", result))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
wil::unique_event_nothrow need_data_event;
|
|
||||||
need_data_event.create();
|
|
||||||
|
|
||||||
audio_client->SetEventHandle(need_data_event.get());
|
|
||||||
|
|
||||||
result = audio_client->Start();
|
|
||||||
|
|
||||||
if (!HandleWinAPI("Failed to get IAudioRenderClient from IAudioClient", result))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
INFO_LOG_FMT(AUDIO, "WASAPI: Successfully initialized!");
|
|
||||||
|
|
||||||
// "Commit" audio client and audio renderer now
|
|
||||||
m_audio_client = std::move(audio_client);
|
|
||||||
m_audio_renderer = std::move(audio_renderer);
|
|
||||||
m_need_data_event = std::move(need_data_event);
|
|
||||||
|
|
||||||
m_running.store(true, std::memory_order_relaxed);
|
|
||||||
m_thread = std::thread(&WASAPIStream::SoundLoop, this);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
m_running.store(false, std::memory_order_relaxed);
|
|
||||||
|
|
||||||
if (m_thread.joinable())
|
if (!HandleWinAPI("Failed to initialize IAudioClient", result))
|
||||||
m_thread.join();
|
return false;
|
||||||
|
|
||||||
m_need_data_event.reset();
|
result = audio_client->GetBufferSize(&m_frames_in_buffer);
|
||||||
m_audio_renderer.Reset();
|
|
||||||
m_audio_client.Reset();
|
if (!HandleWinAPI("Failed to get buffer size from IAudioClient", result))
|
||||||
}
|
return false;
|
||||||
|
|
||||||
|
ComPtr<IAudioRenderClient> audio_renderer;
|
||||||
|
|
||||||
|
result = audio_client->GetService(IID_PPV_ARGS(audio_renderer.GetAddressOf()));
|
||||||
|
|
||||||
|
if (!HandleWinAPI("Failed to get IAudioRenderClient from IAudioClient", result))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
wil::unique_event_nothrow need_data_event;
|
||||||
|
need_data_event.create();
|
||||||
|
|
||||||
|
audio_client->SetEventHandle(need_data_event.get());
|
||||||
|
|
||||||
|
result = audio_client->Start();
|
||||||
|
|
||||||
|
if (!HandleWinAPI("Failed to get IAudioRenderClient from IAudioClient", result))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
INFO_LOG_FMT(AUDIO, "WASAPI: Successfully initialized!");
|
||||||
|
|
||||||
|
// "Commit" audio client and audio renderer now
|
||||||
|
m_audio_client = std::move(audio_client);
|
||||||
|
m_audio_renderer = std::move(audio_renderer);
|
||||||
|
m_need_data_event = std::move(need_data_event);
|
||||||
|
|
||||||
|
m_running.store(true, std::memory_order_relaxed);
|
||||||
|
m_thread = std::thread(&WASAPIStream::SoundLoop, this);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WASAPIStream::Stop()
|
||||||
|
{
|
||||||
|
m_running.store(false, std::memory_order_relaxed);
|
||||||
|
|
||||||
|
if (m_thread.joinable())
|
||||||
|
m_thread.join();
|
||||||
|
|
||||||
|
m_need_data_event.reset();
|
||||||
|
m_audio_renderer.Reset();
|
||||||
|
m_audio_client.Reset();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@ public:
|
||||||
explicit WASAPIStream();
|
explicit WASAPIStream();
|
||||||
~WASAPIStream();
|
~WASAPIStream();
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
bool SetRunning(bool running) override;
|
bool Start() override;
|
||||||
|
bool Stop() override;
|
||||||
|
|
||||||
static bool IsValid();
|
static bool IsValid();
|
||||||
static std::vector<std::string> GetAvailableDevices();
|
static std::vector<std::string> GetAvailableDevices();
|
||||||
|
|
|
@ -232,7 +232,7 @@ void CPUManager::RunAdjacentSystems(bool running)
|
||||||
m_system.GetFifo().EmulatorState(running);
|
m_system.GetFifo().EmulatorState(running);
|
||||||
// Core is responsible for shutting down the sound stream.
|
// Core is responsible for shutting down the sound stream.
|
||||||
if (m_state != State::PowerDown)
|
if (m_state != State::PowerDown)
|
||||||
AudioCommon::SetSoundStreamRunning(m_system, running);
|
running ? AudioCommon::StartSoundStream(m_system) : AudioCommon::StopSoundStream(m_system);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPUManager::Stop()
|
void CPUManager::Stop()
|
||||||
|
|
|
@ -130,9 +130,14 @@ bool System::IsSoundStreamRunning() const
|
||||||
return m_impl->m_sound_stream_running;
|
return m_impl->m_sound_stream_running;
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::SetSoundStreamRunning(bool running)
|
void System::StartSoundStream()
|
||||||
{
|
{
|
||||||
m_impl->m_sound_stream_running = running;
|
m_impl->m_sound_stream_running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::StopSoundStream()
|
||||||
|
{
|
||||||
|
m_impl->m_sound_stream_running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::IsAudioDumpStarted() const
|
bool System::IsAudioDumpStarted() const
|
||||||
|
|
|
@ -154,7 +154,8 @@ public:
|
||||||
SoundStream* GetSoundStream() const;
|
SoundStream* GetSoundStream() const;
|
||||||
void SetSoundStream(std::unique_ptr<SoundStream> sound_stream);
|
void SetSoundStream(std::unique_ptr<SoundStream> sound_stream);
|
||||||
bool IsSoundStreamRunning() const;
|
bool IsSoundStreamRunning() const;
|
||||||
void SetSoundStreamRunning(bool running);
|
void StartSoundStream();
|
||||||
|
void StopSoundStream();
|
||||||
bool IsAudioDumpStarted() const;
|
bool IsAudioDumpStarted() const;
|
||||||
void SetAudioDumpStarted(bool started);
|
void SetAudioDumpStarted(bool started);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue