我找不到 m4a 或 mp3 的编码器,但我可以找到将缓冲区转换为 aac 文件的解决方案。
如果您要实现 mp3,我认为aac 是类似的,因为它们可以按顺序首尾相连,并且可以无缝播放。
所以我的思考过程是:
- 在文档目录中创建一个新的 aac 文件并保持打开状态
- 开始录制并将输入缓冲区附加到此 aac 文件中
- x 秒后,关闭当前打开的文件
- 关闭文件后,将最后一个文件上传到您的服务器
- 从第 1 步开始重复
我使用的资源将帮助您了解我所做的事情:
实施
您可能已经这样做了,但对于其他偶然发现的人来说,首先,在 info.plist 中添加正确的权限以访问麦克风:
<key>NSMicrophoneUsageDescription</key>
<string>Test app needs to access your microphone</string>
接下来,我会记录一些变量:
// AVAudioEngine used to record
var engine = AVAudioEngine()
// Set this as per your liking (512, 1024, 2048)
let estimatedBufferSize: AVAudioFrameCount = 1024
// Will be configured to write the buffer to a file
var file: AVAudioFile?
// Chunk duration in seconds, adjust as needed
let chunkDuration: Float64 = 10
// Will be used to uniquely name the different chunks
var currentChunkCount = 0
// Keeps track of how many frames in current chunk
// Used to check how much time has elapsed
var framesInCurrentChunk: AVAudioFrameCount = 0
这是 cmets 的其余逻辑
// Wire this to your start recording button
@objc
private func startRecording()
{
print("start recording")
// Prepare how the AVAudioEngine should process input
engine.inputNode.installTap(onBus: 0,
bufferSize: 1024,
format: engine.inputNode.inputFormat(forBus: 0))
{ [weak self] (buffer, time) -> Void in
// Write the buffer to your file
self?.writeBufferToFile(buffer: buffer)
}
// Start recording
try! engine.start()
}
// Wire this to your stop recording button
@objc
private func stopAndPlayRecording()
{
print("stop recording")
// Clean up and reset
engine.inputNode.removeTap(onBus: 0)
engine.stop()
file = nil
framesInCurrentChunk = 0
// Here is where you should upload any files that have not been uploaded
// Delete files after upload if you want or manage file name duplicates
}
private func writeBufferToFile(buffer: AVAudioPCMBuffer)
{
let samplesPerSecond = buffer.format.sampleRate
// Check if we have an open file writer
if file == nil
{
// Configure an AVAudioFile to write the audio buffer to file
prepareOutputFile()
}
do
{
try file?.write(from: buffer)
framesInCurrentChunk += buffer.frameLength
}
catch
{
// error appending the chunk to file
print(error)
}
// Check if the current chunk has reached it's duration
if framesInCurrentChunk > AVAudioFrameCount(chunkDuration * samplesPerSecond)
{
// Here is where you have a valid chunk that has been saved in
// the duration you want, put the last saved aac file in a queue to be
// uploaded to your server here
// De-initialize the current file writer so we can start a new one
file = nil
}
}
private func prepareOutputFile()
{
// Increment the current chunk count to create a new file
currentChunkCount += 1
// Set the path of where the file will be stored in the document directory
let documentsURL = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask)[0]
let outputURL = documentsURL.appendingPathComponent("recording_\(currentChunkCount).aac")
print("Recording audio to path: \(outputURL)")
do
{
// Configure the AVAudioFile with the output path and output format
file = try AVAudioFile(forWriting: outputURL,
settings: [AVFormatIDKey: kAudioFormatMPEG4AAC])
}
catch
{
// Handle errors in configuring the the AVAudioFile
print(error)
}
// Reset the frames saved in the current chunk
framesInCurrentChunk = 0
}
如果你在 iOS 设备上运行它,你应该会在运行 startRecording() 函数后看到它打印出当前记录输入流的文件的路径
运行stopAndPlayRecording()后,您可以查看您的文档目录,您将看到保存在您的文档目录中的AAC文件可以上传。 Here is how you can check that on a device
这是我的测试的输出:
最后的想法
- 您需要在最后处理错误,我保持简短
- 您可能希望使用某种逻辑删除文件以避免覆盖记录
试一试,如果这对您有用,请在 cmets 中告诉我,或者如果有帮助,我可以准备一个测试项目。