| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- //
- import AVFoundation
- import Foundation
- // NXAudioRecorder.swift
- // PQSpeed
- //
- // Created by ak on 2021/1/23.
- // Copyright © 2021 BytesFlow. All rights reserved.
- // 本类功能:录制声音,并转换成 MP3
- // alse see https://www.jianshu.com/p/971fff236881
- import UIKit
- // 录制时长
- typealias RecorderProgross = (_ time: Float64) -> Void
- class NXAudioRecorder {
- public let recorder: AVAudioRecorder
- public var finishClosure: ((_ isSuccess: Bool, _ url: String) -> Void)? {
- return delegateHandler.finishClosure
- }
- /// 由于AVAudioRecorderDelegate继承NSObjectProtocol 所以引入这个类处理代理避免污染主类
- private var delegateHandler = EditAudioRecorderDelegateHandler()
- var recorderProgross: RecorderProgross?
- var session: AVAudioSession!
- var recordFilePath: String!
- var displayLink: CADisplayLink?
- /// 初始化录音器
- /// - Parameter path: 保存的文件全路径,注意文件后缀一定要是 caf
- /// - Throws: description
- public init(path: String) throws {
- // 1,判断目录文件夹是否存在
- recordFilePath = path
- BFLog(message: "recorder file path is \(String(describing: recordFilePath))")
- // 2,参数
- let fileURL = URL(fileURLWithPath: recordFilePath)
- // 注意设置参数 设置不对就无法录制
- let settings: [String: Any] = [
- AVFormatIDKey: kAudioFormatLinearPCM,
- AVSampleRateKey: 16000.0,
- AVNumberOfChannelsKey: 1,
- AVEncoderBitDepthHintKey: 16,
- // AVLinearPCMIsFloatKey:true, // 不要打开ios 13有杂音
- AVEncoderAudioQualityKey: AVAudioQuality.max.rawValue, // 录音质量
- ]
- recorder = try AVAudioRecorder(url: fileURL, settings: settings)
- recorder.isMeteringEnabled = true
- recorder.delegate = delegateHandler
- recorder.prepareToRecord()
- }
- /// 开始录制
- public func startRecord() {
- if recorder.isRecording {
- BFLog(message: "正在录制中。。")
- return
- }
- startTimer()
- if AVAudioSession.sharedInstance().category != .playAndRecord {
- do {
- try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: .defaultToSpeaker)
- try AVAudioSession.sharedInstance().setActive(true)
- } catch {
- BFLog(message: error)
- }
- }
- session = AVAudioSession.sharedInstance()
- session.requestRecordPermission { granted in
- if granted {
- DispatchQueue.global().async {
- DispatchQueue.main.async {}
- }
- } else {}
- }
- recorder.record()
- }
- // 暂停录制
- public func pauseRecord() {
- recorder.pause()
- }
- // 停止录制
- public func stopRecord(_ closure: @escaping (_ isSuccess: Bool, _ url: String) -> Void) {
- if !recorder.isRecording {
- BFLog(message: "不是录制状态")
- }
- stopTimer()
- delegateHandler.finishClosure = closure
- recorder.stop()
- }
- @objc func displayLinkClick(_: CADisplayLink) {
- recorder.updateMeters()
- BFLog(message: "当前录制时间长 \(String(describing: recorder.currentTime)) 波值:\(String(describing: recorder.averagePower(forChannel: 0)))")
- if recorderProgross != nil {
- recorderProgross!(recorder.currentTime)
- }
- }
- // 开始计时
- func startTimer() {
- if displayLink == nil {
- // 创建对象
- displayLink = CADisplayLink(target: self, selector: #selector(displayLinkClick(_:)))
- // 设置触发频率 这个周期可以通过frameInterval属性设置,CADisplayLink的selector每秒调用次数=60/frameInterval。比如当frameInterval设为2,每秒调用就变成30次
- // if #available(iOS 10.0, *) {
- // displayLink?.preferredFramesPerSecond = 1
- // } else {
- displayLink?.frameInterval = 1
- // }
- // 加入循环
- displayLink?.add(to: RunLoop.main, forMode: RunLoop.Mode.default)
- }
- }
- // 停止计时
- func stopTimer() {
- if displayLink != nil {
- displayLink?.isPaused = true
- // 将定时器移除主循环
- displayLink?.remove(from: RunLoop.main, forMode: RunLoop.Mode.default)
- // 停止定时器
- displayLink?.invalidate()
- displayLink = nil
- }
- }
- }
- private class EditAudioRecorderDelegateHandler: NSObject {
- var finishClosure: ((_ flag: Bool, _ url: String) -> Void)?
- }
- extension EditAudioRecorderDelegateHandler: AVAudioRecorderDelegate {
- func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
- BFLog(message: "完成录音结果 is \(flag) url is \(recorder.url)")
- if flag {
- finishClosure?(true, recorder.url.relativePath)
- }
- }
- func audioRecorderEncodeErrorDidOccur(_: AVAudioRecorder, error: Error?) {
- guard let error = error else { return }
- BFLog(message: error)
- }
- }
|