zoukankan      html  css  js  c++  java
  • AMR/PCM格式语音采集/编码/转码/解码/播放

    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(&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
    ");
              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

    https://github.com/moutend/getSineWave

    https://github.com/munnellg/SineWave

  • 相关阅读:
    函数PARSENAME使用和截取字符串
    转换为标准IPv4格式
    字母转为大写字母
    字母转为小写字母
    删除多个重复记录
    大写字母或小写字母转换为数字
    给不合法的格式转换成标准格式(案例)
    SQL SERVER动态列名
    快速生成基数的辅助表
    动态为表添加字段
  • 原文地址:https://www.cnblogs.com/dong1/p/10649104.html
Copyright © 2011-2022 走看看