Pcm_AlsaPlay.c 11.6 KB
#include <stdio.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>

#include "JZsdkLib.h"
#include "../AudioDeal.h"
#include "../FF_Statement.h"

#include "./pcm_AlsaPlay.h"
#include "../AudioDealThread/AudioDealThread.h"
#include "alsa/asoundlib.h"

#define FRAME_SIZE 4 // 假设是双声道16位PCM,即每个帧4字节  


struct pcm_AlsaInfo
{
    int SampleRate;
    snd_pcm_t* g_handle;
    snd_pcm_hw_params_t* g_params;
    snd_pcm_sw_params_t* g_sw_params;
    snd_pcm_status_t* pcm_status;

    snd_pcm_uframes_t g_period_size;  //实际处理的数数据长度
    int g_gorup_size;  //每次处理获取的数据长度

    int dealFlag; //处理标志位,用于避免卡死
}pcm_AlsaInfo;

/**********
 * 
 *  参数初始化
 * 
 * *****/
static T_JZsdkReturnCode AlsaParamInit(struct pcm_AlsaInfo *AlsaInfo, int SampleRate)
{
    //设置采样率
    AlsaInfo->SampleRate = SampleRate;

    //音频设备模式为 0 ,也可以用SND_PCM_ASYNC模式,该模式允许以异步方式进行数据的读写操作
    static int open_mode = 0;
    //open_mode = open_mode | SND_PCM_ASYNC;

    //调用默认default音频设备
    int ret = snd_pcm_open(&(AlsaInfo->g_handle), "default", SND_PCM_STREAM_PLAYBACK, open_mode);
    if(ret < 0)
    {
        printf("open device failed\n");
        return 0;
    }    

    //设置解码的周期
    //g_period_size 的值太大,可能会导致写入操作失败,返回一个负数的错误码;如果 g_period_size 的值太小,可能会导致写入操作成功,但音频播放可能会出现不连续或延迟。
    AlsaInfo->g_period_size = 384;
    //AlsaInfo->g_period_size = 512;
    AlsaInfo->g_gorup_size = AlsaInfo->g_period_size * 2 * 16 / 8; //2声道数 16位深 8字节长度 即真正的一组数据的长度

    /***************
     * 
     *  硬件参数分配
     * 
     * **************/

    //为硬件参数分配内存空间,并将其指针保存在 g_params 中
    snd_pcm_hw_params_alloca(&AlsaInfo->g_params);

    //函数初始化硬件参数为默认值
    snd_pcm_hw_params_any(AlsaInfo->g_handle, AlsaInfo->g_params);

    /* 采样位数 Signed 16-bit little-endian format */
    ret = snd_pcm_hw_params_set_format(AlsaInfo->g_handle, AlsaInfo->g_params, SND_PCM_FORMAT_S16);
    if (ret < 0) 
    {
        JZSDK_LOG_ERROR("unable to set hw format: %s\n",snd_strerror(ret));
    }
    
    /* 交错模式 Interleaved mode */
    ret = snd_pcm_hw_params_set_access(AlsaInfo->g_handle, AlsaInfo->g_params, SND_PCM_ACCESS_RW_INTERLEAVED);
    if (ret < 0) 
    {
        JZSDK_LOG_ERROR("unable to set hw access: %s\n",snd_strerror(ret));
    }

    /* 通道数 */
    ret = snd_pcm_hw_params_set_channels(AlsaInfo->g_handle, AlsaInfo->g_params, 2);
    if (ret < 0) {
        JZSDK_LOG_ERROR("unable to set hw channels: %s\n",snd_strerror(ret));
    }
    // ret = snd_pcm_hw_params_set_channels(AlsaInfo->g_handle, AlsaInfo->g_params, 1);
    // if (ret < 0) {
    //     JZSDK_LOG_ERROR("unable to set hw channels: %s\n",snd_strerror(ret));
    // }

    /* 采样率 44100 bits/second sampling rate (CD quality) */
    ret = snd_pcm_hw_params_set_rate_near(AlsaInfo->g_handle, AlsaInfo->g_params,  &AlsaInfo->SampleRate, NULL);
    if (ret < 0) 
    {
        JZSDK_LOG_ERROR("unable to set hw sampleRate: %s\n",snd_strerror(ret));
    }

    //设置一个周期的多少帧
    ret = snd_pcm_hw_params_set_period_size_near(AlsaInfo->g_handle, AlsaInfo->g_params, &AlsaInfo->g_period_size, NULL);
    if (ret < 0) {
        JZSDK_LOG_ERROR("unable to set hw period_size: %s\n",snd_strerror(ret));
    }

    /* 将设置好的参数写入驱动 */
    ret = snd_pcm_hw_params(AlsaInfo->g_handle, AlsaInfo->g_params);
    if (ret < 0) {
        JZSDK_LOG_ERROR("unable to set hw parameters: %s\n",snd_strerror(ret));
        return 0;
    }

/***************
 * 
 *  软件参数分配
 * 
 * **************/
    snd_pcm_sw_params_alloca(&AlsaInfo->g_sw_params);
    snd_pcm_uframes_t start_t = 1;
    snd_pcm_uframes_t stop_t = 65536 * 2;
    ret = snd_pcm_sw_params_current(AlsaInfo->g_handle, AlsaInfo->g_sw_params);
    if (ret < 0) {
        JZSDK_LOG_ERROR("unable to get current sw params: %s\n",snd_strerror(ret));
    }
    ret = snd_pcm_sw_params_set_start_threshold(AlsaInfo->g_handle, AlsaInfo->g_sw_params, start_t);
    if (ret < 0) {
        JZSDK_LOG_ERROR("unable to set sw start_threshold: %s\n",snd_strerror(ret));
    }
    ret = snd_pcm_sw_params_set_stop_threshold(AlsaInfo->g_handle, AlsaInfo->g_sw_params, stop_t);
    if (ret < 0) {
        JZSDK_LOG_ERROR("unable to set sw stop_threshold: %s\n",snd_strerror(ret));
    }
    ret = snd_pcm_sw_params(AlsaInfo->g_handle, AlsaInfo->g_sw_params);/* 将设置好的参数写入驱动 */
    if (ret < 0) {
        JZSDK_LOG_ERROR("unable to set sw parameters: %s\n",snd_strerror(ret));
        return 0;
    }

    //分配音频设备的状态结构体内存
    snd_pcm_status_malloc(&AlsaInfo->pcm_status);

    printf("播放参数初始化完毕\n");

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}

/**********
 * 
 *  播放等待参数
 * 
 * *****/
static T_JZsdkReturnCode AlsaWaitPlayEnd(struct pcm_AlsaInfo *AlsaInfo)
{
    while(1){
        snd_pcm_sframes_t delay_samples = 0;
        snd_pcm_delay(AlsaInfo->g_handle, &delay_samples);
        //未播放的组小于15个就可以返还了
        if(delay_samples <= (AlsaInfo->g_period_size * 15))
        {
            break;
        }
        delayUs(100);
    }

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}

/**********
 * 
 *  pcm播放线程
 * 
 * *****/
static void *Alsa_CheckThread(void *arg)
{
    struct AudioDealInfo *IndexInfo = (struct AudioDealInfo *) arg;
    struct pcm_AlsaInfo *AlsaInfo = (struct pcm_AlsaInfo *)IndexInfo->AlsaInfo;
    int realLen = 0;//实际数据读取长度
    
    int LastStatus = JZ_FLAGCODE_OFF;

    //每10ms检测一次
    while (1)
    {
        snd_pcm_sframes_t delay_samples = 0;
        snd_pcm_delay(AlsaInfo->g_handle, &delay_samples);
        //JZSDK_LOG_INFO("当前alsa存放了%d组",delay_samples);
        if ( (delay_samples > 0) && (LastStatus == JZ_FLAGCODE_OFF))
        {
            //设置alsa状态
            Set_AudioDeal_Alsa_Flag(IndexInfo , JZ_FLAGCODE_ON);
            LastStatus = JZ_FLAGCODE_ON;
        }
        else if ( ( delay_samples == 0 ) && (LastStatus == JZ_FLAGCODE_ON))
        {
            //设置alsa状态
            Set_AudioDeal_Alsa_Flag(IndexInfo , JZ_FLAGCODE_OFF);
            LastStatus = JZ_FLAGCODE_OFF;
        }
        
        delayMs(10);
    }
}

/**********
 * 
 *  alsa play反初始化
 * 
 * *****/
// T_JZsdkReturnCode Pcm_Play_deInit(struct pcm_AlsaInfo *AlsaInfo)
// {
    
//     //printf("snd_pcm_hw_params_free\n");
//     //snd_pcm_hw_params_free(g_params);
//     printf("snd_pcm_drain\n");
//     snd_pcm_drain(AlsaInfo->g_handle);
//     printf("snd_pcm_close\n");
//     snd_pcm_close(AlsaInfo->g_handle);
// }


/**********
 * 
 *  alsa play初始化
 * 
 * *****/
T_JZsdkReturnCode AlsaPlay_Init(struct AudioDealInfo *IndexInfo)
{
    // 尝试获取或分配ResampleInfo  
    struct pcm_AlsaInfo **AlsaInfoPtr = (struct pcm_AlsaInfo **)&IndexInfo->AlsaInfo;  
    if (*AlsaInfoPtr == NULL) {  
        // 分配内存  
        *AlsaInfoPtr = (struct pcm_AlsaInfo *)malloc(sizeof(struct pcm_AlsaInfo));  
        if (*AlsaInfoPtr == NULL) {  
            // 内存分配失败处理  
            return JZ_ERROR_SYSTEM_MODULE_CODE_INVALID_PARAMETER; // 使用更具体的错误码  
        }  
    }  
    else
    {
        JZsdk_Free(*AlsaInfoPtr);
        // 分配内存  
        *AlsaInfoPtr = (struct pcm_AlsaInfo *)malloc(sizeof(struct pcm_AlsaInfo));  
        if (*AlsaInfoPtr == NULL) {  
            // 内存分配失败处理  
            return JZ_ERROR_SYSTEM_MODULE_CODE_INVALID_PARAMETER; // 使用更具体的错误码  
        }  
    }

    struct pcm_AlsaInfo *AlsaInfo = *AlsaInfoPtr;
    
    //设置alsa参数
    AlsaParamInit(AlsaInfo, IndexInfo->Target_SampleRate);

    //创建pcm检测线程
    pthread_t alsa_check_task;
	pthread_attr_t task_attribute; //线程属性
	pthread_attr_init(&task_attribute);  //初始化线程属性
	pthread_attr_setdetachstate(&task_attribute, PTHREAD_CREATE_DETACHED);      //设置线程属性

    int tts_ret = pthread_create(&alsa_check_task,&task_attribute,Alsa_CheckThread,(void *)IndexInfo);		//TTS线程
	if(tts_ret != 0)
	{
		JZSDK_LOG_ERROR("创建pcm检测线程失败!\n");
		return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
	}

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}


T_JZsdkReturnCode Pcm_AlsaPlay(struct AudioDealInfo *IndexInfo, unsigned char *buf, int num_samples)
{
    struct pcm_AlsaInfo *AlsaInfo =  (struct pcm_AlsaInfo *)IndexInfo->AlsaInfo;

    //打开alsa播放标志
    IndexInfo->AudioDeal_Alsa_Execute_Flag = JZ_FLAGCODE_ON;

    int ret = 0;
    //printf("num_samples: %d, g_period_size: %d\n", num_samples, (int)AlsaInfo->g_period_size);
    //当输入的数据长度 》 每次写入音频设备时处理的帧数量384
    int UnDeal_samples = num_samples;

    // 无论哪种都是暂时不行
    // //加入一个降噪
    // if (1)
    // {
    //     //降噪处理
    //     //把数据组成一个数组
    //     short TempPcm;     
    //     for (int i = 0; i < num_samples; i+=2) 
    //     {
    //         // TempPcm = (buf[i] & 0xFF) | ((short)buf[i+1] << 8);
    //         // TempPcm = PcmNoiseReduction(TempPcm);
    //         // buf[i] = TempPcm & 0xFF;
    //         // buf[i+1] = (TempPcm >> 8) & 0xFF;

    //         TempPcm = (buf[i+1] & 0xFF) | ((short)buf[i] << 8);
    //         TempPcm = PcmNoiseReduction(TempPcm);
    //         buf[i+1] = TempPcm & 0xFF;
    //         buf[i] = (TempPcm >> 8) & 0xFF;
    //     }
    // }

    while(UnDeal_samples > 0 && IndexInfo->AudioDeal_Alsa_Execute_Flag != JZ_FLAGCODE_OFF)
    {

        int dealSamples = 0;
        if (UnDeal_samples >= AlsaInfo->g_period_size)
        {
            dealSamples = AlsaInfo->g_period_size;
            UnDeal_samples = UnDeal_samples - dealSamples;
        }
        else
        {
            dealSamples = UnDeal_samples;
            UnDeal_samples = UnDeal_samples - dealSamples;
        }

        //将数据写入alsa音频设备
        ret = snd_pcm_writei(AlsaInfo->g_handle, (void*)buf, dealSamples);

        //当前设备不可用
        if(ret == -EAGAIN)
        {
            //等待一段时间后重试
            printf("again");
            snd_pcm_wait(AlsaInfo->g_handle, 1000);
        }
        
        //音频设备溢出错误
        else if(ret == -EPIPE)
        {
            printf("snd_pcm_prepare 1\n");
            //重置音频设备
            snd_pcm_prepare(AlsaInfo->g_handle);
        }

        //音频设备需要挂起 忙时
        else if(ret == -ESTRPIPE)
        {
            printf("Need suspend!\n");
        }

        //出现其他错误
        else if(ret < 0)
        {
            printf("snd_pcm_writei error:%s\n", snd_strerror(ret));
        }

        //位移数据
        //buf + 解码的帧数*单帧长度 //单帧长度 2个16bit长度的数据 即2*16/8 = 4字节
        buf += dealSamples * FRAME_SIZE;
    }

    //等待播放完毕
    AlsaWaitPlayEnd(AlsaInfo);
   
    //暂停播放
    while(AudioDeal_GetPauseAndContinuePlayStatus() == JZ_FLAGCODE_ON)
    {
        delayMs(1);
    }

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}

/**********
 * 
 *  清空pcm数据
 * 
 * 
 * ***********/
T_JZsdkReturnCode Alsa_DropPcm(struct AudioDealInfo *IndexInfo)
{
    struct pcm_AlsaInfo *AlsaInfo =  (struct pcm_AlsaInfo *)IndexInfo->AlsaInfo;
    snd_pcm_drop(AlsaInfo->g_handle);
    snd_pcm_prepare(AlsaInfo->g_handle);
    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}