为什么 ALSA snd_pcm_hw_params_get_buffer_size 会对本地堆栈帧分配的大小做出反应?
Why does ALSA snd_pcm_hw_params_get_buffer_size react to the size of local stack frame allocations?
发行版:Debian 9.11
编译器:gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
ALSA 版本:高级 Linux 声音架构驱动版本 k4.9.0-11-amd64.
请考虑以下可重现的最小示例(现已更新,具有更多错误处理):
/**
* Trying to set some parameters for ALSA.
*
* Code from: http://equalarea.com/paul/alsa-audio.html
*
*/
#include <alsa/asoundlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#define PCM_DEVICE "default"
int main(int argc, char **argv)
{
unsigned int err;
snd_pcm_t *pcm_handle;
snd_pcm_hw_params_t *params;
if((err = snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
printf("Error: Can't open \"%s\" PCM device. %s\n", PCM_DEVICE, snd_strerror(err));
return 1;
}
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(pcm_handle, params);
if((err = snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
printf("Error: Can't set interleaved mode. %s\n", snd_strerror(err));
return 1;
}
if((err = snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_FLOAT)) < 0) {
printf("Error: Can't set format. %s\n", snd_strerror(err));
return 1;
}
int channels = 2;
if((err = snd_pcm_hw_params_set_channels(pcm_handle, params, channels)) < 0) {
printf("Error: Can't set channels number. %s\n", snd_strerror(err));
return 1;
}
int rate = 44100;
if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0)) < 0) {
printf("Error: Can't set rate. %s\n", snd_strerror(err));
return 1;
}
printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle));
printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));
int ch;
if((err = snd_pcm_hw_params_get_channels(params, &ch)) < 0) {
printf("Error: Unable to get the number of channels: %s\n", snd_strerror(err));
return 1;
}
if(ch > 1) {
printf("Channels: %i, stereo\n", ch);
}
else if(ch == 1) {
printf("Channels: %i, mono\n", ch);
}
else {
printf("Error: Unknown number of channels (%d).\n", ch);
return 1;
}
if((err = snd_pcm_hw_params_get_rate(params, &ch, 0)) < 0) {
printf("Error: Unable to get the rate: %s\n", snd_strerror(err));
return 1;
}
printf("Rate: %d bps\n", ch);
int dir;
snd_pcm_uframes_t period_size = 1024;
printf("Attempting to set the period size to %d\n", period_size);
if((err = snd_pcm_hw_params_set_period_size_near(pcm_handle, params, &period_size, &dir)) < 0) {
printf("Error: Unable to set the period size: %s\n", snd_strerror(err));
return 1;
}
printf("Attempting to get the period size.\n");
if((err = snd_pcm_hw_params_get_period_size(params, &period_size, 0)) < 0) {
printf("Error: Unable to get the period size: %s\n", snd_strerror(err));
return 1;
}
printf("Period size is now: %zd\n", period_size);
int period_time = -1;
printf("Attempting to get the period time.\n");
if((err = snd_pcm_hw_params_get_period_time(params, &period_time, NULL)) < 0) {
printf("Error: Unable to get the period time: %s\n", snd_strerror(err));
return 1;
}
printf("Period time: %d\n", period_time);
int buffer_size = 4096;
printf("Attempting to set the buffer size to: %d\n", buffer_size);
if((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, params, buffer_size)) < 0) {
printf("Error: Unable to set the buffer size: %s\n", snd_strerror(err));
return 1;
}
printf("Finalizing hw params.\n");
if((err = snd_pcm_hw_params(pcm_handle, params)) < 0) {
printf("Error: Can't set harware parameters. %s\n", snd_strerror(err));
return 1;
}
snd_pcm_uframes_t temp;
printf("Attempting to get the buffer size.\n");
if((err = snd_pcm_hw_params_get_buffer_size(params, &temp)) < 0) {
printf("Error: Unable to get the buffer size: %s\n", snd_strerror(err));
return 1;
}
printf("Buffer size is now: %d\n", temp);
if((err = snd_pcm_drain(pcm_handle)) < 0) {
printf("Error: Unable to drain the pcm handle: %s\n", snd_strerror(err));
return 1;
}
if((err = snd_pcm_close(pcm_handle)) < 0) {
printf("Error: Unable to close the pcm handle: %s\n", snd_strerror(err));
return 1;
}
return 0;
}
当 char test[5] 存在时,我得到这个输出:
PCM name: 'default'
PCM state: OPEN
Channels: 2, stereo
Rate: 44100 bps
Attempting to set the period size to 1024
Attempting to get the period size.
Period size is now: 1024
Attempting to get the period time.
Period time: -1
Attempting to set the buffer size to: 4096
Finalizing hw params.
Attempting to get the buffer size.
Buffer size is now: 523774
注意 "Buffer size is now: 523774"。预期输出如下:
但是,如果我将 test[5] 更改为 test[4] 或更少(或完全删除该语句),重新编译,然后 运行,我得到:
PCM name: 'default'
PCM state: OPEN
Channels: 2, stereo
Rate: 44100 bps
Attempting to set frames to 1024
Frames is now: 1024
Attempting to set the buffer size to: 4096
Buffer size is now: 4096
我一定是在滥用 ALSA API,在做一些我没有意识到的奇怪事情,或者 ALSA API 坏了。
为什么它会对这个 5 字节的微不足道的堆栈分配做出反应?
请注意,高于 5 的值也会产生此问题,例如字对齐数字 32。我认为与堆栈对齐或类似的事情无关。
编译器注释,如何复现:
Copy/paste 最小例子变成 broken_alsa.c
然后发出:
$ gcc broken_alsa.c -lasound
$ ./a.out
删除 char temp[5](或将其设置为 4),它应该可以工作。
调用snd_pcm_hw_params_set_period_size_near()
时,将dir
初始化为零。
发行版:Debian 9.11
编译器:gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
ALSA 版本:高级 Linux 声音架构驱动版本 k4.9.0-11-amd64.
请考虑以下可重现的最小示例(现已更新,具有更多错误处理):
/**
* Trying to set some parameters for ALSA.
*
* Code from: http://equalarea.com/paul/alsa-audio.html
*
*/
#include <alsa/asoundlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#define PCM_DEVICE "default"
int main(int argc, char **argv)
{
unsigned int err;
snd_pcm_t *pcm_handle;
snd_pcm_hw_params_t *params;
if((err = snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
printf("Error: Can't open \"%s\" PCM device. %s\n", PCM_DEVICE, snd_strerror(err));
return 1;
}
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(pcm_handle, params);
if((err = snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
printf("Error: Can't set interleaved mode. %s\n", snd_strerror(err));
return 1;
}
if((err = snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_FLOAT)) < 0) {
printf("Error: Can't set format. %s\n", snd_strerror(err));
return 1;
}
int channels = 2;
if((err = snd_pcm_hw_params_set_channels(pcm_handle, params, channels)) < 0) {
printf("Error: Can't set channels number. %s\n", snd_strerror(err));
return 1;
}
int rate = 44100;
if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0)) < 0) {
printf("Error: Can't set rate. %s\n", snd_strerror(err));
return 1;
}
printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle));
printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));
int ch;
if((err = snd_pcm_hw_params_get_channels(params, &ch)) < 0) {
printf("Error: Unable to get the number of channels: %s\n", snd_strerror(err));
return 1;
}
if(ch > 1) {
printf("Channels: %i, stereo\n", ch);
}
else if(ch == 1) {
printf("Channels: %i, mono\n", ch);
}
else {
printf("Error: Unknown number of channels (%d).\n", ch);
return 1;
}
if((err = snd_pcm_hw_params_get_rate(params, &ch, 0)) < 0) {
printf("Error: Unable to get the rate: %s\n", snd_strerror(err));
return 1;
}
printf("Rate: %d bps\n", ch);
int dir;
snd_pcm_uframes_t period_size = 1024;
printf("Attempting to set the period size to %d\n", period_size);
if((err = snd_pcm_hw_params_set_period_size_near(pcm_handle, params, &period_size, &dir)) < 0) {
printf("Error: Unable to set the period size: %s\n", snd_strerror(err));
return 1;
}
printf("Attempting to get the period size.\n");
if((err = snd_pcm_hw_params_get_period_size(params, &period_size, 0)) < 0) {
printf("Error: Unable to get the period size: %s\n", snd_strerror(err));
return 1;
}
printf("Period size is now: %zd\n", period_size);
int period_time = -1;
printf("Attempting to get the period time.\n");
if((err = snd_pcm_hw_params_get_period_time(params, &period_time, NULL)) < 0) {
printf("Error: Unable to get the period time: %s\n", snd_strerror(err));
return 1;
}
printf("Period time: %d\n", period_time);
int buffer_size = 4096;
printf("Attempting to set the buffer size to: %d\n", buffer_size);
if((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, params, buffer_size)) < 0) {
printf("Error: Unable to set the buffer size: %s\n", snd_strerror(err));
return 1;
}
printf("Finalizing hw params.\n");
if((err = snd_pcm_hw_params(pcm_handle, params)) < 0) {
printf("Error: Can't set harware parameters. %s\n", snd_strerror(err));
return 1;
}
snd_pcm_uframes_t temp;
printf("Attempting to get the buffer size.\n");
if((err = snd_pcm_hw_params_get_buffer_size(params, &temp)) < 0) {
printf("Error: Unable to get the buffer size: %s\n", snd_strerror(err));
return 1;
}
printf("Buffer size is now: %d\n", temp);
if((err = snd_pcm_drain(pcm_handle)) < 0) {
printf("Error: Unable to drain the pcm handle: %s\n", snd_strerror(err));
return 1;
}
if((err = snd_pcm_close(pcm_handle)) < 0) {
printf("Error: Unable to close the pcm handle: %s\n", snd_strerror(err));
return 1;
}
return 0;
}
当 char test[5] 存在时,我得到这个输出:
PCM name: 'default'
PCM state: OPEN
Channels: 2, stereo
Rate: 44100 bps
Attempting to set the period size to 1024
Attempting to get the period size.
Period size is now: 1024
Attempting to get the period time.
Period time: -1
Attempting to set the buffer size to: 4096
Finalizing hw params.
Attempting to get the buffer size.
Buffer size is now: 523774
注意 "Buffer size is now: 523774"。预期输出如下:
但是,如果我将 test[5] 更改为 test[4] 或更少(或完全删除该语句),重新编译,然后 运行,我得到:
PCM name: 'default'
PCM state: OPEN
Channels: 2, stereo
Rate: 44100 bps
Attempting to set frames to 1024
Frames is now: 1024
Attempting to set the buffer size to: 4096
Buffer size is now: 4096
我一定是在滥用 ALSA API,在做一些我没有意识到的奇怪事情,或者 ALSA API 坏了。
为什么它会对这个 5 字节的微不足道的堆栈分配做出反应?
请注意,高于 5 的值也会产生此问题,例如字对齐数字 32。我认为与堆栈对齐或类似的事情无关。
编译器注释,如何复现:
Copy/paste 最小例子变成 broken_alsa.c
然后发出:
$ gcc broken_alsa.c -lasound
$ ./a.out
删除 char temp[5](或将其设置为 4),它应该可以工作。
调用snd_pcm_hw_params_set_period_size_near()
时,将dir
初始化为零。