简体   繁体   English

如何正确设置ALSA设备

[英]How to properly set up ALSA device

Edit: This question is different than the proposed duplicate because I'm asking How do you set the period/buffer size that will work with multiple targets each with different sound hardware? 编辑:此问题与建议的重复项不同,因为我在问How do you set the period/buffer size that will work with multiple targets each with different sound hardware? .

I have created some code that attempts to set up ALSA before playback of an OGG file. 我创建了一些代码,试图在播放OGG文件之前设置ALSA。 The code below works on one embedded Linux platform, but on another it fails with the following output: 以下代码可在一个嵌入式Linux平台上运行,但在另一个平台上它将失败,并显示以下输出:

Error setting buffersize.
Playback open error: Operation not permitted

I've included only the code that demonstrates the issue. 我只包含了演示该问题的代码。 setup_alsa() is not complete and won't completely configure an alsa device. 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(&params);

    /* 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);

What is the normal way to setup ALSA that doesn't require a specific buffer/period size be set that provides smooth audio playback?** 不需要设置特定缓冲区/时段大小即可提供流畅音频播放的ALSA的正常设置方法是什么?**

As it turns out, I can program my ALSA setup routine to let ALSA determine what the nearest working period/buffer size is by using snd_pcm_hw_params_set_buffer_size_near() instead of snd_pcm_hw_params_set_buffer_size() . 事实证明,我可以计划我的ALSA安装例程让ALSA确定最近工作时间/缓冲区的大小是通过什么snd_pcm_hw_params_set_buffer_size_near()代替snd_pcm_hw_params_set_buffer_size()

The following code now works on both platforms: 现在,以下代码可在两种平台上使用:

#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(&params);

    /* 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, &current_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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM