src/output: Move event and spsc_queue into thread object
This commit is contained in:
parent
2974737746
commit
db7caa2dac
@ -118,15 +118,14 @@ inline constexpr const unsigned int kErrorId = -1;
|
|||||||
class WasapiOutputThread : public Thread {
|
class WasapiOutputThread : public Thread {
|
||||||
public:
|
public:
|
||||||
enum class Status : uint32_t { FINISH, PLAY, PAUSE };
|
enum class Status : uint32_t { FINISH, PLAY, PAUSE };
|
||||||
WasapiOutputThread(std::shared_ptr<WinEvent> _event, ComPtr<IAudioClient> _client,
|
WasapiOutputThread(IAudioClient *_client,
|
||||||
ComPtr<IAudioRenderClient> &&_render_client,
|
ComPtr<IAudioRenderClient> &&_render_client,
|
||||||
const UINT32 _frame_size, const UINT32 _buffer_size_in_frames,
|
const UINT32 _frame_size, const UINT32 _buffer_size_in_frames,
|
||||||
bool _is_exclusive,
|
bool _is_exclusive)
|
||||||
boost::lockfree::spsc_queue<BYTE> &_spsc_buffer)
|
: Thread(BIND_THIS_METHOD(Work)), client(_client),
|
||||||
: Thread(BIND_THIS_METHOD(Work)), event(std::move(_event)),
|
render_client(std::move(_render_client)), frame_size(_frame_size),
|
||||||
client(std::move(_client)), render_client(std::move(_render_client)),
|
buffer_size_in_frames(_buffer_size_in_frames), is_exclusive(_is_exclusive),
|
||||||
frame_size(_frame_size), buffer_size_in_frames(_buffer_size_in_frames),
|
spsc_buffer(_buffer_size_in_frames * 4 * _frame_size) {}
|
||||||
is_exclusive(_is_exclusive), spsc_buffer(_spsc_buffer) {}
|
|
||||||
void Finish() noexcept { return SetStatus(Status::FINISH); }
|
void Finish() noexcept { return SetStatus(Status::FINISH); }
|
||||||
void Play() noexcept { return SetStatus(Status::PLAY); }
|
void Play() noexcept { return SetStatus(Status::PLAY); }
|
||||||
void Pause() noexcept { return SetStatus(Status::PAUSE); }
|
void Pause() noexcept { return SetStatus(Status::PAUSE); }
|
||||||
@ -144,13 +143,13 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<WinEvent> event;
|
friend class WasapiOutput;
|
||||||
ComPtr<IAudioClient> client;
|
WinEvent event;
|
||||||
|
IAudioClient *client;
|
||||||
ComPtr<IAudioRenderClient> render_client;
|
ComPtr<IAudioRenderClient> render_client;
|
||||||
const UINT32 frame_size;
|
const UINT32 frame_size;
|
||||||
const UINT32 buffer_size_in_frames;
|
const UINT32 buffer_size_in_frames;
|
||||||
bool is_exclusive;
|
bool is_exclusive;
|
||||||
boost::lockfree::spsc_queue<BYTE> &spsc_buffer;
|
|
||||||
alignas(BOOST_LOCKFREE_CACHELINE_BYTES) std::atomic<Status> status =
|
alignas(BOOST_LOCKFREE_CACHELINE_BYTES) std::atomic<Status> status =
|
||||||
Status::PAUSE;
|
Status::PAUSE;
|
||||||
alignas(BOOST_LOCKFREE_CACHELINE_BYTES) struct {
|
alignas(BOOST_LOCKFREE_CACHELINE_BYTES) struct {
|
||||||
@ -162,10 +161,11 @@ private:
|
|||||||
Cond cond;
|
Cond cond;
|
||||||
std::exception_ptr error_ptr = nullptr;
|
std::exception_ptr error_ptr = nullptr;
|
||||||
} error{};
|
} error{};
|
||||||
|
boost::lockfree::spsc_queue<BYTE> spsc_buffer;
|
||||||
|
|
||||||
void SetStatus(Status s) noexcept {
|
void SetStatus(Status s) noexcept {
|
||||||
status.store(s);
|
status.store(s);
|
||||||
event->Set();
|
event.Set();
|
||||||
}
|
}
|
||||||
void Work() noexcept;
|
void Work() noexcept;
|
||||||
};
|
};
|
||||||
@ -203,13 +203,11 @@ private:
|
|||||||
bool enumerate_devices;
|
bool enumerate_devices;
|
||||||
std::string device_config;
|
std::string device_config;
|
||||||
std::vector<std::pair<unsigned int, AllocatedString>> device_desc;
|
std::vector<std::pair<unsigned int, AllocatedString>> device_desc;
|
||||||
std::shared_ptr<WinEvent> event;
|
|
||||||
ComPtr<IMMDeviceEnumerator> enumerator;
|
ComPtr<IMMDeviceEnumerator> enumerator;
|
||||||
ComPtr<IMMDevice> device;
|
ComPtr<IMMDevice> device;
|
||||||
ComPtr<IAudioClient> client;
|
ComPtr<IAudioClient> client;
|
||||||
WAVEFORMATEXTENSIBLE device_format;
|
WAVEFORMATEXTENSIBLE device_format;
|
||||||
std::unique_ptr<WasapiOutputThread> thread;
|
std::optional<WasapiOutputThread> thread;
|
||||||
std::unique_ptr<boost::lockfree::spsc_queue<BYTE>> spsc_buffer;
|
|
||||||
std::size_t watermark;
|
std::size_t watermark;
|
||||||
|
|
||||||
friend bool wasapi_is_exclusive(WasapiOutput &output) noexcept;
|
friend bool wasapi_is_exclusive(WasapiOutput &output) noexcept;
|
||||||
@ -248,7 +246,7 @@ void WasapiOutputThread::Work() noexcept {
|
|||||||
COM com{true};
|
COM com{true};
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
event->Wait(INFINITE);
|
event.Wait(INFINITE);
|
||||||
|
|
||||||
Status current_state = status.load();
|
Status current_state = status.load();
|
||||||
if (current_state == Status::FINISH) {
|
if (current_state == Status::FINISH) {
|
||||||
@ -330,13 +328,10 @@ void WasapiOutput::DoDisable() noexcept {
|
|||||||
err.what());
|
err.what());
|
||||||
}
|
}
|
||||||
thread.reset();
|
thread.reset();
|
||||||
spsc_buffer.reset();
|
|
||||||
client.reset();
|
client.reset();
|
||||||
}
|
}
|
||||||
device.reset();
|
device.reset();
|
||||||
enumerator.reset();
|
enumerator.reset();
|
||||||
com.reset();
|
|
||||||
event.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// run inside COMWorkerThread
|
/// run inside COMWorkerThread
|
||||||
@ -442,11 +437,6 @@ void WasapiOutput::DoOpen(AudioFormat &audio_format) {
|
|||||||
throw FormatHResultError(result, "Unable to get new render client");
|
throw FormatHResultError(result, "Unable to get new render client");
|
||||||
}
|
}
|
||||||
|
|
||||||
result = client->SetEventHandle(event->handle());
|
|
||||||
if (FAILED(result)) {
|
|
||||||
throw FormatHResultError(result, "Unable to set event handler");
|
|
||||||
}
|
|
||||||
|
|
||||||
UINT32 buffer_size_in_frames;
|
UINT32 buffer_size_in_frames;
|
||||||
result = client->GetBufferSize(&buffer_size_in_frames);
|
result = client->GetBufferSize(&buffer_size_in_frames);
|
||||||
if (FAILED(result)) {
|
if (FAILED(result)) {
|
||||||
@ -455,11 +445,14 @@ void WasapiOutput::DoOpen(AudioFormat &audio_format) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watermark = buffer_size_in_frames * 3 * FrameSize();
|
watermark = buffer_size_in_frames * 3 * FrameSize();
|
||||||
spsc_buffer = std::make_unique<boost::lockfree::spsc_queue<BYTE>>(
|
thread.emplace(client.get(), std::move(render_client), FrameSize(),
|
||||||
buffer_size_in_frames * 4 * FrameSize());
|
buffer_size_in_frames, is_exclusive);
|
||||||
thread = std::make_unique<WasapiOutputThread>(
|
|
||||||
event, client, std::move(render_client), FrameSize(),
|
result = client->SetEventHandle(thread->event.handle());
|
||||||
buffer_size_in_frames, is_exclusive, *spsc_buffer);
|
if (FAILED(result)) {
|
||||||
|
throw FormatHResultError(result, "Unable to set event handler");
|
||||||
|
}
|
||||||
|
|
||||||
thread->Start();
|
thread->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,7 +465,6 @@ void WasapiOutput::Close() noexcept {
|
|||||||
thread.reset();
|
thread.reset();
|
||||||
client.reset();
|
client.reset();
|
||||||
}).get();
|
}).get();
|
||||||
spsc_buffer.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::steady_clock::duration WasapiOutput::Delay() const noexcept {
|
std::chrono::steady_clock::duration WasapiOutput::Delay() const noexcept {
|
||||||
@ -480,7 +472,9 @@ std::chrono::steady_clock::duration WasapiOutput::Delay() const noexcept {
|
|||||||
return std::chrono::steady_clock::duration::zero();
|
return std::chrono::steady_clock::duration::zero();
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t data_size = spsc_buffer->read_available();
|
assert(thread);
|
||||||
|
|
||||||
|
const size_t data_size = thread->spsc_buffer.read_available();
|
||||||
const size_t delay_size = std::max(data_size, watermark) - watermark;
|
const size_t delay_size = std::max(data_size, watermark) - watermark;
|
||||||
|
|
||||||
using s = std::chrono::seconds;
|
using s = std::chrono::seconds;
|
||||||
@ -490,13 +484,11 @@ std::chrono::steady_clock::duration WasapiOutput::Delay() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t WasapiOutput::Play(const void *chunk, size_t size) {
|
size_t WasapiOutput::Play(const void *chunk, size_t size) {
|
||||||
if (!client || !thread) {
|
assert(thread);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
const size_t consumed_size =
|
const size_t consumed_size =
|
||||||
spsc_buffer->push(static_cast<const BYTE *>(chunk), size);
|
thread->spsc_buffer.push(static_cast<const BYTE *>(chunk), size);
|
||||||
if (consumed_size == 0) {
|
if (consumed_size == 0) {
|
||||||
assert(is_started);
|
assert(is_started);
|
||||||
thread->WaitWrite();
|
thread->WaitWrite();
|
||||||
@ -523,11 +515,6 @@ size_t WasapiOutput::Play(const void *chunk, size_t size) {
|
|||||||
} while (true);
|
} while (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WasapiOutput::Drain() {
|
|
||||||
spsc_buffer->consume_all([](auto &&) {});
|
|
||||||
thread->CheckException();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WasapiOutput::Pause() {
|
bool WasapiOutput::Pause() {
|
||||||
if (!client || !thread) {
|
if (!client || !thread) {
|
||||||
return false;
|
return false;
|
||||||
@ -549,6 +536,13 @@ bool WasapiOutput::Pause() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WasapiOutput::Drain() {
|
||||||
|
assert(thread);
|
||||||
|
|
||||||
|
thread->spsc_buffer.consume_all([](auto &&) {});
|
||||||
|
thread->CheckException();
|
||||||
|
}
|
||||||
|
|
||||||
/// run inside COMWorkerThread
|
/// run inside COMWorkerThread
|
||||||
void WasapiOutput::OpenDevice() {
|
void WasapiOutput::OpenDevice() {
|
||||||
enumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr,
|
enumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr,
|
||||||
|
Loading…
Reference in New Issue
Block a user