-
Notifications
You must be signed in to change notification settings - Fork 445
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Play generated audio using alsa for TTS (#482)
- Loading branch information
1 parent
9829d7c
commit b18812c
Showing
7 changed files
with
465 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
// 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(¶ms); | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// sherpa-onnx/csrc/alsa-play.h | ||
// | ||
// Copyright (c) 2022-2023 Xiaomi Corporation | ||
|
||
#ifndef SHERPA_ONNX_CSRC_ALSA_PLAY_H_ | ||
#define SHERPA_ONNX_CSRC_ALSA_PLAY_H_ | ||
|
||
#include <cstdint> | ||
#include <memory> | ||
#include <vector> | ||
|
||
#include "alsa/asoundlib.h" | ||
#include "sherpa-onnx/csrc/resample.h" | ||
|
||
namespace sherpa_onnx { | ||
|
||
class AlsaPlay { | ||
public: | ||
AlsaPlay(const char *device_name, int32_t sample_rate); | ||
~AlsaPlay(); | ||
void Play(const std::vector<float> &samples); | ||
|
||
// wait for all the samples to be played | ||
void Drain(); | ||
|
||
private: | ||
void SetParameters(int32_t sample_rate); | ||
|
||
private: | ||
snd_pcm_t *handle_ = nullptr; | ||
std::unique_ptr<LinearResample> resampler_; | ||
std::vector<int16_t> buf_; | ||
}; | ||
|
||
} // namespace sherpa_onnx | ||
|
||
#endif // SHERPA_ONNX_CSRC_ALSA_PLAY_H_ |
Oops, something went wrong.