IOS录制音频并实时分割成文件
IOS record audio and split it into files in real time
我正在制作一个需要将音频流式传输到服务器的应用程序。我想做的是把录制的音频分成块,边录制边上传。
我用了两台录音机来做,但效果不佳;我可以听到块之间的区别(停止几毫秒)。
我该怎么做?
您的问题可以分为两部分:录制和分块(以及上传,但谁在乎)。
从麦克风录音和写入文件,您可以使用AVAudioEngine
和AVAudioFile
快速上手。请参阅下面的示例,该示例以设备的默认输入采样率记录块(您可能希望对其进行速率转换)。
当您谈论 "difference between the chunks" 时,您指的是将音频数据分成几部分的能力,这样当您连接它们时就不会听到不连续的声音。例如LPCM 音频数据可以在样本级别分成块,但 LPCM 比特率很高,因此您更有可能使用打包格式,如 adpcm(在 iOS? 上称为 ima4?),或 mp3 或 aac。这些格式只能在数据包边界上划分,例如例如,64、576 或 1024 个样本。如果你的块是在没有 header 的情况下编写的(通常用于 mp3 和 aac,不确定 ima4),那么连接是微不足道的:只需将块首尾相连,就像 cat
命令行工具一样.遗憾的是,iOS 上没有 mp3 编码器,因此 aac 可能是您的一种格式,但这取决于您的播放要求。 iOS 设备和 mac 绝对可以播放。
import AVFoundation
class ViewController: UIViewController {
let engine = AVAudioEngine()
struct K {
static let secondsPerChunk: Float64 = 10
}
var chunkFile: AVAudioFile! = nil
var outputFramesPerSecond: Float64 = 0 // aka input sample rate
var chunkFrames: AVAudioFrameCount = 0
var chunkFileNumber: Int = 0
func writeBuffer(_ buffer: AVAudioPCMBuffer) {
let samplesPerSecond = buffer.format.sampleRate
if chunkFile == nil {
createNewChunkFile(numChannels: buffer.format.channelCount, samplesPerSecond: samplesPerSecond)
}
try! chunkFile.write(from: buffer)
chunkFrames += buffer.frameLength
if chunkFrames > AVAudioFrameCount(K.secondsPerChunk * samplesPerSecond) {
chunkFile = nil // close file
}
}
func createNewChunkFile(numChannels: AVAudioChannelCount, samplesPerSecond: Float64) {
let fileUrl = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("chunk-\(chunkFileNumber).aac")!
print("writing chunk to \(fileUrl)")
let settings: [String: Any] = [
AVFormatIDKey: kAudioFormatMPEG4AAC,
AVEncoderBitRateKey: 64000,
AVNumberOfChannelsKey: numChannels,
AVSampleRateKey: samplesPerSecond
]
chunkFile = try! AVAudioFile(forWriting: fileUrl, settings: settings)
chunkFileNumber += 1
chunkFrames = 0
}
override func viewDidLoad() {
super.viewDidLoad()
let input = engine.inputNode!
let bus = 0
let inputFormat = input.inputFormat(forBus: bus)
input.installTap(onBus: bus, bufferSize: 512, format: inputFormat) { (buffer, time) -> Void in
DispatchQueue.main.async {
self.writeBuffer(buffer)
}
}
try! engine.start()
}
}
我正在制作一个需要将音频流式传输到服务器的应用程序。我想做的是把录制的音频分成块,边录制边上传。
我用了两台录音机来做,但效果不佳;我可以听到块之间的区别(停止几毫秒)。
我该怎么做?
您的问题可以分为两部分:录制和分块(以及上传,但谁在乎)。
从麦克风录音和写入文件,您可以使用AVAudioEngine
和AVAudioFile
快速上手。请参阅下面的示例,该示例以设备的默认输入采样率记录块(您可能希望对其进行速率转换)。
当您谈论 "difference between the chunks" 时,您指的是将音频数据分成几部分的能力,这样当您连接它们时就不会听到不连续的声音。例如LPCM 音频数据可以在样本级别分成块,但 LPCM 比特率很高,因此您更有可能使用打包格式,如 adpcm(在 iOS? 上称为 ima4?),或 mp3 或 aac。这些格式只能在数据包边界上划分,例如例如,64、576 或 1024 个样本。如果你的块是在没有 header 的情况下编写的(通常用于 mp3 和 aac,不确定 ima4),那么连接是微不足道的:只需将块首尾相连,就像 cat
命令行工具一样.遗憾的是,iOS 上没有 mp3 编码器,因此 aac 可能是您的一种格式,但这取决于您的播放要求。 iOS 设备和 mac 绝对可以播放。
import AVFoundation
class ViewController: UIViewController {
let engine = AVAudioEngine()
struct K {
static let secondsPerChunk: Float64 = 10
}
var chunkFile: AVAudioFile! = nil
var outputFramesPerSecond: Float64 = 0 // aka input sample rate
var chunkFrames: AVAudioFrameCount = 0
var chunkFileNumber: Int = 0
func writeBuffer(_ buffer: AVAudioPCMBuffer) {
let samplesPerSecond = buffer.format.sampleRate
if chunkFile == nil {
createNewChunkFile(numChannels: buffer.format.channelCount, samplesPerSecond: samplesPerSecond)
}
try! chunkFile.write(from: buffer)
chunkFrames += buffer.frameLength
if chunkFrames > AVAudioFrameCount(K.secondsPerChunk * samplesPerSecond) {
chunkFile = nil // close file
}
}
func createNewChunkFile(numChannels: AVAudioChannelCount, samplesPerSecond: Float64) {
let fileUrl = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("chunk-\(chunkFileNumber).aac")!
print("writing chunk to \(fileUrl)")
let settings: [String: Any] = [
AVFormatIDKey: kAudioFormatMPEG4AAC,
AVEncoderBitRateKey: 64000,
AVNumberOfChannelsKey: numChannels,
AVSampleRateKey: samplesPerSecond
]
chunkFile = try! AVAudioFile(forWriting: fileUrl, settings: settings)
chunkFileNumber += 1
chunkFrames = 0
}
override func viewDidLoad() {
super.viewDidLoad()
let input = engine.inputNode!
let bus = 0
let inputFormat = input.inputFormat(forBus: bus)
input.installTap(onBus: bus, bufferSize: 512, format: inputFormat) { (buffer, time) -> Void in
DispatchQueue.main.async {
self.writeBuffer(buffer)
}
}
try! engine.start()
}
}