[英]How to real-time convert the PCM buffer to AAC data when recording for iOS?
[英]How to add playable(such as wav,wmv) header with PCM data/buffer in iOS?
我正在嘗試在原始 PCM 數據之上添加一個 wav header 以使其可通過 AVAudioPlayer 播放。 但是我找不到任何解決方案或源代碼來使用 Objective-C/Swift 在 iOS 上執行此操作。 雖然我找到了這個,但沒有正確答案。
但我在這里找到了一段代碼,它位於 C 中,也包含一些問題。 從該代碼生成的 wav 文件無法正常播放。
我已經在下面給出了我到目前為止編碼的代碼。
int NumChannels = AUDIO_CHANNELS_PER_FRAME;
short BitsPerSample = AUDIO_BITS_PER_CHANNEL;
int SamplingRate = AUDIO_SAMPLE_RATE;
int numOfSamples = [[NSData dataWithContentsOfFile:filePath] length];
int ByteRate = NumChannels*BitsPerSample*SamplingRate/8;
short BlockAlign = NumChannels*BitsPerSample/8;
int DataSize = NumChannels*numOfSamples*BitsPerSample/8;
int chunkSize = 16;
int totalSize = 36 + DataSize;
short audioFormat = 1;
if((fout = fopen([wavFilePath cStringUsingEncoding:1], "w")) == NULL)
{
printf("Error opening out file ");
}
fwrite("RIFF", sizeof(char), 4,fout);
fwrite(&totalSize, sizeof(int), 1, fout);
fwrite("WAVE", sizeof(char), 4, fout);
fwrite("fmt ", sizeof(char), 3, fout);
fwrite(&chunkSize, sizeof(int),1,fout);
fwrite(&audioFormat, sizeof(short), 1, fout);
fwrite(&NumChannels, sizeof(short),1,fout);
fwrite(&SamplingRate, sizeof(int), 1, fout);
fwrite(&ByteRate, sizeof(int), 1, fout);
fwrite(&BlockAlign, sizeof(short), 1, fout);
fwrite(&BitsPerSample, sizeof(short), 1, fout);
fwrite("data", sizeof(char), 3, fout);
fwrite(&DataSize, sizeof(int), 1, fout);
文件播放速度太快,聲音失真,只播放前 10 到 20(大約)秒。 我認為,wav header 沒有正確生成(因為我能夠使用 AudioUnit/AudioQueue 播放相同的 PCM 數據/緩沖區)。 那么我的代碼中缺少什么? 任何幫助將不勝感激。
提前致謝。
好的,如果它對其他人有幫助,我正在回答我自己的問題。 經過幾天的不懈努力,我終於讓它工作了。 下面是一個用 Objective-C 和 C 編寫的完整函數。它以文件路徑作為參數,其中包含直接從麥克風捕獲的 RAW PCM 數據,並返回一個文件路徑,其中包含 PCM 數據,后跟適當的 wav 頭信息。 然后您可以使用 AVAudioPlayer 或 AVPlayer 播放該文件。 這是代碼...
- (NSURL *) getAndCreatePlayableFileFromPcmData:(NSString *)filePath
{
NSString *wavFileName = [[filePath lastPathComponent] stringByDeletingPathExtension];
NSString *wavFileFullName = [NSString stringWithFormat:@"%@.wav",wavFileName];
[self createFileWithName:wavFileFullName];
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
NSString *wavFilePath = [docsDir stringByAppendingPathComponent:wavFileFullName];
NSLog(@"PCM file path : %@",filePath);
FILE *fout;
short NumChannels = AUDIO_CHANNELS_PER_FRAME;
short BitsPerSample = AUDIO_BITS_PER_CHANNEL;
int SamplingRate = AUDIO_SAMPLE_RATE;
int numOfSamples = [[NSData dataWithContentsOfFile:filePath] length];
int ByteRate = NumChannels*BitsPerSample*SamplingRate/8;
short BlockAlign = NumChannels*BitsPerSample/8;
int DataSize = NumChannels*numOfSamples*BitsPerSample/8;
int chunkSize = 16;
int totalSize = 46 + DataSize;
short audioFormat = 1;
if((fout = fopen([wavFilePath cStringUsingEncoding:1], "w")) == NULL)
{
printf("Error opening out file ");
}
fwrite("RIFF", sizeof(char), 4,fout);
fwrite(&totalSize, sizeof(int), 1, fout);
fwrite("WAVE", sizeof(char), 4, fout);
fwrite("fmt ", sizeof(char), 4, fout);
fwrite(&chunkSize, sizeof(int),1,fout);
fwrite(&audioFormat, sizeof(short), 1, fout);
fwrite(&NumChannels, sizeof(short),1,fout);
fwrite(&SamplingRate, sizeof(int), 1, fout);
fwrite(&ByteRate, sizeof(int), 1, fout);
fwrite(&BlockAlign, sizeof(short), 1, fout);
fwrite(&BitsPerSample, sizeof(short), 1, fout);
fwrite("data", sizeof(char), 4, fout);
fwrite(&DataSize, sizeof(int), 1, fout);
fclose(fout);
NSMutableData *pamdata = [NSMutableData dataWithContentsOfFile:filePath];
NSFileHandle *handle;
handle = [NSFileHandle fileHandleForUpdatingAtPath:wavFilePath];
[handle seekToEndOfFile];
[handle writeData:pamdata];
[handle closeFile];
return [NSURL URLWithString:wavFilePath];
}
但該功能僅適用於以下音頻設置。
// Audio settings.
#define AUDIO_SAMPLE_RATE 8000
#define AUDIO_FRAMES_PER_PACKET 1
#define AUDIO_CHANNELS_PER_FRAME 1
#define AUDIO_BITS_PER_CHANNEL 16
#define AUDIO_BYTES_PER_PACKET 2
#define AUDIO_BYTES_PER_FRAME 2
非常有幫助的問題和答案,非常感謝。
這個 swift 版本適用於有需要的人:
static func createWAV(from pcmFilePath: String, to wavFilePath: String) -> Bool {
// Make sure that the path does not contain non-ascii characters
guard let fout = fopen(wavFilePath.cString(using: .ascii), "w") else { return false }
guard let pcmData = try? Data(contentsOf: URL(fileURLWithPath: pcmFilePath)) else { return false }
var numChannels: CShort = 1
let numChannelsInt: CInt = 1
var bitsPerSample: CShort = 16
let bitsPerSampleInt: CInt = 16
var samplingRate: CInt = 16000
let numOfSamples = CInt(pcmData.count)
var byteRate = numChannelsInt * bitsPerSampleInt * samplingRate / 8
var blockAlign = numChannelsInt * bitsPerSampleInt / 8
var dataSize = numChannelsInt * numOfSamples * bitsPerSampleInt / 8
var chunkSize: CInt = 16
var totalSize = 46 + dataSize
var audioFormat: CShort = 1
fwrite("RIFF".cString(using: .ascii), MemoryLayout<CChar>.size, 4, fout)
fwrite(&totalSize, MemoryLayout<CInt>.size, 1, fout)
fwrite("WAVE".cString(using: .ascii), MemoryLayout<CChar>.size, 4, fout);
fwrite("fmt ".cString(using: .ascii), MemoryLayout<CChar>.size, 4, fout);
fwrite(&chunkSize, MemoryLayout<CInt>.size,1,fout);
fwrite(&audioFormat, MemoryLayout<CShort>.size, 1, fout);
fwrite(&numChannels, MemoryLayout<CShort>.size,1,fout);
fwrite(&samplingRate, MemoryLayout<CInt>.size, 1, fout);
fwrite(&byteRate, MemoryLayout<CInt>.size, 1, fout);
fwrite(&blockAlign, MemoryLayout<CShort>.size, 1, fout);
fwrite(&bitsPerSample, MemoryLayout<CShort>.size, 1, fout);
fwrite("data".cString(using: .ascii), MemoryLayout<CChar>.size, 4, fout);
fwrite(&dataSize, MemoryLayout<CInt>.size, 1, fout);
fclose(fout);
guard let handle = FileHandle(forUpdatingAtPath: wavFilePath) else { return false }
handle.seekToEndOfFile()
handle.write(pcmData)
handle.closeFile()
return true
}
修改自 qiz 對 swift 5 的回答
func extractSubchunks(data:Data) -> RiffFile? {
var data = data
var chunks = [SubChunk]()
let position = data.subdata(in: 8..<12)
let filelengthBytes = data.subdata(in: 4..<8).map { UInt32($0) }
let filelength: UInt32 = filelengthBytes[0] << 24 + filelengthBytes[1] << 16 + filelengthBytes[2] << 8 + filelengthBytes[3]
let wave = String(bytes: position, encoding: .utf8) ?? "NoName"
guard wave == "WAVE" else {
print("File is \(wave) not WAVE")
return nil
}
data.removeSubrange(0..<12)
print("Found chunks")
while data.count != 0{
let position = data.subdata(in: 0..<4)
let lengthBytes = data.subdata(in: 4..<8).map { UInt32($0) }
let length: UInt32 = lengthBytes[0] << 24 + lengthBytes[1] << 16 + lengthBytes[2] << 8 + lengthBytes[3]
guard let current = String(bytes: position, encoding: .utf8) else{
return nil
}
data.removeSubrange(0..<8)
let chunkData = data.subdata(in: 0..<Int(length))
data.removeSubrange(0..<Int(length))
let subchunk = SubChunk(name: current, size: Int(length), data: chunkData)
chunks.append(subchunk)
print(subchunk.debugDescription)
}
let riff = RiffFile(size: Int(filelength), subChunks: chunks)
return riff
}
這是 Swift 的數據擴展,返回另一個數據,使用qiz 的答案制作。
extension Data {
func wavValue: Data? {
var numChannels: CShort = 1
let numChannelsInt: CInt = 1
var bitsPerSample: CShort = 16
let bitsPerSampleInt: CInt = 16
var samplingRate: CInt = 44100
let numOfSamples = CInt(pcmData.count)
var byteRate = numChannelsInt * bitsPerSampleInt * samplingRate / 8
var blockAlign = numChannelsInt * bitsPerSampleInt / 8
var dataSize = numChannelsInt * numOfSamples * bitsPerSampleInt / 8
var chunkSize: CInt = 16
var totalSize = 46 + dataSize
var audioFormat: CShort = 1
let wavNSData = NSMutableData()
wavNSData.append("RIFF".cString(using: .ascii) ?? .init(), length: MemoryLayout<CChar>.size * 4)
wavNSData.append(&totalSize, length: MemoryLayout<CInt>.size)
wavNSData.append("WAVE".cString(using: .ascii) ?? .init(), length: MemoryLayout<CChar>.size * 4)
wavNSData.append("fmt ".cString(using: .ascii) ?? .init(), length: MemoryLayout<CChar>.size * 4)
wavNSData.append(&chunkSize, length: MemoryLayout<CInt>.size)
wavNSData.append(&audioFormat, length: MemoryLayout<CShort>.size)
wavNSData.append(&numChannels, length: MemoryLayout<CShort>.size)
wavNSData.append(&samplingRate, length: MemoryLayout<CInt>.size)
wavNSData.append(&byteRate, length: MemoryLayout<CInt>.size)
wavNSData.append(&blockAlign, length: MemoryLayout<CShort>.size)
wavNSData.append(&bitsPerSample, length: MemoryLayout<CShort>.size)
wavNSData.append("data".cString(using: .ascii) ?? .init(), length: MemoryLayout<CChar>.size * 4)
wavNSData.append(&dataSize, length: MemoryLayout<CInt>.size)
wavNSData.append(self)
let wavData = Data(referencing: wavNSData)
return wavData
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.