继上篇: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(¶ms); //分配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; }