amr编程汇总

    技术2022-05-11  84

                                                                              amr编程汇总

     

     

           今年8月我负责研发amr语音的录制和格式转换,现在将经验于大家分享一下。

           amr的特性:

           a静音期间内的存储空间几乎为0,所以amr文件的长度并不与录音时间成正比。

           b压缩比高,连续讲话60秒的录音文件大概20k左右,因此你可以用手机存储传输amr而不用太担心存储空间和gprs流量费。

     

     

      首先说一下格式转换的问题:

    提起格式转换建议看看http://xa.bi/mms/

                           .-----------.                     -->|  MID/MOD  |                        `-----+-----'                              |                          timidity       |                     |                                                             |       v                     v                                                             v  .---------.         .-------------.       .-------.          .-----------.           .-------.  |         +--mp123->|             |       |       +--encode->|           +--ifs2amr->|       |  |   MP3   |         |     WAV     |<-sox->|  RAW  |          |  AMR-IF2  |           |  AMR  |  |         |<--lame--+             |       |       |<-decode--|           |<-amr2ifs--+       |  `----+----'         `---------+---'       `-------'          `-----------'           `---+---'       |                  ^     |                                                          |       v                  |     v                                                          v

    这里介绍了一个大致的转换思路,以及需要用到的相应工具。

       主要思路是首先将amr通过一系列转换变成wav然后在转其他格式。

       本文接下来就剖析一下amr转换成wav的过程。

        

           1首先让我们来了解一下amr的文件格式。

           http://www.ietf.org/rfc/rfc3267.txt定义了amr的文件格式。

           amr是一种有损压缩,最初的版本只用来存储单声道的语音文件,比特率为4.75 kbit/s

           后续版本提供了双声道和更多的表现细节的能力,

           主要是采样率的不断的提高一直到最后的比特率12.5 kbit/s

           最终使得amr可以存储高质量的音乐,当然也占用了大量的存储空间。

           2AMR-IF2文件格式。定制AMR-IF2的目的就是用于格式转换的。AMR-IF2相当于一个接口文件格式。

           这样在amr的版本不断变化的情况下,我们无需对先前的转换播放从程序做太多的改变。

           3两者的区别和联系:

           AMR-IF2是没有文件头的。而amr文件有着不同的文件头但所有的文件头有一个共同的特点,

           都包括 {0x23,0x21,0x41,0x4D,0x52,0x 0A}(#!AMR+回车)

           3.1两者都是分贞的每贞都有固定的长度。与比特率相关。

           主要有两种贞,一种是用来记录一小段时间内的声音采样,

           采用了小波变换进行提取压缩,压缩率视比特率而定。

           另外还有一种特殊的贞叫silenc frame,顾名思义就是静音,表示静音的时间长度。

           3.2两者的贞的存储格式是不同的需要进行转换。要了解更多关于amr-if2文件格式请参阅:

    http://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/Specs_update_after_SA16/26101-500.zip

     

     

     

    Figure A.1: Frame structure for AMR IF2

    其中frametype的订阅如下 0-15,注意只占用了4bit,就是为了节省4bit给我们的转换工作带来了许多麻烦。类型中的***SID是指该贞为silence贞。

    Table 1a: Interpretation of Frame Type,Mode Indication and Mode Request fields

    Frame Type

    Mode Indication

    Mode Request

    Frame content (AMR mode, comfort noise, or other)

    0

    0

    0

    AMR 4,75 kbit/s

    1

    1

    1

    AMR 5,15 kbit/s

    2

    2

    2

    AMR 5,90 kbit/s

    3

    3

    3

    AMR 6,70 kbit/s (PDC-EFR)

    4

    4

    4

    AMR 7,40 kbit/s (TDMA-EFR)

    5

    5

    5

    AMR 7,95 kbit/s

    6

    6

    6

    AMR 10,2 kbit/s

    7

    7

    7

    AMR 12,2 kbit/s (GSM-EFR)

    8

    -

    -

    AMR SID

    9

    -

    -

    GSM-EFR SID

    10

    -

    -

    TDMA-EFR SID

    11

    -

    -

    PDC-EFR SID

    12-14

    -

    -

    For future use

    15

    -

    -

    No Data (No transmission/No reception)

     

     

     

    那么每个贞到底是多长呢,这与比特率有关。如下:

    Table 2: Number of bits in Classes A, B, and C for each AMR codec mode

    Frame Type

    AMRcodec mode

    Total number of bits

    Class A

    Class B

    Class C

    0

    4,75

    95

    42

    53

    0

    1

    5,15

    103

    49

    54

    0

    2

    5,90

    118

    55

    63

    0

    3

    6,70

    134

    58

    76

    0

    4

    7,40

    148

    61

    87

    0

    5

    7,95

    159

    75

    84

    0

    6

    10,2

    204

    65

    99

    40

    7

    12,2

    244

    81

    103

    60

    amr475来说

    4bitframe type 接下来的95bit放音频数据,其中classA42bitclassB53bit。如下所示:

     

     

    由上面两表可见,if2的贞与amr文件的贞相比多了一个4bit的类型,相应的字节也都左移了。

    由于去掉了类型,amr文件存储有时会比if2小一些。

    http://xa.bi/files/amrconvert中使用了perl的字符串和正则处理,先将二进制bit转换成串

    再去掉type,最后转换成二进制。

    我们也可以用位操作来完成这两种贞的转换。

    我们可以这样转换两种文件:

    If2->amr

    1取出第一个字节,判断贞的类型。

    2根据贞类型生成文件头并写入文件。

    3根据贞类型确定贞长度。

    4如果有则读取一贞,转换成amr贞执行5,否则结束。

    5amr贞写文件 执行4

    Amr->if2:

    1读取文件头,判断文件类型。

    2根据文件类型确定贞长度。

    3如果有则读取一贞,转换成if2贞执行5,否则结束。

    4amr贞写文件 执行4

    26104-500描述了从rawif2的转换

    你可以参考里面的26104-500_ANSI-C_source_code

            如果使用特殊的wav文件,从wavraw的转换是非常简单的,所谓的raw是特定wav

    (单声道,8KHz16Bit)。如果你使用的文件是单声道16bit8KHz的文件,只需要去掉文件头就可以了。

    否则必须用Sox,或者用sox相同的方法。这是一个开源的软件,有兴趣的话你可以参考一下。

    http://sox.sourceforge.net/

    二接着谈谈amr的录制和播放

    如果是pc的话建议录制成wav文件然后转换成amr,手机可不能这么搞,文件太大了转换也麻烦,所以应该直接录制成amr

    Java手机的录制和播放:J2ME (MIDP 2.0, CLDC 1.0)

    http://www.hcilab.org/documents/tutorials/AudioTest/

    录制:

            Player player;         ...         player=Manager.createPlayer(“capture://audio?encoding=amr”);

            player.realize();

           RecordControl rc = (RecordControl)player.getControl("RecordControl");        ByteArrayOutputStream output = new ByteArrayOutputStream();        rc.setRecordStream(output);        rc.startRecord();        player.start();

           Thread.currentThread().sleep(5000);        rc.commit();

    播放:

           ByteArrayInputStream recordedInputStream = new ByteArrayInputStream(recordedSoundArray);        Player p2 = Manager.createPlayer(recordedInputStream,"audio/x-wav");        p2.prefetch();        p2.start();

    Symbian手机的录制和播放:

    主要是CMdaAudioRecorderUtility的使用方法的问题。

     

    播放:

     TFileName tFullFileName = GetFullFileName(aBarFileName); iCurrentFileName=tFullFileName; RLog::Log(aBarFileName);  RLog::Log(tFullFileName); #ifdef __WINS__   TRAPD(err,  iMdaAudioRecorderUtility->OpenFileL(    tFullFileName,    KMMFExControllerUID,    KMMFExControllerUID,    KMMFExDesFormatUID,    KMMFFourCCCodeAMR  ));#else TRAPD(err, iMdaAudioRecorderUtility->OpenFileL(  tFullFileName  ));#endif if(err)  {  RLog::Log(_L("OpenFile err"),err);  User::Leave(err);   }

      CMdaAudioRecorderUtility* iMdaAudioRecorderUtility;iMdaAudioRecorderUtility= new ...

    iMdaAudioRecorderUtility= new ...

     iMdaAudioRecorderUtility->SetAudioDeviceMode(CMdaAudioRecorderUtility::ELocal);     // Set maximum volume for playback    iMdaAudioRecorderUtility->SetVolume(iMdaAudioRecorderUtility->MaxVolume());     // Set the playback position to the start of the file    iMdaAudioRecorderUtility->SetPosition(TTimeIntervalMicroSeconds(0));  TRAPD(errp,iMdaAudioRecorderUtility->PlayL()); if(errp) {  RLog::Log(_L("Play"),errp); }

    录制:

     if(iMdaAudioRecorderUtility->State()==CMdaAudioClipUtility::ERecording  ||iMdaAudioRecorderUtility->State()==CMdaAudioClipUtility::EPlaying)  return; RLog::Log(_L("RecordL 1")); if(iMdaAudioRecorderUtility->State()==CMdaAudioClipUtility::EOpen)  iMdaAudioRecorderUtility->Close(); //TPtrC aFileName(KRecorderFile); TRAPD(errc,this->CleanAmrFileL(aBarFileName)); if(errc) {  RLog::Log(_L("CleanAmrFileL:"),errc);  User::Leave(errc); } else{  RLog::Log(_L("CleanAmrFileL:OK")); } TRAPD(erro,this->OpenRecordFileL(aBarFileName)); if(erro) {  RLog::Log(_L("OpenRecordFile:"),erro);  User::Leave(erro); } else{  RLog::Log(_L("OpenRfileOK")); } // Record from the device microphone    iMdaAudioRecorderUtility->SetAudioDeviceMode(CMdaAudioRecorderUtility::ELocal);     // Set maximum gain for recording    iMdaAudioRecorderUtility->SetGain(iMdaAudioRecorderUtility->MaxGain());        // Delete current audio sample from beginning of file    iMdaAudioRecorderUtility->SetPosition(TTimeIntervalMicroSeconds(0));    //iMdaAudioRecorderUtility->CropL();     TRAPD(err,iMdaAudioRecorderUtility->RecordL()); if(err){  RLog::Log(_L("RecordL Err"),err); } //iEngineStatus=ERECORDING;

    也用CMdaAudioOutputStream可以进行流播放

    我用的是syimbian的stream带的sdk iAudioStreamPlayer = CMdaAudioOutputStream::NewL(*this); iSettings.iVolume = iAudioStreamPlayer -> MaxVolume()/iVolume; iSettings.iChannels = TMdaAudioDataSettings::EChannelsMono; iSettings.iSampleRate =(aMMF4CCode==KMMFFourCCCodeMP3)? TMdaAudioDataSettings::ESampleRate8000Hz: TMdaAudioDataSettings::ESampleRate32000Hz; #ifndef __WINS__ this->iAudioStreamPlayer->SetDataTypeL(aMMF4CCode); this->iAudioStreamPlayer->SetPriority(EPriorityNormal, EMdaPriorityPreferenceNone); #endif //this->iAudioStreamPlayer->SetDataTypeL(KMMFFourCCCodePCM16); iAudioStreamPlayer -> Open(&iSettings);

    流的播放:

    有时候你只能在内存里面播放amr而不是从文件里面打开。这需要你完成转码和流的播放两步工作。

    在SDK 1.2中你只能用 CAmrToPcmDecoder 在 amrcodec.h 里面。

    如果SDK是 2.0 或者更高的版本中使用 CMMFCodec

           未完待续


    最新回复(0)