如何正确设置 ALSA 设备
How to properly set up ALSA device
编辑:这个问题与提议的重复问题不同,因为我问的是 How do you set the period/buffer size that will work with multiple targets each with different sound hardware?
。
我创建了一些代码,试图在播放 OGG 文件之前设置 ALSA。下面的代码在一个嵌入式 Linux 平台上运行,但在另一个平台上运行失败,输出如下:
Error setting buffersize.
Playback open error: Operation not permitted
我只包含了演示该问题的代码。 setup_alsa()
不完整,无法完全配置 alsa
设备。
#include <alsa/asoundlib.h>
char *buffer;
static char *device = "default";
snd_pcm_uframes_t periodsize = 8192; /* Periodsize (bytes) */
int setup_alsa(snd_pcm_t *handle)
{
int rc;
int dir = 0;
snd_pcm_uframes_t periods; /* Number of fragments/periods */
snd_pcm_hw_params_t *params;
snd_pcm_sw_params_t *sw_params;
int rate = 44100;
int exact_rate;
int i = 0;
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(¶ms);
/* Fill it in with default values. */
if (snd_pcm_hw_params_any(handle, params) < 0)
{
fprintf(stderr, "Can not configure this PCM device.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set number of periods. Periods used to be called fragments. */
periods = 4;
if ( snd_pcm_hw_params_set_periods(handle, params, periods, 0) < 0 )
{
fprintf(stderr, "Error setting periods.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set buffer size (in frames). The resulting latency is given by */
/* latency = periodsize * periods / (rate * bytes_per_frame) */
if (snd_pcm_hw_params_set_buffer_size(handle, params, (periodsize * periods)>>2) < 0)
{
fprintf(stderr, "Error setting buffersize.\n");
snd_pcm_close(handle);
return(-1);
}
/* Write the parameters to the driver */
rc = snd_pcm_hw_params(handle, params);
if (rc < 0)
{
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
snd_pcm_close(handle);
return -1;
}
snd_pcm_hw_params_free(params);
无需设置特定 buffer/period 大小即可提供流畅音频播放的 ALSA 正常设置方法是什么?**
事实证明,我可以编写我的 ALSA
安装例程,让 ALSA
通过使用 snd_pcm_hw_params_set_buffer_size_near()
而不是 [来确定最近的工作 period/buffer 尺寸是多少=14=].
以下代码现在适用于两个平台:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <vorbis/vorbisfile.h>
#include <alsa/asoundlib.h>
char *buffer;
//static char *device = "default";
static char *device = "plughw:0,0";
snd_pcm_uframes_t periodsize = 4096; /* Periodsize (bytes) */
int setup_alsa(snd_pcm_t *handle)
{
int rc;
int dir = 0;
snd_pcm_uframes_t periods; /* Number of fragments/periods */
snd_pcm_hw_params_t *params;
snd_pcm_sw_params_t *sw_params;
int rate = 44100;
int exact_rate;
int i = 0;
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_malloc(¶ms);
/* Fill it in with default values. */
if (snd_pcm_hw_params_any(handle, params) < 0)
{
fprintf(stderr, "Can not configure this PCM device.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set the desired hardware parameters. */
/* Non-Interleaved mode */
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_NONINTERLEAVED);
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
/* 44100 bits/second sampling rate (CD quality) */
/* Set sample rate. If the exact rate is not supported */
/* by the hardware, use nearest possible rate. */
exact_rate = rate;
if (snd_pcm_hw_params_set_rate_near(handle, params, &exact_rate, 0) < 0)
{
fprintf(stderr, "Error setting rate.\n");
snd_pcm_close(handle);
return(-1);
}
if (rate != exact_rate)
{
fprintf(stderr, "The rate %d Hz is not supported by your hardware.\n==> Using %d Hz instead.\n", rate, exact_rate);
}
/* Set number of channels to 1 */
if( snd_pcm_hw_params_set_channels(handle, params, 1 ) < 0 )
{
fprintf(stderr, "Error setting channels.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set number of periods. Periods used to be called fragments. */
periods = 4;
if ( snd_pcm_hw_params_set_periods(handle, params, periods, 0) < 0 )
{
fprintf(stderr, "Error setting periods.\n");
snd_pcm_close(handle);
return(-1);
}
snd_pcm_uframes_t size = (periodsize * periods) >> 2;
if( (rc = snd_pcm_hw_params_set_buffer_size_near( handle, params, &size )) < 0)
{
fprintf(stderr, "Error setting buffersize: [%s]\n", snd_strerror(rc) );
snd_pcm_close(handle);
return(-1);
}
else
{
printf("Buffer size = %lu\n", (unsigned long)size);
}
/* Write the parameters to the driver */
rc = snd_pcm_hw_params(handle, params);
if (rc < 0)
{
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
snd_pcm_close(handle);
return -1;
}
snd_pcm_hw_params_free(params);
/* Allocate a software parameters object. */
rc = snd_pcm_sw_params_malloc(&sw_params);
if( rc < 0 )
{
fprintf (stderr, "cannot allocate software parameters structure (%s)\n", snd_strerror(rc) );
return(-1);
}
rc = snd_pcm_sw_params_current(handle, sw_params);
if( rc < 0 )
{
fprintf (stderr, "cannot initialize software parameters structure (%s)\n", snd_strerror(rc) );
return(-1);
}
if((rc = snd_pcm_sw_params_set_avail_min(handle, sw_params, 1024)) < 0)
{
fprintf (stderr, "cannot set minimum available count (%s)\n", snd_strerror (rc));
return(-1);
}
rc = snd_pcm_sw_params_set_start_threshold(handle, sw_params, 1);
if( rc < 0 )
{
fprintf(stderr, "Error setting start threshold\n");
snd_pcm_close(handle);
return -1;
}
if((rc = snd_pcm_sw_params(handle, sw_params)) < 0)
{
fprintf (stderr, "cannot set software parameters (%s)\n", snd_strerror (rc));
return(-1);
}
snd_pcm_sw_params_free(sw_params);
return 0;
}
/* copied from libvorbis source */
int ov_fopen(const char *path, OggVorbis_File *vf)
{
int ret = 0;
FILE *f = fopen(path, "rb");
if( f )
{
ret = ov_open(f, vf, NULL, 0);
if( ret )
{
fclose(f);
}
}
else
{
ret = -1;
}
return( ret );
}
int main(int argc, char *argv[])
{
// sample rate * bytes per sample * channel count * seconds
//int bufferSize = 44100 * 2 * 1 * 2;
int err;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
buffer = (char *) malloc( periodsize );
if( buffer )
{
if((err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
{
printf("Playback open error #1: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if(err = setup_alsa(handle))
{
printf("Playback open error #2: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
OggVorbis_File vf;
int eof = 0;
int current_section;
err = ov_fopen(argv[1], &vf);
if(err != 0)
{
perror("Error opening file");
}
else
{
vorbis_info *vi = ov_info(&vf, -1);
fprintf(stderr, "Bitstream is %d channel, %ldHz\n", vi->channels, vi->rate);
fprintf(stderr, "Encoded by: %s\n\n", ov_comment(&vf, -1)->vendor);
while(!eof)
{
long ret = ov_read(&vf, buffer, periodsize, 0, 2, 1, ¤t_section);
if(ret == 0)
{
/* EOF */
eof = 1;
}
else if(ret < 0)
{
/* error in the stream. */
fprintf( stderr, "ov_read error %l", ret );
}
else
{
frames = snd_pcm_writen(handle, (void *)&buffer, ret/2);
if(frames < 0)
{
printf("snd_pcm_writen failed: %s\n", snd_strerror(frames));
if( frames == -EPIPE )
{
snd_pcm_prepare(handle);
//frames = snd_pcm_writen(handle, (void *)&buffer, ret/2);
}
else
{
break;
}
}
}
}
ov_clear(&vf);
}
free( buffer );
snd_pcm_drain(handle);
snd_pcm_close(handle);
}
return 0;
}
编辑:这个问题与提议的重复问题不同,因为我问的是 How do you set the period/buffer size that will work with multiple targets each with different sound hardware?
。
我创建了一些代码,试图在播放 OGG 文件之前设置 ALSA。下面的代码在一个嵌入式 Linux 平台上运行,但在另一个平台上运行失败,输出如下:
Error setting buffersize.
Playback open error: Operation not permitted
我只包含了演示该问题的代码。 setup_alsa()
不完整,无法完全配置 alsa
设备。
#include <alsa/asoundlib.h>
char *buffer;
static char *device = "default";
snd_pcm_uframes_t periodsize = 8192; /* Periodsize (bytes) */
int setup_alsa(snd_pcm_t *handle)
{
int rc;
int dir = 0;
snd_pcm_uframes_t periods; /* Number of fragments/periods */
snd_pcm_hw_params_t *params;
snd_pcm_sw_params_t *sw_params;
int rate = 44100;
int exact_rate;
int i = 0;
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(¶ms);
/* Fill it in with default values. */
if (snd_pcm_hw_params_any(handle, params) < 0)
{
fprintf(stderr, "Can not configure this PCM device.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set number of periods. Periods used to be called fragments. */
periods = 4;
if ( snd_pcm_hw_params_set_periods(handle, params, periods, 0) < 0 )
{
fprintf(stderr, "Error setting periods.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set buffer size (in frames). The resulting latency is given by */
/* latency = periodsize * periods / (rate * bytes_per_frame) */
if (snd_pcm_hw_params_set_buffer_size(handle, params, (periodsize * periods)>>2) < 0)
{
fprintf(stderr, "Error setting buffersize.\n");
snd_pcm_close(handle);
return(-1);
}
/* Write the parameters to the driver */
rc = snd_pcm_hw_params(handle, params);
if (rc < 0)
{
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
snd_pcm_close(handle);
return -1;
}
snd_pcm_hw_params_free(params);
无需设置特定 buffer/period 大小即可提供流畅音频播放的 ALSA 正常设置方法是什么?**
事实证明,我可以编写我的 ALSA
安装例程,让 ALSA
通过使用 snd_pcm_hw_params_set_buffer_size_near()
而不是 [来确定最近的工作 period/buffer 尺寸是多少=14=].
以下代码现在适用于两个平台:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <vorbis/vorbisfile.h>
#include <alsa/asoundlib.h>
char *buffer;
//static char *device = "default";
static char *device = "plughw:0,0";
snd_pcm_uframes_t periodsize = 4096; /* Periodsize (bytes) */
int setup_alsa(snd_pcm_t *handle)
{
int rc;
int dir = 0;
snd_pcm_uframes_t periods; /* Number of fragments/periods */
snd_pcm_hw_params_t *params;
snd_pcm_sw_params_t *sw_params;
int rate = 44100;
int exact_rate;
int i = 0;
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_malloc(¶ms);
/* Fill it in with default values. */
if (snd_pcm_hw_params_any(handle, params) < 0)
{
fprintf(stderr, "Can not configure this PCM device.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set the desired hardware parameters. */
/* Non-Interleaved mode */
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_NONINTERLEAVED);
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
/* 44100 bits/second sampling rate (CD quality) */
/* Set sample rate. If the exact rate is not supported */
/* by the hardware, use nearest possible rate. */
exact_rate = rate;
if (snd_pcm_hw_params_set_rate_near(handle, params, &exact_rate, 0) < 0)
{
fprintf(stderr, "Error setting rate.\n");
snd_pcm_close(handle);
return(-1);
}
if (rate != exact_rate)
{
fprintf(stderr, "The rate %d Hz is not supported by your hardware.\n==> Using %d Hz instead.\n", rate, exact_rate);
}
/* Set number of channels to 1 */
if( snd_pcm_hw_params_set_channels(handle, params, 1 ) < 0 )
{
fprintf(stderr, "Error setting channels.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set number of periods. Periods used to be called fragments. */
periods = 4;
if ( snd_pcm_hw_params_set_periods(handle, params, periods, 0) < 0 )
{
fprintf(stderr, "Error setting periods.\n");
snd_pcm_close(handle);
return(-1);
}
snd_pcm_uframes_t size = (periodsize * periods) >> 2;
if( (rc = snd_pcm_hw_params_set_buffer_size_near( handle, params, &size )) < 0)
{
fprintf(stderr, "Error setting buffersize: [%s]\n", snd_strerror(rc) );
snd_pcm_close(handle);
return(-1);
}
else
{
printf("Buffer size = %lu\n", (unsigned long)size);
}
/* Write the parameters to the driver */
rc = snd_pcm_hw_params(handle, params);
if (rc < 0)
{
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
snd_pcm_close(handle);
return -1;
}
snd_pcm_hw_params_free(params);
/* Allocate a software parameters object. */
rc = snd_pcm_sw_params_malloc(&sw_params);
if( rc < 0 )
{
fprintf (stderr, "cannot allocate software parameters structure (%s)\n", snd_strerror(rc) );
return(-1);
}
rc = snd_pcm_sw_params_current(handle, sw_params);
if( rc < 0 )
{
fprintf (stderr, "cannot initialize software parameters structure (%s)\n", snd_strerror(rc) );
return(-1);
}
if((rc = snd_pcm_sw_params_set_avail_min(handle, sw_params, 1024)) < 0)
{
fprintf (stderr, "cannot set minimum available count (%s)\n", snd_strerror (rc));
return(-1);
}
rc = snd_pcm_sw_params_set_start_threshold(handle, sw_params, 1);
if( rc < 0 )
{
fprintf(stderr, "Error setting start threshold\n");
snd_pcm_close(handle);
return -1;
}
if((rc = snd_pcm_sw_params(handle, sw_params)) < 0)
{
fprintf (stderr, "cannot set software parameters (%s)\n", snd_strerror (rc));
return(-1);
}
snd_pcm_sw_params_free(sw_params);
return 0;
}
/* copied from libvorbis source */
int ov_fopen(const char *path, OggVorbis_File *vf)
{
int ret = 0;
FILE *f = fopen(path, "rb");
if( f )
{
ret = ov_open(f, vf, NULL, 0);
if( ret )
{
fclose(f);
}
}
else
{
ret = -1;
}
return( ret );
}
int main(int argc, char *argv[])
{
// sample rate * bytes per sample * channel count * seconds
//int bufferSize = 44100 * 2 * 1 * 2;
int err;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
buffer = (char *) malloc( periodsize );
if( buffer )
{
if((err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
{
printf("Playback open error #1: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if(err = setup_alsa(handle))
{
printf("Playback open error #2: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
OggVorbis_File vf;
int eof = 0;
int current_section;
err = ov_fopen(argv[1], &vf);
if(err != 0)
{
perror("Error opening file");
}
else
{
vorbis_info *vi = ov_info(&vf, -1);
fprintf(stderr, "Bitstream is %d channel, %ldHz\n", vi->channels, vi->rate);
fprintf(stderr, "Encoded by: %s\n\n", ov_comment(&vf, -1)->vendor);
while(!eof)
{
long ret = ov_read(&vf, buffer, periodsize, 0, 2, 1, ¤t_section);
if(ret == 0)
{
/* EOF */
eof = 1;
}
else if(ret < 0)
{
/* error in the stream. */
fprintf( stderr, "ov_read error %l", ret );
}
else
{
frames = snd_pcm_writen(handle, (void *)&buffer, ret/2);
if(frames < 0)
{
printf("snd_pcm_writen failed: %s\n", snd_strerror(frames));
if( frames == -EPIPE )
{
snd_pcm_prepare(handle);
//frames = snd_pcm_writen(handle, (void *)&buffer, ret/2);
}
else
{
break;
}
}
}
}
ov_clear(&vf);
}
free( buffer );
snd_pcm_drain(handle);
snd_pcm_close(handle);
}
return 0;
}