AVFoundation(录音、播放)

1,177 阅读2分钟
原文链接: www.jianshu.com

项目地址:github.com/zhuyunlongY…

typealias PlayerDidFinishPlayingBlock = () -> Void

class VoiceManager:NSObject{

// 单例
static let shared = VoiceManager()
private override init(){}

var duration:Int = 0 // 录音时间
var recorder_file_path:String? = nil // 录音路径

fileprivate var recorder: AVAudioRecorder? = nil
fileprivate var player: AVAudioPlayer? = nil

fileprivate var timer:Timer? = nil
fileprivate var pathTag:Int = 0

fileprivate var completeBlock:PlayerDidFinishPlayingBlock?

deinit {
    timer?.invalidate()
    timer = nil
}

// 是否使用扬声器
func isProximity(_ isProximity:Bool) {
    do {
        if isProximity {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
        }else {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
        }
    } catch let err {
        print("设置扬声器失败:\(err.localizedDescription)")
    }
}

//开始录音
func beginRecord() {
    duration = 0

    let session = AVAudioSession.sharedInstance()
    //设置session类型
    do {
        try session.setCategory(AVAudioSessionCategoryPlayAndRecord)
    } catch let err{
        print("设置类型失败:\(err.localizedDescription)")
    }
    //设置session动作
    do {
        try session.setActive(true)
    } catch let err {
        print("初始化动作失败:\(err.localizedDescription)")
    }

    //录音设置,注意,后面需要转换成NSNumber,如果不转换,你会发现,无法录制音频文件,我猜测是因为底层还是用OC写的原因
    let recordSetting: [String: Any] = [AVSampleRateKey: NSNumber(value: 16000),//采样率
        AVFormatIDKey: NSNumber(value: kAudioFormatLinearPCM),//音频格式
        AVLinearPCMBitDepthKey: NSNumber(value: 16),//采样位数
        AVNumberOfChannelsKey: NSNumber(value: 1),//通道数
        AVEncoderAudioQualityKey: NSNumber(value: AVAudioQuality.min.rawValue)//录音质量
    ];
    //开始录音
    do {
        recorder_file_path = getFilePath()
        if let file_path = recorder_file_path {
            let url = URL(fileURLWithPath: file_path)
            recorder = try AVAudioRecorder(url: url, settings: recordSetting)
            recorder?.isMeteringEnabled = true
            recorder!.prepareToRecord()
            recorder!.record()

            timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: (#selector(VoiceManager.recordingTime)), userInfo: nil, repeats: true)
            print("开始录音")
        }
    } catch let err {
        print("录音失败:\(err.localizedDescription)")
    }
}

//结束录音
func stopRecord() {
    timer?.invalidate()
    timer = nil
    if let recorder = recorder {
        if recorder.isRecording {
            if let file_path = recorder_file_path {
                print("正在录音,马上结束它,文件保存到了:\(file_path)")
            }
        }else {
            print("没有录音,但是依然结束它")
        }
        recorder.stop()
        self.recorder = nil
    }else {
        print("没有初始化")
    }
}

// 取消录音
func cancelRecord() {
    stopRecord()
    if let file_path = recorder_file_path {
        try? FileManager.default.removeItem(at: URL(fileURLWithPath: file_path))
        print("删除录音:\(file_path)")
    }

}

// 录音获取音量
func getRecordVolume() -> Float {
    var level:Float = 0.0
    if let recorder = recorder {
        if recorder.isRecording {
            recorder.updateMeters()
            //获取音量的平均值  [recorder averagePowerForChannel:0];
            //音量的最大值  [recorder peakPowerForChannel:0];
            let minDecibels:Float = -80.0
            let decibels:Float = recorder.peakPower(forChannel: 0)
            if decibels < minDecibels {
                level = 0.0
            }
            else if decibels >= 0.0 {
                level = 1.0
            }else {
                let root:Float = 2.0
                let minAmp:Float = powf(10.0, 0.05 * minDecibels)
                let inverseAmpRange:Float = 1.0 / (1.0 - minAmp)
                let amp = powf(10.0, 0.05 * decibels)
                let adjAmp = (amp - minAmp) * inverseAmpRange

                level = powf(adjAmp, 1.0 / root)
            }

        }
    }
    print("录音音量大小0~1--\(level)")
    return level
}

//播放
func play(_ path: String?,_ block: PlayerDidFinishPlayingBlock?) {

    UIDevice.current.isProximityMonitoringEnabled = true

    // 默认扬声器播放
    isProximity(true)

    do {
        completeBlock = block

        if let path = path {
            player = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
            player?.delegate = self
            player?.prepareToPlay()
            player!.play()
        }
    } catch let err {
        UIDevice.current.isProximityMonitoringEnabled = false
        print("播放失败:\(err.localizedDescription)")
    }
}

//结束播放
func stopPlay() {
    UIDevice.current.isProximityMonitoringEnabled = false
    if let player = player {
        if player.isPlaying {
            print("停止播放")
        }else {
            print("没有播放")
        }
        player.stop()
        completeBlock = nil
        self.player?.delegate = nil
        self.player = nil
    }else {
        print("没有初始化")
    }
}


// 录音记时
@objc fileprivate func recordingTime() {

    duration += 1
    print("录音时间:\(duration)")
}

// 获取路径
fileprivate func getFilePath() -> String! {

    pathTag += 1

    if pathTag > 1000 {
        pathTag = 0
    }

    return NSHomeDirectory() + "/Library/Caches/\(pathTag)-\(Date().timeIntervalSince1970).wav"
}

}

//// MARK: - AVAudioPlayerDelegateextension VoiceManager:AVAudioPlayerDelegate {

func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
    UIDevice.current.isProximityMonitoringEnabled = false
    if let completeBlock = completeBlock {
        completeBlock()
    }

}

}

// 添加监听
// 监听用户耳朵靠近手机或者远离手机
NotificationCenter.default.addObserver(self, selector: #selector(BaseChatVC.proximitySensorChanged), name: NSNotification.Name.UIDeviceProximityStateDidChange, object: nil)

// 监听用户耳朵靠近手机或者远离手机 @objc fileprivate func proximitySensorChanged() {

    if UIDevice.current.proximityState == true {
        VoiceManager.shared.isProximity(false)
    }else {
        VoiceManager.shared.isProximity(true)
    }

}