mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-05-29 00:21:34 +08:00
133 lines
4.0 KiB
C++
133 lines
4.0 KiB
C++
#pragma once
|
|
|
|
#include "Audio/AudioBackend.h"
|
|
#include <atomic>
|
|
#include <mutex>
|
|
#include <string>
|
|
|
|
#include <windows.h>
|
|
#include <mmdeviceapi.h>
|
|
#include <audioclient.h>
|
|
#include <thread>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <memory>
|
|
#include <wrl/client.h>
|
|
|
|
class WASAPIContext : public AudioBackend {
|
|
public:
|
|
WASAPIContext();
|
|
~WASAPIContext();
|
|
|
|
// This is only called on init.
|
|
void SetRenderCallback(RenderCallback callback, void *userdata) override {
|
|
callback_ = callback;
|
|
userdata_ = userdata;
|
|
}
|
|
|
|
void EnumerateDevices(std::vector<AudioDeviceDesc> *outputDevices, bool captureDevices = false) override;
|
|
|
|
bool InitOutputDevice(std::string_view uniqueId, LatencyMode latencyMode, bool *revertedToDefault) override;
|
|
|
|
void FrameUpdate(bool allowAutoChange) override;
|
|
|
|
int PeriodFrames() const override { return actualPeriodFrames_; } // NOTE: This may have the wrong value (too large) until audio has started playing.
|
|
int BufferSize() const override { return reportedBufferSize_; }
|
|
int SampleRate() const override { return curSamplesPerSec_; }
|
|
|
|
// Implements device change notifications
|
|
class DeviceNotificationClient : public IMMNotificationClient {
|
|
public:
|
|
DeviceNotificationClient(WASAPIContext *engine) : engine_(engine) {}
|
|
ULONG STDMETHODCALLTYPE AddRef() override { return 1; }
|
|
ULONG STDMETHODCALLTYPE Release() override { return 1; }
|
|
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppv) override {
|
|
if (iid == __uuidof(IUnknown) || iid == __uuidof(IMMNotificationClient)) {
|
|
*ppv = static_cast<IMMNotificationClient*>(this);
|
|
return S_OK;
|
|
}
|
|
*ppv = nullptr;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device) override;
|
|
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device) override;
|
|
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR device) override;
|
|
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR device, DWORD state) override;
|
|
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR device, const PROPERTYKEY key) override;
|
|
private:
|
|
WASAPIContext *engine_;
|
|
};
|
|
|
|
void DescribeOutputFormat(char *buffer, size_t bufferSize) const override;
|
|
|
|
std::string GetCurrentDeviceName() const override {
|
|
std::lock_guard<std::mutex> guard(deviceLock_);
|
|
return curDeviceName_ + ":" + curDeviceId_;
|
|
}
|
|
|
|
std::string GetErrorString() const override {
|
|
std::string temp;
|
|
{
|
|
std::lock_guard<std::mutex> guard(errorLock_);
|
|
temp = errorString_;
|
|
}
|
|
return temp;
|
|
}
|
|
|
|
private:
|
|
void SetErrorString(std::string_view str, HRESULT hr);
|
|
void ClearErrorString();
|
|
|
|
bool TryInitAudioClient3(IMMDevice *device, LatencyMode latencyMode);
|
|
bool TryInitAudioClient(IMMDevice *device, LatencyMode latencyMode);
|
|
|
|
void Start();
|
|
void Stop();
|
|
|
|
void AudioLoop();
|
|
|
|
enum class AudioFormat {
|
|
Float,
|
|
PCM16,
|
|
Unhandled,
|
|
};
|
|
static AudioFormat Classify(const WAVEFORMATEX *format);
|
|
|
|
// Only one of these can be non-null at a time. Check audioClient3 to determine if it's being used.
|
|
Microsoft::WRL::ComPtr<IAudioClient3> audioClient3_;
|
|
Microsoft::WRL::ComPtr<IAudioClient> audioClient_;
|
|
|
|
Microsoft::WRL::ComPtr<IAudioRenderClient> renderClient_;
|
|
WAVEFORMATEX *format_ = nullptr;
|
|
HANDLE audioEvent_ = nullptr;
|
|
std::thread audioThread_;
|
|
int curSamplesPerSec_ = 0;
|
|
UINT32 defaultPeriodFrames_ = 0;
|
|
UINT32 fundamentalPeriodFrames_ = 0;
|
|
UINT32 minPeriodFrames_ = 0;
|
|
UINT32 maxPeriodFrames_ = 0;
|
|
std::atomic<bool> running_ = true;
|
|
|
|
// NOTE: these do not need to be atomic, due to usage.
|
|
UINT32 actualPeriodFrames_ = 0; // may not be the requested.
|
|
UINT32 reportedBufferSize_ = 0;
|
|
|
|
Microsoft::WRL::ComPtr<IMMDeviceEnumerator> enumerator_;
|
|
DeviceNotificationClient notificationClient_;
|
|
RenderCallback callback_{};
|
|
void *userdata_ = nullptr;
|
|
LatencyMode latencyMode_ = LatencyMode::Aggressive;
|
|
|
|
mutable std::mutex deviceLock_;
|
|
std::string curDeviceName_;
|
|
std::string curDeviceId_;
|
|
std::string newDeviceId_;
|
|
bool defaultDeviceChanged_ = false;
|
|
|
|
mutable std::mutex errorLock_;
|
|
std::string errorString_;
|
|
|
|
std::unique_ptr<float[]> tempBuf_;
|
|
};
|