将 snd_pcm_sw_params_set_stop_threshold 设置为边界,在 snd_pcm_writei 上仍会出现资源不足
Set snd_pcm_sw_params_set_stop_threshold to boundary, still getting underrun on snd_pcm_writei
问题说明了一切。我在这里兜圈子。我将 snd_pcm_sw_params_set_stop_threshold 设置为边界(为了好玩也设置为零),但我仍然在 snd_pcm_writei 上遇到缓冲区欠载错误。我不明白为什么。文档对此非常清楚:
If the stop threshold is equal to boundary (also software parameter - sw_param) then automatic stop will be disabled
这是一个可重现性最低的示例:
#include <alsa/asoundlib.h>
#include <iostream>
#define AUDIO_DEV "default"
#define AC_FRAME_SIZE 960
#define AC_SAMPLE_RATE 48000
#define AC_CHANNELS 2
//BUILD g++ -o main main.cpp -lasound
using namespace std;
int main() {
int err;
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
snd_pcm_uframes_t boundary;
snd_pcm_sw_params_t *sw;
snd_pcm_hw_params_t *params;
unsigned int s_rate;
unsigned int buffer_time;
snd_pcm_uframes_t f_size;
unsigned char buffer[AC_FRAME_SIZE * 2];
int rc;
for (i = 0; i < sizeof(buffer); i++)
buffer[i] = random() & 0xff;
if ((err = snd_pcm_open(&handle, AUDIO_DEV, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
cout << "open error " << snd_strerror(err) << endl;
return 0;
}
s_rate = AC_SAMPLE_RATE;
f_size = AC_FRAME_SIZE;
buffer_time = 2500;
cout << s_rate << " " << f_size << endl;
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(handle, params);
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_channels(handle, params, AC_CHANNELS);
snd_pcm_hw_params_set_rate_near(handle, params, &s_rate, 0);
snd_pcm_hw_params_set_period_size_near(handle, params, &f_size, 0);
cout << s_rate << " " << f_size << endl;
rc = snd_pcm_hw_params(handle, params);
if (rc < 0) {
cout << "open error " << snd_strerror(err) << endl;
return 0;
}
snd_pcm_sw_params_alloca(&sw);
snd_pcm_sw_params_current(handle, sw);
snd_pcm_sw_params_get_boundary(sw, &boundary);
snd_pcm_sw_params_set_stop_threshold(handle, sw, boundary);
rc = snd_pcm_sw_params(handle, sw);
if (rc < 0) {
cout << "open error " << snd_strerror(err) << endl;
return 0;
}
snd_pcm_sw_params_current(handle, sw);
snd_pcm_sw_params_get_stop_threshold(sw, &boundary);
cout << "VALUE " << boundary << endl;
for (i = 0; i < 1600; i++) {
usleep(100 * 1000);
frames = snd_pcm_writei(handle, buffer, f_size);
if (frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
cout << "open error " << snd_strerror(frames) << endl;
break;
}
}
return 0;
}
好的,我明白了。对于 运行 陷入此问题且将 pipwire 或 pulse(或任何其他第三方非 alsa 音频卡)启用为“默认”卡的任何人,解决方案是不直接使用 pipwire 或 pulse。似乎 snd_pcm_sw_params_set_stop_threshold 在 pipewire/pulseaudio 中没有正确实现。您会注意到,如果您禁用 pipwire 或 pulse,此代码将 运行 完全按照您希望的方式 运行。
这是禁用 pulseaudio 的方法(这是我系统上的问题):
systemctl --user stop pulseaudio.socket
systemctl --user stop pulseaudio.service
尽管更好的解决方案是将 AUDIO_DEV 设置为直接写入 alsa 卡。您可以通过 运行ning aplay -L 找到这些卡的名称。但在 95% 的情况下,将我的示例代码中的 AUDIO_DEV 更新为以下内容:
#define AUDIO_DEV "hw:0,0"
通常会解决问题。
问题说明了一切。我在这里兜圈子。我将 snd_pcm_sw_params_set_stop_threshold 设置为边界(为了好玩也设置为零),但我仍然在 snd_pcm_writei 上遇到缓冲区欠载错误。我不明白为什么。文档对此非常清楚:
If the stop threshold is equal to boundary (also software parameter - sw_param) then automatic stop will be disabled
这是一个可重现性最低的示例:
#include <alsa/asoundlib.h>
#include <iostream>
#define AUDIO_DEV "default"
#define AC_FRAME_SIZE 960
#define AC_SAMPLE_RATE 48000
#define AC_CHANNELS 2
//BUILD g++ -o main main.cpp -lasound
using namespace std;
int main() {
int err;
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
snd_pcm_uframes_t boundary;
snd_pcm_sw_params_t *sw;
snd_pcm_hw_params_t *params;
unsigned int s_rate;
unsigned int buffer_time;
snd_pcm_uframes_t f_size;
unsigned char buffer[AC_FRAME_SIZE * 2];
int rc;
for (i = 0; i < sizeof(buffer); i++)
buffer[i] = random() & 0xff;
if ((err = snd_pcm_open(&handle, AUDIO_DEV, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
cout << "open error " << snd_strerror(err) << endl;
return 0;
}
s_rate = AC_SAMPLE_RATE;
f_size = AC_FRAME_SIZE;
buffer_time = 2500;
cout << s_rate << " " << f_size << endl;
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(handle, params);
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_channels(handle, params, AC_CHANNELS);
snd_pcm_hw_params_set_rate_near(handle, params, &s_rate, 0);
snd_pcm_hw_params_set_period_size_near(handle, params, &f_size, 0);
cout << s_rate << " " << f_size << endl;
rc = snd_pcm_hw_params(handle, params);
if (rc < 0) {
cout << "open error " << snd_strerror(err) << endl;
return 0;
}
snd_pcm_sw_params_alloca(&sw);
snd_pcm_sw_params_current(handle, sw);
snd_pcm_sw_params_get_boundary(sw, &boundary);
snd_pcm_sw_params_set_stop_threshold(handle, sw, boundary);
rc = snd_pcm_sw_params(handle, sw);
if (rc < 0) {
cout << "open error " << snd_strerror(err) << endl;
return 0;
}
snd_pcm_sw_params_current(handle, sw);
snd_pcm_sw_params_get_stop_threshold(sw, &boundary);
cout << "VALUE " << boundary << endl;
for (i = 0; i < 1600; i++) {
usleep(100 * 1000);
frames = snd_pcm_writei(handle, buffer, f_size);
if (frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
cout << "open error " << snd_strerror(frames) << endl;
break;
}
}
return 0;
}
好的,我明白了。对于 运行 陷入此问题且将 pipwire 或 pulse(或任何其他第三方非 alsa 音频卡)启用为“默认”卡的任何人,解决方案是不直接使用 pipwire 或 pulse。似乎 snd_pcm_sw_params_set_stop_threshold 在 pipewire/pulseaudio 中没有正确实现。您会注意到,如果您禁用 pipwire 或 pulse,此代码将 运行 完全按照您希望的方式 运行。
这是禁用 pulseaudio 的方法(这是我系统上的问题):
systemctl --user stop pulseaudio.socket
systemctl --user stop pulseaudio.service
尽管更好的解决方案是将 AUDIO_DEV 设置为直接写入 alsa 卡。您可以通过 运行ning aplay -L 找到这些卡的名称。但在 95% 的情况下,将我的示例代码中的 AUDIO_DEV 更新为以下内容:
#define AUDIO_DEV "hw:0,0"
通常会解决问题。