100 lines
3.2 KiB
C++
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_
|