This repository has been archived on 2025-08-26. You can view files and clone it, but cannot push or open issues or pull requests.
Files
enginex-mr_series-sherpa-onnx/sherpa-onnx/csrc/alsa-play.cc
2023-12-13 22:28:03 +08:00

151 lines
3.9 KiB
C++

// sherpa-onnx/csrc/alsa-play.cc
//
// Copyright (c) 2022-2023 Xiaomi Corporation
#ifdef SHERPA_ONNX_ENABLE_ALSA
#include "sherpa-onnx/csrc/alsa-play.h"
#include <algorithm>
namespace sherpa_onnx {
AlsaPlay::AlsaPlay(const char *device_name, int32_t sample_rate) {
int32_t err = snd_pcm_open(&handle_, device_name, SND_PCM_STREAM_PLAYBACK, 0);
if (err) {
fprintf(stderr, "Unable to open: %s. %s\n", device_name, snd_strerror(err));
exit(-1);
}
SetParameters(sample_rate);
}
AlsaPlay::~AlsaPlay() {
if (handle_) {
int32_t err = snd_pcm_close(handle_);
if (err < 0) {
printf("Failed to close pcm: %s\n", snd_strerror(err));
}
}
}
void AlsaPlay::SetParameters(int32_t sample_rate) {
// set the following parameters
// 1. sample_rate
// 2. sample format: int16_t
// 3. num_channels: 1
snd_pcm_hw_params_t *params;
snd_pcm_hw_params_alloca(&params);
snd_pcm_hw_params_any(handle_, params);
int32_t err = snd_pcm_hw_params_set_access(handle_, params,
SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0) {
printf("SND_PCM_ACCESS_RW_INTERLEAVED is not supported: %s\n",
snd_strerror(err));
exit(-1);
}
err = snd_pcm_hw_params_set_format(handle_, params, SND_PCM_FORMAT_S16_LE);
if (err < 0) {
printf("Can't set format to 16-bit: %s\n", snd_strerror(err));
exit(-1);
}
err = snd_pcm_hw_params_set_channels(handle_, params, 1);
if (err < 0) {
printf("Can't set channel number to 1: %s\n", snd_strerror(err));
}
uint32_t rate = sample_rate;
err = snd_pcm_hw_params_set_rate_near(handle_, params, &rate, 0);
if (err < 0) {
printf("Can't set rate to %d. %s\n", rate, snd_strerror(err));
}
err = snd_pcm_hw_params(handle_, params);
if (err < 0) {
printf("Can't set hardware parameters. %s\n", snd_strerror(err));
exit(-1);
}
uint32_t tmp;
snd_pcm_hw_params_get_rate(params, &tmp, 0);
int32_t actual_sample_rate = tmp;
if (actual_sample_rate != sample_rate) {
fprintf(stderr,
"Creating a resampler:\n"
" in_sample_rate: %d\n"
" output_sample_rate: %d\n",
sample_rate, actual_sample_rate);
float min_freq = std::min(actual_sample_rate, sample_rate);
float lowpass_cutoff = 0.99 * 0.5 * min_freq;
int32_t lowpass_filter_width = 6;
resampler_ = std::make_unique<LinearResample>(
sample_rate, actual_sample_rate, lowpass_cutoff, lowpass_filter_width);
}
snd_pcm_uframes_t frames;
snd_pcm_hw_params_get_period_size(params, &frames, 0);
buf_.resize(frames);
}
void AlsaPlay::Play(const std::vector<float> &samples) {
std::vector<float> tmp;
const float *p = samples.data();
int32_t num_samples = samples.size();
if (resampler_) {
resampler_->Resample(samples.data(), samples.size(), false, &tmp);
p = tmp.data();
num_samples = tmp.size();
}
int32_t frames = buf_.size();
int32_t i = 0;
for (; i + frames < num_samples; i += frames) {
for (int32_t k = 0; k != frames; ++k) {
buf_[k] = p[i + k] * 32767;
}
int32_t err = snd_pcm_writei(handle_, buf_.data(), frames);
if (err == -EPIPE) {
printf("XRUN.\n");
snd_pcm_prepare(handle_);
} else if (err < 0) {
printf("Can't write to PCM device: %s\n", snd_strerror(err));
exit(-1);
}
}
if (i < num_samples) {
for (int32_t k = 0; k + i < num_samples; ++k) {
buf_[k] = p[i + k] * 32767;
}
int32_t err = snd_pcm_writei(handle_, buf_.data(), num_samples - i);
if (err == -EPIPE) {
printf("XRUN.\n");
snd_pcm_prepare(handle_);
} else if (err < 0) {
printf("Can't write to PCM device: %s\n", snd_strerror(err));
exit(-1);
}
}
}
void AlsaPlay::Drain() {
int32_t err = snd_pcm_drain(handle_);
if (err < 0) {
printf("Failed to drain pcm. %s\n", snd_strerror(err));
}
}
} // namespace sherpa_onnx
#endif // SHERPA_ONNX_ENABLE_ALSA