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 = 0;

    if (argc < 2) {
        fprintf(stderr, "%s out.amr\n", argv[0]);
        return 1;
    }

    amr = Encoder_Interface_init(0);
    out = fopen(argv[1], "wb");
    if (!out) {
        perror(argv[1]);
        return 1;
    }

    fwrite("#!AMR\n", 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]\n");
        exit(1);
    }
    printf("play song %s by wolf\n", 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\n", 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\n", 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\n",
            size);
    
    while (1) 
    {
        ret = fread(buffer, 1, size, fp);
        if(ret == 0) 
        {
              fprintf(stderr, "end of file on input\n");
              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\n");
                  //完成硬件参数设置,使设备准备好
                  snd_pcm_prepare(playback_handle);
            } 
            else if (ret < 0) 
            {
                  fprintf(stderr,
                      "error from writei: %s\n",
                      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\n");
            return(-1);
        }
     
        ret = snd_pcm_hw_params_any(pcm_handle, hwparams);
        if (ret < 0) {
            printf("snd_pcm_hw_params_any failed\n");
            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\n");
            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\n");
            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\n");
            return(-1);
        }
        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, 2);
        if (ret < 0) {
            printf("snd_pcm_hw_params_set_channels failed\n");
            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\n");
            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\n");
            return(-1);
        }
     
        ret = snd_pcm_hw_params(pcm_handle, hwparams);
        if (ret < 0) {
            printf("snd_pcm_hw_params failed\n");
            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>>>>>>>>>>>>\n");
            }
        }
     
        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(&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,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\n");
          snd_pcm_prepare(handle);
       }
       else if ( ret < 0 )
       {
          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, 1, 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 = 0; i < 125*60; 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 = 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>>>>>>>>>>>>\n");
                    }
                }
            }

#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>>>>>>>>>>>>\n");
                }
            }
#endif

 

9、拓展

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

https://github.com/tinyalsa/tinyalsa

 

转载于:https://www.cnblogs.com/dong1/p/10649104.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值