音频pcm转wav

作者: 大刘 | 来源:发表于2022-06-13 12:30 被阅读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.png

    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]);
    }
    

    相关文章

      网友评论

        本文标题:音频pcm转wav

        本文链接:https://www.haomeiwen.com/subject/dadomrtx.html