版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Microsoft ADPCM 编码解码算法因为种种原因,最近需要把原始的wav文件压缩成ADPCM格式。但是网上几乎搜不到相关的中文资料。花了相当长的时间,七拼八凑的从一些文章中得到了些信息,终于搞定了它。为了方便遇到跟我一样麻烦的人,我决定把它详细的写下来。1. 关于DPCMDPCM是differential pulse code modulation的缩写,也就是差分脉冲编码调制的意思。他的主要思想是通过已知的数据预测下一个数据,然后传递预测值与实际值之间的差值。具体的细节可以在很多信号处理相关的书上找到。一般的DPCM编码器都是采用的线性预测。假设传递的数据是X1,X2,.Xn,而下一
2、个数据,Xn+1还是未知。可以通过前面的X1,X2,.Xn的加权和来预测Xn+1,也就是Xn+1 = (Ai*Xi),其中i属于1.n为了简化计算,大部分编码的实现只取前两项,也就是, Xn+1 = a*Xn + b*Xn-1, 现在,最主要的事情就是如何对a,b进行取值,才能使得Xn+1的误差最小。如果假设 xi 是预测值,xi是实际值,那么,(xi-xi)2 最小的时候,a,b就是最优的。设 F=(Xi-Xi)2,因为 Xi = a*Xi-1 + b*Xi-2,可以得出,F是关于a,b的二元函数.也就是 F=f(a,b) 。可以分别对a和b求偏导数,求出它的极值点。fa(a,b) = 0
3、;fb(a,b) = 0 ;可以得到a * (Xi-1)2 + b * (Xi-1)*(Xi-2) =Xi*Xi-1a * (Xi-1)*(Xi-2) + b * (Xi-2)2=Xi*Xi-2如果设alpha = (Xi-1)2beta= (Xi-1)*(Xi-2)gama= (Xi-2)2m = Xi*Xi-1 n = Xi*Xi-2上面的式子就可以写成a*alpha + b*beta = ma*beta + b*gama = n算出alpha,beta,gama,m,n以后,a和b的值就可以计算出来了,实际上我们只需要一个循环遍历前n个数就能把它们都求出来。2. ADPCM的思想如果直接
4、使用DPCM进行编码的话,是得不到什么压缩的效率的。缘故是,需要传输或保存的是预测值后的值与实际值之间的差值,这与原来的数据占用同样的空间。为了满足我们原始的压缩数据的动机,你可以对这些差值进行各种各样的编码。因为,大部分情况下,差值都是像1,1,1,2,3,5,5,5之类的数。可以对它们进行通常的游程编码或者huffman编码,运气好的话能够得到很大的压缩比。这样做会有一个很大的弊端。因为有些数据可能之间的联系会呈线性或者某种连续函数的性质。但是大部分情况下,数据的分布还是有一定的离散性的。当数据之间出现很大的跳跃的时候,这种方法就显得很苍白无力了。我们可以这么做,每次对得到的差值用一个随着
5、差值大小变化的数来除。这样就可以随着差值的变化,不断调整比例因子。这样出现较大的跳跃时也能把我们要存储的差值限定在一个较小的范围之内。如果你现在有些迷惑,没事,我们换种方式来说明一下。假设差值是 diff,也就是 diff = Xi - Xi,那么,diff就有可能变动很大,如果引入一个不断变化的因子iDelta,那么,diff = diff / iDelta,而对于iDelta,每当diff变大的时候,他就变大比较大,当diff变得比较小的时候,他就相应的减小。这样,我们的diff就能保持相对的稳定了。通过iDelta的引入,可以使得我们的DPCM编码自动的适应数据间大幅度的跳跃。这就是自适
6、应脉冲编码调制,ADPCM的主要思想。你现在可能会想,iDelta到底怎么变化,才能自动的匹配diff的变化? 一种可行的方法就是,把它定义为diff的一个函数,这个函数根据不同的diff的值的大小取不同大小的值。通常我们会做一个iDelta值的表,通过 diff作为索引,这样,就可以根据不同的diff值,iDelta就可以作相应的变化了。3. WAV文件的格式IMA-ADPCM压缩的音频文件并没有一个统一的格式。我们现在只考虑微软的自己的wav格式。apple公司的网站上有一篇写得很不错的 technical note, 可以看后面的链接地址。wav文件是微软定义的一系列资源文件中的一个。这
7、些文件通常是由一系列的chunk组成。所有的文件都以RIFF标记开头,然后指出文件的大小。接着表明类型,比如WAVE,MIDI等. 一个wav文件的结构大致如下_| RIFF WAVE Chunk|groupID = RIFF|riffType = WAVE|_| Format Chunk| ckID = fmt |_|_| Sound Data Chunk | ckID = data|_|_|一般每一个chunk都有一个id和一个size来表明chunk的类型和大小,这样就可以很容易的将chunk读出。id一般是四个字节,用ASCII 码的值来标记,比如data chunk的id就是data
8、,而format chunk的id就是fmt .要注意的是,size表明的大小是以字节为单位的,而且不包括id和size字段本身所占的空间。原始的wave文件一般只有两个chunk,也就是fmt和data,原则上,你可以添加任何的其他chunk,用来添加不同的信息.这也是wav文件出现很多变种的原因.windows下一般有两种格式的wav文件,一种是普通未压缩的原始数据,另一种就是采用了ADPCM压缩了的。无论哪一种,你都可以使用现存的播放器播放。未压缩的存储格式比较简单,只需要一个format chunk来描述格式信息,然后再用一个data chunk来存储数据。现在的wave文件按规定都必
9、须包括一个fact chunk,而且在fmt chunk里必须包含一个扩展了的format description,这通过一个WAVEFORMATEX的结构来描述,但是以WAVE_FORMAT_PCM为格式的文件并不需要这些额外的信息。下面是WAVEFORMATEX的定义typedef struct waveformat_extended_tag WORD wFormatTag;/* format type */WORD nChannels;/* number of channels (i.e. mono, stereo.) */DWORD nSamplesPerSec; /* sample
10、rate */DWORD nAvgBytesPerSec; /* for buffer estimation */WORD nBlockAlign;/* block size of data */WORD wBitsPerSample;/* Number of bits per sample of mono data */WORD cbSize; /* The count in bytes of the extra size */ WAVEFORMATEX;其中wFormatTag标明了文件的类型,这样我们就可以判断后面的数据是以什么方式来存储和表示的了,比如,#define WAVE_FOR
11、MAT_PCM 0x0001 这样,WAVE_FORMAT_PCM表示的就是普通的原始wav文件格式。再比如,#define WAVE_FORMAT_ADPCM 0x0002 我们就可以知道,wFormatTag的值为WAVE_FORMAT_ADPCM就是以ADPCM压缩的格式了。具体的文件的类型有很多种,详细的定义可以在mmreg.h头文件里找到。WAVEFORMATEX是所有的format所共有的一个头部,不同格式会在后面添加自己的相关的数据段,添加的段的字节数可以通过cbSize来得到。cbSize用来描述不同的格式后面添加的多余的字节数。比如WAVE_FORMAT_ADPCM格式的fo
12、rmat的这个值就是32,表明还有32个字节在后面。另外一些字段会在后面解释。fact chunk是一个值得注意的chunk,现在的wav文件,无论是否压缩,都必须包含一个fact chunk,用来存储文件文件相关的信息。但是现在它里面只定义了一个字段,用来指出文件里一个有多少个sample。4. WAVE_FORMAT_ADPCM 的wav文件格式再回到我们前面讨论的DPCM,对于预测后得到的差值diff,我们应该怎么处理它才能的比较好的压缩比?比如我们的数据是3,3,4,7,9,2. 可以注意到,如果预测做的比较好的话,得到的差值可能会很小,甚至为0,假设我们的原始的音频数据时16位的,那
13、么,如果仍然使用16位来存储这些数据,肯定是一种浪费。很直观的,你会想到减少每一个diff的存储空间,没错,这就是ADPCM格式压缩的wav文件采用的方法。在压缩过的wav文件里,每一个diff使用4个bit来存储的,被称作一个nibble。这样,我们将一个16bit的原始数据缩减到4bit,可以得到一个稳定的4:1的压缩比。因为我们采用的是预测编码,这就需要选择预测的系数a和b,我们在前面已经详细的推导过了a和b的计算方法。现在,我们需要解决的是,一个wav文件,我们是对整个文件计算来得出合理的a和b吗?显然,对整个文件采用相同预测系数并不实际,首先是计算起来麻烦,再一个,一个wav文件太长
14、,如果对整个文件采用相同的a和b起不到什么效果,对于局部的数据,偏差仍然会是很大,这就丧失了我们的初衷。不妨这么考虑,我们将音频数据分成不同的块,分别对每一块求不同的系数,来进行预测编码,一般声音都是连续的,在一个局部的小区域里变化很小,所以a和b就能很准确的预测出每一个值了。在apple下系统下,每一个块称作一个packet,以64个sample为固定的大小。而在windows下被称作一个block,他的大小是可变的,所包含的sample的个数由nSamplesPerBlock来指出,后面我们会看到. 在WAVEFORMATEX结构里的nBlockAlign描述了每一个block所占的字节数
15、.不同的采样率会有不同的大小,下面是blockAlign在不同采样率大小下的值nSamplesPerSec x ChannelsnBlockAlign8k25611k25622k51244k1024这样,我们可以把压缩后的数据以block为单位存储在data chunk里.但是,除了压缩的数据以外,我们同样还需要存储当前块的a和b系数的值.下面是ADPCM格式的format chunk的具体定义 typedef struct adpcmcoef_tag int iCoef1;int iCoef2; ADPCMCOEFSET;typedef struct adpcmwaveformat_tag
16、WAVEFORMATEX wfxx;WORD wSamplesPerBlock;WORD wNumCoef;ADPCMCOEFSET aCoeffwNumCoef; ADPCMWAVEFORMAT;这下子就很清楚了,ADPCMCOEFSET里的两个正是我们的系数a和b,ADPCMWAVEFORMAT扩展了WAVEFORMATEX的结构,添加了一些列自有的信息,wSamplesPerBlock表明了每一个block里含有多少个sample,而wNumCoef说明了后面的系数表的大小.微软定义了一个7个的标准的系数表,当然,你也可以在后面添加自己的系数,下面是他的定义:Coef SetCoef1C
17、oef20 256 01 512 -2562 0 03 192 644 240 05 460 -2086 392 -232每一个coef是使用定点数来表示的,这样是为了方便存储和加快编解码的速度,使用的时候需要把它除以256得到实际的值可以通过我们前面的方法算出来a和b,然后再在这里找出和a和b最近的一组数. 因为通常情况下,音频文件的数据量是相当大的,如果对每个样本都算出最优的a和b,他们的存储就会占相当大的空间, 如果我们只用这七个,可以把他们放在文件头里的一个表里面,然后对每一个block只需要存储他们最近的系数在表里的索引值就行了. ADPCMWAVEFORMAT里的aCoeff存储的
18、就是这个系数表.ADPCM压缩格式存储的wav文件,一般包含三个chunk,一个是 format chunk,就是我们上面的那个ADPCMWAVEFORMAT结构. 接着会有一个fact chunk,这是必不可少的,现在它里面只有一个字段,就是整个文件含有的sample的个数.接下来是最重要的data chunk, 它里面存储的就是我们的数据,里面是一个block接一个block存储的。一般block由三部分组成,分别是header,data,padding。header的定义:typedef struct adpcmblockheader_tag BYTE bPredictornChanne
19、ls;int iDeltanChannels;int iSamp1nChannels;int iSamp2nChannels; ADPCMBLOCKHEADER;bPredictor就是我们前面说过的系数表的索引值。如果文件的channel不止一个,那么不同的channel就挨个向后排。channels12stereo_ _| left| right|_|_|123_ _ _| left| right| center|3 channel|_|_|_|1234_ _ _ _| front| front| rear| rear|quad| left| right| left| right|_|_|
20、_|_|1234_ _ _ _ _| left| center| right| surround |4 channel|_|_ _|_|_|123456_ _ _ _ _ _| left| left| center| right| right|surround |6 channel| center| center|_|_|_|_|_|_ADPCMBLOCKHEADER结构里的iDelta是iDelta的初始值。iSample1和iSample2是用来预测后面数据的初始的两个samplevalue。需要注意的是,iSample1实际上是第二个sample的值,而iSample2则是第一个。这样做
21、是为了方便编解码。header后面紧接着就是数据了。data里面是4bit接4bit的sample数据。如果存储的数据没有到整整一个block,那么就要在最后存一系列的0来使整体大小填满整个block,这被叫做padding5. 编码和解码首先要说明的是,我们通过预测之后的到的diff,需要再除上一个iDelta的比例因子,得到的值就是我们要存储的结果。这个值一般被称作 iErrorDelta,他一般是一个可正可负的有符号数。因为我们需要把这个iErrorDelta存储到一个nibble里,需要对他作一定的裁剪,因为一个nibble是4bit的,如果用补码表示,他的范围从-8到7,很明显,我们
22、的iErrorDelta只要大于7或者小于-8就都得用7或-8 来表示,这样就丧失了一些精度。不过这些损失是很少的,因为如果我们的预测和iDelta比较好的话,大部分的iErrorDelta都会落在-8到7之间。即使丧失一点点音质,人的耳朵基本上听不出来,可以忽略它的影响。每次产生新的iErrorDelta之后,都要根据iErrorDelta的值对iDelta的值进行更改。以使得iDelta能够自动适应sample 的变化。因为一个iErrorDelta被限定到4bit,我们就完全可以做一个16项的表来表示它。下面的这个就是microsoft的标准的 adaptation tableint A
23、daptationTable = 230, 230, 230, 230, 307, 409, 512, 614,768, 614, 512, 409, 307, 230, 230, 230 ;好了,我们现在已经把一切前提工作都做完了,下面就是具体的编码及解码步骤了,因为编解码对于每一个block都是相同的,我们仅仅使用一个block来说明。编码:对每一个block,编码的过程是通过下面的几个步骤进行的确定需要使用的系数predictor确定初始的iDelta值输出block header编码并输出数据其中predictor的确定已经在开头部分详细的描写了。我们只要找出与a和b最相近的系数表的索
24、引值就行了。初始的idelta的值确定有很多种,你可以对每一个block都使用相同的iDelta值。也可以通过开始的几个数据来估计iDelta,比如,通过算出第一个sample的预测值跟实际值之间的diff,可以使用diff/4来表示iDelta,这样iErrorDelta的值就能刚好被限制在-8到 7的比较靠中间的位置每一个block的初始iDelta的值也可以使用前一个block的最后一个iDelta的值来确定。不过需要注意,第一个block的初始iDelta的值需要单独考虑。当predictor和初始的iDelta确定之后,block header就可以写出了。首先将每一个channel
25、的predictor输出再将每一个channel的iDelta输出接着将16bit的第二个sample的PCM值输出(iSample1)最后将16bit的第一个sample的PCM值输出(iSample2)下面就是对剩下的数据编码了。如果block里还有sample尚待编码/ 通过前两个sample预测下一个sample的值lPredSamp = (iSamp1 * iCoef1) + (iSamp2 *iCoef2) / 256/ 计算iErrorDeltaiErrorDelta = (Sample(n) - lPredSamp) / iDelta如果iErrorDelta大于7,把它设成7
26、如果iErrorDelta小于-8,把它设成-8把iErrorDelta作为一个nibble输出/算出使用iDelta和iErrorDelta预测得到的新的sample的值 lNewSamp = lPredSample + iDelta * iErrorDelta把lNewSamp限定到16bit所允许的大小/调整iDelta的值iDelta = iDelta * AdaptionTable iErrorDelta / 256把iDelta限定到16bit所允许大小的范围内,并确保它不为0/更新预测用的两个sample的值iSamp2 = iSamp1;iSamp1 = lNewSample.
27、重复上面的过程,直到block里没有需要再编码的sample解码实际上就是上述过程的逆过程。如果你把上边的编码过程搞明白了,解码的过程就很简单,这里就不详细说了。上面就是它的整个过程,如果你还有什么不明白,可以给我发邮件: 下面是相关的一些链接,可以参考一下。UnderstandingTheDifferencesBetweenAppleAndWindowsIMA-ADPCM Compressed Sound Files:/technotes/tn/tn1081.htmlADPCM reference impl
28、ementation:ftp:/ftp.cwi.nl/pub/audio/adpcm.tar.gz这个描述的比较详细一点:/program/FORMAT/windows/wavec.htmlibsndfile:.au/erikd/libsndfile/SoX Sound eXchange:WAVE File Format:/jglatt/tech/wave.htm /courses/
29、422/projects/WaveFormat/后面附带上我写的一个对8000采样率,单声道16位的wav格式压缩成ADPCM的代码 .#include #include #include #include int AdaptationTable = 230, 230, 230, 230, 307, 409, 512, 614,768, 614, 512, 409, 307, 230, 230, 230;typedef struct _ADPCM_COEF_SETshort coef1;short coef2;ADPCM_COEF_SET;ADPCM_COEF_SET CoeffTable =
30、 256,0,512,-256,0,0,192,64,240,0,460,-208,392,-232;/* force memory align to 1 byte */#pragma pack(push,1)typedef unsigned short WORD;typedef unsigned int DWORD;typedef unsigned char BYTE;typedef struct _RIFFWAVECHUNKDWORD id;DWORD size;DWORD format;RIFFWAVE_CHUNK;typedef struct _WAVEFORMATEXWORD wFo
31、rmatTag;WORD nChannels;DWORD nSamplesPerSec;DWORD nAvgBytesPerSec;WORD nBlockAlign;WORD wBitsPerSample;WORD cbSize;WAVEFORMAT_EX;typedef struct _WAVEFORMATCHUNK DWORD id;DWORD size;WORD wFormatTag;WORD nChannels;DWORD nSamplesPerSec;DWORD nAvgBytesPerSec;WORD nBlockAlign;WORD wBitsPerSample;FORMAT_C
32、HUNK;typedef struct _MICROSOFT_ADPCM_FORMAT DWORD id;DWORD size;WAVEFORMAT_EX format;WORD wSamplesPerBlock;WORD wNumCoef;ADPCM_COEF_SET coeffs7;MICROSOFT_ADPCM_FORMAT;typedef struct _DATA_CHUNKDWORD id;DWORD size;BYTE data1;DATA_CHUNK;typedef structDWORD id;DWORD size;DWORD nSamples;FACT_CHUNK;typed
33、ef structDWORD id;DWORD size;CHUNK;typedef structBYTE predictor;short iDelta;short iSample1;short iSample2;BYTE pNibbles1;BLOCK;#pragma pack(pop)class Wavfilestd:vector chunks;char* pEncoded;int totalSize;int curSize;public:Wavfile(const char* path);int encode();int Wavfile:writeEncodedFile(char* pa
34、th);void calculateCoefficient(ADPCM_COEF_SET& coeff,short* pdata,int len) double alpha = 0;double beta = 0;double gama = 0;double m=0,n=0;int i;for( i=2;ilen;+i )alpha += (double)pdatai-1*pdatai-1; beta += (double)pdatai-1*pdatai-2; gama += (double)pdatai-2*pdatai-2; m += (double)pdatai*pdatai-1; n
35、+= (double)pdatai*pdatai-2;coeff.coef1 = (m*gama-n*beta)*256/(alpha*gama-beta*beta); coeff.coef2 = (m*beta-n*alpha)*256/(beta*beta-alpha*gama); int getClosestCoefficientIndex(ADPCM_COEF_SET& coeff)unsigned diff = -1;int index = 0;for( int i=0;isizeof(CoeffTable)/sizeof(CoeffTable0); +i)int dx = (Coe
36、ffTablei.coef1-coeff.coef1); int dy = (CoeffTablei.coef2-coeff.coef2);if( dx*dx+dy*dy 7 )c = 7;if( c-8 )c = -8;if( c= totalSize )totalSize = (size+totalSize)*2;pEncoded = (char*)realloc(pEncoded,totalSize);memcpy(&pEncodedcurSize,p,size);curSize += size;DATA_CHUNK* getDataChunk()for(int i=0;ichunks.
37、size();+i)if( *(DWORD*)(chunksi)=0x61746164 )return (DATA_CHUNK*)(chunksi);return NULL;FORMAT_CHUNK* getFormatChunk()for(int i=0;ichunks.size();+i)if( *(DWORD*)(chunksi)=0x20746d66 )return (FORMAT_CHUNK*)(chunksi);return NULL;Wavfile()for(int i=0;igetDataChunk(); ADPCM_COEF_SET coeff;int len = pChun
38、k-size;short* p = (short*)pChunk-data;short* pend = p + (len+1)/2;this-calculateCoefficient(coeff,p,500);int index = this-getClosestCoefficientIndex(coeff); ADPCM_COEF_SET* pCoeff = &CoeffTableindex;pbuff-iSample2 = *p+;pbuff-iSample1 = *p+;short iDelta = (pCoeff-coef1*pbuff-iSample1 +pCoeff-coef2*p
39、buff-iSample2)/256 - *p;iDelta /= 4;if( iDeltaiDelta = iDelta;pbuff-predictor = index;short iSample1 = pbuff-iSample1;short iSample2 = pbuff-iSample2;int flag = 0;BYTE bytebuff = 0;int cnt = 0;while( pcoef1*iSample1 + pCoeff-coef2*iSample2)/256;char iErrorDelta = (*p - iPredSamp)/iDelta; int remaind
40、er = (*p-iPredSamp)%iDelta; if( remainderiDelta/2 ) +iErrorDelta;if( iErrorDelta7 )iErrorDelta = 7;if( iErrorDelta-8 )iErrorDelta = -8;short iNewSample = iPredSamp + iErrorDelta*iDelta;iDelta = iDelta * AdaptationTabletrimToNibble(iErrorDelta)/256;if( iDelta1 )iDelta = 1;iSample2 = iSample1;iSample1
41、 = iNewSample;+p;if( !flag )flag = 1, bytebuff = trimToNibble(iErrorDelta);elseflag = 0;bytebuff = (bytebuffpNibblescnt+ = bytebuff; if( !(cnt249) )addBlock(char*)pbuff,256);cnt = 0;if( !(pcalculateCoefficient(coeff,p,500);index = this-getClosestCoefficientIndex(coeff); pCoeff = &CoeffTableindex; pbuff-predictor = index; pbuff-iDelta = iDelta;pbuff-iSample2 = *p+;pbuff-iSample1 = *p+;iSample1 = pbuff-iSample1;iSample2 = pbuff-iSample2;if( cnt249 )while( cntpNibblescnt+ = 0;addBlock( (char*)pbuff,256 );ret
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- T/CSRME 058-2025地下水封液化石油气库土建工程设计规范
- T/ZBZLXH 006-2024泵类行业绿色工厂建设指南
- T/HEBQIA 166-2023混凝土用方圆颗粒机制砂
- T/CIE 286-2024民航客运销售富媒体数据及应用规范 第3部分:附加服务查询应用接口
- T/CAPID 009-2024零碳能源证书自愿核证体系 生物天然气核证规范
- T/CSAE 497-2025轻型混合动力电动汽车高寒续驶里程道路试验方法
- 2025~2026学年江西省抚州市某校人教版五年级上册期中练习数学试卷【附答案】
- 2025~2026学年广东省佛山市顺德区人教版三年级上册课堂学习材料三数学试卷【附答案】
- 2025-2026学年河北省新乐市初中政治七年级期末上册点睛提升培优拓展题附答案
- PHP 职场场景面试题(含详细真实答案)
- 2026年漠河市法院聘用制书记员考试试题及答案
- 【王朝霞单元活页卷】2026秋五年级上册数学期中综合测试卷
- 2026年中国混凝土搅拌站行业市场分析与发展策略咨询报告
- 临床护理实践指南(2025版)
- 钻井井场安全用电管理办法培训课件
- 2025年国家能源集团笔试试题及答案
- 2026年中国电信校园招聘考试笔试试题及答案
- 47911-2026《小微型企业安全生产标准化管理体系要求》解读
- 《医疗设备报废管理规范(2025版)》
- 2026年部队预防犯罪安全形势分析
- 2026版2型糖尿病全周期分级护理指南
评论
0/150
提交评论