Alsa 讀取wave文件,並播放wave 文件


 

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播放需要遵循一套規定,包括設置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命令中被顯示出來)

這是aplay -l

對於指定alsa device這個事情,可以在2個文件中配置,一個是asound.conf 一個是.asoundrc. 如果已經安裝了alsa,那么打開/usr/share/alsa/alsa.conf就可以看到

對於這兩個文件,是preload的,可以通過修改這兩個文件來制定你想要的pcm別名.但是如果如果你沒做過任何修改,這讓兩個文件是不存在的,也就是說你需要自己去創建.下面這個是我自己創建的在home目錄下的.asoundrc文件,僅僅只是用來做這個測試,指定了device,plugin的hw,sample frequency是16000,具體可以百度

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

/**
@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

 

/**@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

 

 

 

未完待續...

 

希望能幫到和我一樣的菜鳥...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM