From 5a76cac218a0d3735476219df362d1f10613f82c Mon Sep 17 00:00:00 2001 From: Hendi Date: Tue, 15 Oct 2024 00:55:52 +0200 Subject: [PATCH] [APU] Refactor audio system to work with different frequencies/channel layouts --- src/xenia/apu/audio_driver.cc | 2 - src/xenia/apu/audio_driver.h | 21 +++--- src/xenia/apu/audio_system.cc | 4 +- src/xenia/apu/audio_system.h | 7 +- src/xenia/apu/nop/nop_audio_system.cc | 6 ++ src/xenia/apu/nop/nop_audio_system.h | 3 + src/xenia/apu/sdl/sdl_audio_driver.cc | 63 +++++++++++++---- src/xenia/apu/sdl/sdl_audio_driver.h | 26 ++++--- src/xenia/apu/sdl/sdl_audio_system.cc | 9 ++- src/xenia/apu/sdl/sdl_audio_system.h | 3 + src/xenia/apu/xaudio2/xaudio2_audio_driver.cc | 67 ++++++++++++++++--- src/xenia/apu/xaudio2/xaudio2_audio_driver.h | 27 +++++--- src/xenia/apu/xaudio2/xaudio2_audio_system.cc | 9 ++- src/xenia/apu/xaudio2/xaudio2_audio_system.h | 3 + src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc | 5 +- 15 files changed, 195 insertions(+), 60 deletions(-) diff --git a/src/xenia/apu/audio_driver.cc b/src/xenia/apu/audio_driver.cc index 0cd9c5018..c0f16b53c 100644 --- a/src/xenia/apu/audio_driver.cc +++ b/src/xenia/apu/audio_driver.cc @@ -12,8 +12,6 @@ namespace xe { namespace apu { -AudioDriver::AudioDriver(Memory* memory) : memory_(memory) {} - AudioDriver::~AudioDriver() = default; } // namespace apu diff --git a/src/xenia/apu/audio_driver.h b/src/xenia/apu/audio_driver.h index 2cda05ca0..f77a965b1 100644 --- a/src/xenia/apu/audio_driver.h +++ b/src/xenia/apu/audio_driver.h @@ -18,17 +18,22 @@ namespace apu { class AudioDriver { public: - explicit AudioDriver(Memory* memory); + static const uint32_t kFrameFrequencyDefault = 48000; + static const uint32_t kFrameChannelsDefault = 6; + static const uint32_t kChannelSamplesDefault = 256; + static const uint32_t kFrameSamplesMax = + kFrameChannelsDefault * kChannelSamplesDefault; + static const uint32_t kFrameSizeMax = sizeof(float) * kFrameSamplesMax; + virtual ~AudioDriver(); - virtual void SubmitFrame(uint32_t samples_ptr) = 0; + virtual bool Initialize() = 0; + virtual void Shutdown() = 0; - protected: - inline uint8_t* TranslatePhysical(uint32_t guest_address) const { - return memory_->TranslatePhysical(guest_address); - } - - Memory* memory_ = nullptr; + virtual void SubmitFrame(float* samples) = 0; + virtual void Pause() = 0; + virtual void Resume() = 0; + virtual void SetVolume(float volume) = 0; }; } // namespace apu diff --git a/src/xenia/apu/audio_system.cc b/src/xenia/apu/audio_system.cc index 047cc2924..b144905f5 100644 --- a/src/xenia/apu/audio_system.cc +++ b/src/xenia/apu/audio_system.cc @@ -210,13 +210,13 @@ X_STATUS AudioSystem::RegisterClient(uint32_t callback, uint32_t callback_arg, return X_STATUS_SUCCESS; } -void AudioSystem::SubmitFrame(size_t index, uint32_t samples_ptr) { +void AudioSystem::SubmitFrame(size_t index, float* samples) { SCOPE_profile_cpu_f("apu"); auto global_lock = global_critical_region_.Acquire(); assert_true(index < kMaximumClientCount); assert_true(clients_[index].driver != NULL); - (clients_[index].driver)->SubmitFrame(samples_ptr); + (clients_[index].driver)->SubmitFrame(samples); } void AudioSystem::UnregisterClient(size_t index) { diff --git a/src/xenia/apu/audio_system.h b/src/xenia/apu/audio_system.h index 54e2380d9..482559b6d 100644 --- a/src/xenia/apu/audio_system.h +++ b/src/xenia/apu/audio_system.h @@ -42,7 +42,12 @@ class AudioSystem { X_STATUS RegisterClient(uint32_t callback, uint32_t callback_arg, size_t* out_index); void UnregisterClient(size_t index); - void SubmitFrame(size_t index, uint32_t samples_ptr); + void SubmitFrame(size_t index, float* samples); + + // Creates an independent, non-registered driver instance. + virtual AudioDriver* CreateDriver(xe::threading::Semaphore* semaphore, + uint32_t frequency, uint32_t channels, + bool need_format_conversion) = 0; bool Save(ByteStream* stream); bool Restore(ByteStream* stream); diff --git a/src/xenia/apu/nop/nop_audio_system.cc b/src/xenia/apu/nop/nop_audio_system.cc index b65a5bb50..cfac7e74b 100644 --- a/src/xenia/apu/nop/nop_audio_system.cc +++ b/src/xenia/apu/nop/nop_audio_system.cc @@ -30,6 +30,12 @@ X_STATUS NopAudioSystem::CreateDriver(size_t index, return X_STATUS_NOT_IMPLEMENTED; } +AudioDriver* NopAudioSystem::CreateDriver(xe::threading::Semaphore* semaphore, + uint32_t frequency, uint32_t channels, + bool need_format_conversion) { + return nullptr; +} + void NopAudioSystem::DestroyDriver(AudioDriver* driver) { assert_always(); } } // namespace nop diff --git a/src/xenia/apu/nop/nop_audio_system.h b/src/xenia/apu/nop/nop_audio_system.h index 35e1286ef..3d101bc94 100644 --- a/src/xenia/apu/nop/nop_audio_system.h +++ b/src/xenia/apu/nop/nop_audio_system.h @@ -27,6 +27,9 @@ class NopAudioSystem : public AudioSystem { X_STATUS CreateDriver(size_t index, xe::threading::Semaphore* semaphore, AudioDriver** out_driver) override; + AudioDriver* CreateDriver(xe::threading::Semaphore* semaphore, + uint32_t frequency, uint32_t channels, + bool need_format_conversion) override; void DestroyDriver(AudioDriver* driver) override; }; diff --git a/src/xenia/apu/sdl/sdl_audio_driver.cc b/src/xenia/apu/sdl/sdl_audio_driver.cc index bdda565e1..e09997c63 100644 --- a/src/xenia/apu/sdl/sdl_audio_driver.cc +++ b/src/xenia/apu/sdl/sdl_audio_driver.cc @@ -23,9 +23,27 @@ namespace xe { namespace apu { namespace sdl { -SDLAudioDriver::SDLAudioDriver(Memory* memory, - xe::threading::Semaphore* semaphore) - : AudioDriver(memory), semaphore_(semaphore) {} +SDLAudioDriver::SDLAudioDriver(xe::threading::Semaphore* semaphore, + uint32_t frequency, uint32_t channels, + bool need_format_conversion) + : semaphore_(semaphore), + frame_frequency_(frequency), + frame_channels_(channels), + need_format_conversion_(need_format_conversion) { + switch (frame_channels_) { + case 6: + channel_samples_ = 256; + break; + case 2: + channel_samples_ = 768; + break; + default: + assert_unhandled_case(frame_channels_); + } + frame_size_ = sizeof(float) * frame_channels_ * channel_samples_; + assert_true(frame_size_ <= kFrameSizeMax); + assert_true(!need_format_conversion_ || frame_channels_ == 6); +} SDLAudioDriver::~SDLAudioDriver() { assert_true(frames_queued_.empty()); @@ -58,8 +76,10 @@ bool SDLAudioDriver::Initialize() { desired_spec.samples = channel_samples_; desired_spec.callback = SDLCallback; desired_spec.userdata = this; - // Allow the hardware to decide between 5.1 and stereo - int allowed_change = SDL_AUDIO_ALLOW_CHANNELS_CHANGE; + // Allow the hardware to decide between 5.1 and stereo, + // unless the input is stereo + int allowed_change = + frame_channels_ != 2 ? SDL_AUDIO_ALLOW_CHANNELS_CHANGE : 0; for (int i = 0; i < 2; i++) { sdl_device_id_ = SDL_OpenAudioDevice(nullptr, 0, &desired_spec, &obtained_spec, allowed_change); @@ -86,20 +106,19 @@ bool SDLAudioDriver::Initialize() { return true; } -void SDLAudioDriver::SubmitFrame(uint32_t frame_ptr) { - const auto input_frame = memory_->TranslateVirtual(frame_ptr); +void SDLAudioDriver::SubmitFrame(float* frame) { float* output_frame; { std::unique_lock guard(frames_mutex_); if (frames_unused_.empty()) { - output_frame = new float[frame_samples_]; + output_frame = new float[frame_channels_ * channel_samples_]; } else { output_frame = frames_unused_.top(); frames_unused_.pop(); } } - std::memcpy(output_frame, input_frame, frame_samples_ * sizeof(float)); + std::memcpy(output_frame, frame, frame_size_); { std::unique_lock guard(frames_mutex_); @@ -107,6 +126,10 @@ void SDLAudioDriver::SubmitFrame(uint32_t frame_ptr) { } } +void SDLAudioDriver::Pause() { SDL_PauseAudioDevice(sdl_device_id_, 1); } + +void SDLAudioDriver::Resume() { SDL_PauseAudioDevice(sdl_device_id_, 0); } + void SDLAudioDriver::Shutdown() { if (sdl_device_id_ > 0) { SDL_CloseAudioDevice(sdl_device_id_); @@ -134,8 +157,8 @@ void SDLAudioDriver::SDLCallback(void* userdata, Uint8* stream, int len) { return; } const auto driver = static_cast(userdata); - assert_true(len == - sizeof(float) * channel_samples_ * driver->sdl_device_channels_); + assert_true(len == sizeof(float) * driver->channel_samples_ * + driver->sdl_device_channels_); std::unique_lock guard(driver->frames_mutex_); if (driver->frames_queued_.empty()) { @@ -145,20 +168,32 @@ void SDLAudioDriver::SDLCallback(void* userdata, Uint8* stream, int len) { driver->frames_queued_.pop(); if (cvars::mute) { std::memset(stream, 0, len); - } else { + } else if (driver->need_format_conversion_) { switch (driver->sdl_device_channels_) { case 2: conversion::sequential_6_BE_to_interleaved_2_LE( - reinterpret_cast(stream), buffer, channel_samples_); + reinterpret_cast(stream), buffer, + driver->channel_samples_); break; case 6: conversion::sequential_6_BE_to_interleaved_6_LE( - reinterpret_cast(stream), buffer, channel_samples_); + reinterpret_cast(stream), buffer, + driver->channel_samples_); break; default: assert_unhandled_case(driver->sdl_device_channels_); break; } + } else { + assert_true(driver->sdl_device_channels_ == driver->frame_channels_); + if (driver->volume_ != 1.0f) { + std::memset(stream, 0, len); + SDL_MixAudioFormat( + stream, reinterpret_cast(buffer), AUDIO_F32, len, + static_cast(driver->volume_ * SDL_MIX_MAXVOLUME)); + } else { + std::memcpy(stream, buffer, len); + } } driver->frames_unused_.push(buffer); diff --git a/src/xenia/apu/sdl/sdl_audio_driver.h b/src/xenia/apu/sdl/sdl_audio_driver.h index 3d32b86e0..ec805ef7e 100644 --- a/src/xenia/apu/sdl/sdl_audio_driver.h +++ b/src/xenia/apu/sdl/sdl_audio_driver.h @@ -24,12 +24,18 @@ namespace sdl { class SDLAudioDriver : public AudioDriver { public: - SDLAudioDriver(Memory* memory, xe::threading::Semaphore* semaphore); + SDLAudioDriver(xe::threading::Semaphore* semaphore, + uint32_t frequency = kFrameFrequencyDefault, + uint32_t channels = kFrameChannelsDefault, + bool need_format_conversion = true); ~SDLAudioDriver() override; - bool Initialize(); - void SubmitFrame(uint32_t frame_ptr) override; - void Shutdown(); + bool Initialize() override; + void SubmitFrame(float* frame) override; + void Pause() override; + void Resume() override; + void SetVolume(float volume) override { volume_ = volume; }; + void Shutdown() override; protected: static void SDLCallback(void* userdata, Uint8* stream, int len); @@ -40,11 +46,13 @@ class SDLAudioDriver : public AudioDriver { bool sdl_initialized_ = false; uint8_t sdl_device_channels_ = 0; - static const uint32_t frame_frequency_ = 48000; - static const uint32_t frame_channels_ = 6; - static const uint32_t channel_samples_ = 256; - static const uint32_t frame_samples_ = frame_channels_ * channel_samples_; - static const uint32_t frame_size_ = sizeof(float) * frame_samples_; + float volume_ = 1.0f; + + uint32_t frame_frequency_; + uint32_t frame_channels_; + uint32_t channel_samples_; + uint32_t frame_size_; + bool need_format_conversion_; std::queue frames_queued_ = {}; std::stack frames_unused_ = {}; std::mutex frames_mutex_ = {}; diff --git a/src/xenia/apu/sdl/sdl_audio_system.cc b/src/xenia/apu/sdl/sdl_audio_system.cc index 12a4c4616..1d67ebce6 100644 --- a/src/xenia/apu/sdl/sdl_audio_system.cc +++ b/src/xenia/apu/sdl/sdl_audio_system.cc @@ -31,7 +31,7 @@ X_STATUS SDLAudioSystem::CreateDriver(size_t index, xe::threading::Semaphore* semaphore, AudioDriver** out_driver) { assert_not_null(out_driver); - auto driver = new SDLAudioDriver(memory_, semaphore); + auto driver = new SDLAudioDriver(semaphore); if (!driver->Initialize()) { driver->Shutdown(); return X_STATUS_UNSUCCESSFUL; @@ -41,6 +41,13 @@ X_STATUS SDLAudioSystem::CreateDriver(size_t index, return X_STATUS_SUCCESS; } +AudioDriver* SDLAudioSystem::CreateDriver(xe::threading::Semaphore* semaphore, + uint32_t frequency, uint32_t channels, + bool need_format_conversion) { + return new SDLAudioDriver(semaphore, frequency, channels, + need_format_conversion); +} + void SDLAudioSystem::DestroyDriver(AudioDriver* driver) { assert_not_null(driver); auto sdldriver = dynamic_cast(driver); diff --git a/src/xenia/apu/sdl/sdl_audio_system.h b/src/xenia/apu/sdl/sdl_audio_system.h index 9f9d3108f..16ef6d329 100644 --- a/src/xenia/apu/sdl/sdl_audio_system.h +++ b/src/xenia/apu/sdl/sdl_audio_system.h @@ -27,6 +27,9 @@ class SDLAudioSystem : public AudioSystem { X_RESULT CreateDriver(size_t index, xe::threading::Semaphore* semaphore, AudioDriver** out_driver) override; + AudioDriver* CreateDriver(xe::threading::Semaphore* semaphore, + uint32_t frequency, uint32_t channels, + bool need_format_conversion) override; void DestroyDriver(AudioDriver* driver) override; protected: diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc b/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc index 0c869fae3..382eb23aa 100644 --- a/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc +++ b/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc @@ -43,9 +43,27 @@ class XAudio2AudioDriver::VoiceCallback : public api::IXAudio2VoiceCallback { xe::threading::Semaphore* semaphore_ = nullptr; }; -XAudio2AudioDriver::XAudio2AudioDriver(Memory* memory, - xe::threading::Semaphore* semaphore) - : AudioDriver(memory), semaphore_(semaphore) {} +XAudio2AudioDriver::XAudio2AudioDriver(xe::threading::Semaphore* semaphore, + uint32_t frequency, uint32_t channels, + bool need_format_conversion) + : semaphore_(semaphore), + frame_frequency_(frequency), + frame_channels_(channels), + need_format_conversion_(need_format_conversion) { + switch (frame_channels_) { + case 6: + channel_samples_ = 256; + break; + case 2: + channel_samples_ = 768; + break; + default: + assert_unhandled_case(frame_channels_); + } + frame_size_ = sizeof(float) * frame_channels_ * channel_samples_; + assert_true(frame_size_ <= kFrameSizeMax); + assert_true(!need_format_conversion_ || frame_channels_ == 6); +} XAudio2AudioDriver::~XAudio2AudioDriver() = default; @@ -136,7 +154,7 @@ bool XAudio2AudioDriver::InitializeObjects(Objects& objects) { waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; waveformat.Format.nChannels = frame_channels_; - waveformat.Format.nSamplesPerSec = 48000; + waveformat.Format.nSamplesPerSec = frame_frequency_; waveformat.Format.wBitsPerSample = 32; waveformat.Format.nBlockAlign = (waveformat.Format.nChannels * waveformat.Format.wBitsPerSample) / 8; @@ -184,8 +202,7 @@ bool XAudio2AudioDriver::InitializeObjects(Objects& objects) { return true; } -void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) { - // Process samples! They are big-endian floats. +void XAudio2AudioDriver::SubmitFrame(float* frame) { HRESULT hr; api::XAUDIO2_VOICE_STATE state; @@ -197,13 +214,15 @@ void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) { } assert_true(state.BuffersQueued < frame_count_); - auto input_frame = memory_->TranslateVirtual(frame_ptr); auto output_frame = reinterpret_cast(frames_[current_frame_]); - auto interleave_channels = frame_channels_; - // interleave the data - conversion::sequential_6_BE_to_interleaved_6_LE(output_frame, input_frame, - channel_samples_); + if (need_format_conversion_) { + // Convert planar big endian samples into interleaved little endian. + conversion::sequential_6_BE_to_interleaved_6_LE(output_frame, frame, + channel_samples_); + } else { + memcpy(output_frame, frame, frame_size_); + } api::XAUDIO2_BUFFER buffer; buffer.Flags = 0; @@ -237,6 +256,32 @@ void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) { } } +void XAudio2AudioDriver::Pause() { + if (api_minor_version_ >= 8) { + objects_.api_2_8.pcm_voice->Stop(); + } else { + objects_.api_2_7.pcm_voice->Stop(); + } +} + +void XAudio2AudioDriver::Resume() { + if (api_minor_version_ >= 8) { + objects_.api_2_8.pcm_voice->Start(); + } else { + objects_.api_2_7.pcm_voice->Start(); + } +} + +void XAudio2AudioDriver::SetVolume(float volume) { + if (cvars::mute) return; + + if (api_minor_version_ >= 8) { + objects_.api_2_8.pcm_voice->SetVolume(volume); + } else { + objects_.api_2_7.pcm_voice->SetVolume(volume); + } +} + void XAudio2AudioDriver::Shutdown() { // XAudio2 lifecycle is managed by the MTA thread. if (mta_thread_.joinable()) { diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_driver.h b/src/xenia/apu/xaudio2/xaudio2_audio_driver.h index ca57d615f..1de442375 100644 --- a/src/xenia/apu/xaudio2/xaudio2_audio_driver.h +++ b/src/xenia/apu/xaudio2/xaudio2_audio_driver.h @@ -29,18 +29,24 @@ namespace xaudio2 { class XAudio2AudioDriver : public AudioDriver { public: - XAudio2AudioDriver(Memory* memory, xe::threading::Semaphore* semaphore); + XAudio2AudioDriver(xe::threading::Semaphore* semaphore, + uint32_t frequency = kFrameFrequencyDefault, + uint32_t channels = kFrameChannelsDefault, + bool need_format_conversion = true); ~XAudio2AudioDriver() override; - bool Initialize(); + bool Initialize() override; // Must not be called from COM STA threads as MTA XAudio2 will be used. It's // fine to call this from threads that have never initialized COM as // initializing MTA for any thread implicitly initializes it for all threads // not explicitly requesting STA (until COM is uninitialized all threads that // have initialized MTA). // https://devblogs.microsoft.com/oldnewthing/?p=4613 - void SubmitFrame(uint32_t frame_ptr) override; - void Shutdown(); + void SubmitFrame(float* frame) override; + void Pause() override; + void Resume() override; + void SetVolume(float volume) override; + void Shutdown() override; private: // First CPU (2.8 default). XAUDIO2_ANY_PROCESSOR (2.7 default) steals too @@ -94,12 +100,15 @@ class XAudio2AudioDriver : public AudioDriver { class VoiceCallback; VoiceCallback* voice_callback_ = nullptr; + uint32_t frame_frequency_; + uint32_t frame_channels_; + uint32_t channel_samples_; + uint32_t frame_size_; + bool need_format_conversion_; + static const uint32_t frame_count_ = api::XE_XAUDIO2_MAX_QUEUED_BUFFERS; - static const uint32_t frame_channels_ = 6; - static const uint32_t channel_samples_ = 256; - static const uint32_t frame_samples_ = frame_channels_ * channel_samples_; - static const uint32_t frame_size_ = sizeof(float) * frame_samples_; - float frames_[frame_count_][frame_samples_]; + + float frames_[frame_count_][kFrameSamplesMax]; uint32_t current_frame_ = 0; }; diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_system.cc b/src/xenia/apu/xaudio2/xaudio2_audio_system.cc index e9ddeb6ec..466b878b0 100644 --- a/src/xenia/apu/xaudio2/xaudio2_audio_system.cc +++ b/src/xenia/apu/xaudio2/xaudio2_audio_system.cc @@ -32,7 +32,7 @@ X_STATUS XAudio2AudioSystem::CreateDriver(size_t index, xe::threading::Semaphore* semaphore, AudioDriver** out_driver) { assert_not_null(out_driver); - auto driver = new XAudio2AudioDriver(memory_, semaphore); + auto driver = new XAudio2AudioDriver(semaphore); if (!driver->Initialize()) { driver->Shutdown(); return X_STATUS_UNSUCCESSFUL; @@ -42,6 +42,13 @@ X_STATUS XAudio2AudioSystem::CreateDriver(size_t index, return X_STATUS_SUCCESS; } +AudioDriver* XAudio2AudioSystem::CreateDriver( + xe::threading::Semaphore* semaphore, uint32_t frequency, uint32_t channels, + bool need_format_conversion) { + return new XAudio2AudioDriver(semaphore, frequency, channels, + need_format_conversion); +} + void XAudio2AudioSystem::DestroyDriver(AudioDriver* driver) { assert_not_null(driver); auto xdriver = static_cast(driver); diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_system.h b/src/xenia/apu/xaudio2/xaudio2_audio_system.h index 705f4a0a8..6df42106a 100644 --- a/src/xenia/apu/xaudio2/xaudio2_audio_system.h +++ b/src/xenia/apu/xaudio2/xaudio2_audio_system.h @@ -27,6 +27,9 @@ class XAudio2AudioSystem : public AudioSystem { X_RESULT CreateDriver(size_t index, xe::threading::Semaphore* semaphore, AudioDriver** out_driver) override; + AudioDriver* CreateDriver(xe::threading::Semaphore* semaphore, + uint32_t frequency, uint32_t channels, + bool need_format_conversion) override; void DestroyDriver(AudioDriver* driver) override; protected: diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc index 0cf538ffa..c003f49ea 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc @@ -96,8 +96,9 @@ dword_result_t XAudioSubmitRenderDriverFrame_entry(lpunknown_t driver_ptr, assert_true((driver_ptr.guest_address() & 0xFFFF0000) == 0x41550000); auto audio_system = kernel_state()->emulator()->audio_system(); - audio_system->SubmitFrame(driver_ptr.guest_address() & 0x0000FFFF, - samples_ptr); + auto samples = + kernel_state()->memory()->TranslateVirtual(samples_ptr); + audio_system->SubmitFrame(driver_ptr.guest_address() & 0x0000FFFF, samples); return X_ERROR_SUCCESS; }