diff --git a/src/xenia/apu/audio_system.cc b/src/xenia/apu/audio_system.cc index d4580991e..659934595 100644 --- a/src/xenia/apu/audio_system.cc +++ b/src/xenia/apu/audio_system.cc @@ -62,18 +62,22 @@ AudioSystem::AudioSystem(Emulator* emulator) worker_running_(false), decoder_running_(false) { std::memset(clients_, 0, sizeof(clients_)); - for (size_t i = 0; i < maximum_client_count_; ++i) { + for (size_t i = 0; i < kMaximumClientCount; ++i) { unused_clients_.push(i); } - for (size_t i = 0; i < xe::countof(client_wait_handles_); ++i) { - client_wait_handles_[i] = CreateEvent(NULL, TRUE, FALSE, NULL); + for (size_t i = 0; i < kMaximumClientCount; ++i) { + client_semaphores_[i] = CreateSemaphore(NULL, 0, kMaximumQueuedFrames, NULL); + wait_handles_[i] = client_semaphores_[i]; } + shutdown_event_ = CreateEvent(NULL, TRUE, FALSE, NULL); + wait_handles_[kMaximumClientCount] = shutdown_event_; } AudioSystem::~AudioSystem() { - for (size_t i = 0; i < xe::countof(client_wait_handles_); ++i) { - CloseHandle(client_wait_handles_[i]); + for (size_t i = 0; i < kMaximumClientCount; ++i) { + CloseHandle(client_semaphores_[i]); } + CloseHandle(shutdown_event_); } void av_log_callback(void *avcl, int level, const char *fmt, va_list va) { @@ -149,16 +153,16 @@ void AudioSystem::WorkerThreadMain() { // Main run loop. while (worker_running_) { auto result = - WaitForMultipleObjectsEx(DWORD(xe::countof(client_wait_handles_)), - client_wait_handles_, FALSE, INFINITE, FALSE); + WaitForMultipleObjectsEx(DWORD(xe::countof(wait_handles_)), + wait_handles_, FALSE, INFINITE, FALSE); if (result == WAIT_FAILED || - result == WAIT_OBJECT_0 + maximum_client_count_) { + result == WAIT_OBJECT_0 + kMaximumClientCount) { continue; } size_t pumped = 0; if (result >= WAIT_OBJECT_0 && - result <= WAIT_OBJECT_0 + (maximum_client_count_ - 1)) { + result <= WAIT_OBJECT_0 + (kMaximumClientCount - 1)) { size_t index = result - WAIT_OBJECT_0; do { lock_.lock(); @@ -174,8 +178,8 @@ void AudioSystem::WorkerThreadMain() { } pumped++; index++; - } while (index < maximum_client_count_ && - WaitForSingleObject(client_wait_handles_[index], 0) == + } while (index < kMaximumClientCount && + WaitForSingleObject(client_semaphores_[index], 0) == WAIT_OBJECT_0); } @@ -225,7 +229,7 @@ void AudioSystem::Initialize() {} void AudioSystem::Shutdown() { worker_running_ = false; - SetEvent(client_wait_handles_[maximum_client_count_]); + SetEvent(shutdown_event_); worker_thread_->Wait(0, 0, 0, nullptr); worker_thread_.reset(); @@ -297,10 +301,10 @@ X_STATUS AudioSystem::RegisterClient(uint32_t callback, uint32_t callback_arg, auto index = unused_clients_.front(); - auto wait_handle = client_wait_handles_[index]; - ResetEvent(wait_handle); + auto client_semaphore = client_semaphores_[index]; + assert_true(ReleaseSemaphore(client_semaphore, kMaximumQueuedFrames, NULL) == TRUE); AudioDriver* driver; - auto result = CreateDriver(index, wait_handle, &driver); + auto result = CreateDriver(index, client_semaphore, &driver); if (XFAILED(result)) { return result; } @@ -324,7 +328,7 @@ void AudioSystem::SubmitFrame(size_t index, uint32_t samples_ptr) { SCOPE_profile_cpu_f("apu"); std::lock_guard lock(lock_); - assert_true(index < maximum_client_count_); + assert_true(index < kMaximumClientCount); assert_true(clients_[index].driver != NULL); (clients_[index].driver)->SubmitFrame(samples_ptr); } @@ -333,18 +337,25 @@ void AudioSystem::UnregisterClient(size_t index) { SCOPE_profile_cpu_f("apu"); std::lock_guard lock(lock_); - assert_true(index < maximum_client_count_); + assert_true(index < kMaximumClientCount); DestroyDriver(clients_[index].driver); clients_[index] = {0}; unused_clients_.push(index); - ResetEvent(client_wait_handles_[index]); + + // drain the semaphore of its count + auto client_semaphore = client_semaphores_[index]; + DWORD wait_result; + do { + wait_result = WaitForSingleObject(client_semaphore, 0); + } while (wait_result == WAIT_OBJECT_0); + assert_true(wait_result == WAIT_TIMEOUT); } void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { SCOPE_profile_cpu_f("apu"); // Translate this for future use. - uint8_t* out = memory()->TranslatePhysical(data.output_buffer_ptr); + uint8_t* output_buffer = memory()->TranslatePhysical(data.output_buffer_ptr); // What I see: // XMA outputs 2 bytes per sample @@ -371,13 +382,15 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { // Output buffers are in raw PCM samples, 256 bytes per block. // Output buffer is a ring buffer. We need to write from the write offset // to the read offset. - uint32_t output_size_bytes = data.output_buffer_block_count * 256; - uint32_t output_write_offset_bytes = data.output_buffer_write_offset * 256; - uint32_t output_read_offset_bytes = data.output_buffer_read_offset * 256; + uint32_t output_capacity = data.output_buffer_block_count * 256; + uint32_t output_read_offset = data.output_buffer_read_offset * 256; + uint32_t output_write_offset = data.output_buffer_write_offset * 256; - RingBuffer output_buffer(out, output_size_bytes, output_read_offset_bytes, output_write_offset_bytes); - size_t output_remaining_bytes = output_buffer.write_size(); + RingBuffer output_rb(output_buffer, output_capacity); + output_rb.set_read_offset(output_read_offset); + output_rb.set_write_offset(output_write_offset); + size_t output_remaining_bytes = output_rb.write_count(); if (!output_remaining_bytes) { // Can't write any more data. Break. // The game will kick us again with a new output buffer later. @@ -395,7 +408,9 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { read_bytes = context.decoder->DecodePacket(tmp_buff, 0, output_remaining_bytes); if (read_bytes >= 0) { - output_buffer.Write(tmp_buff, read_bytes); + assert_true((read_bytes % 256) == 0); + auto written_bytes = output_rb.Write(tmp_buff, read_bytes); + assert_true(read_bytes == written_bytes); // Ok. break; @@ -421,11 +436,7 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { } } - data.output_buffer_write_offset += uint32_t(read_bytes) / 256; - if (data.output_buffer_write_offset > data.output_buffer_block_count) { - // Wraparound! - data.output_buffer_write_offset -= data.output_buffer_block_count; - } + data.output_buffer_write_offset = output_rb.write_offset() / 256; // If we need more data and the input buffers have it, grab it. if (read_bytes) { diff --git a/src/xenia/apu/audio_system.h b/src/xenia/apu/audio_system.h index 7373b4c66..d0c7769d3 100644 --- a/src/xenia/apu/audio_system.h +++ b/src/xenia/apu/audio_system.h @@ -142,13 +142,16 @@ class AudioSystem { void UnregisterClient(size_t index); void SubmitFrame(size_t index, uint32_t samples_ptr); - virtual X_STATUS CreateDriver(size_t index, HANDLE wait_handle, + virtual X_STATUS CreateDriver(size_t index, HANDLE semaphore, AudioDriver** out_driver) = 0; virtual void DestroyDriver(AudioDriver* driver) = 0; virtual uint64_t ReadRegister(uint32_t addr); virtual void WriteRegister(uint32_t addr, uint64_t value); + // TODO(gibbed): respect XAUDIO2_MAX_QUEUED_BUFFERS somehow (ie min(64, XAUDIO2_MAX_QUEUED_BUFFERS)) + static const size_t kMaximumQueuedFrames = 64; + protected: virtual void Initialize(); @@ -204,6 +207,7 @@ class AudioSystem { } registers_; uint32_t register_file_[0xFFFF / 4]; }; + struct XMAContext { uint32_t guest_ptr; xe::mutex lock; @@ -218,16 +222,18 @@ class AudioSystem { std::vector xma_context_free_list_; std::vector xma_context_used_list_; // XMA contexts in use - static const size_t maximum_client_count_ = 8; + static const size_t kMaximumClientCount = 8; struct { AudioDriver* driver; uint32_t callback; uint32_t callback_arg; uint32_t wrapped_callback_arg; - } clients_[maximum_client_count_]; - // Last handle is always there in case we have no clients. - HANDLE client_wait_handles_[maximum_client_count_ + 1]; + } clients_[kMaximumClientCount]; + + HANDLE client_semaphores_[kMaximumClientCount]; + HANDLE shutdown_event_; // Event is always there in case we have no clients. + HANDLE wait_handles_[kMaximumClientCount + 1]; std::queue unused_clients_; }; diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc b/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc index 608b201fa..11bee294a 100644 --- a/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc +++ b/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc @@ -20,26 +20,26 @@ namespace xaudio2 { class XAudio2AudioDriver::VoiceCallback : public IXAudio2VoiceCallback { public: - VoiceCallback(HANDLE wait_handle) : wait_handle_(wait_handle) {} + VoiceCallback(HANDLE semaphore) : semaphore_(semaphore) {} ~VoiceCallback() {} void OnStreamEnd() {} void OnVoiceProcessingPassEnd() {} void OnVoiceProcessingPassStart(uint32_t samples_required) {} - void OnBufferEnd(void* context) { SetEvent(wait_handle_); } + void OnBufferEnd(void* context) { assert_true(ReleaseSemaphore(semaphore_, 1, NULL) == TRUE); } void OnBufferStart(void* context) {} void OnLoopEnd(void* context) {} void OnVoiceError(void* context, HRESULT result) {} private: - HANDLE wait_handle_; + HANDLE semaphore_; }; -XAudio2AudioDriver::XAudio2AudioDriver(Emulator* emulator, HANDLE wait) +XAudio2AudioDriver::XAudio2AudioDriver(Emulator* emulator, HANDLE semaphore) : audio_(nullptr), mastering_voice_(nullptr), pcm_voice_(nullptr), - wait_handle_(wait), + semaphore_(semaphore), voice_callback_(nullptr), current_frame_(0), AudioDriver(emulator) {} @@ -61,7 +61,7 @@ const DWORD ChannelMasks[] = { void XAudio2AudioDriver::Initialize() { HRESULT hr; - voice_callback_ = new VoiceCallback(wait_handle_); + voice_callback_ = new VoiceCallback(semaphore_); hr = XAudio2Create(&audio_, 0, XAUDIO2_DEFAULT_PROCESSOR); if (FAILED(hr)) { @@ -123,8 +123,6 @@ void XAudio2AudioDriver::Initialize() { if (FLAGS_mute) { pcm_voice_->SetVolume(0.0f); } - - SetEvent(wait_handle_); } void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) { @@ -169,13 +167,6 @@ void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) { // Update playback ratio to our time scalar. // This will keep audio in sync with the game clock. pcm_voice_->SetFrequencyRatio(float(xe::Clock::guest_time_scalar())); - - XAUDIO2_VOICE_STATE state2; - pcm_voice_->GetState(&state2, XAUDIO2_VOICE_NOSAMPLESPLAYED); - - if (state2.BuffersQueued >= frame_count_) { - ResetEvent(wait_handle_); - } } void XAudio2AudioDriver::Shutdown() { diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_driver.h b/src/xenia/apu/xaudio2/xaudio2_audio_driver.h index cffb5af90..485ad4a4e 100644 --- a/src/xenia/apu/xaudio2/xaudio2_audio_driver.h +++ b/src/xenia/apu/xaudio2/xaudio2_audio_driver.h @@ -21,7 +21,7 @@ namespace xaudio2 { class XAudio2AudioDriver : public AudioDriver { public: - XAudio2AudioDriver(Emulator* emulator, HANDLE wait); + XAudio2AudioDriver(Emulator* emulator, HANDLE semaphore); virtual ~XAudio2AudioDriver(); virtual void Initialize(); @@ -32,7 +32,7 @@ class XAudio2AudioDriver : public AudioDriver { IXAudio2* audio_; IXAudio2MasteringVoice* mastering_voice_; IXAudio2SourceVoice* pcm_voice_; - HANDLE wait_handle_; + HANDLE semaphore_; class VoiceCallback; VoiceCallback* voice_callback_; diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_system.cc b/src/xenia/apu/xaudio2/xaudio2_audio_system.cc index ee549e45b..1d5f85e68 100644 --- a/src/xenia/apu/xaudio2/xaudio2_audio_system.cc +++ b/src/xenia/apu/xaudio2/xaudio2_audio_system.cc @@ -25,10 +25,10 @@ XAudio2AudioSystem::~XAudio2AudioSystem() {} void XAudio2AudioSystem::Initialize() { AudioSystem::Initialize(); } -X_STATUS XAudio2AudioSystem::CreateDriver(size_t index, HANDLE wait, +X_STATUS XAudio2AudioSystem::CreateDriver(size_t index, HANDLE semaphore, AudioDriver** out_driver) { assert_not_null(out_driver); - auto driver = new XAudio2AudioDriver(emulator_, wait); + auto driver = new XAudio2AudioDriver(emulator_, semaphore); driver->Initialize(); *out_driver = driver; return X_STATUS_SUCCESS; diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_system.h b/src/xenia/apu/xaudio2/xaudio2_audio_system.h index b0e75f1da..dd6fd1e47 100644 --- a/src/xenia/apu/xaudio2/xaudio2_audio_system.h +++ b/src/xenia/apu/xaudio2/xaudio2_audio_system.h @@ -24,7 +24,7 @@ class XAudio2AudioSystem : public AudioSystem { XAudio2AudioSystem(Emulator* emulator); virtual ~XAudio2AudioSystem(); - virtual X_RESULT CreateDriver(size_t index, HANDLE wait, + virtual X_RESULT CreateDriver(size_t index, HANDLE semaphore, AudioDriver** out_driver); virtual void DestroyDriver(AudioDriver* driver); diff --git a/src/xenia/base/ring_buffer.cc b/src/xenia/base/ring_buffer.cc index 45225325b..4f4b37a8b 100644 --- a/src/xenia/base/ring_buffer.cc +++ b/src/xenia/base/ring_buffer.cc @@ -14,60 +14,50 @@ namespace xe { -RingBuffer::RingBuffer(uint8_t* raw_buffer, size_t size, size_t read_offset, size_t write_offset) - : raw_buffer_(raw_buffer) - , size_(size) - , read_offset_(read_offset) - , write_offset_(write_offset) {} +RingBuffer::RingBuffer(uint8_t* buffer, size_t capacity) + : buffer_(buffer) + , capacity_(capacity) + , read_offset_(0) + , write_offset_(0) {} -size_t RingBuffer::Skip(size_t num_bytes) { - num_bytes = std::min(read_size(), num_bytes); - if (read_offset_ + num_bytes < size_) { - read_offset_ += num_bytes; - } else { - read_offset_ = num_bytes - (size_ - read_offset_); - } - return num_bytes; -} - -size_t RingBuffer::Read(uint8_t* buffer, size_t num_bytes) { - num_bytes = std::min(read_size(), num_bytes); - if (!num_bytes) { +size_t RingBuffer::Read(uint8_t* buffer, size_t count) { + count = std::min(count, capacity_); + if (!count) { return 0; } - if (read_offset_ + num_bytes < size_) { - std::memcpy(buffer, raw_buffer_ + read_offset_, num_bytes); - read_offset_ += num_bytes; + if (read_offset_ + count < capacity_) { + std::memcpy(buffer, buffer_ + read_offset_, count); + read_offset_ += count; } else { - size_t left_half = size_ - read_offset_; - size_t right_half = size_ - left_half; - std::memcpy(buffer, raw_buffer_ + read_offset_, left_half); - std::memcpy(buffer + left_half, raw_buffer_, right_half); + size_t left_half = capacity_ - read_offset_; + size_t right_half = count - left_half; + std::memcpy(buffer, buffer_ + read_offset_, left_half); + std::memcpy(buffer + left_half, buffer_, right_half); read_offset_ = right_half; } - return num_bytes; + return count; } -size_t RingBuffer::Write(uint8_t* buffer, size_t num_bytes) { - num_bytes = std::min(num_bytes, write_size()); - if (!num_bytes) { +size_t RingBuffer::Write(uint8_t* buffer, size_t count) { + count = std::min(count, capacity_); + if (!count) { return 0; } - if (write_offset_ + num_bytes < size_) { - std::memcpy(raw_buffer_ + write_offset_, buffer, num_bytes); - write_offset_ += num_bytes; + if (write_offset_ + count < capacity_) { + std::memcpy(buffer_ + write_offset_, buffer, count); + write_offset_ += count; } else { - size_t left_half = size_ - write_offset_; - size_t right_half = size_ - left_half; - std::memcpy(raw_buffer_ + write_offset_, buffer, left_half); - std::memcpy(raw_buffer_, buffer + left_half, right_half); + size_t left_half = capacity_ - write_offset_; + size_t right_half = count - left_half; + std::memcpy(buffer_ + write_offset_, buffer, left_half); + std::memcpy(buffer_, buffer + left_half, right_half); write_offset_ = right_half; } - return num_bytes; + return count; } } // namespace xe diff --git a/src/xenia/base/ring_buffer.h b/src/xenia/base/ring_buffer.h index 4d3830195..0a2c8825d 100644 --- a/src/xenia/base/ring_buffer.h +++ b/src/xenia/base/ring_buffer.h @@ -18,38 +18,47 @@ namespace xe { class RingBuffer { public: - RingBuffer(uint8_t* raw_buffer, size_t size, size_t read_offset, size_t write_offset); + RingBuffer(uint8_t* buffer, size_t capacity); - size_t Read(uint8_t* buffer, size_t num_bytes); - size_t Skip(size_t num_bytes); - size_t Write(uint8_t* buffer, size_t num_bytes); + size_t Read(uint8_t* buffer, size_t count); + size_t Write(uint8_t* buffer, size_t count); + + uint8_t* buffer() { return buffer_; } + size_t capacity() { return capacity_; } size_t read_offset() { return read_offset_; } - size_t write_offset() { return write_offset_; } - - size_t read_size() { + size_t read_count() { if (read_offset_ == write_offset_) { return 0; + } else if (read_offset_ < write_offset_) { + return write_offset_ - read_offset_; + } else { + return (capacity_ - read_offset_) + write_offset_; } - if (read_offset_ < write_offset_) { - return write_offset_ - read_offset_; - } - return (size_ - read_offset_) + write_offset_; } - size_t write_size() { - if (write_offset_ == read_offset_) { - return size_; - } - if (write_offset_ < read_offset_) { + size_t write_offset() { return write_offset_; } + size_t write_count() { + if (read_offset_ == write_offset_) { + return capacity_; + } else if (write_offset_ < read_offset_) { return read_offset_ - write_offset_; + } else { + return (capacity_ - write_offset_) + read_offset_; } - return (size_ - write_offset_) + read_offset_; + } + + void set_read_offset(size_t offset) { + read_offset_ = offset % capacity_; + } + + void set_write_offset(size_t offset) { + write_offset_ = offset % capacity_; } private: - uint8_t* raw_buffer_; - size_t size_; + uint8_t* buffer_; + size_t capacity_; size_t read_offset_; size_t write_offset_; };