AudioKit 5:如何在Xcode/Swift中归一化.caf格式音频文件
CAF音频文件归一化实现方案(替代AudioKit 5缺失功能)
直接基于AVFoundation实现CAF音频的音量归一化,无需依赖AudioKit,核心逻辑是读取音频样本、计算峰值、调整增益后重新导出。
完整实现代码
import AVFoundation /// 归一化CAF音频文件,将最大音量调整到目标dB值 /// - Parameters: /// - url: 源CAF文件URL /// - targetMaxLevelDB: 目标最大音量(dB,默认-4dB) /// - Returns: 归一化后的音频文件URL /// - Throws: 处理过程中的错误 func normalizeCAFAudio(at url: URL, targetMaxLevelDB: Float = -4) throws -> URL { // 读取源音频文件 let sourceFile = try AVAudioFile(forReading: url) let format = sourceFile.processingFormat let frameCount = UInt32(sourceFile.length) // 创建音频缓冲区存储样本数据 guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCount) else { throw NSError(domain: "NormalizationError", code: 1, userInfo: [NSLocalizedDescriptionKey: "无法创建音频缓冲区"]) } try sourceFile.read(into: buffer) // 计算当前音频的最大峰值(线性值) var maxPeak: Float = 0 guard let channelData = buffer.floatChannelData else { throw NSError(domain: "NormalizationError", code: 2, userInfo: [NSLocalizedDescriptionKey: "无法获取音频通道数据"]) } let channelCount = Int(format.channelCount) let sampleCount = Int(buffer.frameLength) for channel in 0..<channelCount { let samples = channelData[channel] for sampleIndex in 0..<sampleCount { let sample = abs(samples[sampleIndex]) if sample > maxPeak { maxPeak = sample } } } // 静音文件直接返回原URL guard maxPeak > 0 else { return url } // 将目标dB值转换为线性振幅 let targetMaxLinear = pow(10, targetMaxLevelDB / 20) // 计算增益:避免过度放大导致削波,最大增益限制为1 let gain = min(targetMaxLinear / maxPeak, 1.0) // 应用增益到所有音频样本 for channel in 0..<channelCount { let samples = channelData[channel] for sampleIndex in 0..<sampleCount { samples[sampleIndex] *= gain } } // 创建输出文件(原路径添加_normalized后缀) let outputFileName = url.deletingPathExtension().lastPathComponent + "_normalized.caf" let outputURL = url.deletingLastPathComponent().appendingPathComponent(outputFileName) // 写入归一化后的音频数据 let outputFile = try AVAudioFile(forWriting: outputURL, settings: sourceFile.fileFormat.settings, commonFormat: format.commonFormat, interleaved: format.isInterleaved) try outputFile.write(from: buffer) print("归一化完成,输出路径:\(outputURL.path)") return outputURL }
使用示例
let inputCAFURL = URL(fileURLWithPath: "/Users/xxx/Documents/recorded.caf") do { let normalizedURL = try normalizeCAFAudio(at: inputCAFURL) // 后续处理归一化后的文件 } catch { print("归一化失败:\(error.localizedDescription)") }
注意事项
- 上述代码一次性读取全部音频样本,适合中小体积的CAF文件;处理大文件时建议分块读取,降低内存占用
- 增益限制为≤1是为了防止原本音量高于目标值时被放大导致削波;若需要对低音量音频进行放大,可移除
min(..., 1.0)的限制,但需注意监听是否出现音频失真 - 确保项目已导入
AVFoundation框架,无需额外依赖第三方库
内容的提问来源于stack exchange,提问作者user2876642
相关产品推荐
相关产品推荐

