I'm using AVAudioEngine to record audio in my iOS app. I want to stop the recording automatically if there is no audio input for a specified duration (e.g., 5 seconds). How can I achieve this?
Here is the code I have so far for starting the recording:
func startRecording() throws {
guard let engine = self.audioEngine, let mic = mic else { return }
let micFormat = mic.inputFormat(forBus: 0)
self.state = .recording
let startTime = DispatchTime.now()
mic.installTap(onBus: 0, bufferSize: 4096, format: micFormat) { (buffer, when) in
// Data(buffer: buffer, time: when)
let sampleData = UnsafeBufferPointer(start: buffer.floatChannelData![0], count: Int(buffer.frameLength))
// print(buffer)
let currentTime = DispatchTime.now()
let elapsedTime = currentTime.uptimeNanoseconds - startTime.uptimeNanoseconds
let timeout = 5
let isFinal = elapsedTime > timeout * 1_000_000_000
self.baseData = buffer.data().base64EncodedString()
if isFinal {
self.stopRecording()
}
}
if engine.isRunning { return }
try engine.start()
}
i believe there should be something that i need to do in that install tap closure but not sure how.
1 Answer 1
To stop the recording automatically after a specified period of silence (e.g., 5 seconds) using AVAudioEngine, you need to monitor the audio buffer’s amplitude level and detect if the audio input level stays below a certain threshold (silence threshold) for the specified duration.
func startRecording() throws {
guard let engine = self.audioEngine, let mic = mic else { return }
let micFormat = mic.inputFormat(forBus: 0)
let silenceThreshold: Float = 0.01 // Adjust based on what you consider silence
let silenceTimeout: TimeInterval = 5 // 5 seconds of silence to stop recording
var silenceStartTime: TimeInterval? // Tracks when silence started
self.state = .recording
mic.installTap(onBus: 0, bufferSize: 4096, format: micFormat) { [weak self] (buffer, _) in
guard let self = self else { return }
// Analyze the audio buffer
let channelData = buffer.floatChannelData?[0]
let frameLength = Int(buffer.frameLength)
// Compute the RMS (Root Mean Square) to detect audio level
let rms = channelData?.prefix(frameLength).reduce(0, { 0ドル + 1ドル * 1ドル }).squareRoot() ?? 0
// Check if the audio level is below the silence threshold
if rms < silenceThreshold {
// Start counting silence duration
if silenceStartTime == nil {
silenceStartTime = Date().timeIntervalSince1970
} else if let startTime = silenceStartTime,
Date().timeIntervalSince1970 - startTime > silenceTimeout {
// Stop recording if silence duration exceeds timeout
DispatchQueue.main.async {
self.stopRecording()
}
}
} else {
// Reset silence start time if audio is detected
silenceStartTime = nil
}
}
if !engine.isRunning {
try engine.start()
}
}
func stopRecording() {
guard let engine = self.audioEngine else { return }
if engine.isRunning {
engine.stop()
mic?.removeTap(onBus: 0)
}
self.state = .stopped
print("Recording stopped due to silence.")
}