Alsa 读取wave文件,并播放wave 文件

时间:2022-06-03 19:44:21

 

Chapter1.设计思路

 

对于一个wave文件,如果需要播放,涉及到几个方面

1.对于wave文件的解析

2.通过解析wave文件,将得到的参数(主要是sampfrequency, bitsperSample,channel)通过alsa api设下去

3.正确找到data的起始点

4.play alsa

 

 

1.1 detail:

1.对于wave文件的解析,需要知道wave文件的格式

 注意几点,标准的是44byte的头,但是有些情况下会有additional info, 占据2字节。头信息参见下图,也可以参考wave 文件解析

endian

field name

Size

 
big ChunkID 4 文件头标识,一般就是" RIFF" 四个字母
little ChunkSize 4 整个数据文件的大小,不包括上面ID和Size本身
big Format 4 一般就是" WAVE" 四个字母
big SubChunk1ID 4 格式说明块,本字段一般就是"fmt "
little SubChunk1Size 4 本数据块的大小,不包括ID和Size字段本身
little AudioFormat 2 音频的格式说明
little NumChannels 2 声道数
little SampleRate 4 采样率
little ByteRate 4 比特率,每秒所需要的字节数
little BlockAlign 2 数据块对齐单元
little BitsPerSample 2 采样时模数转换的分辨率
big SubChunk2ID 4 真正的声音数据块,本字段一般是"data"
little SubChunk2Size 4 本数据块的大小,不包括ID和Size字段本身
little Data N 音频的采样数据

 

2.设置alsa的参数可以详见代码

3.通过解析wave file可以知道我们data的起始位置

4.通过alsa来play,详见代码

 

注意点:仔细点...指针操作别搞错了..

代码适合初学者看..

 

Chapter2.Alsa prepare

2.1.pre-prepare

   alsa的软件体系结构

    Alsa 读取wave文件,并播放wave 文件

 

Alsa播放需要遵循一套规定,包括设置alsa的open,samplerate,bitrate(format,涉及到大端小端),period,access,channel,prepare,这是一套相对来说固定格式的准备工作。

在打开alsa 设备的时候需要指定具体打开哪个alsa设备,如同aplay的时候我们需要用 -D 去指定device,例如 -D hw:0,0, -D sd_out_16k

关于这个device,可以通过 aplay/arecord -l 去查看

(注意 -L 和-l 是两个命令一个是list pcms 一个是list device, 其中我们指定的sd_out_16k的别名就会在-L命令中被显示出来)

Alsa 读取wave文件,并播放wave 文件

这是aplay -l

Alsa 读取wave文件,并播放wave 文件

对于指定alsa device这个事情,可以在2个文件中配置,一个是asound.conf 一个是.asoundrc. 如果已经安装了alsa,那么打开/usr/share/alsa/alsa.conf就可以看到

Alsa 读取wave文件,并播放wave 文件

对于这两个文件,是preload的,可以通过修改这两个文件来制定你想要的pcm别名.但是如果如果你没做过任何修改,这让两个文件是不存在的,也就是说你需要自己去创建.下面这个是我自己创建的在home目录下的.asoundrc文件,仅仅只是用来做这个测试,指定了device,plugin的hw,sample frequency是16000,具体可以百度

Alsa 读取wave文件,并播放wave 文件

PS:在alsa说明文档有这么一句话

The most important ALSA interfaces to the PCM devices are the "plughw" and the "hw" interface. If you use the "plughw" interface, you need not care much about the sound hardware. If your soundcard does not support the sample rate or sample format you specify, your data will be automatically converted. This also applies to the access type and the number of channels. With the "hw" interface, you have to check whether your hardware supports the configuration you would like to use.

    /* Name of the PCM device, like plughw:0,0          */
    /* The first number is the number of the soundcard, */
    /* the second number is the number of the device.   */
    char *pcm_name;

Then we initialize the variables and allocate a hwparams structure:
    /* Init pcm_name. Of course, later you */
    /* will make this configurable ;-)     */
    pcm_name = strdup("plughw:0,0");
    /* Allocate the snd_pcm_hw_params_t structure on the stack. */
    snd_pcm_hw_params_alloca(&hwparams);

 指定了alsa device之后,你可以通过aplay来试一下是否可以使用

 例如:

aplay -D hw:0,0 /home/root/left_1k_right_400hz.wav 

aplay -r 16000 -f S16_LE -D sd_out_16k -c 2 left_1k_right_400hz.wav

 

 2.2.open alsa device

 接下去就是对于alsadevice 的打开,可以参见ALSA_HOWTO里面说的还是挺详细的.

 注意access设置的时候有一段说明

/* Open PCM. The last parameter of this function is the mode. */
    /* If this is set to 0, the standard mode is used. Possible   */
    /* other values are SND_PCM_NONBLOCK and SND_PCM_ASYNC.       */ 
    /* If SND_PCM_NONBLOCK is used, read / write access to the    */
    /* PCM device will return immediately. If SND_PCM_ASYNC is    */
    /* specified, SIGIO will be emitted whenever a period has     */
    /* been completely processed by the soundcard.                */
    if (snd_pcm_open(&pcm_handle, pcm_name, stream, 0) < 0) {
      fprintf(stderr, "Error opening PCM device %s\n", pcm_name);
      return(-1);
其他具体顺序参见代码 bool PrepareAlsaDevice(Alsa_Conf* palsaCfg,snd_pcm_t** phandler);
对于别的alsa代码,同样的一套方式可以去使用,因为里面我使用的都是palsaCfg传进来的configuration,所以以后如果使用则只需要修改configuration的值就好.

2.3.HW/SW的设置

未完待续


Chapter3 PlayAlsa

对于play alsa,有几种方式

3.1.读写传输(read/write)

可以用于传输interleaved/non-interleaved的数据,并且有阻塞和非阻塞两种模式。在代码里面我用的是这种,对于write,根据你的interleaved/non-interleaved来确定到底是用writei还是writen。

我用的是writei,只需要把数据灌给alsa设备就可以了。具体方式见代码。

3.2.直接读写传输(mmap)

可以用于传输interleaved/non-interleaved/complex的数据。正在写sample code.基于HOW_TO的samplecode自己改了改...可以直接用,因为wave文件本身LR两个channel都放好了,所以2中copy方式都可以,详见下面的示例代码

 

Chapter 4 alsa confugiration under linux

Chapter 5 Sound Generateor

Chapter 6 mix the wave by Sound Generateor

Chapter 7 pending

Chapter 8 pending

未完待续....

 

Chapter 9:Code

9.1 Sample code 1, parse wave file and play , use rw to play

Alsa 读取wave文件,并播放wave 文件Alsa 读取wave文件,并播放wave 文件
/**
@file         TestAlsaPlayWave.cpp
@brief       This is a short example to play the audio wave, please define the path in the main func
@par          author: jlm
@par          pre env: alsa
@todo
*/

#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sched.h>
#include <errno.h>
#include <getopt.h>
#include <iostream>
#include <asoundlib.h>
#include <sys/time.h>
#include <math.h>
using namespace std;

/*********Type definition***********************/
typedef unsigned char  uint8;
typedef unsigned short uint16;

typedef enum EBitsPerSample
{
    BITS_UNKNOWN = 0,
    BITS_PER_SAMPLE_8 = 8,
    BITS_PER_SAMPLE_16 = 16,
    BITS_PER_SAMPLE_32 = 32
}EBitsPerSample_t;

typedef enum ENumOfChannels
{
    NUM_OF_CHANNELS_1 = 1,
    NUM_OF_CHANNELS_2 = 2
}ENumOfChannels_t;

#if 0
/** PCM state */
typedef enum _snd_pcm_state {
    /** Open */
    SND_PCM_STATE_OPEN = 0,
    /** Setup installed */ 
    SND_PCM_STATE_SETUP,
    /** Ready to start */
    SND_PCM_STATE_PREPARED,
    /** Running */
    SND_PCM_STATE_RUNNING,
    /** Stopped: underrun (playback) or overrun (capture) detected */
    SND_PCM_STATE_XRUN,
    /** Draining: running (playback) or stopped (capture) */
    SND_PCM_STATE_DRAINING,
    /** Paused */
    SND_PCM_STATE_PAUSED,
    /** Hardware is suspended */
    SND_PCM_STATE_SUSPENDED,
    /** Hardware is disconnected */
    SND_PCM_STATE_DISCONNECTED,
    SND_PCM_STATE_LAST = SND_PCM_STATE_DISCONNECTED
} snd_pcm_state_t;
#endif

typedef struct ALSA_CONFIGURATION
{

   std::string alsaDevice;

   std::string friendlyName;

   /// Read: Buffer size should be large enough to prevent overrun (read / write buffer full)
   unsigned int alsaBufferSize;

   /// Chunk size should be smaller to prevent underrun (write buffer empty)
   unsigned int alsaPeriodFrame;

   unsigned int samplingFrequency;//48kHz

   EBitsPerSample bitsPerSample;//16

   ENumOfChannels numOfChannels;

   bool block; // false means nonblock

   snd_pcm_access_t accessType;

   snd_pcm_stream_t streamType; // Playback or capture

   unsigned int alsaCapturePeriod; // Length of each capture period
}Alsa_Conf;

typedef struct Wave_Header
{
    uint8 ChunkID[4];
    uint8 ChunkSize[4];
    uint8 Format[4];
    uint8 SubChunk1ID[4];
    uint8 SubChunk1Size[4];
    uint8 AudioFormat[2];
    uint8 NumChannels[2];
    uint8 SampleRate[4];
    uint8 ByteRate[4];
    uint8 BlockAlign[2];
    uint8 BitsPerSample[2];
    uint8 CombineWaveFileExtra2Bytes[2];
    uint8 SubChunk2ID[4];
    uint8 SubChunk2Size[4];
}Wave_Header_t;


typedef struct Wave_Header_Size_Info
{
    uint8 ChunkID[4];
    uint8 ChunkSize[4];
    uint8 Format[4];
    uint8 SubChunk1ID[4];
    uint8 SubChunk1Size[4];
}Wave_Header_Size_Info_t;

typedef struct Wave_Header_Audio_Info
{
    uint8 AudioFormat[2];
    uint8 NumChannels[2];
    uint8 SampleRate[4];
    uint8 ByteRate[4];
    uint8 BlockAlign[2];
    uint8 BitsPerSample[2];
}Wave_Header_Audio_Info_t;

typedef struct Wave_Header_Additional_Info
{
    uint8 AdditionalInfo_2Bytes[2]; //this depends on the SubChunk1Size,normal if SubChunk1Size=16 then match the normal wave format, if SubChunk1Size=18 then 2 more additional info bytes
}Wave_Header_Additional_Info_t;

typedef struct Wave_Header_Data_Info
{
    uint8 SubChunk2ID[4];
    uint8 SubChunk2Size[4];
}Wave_Header_Data_Info_t;



/*********Global Variable***********************/
snd_pcm_uframes_t g_frames; //just test purpose

/*********Func Declaration***********************/
void TestAlsaDevice(snd_pcm_t** phandler);
bool PrepareAlsaDevice(Alsa_Conf* palsaCfg,snd_pcm_t** phandler);
bool closeAlsaDevice(snd_pcm_t** phandler);
bool ParseWaveFile(const string wavepath,Alsa_Conf* palsaCfg);
uint16 HandleLittleEndian(uint8* arr,int size);
bool PlayWave(FILE** fp,snd_pcm_t** phandler,Alsa_Conf* palsaCfg);

uint16 HandleLittleEndian(uint8* arr,int size)
{
    uint16 value=0;
    for(int i=0;i<size;i++)
    {
        value=value+(arr[i]<<(8*i));
    }
    return value;
}

#if 0
//this is the return value 
ChunkID = "RIFF"
ChunkSize = 54310
Format = "WAVE"
fmt = "fmt "
SubChunk1Size = 18
AudioFormat = 1
NumChannels = 2
SampleRate = 16000
ByteRate = 64000
BlockAlign = 4
BitsPerSample = 16
SubChunk2ID = "data"
SubChunk2Size = 54272
#endif 


//parse the wave file
bool ParseWaveFile(const string wavepath,Alsa_Conf* palsaCfg,FILE** fp)
{
    int ret=0;
    //FILE* fp=NULL;
    *fp=fopen(wavepath.c_str(),"rb");
    if(*fp==NULL)
    {
        cout<<"Can parse the wave file-->need check the file name"<<endl;
    }
    
    /*********************size info parse begin*************************/
    //read size info
    Wave_Header_Size_Info_t wav_size_info;    
    memset(&wav_size_info,0,sizeof(Wave_Header_Size_Info_t));
    ret=fread(&wav_size_info,sizeof(Wave_Header_Size_Info_t),1,*fp); 
    
    if(ret<1)
    {
        cout<<"read error"<<endl;
        return false;
    }
    string ChunkID="";
    for(int i=0;i<4;i++)
    {
        ChunkID+=wav_size_info.ChunkID[i];
    }
    string riff="RIFF";
    if(0!=strcmp(ChunkID.c_str(),riff.c_str()))
    {
        cout<<"Invalid the fist Chunk id"<<endl;
        return false;
    }
    
    uint16 ChunkSize=HandleLittleEndian(wav_size_info.ChunkSize,4);
    cout<<"The ChunSize is "<<ChunkSize<<endl;
    
    string Format="";
    for(int i=0;i<4;i++)
    {
        Format+=wav_size_info.Format[i];
    }
        
    if(0!=strcmp(Format.c_str(),"WAVE"))
    {
        cout<<"Invalid the format"<<endl;
        return false;
    }

    string SubChunk1ID="";
    for(int i=0;i<4;i++)
    {
        SubChunk1ID+=wav_size_info.SubChunk1ID[i];
    }
    string fmt="fmt ";
    if(0!=strcmp(SubChunk1ID.c_str(),fmt.c_str()))
    {
        cout<<"Invalid the SubChunk1ID "<<endl;
        return false;
    }
    uint16 SubChunk1Size=HandleLittleEndian(wav_size_info.SubChunk1Size,4);
    
    if(SubChunk1Size!=16 && SubChunk1Size!=18)
    {
        cout<<"Invalid the SubChunk1Size"<<endl;
        return false;
    }
    /*********************Audio info parse begin*************************/
    Wave_Header_Audio_Info_t wav_audio_info;    
    memset(&wav_audio_info,0,sizeof(Wave_Header_Audio_Info_t));
    ret=fread(&wav_audio_info,sizeof(Wave_Header_Audio_Info_t),1,*fp); 
    if(ret<1)
    {
        cout<<"read error"<<endl;
        return false;
    }
    //fseek(fp,sizeof(Wave_Header_Size_Info_t),0);//文件指针偏移3个字节到'2' because fread will shift the pointer
    uint16 AudioFormat    =HandleLittleEndian(wav_audio_info.AudioFormat,2);
    
    uint16 NumChannels    =HandleLittleEndian(wav_audio_info.NumChannels,2);
    
    uint16 SampleRate    =HandleLittleEndian(wav_audio_info.SampleRate,4);
    
    uint16 ByteRate        =HandleLittleEndian(wav_audio_info.ByteRate,4);
    
    uint16 BlockAlign    =HandleLittleEndian(wav_audio_info.BlockAlign,2);
    
    uint16 BitsPerSample=HandleLittleEndian(wav_audio_info.BitsPerSample,2);


    palsaCfg->numOfChannels=(ENumOfChannels)NumChannels;
    palsaCfg->samplingFrequency=SampleRate;
    palsaCfg->bitsPerSample=(EBitsPerSample)BitsPerSample;
    
    /*********************Additional info parse begin if needed*************************/
    if(SubChunk1Size==18)
    {
        Wave_Header_Additional_Info_t wav_additional_info;    
        memset(&wav_additional_info,0,sizeof(Wave_Header_Additional_Info_t));
        fread(&wav_additional_info,sizeof(Wave_Header_Additional_Info_t),1,*fp);
        
        cout<<"read wav_additional_info"<<endl;
        if(ret<1)
        {
            cout<<"read error"<<endl;
            return false;
        }
        uint16 AdditionalInfo=HandleLittleEndian(wav_additional_info.AdditionalInfo_2Bytes,2);
        cout<<"read AdditionalInfo value="<<AdditionalInfo<<endl;

    }

    /*********************Data info parse begin *************************/
    Wave_Header_Data_Info_t    wave_data_info;
    memset(&wave_data_info,0,sizeof(Wave_Header_Data_Info_t));
    fread(&wave_data_info,sizeof(Wave_Header_Data_Info_t),1,*fp); 
    
    if(ret<1)
    {
        cout<<"read error"<<endl;
        return false;
    }
    string SubChunk2ID="";
    for(int i=0;i<4;i++)
    {
        SubChunk2ID+=wave_data_info.SubChunk2ID[i];
    }
    string fact="fact";
    string data="data";
    if(0==strcmp(SubChunk2ID.c_str(),fact.c_str()))
    {
        cout<<"SubChunk2ID fact"<<endl;
    }
    else if(0==strcmp(SubChunk2ID.c_str(),data.c_str()))
    {
        cout<<"SubChunk2ID data"<<endl;
    }
    else
    {
        cout<<"Invalid SubChunk2ID "<<endl;
        return false;
    }
    uint16 SubChunk2Size=HandleLittleEndian(wave_data_info.SubChunk2Size,4);

    
    cout<<"End Parse"<<endl;
    return true;
}

bool PlayWave(FILE** fp,snd_pcm_t** phandler,Alsa_Conf* palsaCfg)
{

    int err=0;
    bool ret=false;
    snd_pcm_uframes_t frames=palsaCfg->alsaPeriodFrame;
    int bytesPerFrame=(int)palsaCfg->numOfChannels*palsaCfg->bitsPerSample/8; //4bytes
    uint16 audio_data_size=frames*bytesPerFrame;//one period 10ms ,1600*10/1000*(2*16/8)=640bytes one period 
    uint8* buffer=new uint8[audio_data_size]; 
    cout<<"Start play wave"<<endl;

    if(*fp==NULL || *phandler==NULL || palsaCfg==NULL)
    {
        cout<<"End play wave because something is NULL"<<endl;
        return false;
    }
    //fseek(*fp,46,SEEK_SET);  //no need to do fseek because already shifted 
    cout<<"available frame "<<snd_pcm_avail(*phandler)<<"my frames is "<<frames<<endl;

    while(true)
    {
        if(feof(*fp))
        {
            cout<<"Reach end of the file"<<endl;
            break;
        }
        else
        {    
            if(snd_pcm_avail(*phandler)<frames)
            {
                continue;
            }
            else
            {
                memset(reinterpret_cast<void*>(buffer),0,sizeof(uint8)*audio_data_size);
                err=fread(buffer,sizeof(uint8)*audio_data_size,1,*fp);
                if(err=0)
                {
                    cout<<"read error"<<endl;
                }
                if ( NULL != buffer ) 
                {
                    err = snd_pcm_writei(*phandler, buffer, frames);
                    if (err < 0)
                    {
                        cout<<"Fail to write the audio data to ALSA. Reason: "<<(snd_strerror(err));
                        // recover ALSA device
                        err = snd_pcm_recover(*phandler, err, 0);
                        if (err < 0)
                        {
                            cout<<"Fail to recover ALSA device. Reason: "<<(snd_strerror(err));
                            ret = false;
                        }
                        else
                        {
                            cout<<"ALSA device is recovered from error state"<<endl;
                        }
                    }
                }
                else 
                {
                    cout<<"Write buffer is NULL!"<<endl;
                }
            }
        }
        usleep(palsaCfg->alsaCapturePeriod / (2 * 1000));
    }
    delete[] buffer;
    buffer=NULL;
    return ret;
}
bool PrepareAlsaDevice(Alsa_Conf* palsaCfg,snd_pcm_t** phandler)
{
    bool ret=false;
    bool success=true;
    int error=0;    
    snd_pcm_format_t format;
    snd_pcm_hw_params_t *hw_params = NULL;
    int dir=0;
    if(palsaCfg!=NULL)
    {
        // open ALSA device
        error=snd_pcm_open(phandler,palsaCfg->alsaDevice.c_str(),palsaCfg->streamType,palsaCfg->block? 0:SND_PCM_NONBLOCK);
        if(error<0) //0 on success otherwise a negative error code
        {
            success=false;
            cout<<"Open Alsadevice error error code="<<snd_strerror(error)<<endl;
        }
        
        if(success)
        {
            //allocate hardware parameter structur
            error=snd_pcm_hw_params_malloc(&hw_params);//alsao can use  snd_pcm_hw_params_alloca(&hwparams)
            if(error<0)
            {
                success=false;
                hw_params=NULL;
                cout<<"Set hw params error error code="<<snd_strerror(error)<<endl;
            }
        }
        
        if(success)
        {
            //Fill params with a full configuration space for a PCM.  initialize the hardware parameter
            error=snd_pcm_hw_params_any(*phandler,hw_params);        
            if (error < 0) 
            {                
                success=false;
                cout<<"Broken configuration for PCM: no configurations available: "<<snd_strerror(error)<<endl;
            }
        }
        
        if(success)
        {
            // set the access type
            error = snd_pcm_hw_params_set_access(*phandler, hw_params, palsaCfg->accessType);
            if (error < 0)
            {
                cout<<"Fail to set access type. Reason: "<<snd_strerror(error)<<endl;
                success = false;
            }
        }
        
        if(success)
        {
            switch (palsaCfg->bitsPerSample)
            {
                case BITS_PER_SAMPLE_8:
                {
                    format = SND_PCM_FORMAT_U8;
                    break;
                }
                case BITS_PER_SAMPLE_16:
                {
                    format = SND_PCM_FORMAT_S16_LE; //indicate this was little endian
                    break;
                }
                case BITS_PER_SAMPLE_32:
                {
                    format = SND_PCM_FORMAT_S32_LE;
                    break;
                }
                default:
                {
                    format = SND_PCM_FORMAT_S16_LE;
                    cout<<"Invalid format"<<endl;
                    success=false;
                }
            }
            
            if(success)
            {
                error=snd_pcm_hw_params_set_format(*phandler,hw_params,format);
                if(error<0)
                {
                    cout<<"set format not available for "<<snd_strerror(error)<<endl;
                    success=false;
                }
            }

        }
        
        if(success)
        {
            error=snd_pcm_hw_params_set_rate_near(*phandler,hw_params,&palsaCfg->samplingFrequency,0);
            if(error<0)
            {
                cout<<"set rate not available for "<<snd_strerror(error)<<endl;
                success=false;
            }
        }
        
        if(success)
        {
            error=snd_pcm_hw_params_set_channels(*phandler,hw_params,palsaCfg->numOfChannels);
            if(error<0)
            {
                cout<<"set_channels not available for "<<snd_strerror(error)<<endl;
                success=false;
            }
        }
        if (success)
        {    
            // set period size (period size is also a chunk size for reading from ALSA)
            snd_pcm_uframes_t alsaPeriodFrame = static_cast<snd_pcm_uframes_t>(palsaCfg->alsaPeriodFrame); // One frame could be 4 bytes at most

            // set period size
            error = snd_pcm_hw_params_set_period_size_near(*phandler, hw_params, &alsaPeriodFrame, &dir);
            if (error < 0)
            {
                cout<<"Fail to set period size. Reason: "<<snd_strerror(error)<<endl;
                success = false;
            }
        }

        if (success)
        {
            // set hardware parameters
            error = snd_pcm_hw_params(*phandler, hw_params);
            if (error < 0)
            {
                cout<<"Fail to set hardware parameter. Reason: "<<snd_strerror(error)<<endl;
                success = false;
            }
        }

        if (success)
        {            
            error=snd_pcm_hw_params_get_period_size(hw_params, &g_frames, &dir); //get frame
            cout<<"Frame is "<<g_frames<<endl;

            // free the memory for hardware parameter structure
            snd_pcm_hw_params_free(hw_params);
            hw_params = NULL;
            // Prepare ALSA device
            error = snd_pcm_prepare(*phandler);
            if (error < 0)
            {
                cout<<"Fail to prepare ALSA device. Reason: "<<(snd_strerror(error))<<endl;
                success = false;
            }
        }

        if (success)
        {
            cout<<"ALSA device is ready to use"<<endl;
        }
        else
        {
            // fail to prepare ALSA device ==> un-initialize ALSA device
            if (hw_params != NULL)
            {
                snd_pcm_hw_params_free(hw_params);
                hw_params = NULL;
            }
            closeAlsaDevice(phandler);
        }

    }
    return success;
}





bool closeAlsaDevice(snd_pcm_t** phandler)
{
    bool ret = true;
    snd_pcm_state_t state;
    int snd_ret;

    if (*phandler != NULL)
    {
        // drop the pending audio frame if needed
        state = snd_pcm_state(*phandler);                
        cout<<"Alsa handler sate: "<<state<<endl;

        if ((SND_PCM_STATE_RUNNING == state) || (SND_PCM_STATE_XRUN == state) || (SND_PCM_STATE_SUSPENDED == state))
        {
            snd_ret = snd_pcm_drop(*phandler);
            if (snd_ret < 0)
            {
                cout<<"Fail to drop ALSA device. Reason: "<<(snd_strerror(snd_ret))<<endl;
            }
        }
        // close ALSA handler
        snd_ret = snd_pcm_close(*phandler);
        if (snd_ret < 0)
        {
            cout<<"Fail to close ALSA device. Reason: "<<(snd_strerror(snd_ret))<<endl;
            ret = false;
        }
        *phandler = NULL;
        cout<<"CLOSE ALSA DEVICE"<<endl;
    }
    return ret;

}

int main()
{        
    bool ret=false;
    snd_pcm_t* m_phandler=NULL;
    Alsa_Conf* m_palsaCfg=new Alsa_Conf();
    m_palsaCfg->alsaDevice = string("sd_out_16k");
    //m_palsaCfg->samplingFrequency = 16000;
    m_palsaCfg->alsaCapturePeriod = 50;
    //m_palsaCfg->numOfChannels = NUM_OF_CHANNELS_1;
    m_palsaCfg->block = true; //block
    m_palsaCfg->friendlyName = "AlsaWave";
    //m_palsaCfg->bitsPerSample = BITS_PER_SAMPLE_16;
    m_palsaCfg->alsaPeriodFrame = m_palsaCfg->samplingFrequency * m_palsaCfg->alsaCapturePeriod / 1000; // calculate the number of frame in one period
    m_palsaCfg->alsaBufferSize = m_palsaCfg->alsaPeriodFrame * 8;  //means the whole buffer was perdion*8, e.g. 10ms means every 10ms we will get/send the data
    m_palsaCfg->accessType = SND_PCM_ACCESS_RW_INTERLEAVED;
    m_palsaCfg->streamType = SND_PCM_STREAM_PLAYBACK;

    FILE* fp=NULL;
    const string wavePath="/mnt/hgfs/0_SharedFolder/0_Local_Test_Folder/01_TestFolder/TestALSA/left_1k_right_400hz.wav";
    //parse the wave file 
    ret=ParseWaveFile(wavePath,m_palsaCfg,&fp);
    //update the value
    m_palsaCfg->alsaPeriodFrame = m_palsaCfg->samplingFrequency * m_palsaCfg->alsaCapturePeriod / 1000; // calculate the number of frame in one period
    
    if(ret)
    {
        //open alsa device
        ret=PrepareAlsaDevice(m_palsaCfg,&m_phandler);
    }

    if(ret)
    {
        PlayWave(&fp,&m_phandler,m_palsaCfg);
    }

    closeAlsaDevice(&m_phandler);
    if(fp!=NULL)
    {
        fclose(fp);
        fp=NULL;
    }
    delete m_palsaCfg;
    m_palsaCfg=NULL;

    
    return 0;
    
}
View Code

 

 9.2 Sample code 2, use mmap to play wave

 

Alsa 读取wave文件,并播放wave 文件Alsa 读取wave文件,并播放wave 文件
/**@file         TestAlsaPlayWave.cpp
    @brief       This is a short example to play the audio wave, please define the path in the main func
    @par          author: jlm
    @par          pre env: alsa
    @todo*/

#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sched.h>
#include <errno.h>
#include <getopt.h>
#include "asoundlib.h"
#include <sys/time.h>
#include <math.h>
#include <iostream>
using namespace std;


/*********Type definition***********************/
typedef unsigned char  uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;

typedef enum EBitsPerSample
{
    BITS_UNKNOWN = 0,
    BITS_PER_SAMPLE_8 = 8,
    BITS_PER_SAMPLE_16 = 16,
    BITS_PER_SAMPLE_32 = 32
}EBitsPerSample_t;

typedef enum ENumOfChannels
{
    NUM_OF_CHANNELS_1 = 1,
    NUM_OF_CHANNELS_2 = 2
}ENumOfChannels_t;

#if 0
/** PCM state */
typedef enum _snd_pcm_state {
    /** Open */
    SND_PCM_STATE_OPEN = 0,
    /** Setup installed */ 
    SND_PCM_STATE_SETUP,
    /** Ready to start */
    SND_PCM_STATE_PREPARED,
    /** Running */
    SND_PCM_STATE_RUNNING,
    /** Stopped: underrun (playback) or overrun (capture) detected */
    SND_PCM_STATE_XRUN,
    /** Draining: running (playback) or stopped (capture) */
    SND_PCM_STATE_DRAINING,
    /** Paused */
    SND_PCM_STATE_PAUSED,
    /** Hardware is suspended */
    SND_PCM_STATE_SUSPENDED,
    /** Hardware is disconnected */
    SND_PCM_STATE_DISCONNECTED,
    SND_PCM_STATE_LAST = SND_PCM_STATE_DISCONNECTED
} snd_pcm_state_t;
#endif

typedef struct ALSA_CONFIGURATION
{

   std::string alsaDevice;

   std::string friendlyName;

   /// Read: Buffer size should be large enough to prevent overrun (read / write buffer full)
   unsigned int alsaBufferSize;

   /// Chunk size should be smaller to prevent underrun (write buffer empty)
   unsigned int alsaPeriodFrame;

   unsigned int samplingFrequency;//48kHz

   EBitsPerSample bitsPerSample;//16

   ENumOfChannels numOfChannels;

   bool block; // false means nonblock

   snd_pcm_access_t accessType;

   snd_pcm_stream_t streamType; // Playback or capture

   unsigned int alsaCapturePeriod; // Length of each capture period
}Alsa_Conf;

typedef struct Wave_Header
{
    uint8 ChunkID[4];
    uint8 ChunkSize[4];
    uint8 Format[4];
    uint8 SubChunk1ID[4];
    uint8 SubChunk1Size[4];
    uint8 AudioFormat[2];
    uint8 NumChannels[2];
    uint8 SampleRate[4];
    uint8 ByteRate[4];
    uint8 BlockAlign[2];
    uint8 BitsPerSample[2];
    uint8 CombineWaveFileExtra2Bytes[2];
    uint8 SubChunk2ID[4];
    uint8 SubChunk2Size[4];
}Wave_Header_t;


typedef struct Wave_Header_Size_Info
{
    uint8 ChunkID[4];
    uint8 ChunkSize[4];
    uint8 Format[4];
    uint8 SubChunk1ID[4];
    uint8 SubChunk1Size[4];
}Wave_Header_Size_Info_t;

typedef struct Wave_Header_Audio_Info
{
    uint8 AudioFormat[2];
    uint8 NumChannels[2];
    uint8 SampleRate[4];
    uint8 ByteRate[4];
    uint8 BlockAlign[2];
    uint8 BitsPerSample[2];
}Wave_Header_Audio_Info_t;

typedef struct Wave_Header_Additional_Info
{
    uint8 AdditionalInfo_2Bytes[2]; //this depends on the SubChunk1Size,normal if SubChunk1Size=16 then match the normal wave format, if SubChunk1Size=18 then 2 more additional info bytes
}Wave_Header_Additional_Info_t;

typedef struct Wave_Header_Data_Info
{
    uint8 SubChunk2ID[4];
    uint8 SubChunk2Size[4];
}Wave_Header_Data_Info_t;



static char *device = "sd_out_16k";//"plughw:0,0";                     /* playback device */
static snd_pcm_format_t format = SND_PCM_FORMAT_S16;    /* sample format */
static unsigned int rate = 44100;                       /* stream rate */
static unsigned int channels = 2;                       /* count of channels */
static unsigned int buffer_time = 50000;               /* ring buffer length in us */
static unsigned int period_time = 10000;               /* period time in us */
static double freq = 440;                               /* sinusoidal wave frequency in Hz */
static int verbose = 0;                                 /* verbose flag */
static int resample = 1;                                /* enable alsa-lib resampling */
static int period_event = 0;                            /* produce poll event after each period */
static snd_pcm_sframes_t buffer_size;
static snd_pcm_sframes_t period_size;
static snd_output_t *output = NULL;



uint16 HandleLittleEndian(uint8* arr,int size);

//parse the wave file
static bool ParseWaveFile(const string wavepath,Alsa_Conf* palsaCfg,FILE** fp);

static int set_hwparams(snd_pcm_t *handle,
                        snd_pcm_hw_params_t *params,
                        snd_pcm_access_t access)
{
        unsigned int rrate;
        snd_pcm_uframes_t size;
        int err, dir;
        /* choose all parameters */
        err = snd_pcm_hw_params_any(handle, params);
        if (err < 0) {
                printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
                return err;
        }
        /* set hardware resampling */
        err = snd_pcm_hw_params_set_rate_resample(handle, params, resample);
        if (err < 0) {
                printf("Resampling setup failed for playback: %s\n", snd_strerror(err));
                return err;
        }
        /* set the interleaved read/write format */
        err = snd_pcm_hw_params_set_access(handle, params, access);
        if (err < 0) {
                printf("Access type not available for playback: %s\n", snd_strerror(err));
                return err;
        }
        /* set the sample format */
        err = snd_pcm_hw_params_set_format(handle, params, format);
        if (err < 0) {
                printf("Sample format not available for playback: %s\n", snd_strerror(err));
                return err;
        }
        /* set the count of channels */
        err = snd_pcm_hw_params_set_channels(handle, params, channels);
        if (err < 0) {
                printf("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
                return err;
        }
        /* set the stream rate */
        rrate = rate;
        err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
        if (err < 0) {
                printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
                return err;
        }
        if (rrate != rate) {
                printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
                return -EINVAL;
        }
        /* set the buffer time */
        err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
        if (err < 0) {
                printf("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
                return err;
        }
        err = snd_pcm_hw_params_get_buffer_size(params, &size);
        if (err < 0) {
                printf("Unable to get buffer size for playback: %s\n", snd_strerror(err));
                return err;
        }
        buffer_size = size;
        /* set the period time */
        err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
        if (err < 0) {
                printf("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
                return err;
        }
        err = snd_pcm_hw_params_get_period_size(params, &size, &dir);
        if (err < 0) {
                printf("Unable to get period size for playback: %s\n", snd_strerror(err));
                return err;
        }
        period_size = size;
        /* write the parameters to device */
        err = snd_pcm_hw_params(handle, params);
        if (err < 0) {
                printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
                return err;
        }
        return 0;
}
static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
{
        int err;
        /* get the current swparams */
        err = snd_pcm_sw_params_current(handle, swparams);
        if (err < 0) {
                printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
                return err;
        }
        /* start the transfer when the buffer is almost full: */
        /* (buffer_size / avail_min) * avail_min */
        err = snd_pcm_sw_params_set_start_threshold(handle, swparams, (buffer_size / period_size) * period_size);
        if (err < 0) {
                printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
                return err;
        }
        /* allow the transfer when at least period_size samples can be processed */
        /* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
        err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_event ? buffer_size : period_size);
        if (err < 0) {
                printf("Unable to set avail min for playback: %s\n", snd_strerror(err));
                return err;
        }
        /* enable period events when requested */
        if (period_event) {
                err = snd_pcm_sw_params_set_period_event(handle, swparams, 1);
                if (err < 0) {
                        printf("Unable to set period event: %s\n", snd_strerror(err));
                        return err;
                }
        }
        /* write the parameters to the playback device */
        err = snd_pcm_sw_params(handle, swparams);
        if (err < 0) {
                printf("Unable to set sw params for playback: %s\n", snd_strerror(err));
                return err;
        }
        return 0;
}
/*
 *   Underrun and suspend recovery
 */
 
static int xrun_recovery(snd_pcm_t *handle, int err)
{
        if (verbose)
                printf("stream recovery\n");
        if (err == -EPIPE) {    /* under-run */
                err = snd_pcm_prepare(handle);
                if (err < 0)
                        printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
                return 0;
        } else if (err == -ESTRPIPE) {
                while ((err = snd_pcm_resume(handle)) == -EAGAIN)
                        sleep(1);       /* wait until the suspend flag is released */
                if (err < 0) {
                        err = snd_pcm_prepare(handle);
                        if (err < 0)
                                printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
                }
                return 0;
        }
        return err;
}
/*
 *   Transfer method - direct write only
 */
static int direct_loop(snd_pcm_t *handle,
                       signed short *samples ATTRIBUTE_UNUSED,
                       snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED)
{
        printf("JLM Test Direct Loop\r\n");
        double phase = 0;
        const snd_pcm_channel_area_t *my_areas;
        snd_pcm_uframes_t offset, frames, size;
        snd_pcm_sframes_t avail, commitres;
        snd_pcm_state_t state;
        int err, first = 1;

        Alsa_Conf* m_palsaCfg=new Alsa_Conf();

        FILE* fp=NULL;        
        const string wavePath="/mnt/hgfs/0_SharedFolder/0_Local_Test_Folder/01_TestFolder/TestALSA/left_1k_right_400_60ms_16bit_SampleRate_1600_stero.wav";        
        //parse the wave file         
        int ret=ParseWaveFile(wavePath,m_palsaCfg,&fp);
        while (1) 
        {
                state = snd_pcm_state(handle);
                if (state == SND_PCM_STATE_XRUN) 
                {
                        err = xrun_recovery(handle, -EPIPE);
                        if (err < 0) 
                        {
                                printf("XRUN recovery failed: %s\n", snd_strerror(err));
                                return err;
                        }
                        first = 1;
                } 
                else if (state == SND_PCM_STATE_SUSPENDED) 
                {
                        err = xrun_recovery(handle, -ESTRPIPE);
                        if (err < 0) 
                        {
                                printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
                                return err;
                        }
                }
                avail = snd_pcm_avail_update(handle);
                if (avail < 0) 
                {
                        err = xrun_recovery(handle, avail);
                        if (err < 0) 
                        {
                                printf("avail update failed: %s\n", snd_strerror(err));
                                return err;
                        }
                        first = 1;
                        continue;
                }
                if (avail < period_size) 
                {
                        if (first) 
                        {
                                first = 0;
                                err = snd_pcm_start(handle);
                                if (err < 0) 
                                {
                                        printf("Start error: %s\n", snd_strerror(err));
                                        exit(EXIT_FAILURE);
                                }
                                else
                                {
                                        printf("JLM start: %s with state=%d\n", snd_strerror(err),snd_pcm_state(handle));
                                }
                        } 
                        else 
                        {
                                err = snd_pcm_wait(handle, -1);
                                if (err < 0) {
                                        if ((err = xrun_recovery(handle, err)) < 0) {
                                                printf("snd_pcm_wait error: %s\n", snd_strerror(err));
                                                exit(EXIT_FAILURE);
                                        }
                                        first = 1;
                                }
                        }
                        continue;
                }
                size = period_size;
                while (size > 0) 
                {
                        frames = size;
                        err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
                        if (err < 0) 
                        {
                                if ((err = xrun_recovery(handle, err)) < 0) 
                                {
                                        printf("MMAP begin avail error: %s\n", snd_strerror(err));
                                        exit(EXIT_FAILURE);
                                }
                                first = 1;
                        }
                        //JLM edit
                        {
                        
                            //printf("start to do jlm \r\n");
                            int bytesPerFrame=2*16/8; //4bytes    
                            uint16 audio_data_size=frames*bytesPerFrame;//one period 10ms ,1600*10/1000*(2*16/8)=640bytes one period     
                            //snd_pcm_channel_area_t *playWavareas=NULL;
                            int stepsize=my_areas->step/8;        
                            
                            uint8* buffer=new uint8[audio_data_size]; 
                            memset(reinterpret_cast<void*>(buffer),0,sizeof(uint8)*audio_data_size);
                            
                            if(fp!=NULL)
                            {                        
                                //printf("FP is NOT NULL jlm \r\n");
                                if(feof(fp))
                                {            
                                    cout<<"Reach end of the file"<<endl;            
                                    return 0;
                                }
                                err=fread(buffer,sizeof(uint8),audio_data_size,fp);
                                if(err<0)
                                {                
                                    cout<<"read error"<<endl;                
                                    fp=NULL;                
                                    break;
                                }
                                #define method_2
                                #ifdef method_1
                                //printf("jlm audio_data_size=%d ,size=%d",audio_data_size,size);
                                unsigned char* playAddress =(unsigned char*)my_areas->addr+my_areas->first/8+offset*stepsize;
                                memcpy(playAddress,buffer,sizeof(uint8)*audio_data_size);
                                #endif

                                #ifdef method_2
                                // 0 stands for left, 1 stands for right, because 1 frame=4bytes and 1frame have 2samples ,left have 2 bytes, right have 2bytes
                                uint8* playAddressLeft =(uint8*)my_areas[0].addr+my_areas[0].first/8+offset*stepsize;
                                uint8* playAddressRight=(uint8*)my_areas[1].addr+my_areas[1].first/8+offset*stepsize;
                                
                                for(int i=0;i<frames;i++)
                                {        
                                    memcpy((playAddressLeft+stepsize*i),buffer+i*4,sizeof(uint8)*2);
                                    memcpy((playAddressRight+stepsize*i),buffer+i*4+2,sizeof(uint8)*2);
                                }
                                #endif
                            }
                            else
                            {
                                printf("FP is NULL jlm \r\n");
                            }
                            delete [] buffer;
                            buffer=NULL;

                        }
                        //generate_sine(my_areas, offset, frames, &phase);
                        commitres = snd_pcm_mmap_commit(handle, offset, frames);
                        if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) 
                        {
                                if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
                                        printf("MMAP commit error: %s\n", snd_strerror(err));
                                        exit(EXIT_FAILURE);
                                }
                                first = 1;
                        }
                        size -= frames;
                }
        }
}
 
 
/*
 *
 */
struct transfer_method {
        const char *name;
        snd_pcm_access_t access;
        int (*transfer_loop)(snd_pcm_t *handle,
                             signed short *samples,
                             snd_pcm_channel_area_t *areas);
};
static struct transfer_method transfer_methods[] = {
        //{ "write",                     SND_PCM_ACCESS_RW_INTERLEAVED, write_loop },
        //{ "write_and_poll",         SND_PCM_ACCESS_RW_INTERLEAVED, write_and_poll_loop },
        //{ "async",                     SND_PCM_ACCESS_RW_INTERLEAVED, async_loop },
        //{ "async_direct",             SND_PCM_ACCESS_MMAP_INTERLEAVED, async_direct_loop },
        { "direct_interleaved",     SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_loop },
        //{ "direct_noninterleaved",     SND_PCM_ACCESS_MMAP_NONINTERLEAVED, direct_loop },
        //{ "direct_write",             SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_write_loop },
        { NULL,                     SND_PCM_ACCESS_RW_INTERLEAVED, NULL }
};

/******************************************************************************************
*************************************ALSA TEST *******************************************/


uint16 HandleLittleEndian(uint8* arr,int size)
{    uint16 value=0;    for(int i=0;i<size;i++)    
    {        value=value+(arr[i]<<(8*i));    
    }    
    return value;
}

//parse the wave file
static bool ParseWaveFile(const string wavepath,Alsa_Conf* palsaCfg,FILE** fp)
{
    int ret=0;
    //FILE* fp=NULL;
    *fp=fopen(wavepath.c_str(),"rb");
    if(*fp==NULL)
    {
        cout<<"Can parse the wave file-->need check the file name"<<endl;
    }
    
    /*********************size info parse begin*************************/
    //read size info
    Wave_Header_Size_Info_t wav_size_info;    
    memset(&wav_size_info,0,sizeof(Wave_Header_Size_Info_t));
    ret=fread(&wav_size_info,sizeof(Wave_Header_Size_Info_t),1,*fp); 
    
    if(ret<1)
    {
        cout<<"read error"<<endl;
        return false;
    }
    string ChunkID="";
    for(int i=0;i<4;i++)
    {
        ChunkID+=wav_size_info.ChunkID[i];
    }
    string riff="RIFF";
    if(0!=strcmp(ChunkID.c_str(),riff.c_str()))
    {
        cout<<"Invalid the fist Chunk id"<<endl;
        return false;
    }
    
    uint16 ChunkSize=HandleLittleEndian(wav_size_info.ChunkSize,4);
    cout<<"The ChunSize is "<<ChunkSize<<endl;
    
    string Format="";
    for(int i=0;i<4;i++)
    {
        Format+=wav_size_info.Format[i];
    }
        
    if(0!=strcmp(Format.c_str(),"WAVE"))
    {
        cout<<"Invalid the format"<<endl;
        return false;
    }

    string SubChunk1ID="";
    for(int i=0;i<4;i++)
    {
        SubChunk1ID+=wav_size_info.SubChunk1ID[i];
    }
    string fmt="fmt ";
    if(0!=strcmp(SubChunk1ID.c_str(),fmt.c_str()))
    {
        cout<<"Invalid the SubChunk1ID "<<endl;
        return false;
    }
    uint16 SubChunk1Size=HandleLittleEndian(wav_size_info.SubChunk1Size,4);
    
    if(SubChunk1Size!=16 && SubChunk1Size!=18)
    {
        cout<<"Invalid the SubChunk1Size"<<endl;
        return false;
    }
    /*********************Audio info parse begin*************************/
    Wave_Header_Audio_Info_t wav_audio_info;    
    memset(&wav_audio_info,0,sizeof(Wave_Header_Audio_Info_t));
    ret=fread(&wav_audio_info,sizeof(Wave_Header_Audio_Info_t),1,*fp); 
    if(ret<1)
    {
        cout<<"read error"<<endl;
        return false;
    }
    //fseek(fp,sizeof(Wave_Header_Size_Info_t),0);//文件指针偏移3个字节到'2' because fread will shift the pointer
    uint16 AudioFormat    =HandleLittleEndian(wav_audio_info.AudioFormat,2);
    
    uint16 NumChannels    =HandleLittleEndian(wav_audio_info.NumChannels,2);
    
    uint16 SampleRate    =HandleLittleEndian(wav_audio_info.SampleRate,4);
    
    uint16 ByteRate        =HandleLittleEndian(wav_audio_info.ByteRate,4);
    
    uint16 BlockAlign    =HandleLittleEndian(wav_audio_info.BlockAlign,2);
    
    uint16 BitsPerSample=HandleLittleEndian(wav_audio_info.BitsPerSample,2);


    palsaCfg->numOfChannels=(ENumOfChannels)NumChannels;
    palsaCfg->samplingFrequency=SampleRate;
    palsaCfg->bitsPerSample=(EBitsPerSample)BitsPerSample;
    
    /*********************Additional info parse begin if needed*************************/
    if(SubChunk1Size==18)
    {
        Wave_Header_Additional_Info_t wav_additional_info;    
        memset(&wav_additional_info,0,sizeof(Wave_Header_Additional_Info_t));
        fread(&wav_additional_info,sizeof(Wave_Header_Additional_Info_t),1,*fp);
        
        cout<<"read wav_additional_info"<<endl;
        if(ret<1)
        {
            cout<<"read error"<<endl;
            return false;
        }
        uint16 AdditionalInfo=HandleLittleEndian(wav_additional_info.AdditionalInfo_2Bytes,2);
        cout<<"read AdditionalInfo value="<<AdditionalInfo<<endl;

    }

    /*********************Data info parse begin *************************/
    Wave_Header_Data_Info_t    wave_data_info;
    memset(&wave_data_info,0,sizeof(Wave_Header_Data_Info_t));
    fread(&wave_data_info,sizeof(Wave_Header_Data_Info_t),1,*fp); 
    
    if(ret<1)
    {
        cout<<"read error"<<endl;
        return false;
    }
    string SubChunk2ID="";
    for(int i=0;i<4;i++)
    {
        SubChunk2ID+=wave_data_info.SubChunk2ID[i];
    }
    string fact="fact";
    string data="data";
    if(0==strcmp(SubChunk2ID.c_str(),fact.c_str()))
    {
        cout<<"SubChunk2ID fact"<<endl;
    }
    else if(0==strcmp(SubChunk2ID.c_str(),data.c_str()))
    {
        cout<<"SubChunk2ID data"<<endl;
    }
    else
    {
        cout<<"Invalid SubChunk2ID "<<endl;
        return false;
    }
    uint16 SubChunk2Size=HandleLittleEndian(wave_data_info.SubChunk2Size,4);

    
    cout<<"End Parse"<<endl;
    return true;
}

/*************************************ALSA TEST *******************************************
******************************************************************************************/


#define  ALSA_TEST_FROM_HOW_TO
int main(int argc, char *argv[])
{

//#ifdef ALSA_TEST_FROM_HOW_TO
#if 1
        struct option long_option[] =
        {
                {"help", 0, NULL, 'h'},
                {"device", 1, NULL, 'D'},
                {"rate", 1, NULL, 'r'},
                {"channels", 1, NULL, 'c'},
                {"frequency", 1, NULL, 'f'},
                {"buffer", 1, NULL, 'b'},
                {"period", 1, NULL, 'p'},
                {"method", 1, NULL, 'm'},
                {"format", 1, NULL, 'o'},
                {"verbose", 1, NULL, 'v'},
                {"noresample", 1, NULL, 'n'},
                {"pevent", 1, NULL, 'e'},
                {NULL, 0, NULL, 0},
        };
        snd_pcm_t *handle;
        int err, morehelp;
        snd_pcm_hw_params_t *hwparams;
        snd_pcm_sw_params_t *swparams;
        int method = 0;
        signed short *samples;
        unsigned int chn;
        snd_pcm_channel_area_t *areas;
        snd_pcm_hw_params_alloca(&hwparams);
        snd_pcm_sw_params_alloca(&swparams);
        morehelp = 0;
        while (1) {
                int c;
                if ((c = getopt_long(argc, argv, "hD:r:c:f:b:p:m:o:vne", long_option, NULL)) < 0)
                        break;
                switch (c) {
                case 'h':
                        morehelp++;
                        break;
                case 'D':
                        device = strdup(optarg);
                        break;
                case 'r':
                        rate = atoi(optarg);
                        rate = rate < 4000 ? 4000 : rate;
                        rate = rate > 196000 ? 196000 : rate;
                        break;
                case 'c':
                        channels = atoi(optarg);
                        channels = channels < 1 ? 1 : channels;
                        channels = channels > 1024 ? 1024 : channels;
                        break;
                case 'f':
                        freq = atoi(optarg);
                        freq = freq < 50 ? 50 : freq;
                        freq = freq > 5000 ? 5000 : freq;
                        break;
                case 'b':
                        buffer_time = atoi(optarg);
                        buffer_time = buffer_time < 1000 ? 1000 : buffer_time;
                        buffer_time = buffer_time > 1000000 ? 1000000 : buffer_time;
                        break;
                case 'p':
                        period_time = atoi(optarg);
                        period_time = period_time < 1000 ? 1000 : period_time;
                        period_time = period_time > 1000000 ? 1000000 : period_time;
                        break;
                case 'm':
                        for (method = 0; transfer_methods[method].name; method++)
                                        if (!strcasecmp(transfer_methods[method].name, optarg))
                                        break;
                        if (transfer_methods[method].name == NULL)
                                method = 0;
                        break;
                case 'o':
                {    
                    int tempformat;
                        for (tempformat = 0; format < SND_PCM_FORMAT_LAST; tempformat++) {
                                const char *format_name = snd_pcm_format_name((snd_pcm_format_t)tempformat);
                                if (format_name)
                                        if (!strcasecmp(format_name, optarg))
                                        break;
                        }
                        format=(snd_pcm_format_t)tempformat;
                        if (format == SND_PCM_FORMAT_LAST)
                                format = SND_PCM_FORMAT_S16;
                        if (!snd_pcm_format_linear(format) &&
                            !(format == SND_PCM_FORMAT_FLOAT_LE ||
                              format == SND_PCM_FORMAT_FLOAT_BE)) {
                                printf("Invalid (non-linear/float) format %s\n",
                                       optarg);
                                return 1;
                        }
                        break;
                }
                case 'v':
                        verbose = 1;
                        break;
                case 'n':
                        resample = 0;
                        break;
                case 'e':
                        period_event = 1;
                        break;
                }
        }
        err = snd_output_stdio_attach(&output, stdout, 0);
        if (err < 0) {
                printf("Output failed: %s\n", snd_strerror(err));
                return 0;
        }
        std::string alsaDevice="sd_out_16k";
        printf("Playback device is %s\n", device);
        printf("Stream parameters are %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels);
        printf("Sine wave rate is %.4fHz\n", freq);
        printf("Using transfer method: %s\n", transfer_methods[method].name);
        if ((err = snd_pcm_open(&handle, alsaDevice.c_str(), SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
                printf("Playback open error: %s\n", snd_strerror(err));
                return 0;
        }
        
        if ((err = set_hwparams(handle, hwparams, transfer_methods[method].access)) < 0) {
                printf("Setting of hwparams failed: %s\n", snd_strerror(err));
                exit(EXIT_FAILURE);
        }
        if ((err = set_swparams(handle, swparams)) < 0) {
                printf("Setting of swparams failed: %s\n", snd_strerror(err));
                exit(EXIT_FAILURE);
        }
        if (verbose > 0)
                snd_pcm_dump(handle, output);
        samples =(signed short *) malloc((period_size * channels * snd_pcm_format_physical_width(format)) / 8);
        if (samples == NULL) 
        {
                printf("No enough memory\n");
                exit(EXIT_FAILURE);
        }
        
        areas = (snd_pcm_channel_area_t*)calloc(channels, sizeof(snd_pcm_channel_area_t));
        if (areas == NULL) 
        {
                printf("No enough memory\n");
                exit(EXIT_FAILURE);
        }
        for (chn = 0; chn < channels; chn++) 
        {
                areas[chn].addr = samples;
                areas[chn].first = chn * snd_pcm_format_physical_width(format);
                areas[chn].step = channels * snd_pcm_format_physical_width(format);
        }

        err = transfer_methods[method].transfer_loop(handle, samples, areas);
        if (err < 0)
                printf("Transfer failed: %s\n", snd_strerror(err));
        free(areas);
        free(samples);
        snd_pcm_close(handle);
    #endif
        return 0;
}
View Code

 

 

 

未完待续...

 

希望能帮到和我一样的菜鸟...