[APU] Add ALSA apu on Linux

This commit is contained in:
Herman S.
2025-09-27 18:38:27 +09:00
parent 48c13be2d5
commit fade0cd889
9 changed files with 929 additions and 6 deletions

View File

@@ -0,0 +1,99 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2025. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_APU_ALSA_ALSA_AUDIO_DRIVER_H_
#define XENIA_APU_ALSA_ALSA_AUDIO_DRIVER_H_
#include <alsa/asoundlib.h>
#include <atomic>
#include <memory>
#include <thread>
#include "xenia/apu/audio_driver.h"
#include "xenia/base/threading.h"
namespace xe {
namespace apu {
namespace alsa {
class ALSAAudioDriver : public AudioDriver {
public:
ALSAAudioDriver(xe::threading::Semaphore* semaphore,
uint32_t frequency = kFrameFrequencyDefault,
uint32_t channels = kFrameChannelsDefault,
bool need_format_conversion = true);
~ALSAAudioDriver() override;
bool Initialize() override;
void SubmitFrame(float* frame) override;
void Pause() override;
void Resume() override;
void SetVolume(float volume) override { volume_.store(volume); }
void Shutdown() override;
private:
void WorkerThread();
bool SetupAlsaDevice();
bool RecoverFromUnderrun(int err);
void ConvertChannels(const float* input, float* output,
size_t channel_samples);
size_t ResampleFrame(const float* input, float* output,
size_t input_frame_count, size_t output_capacity_frames,
float frequency_ratio, uint32_t channels);
void ApplyVolume(float* buffer, size_t sample_count, float volume);
xe::threading::Semaphore* semaphore_ = nullptr;
// ALSA handles
snd_pcm_t* pcm_handle_ = nullptr;
snd_pcm_hw_params_t* hw_params_ = nullptr;
snd_pcm_sw_params_t* sw_params_ = nullptr;
// Device configuration
uint32_t frame_frequency_;
uint32_t frame_channels_;
uint32_t channel_samples_;
uint32_t frame_size_;
bool need_format_conversion_;
// Output configuration (may differ from input)
uint32_t output_channels_ = 0;
snd_pcm_uframes_t period_size_ = 0;
snd_pcm_uframes_t buffer_size_ = 0;
// Threading
std::unique_ptr<std::thread> worker_thread_;
std::atomic<bool> running_{false};
std::atomic<bool> paused_{false};
std::atomic<float> volume_{1.0f};
// Ring buffer for frames (larger size to reduce underruns)
static constexpr size_t kRingBufferSize = 32;
float* ring_buffer_[kRingBufferSize] = {};
std::atomic<size_t> read_index_{0};
std::atomic<size_t> write_index_{0};
// ALSA period and buffer configuration
static constexpr snd_pcm_uframes_t kPeriodSize51 = 512;
static constexpr snd_pcm_uframes_t kPeriodSizeStereo = 1024;
static constexpr size_t kBufferPeriods = 4;
// Conversion buffer
std::unique_ptr<float[]> conversion_buffer_;
// Time scaling / resampling state
std::unique_ptr<float[]> resample_buffer_;
double resample_frac_position_ = 0.0;
};
} // namespace alsa
} // namespace apu
} // namespace xe
#endif // XENIA_APU_ALSA_ALSA_AUDIO_DRIVER_H_