Android : 基于alsa库的音乐播放

继上篇:Android : alsa-lib 移植 ,这篇随笔实现一个demo基于移植好的alsa库在Android平台上播放wav文件:

一、利用ffmeg将一个mp3文件转换成wav文件

 (1)ubuntu安装ffmeg工具:

     sudo add-apt-repository ppa:djcj/hybrid

     sudo apt-get update

     sudo apt-get install ffmpeg

 (2)mp3转wav:

   ffmpeg -i duandian.mp3 -f wav duandian.wav

二、demo代码:

/*
 *  This small demo sends a wave to your speakers.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <errno.h>
#include <getopt.h>
#include "../include/asoundlib.h"
#include <sys/time.h>
#include <math.h>

static char *device = "hw:0,0";                     /* playback device */
int blockMode = 0;                  /* block mode */

/*
 * RIFF WAVE file struct. 
 * For details see WAVE file format documentation  
 * (for example at http://www.wotsit.org). 
 */  
struct WAV_HEADER_S
{  
    char            riffType[4];    //4byte,资源交换文件标志:RIFF     
    unsigned int    riffSize;       //4byte,从下个地址到文件结尾的总字节数   
    char            waveType[4];    //4byte,wav文件标志:WAVE      
    char            formatType[4];  //4byte,波形文件标志:FMT(最后一位空格符)   
    unsigned int    formatSize;     //4byte,音频属性(compressionCode,numChannels,sampleRate,bytesPerSecond,blockAlign,bitsPerSample)所占字节数  
    unsigned short  compressionCode;//2byte,编码格式种类(1-线性pcm-WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM)  
    unsigned short  numChannels;    //2byte,通道数  
    unsigned int    sampleRate;     //4byte,采样率  
    unsigned int    bytesPerSecond; //4byte,传输速率  
    unsigned short  blockAlign;     //2byte,数据块的对齐,即DATA数据块长度  
    unsigned short  bitsPerSample;  //2byte,采样精度-PCM位宽  
    char            dataType[4];    //4byte,数据标志:data  
    unsigned int    dataSize;       //4byte,从下个地址到文件结尾的总字节数,即除了wav header以外的pcm data length  
} wav_header;  

/*
 *   Underrun and suspend recovery
 */
static int xrun_recovery(snd_pcm_t *handle, int err)
{
        int wait_cnt=0;
        if (err == -EPIPE) {    /* under-run */
        //重新准备设备进行读写
                err = snd_pcm_prepare(handle);
                if (err < 0)
                        printf("Can't recovery from underrun, prepare failed: %s
", snd_strerror(err));
        else
            printf("underrun occurred
");  
                return 0;
        } else if (err == -ESTRPIPE) {
                while ((err = snd_pcm_resume(handle)) == -EAGAIN){
             sleep(1);       /* wait until the suspend flag is released */
             wait_cnt++;
             if(wait_cnt >= 3){
                 printf("suspend flag  released fail after retry %d times!
", wait_cnt);
                break;
             }
        }
                      
                if (err < 0) {
                        err = snd_pcm_prepare(handle);
                        if (err < 0)
                                printf("Can't recovery from suspend, prepare failed: %s
", snd_strerror(err));
                }
                return 0;
        }
        return err;
}
  
int set_pcm_play(FILE *fp)  
{  
        int    rc;  
        int    ret;  
        int    size;  
        snd_pcm_t*       handle;        //PCI设备句柄  
        snd_pcm_hw_params_t*      params;//硬件信息和PCM流配置  
        unsigned int    val;  
        int    dir=0;  
        snd_pcm_uframes_t  frames;  
        char   *buffer;  
        int channels=wav_header.numChannels;  
        int frequency=wav_header.sampleRate;  
        int bit=wav_header.bitsPerSample;  
        int datablock=wav_header.blockAlign;  
      
        rc=snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, blockMode ? 0 : SND_PCM_NONBLOCK);  
        if(rc<0)  
        {  
                perror("open PCM device failed:");  
                exit(1);  
        }  
  
        snd_pcm_hw_params_alloca(&params); //分配params结构体
        if(rc<0)  
        {  
                perror("snd_pcm_hw_params_alloca:");  
                exit(1);  
        }  
        
        rc=snd_pcm_hw_params_any(handle, params);//初始化params  
        if(rc<0)  
        {  
                perror("snd_pcm_hw_params_any:");  
                exit(1);  
        }  
        
        rc=snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);                                 //初始化访问权限  
        if(rc<0)  
        {  
                perror("sed_pcm_hw_set_access:");  
                exit(1);  
        }  
  
        //采样位数  
        switch(bit/8)  {  
            case 1:snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_U8);  
                    break ;  
            case 2:snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);  
                    break ;  
            case 3:snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S24_LE);  
                    break ;  
    }  
        
        rc=snd_pcm_hw_params_set_channels(handle, params, channels);  //设置声道,1表示单声>道,2表示立体声  
        if(rc<0)  
        {  
                perror("
snd_pcm_hw_params_set_channels:");  
                exit(1);  
        }  
        
        val = frequency;  
        rc=snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);  //设置>频率  
        if(rc<0)  
        {  
                perror("
snd_pcm_hw_params_set_rate_near:");  
                exit(1);  
        }  
  
        rc = snd_pcm_hw_params(handle, params);  
        if(rc<0)  
        {  
            perror("
snd_pcm_hw_params: ");  
            exit(1);  
        }  
  
        rc=snd_pcm_hw_params_get_period_size(params, &frames, &dir);  /*获取周期长度*/  
        if(rc<0)  
        {  
                perror("
snd_pcm_hw_params_get_period_size:");  
                exit(1);  
        }  
  
        size = frames * datablock;   /*4 : 代表数据块长度*/  
  
        buffer =(char*)malloc(size);  
        fseek(fp,sizeof(wav_header),SEEK_SET);  //定位歌曲到数据区  
  
        while (1)  
        {  
             memset(buffer,0,sizeof(buffer));  
             ret = fread(buffer, 1, size, fp);  
             if(ret == 0)  
             {  
                     printf("music write done!
");  
                     break;  
             }  else if (ret != size)  {  
                      printf("music read out of size !
");  
             }  
         
/* use poll to wait for next event */           
write_data:
            
               if(snd_pcm_wait(handle, 50)){
            ret = snd_pcm_writei(handle,buffer,frames);   // 写音频数据到PCM设备    
            if(ret == -EAGAIN){
                printf("write EAGAIN
");
                goto write_data;
            }
            else if(ret < 0){
                    if (xrun_recovery(handle, ret) < 0) {
                                        printf("Write error: %s
", snd_strerror(ret));
                        break;
                            }
            }    
               }
       }  
  
        snd_pcm_drain(handle);  
        snd_pcm_close(handle);  
        free(buffer);  
        return 0;  
}  
  
int pcm_main(int argc,char *argv[])  
{  
  
    if(argc!=2)  
    {  
        printf("Usage : ./nanosic_apps  file_name.wav
");  
        exit(1);  
    }  
  
    int nread;  
    FILE *fp;  
    fp=fopen(argv[1],"rb");  
    if(fp==NULL)  
    {  
        perror("open file failed:
");  
        exit(1);  
    }  
      
    nread=fread(&wav_header,1,sizeof(wav_header),fp);  
    /*example:
    Read wav_header size = 44
    RIFF flag = RIFF?à?WAVEfmt 
    riffSize = 47308956
    waveType = WAVEfmt 
    formatType = fmt 
    formatSize = 16
    compressionCode = 1
    channels = 2
    Sample rate = 44100
    bytesPerSecond = 176400
    blockAlign = 4
    bitsPerSample = 16
    data = LISTp
    dataSize = 112
    */
    printf("Read wav_header size = %d
",nread);  
    printf("RIFF flag = %s
",wav_header.riffType);  
    printf("riffSize = %d
",wav_header.riffSize);  
    printf("waveType = %s
",wav_header.waveType);  
    printf("formatType = %s
",wav_header.formatType);   
    printf("formatSize = %d
",wav_header.formatSize);  
    printf("compressionCode = %d
",wav_header.compressionCode);  
    printf("channels = %d
",wav_header.numChannels);  
    printf("Sample rate = %d
",wav_header.sampleRate);  
    printf("bytesPerSecond = %d
",wav_header.bytesPerSecond);  
    printf("blockAlign = %d
",wav_header.blockAlign);  
    printf("bitsPerSample = %d
",wav_header.bitsPerSample);       
    printf("data = %s
",wav_header.dataType);  
    printf("dataSize = %d
",wav_header.dataSize);  
      
    set_pcm_play(fp);  //向PCM设备写入数据
    
    return 0;  
}  
原文地址:https://www.cnblogs.com/blogs-of-lxl/p/6857347.html