音频pcm转wav
2022-06-13 本文已影响0人
大刘
Created by 大刘 liuxing8807@126.com
什么是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的文件头信息
audio_1.png audio_2.pngZZWavHeader
#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]);
}