[APU] Add ALSA apu on Linux
This commit is contained in:
99
src/xenia/apu/alsa/alsa_audio_driver.h
Normal file
99
src/xenia/apu/alsa/alsa_audio_driver.h
Normal 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_
|
||||
Reference in New Issue
Block a user