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

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

1.对于wave文件的解析

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

3.正确找到data的起始点

4.play alsa

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,详见代码

 /**
 @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 = ,
     BITS_PER_SAMPLE_8 = ,
     BITS_PER_SAMPLE_16 = ,
     BITS_PER_SAMPLE_32 =
 }EBitsPerSample_t;

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

 #if 0
 /** PCM state */
 typedef enum _snd_pcm_state {
     /** Open */
     SND_PCM_STATE_OPEN = ,
     /** 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;

    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[];
     uint8 ChunkSize[];
     uint8 Format[];
     uint8 SubChunk1ID[];
     uint8 SubChunk1Size[];
     uint8 AudioFormat[];
     uint8 NumChannels[];
     uint8 SampleRate[];
     uint8 ByteRate[];
     uint8 BlockAlign[];
     uint8 BitsPerSample[];
     uint8 CombineWaveFileExtra2Bytes[];
     uint8 SubChunk2ID[];
     uint8 SubChunk2Size[];
 }Wave_Header_t;

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

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

 typedef struct Wave_Header_Additional_Info
 {
     uint8 AdditionalInfo_2Bytes[]; //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[];
     uint8 SubChunk2Size[];
 }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=;
     ;i<size;i++)
     {
         value=value+(arr[i]<<(*i));
     }
     return value;
 }

 #if 0
 //this is the return value
 ChunkID = "RIFF"
 ChunkSize =
 Format = "WAVE"
 fmt = "fmt "
 SubChunk1Size =
 AudioFormat =
 NumChannels =
 SampleRate =
 ByteRate =
 BlockAlign =
 BitsPerSample =
 SubChunk2ID = "data"
 SubChunk2Size =
 #endif 

 //parse the wave file
 bool ParseWaveFile(const string wavepath,Alsa_Conf* palsaCfg,FILE** fp)
 {
     ;
     //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,,sizeof(Wave_Header_Size_Info_t));
     ret=fread(&wav_size_info,,*fp); 

     )
     {
         cout<<"read error"<<endl;
         return false;
     }
     string ChunkID="";
     ;i<;i++)
     {
         ChunkID+=wav_size_info.ChunkID[i];
     }
     string riff="RIFF";
     !=strcmp(ChunkID.c_str(),riff.c_str()))
     {
         cout<<"Invalid the fist Chunk id"<<endl;
         return false;
     }

     uint16 ChunkSize=HandleLittleEndian(wav_size_info.ChunkSize,);
     cout<<"The ChunSize is "<<ChunkSize<<endl;

     string Format="";
     ;i<;i++)
     {
         Format+=wav_size_info.Format[i];
     }

     !=strcmp(Format.c_str(),"WAVE"))
     {
         cout<<"Invalid the format"<<endl;
         return false;
     }

     string SubChunk1ID="";
     ;i<;i++)
     {
         SubChunk1ID+=wav_size_info.SubChunk1ID[i];
     }
     string fmt="fmt ";
     !=strcmp(SubChunk1ID.c_str(),fmt.c_str()))
     {
         cout<<"Invalid the SubChunk1ID "<<endl;
         return false;
     }
     uint16 SubChunk1Size=HandleLittleEndian(wav_size_info.SubChunk1Size,);

      && SubChunk1Size!=)
     {
         cout<<"Invalid the SubChunk1Size"<<endl;
         return false;
     }
     /*********************Audio info parse begin*************************/
     Wave_Header_Audio_Info_t wav_audio_info;
     memset(&wav_audio_info,,sizeof(Wave_Header_Audio_Info_t));
     ret=fread(&wav_audio_info,,*fp);
     )
     {
         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,);

     uint16 NumChannels    =HandleLittleEndian(wav_audio_info.NumChannels,);

     uint16 SampleRate    =HandleLittleEndian(wav_audio_info.SampleRate,);

     uint16 ByteRate        =HandleLittleEndian(wav_audio_info.ByteRate,);

     uint16 BlockAlign    =HandleLittleEndian(wav_audio_info.BlockAlign,);

     uint16 BitsPerSample=HandleLittleEndian(wav_audio_info.BitsPerSample,);

     palsaCfg->numOfChannels=(ENumOfChannels)NumChannels;
     palsaCfg->samplingFrequency=SampleRate;
     palsaCfg->bitsPerSample=(EBitsPerSample)BitsPerSample;

     /*********************Additional info parse begin if needed*************************/
     )
     {
         Wave_Header_Additional_Info_t wav_additional_info;
         memset(&wav_additional_info,,sizeof(Wave_Header_Additional_Info_t));
         fread(&wav_additional_info,,*fp);

         cout<<"read wav_additional_info"<<endl;
         )
         {
             cout<<"read error"<<endl;
             return false;
         }
         uint16 AdditionalInfo=HandleLittleEndian(wav_additional_info.AdditionalInfo_2Bytes,);
         cout<<"read AdditionalInfo value="<<AdditionalInfo<<endl;

     }

     /*********************Data info parse begin *************************/
     Wave_Header_Data_Info_t    wave_data_info;
     memset(&wave_data_info,,sizeof(Wave_Header_Data_Info_t));
     fread(&wave_data_info,,*fp); 

     )
     {
         cout<<"read error"<<endl;
         return false;
     }
     string SubChunk2ID="";
     ;i<;i++)
     {
         SubChunk2ID+=wave_data_info.SubChunk2ID[i];
     }
     string fact="fact";
     string data="data";
     ==strcmp(SubChunk2ID.c_str(),fact.c_str()))
     {
         cout<<"SubChunk2ID fact"<<endl;
     }
     ==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,);

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

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

     ;
     bool ret=false;
     snd_pcm_uframes_t frames=palsaCfg->alsaPeriodFrame;
     ; //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<,sizeof(uint8)*audio_data_size);
                 err=fread(buffer,,*fp);
                 )
                 {
                     cout<<"read error"<<endl;
                 }
                 if ( NULL != buffer )
                 {
                     err = snd_pcm_writei(*phandler, buffer, frames);
                     )
                     {
                         cout<<"Fail to write the audio data to ALSA. Reason: "<<(snd_strerror(err));
                         // recover ALSA device
                         err = snd_pcm_recover(*phandler, err, );
                         )
                         {
                             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 / ( * ));
     }
     delete[] buffer;
     buffer=NULL;
     return ret;
 }
 bool PrepareAlsaDevice(Alsa_Conf* palsaCfg,snd_pcm_t** phandler)
 {
     bool ret=false;
     bool success=true;
     ;
     snd_pcm_format_t format;
     snd_pcm_hw_params_t *hw_params = NULL;
     ;
     if(palsaCfg!=NULL)
     {
         // open ALSA device
         error=snd_pcm_open(phandler,palsaCfg->alsaDevice.c_str(),palsaCfg->streamType,palsaCfg->block? :SND_PCM_NONBLOCK);
         ) //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)
             )
             {
                 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);
             )
             {
                 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);
             )
             {
                 cout<<"[SG]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);
                 )
                 {
                     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,);
             )
             {
                 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);
             )
             {
                 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);
             )
             {
                 cout<<"[SG]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);
             )
             {
                 cout<<"[SG]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);
             )
             {
                 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);
             )
             {
                 cout<<"Fail to drop ALSA device. Reason: "<<(snd_strerror(snd_ret))<<endl;
             }
         }
         // close ALSA handler
         snd_ret = snd_pcm_close(*phandler);
         )
         {
             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 = ;
     //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 / ; // calculate the number of frame in one period
     m_palsaCfg->alsaBufferSize = m_palsaCfg->alsaPeriodFrame * ;  //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 / ; // 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;

     ;

 }
上一篇:WAVE文件格式解析


下一篇:linux配置服务器