對於Linux alsa設備寫操作

set_pcm_play函數用於播放長度爲datalen的buffer中的字符串,其中buffer中字符串爲除去WAV文件頭得到的二進制歌曲文件。

int demo_sound::set_pcm_play(char *buffer,int datalen)
{
        int    rc;
        int    ret=0;
        int    size;
        snd_pcm_t*       handle;        //PCI設備句柄
        snd_pcm_hw_params_t*      params;//硬件信息和PCM流配置
        unsigned int       val;
        int                dir=0;
        snd_pcm_uframes_t  frames;
        int channels=1;
        int frequency=16000;
        int bit=16;
        int datablock=2;
        unsigned char ch[100];  //用來存儲wav文件的頭信息

        rc=snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
        if(rc<0)
        {
                perror("\nopen PCM device failed:");
                exit(1);
        }


        snd_pcm_hw_params_alloca(&params); //分配params結構體
        if(rc<0)
        {
                perror("\nsnd_pcm_hw_params_alloca:");
                exit(1);
        }
         rc=snd_pcm_hw_params_any(handle, params);//初始化params
        if(rc<0)
        {
                perror("\nsnd_pcm_hw_params_any:");
                exit(1);
        }

        rc=snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);                                 //初始化訪問權限
        if(rc<0)
        {
                perror("\nsed_pcm_hw_set_access:");
                exit(1);

        }

        /* Signed 16-bit little-endian format */
        snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);


        rc=snd_pcm_hw_params_set_channels(handle, params, 1);  //設置聲道,1表示單聲>道,2表示立體聲
        if(rc<0)
        {
                perror("\nsnd_pcm_hw_params_set_channels:");
                exit(1);
        }
        val = frequency;
        rc=snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);  //設置>頻率
        if(rc<0)
        {
                perror("\nsnd_pcm_hw_params_set_rate_near:");
                exit(1);
        }

        rc = snd_pcm_hw_params(handle, params);
        if(rc<0)
        {
        perror("\nsnd_pcm_hw_params: ");
        exit(1);
        }

        if ((rc = snd_pcm_set_params(handle,
                                              SND_PCM_FORMAT_S16_LE,
                                              SND_PCM_ACCESS_RW_INTERLEAVED,
                                              1,
                                              16000,
                                              1,
                                              500000)) < 0) {   /* 0.5sec */
                        printf("Playback open error: %s\n", snd_strerror(rc));
                        exit(EXIT_FAILURE);
                }

        rc=snd_pcm_hw_params_get_period_size(params, &frames, &dir);  /*獲取週期長度*/
        if(rc<0)
        {
                perror("\nsnd_pcm_hw_params_get_period_size:");
                exit(1);
        }

        size = frames * datablock;   /*4 代表數據快長度*/
        int readlen=0;
        while (1)
        {        
        // 寫音頻數據到PCM設備     
        int try_count=0;        //錯誤重試計數
        while((ret = snd_pcm_writei(handle, buffer+readlen, frames))<0)
       {            
//          usleep(2000);
            if (ret == -EPIPE)          /*設備被搶佔,重新配置設備*/
            {
                SYS_LOG(INFO,"underrun occurred\n");
                int err = snd_pcm_prepare(handle);              //完成硬件參數設置,使設備準備好
                if (err < 0){
                    printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
                    goto ERROR_MASK;
                }     
                err = snd_pcm_recover(handle, ret, 0);
                if (err < 0){
                    printf("snd_pcm_recover prepare failed: %s\n", snd_strerror(err));
                    goto ERROR_MASK;
                } 
                printf("err  %d",err);
            }
            else if (ret < 0)
            {
                SYS_LOG(INFO,"error from writei: %s\n",snd_strerror(ret));
            }
            printf("try_count   %d      ",try_count);   
            /*寫設備出錯,重試10次,避免死循環*/
            {
                try_count++;
                if(try_count>10)
                {
                    SYS_LOG(INFO,"the count of retry  out of time !\n");
                    try_count=0;
                    goto ERROR_MASK;
                    break;
                }
            }
        }
        readlen+=size;
        if(readlen+size>datalen)
            break;
        }
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return 0;
ERROR_MASK:
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return -1;
}

錯誤:出現underrun的原因是:初始化配置有問題。

發佈了19 篇原創文章 · 獲贊 30 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章