加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

IMA ADPCM与PCM格式之间的相互转换(VB.NET ADPCM编解码)

(2013-08-12 11:57:22)

    因为种种原因,最近需要把原始的wav文件压缩成ADPCM格式。但是网上几乎搜不到相关的中文资料。花了相当长的时间,七拼八凑的从一些文章中得到了些信息,终于搞定了它。为了方便遇到跟我一样麻烦的人,我决定把它详细的写下来。

    ADPCM分为Microsoft ADPCM和IMA ADPCM两种数据格式,如果应用到STM32中,建议大家使用IMA ADPCM格式来编码和解码,这种格式方便C语言编程,算法工作量比较小。Microsoft ADPCM解码算法用STM32容易实现,但是编码算法需要耗费的系统资源比较大,影响实时语音数据的传输。

    首先要了解PCM文件格式,一般默认存储为*.wav。

http://s10/mw690/56e19aa7gx6BNh9JXBfb9&690ADPCM与PCM格式之间的相互转换(VB.NET ADPCM编解码)" TITLE="IMA ADPCM与PCM格式之间的相互转换(VB.NET ADPCM编解码)" />

Offset  Size  Name             Description



The canonical WAVE format starts with the RIFF header:
         ChunkID          Contains the letters "RIFF" in ASCII form
                               (0x52494646 big-endian form).
         ChunkSize        36 + SubChunk2Size, or more precisely:
                               4 + (8 + SubChunk1Size) + (8 + SubChunk2Size)
                               This is the size of the rest of the chunk
                               following this number.  This is the size of the
                               entire file in bytes minus 8 bytes for the
                               two fields not included in this count:
                               ChunkID and ChunkSize.
         Format           Contains the letters "WAVE"
                               (0x57415645 big-endian form).

The "WAVE" format consists of two subchunks: "fmt " and "data":
The "fmt " subchunk describes the sound data's format:

12         Subchunk1ID      Contains the letters "fmt "
                               (0x666d7420 big-endian form).
16         Subchunk1Size    16 for PCM.  This is the size of the
                               rest of the Subchunk which follows this number.
20         AudioFormat      PCM = 1 (i.e. Linear quantization)
                               Values other than 1 indicate some
                               form of compression.
22         NumChannels      Mono = 1, Stereo = 2, etc.
24         SampleRate       8000, 44100, etc.
28         ByteRate         == SampleRate * NumChannels * BitsPerSample/8
32         BlockAlign       == NumChannels * BitsPerSample/8
                               The number of bytes for one sample including
                               all channels. I wonder what happens when
                               this number isn't an integer?
34         BitsPerSample    8 bits = 8, 16 bits = 16, etc.
           ExtraParamSize   if PCM, then doesn't exist
           ExtraParams      space for extra parameters


The "data" subchunk contains the size of the data and the actual sound:

36         Subchunk2ID      Contains the letters "data"
                               (0x64617461 big-endian form).
40         Subchunk2Size    == NumSamples * NumChannels * BitsPerSample/8
                               This is the number of bytes in the data.
                               You can also think of this as the size
                               of the read of the subchunk following this
                               number.
44         Data             The actual sound data.

As an example, here are the opening 72 bytes of a WAVE file with bytes shown as hexadecimal numbers:

52 49 46 46 24 08 00 00 57 41 56 45 66 6d 74 20 10 00 00 00 01 00 02 00
22 56 00 00 88 58 01 00 04 00 10 00 64 61 74 61 00 08 00 00 00 00 00 00
24 17 1e f3 3c 13 3c 14 16 f9 18 f9 34 e7 23 a6 3c f2 24 f2 11 ce 1a 0d

http://s9/mw690/56e19aa7gx6BNhnCCUg18&690ADPCM与PCM格式之间的相互转换(VB.NET ADPCM编解码)" TITLE="IMA ADPCM与PCM格式之间的相互转换(VB.NET ADPCM编解码)" />

   

      再来了解一下IMA ADPCM文件的格式,注意一下,这是说明的是4BIT的IMA ADPCM文件,如果使用COOL EDIT软件只能转换成3bit的IMA DPCM文件,需要使用GoldWave软件才能转换成4BIT IMA ADPCM文件。方便验证编码解码是否与使用软件转换的一致。

     用WINHEX打开IMA ADPCM格式文件,文件头如下(十六进制,低地址低字节,高地址高字节):

       00  01  02  03  04  05  06  07     08  09  0A  0B  0C  0D  0E  0F 

0000    52  49  46  46  88  4C  65  00    57  41  56  45  66  6d  74  20   //文件开始

0010    14  00  00  00  11  00  01  00    44  AC  00  00  6D  56  00  00

0020    00  04  04  00  02  00  F9  07    64  61  74  61  00  4C  65  00
0030    20  05  00  00  3C  13  3C  14    16  F9  18  F9  34  E7  23  A6

0040    3C  F2  24  F2  11  CE  1A  0D    ..............................   //第一个BLOCK

     ................................................................

     ................................................................

     ................................................................

0430   2D 02  1C 00.............................................   //第二个BLOCK

    .........................................................
0x0000~0x0003     "RIFF"         

0x0004~0x0007     4个字节表示文件大小,不包含前8个字节

0x0008~0x000b     “WAVE”,所有wav文件均相同,表明文件类型

0x000c~0x000f     “fmt ”,fmt chunk起始位置,4个字节

0x0010~0x0013      fmt chunk大小,四个字节

0x0014~0x0015      格式类型:0x0011---IMA ADPCM    0x0001---PCM

0x0016~0x0017      声道数: 0x0001---单声道,0x0002---双声道

0x0018~0x001b      采样频率

0x001c~0x001f      播放码率

0x0020~0x0021      数据块尺寸,根据采样频率不同,块大小不同

0x0022~0x0023      数据块头占用字节,IMA ADPCM为4个字节

0x0024~0x0025      不同的格式添加的描述的多余字节数

0x0026~0x0027      每个数据块包含的PCM数据的个数

0x0028~0x002b      “data”数据块开始

0x002c~0x002f       所有数据块的大小

0x0030~0x031       第一个block块的第一个未压缩的数据

0x0032            index

0x0033            保留,未用

0x0034            第2个(高4bit)和第1个(低4bit)数据,每个占用4bit,

......................

......................

0x0430~0x0431   第二个block块的第一个未压缩的数据

0x0432          index

0x0433          保留

......................

......................
附VB.NET编解码程序(单声道,16bit,44.1KHZ)

1、编码程序

Dim my_file As IO.FileStream
        Dim my_txtfileStream As IO.FileStream
        Dim my_buf(4100) As Byte
        Dim Write_buf(2100) As Byte
        Dim i, len As Integer
        Dim Read_Data_len As Integer
        Dim diff As Integer          '
        Dim tempstep, pcmstep As Integer       '
        Dim prev_sample, valpred, val As Integer       '
        Dim index As Short           '
        Dim code As Short            '
        'Dim Add_code As Integer
        Dim outputcode As Short
        Dim bufferstep As Byte
        Dim vpdiff As Integer
        my_txtfileStream = IO.File.Open("d:\\mp3\\PCM_TEST_ADPCM.wav", IO.FileMode.OpenOrCreate, IO.FileAccess.Write)
        my_file = IO.File.Open(DataFile_PathAndName, IO.FileMode.Open, IO.FileAccess.Read)
        my_file.Seek(0, IO.SeekOrigin.Begin)
        my_file.Read(my_buf, 0, &H2B)
        Dim RIFF_DATA() As Byte = {&H52, &H49, &H46, &H46, &H88, &H4C, &H65, &H0, &H57, &H41, &H56, &H45, &H66, &H6D, &H74, &H20, &H14, &H0, &H0, &H0, &H11, &H0, &H1, &H0, &H44, &HAC, &H0, &H0, &H6D, &H56, &H0, &H0, &H0, &H4, &H4, &H0, &H2, &H0, &HF9, &H7, &H64, &H61, &H74, &H61, &H0, &H0, &H20, &H0}
        my_txtfileStream.Seek(0, IO.SeekOrigin.Begin)
        my_txtfileStream.Write(RIFF_DATA, 0, 48)
        For Read_Data_len = 0 To 4082 * 3000 Step 4082
            len = Read_Data_len + &H2C
            my_file.Seek(len, IO.SeekOrigin.Begin)
            my_file.Read(my_buf, 0, 4082)
            If Read_Data_len = 0 Then
                '------------------------
                '第一次index取值为0
                Write_buf(2) = &H0
                index = 0
            Else
                '--------------------------
                '第二次后index值为前一个block

                '(1024个字节,根据采样率不同,block大小不同)的最后一次更新index值
                Write_buf(2) = Pre_index
                index = Pre_index
            End If
            Write_buf(0) = my_buf(0)
            Write_buf(1) = my_buf(1)
            Write_buf(3) = 0
            '-----------------------------------------
            '每个block开始第一个presample值为该block的第一个值
            valpred = my_buf(0) + my_buf(1) * 256
            If valpred > 32767 Then
                valpred = valpred - &H10000
            End If
            '-----------------------------------------
            bufferstep = 0
            For i = 0 To 2039 Step 1
                val = my_buf(2 * i + 2) + my_buf(2 * i + 3) * 256
                If val > 32767 Then
                    val = val - &H10000
                End If
                diff = val - valpred     '// 计算出和上一个的增量
                If (diff >= 0) Then
                    code = 0
                Else
                    diff = -diff
                    code = 8
                End If                '// sb 保存的是符号位
                pcmstep = stepsizeTable(index)
                '---------------------------------------------
                '---------------------------------------------
                tempstep = pcmstep
                If diff >= tempstep Then
                    code = code Or 4
                    diff = diff - tempstep
                End If
                tempstep = tempstep >> 1
                If diff >= tempstep Then
                    code = code Or 2
                    diff = diff - tempstep
                End If
                tempstep = tempstep >> 1
                If diff >= tempstep Then
                    code = code Or 1
                End If
                '-----------------------------------------------
                vpdiff = pcmstep >> 3
                If (code And 4) = 4 Then
                    vpdiff += pcmstep
                End If
                If (code And 2) = 2 Then
                    vpdiff += (pcmstep >> 1)
                End If
                If (code And 1) = 1 Then
                    vpdiff += (pcmstep >> 2)
                End If
                '-----------------------------------------
                If code > 7 Then
                    valpred -= vpdiff
                Else
                    valpred += vpdiff
                End If

                '
                If (valpred > 32767) Then
                    valpred = 32767
                ElseIf (valpred < -32768) Then
                    valpred = -32768
                End If
                '---------------------------------------------
                index += indexTable(code)
                If (index < 0) Then
                    index = 0
                End If

                If (index > 88) Then
                    index = 88
                End If

                
                If bufferstep = 0 Then
                    outputcode = (code And &HF)
                    bufferstep = 1
                Else
                    outputcode = (code << 4) And &HF0 Or outputcode
                    Write_buf(i \ 2 + 4) = outputcode And &HFF
                    bufferstep = 0
                End If
            Next
            Pre_index = index
            prev_sample = valpred
            '-----------------------------------------
            my_txtfileStream.Seek((len \ 4082) * 1024 + &H30, IO.SeekOrigin.Begin)
            my_txtfileStream.Write(Write_buf, 0, 1024)
            '-----------------------------------------
        Next
        my_txtfileStream.Close()
        my_file.Close()

2、解码程序

Dim myfilestream As IO.FileStream
        Dim my_txtfileStream As IO.FileStream
        Dim Data_len As Integer
        Dim len As Integer
        Dim Read_Data_len As Integer
        Dim my_buf(1100) As Byte
        Dim Write_buf(2100) As Short
        Dim i, j As Integer
        Dim bufferstep, inputbuffer As Byte     
        Dim OutData(4100) As Byte
        Dim pcmstep As Integer
        Dim code As Short
        Dim vpdiff As Integer
        Dim presample As Integer
        Dim index As Short
        my_txtfileStream = IO.File.Open("D:\\mp3\\IAM_ADPCM_TEST_PCM.wav", IO.FileMode.OpenOrCreate, IO.FileAccess.ReadWrite, IO.FileShare.ReadWrite)
        myfilestream = IO.File.Open(DataFile_PathAndName, IO.FileMode.Open, IO.FileAccess.Read)
        Data_len = myfilestream.Length
        myfilestream.Seek(0, IO.SeekOrigin.Begin)
        myfilestream.Read(my_buf, 0, &H30)
        Read_Data_len = 0
        index = 0
        If (my_buf(0) = &H52 And my_buf(1) = &H49 And my_buf(2) = &H46 And my_buf(3) = &H46) Then
            Dim RIFF_DATA() As Byte = {&H52, &H49, &H46, &H46, &H88, &H4C, &H65, &H0, &H57, &H41, &H56, &H45, &H66, &H6D, &H74, &H20, &H10, &H0, &H0, &H0, &H1, &H0, &H1, &H0, &H44, &HAC, &H0, &H0, &H88, &H58, &H1, &H0, &H2, &H0, &H10, &H0, &H64, &H61, &H74, &H61, &HA, &HCA, &H76, &H0}
            my_txtfileStream.Seek(0, IO.SeekOrigin.Begin)
            my_txtfileStream.Write(RIFF_DATA, 0, 44)
            For Read_Data_len = 0 To 1024 * 3000 Step 1024
                len = Read_Data_len + &H30
                myfilestream.Seek(len, IO.SeekOrigin.Begin)
                myfilestream.Read(my_buf, 0, 1024)
                index = my_buf(2)
                '---------------------------------------------------------------------------------                presample = my_buf(0) + my_buf(1) * 256

                '//每一个block里面帧头有1个未压缩的数据存储时 先低后高
                If presample > 32767 Then
                    presample = presample - &H10000
                End If
                Write_buf(0) = presample '//存储第1个没有被压缩的数据
                '---------------------------------------------------------------------------------                '---------------------------------------------------------------------------------                pcmstep = stepsizeTable(index)
                bufferstep = 0
                For i = 0 To 2039 Step 1
                    '
                    If bufferstep = 1 Then
                        code = (inputbuffer >> 4) And &HF
                        bufferstep = 0
                    Else
                        inputbuffer = my_buf(i \ 2 + 4)
                        code = inputbuffer And &HF
                        bufferstep = 1
                    End If
                    '---------------------------------------------------------
                    '-----------------------------------------------
                    vpdiff = pcmstep >> 3
                    If (code And 4) = 4 Then
                        vpdiff += pcmstep
                    End If
                    If (code And 2) = 2 Then
                        vpdiff += (pcmstep >> 1)
                    End If
                    If (code And 1) = 1 Then
                        vpdiff += (pcmstep >> 2)
                    End If
                    '-----------------------------------------------
                    If code > 7 Then
                        presample -= vpdiff
                    Else
                        presample += vpdiff
                    End If
                    '---------------------------------
                    If (presample > 32767) Then
                        presample = 32767
                    ElseIf (presample < -32768) Then
                        presample = -32768
                    End If
                    '-----------------------------------
                    index += indexTable(code)
                    If (index < 1) Then
                        index = 0
                    ElseIf (index > 88) Then
                        index = 88
                    End If
                    '------------------------------------
                    pcmstep = stepsizeTable(index)

                    '
                    Write_buf(i + 1) = presample
                    'Write_buf(i) = iNewSample
                Next
                For j = 0 To 2040 Step 1
                    OutData(2 * j) = Write_buf(j) And &HFF
                    OutData(2 * j + 1) = (Write_buf(j) >> 8) And &HFF
                Next
                my_txtfileStream.Seek((len \ 1024) * 4082 + &H2C, IO.SeekOrigin.Begin)
                my_txtfileStream.Write(OutData, 0, 4082)
            Next
            my_txtfileStream.Close()
            my_txtfileStream.Dispose()
            myfilestream.Close()
            myfilestream.Dispose()
        Else
            MsgBox("Read file data error!")
        End If

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有