/** ****************************************************************************** * 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 #include #include #include #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 worker_thread_; std::atomic running_{false}; std::atomic paused_{false}; std::atomic volume_{1.0f}; // Ring buffer for frames (larger size to reduce underruns) static constexpr size_t kRingBufferSize = 32; float* ring_buffer_[kRingBufferSize] = {}; std::atomic read_index_{0}; std::atomic 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 conversion_buffer_; // Time scaling / resampling state std::unique_ptr resample_buffer_; double resample_frac_position_ = 0.0; }; } // namespace alsa } // namespace apu } // namespace xe #endif // XENIA_APU_ALSA_ALSA_AUDIO_DRIVER_H_