Files
Xenia-Canary/src/xenia/apu/alsa/alsa_audio_driver.h
2026-02-17 23:46:16 +09:00

100 lines
3.2 KiB
C++

/**
******************************************************************************
* 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_