OC (已验证)
//
// WAVWriter.h
// NUIdemo
// Created by JoyTim on 2024/6/5
// Copyright © 2024 skydrui.regular. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface WAVWriter : NSObject
- (void)appendBuffer:(NSData *)buffer;
- (void)closeFile;
- (instancetype)initWithSampleRate:(int)sampleRate
bitDepth:(int)bitDepth
channels:(int)channels
outputFile:(NSString *)outputFile;
@end
NS_ASSUME_NONNULL_END
#import <Foundation/Foundation.h>
@interface WAVWriter : NSObject
- (void)appendBuffer:(NSData *)buffer;
- (void)closeFile;
@end
@implementation WAVWriter {
NSFileHandle *_fileHandle;
uint32_t _dataSize;
int _sampleRate;
int _bitDepth;
int _channels;
}
- (instancetype)initWithSampleRate:(int)sampleRate
bitDepth:(int)bitDepth
channels:(int)channels
outputFile:(NSString *)outputFile {
if (self) {
_sampleRate = sampleRate;
_bitDepth = bitDepth;
_channels = channels;
_dataSize = 0;
// 创建文件并写入文件头部信息
[[NSFileManager defaultManager] createFileAtPath:outputFile contents:nil attributes:nil];
_fileHandle = [NSFileHandle fileHandleForWritingAtPath:outputFile];
[self writeWAVHeaderWithDataSize:0];
}
return self;
}
- (void)appendBuffer:(NSData *)buffer {
// 更新文件头部中的数据长度信息
_dataSize += (uint32_t)buffer.length;
[self updateWAVHeaderWithDataSize:_dataSize];
// 追加 buffer 到文件末尾
[_fileHandle seekToEndOfFile];
[_fileHandle writeData:buffer];
}
- (void)closeFile {
[_fileHandle closeFile];
}
- (void)writeWAVHeaderWithDataSize:(uint32_t)dataSize {
NSMutableData *headerData = [NSMutableData data];
uint32_t chunkSize = 36 + dataSize;
uint16_t audioFormat = 1;
uint16_t blockAlign = _channels * _bitDepth / 8;
uint32_t byteRate = _sampleRate * _channels * _bitDepth / 8;
[headerData appendBytes:"RIFF" length:4];
[headerData appendBytes:&chunkSize length:4];
[headerData appendBytes:"WAVE" length:4];
[headerData appendBytes:"fmt " length:4];
uint32_t subchunk1Size = 16;
[headerData appendBytes:&subchunk1Size length:4];
[headerData appendBytes:&audioFormat length:2];
[headerData appendBytes:&_channels length:2];
[headerData appendBytes:&_sampleRate length:4];
[headerData appendBytes:&byteRate length:4];
[headerData appendBytes:&blockAlign length:2];
[headerData appendBytes:&_bitDepth length:2];
[headerData appendBytes:"data" length:4];
[headerData appendBytes:&dataSize length:4];
[_fileHandle seekToFileOffset:0];
[_fileHandle writeData:headerData];
}
- (void)updateWAVHeaderWithDataSize:(uint32_t)dataSize {
[_fileHandle seekToFileOffset:4];
uint32_t chunkSize = 36 + dataSize;
[_fileHandle writeData:[NSData dataWithBytes:&chunkSize length:4]];
[_fileHandle seekToFileOffset:40];
[_fileHandle writeData:[NSData dataWithBytes:&dataSize length:4]];
}
@end
swift 代码 (未测试)
import Foundation
class WAVWriter {
private let fileHandle: FileHandle
private var dataSize: UInt32 = 0
private let sampleRate: Int
private let bitDepth: Int
private let channels: Int
init(sampleRate: Int, bitDepth: Int, channels: Int, outputFile: String) {
self.sampleRate = sampleRate
self.bitDepth = bitDepth
self.channels = channels
// 创建文件并写入文件头部信息
FileManager.default.createFile(atPath: outputFile, contents: nil, attributes: nil)
fileHandle = FileHandle(forWritingAtPath: outputFile)!
writeWAVHeader(dataSize: 0)
}
func appendBuffer(_ buffer: Data) {
// 更新文件头部中的数据长度信息
dataSize += UInt32(buffer.count)
updateWAVHeader(dataSize: dataSize)
// 追加 buffer 到文件末尾
fileHandle.seekToEndOfFile()
fileHandle.write(buffer)
}
func closeFile() {
fileHandle.closeFile()
}
private func writeWAVHeader(dataSize: UInt32) {
var headerData = Data()
let chunkSize = 36 + dataSize
let audioFormat: UInt16 = 1
let blockAlign = UInt16(channels * bitDepth / 8)
let byteRate = UInt32(sampleRate * channels * bitDepth / 8)
headerData.append("RIFF".data(using: .ascii)!)
headerData.append(chunkSize.littleEndian.data)
headerData.append("WAVE".data(using: .ascii)!)
headerData.append("fmt ".data(using: .ascii)!)
let subchunk1Size: UInt32 = 16
headerData.append(subchunk1Size.littleEndian.data)
headerData.append(audioFormat.littleEndian.data)
headerData.append(UInt16(channels).littleEndian.data)
headerData.append(UInt32(sampleRate).littleEndian.data)
headerData.append(byteRate.littleEndian.data)
headerData.append(blockAlign.littleEndian.data)
headerData.append(UInt16(bitDepth).littleEndian.data)
headerData.append("data".data(using: .ascii)!)
headerData.append(dataSize.littleEndian.data)
fileHandle.seek(toFileOffset: 0)
fileHandle.write(headerData)
}
private func updateWAVHeader(dataSize: UInt32) {
fileHandle.seek(toFileOffset: 4)
let chunkSize = 36 + dataSize
fileHandle.write(chunkSize.littleEndian.data)
fileHandle.seek(toFileOffset: 40)
fileHandle.write(dataSize.littleEndian.data)
}
}
extension UInt16 {
var data: Data {
var value = self.littleEndian
return Data(bytes: &value, count: MemoryLayout.size(ofValue: value))
}
}
extension UInt32 {
var data: Data {
var value = self.littleEndian
return Data(bytes: &value, count: MemoryLayout.size(ofValue: value))
}
}