调用CImg库显示WAV格式音频波形

最近在做傅里叶变换和小波变换时经常要通过显示波形来检验算法,但通过visual studio之类显示波形又显得麻烦,而且不能跨平台。

CImg是一个跨平台的C++的图像处理库,提供的图像处理等功能十分强大,而且加入项目中十分轻便,只需将头文件包含在项目中即可,十分轻便。

可自行到http://www.cimg.eu/下载

利用它来在linux、Mac OS X中显示波形,再合适不过了,下面是音频波形显示的代码。

主函数

main.cpp

#include <iostream>
#include "wavfile.h"
#include "CImg.h"

using namespace std;
using namespace cimg_library;
using namespace AudioUtils;

int main(void)
{
    WaveFile *wf=new WaveFile();
    /*
    读取本目录里的audio.wav文件,并生成波形
    */
    wf->readwav("audio.wav");

    int datalength=wf->size()/wf->bit();

    wf->output_WAVfile_info();

    int *data=(int*)malloc(sizeof(int)*datalength);
    for(int i=0;i<datalength;i++)
    {
        data[i]=(float)(wf->data[i]/100);
    }
    /*
    调用CImg显示波形
    */
    const unsigned char red[] = { 255, 0, 0 }, yellow[] = { 255, 255, 0 },
    green[] = { 0, 255, 0 };
    /*
    一维信号
    */
    int num=wf->size()/(8*(wf->bit()));
    CImg<float> signal_wav(num,1, 1, 1, 0);
    for(int i=0;i<num;i++)
    {
        signal_wav[i]=(data[i*8])*100;
    }

    CImg<unsigned char> signal_visu( 1200, 400, 1, 3, 0 );
   /*
   画格子
   */
    signal_visu.draw_grid( 50, 50, 0, 0, 0, 0, yellow, 0.5 );

    signal_visu.draw_graph(signal_wav,red,1,3,0,35000,-35000,0);
    /*
    画波形
    */
    CImgDisplay main_disp_wav (signal_visu,  "signal" );

    getchar();

    return 0;
} 

wavfile.h

读取WAV格式文件

  1 #ifndef WAVFILE_H
  2 #define WAVFILE_H
  3 #include <iostream>
  4 #include <fstream>
  5 using namespace std;
  6 
  7 /*
  8 BYTE=unsigned char(完全等同)         WORD=unsigned short(完全等同)         DWORD=unsigned long(完全等同)
  9 
 10 unsigned char是无符号字节型
 11 */
 12 namespace AudioUtils
 13 {
 14     /*
 15     RIFF WAVE Chunk
 16     ==================================
 17     |       |所占字节数|  具体内容   |
 18     ==================================
 19     | ID    |  4 Bytes |   'RIFF'    |
 20     ----------------------------------
 21     | Size  |  4 Bytes |             |
 22     ----------------------------------
 23     | Type  |  4 Bytes |   'WAVE'    |
 24     ----------------------------------
 25     
 26 
 27     */
 28     class RiffBlock
 29     {
 30     public:
 31         void ReadRiff(ifstream &infile)
 32         {
 33             int i=0;
 34             for(i=0;i<4;i++)
 35             {
 36                 infile.read((char*)&m_RiffID[i],1);
 37             }
 38             infile.read((char*)&m_RiffSize,4);
 39             for(i=0;i<4;i++)
 40             {
 41                 infile.read((char*)&Format[i],1);
 42             }
 43         }
 44 
 45         /*
 46         此处应当声明为pravte 用函数return, 不安全代码,暂简略处理
 47         */
 48         unsigned char m_RiffID[4];
 49         unsigned long m_RiffSize;
 50         unsigned char Format[4];
 51     };
 52     /*
 53     ====================================================================
 54     |               |   字节数  |              具体内容                |
 55     ====================================================================
 56     | ID            |  4 Bytes  |   'fmt '                             |
 57     --------------------------------------------------------------------
 58     | Size          |  4 Bytes  | 数值为16或18,18则最后又附加信息     |
 59     --------------------------------------------------------------------  ----
 60     | FormatTag     |  2 Bytes  | 编码方式,一般为0x0001               |     |
 61     --------------------------------------------------------------------     |
 62     | Channels      |  2 Bytes  | 声道数目,1--单声道;2--双声道       |     |
 63     --------------------------------------------------------------------     |
 64     | SamplesPerSec |  4 Bytes  | 采样频率                             |     |
 65     --------------------------------------------------------------------     |
 66     | AvgBytesPerSec|  4 Bytes  | 每秒所需字节数                       |     |===> WAVE_FORMAT
 67     --------------------------------------------------------------------     |
 68     | BlockAlign    |  2 Bytes  | 数据块对齐单位(每个采样需要的字节数) |     |
 69     --------------------------------------------------------------------     |
 70     | BitsPerSample |  2 Bytes  | 每个采样需要的bit数                  |     |
 71     --------------------------------------------------------------------     |
 72     |               |  2 Bytes  | 附加信息(可选,通过Size来判断有无) |     |
 73     --------------------------------------------------------------------  ----
 74 
 75     */
 76     class FmtBlock
 77     {
 78     public:
 79         //seekg()移动文件指针
 80         void ReadFmt(ifstream &infile)
 81         {
 82             int i=0;
 83             for(i=0;i<4;i++)
 84             {
 85                 infile.read((char*)&m_FmtID[i],1);
 86             }
 87             infile.read((char*)&m_FmtSize,4);
 88             int size=m_FmtSize;
 89             infile.read((char*)&m_FmtTag,2);
 90             infile.read((char*)&m_Channels,2);
 91             infile.read((char*)&m_SamplesPerSec,4);
 92             infile.read((char*)&m_AverageBytesPerSec,4);
 93             infile.read((char*)&m_BlockAlign,2);
 94             infile.read((char*)&m_BitsPerSample,2);
 95             if(size==18)
 96             {
 97                 infile.read((char*)&m_OtherMeg,2);
 98             }
 99         }
100         
101         int BitsPerSample()
102         {
103             return (m_BitsPerSample/8);
104         }
105     
106         unsigned char m_FmtID[4];
107         unsigned long m_FmtSize;
108         unsigned short m_FmtTag;
109         unsigned short m_Channels;
110         unsigned long m_SamplesPerSec;
111         unsigned long m_AverageBytesPerSec;
112         unsigned short m_BlockAlign;
113         unsigned short m_BitsPerSample;
114         unsigned short m_OtherMeg;
115     };
116 
117     /*
118     Data Chunk
119     ==================================
120     |       |所占字节数|  具体内容   |
121     ==================================
122     | ID    |  4 Bytes |   'data'    |
123     ----------------------------------
124     | Size  |  4 Bytes |             |
125     ----------------------------------
126     | data  |          |             |
127     ----------------------------------
128 
129     1Bytes
130     ---------------------------------------------------------------------
131     |   单声道    |    取样1    |    取样2    |    取样3    |    取样4    |
132     |                   |  --------------------------------------------------------
133     |  8bit量化 |    声道0    |    声道0    |    声道0    |    声道0    |
134     ---------------------------------------------------------------------
135     |   双声道    |          取样1                      |           取样2                      |
136     |                   |--------------------------------------------------------
137     |  8bit量化 |  声道0(左)  |  声道1(右)  |  声道0(左)  |  声道1(右)  |
138    1WORD
139    ---------------------------------------------------------------------
140     |                    |          取样1                        |           取样2                       |
141     |   单声道     |--------------------------------------------------------
142     | 16bit量化 |    声道0       |  声道0          |    声道0       |  声道0          |
143     |                    | (低位字节)  | (高位字节)  | (低位字节)  | (高位字节)   |
144     ---------------------------------------------------------------------
145     |                    |                         取样1                                                       |
146     |   双声道     |--------------------------------------------------------
147     | 16bit量化 |  声道0(左)    |  声道0(左)  |  声道1(右)   |  声道1(右)   |
148     |                     | (低位字节)  | (高位字节)  | (低位字节)  | (高位字节)  |
149     ---------------------------------------------------------------------
150 
151     */
152     class DataBlock
153     {
154     public:
155         void ReadData(ifstream &infile,int BitsPerSample)//BitsPerSample:1 8bit;2 16bit
156         {
157             int i=0;
158 
159             bool un_find_data=true;
160             while(un_find_data)
161             {
162                 infile.read((char*)&temp,1);
163                 if(temp=='d')
164                 {
165                     infile.read((char*)&temp,1);
166                     if(temp=='a')
167                     {
168                         infile.read((char*)&temp,1);
169                         if(temp=='t')
170                         {
171                             infile.read((char*)&temp,1);
172                             if(temp=='a')
173                             {
174                                 un_find_data=false;
175                             }
176                         }
177                     }
178 
179                 }
180             }
181 
182             infile.read((char*)&m_DataSize,4);
183             m_NumSamples=m_DataSize/BitsPerSample;
184             if(BitsPerSample==1)
185             {
186               m_Data8=new char[m_NumSamples];
187               for(i=0;i<m_NumSamples;i++)
188               {
189                 infile.read((char*)&m_Data8[i],BitsPerSample);
190               }
191             }
192             else if(BitsPerSample==2)
193             {
194               m_Data16=new short[m_NumSamples];
195               for(i=0;i<m_NumSamples;i++)
196               {
197                 infile.read((char*)&m_Data16[i],BitsPerSample);
198               }
199             }
200             
201             
202         }
203         
204         short *m_Data16;
205         char *m_Data8;
206 
207         unsigned char temp;
208         unsigned char m_DataID[4];
209         unsigned long m_DataSize;
210         
211         
212         int    m_NumSamples;//样本数量
213     };
214     class WaveFile
215     {
216     public:
217         void readwav(char *path)
218         {
219             ifstream infile(path,ios::binary);
220             m_Riff = new RiffBlock();
221             m_Fmt = new FmtBlock();
222             m_Data = new DataBlock();
223 
224             m_Riff->ReadRiff(infile);
225             m_Fmt->ReadFmt(infile);
226             m_Data->ReadData(infile,m_Fmt->BitsPerSample());
227             int i=0;
228             data=new int[m_Data->m_DataSize/m_Fmt->BitsPerSample()];
229             for(i=0;i<(m_Data->m_DataSize)/(m_Fmt->BitsPerSample());i++)
230             {
231                 
232                 if(m_Fmt->BitsPerSample()==1)
233                 {
234                    data[i]=(int)m_Data->m_Data8[i];
235                 }
236                 else if(m_Fmt->BitsPerSample()==2)
237                 {
238                    data[i]=(int)m_Data->m_Data16[i];
239                 }
240             }
241             
242         }
243         
244         
245         void output_WAVfile_info(void)//test
246         {
247             cout<<"Audio Msg:"<<endl;
248             /*
249             RIFF WAVE Chunk Message
250             */
251             cout<<m_Riff->m_RiffID<<endl;
252             cout<<m_Riff->m_RiffSize<<endl;
253             cout<<m_Riff->Format<<endl;
254 
255             /*
256             Fmt Message
257             */
258             cout<<m_Fmt->m_FmtID<<endl;
259             cout<<m_Fmt->m_FmtSize<<endl;
260             cout<<m_Fmt->m_FmtTag<<endl;
261             cout<<m_Fmt->m_Channels<<endl;
262             cout<<m_Fmt->m_SamplesPerSec<<endl;
263             cout<<m_Fmt->m_AverageBytesPerSec<<endl;
264             cout<<m_Fmt->m_BlockAlign<<endl;
265             cout<<m_Fmt->m_BitsPerSample<<endl;
266             cout<<m_Fmt->m_OtherMeg<<endl;
267             /*
268             Data Chunk Message
269             */
270             cout<<m_Data->temp<<endl;
271             cout<<m_Data->m_DataID<<endl;
272             cout<<m_Data->m_DataSize<<endl;
273 
274         }
275         int size(void)//音频长度
276         {
277             return m_Data->m_DataSize;
278         }
279         int channels(void)
280         {
281             return m_Fmt->m_Channels;
282         }
283         int bit(void)
284         {
285             return m_Fmt->BitsPerSample();
286         }
287         int *data;
288     private: 
289          RiffBlock            *m_Riff;
290          FmtBlock            *m_Fmt;
291          DataBlock            *m_Data;
292     };
293 }
294 
295 #endif

编译时运行如下命令:

1 g++ -o out main.cpp -O2 -lm -lpthread -L/usr/X11R6/lib -lm -lpthread -lX11

结果:

调用CImg库显示WAV格式音频波形