You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

实现用户说话自动启停录音并限制最长录音时长

解决方案:基于音频计量实现自动启停录音+最长时长限制

你说得对,AVAudioRecorderDelegate确实没有直接提供检测语音启停的回调方法,但我们可以利用它的音频计量(Metering)功能来实现这个需求——通过定时采样音频的音量电平,判断用户是否在说话,再结合计时器完成自动启停和最长时长限制。下面是具体的实现步骤和修改后的代码:

第一步:添加必要的属性

在你的ViewController中先定义这些变量,用来控制录音的检测逻辑:

import UIKit
import AVFoundation

class RecordViewController: UIViewController, AVAudioRecorderDelegate {
    @IBOutlet weak var recordingLabel: UILabel!
    @IBOutlet weak var recordButton: UIButton!
    @IBOutlet weak var stopRecordingButton: UIButton!
    
    var audioRecorder: AVAudioRecorder!
    // 音频检测相关
    var soundDetectionTimer: Timer?
    let silenceThreshold: Float = -40.0 // 静音阈值(可调整,值越小越敏感)
    let silenceDurationLimit: TimeInterval = 1.5 // 静音持续多久后停止录音
    var silenceStartTime: Date?
    // 最长录音时长
    let maxRecordingDuration: TimeInterval = 60.0 // 最长60秒,可调整
    var recordingStartTime: Date?
}

第二步:修改录音启动逻辑

recordAudio方法中,启动录音的同时,初始化计时器并开始采样音频电平,同时记录录音开始时间:

@IBAction func recordAudio(_ sender: Any) {
    recordingLabel.text = "Recording in progress..."
    recordButton.isEnabled = false
    stopRecordingButton.isEnabled = true
    
    // 配置录音路径
    let dirPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
    let recordingName = "recordedVoice.wav"
    let pathArray = [dirPath, recordingName]
    guard let filePath = URL(string: pathArray.joined(separator: "/")) else {
        print("Failed to create file path")
        return
    }
    
    // 配置音频会话
    let session = AVAudioSession.sharedInstance()
    do {
        try session.setCategory(.playAndRecord, mode: .default, options: .defaultToSpeaker)
        try session.setActive(true)
        
        // 初始化录音器(建议添加音频设置,避免默认设置可能的问题)
        let settings: [String: Any] = [
            AVFormatIDKey: Int(kAudioFormatLinearPCM),
            AVSampleRateKey: 44100.0,
            AVNumberOfChannelsKey: 1,
            AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
        ]
        audioRecorder = try AVAudioRecorder(url: filePath, settings: settings)
        audioRecorder.delegate = self
        audioRecorder.isMeteringEnabled = true
        audioRecorder.prepareToRecord()
        audioRecorder.record()
        
        // 记录录音开始时间,启动检测计时器
        recordingStartTime = Date()
        soundDetectionTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(checkAudioLevel), userInfo: nil, repeats: true)
        
    } catch let error {
        print("Audio session or recorder setup failed: \(error.localizedDescription)")
        resetRecordingUI()
    }
}

第三步:实现音频电平检测逻辑

这个方法会定时被调用,采样当前音频的音量,判断是否处于静音状态,进而触发自动停止:

@objc func checkAudioLevel() {
    guard let recorder = audioRecorder, recorder.isRecording else {
        soundDetectionTimer?.invalidate()
        return
    }
    
    // 更新计量数据
    recorder.updateMeters()
    let averagePower = recorder.averagePower(forChannel: 0)
    
    // 检查是否超过最长录音时长
    if let startTime = recordingStartTime, Date().timeIntervalSince(startTime) >= maxRecordingDuration {
        print("Reached max recording duration")
        stopRecording(nil)
        return
    }
    
    // 判断是否静音
    if averagePower < silenceThreshold {
        // 开始计时静音时长
        if silenceStartTime == nil {
            silenceStartTime = Date()
        } else {
            let silenceDuration = Date().timeIntervalSince(silenceStartTime!)
            if silenceDuration >= silenceDurationLimit {
                print("Silence detected for too long, stopping recording")
                stopRecording(nil)
            }
        }
    } else {
        // 检测到声音,重置静音计时器
        silenceStartTime = nil
    }
}

第四步:完善停止录音和UI重置逻辑

修改stopRecording方法,确保停止计时器和清理资源:

@IBAction func stopRecording(_ sender: Any?) {
    soundDetectionTimer?.invalidate()
    soundDetectionTimer = nil
    silenceStartTime = nil
    recordingStartTime = nil
    
    recordButton.isEnabled = true
    stopRecordingButton.isEnabled = false
    recordingLabel.text = "Tap to record..."
    
    if audioRecorder.isRecording {
        audioRecorder.stop()
        let audioSession = AVAudioSession.sharedInstance()
        do {
            try audioSession.setActive(false)
        } catch {
            print("Failed to deactivate audio session: \(error.localizedDescription)")
        }
    }
}

// 重置UI的辅助方法
func resetRecordingUI() {
    recordButton.isEnabled = true
    stopRecordingButton.isEnabled = false
    recordingLabel.text = "Tap to record..."
}

第五步:处理录音完成的回调

保持你原有的audioRecorderDidFinishRecording方法,可根据需要扩展:

func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
    if flag {
        print("Recording saved successfully at \(recorder.url)")
        // 这里可以添加录音完成后的逻辑,比如上传、播放等
    } else {
        print("Could not save audio recording!")
        resetRecordingUI()
    }
}

额外注意事项

  • 权限请求:记得在Info.plist中添加NSMicrophoneUsageDescription,说明使用麦克风的原因,否则App会崩溃。
  • 阈值调整silenceThresholdsilenceDurationLimit可以根据实际场景调整,比如环境噪音大的话,阈值可以调大(比如-30.0),避免误判。
  • 错误处理:我把你原有的try!换成了do-catch,这样可以捕获错误,避免App崩溃。

内容的提问来源于stack exchange,提问作者John

火山引擎 最新活动