什么是wav和PCM?

WAV:wav是一种无损的音频文件格式,WAV符合 PifF(Resource Interchange File Format)规范。所有的WAV都有一个文件头,这个文件头音频流的编码参数。WAV对音频流的编码没有硬性规定,除了PCM之外,还有几乎所有支持ACM规范的编码都可以为WAV的音频流进行编码。

PCM:PCM(Pulse Code Modulation----脉码调制录音)。所谓PCM录音就是将声音等模拟信号变成符号化的脉冲列,再予以记录。PCM信号是由[1]、[0]等符号构成的数字信号,而未经过任何编码和压缩处理。

简单来说:wav是一种无损的音频文件格式,pcm是没有压缩的编码方式。

有时候需要将录音文件保存为wav格式,这需要手动填充wav的文件头信息

怎么改wav音频(音频pcm转wav)(1)

C 音视频开发学习资料:点击领取→音视频开发(资料文档 视频教程 面试题)(FFmpeg WebRTC RTMP RTSP HLS RTP)

怎么改wav音频(音频pcm转wav)(2)

ZZWavHeader

#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN /// WAV Header class @interface ZZWavHeader : NSObject /// 数据长度 @property (nonatomic, assign) UInt32 dataLength; /// 音频数据的编码方式, 1表示PCM编码 @property (nonatomic, assign) UInt16 formatTag; /// 通道数 @property (nonatomic, assign) UInt16 channels; /// 采样率 @property (nonatomic, assign) UInt32 samplesPerSec; /// 精度,即位深 @property (nonatomic, assign) UInt16 bitsPerSample; /// 获取WAV Header数据 - (NSData *)getData; @end NS_ASSUME_NONNULL_END

// http://www.skyfox.org/ios-audio-wav-write-header.html // https://stackoverflow.com/questions/10914888/recording-wav-format-file-with-avaudiorecorder #import "ZZWavHeader.h" @implementation ZZWavHeader - (NSData *)getData { UInt32 totalDataLen = self.dataLength (44 - 8); UInt16 blockAlign = self.channels * self.bitsPerSample / 8; UInt32 byteRate = blockAlign * self.samplesPerSec; Byte header[44]; // RIFF:4字节 header[0] = 'R'; // 0x52 header[1] = 'I'; // 0x49 header[2] = 'F'; // 0x46 header[3] = 'F'; // 0x46 // 文件长度: 4字节 // 这个长度不包括"RIFF"标志(4节节)和文件长度本身所占字节(4字节) header[4] = (Byte) (totalDataLen & 0xff); header[5] = (Byte) ((totalDataLen >> 8) & 0xff); header[6] = (Byte) ((totalDataLen >> 16) & 0xff); header[7] = (Byte) ((totalDataLen >> 24) & 0xff); // Wave:4字节 header[8] = 'W'; // 0x57 header[9] = 'A'; // 0x41 header[10] = 'V'; // 0x56 header[11] = 'E'; // 0x45 // fmt : 4字节,注:fmt后跟一英文空格字符 header[12] = 'f'; // 0x66 header[13] = 'm'; // 0x6d header[14] = 't'; // 0x74 header[15] = ' '; // 0x20 // 4 bytes: size of 'fmt ' chunk, Length of format data. Always 16 // 文件内部格式信息数据的大小: 4字节 header[16] = 16; header[17] = 0; header[18] = 0; header[19] = 0; // 音频数据的编码方式: 2字节 // 1:表示是PCM 编码 // format = 1, Wave type PCM header[20] = self.formatTag; header[21] = 0; // 声道数channels: 2字节 header[22] = (Byte)self.channels; header[23] = 0; // 采样率: 4字节 header[24] = (Byte) (self.samplesPerSec & 0xff); header[25] = (Byte) ((self.samplesPerSec >> 8) & 0xff); header[26] = (Byte) ((self.samplesPerSec >> 16) & 0xff); header[27] = (Byte) ((self.samplesPerSec >> 24) & 0xff); // 音频数据传送速率: 4字节 // 播放软件利用此值可以估计缓冲区的大小 // 其值为采样率×每次采样大小 // 传送速率单位是字节,因此采样大小需要位深除以 8,然后乘以通道数 // bytePerSecond = sampleRate(比如16000) * (bitsPerSample(16位深) / 8) * channels header[28] = (Byte) (byteRate & 0xff); header[29] = (Byte) ((byteRate >> 8) & 0xff); header[30] = (Byte) ((byteRate >> 16) & 0xff); header[31] = (Byte) ((byteRate >> 24) & 0xff); // 每次采样大小: 2字节 // blockAlign = 通道数(比如1) * bitPerSample(比如位深16) / 8 header[32] = (Byte) (self.channels * self.bitsPerSample / 8); header[33] = 0; // 采样精度,即位深 header[34] = self.bitsPerSample & 0xff; header[35] = 0; // data: 4字节 header[36] = 'd'; header[37] = 'a'; header[38] = 't'; header[39] = 'a'; // 数据长度: 4字节 // 此数据长度即未添加header之前的原始数据长度 // iOS: [attributes fileSize] header[40] = (Byte)(self.dataLength & 0xff); header[41] = (Byte)((self.dataLength >> 8) & 0xff); header[42] = (Byte)((self.dataLength >> 16) & 0xff); header[43] = (Byte)((self.dataLength >> 24) & 0xff); return [[NSData alloc] initWithBytes:header length:44]; } @end

调用代码

- (void)encode:(NSError * __autoreleasing _Nullable *)error { NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:self.srcFilePath]) { NSString *message = [NSString stringWithFormat:@"encode wav data fail, file not exists: %@", self.srcFilePath]; NSLog(@"%@", message); return; } NSError *err = nil; NSDictionary *attributes = [fileManager attributesOfItemAtPath:self.srcFilePath error:&err]; if (!attributes || err) { NSString *message = [NSString stringWithFormat:@"get file attribute error"]; NSLog(@"%@", message); return; } int totalSize = (int)[attributes fileSize]; ZZWavHeader *header = [[ZZWavHeader alloc] init]; header.channels = _CHANNEL_COUNT; header.formatTag = 0x0001; header.samplesPerSec = _SAMPLE_RATE; header.bitsPerSample = _BIT_DEPTH; header.dataLength = totalSize; NSData *headerData = [header getData]; if (headerData.length != 44) { NSString *message = [NSString stringWithFormat:@"convert to wav fail, header not 44 byte length"]; NSLog(@"%@", message); return; } NSMutableData *data = [NSMutableData dataWithData:headerData]; [data appendData:[NSData dataWithContentsOfFile:self.srcFilePath]]; NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:self.srcFilePath]; [fileHandle writeData:data]; [fileHandle closeFile]; NSLog(@"%@", [NSString stringWithFormat:@"转换成功:%@", self.srcFilePath]); }

,