AMR格式语音采集编码转码解码播放

AMR格式语音采集编码转码解码播放,第1张

AMR格式语音采集/编码/转码/解码/播放

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 = ; if (argc < ) {
fprintf(stderr, "%s out.amr\n", argv[]);
return ;
} amr = Encoder_Interface_init();
out = fopen(argv[], "wb");
if (!out) {
perror(argv[]);
return ;
} fwrite("#!AMR\n", , , out);
for (i = ; i < ; i++) {
short buf[];
uint8_t outbuf[];
int n;
for (j = ; j < ; j++) {
buf[j] = *sin(**3.141592654*sample_pos/);
sample_pos++;
}
n = Encoder_Interface_Encode(amr, MR475, buf, outbuf, );
fwrite(outbuf, , n, out);
}
fclose(out);
Encoder_Interface_exit(amr); return ;
}

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[];
unsigned int val;
int dir=;
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 != ) {
printf("error: alsa_play_test [music name]\n");
exit();
}
printf("play song %s by wolf\n", argv[]);
FILE *fp = fopen(argv[], "rb");
if(fp == NULL)
return ;
fseek(fp, , SEEK_SET); //1. 打开PCM,最后一个参数为0意味着标准配置
ret = snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, );
if (ret < ) {
perror("snd_pcm_open");
exit();
} //2. 分配snd_pcm_hw_params_t结构体
ret = snd_pcm_hw_params_malloc(&hw_params);
if (ret < ) {
perror("snd_pcm_hw_params_malloc");
exit();
}
//3. 初始化hw_params
ret = snd_pcm_hw_params_any(playback_handle, hw_params);
if (ret < ) {
perror("snd_pcm_hw_params_any");
exit();
}
//4. 初始化访问权限
ret = snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
if (ret < ) {
perror("snd_pcm_hw_params_set_access");
exit();
}
//5. 初始化采样格式SND_PCM_FORMAT_U8,8位
ret = snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_U8);
if (ret < ) {
perror("snd_pcm_hw_params_set_format");
exit();
}
//6. 设置采样率,如果硬件不支持我们设置的采样率,将使用最接近的
//val = 44100,有些录音采样频率固定为8KHz val = ;
ret = snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &val, &dir);
if (ret < ) {
perror("snd_pcm_hw_params_set_rate_near");
exit();
}
//7. 设置通道数量
ret = snd_pcm_hw_params_set_channels(playback_handle, hw_params, );
if (ret < ) {
perror("snd_pcm_hw_params_set_channels");
exit();
} /* Set period size to 32 frames. */
frames = ;
periodsize = frames * ;
ret = snd_pcm_hw_params_set_buffer_size_near(playback_handle, hw_params, &periodsize);
if (ret < )
{
printf("Unable to set buffer size %li : %s\n", frames * , snd_strerror(ret)); }
periodsize /= ; ret = snd_pcm_hw_params_set_period_size_near(playback_handle, hw_params, &periodsize, );
if (ret < )
{
printf("Unable to set period size %li : %s\n", periodsize, snd_strerror(ret));
} //8. 设置hw_params
ret = snd_pcm_hw_params(playback_handle, hw_params);
if (ret < ) {
perror("snd_pcm_hw_params");
exit();
} /* Use a buffer large enough to hold one period */
snd_pcm_hw_params_get_period_size(hw_params, &frames, &dir); size = frames * ; /* 2 bytes/sample, 2 channels */
buffer = (char *) malloc(size);
fprintf(stderr,
"size = %d\n",
size); while ()
{
ret = fread(buffer, , size, fp);
if(ret == )
{
fprintf(stderr, "end of file on input\n");
break;
}
else if (ret != size)
{
}
//9. 写音频数据到PCM设备
while(ret = snd_pcm_writei(playback_handle, buffer, frames)<)
{
usleep();
if (ret == -EPIPE)
{
/* EPIPE means underrun */
fprintf(stderr, "underrun occurred\n");
//完成硬件参数设置,使设备准备好
snd_pcm_prepare(playback_handle);
}
else if (ret < )
{
fprintf(stderr,
"error from writei: %s\n",
snd_strerror(ret));
}
} }
//10. 关闭PCM设备句柄
snd_pcm_close(playback_handle); return ;
}

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, );
if (ret < ) {
printf("snd_pcm_open failed\n");
return(-);
} ret = snd_pcm_hw_params_any(pcm_handle, hwparams);
if (ret < ) {
printf("snd_pcm_hw_params_any failed\n");
return(-);
} int rate = ;
int exact_rate;
int dir;
int periods = ;
snd_pcm_uframes_t periodsize = ; ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams,
SND_PCM_ACCESS_RW_INTERLEAVED);
if (ret < ) {
printf("snd_pcm_hw_params_set_access failed\n");
return(-);
} ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams,
SND_PCM_FORMAT_S16_LE);
if (ret < ) {
printf("snd_pcm_hw_params_set_format failed\n");
return(-);
} exact_rate = rate;
ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams,
&exact_rate, );
if (ret < ) {
printf("snd_pcm_hw_params_set_rate_near failed\n");
return(-);
}
if (rate != exact_rate) {
printf("The rate %d Hz is not supported by your hardware\n"
"==> Using %d Hz instead\n", rate, exact_rate);
} ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, );
if (ret < ) {
printf("snd_pcm_hw_params_set_channels failed\n");
return(-);
}
/*
ret = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0);
if (ret < 0) {
printf("snd_pcm_hw_params_set_periods failed\n");
return(-1);
}
*/
ret = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams,
(periodsize * periods) >> );
if (ret < ) {
printf("snd_pcm_hw_params_set_buffer_size failed\n");
return(-);
} ret = snd_pcm_hw_params(pcm_handle, hwparams);
if (ret < ) {
printf("snd_pcm_hw_params failed\n");
return(-);
} unsigned char *data;
int l1, l2;
short s1, s2;
int frames; data = malloc(periodsize);
frames = periodsize >> ; for (l1 = ; l1 < ; l1++) {
for (l2 = ; l2 < frames; l2++) {
s1 = (l2 % ) * - ;
s2 = (l2 % ) * - ;
data[*l2] = (unsigned char)s1;
data[*l2+] = s1 >> ;
data[*l2+] = (unsigned char)s2;
data[*l2+] = s2 >> ;
}
while ((ret = snd_pcm_writei(pcm_handle, data, frames)) < ) {
snd_pcm_prepare(pcm_handle);
printf("<<<<<<<<<<<<<<Buffer Underrun>>>>>>>>>>>>\n");
}
} snd_pcm_drop(pcm_handle);
snd_pcm_drain(pcm_handle); return ;
}

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,);
if( rc < )
{
return -;
}
/* allocate a hardware parameters object */
snd_pcm_hw_params_alloca(&params);
/* 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,);
/* sampling rate */
val = ;
snd_pcm_hw_params_set_rate_near(handle,params,&val,&dir);
/* set period size */
frames = ;
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 < )
{
return -;
}
/* 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 ;
} int main()
{
long loops;
int ret;
int size;
FILE * out_fd;
out_fd = fopen("out_pcm.raw","wb+"); alsa_capture_init();
loops = / val; /* 2 bytes/sample, 1 channels */
size = frames * ;
buffer = ( char * ) malloc(size); while( loops > )
{
loops--;
ret = snd_pcm_readi(handle,buffer,frames);
if ( ret == -EPIPE )
{
/* EPIPE means overrun */
fprintf(stderr,"overrun occured\n");
snd_pcm_prepare(handle);
}
else if ( ret < )
{
fprintf(stderr,"error from read: %s\n",
snd_strerror(ret));
}
else if ( ret != (int)frames)
{
fprintf(stderr,"short read, read %d frames\n",ret);
} ret = fwrite(buffer, , size, out_fd);
// ret = write(1, buffer, size);
if ( ret != size )
{
fprintf(stderr,"short write: wrote %d bytes\n",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 = ; i < *; i++) {
ret = snd_pcm_readi(handle,buffer,frames);
if ( ret == -EPIPE )
{
printf("overrun occured\n");
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 = ;
amrFrame->m_nFrameCount = ;
amrFrame->m_pFrame = outbuf;
NxZDPttAccess_AmrFrame(snCurCallSessionId, amrFrame);
} #else
//------------------------------------
//sin data test
int i, j;
int sample_pos = ; for (i = ; i < ; i++) {
short buf[];
char outbuf[];
int n;
for (j = ; j < ; j++) {
buf[j] = *sin(**3.141592654*sample_pos/);
sample_pos++;
}
buffer_pcm2amr_encode((char*)buf, , outbuf); //n = Encoder_Interface_Encode(amr, MR515, buf, outbuf, 0);
amrFrame->m_nFrameLen = ;
amrFrame->m_nFrameCount = ;
amrFrame->m_pFrame = outbuf;
NxZDPttAccess_AmrFrame(snCurCallSessionId, amrFrame);
}
//-------------------------------------
#endif

接收一段AMR格式语音数据,转码成PCM格式,然后写入声卡播放

#if 1
int i;
for (i = ; 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 > ){
if((ret = snd_pcm_writei(pcm_handle, pcm_data_buf, PCM_DATA_LENGTH/)) < ) {
snd_pcm_prepare(pcm_handle);
printf("<<<<<<<<<<<<<<Buffer Underrun>>>>>>>>>>>>\n");
}
}
} #else
unsigned char *data;
int l1, l2;
short s1, s2;
int frames;
int periodsize = ;
data = (unsigned char*)malloc(periodsize);
frames = periodsize >> ; for (l1 = ; l1 < ; l1++) {
for (l2 = ; l2 < frames; l2++) {
s1 = (l2 % ) * - ;
s2 = (l2 % ) * - ;
data[*l2] = (unsigned char)s1;
data[*l2+] = s1 >> ;
data[*l2+] = (unsigned char)s2;
data[*l2+] = s2 >> ;
}
if((ret = snd_pcm_writei(pcm_handle, data, frames)) < ) {
snd_pcm_prepare(pcm_handle);
printf("<<<<<<<<<<<<<<Buffer Underrun>>>>>>>>>>>>\n");
}
}
#endif

9、拓展

tinyalsa做了很多琐事,省了不少时间,感谢作者。


https://github.com/tinyalsa/tinyalsa

generation-sine-wave

https://github.com/ichgw/generation-sine-wave

https://github.com/moutend/getSineWave

https://github.com/munnellg/SineWave

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/587721.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-12
下一篇 2022-04-12

发表评论

登录后才能评论

评论列表(0条)

保存