1、opencore-amr源码下载
https://sourceforge.net/projects/opencore-amr/files/opencore-amr/
2、opencore-amr编译
交叉编译到arm平台
./configure --host=arm-linux-gnueabihf --prefix='/home/dong/pocdemo/opencore-amr-0.1.3/arm'
make
make install
3、opencore-amr的应用
1) opencore-amr静态库的使用
arm-linux-gnueabihf-g++ -o demo demo.c -I ./opencore-amr/include/opencore-amrnb libpoc.a ./opencore-amr/lib/libopencore-amrnb.a -lpthread
2) opencore-amr动态库的使用
arm-linux-gnueabihf-g++ -o demo demo.c -I ./opencore-amr/include/opencore-amrnb -L ./ -lpoc -L ./opencore-amr/lib -lopencore-amrnb -lpthread
3) opencore-amr静态库动态库混合的使用
arm-linux-gnueabihf-g++ -o demo demo.c -I ./opencore-amr/include/opencore-amrnb -L ./ -lpoc ./opencore-amr/lib/libopencore-amrnb.a -lpthread
4、源码包里的 opencore-amr/test/amrnb-enc-sine.c
编码一段正玄波
/* ------------------------------------------------------------------ * Copyright (C) 2009 Martin Storsjo * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. * See the License for the specific language governing permissions * and limitations under the License. * ------------------------------------------------------------------- */ #include <stdio.h> #include <stdint.h> #include <math.h> #include <interf_enc.h> int main(int argc, char *argv[]) { int i, j; void* amr; FILE* out; int sample_pos = 0; if (argc < 2) { fprintf(stderr, "%s out.amr ", argv[0]); return 1; } amr = Encoder_Interface_init(0); out = fopen(argv[1], "wb"); if (!out) { perror(argv[1]); return 1; } fwrite("#!AMR ", 1, 6, out); for (i = 0; i < 1000; i++) { short buf[160]; uint8_t outbuf[500]; int n; for (j = 0; j < 160; j++) { buf[j] = 32767*sin(440*2*3.141592654*sample_pos/8000); sample_pos++; } n = Encoder_Interface_Encode(amr, MR475, buf, outbuf, 0); fwrite(outbuf, 1, n, out); } fclose(out); Encoder_Interface_exit(amr); return 0; }
arm-linux-gnueabihf-gcc -o amrnb-enc-sine amrnb-enc-sine.c -I ./opencore-amr/include/opencore-amrnb ./opencore-amr/lib/libopencore-amrnb.a
5、PCM与AMR互转
https://github.com/gansidui/pcm_amr_codec
dong@ubuntu:~/amr/example$ tree
.
├── build_example_amr2pcm_arm.sh
├── build_example_pcm2amr_x86.sh
├── codec
│ ├── amrnb.c
│ ├── amrnb.h
│ ├── audio_format_convert.c
│ ├── audio_format_convert.h
│ └── bs.h
├── dec
├── example_amr2pcm.c
├── example_pcm2amr.c
├── libopencore-amrnb.so.0
├── opencore-amr
│ ├── include
│ │ ├── opencore-amrnb
│ │ │ ├── interf_dec.h
│ │ │ └── interf_enc.h
│ │ └── opencore-amrwb
│ │ ├── dec_if.h
│ │ └── if_rom.h
│ └── lib
│ ├── libopencore-amrnb.a
│ ├── libopencore-amrnb.la
│ ├── libopencore-amrnb.so -> libopencore-amrnb.so.0.0.3
│ ├── libopencore-amrnb.so.0 -> libopencore-amrnb.so.0.0.3
│ ├── libopencore-amrnb.so.0.0.3
│ ├── libopencore-amrwb.a
│ ├── libopencore-amrwb.la
│ ├── libopencore-amrwb.so -> libopencore-amrwb.so.0.0.3
│ ├── libopencore-amrwb.so.0 -> libopencore-amrwb.so.0.0.3
│ ├── libopencore-amrwb.so.0.0.3
│ └── pkgconfig
│ ├── opencore-amrnb.pc
│ └── opencore-amrwb.pc
├── run_dec.sh
├── run_enc.sh
├── test.amr
└── test.amr.pcm
1) gcc可以直接编译
#arm-linux-gnueabihf-gcc example_amr2pcm.c ./codec/audio_format_convert.c ./codec/amrnb.c ./codec/bs.h -o dec -I'./opencore-amr/include/opencore-amrnb' -I'./opencore-amr/include/opencore-amrwb' -I'./codec' ./opencore-amr/lib/libopencore-amrnb.a ./opencore-amr/lib/libopencore-amrwb.a
2) g++和gcc编译需要一点小改动
更改audio_format_convert.h
//#ifdef __cplusplus
//extern "C" {
//#endif
...
...
//#ifdef __cplusplus
//}
//#endif
amrnb.c的第294和368行
int ret = Encoder_Interface_Encode(amrnb_enc, MR475, &samples[offset / sizeof (int16_t)], tmp, amrnb_dtx);
参数mode改成MR475
arm-linux-gnueabihf-g++ example_amr2pcm.c ./codec/audio_format_convert.c ./codec/amrnb.c ./codec/bs.h -o dec -I'./opencore-amr/include/opencore-amrnb' -I'./opencore-amr/include/opencore-amrwb' -I'./codec' ./opencore-amr/lib/libopencore-amrnb.a ./opencore-amr/lib/libopencore-amrwb.a
我的项目整理下编译指令 build.sh
arm-linux-gnueabihf-g++ -o poc_client
poc_client.c
./codec/audio_format_convert.c
./codec/amrnb.c
./codec/bs.h
./fifo/app_fifo.c
./fifo/app_fifo.h
./list/list.c
./list/list.h
-I'./opencore-amr/include/opencore-amrnb'
-I'./opencore-amr/include/opencore-amrwb'
-I'./codec'
./opencore-amr/lib/libopencore-amrnb.a
./opencore-amr/lib/libopencore-amrwb.a
./poc/libpoc.a
-I ./alsa/include -L ./alsa/lib -lasound
-lpthread
6、alsa播放pcm音频
播放pcm文件
/**alsa play test *ALSA用户空间编译,ALSA驱动的声卡在用户空间,不宜直接使用 *文件接口中,而应使用alsa-lib *打开---->设置参数--->读写音频数据 ALSA全部使用alsa-lib中的API *交叉编译 *export LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH *arm-linux-gcc -o alsa_play alsa_play_test.c -L. -lasound *需要交叉编译后的libasound.so库的支持 * */ #include <stdio.h> #include <stdlib.h> #include "alsa/asoundlib.h" int main(int argc, char *argv[]) { int i; int ret; int buf[128]; unsigned int val; int dir=0; char *buffer; int size; snd_pcm_uframes_t frames; snd_pcm_uframes_t periodsize; snd_pcm_t *playback_handle;//PCM设备句柄pcm.h snd_pcm_hw_params_t *hw_params;//硬件信息和PCM流配置 if (argc != 2) { printf("error: alsa_play_test [music name] "); exit(1); } printf("play song %s by wolf ", argv[1]); FILE *fp = fopen(argv[1], "rb"); if(fp == NULL) return 0; fseek(fp, 100, SEEK_SET); //1. 打开PCM,最后一个参数为0意味着标准配置 ret = snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0); if (ret < 0) { perror("snd_pcm_open"); exit(1); } //2. 分配snd_pcm_hw_params_t结构体 ret = snd_pcm_hw_params_malloc(&hw_params); if (ret < 0) { perror("snd_pcm_hw_params_malloc"); exit(1); } //3. 初始化hw_params ret = snd_pcm_hw_params_any(playback_handle, hw_params); if (ret < 0) { perror("snd_pcm_hw_params_any"); exit(1); } //4. 初始化访问权限 ret = snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); if (ret < 0) { perror("snd_pcm_hw_params_set_access"); exit(1); } //5. 初始化采样格式SND_PCM_FORMAT_U8,8位 ret = snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_U8); if (ret < 0) { perror("snd_pcm_hw_params_set_format"); exit(1); } //6. 设置采样率,如果硬件不支持我们设置的采样率,将使用最接近的 //val = 44100,有些录音采样频率固定为8KHz val = 8000; ret = snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &val, &dir); if (ret < 0) { perror("snd_pcm_hw_params_set_rate_near"); exit(1); } //7. 设置通道数量 ret = snd_pcm_hw_params_set_channels(playback_handle, hw_params, 2); if (ret < 0) { perror("snd_pcm_hw_params_set_channels"); exit(1); } /* Set period size to 32 frames. */ frames = 32; periodsize = frames * 2; ret = snd_pcm_hw_params_set_buffer_size_near(playback_handle, hw_params, &periodsize); if (ret < 0) { printf("Unable to set buffer size %li : %s ", frames * 2, snd_strerror(ret)); } periodsize /= 2; ret = snd_pcm_hw_params_set_period_size_near(playback_handle, hw_params, &periodsize, 0); if (ret < 0) { printf("Unable to set period size %li : %s ", periodsize, snd_strerror(ret)); } //8. 设置hw_params ret = snd_pcm_hw_params(playback_handle, hw_params); if (ret < 0) { perror("snd_pcm_hw_params"); exit(1); } /* Use a buffer large enough to hold one period */ snd_pcm_hw_params_get_period_size(hw_params, &frames, &dir); size = frames * 2; /* 2 bytes/sample, 2 channels */ buffer = (char *) malloc(size); fprintf(stderr, "size = %d ", size); while (1) { ret = fread(buffer, 1, size, fp); if(ret == 0) { fprintf(stderr, "end of file on input "); break; } else if (ret != size) { } //9. 写音频数据到PCM设备 while(ret = snd_pcm_writei(playback_handle, buffer, frames)<0) { usleep(2000); if (ret == -EPIPE) { /* EPIPE means underrun */ fprintf(stderr, "underrun occurred "); //完成硬件参数设置,使设备准备好 snd_pcm_prepare(playback_handle); } else if (ret < 0) { fprintf(stderr, "error from writei: %s ", snd_strerror(ret)); } } } //10. 关闭PCM设备句柄 snd_pcm_close(playback_handle); return 0; }
arm-linux-gnueabihf-gcc -o test test.c -I ./alsa/include -L ./alsa/lib -lasound -lpthread
./test file.pcm
播放pcm缓存
#include <alsa/asoundlib.h> int main() { int ret; snd_pcm_t *pcm_handle; snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; snd_pcm_hw_params_t *hwparams; char *pcm_name; pcm_name = strdup("plughw:0,0"); snd_pcm_hw_params_alloca(&hwparams); ret = snd_pcm_open(&pcm_handle, pcm_name, stream, 0); if (ret < 0) { printf("snd_pcm_open failed "); return(-1); } ret = snd_pcm_hw_params_any(pcm_handle, hwparams); if (ret < 0) { printf("snd_pcm_hw_params_any failed "); return(-1); } int rate = 44100; int exact_rate; int dir; int periods = 2; snd_pcm_uframes_t periodsize = 8192; ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (ret < 0) { printf("snd_pcm_hw_params_set_access failed "); return(-1); } ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE); if (ret < 0) { printf("snd_pcm_hw_params_set_format failed "); return(-1); } exact_rate = rate; ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0); if (ret < 0) { printf("snd_pcm_hw_params_set_rate_near failed "); return(-1); } if (rate != exact_rate) { printf("The rate %d Hz is not supported by your hardware " "==> Using %d Hz instead ", rate, exact_rate); } ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2); if (ret < 0) { printf("snd_pcm_hw_params_set_channels failed "); return(-1); } /* ret = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0); if (ret < 0) { printf("snd_pcm_hw_params_set_periods failed "); return(-1); } */ ret = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, (periodsize * periods) >> 2); if (ret < 0) { printf("snd_pcm_hw_params_set_buffer_size failed "); return(-1); } ret = snd_pcm_hw_params(pcm_handle, hwparams); if (ret < 0) { printf("snd_pcm_hw_params failed "); return(-1); } unsigned char *data; int l1, l2; short s1, s2; int frames; data = malloc(periodsize); frames = periodsize >> 2; for (l1 = 0; l1 < 100; l1++) { for (l2 = 0; l2 < frames; l2++) { s1 = (l2 % 128) * 100 - 5000; s2 = (l2 % 256) * 100 - 5000; data[4*l2] = (unsigned char)s1; data[4*l2+1] = s1 >> 8; data[4*l2+2] = (unsigned char)s2; data[4*l2+3] = s2 >> 8; } while ((ret = snd_pcm_writei(pcm_handle, data, frames)) < 0) { snd_pcm_prepare(pcm_handle); printf("<<<<<<<<<<<<<<Buffer Underrun>>>>>>>>>>>> "); } } snd_pcm_drop(pcm_handle); snd_pcm_drain(pcm_handle); return 0; }
arm-linux-gnueabihf-gcc -o test test.c -I ./alsa/include -L ./alsa/lib -lasound -lpthread
./test
7、alsa采集pcm音频
采集pcm到缓存/文件
/* read from the default PCM device and writes to standard output for 5 seconds of data */ #define ALSA_PCM_NEW_HW_PARAMS_API #include <alsa/asoundlib.h> snd_pcm_t * handle; snd_pcm_hw_params_t * params; snd_pcm_uframes_t frames; char * buffer; unsigned int val; int alsa_capture_init() { int dir; int rc; /* open PCM device for recording (capture). */ rc = snd_pcm_open(&handle, "default",SND_PCM_STREAM_CAPTURE,0); if( rc < 0 ) { return -1; } /* allocate a hardware parameters object */ snd_pcm_hw_params_alloca(¶ms); /* fill it with default values. */ snd_pcm_hw_params_any(handle,params); /* set the desired hardware parameters */ snd_pcm_hw_params_set_access(handle,params, SND_PCM_ACCESS_RW_INTERLEAVED); /* signed 16-bit little-endian format */ snd_pcm_hw_params_set_format(handle,params, SND_PCM_FORMAT_S16_LE); /* two channels(stereo) */ snd_pcm_hw_params_set_channels(handle,params,1); /* sampling rate */ val = 8000; snd_pcm_hw_params_set_rate_near(handle,params,&val,&dir); /* set period size */ frames = 160; snd_pcm_hw_params_set_period_size_near(handle,params,&frames,&dir); /* write parameters to the driver */ rc = snd_pcm_hw_params(handle,params); if ( rc < 0 ) { return -1; } /* use a buffer large enough to hold one period */ snd_pcm_hw_params_get_period_size(params,&frames,&dir); /* loop for 5 seconds */ snd_pcm_hw_params_get_period_time(params, &val, &dir); return 0; } int main() { long loops; int ret; int size; FILE * out_fd; out_fd = fopen("out_pcm.raw","wb+"); alsa_capture_init(); loops = 5000000 / val; /* 2 bytes/sample, 1 channels */ size = frames * 2; buffer = ( char * ) malloc(size); while( loops > 0 ) { loops--; ret = snd_pcm_readi(handle,buffer,frames); if ( ret == -EPIPE ) { /* EPIPE means overrun */ fprintf(stderr,"overrun occured "); snd_pcm_prepare(handle); } else if ( ret < 0 ) { fprintf(stderr,"error from read: %s ", snd_strerror(ret)); } else if ( ret != (int)frames) { fprintf(stderr,"short read, read %d frames ",ret); } ret = fwrite(buffer, 1, size, out_fd); // ret = write(1, buffer, size); if ( ret != size ) { fprintf(stderr,"short write: wrote %d bytes ",ret); } } snd_pcm_drain(handle); snd_pcm_close(handle); free(buffer); fclose(out_fd); }
arm-linux-gnueabihf-gcc -o test test.c -I ./alsa/include -L ./alsa/lib -lasound -lpthread
./test
8、综合应用实例
采集一段PCM格式语音数据 ,转码成AMR格式然后发送至rtp网络
#if 1 int i; char outbuf[PCM_DATA_LENGTH]; for (i = 0; i < 125*60; i++) { ret = snd_pcm_readi(handle,buffer,frames); if ( ret == -EPIPE ) { printf("overrun occured "); snd_pcm_prepare(handle); } buffer_pcm2amr_encode((char*)buffer, PCM_DATA_LENGTH, outbuf); //n = Encoder_Interface_Encode(amr, MR795, buf, outbuf, 0); amrFrame->m_nFrameLen = 21; amrFrame->m_nFrameCount = 1; amrFrame->m_pFrame = outbuf; NxZDPttAccess_AmrFrame(snCurCallSessionId, amrFrame); } #else //------------------------------------ //sin data test int i, j; int sample_pos = 0; for (i = 0; i < 100; i++) { short buf[160]; char outbuf[320]; int n; for (j = 0; j < 160; j++) { buf[j] = 32767*sin(440*2*3.141592654*sample_pos/8000); sample_pos++; } buffer_pcm2amr_encode((char*)buf, 320, outbuf); //n = Encoder_Interface_Encode(amr, MR515, buf, outbuf, 0); amrFrame->m_nFrameLen = 21; amrFrame->m_nFrameCount = 1; amrFrame->m_pFrame = outbuf; NxZDPttAccess_AmrFrame(snCurCallSessionId, amrFrame); } //------------------------------------- #endif
接收一段AMR格式语音数据,转码成PCM格式,然后写入声卡播放
#if 1 int i; for (i = 0; i < pAmrFrame->m_nFrameCount; i++) { int ret = buffer_amr2pcm_decode(pAmrFrame->m_pFrame + i*pAmrFrame->m_nFrameLen, pAmrFrame->m_nFrameLen, pcm_data_buf); if(ret > 0){ if((ret = snd_pcm_writei(pcm_handle, pcm_data_buf, PCM_DATA_LENGTH/2)) < 0) { snd_pcm_prepare(pcm_handle); printf("<<<<<<<<<<<<<<Buffer Underrun>>>>>>>>>>>> "); } } } #else unsigned char *data; int l1, l2; short s1, s2; int frames; int periodsize = 320; data = (unsigned char*)malloc(periodsize); frames = periodsize >> 2; for (l1 = 0; l1 < 2; l1++) { for (l2 = 0; l2 < frames; l2++) { s1 = (l2 % 128) * 100 - 5000; s2 = (l2 % 256) * 100 - 5000; data[4*l2] = (unsigned char)s1; data[4*l2+1] = s1 >> 8; data[4*l2+2] = (unsigned char)s2; data[4*l2+3] = s2 >> 8; } if((ret = snd_pcm_writei(pcm_handle, data, frames)) < 0) { snd_pcm_prepare(pcm_handle); printf("<<<<<<<<<<<<<<Buffer Underrun>>>>>>>>>>>> "); } } #endif
9、拓展
tinyalsa做了很多琐事,省了不少时间,感谢作者。
https://github.com/tinyalsa/tinyalsa
generation-sine-wave
https://github.com/ichgw/generation-sine-wave