[APU] Refactor audio system to work with different frequencies/channel layouts
This commit is contained in:
@@ -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<float*>(frame_ptr);
|
||||
auto output_frame = reinterpret_cast<float*>(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()) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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<XAudio2AudioDriver*>(driver);
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user