[APU] Refactor audio system to work with different frequencies/channel layouts
This commit is contained in:
parent
da6afabf60
commit
5a76cac218
|
@ -12,8 +12,6 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace apu {
|
namespace apu {
|
||||||
|
|
||||||
AudioDriver::AudioDriver(Memory* memory) : memory_(memory) {}
|
|
||||||
|
|
||||||
AudioDriver::~AudioDriver() = default;
|
AudioDriver::~AudioDriver() = default;
|
||||||
|
|
||||||
} // namespace apu
|
} // namespace apu
|
||||||
|
|
|
@ -18,17 +18,22 @@ namespace apu {
|
||||||
|
|
||||||
class AudioDriver {
|
class AudioDriver {
|
||||||
public:
|
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 ~AudioDriver();
|
||||||
|
|
||||||
virtual void SubmitFrame(uint32_t samples_ptr) = 0;
|
virtual bool Initialize() = 0;
|
||||||
|
virtual void Shutdown() = 0;
|
||||||
|
|
||||||
protected:
|
virtual void SubmitFrame(float* samples) = 0;
|
||||||
inline uint8_t* TranslatePhysical(uint32_t guest_address) const {
|
virtual void Pause() = 0;
|
||||||
return memory_->TranslatePhysical(guest_address);
|
virtual void Resume() = 0;
|
||||||
}
|
virtual void SetVolume(float volume) = 0;
|
||||||
|
|
||||||
Memory* memory_ = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace apu
|
} // namespace apu
|
||||||
|
|
|
@ -210,13 +210,13 @@ X_STATUS AudioSystem::RegisterClient(uint32_t callback, uint32_t callback_arg,
|
||||||
return X_STATUS_SUCCESS;
|
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");
|
SCOPE_profile_cpu_f("apu");
|
||||||
|
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
auto global_lock = global_critical_region_.Acquire();
|
||||||
assert_true(index < kMaximumClientCount);
|
assert_true(index < kMaximumClientCount);
|
||||||
assert_true(clients_[index].driver != NULL);
|
assert_true(clients_[index].driver != NULL);
|
||||||
(clients_[index].driver)->SubmitFrame(samples_ptr);
|
(clients_[index].driver)->SubmitFrame(samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioSystem::UnregisterClient(size_t index) {
|
void AudioSystem::UnregisterClient(size_t index) {
|
||||||
|
|
|
@ -42,7 +42,12 @@ class AudioSystem {
|
||||||
X_STATUS RegisterClient(uint32_t callback, uint32_t callback_arg,
|
X_STATUS RegisterClient(uint32_t callback, uint32_t callback_arg,
|
||||||
size_t* out_index);
|
size_t* out_index);
|
||||||
void UnregisterClient(size_t 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 Save(ByteStream* stream);
|
||||||
bool Restore(ByteStream* stream);
|
bool Restore(ByteStream* stream);
|
||||||
|
|
|
@ -30,6 +30,12 @@ X_STATUS NopAudioSystem::CreateDriver(size_t index,
|
||||||
return X_STATUS_NOT_IMPLEMENTED;
|
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(); }
|
void NopAudioSystem::DestroyDriver(AudioDriver* driver) { assert_always(); }
|
||||||
|
|
||||||
} // namespace nop
|
} // namespace nop
|
||||||
|
|
|
@ -27,6 +27,9 @@ class NopAudioSystem : public AudioSystem {
|
||||||
|
|
||||||
X_STATUS CreateDriver(size_t index, xe::threading::Semaphore* semaphore,
|
X_STATUS CreateDriver(size_t index, xe::threading::Semaphore* semaphore,
|
||||||
AudioDriver** out_driver) override;
|
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;
|
void DestroyDriver(AudioDriver* driver) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,27 @@ namespace xe {
|
||||||
namespace apu {
|
namespace apu {
|
||||||
namespace sdl {
|
namespace sdl {
|
||||||
|
|
||||||
SDLAudioDriver::SDLAudioDriver(Memory* memory,
|
SDLAudioDriver::SDLAudioDriver(xe::threading::Semaphore* semaphore,
|
||||||
xe::threading::Semaphore* semaphore)
|
uint32_t frequency, uint32_t channels,
|
||||||
: AudioDriver(memory), semaphore_(semaphore) {}
|
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() {
|
SDLAudioDriver::~SDLAudioDriver() {
|
||||||
assert_true(frames_queued_.empty());
|
assert_true(frames_queued_.empty());
|
||||||
|
@ -58,8 +76,10 @@ bool SDLAudioDriver::Initialize() {
|
||||||
desired_spec.samples = channel_samples_;
|
desired_spec.samples = channel_samples_;
|
||||||
desired_spec.callback = SDLCallback;
|
desired_spec.callback = SDLCallback;
|
||||||
desired_spec.userdata = this;
|
desired_spec.userdata = this;
|
||||||
// Allow the hardware to decide between 5.1 and stereo
|
// Allow the hardware to decide between 5.1 and stereo,
|
||||||
int allowed_change = SDL_AUDIO_ALLOW_CHANNELS_CHANGE;
|
// unless the input is stereo
|
||||||
|
int allowed_change =
|
||||||
|
frame_channels_ != 2 ? SDL_AUDIO_ALLOW_CHANNELS_CHANGE : 0;
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
sdl_device_id_ = SDL_OpenAudioDevice(nullptr, 0, &desired_spec,
|
sdl_device_id_ = SDL_OpenAudioDevice(nullptr, 0, &desired_spec,
|
||||||
&obtained_spec, allowed_change);
|
&obtained_spec, allowed_change);
|
||||||
|
@ -86,20 +106,19 @@ bool SDLAudioDriver::Initialize() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLAudioDriver::SubmitFrame(uint32_t frame_ptr) {
|
void SDLAudioDriver::SubmitFrame(float* frame) {
|
||||||
const auto input_frame = memory_->TranslateVirtual<float*>(frame_ptr);
|
|
||||||
float* output_frame;
|
float* output_frame;
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> guard(frames_mutex_);
|
std::unique_lock<std::mutex> guard(frames_mutex_);
|
||||||
if (frames_unused_.empty()) {
|
if (frames_unused_.empty()) {
|
||||||
output_frame = new float[frame_samples_];
|
output_frame = new float[frame_channels_ * channel_samples_];
|
||||||
} else {
|
} else {
|
||||||
output_frame = frames_unused_.top();
|
output_frame = frames_unused_.top();
|
||||||
frames_unused_.pop();
|
frames_unused_.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memcpy(output_frame, input_frame, frame_samples_ * sizeof(float));
|
std::memcpy(output_frame, frame, frame_size_);
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> guard(frames_mutex_);
|
std::unique_lock<std::mutex> 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() {
|
void SDLAudioDriver::Shutdown() {
|
||||||
if (sdl_device_id_ > 0) {
|
if (sdl_device_id_ > 0) {
|
||||||
SDL_CloseAudioDevice(sdl_device_id_);
|
SDL_CloseAudioDevice(sdl_device_id_);
|
||||||
|
@ -134,8 +157,8 @@ void SDLAudioDriver::SDLCallback(void* userdata, Uint8* stream, int len) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto driver = static_cast<SDLAudioDriver*>(userdata);
|
const auto driver = static_cast<SDLAudioDriver*>(userdata);
|
||||||
assert_true(len ==
|
assert_true(len == sizeof(float) * driver->channel_samples_ *
|
||||||
sizeof(float) * channel_samples_ * driver->sdl_device_channels_);
|
driver->sdl_device_channels_);
|
||||||
|
|
||||||
std::unique_lock<std::mutex> guard(driver->frames_mutex_);
|
std::unique_lock<std::mutex> guard(driver->frames_mutex_);
|
||||||
if (driver->frames_queued_.empty()) {
|
if (driver->frames_queued_.empty()) {
|
||||||
|
@ -145,20 +168,32 @@ void SDLAudioDriver::SDLCallback(void* userdata, Uint8* stream, int len) {
|
||||||
driver->frames_queued_.pop();
|
driver->frames_queued_.pop();
|
||||||
if (cvars::mute) {
|
if (cvars::mute) {
|
||||||
std::memset(stream, 0, len);
|
std::memset(stream, 0, len);
|
||||||
} else {
|
} else if (driver->need_format_conversion_) {
|
||||||
switch (driver->sdl_device_channels_) {
|
switch (driver->sdl_device_channels_) {
|
||||||
case 2:
|
case 2:
|
||||||
conversion::sequential_6_BE_to_interleaved_2_LE(
|
conversion::sequential_6_BE_to_interleaved_2_LE(
|
||||||
reinterpret_cast<float*>(stream), buffer, channel_samples_);
|
reinterpret_cast<float*>(stream), buffer,
|
||||||
|
driver->channel_samples_);
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
conversion::sequential_6_BE_to_interleaved_6_LE(
|
conversion::sequential_6_BE_to_interleaved_6_LE(
|
||||||
reinterpret_cast<float*>(stream), buffer, channel_samples_);
|
reinterpret_cast<float*>(stream), buffer,
|
||||||
|
driver->channel_samples_);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert_unhandled_case(driver->sdl_device_channels_);
|
assert_unhandled_case(driver->sdl_device_channels_);
|
||||||
break;
|
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<Uint8*>(buffer), AUDIO_F32, len,
|
||||||
|
static_cast<int>(driver->volume_ * SDL_MIX_MAXVOLUME));
|
||||||
|
} else {
|
||||||
|
std::memcpy(stream, buffer, len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
driver->frames_unused_.push(buffer);
|
driver->frames_unused_.push(buffer);
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,18 @@ namespace sdl {
|
||||||
|
|
||||||
class SDLAudioDriver : public AudioDriver {
|
class SDLAudioDriver : public AudioDriver {
|
||||||
public:
|
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;
|
~SDLAudioDriver() override;
|
||||||
|
|
||||||
bool Initialize();
|
bool Initialize() override;
|
||||||
void SubmitFrame(uint32_t frame_ptr) override;
|
void SubmitFrame(float* frame) override;
|
||||||
void Shutdown();
|
void Pause() override;
|
||||||
|
void Resume() override;
|
||||||
|
void SetVolume(float volume) override { volume_ = volume; };
|
||||||
|
void Shutdown() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void SDLCallback(void* userdata, Uint8* stream, int len);
|
static void SDLCallback(void* userdata, Uint8* stream, int len);
|
||||||
|
@ -40,11 +46,13 @@ class SDLAudioDriver : public AudioDriver {
|
||||||
bool sdl_initialized_ = false;
|
bool sdl_initialized_ = false;
|
||||||
uint8_t sdl_device_channels_ = 0;
|
uint8_t sdl_device_channels_ = 0;
|
||||||
|
|
||||||
static const uint32_t frame_frequency_ = 48000;
|
float volume_ = 1.0f;
|
||||||
static const uint32_t frame_channels_ = 6;
|
|
||||||
static const uint32_t channel_samples_ = 256;
|
uint32_t frame_frequency_;
|
||||||
static const uint32_t frame_samples_ = frame_channels_ * channel_samples_;
|
uint32_t frame_channels_;
|
||||||
static const uint32_t frame_size_ = sizeof(float) * frame_samples_;
|
uint32_t channel_samples_;
|
||||||
|
uint32_t frame_size_;
|
||||||
|
bool need_format_conversion_;
|
||||||
std::queue<float*> frames_queued_ = {};
|
std::queue<float*> frames_queued_ = {};
|
||||||
std::stack<float*> frames_unused_ = {};
|
std::stack<float*> frames_unused_ = {};
|
||||||
std::mutex frames_mutex_ = {};
|
std::mutex frames_mutex_ = {};
|
||||||
|
|
|
@ -31,7 +31,7 @@ X_STATUS SDLAudioSystem::CreateDriver(size_t index,
|
||||||
xe::threading::Semaphore* semaphore,
|
xe::threading::Semaphore* semaphore,
|
||||||
AudioDriver** out_driver) {
|
AudioDriver** out_driver) {
|
||||||
assert_not_null(out_driver);
|
assert_not_null(out_driver);
|
||||||
auto driver = new SDLAudioDriver(memory_, semaphore);
|
auto driver = new SDLAudioDriver(semaphore);
|
||||||
if (!driver->Initialize()) {
|
if (!driver->Initialize()) {
|
||||||
driver->Shutdown();
|
driver->Shutdown();
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
|
@ -41,6 +41,13 @@ X_STATUS SDLAudioSystem::CreateDriver(size_t index,
|
||||||
return X_STATUS_SUCCESS;
|
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) {
|
void SDLAudioSystem::DestroyDriver(AudioDriver* driver) {
|
||||||
assert_not_null(driver);
|
assert_not_null(driver);
|
||||||
auto sdldriver = dynamic_cast<SDLAudioDriver*>(driver);
|
auto sdldriver = dynamic_cast<SDLAudioDriver*>(driver);
|
||||||
|
|
|
@ -27,6 +27,9 @@ class SDLAudioSystem : public AudioSystem {
|
||||||
|
|
||||||
X_RESULT CreateDriver(size_t index, xe::threading::Semaphore* semaphore,
|
X_RESULT CreateDriver(size_t index, xe::threading::Semaphore* semaphore,
|
||||||
AudioDriver** out_driver) override;
|
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;
|
void DestroyDriver(AudioDriver* driver) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -43,9 +43,27 @@ class XAudio2AudioDriver::VoiceCallback : public api::IXAudio2VoiceCallback {
|
||||||
xe::threading::Semaphore* semaphore_ = nullptr;
|
xe::threading::Semaphore* semaphore_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
XAudio2AudioDriver::XAudio2AudioDriver(Memory* memory,
|
XAudio2AudioDriver::XAudio2AudioDriver(xe::threading::Semaphore* semaphore,
|
||||||
xe::threading::Semaphore* semaphore)
|
uint32_t frequency, uint32_t channels,
|
||||||
: AudioDriver(memory), semaphore_(semaphore) {}
|
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;
|
XAudio2AudioDriver::~XAudio2AudioDriver() = default;
|
||||||
|
|
||||||
|
@ -136,7 +154,7 @@ bool XAudio2AudioDriver::InitializeObjects(Objects& objects) {
|
||||||
|
|
||||||
waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||||
waveformat.Format.nChannels = frame_channels_;
|
waveformat.Format.nChannels = frame_channels_;
|
||||||
waveformat.Format.nSamplesPerSec = 48000;
|
waveformat.Format.nSamplesPerSec = frame_frequency_;
|
||||||
waveformat.Format.wBitsPerSample = 32;
|
waveformat.Format.wBitsPerSample = 32;
|
||||||
waveformat.Format.nBlockAlign =
|
waveformat.Format.nBlockAlign =
|
||||||
(waveformat.Format.nChannels * waveformat.Format.wBitsPerSample) / 8;
|
(waveformat.Format.nChannels * waveformat.Format.wBitsPerSample) / 8;
|
||||||
|
@ -184,8 +202,7 @@ bool XAudio2AudioDriver::InitializeObjects(Objects& objects) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) {
|
void XAudio2AudioDriver::SubmitFrame(float* frame) {
|
||||||
// Process samples! They are big-endian floats.
|
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
api::XAUDIO2_VOICE_STATE state;
|
api::XAUDIO2_VOICE_STATE state;
|
||||||
|
@ -197,13 +214,15 @@ void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) {
|
||||||
}
|
}
|
||||||
assert_true(state.BuffersQueued < frame_count_);
|
assert_true(state.BuffersQueued < frame_count_);
|
||||||
|
|
||||||
auto input_frame = memory_->TranslateVirtual<float*>(frame_ptr);
|
|
||||||
auto output_frame = reinterpret_cast<float*>(frames_[current_frame_]);
|
auto output_frame = reinterpret_cast<float*>(frames_[current_frame_]);
|
||||||
auto interleave_channels = frame_channels_;
|
|
||||||
|
|
||||||
// interleave the data
|
if (need_format_conversion_) {
|
||||||
conversion::sequential_6_BE_to_interleaved_6_LE(output_frame, input_frame,
|
// Convert planar big endian samples into interleaved little endian.
|
||||||
channel_samples_);
|
conversion::sequential_6_BE_to_interleaved_6_LE(output_frame, frame,
|
||||||
|
channel_samples_);
|
||||||
|
} else {
|
||||||
|
memcpy(output_frame, frame, frame_size_);
|
||||||
|
}
|
||||||
|
|
||||||
api::XAUDIO2_BUFFER buffer;
|
api::XAUDIO2_BUFFER buffer;
|
||||||
buffer.Flags = 0;
|
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() {
|
void XAudio2AudioDriver::Shutdown() {
|
||||||
// XAudio2 lifecycle is managed by the MTA thread.
|
// XAudio2 lifecycle is managed by the MTA thread.
|
||||||
if (mta_thread_.joinable()) {
|
if (mta_thread_.joinable()) {
|
||||||
|
|
|
@ -29,18 +29,24 @@ namespace xaudio2 {
|
||||||
|
|
||||||
class XAudio2AudioDriver : public AudioDriver {
|
class XAudio2AudioDriver : public AudioDriver {
|
||||||
public:
|
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;
|
~XAudio2AudioDriver() override;
|
||||||
|
|
||||||
bool Initialize();
|
bool Initialize() override;
|
||||||
// Must not be called from COM STA threads as MTA XAudio2 will be used. It's
|
// 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
|
// fine to call this from threads that have never initialized COM as
|
||||||
// initializing MTA for any thread implicitly initializes it for all threads
|
// initializing MTA for any thread implicitly initializes it for all threads
|
||||||
// not explicitly requesting STA (until COM is uninitialized all threads that
|
// not explicitly requesting STA (until COM is uninitialized all threads that
|
||||||
// have initialized MTA).
|
// have initialized MTA).
|
||||||
// https://devblogs.microsoft.com/oldnewthing/?p=4613
|
// https://devblogs.microsoft.com/oldnewthing/?p=4613
|
||||||
void SubmitFrame(uint32_t frame_ptr) override;
|
void SubmitFrame(float* frame) override;
|
||||||
void Shutdown();
|
void Pause() override;
|
||||||
|
void Resume() override;
|
||||||
|
void SetVolume(float volume) override;
|
||||||
|
void Shutdown() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// First CPU (2.8 default). XAUDIO2_ANY_PROCESSOR (2.7 default) steals too
|
// First CPU (2.8 default). XAUDIO2_ANY_PROCESSOR (2.7 default) steals too
|
||||||
|
@ -94,12 +100,15 @@ class XAudio2AudioDriver : public AudioDriver {
|
||||||
class VoiceCallback;
|
class VoiceCallback;
|
||||||
VoiceCallback* voice_callback_ = nullptr;
|
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_count_ = api::XE_XAUDIO2_MAX_QUEUED_BUFFERS;
|
||||||
static const uint32_t frame_channels_ = 6;
|
|
||||||
static const uint32_t channel_samples_ = 256;
|
float frames_[frame_count_][kFrameSamplesMax];
|
||||||
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_];
|
|
||||||
uint32_t current_frame_ = 0;
|
uint32_t current_frame_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ X_STATUS XAudio2AudioSystem::CreateDriver(size_t index,
|
||||||
xe::threading::Semaphore* semaphore,
|
xe::threading::Semaphore* semaphore,
|
||||||
AudioDriver** out_driver) {
|
AudioDriver** out_driver) {
|
||||||
assert_not_null(out_driver);
|
assert_not_null(out_driver);
|
||||||
auto driver = new XAudio2AudioDriver(memory_, semaphore);
|
auto driver = new XAudio2AudioDriver(semaphore);
|
||||||
if (!driver->Initialize()) {
|
if (!driver->Initialize()) {
|
||||||
driver->Shutdown();
|
driver->Shutdown();
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
|
@ -42,6 +42,13 @@ X_STATUS XAudio2AudioSystem::CreateDriver(size_t index,
|
||||||
return X_STATUS_SUCCESS;
|
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) {
|
void XAudio2AudioSystem::DestroyDriver(AudioDriver* driver) {
|
||||||
assert_not_null(driver);
|
assert_not_null(driver);
|
||||||
auto xdriver = static_cast<XAudio2AudioDriver*>(driver);
|
auto xdriver = static_cast<XAudio2AudioDriver*>(driver);
|
||||||
|
|
|
@ -27,6 +27,9 @@ class XAudio2AudioSystem : public AudioSystem {
|
||||||
|
|
||||||
X_RESULT CreateDriver(size_t index, xe::threading::Semaphore* semaphore,
|
X_RESULT CreateDriver(size_t index, xe::threading::Semaphore* semaphore,
|
||||||
AudioDriver** out_driver) override;
|
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;
|
void DestroyDriver(AudioDriver* driver) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -96,8 +96,9 @@ dword_result_t XAudioSubmitRenderDriverFrame_entry(lpunknown_t driver_ptr,
|
||||||
assert_true((driver_ptr.guest_address() & 0xFFFF0000) == 0x41550000);
|
assert_true((driver_ptr.guest_address() & 0xFFFF0000) == 0x41550000);
|
||||||
|
|
||||||
auto audio_system = kernel_state()->emulator()->audio_system();
|
auto audio_system = kernel_state()->emulator()->audio_system();
|
||||||
audio_system->SubmitFrame(driver_ptr.guest_address() & 0x0000FFFF,
|
auto samples =
|
||||||
samples_ptr);
|
kernel_state()->memory()->TranslateVirtual<float*>(samples_ptr);
|
||||||
|
audio_system->SubmitFrame(driver_ptr.guest_address() & 0x0000FFFF, samples);
|
||||||
|
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue