BFVoiceRecordManager.swift 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. //
  2. // BFVoiceRecordManager.swift
  3. // BFRecordScreenKit
  4. //
  5. // Created by 胡志强 on 2021/11/24.
  6. //
  7. import Foundation
  8. import BFCommonKit
  9. import BFFramework
  10. import BFVideoEditKit
  11. class BFVoiceRecordManager {
  12. // 录音相关
  13. var audioRecorder: NXAudioRecorder?
  14. var limitedDuration:Double = 600 // 限制录制时长
  15. var endRecordHandle : ((PQVoiceModel?, Error?) -> Void)?
  16. var cancelRecordHandle : ((Error?) -> Void)?
  17. var recorderFilePath : String = ""
  18. var beginRecordTime:Date = Date()
  19. var voiceModel : PQVoiceModel?
  20. /// 录制音频
  21. func startRecord(index:Int){
  22. recorderFilePath = exportAudiosDirectory
  23. if !directoryIsExists(dicPath: recorderFilePath) {
  24. BFLog(message: "文件夹不存在 \(recorderFilePath)")
  25. createDirectory(path: recorderFilePath)
  26. }
  27. recorderFilePath.append("recorder_\(index)_\(Date().timeIntervalSince1970).wav")
  28. BFLog(1, message: "开始录音 \(recorderFilePath)")
  29. do {
  30. try audioRecorder = NXAudioRecorder(path: recorderFilePath)
  31. } catch {
  32. BFLog(message: "录音准备失败,当前无法录音 \(error))")
  33. self.cancelRecordHandle?(error)
  34. return
  35. }
  36. // audioRecorder?.recorderProgross = { [weak self] timer in
  37. // // 所有小段加在一起 > 10 min 自动取消
  38. // let sumTime = timer + (self?.getAudioFileslDuration() ?? 0)
  39. // if sumTime >= (self?.limitedDuration ?? 600) {
  40. // cShowHUB(superView: nil, msg: "最长可录音10分钟")
  41. // self?.stopRecord(isCancel: false)
  42. // return
  43. // }
  44. // }
  45. BFLog(1, message: "开始录制")
  46. audioRecorder?.startRecord()
  47. beginRecordTime = Date()
  48. // beginRecordTime = Date().timeIntervalSince1970
  49. }
  50. /// 取消音频录制
  51. func cancleRecord(){
  52. stopRecord(isCancel: true)
  53. cancelRecordHandle?(nil)
  54. }
  55. /// 结束音频录制
  56. func endRecord(){
  57. stopRecord(isCancel: false)
  58. }
  59. // 取所有声音文件的时长
  60. func getAudioFileslDuration() -> Float64 {
  61. let duration: Float64 = 0.0
  62. // if recorderPart.cacheRecorderCount == 0 { return duration }
  63. // for fileURL in recorderPart.cacheRecorderFiles {
  64. // duration = duration + AVURLAsset(url: fileURL, options: avAssertOptions).duration.seconds
  65. // BFLog(message: "duration === \(duration)")
  66. // }
  67. return duration
  68. }
  69. /// 停止录制 1,正常停止 2,取消停止
  70. /// - Parameter isCancel: 是否为取消
  71. func stopRecord(isCancel: Bool) {
  72. if !(audioRecorder?.recorder.isRecording ?? false) {
  73. BFLog(message: "不是录制状态")
  74. return
  75. }
  76. audioRecorder?.stopRecord { [weak self] isSuccess, url in
  77. guard let strongSelf = self else { return }
  78. if strongSelf.getAudioFileslDuration() < 599, (Date().timeIntervalSince( strongSelf.beginRecordTime)) < 1 {
  79. cShowHUB(superView: nil, msg: "说话时间太短")
  80. }
  81. let duration = Date().timeIntervalSince( strongSelf.beginRecordTime)
  82. if isSuccess && !isCancel && duration > 1 {
  83. BFLog(1, message: "结束录音 结果:\(isSuccess) \n url is \(url)")
  84. // 处理降噪
  85. let noiseFilePath = url.replacingOccurrences(of: ".wav", with: "_noise_\(1)_.wav")
  86. BFLog(1, message: "降噪后地址:\(noiseFilePath)")
  87. NXNoiseReduction().denoise(url, outFile: noiseFilePath)
  88. if let model = self?.voiceModel{
  89. model.wavFilePath = noiseFilePath
  90. self?.endRecordHandle?(model, nil)
  91. }
  92. //
  93. // strongSelf.recorderPart.cacheRecorderFiles.append(URL(fileURLWithPath: noiseFilePath))
  94. // 删除录制的原文件
  95. do {
  96. try FileManager.default.removeItem(atPath: url)
  97. print("Success to remove recorder file. \(url)")
  98. } catch {
  99. print("Failed to remove recorder file. \(url)")
  100. }
  101. }
  102. }
  103. }
  104. // 合并 成 MP3文件
  105. func mergeToMP3file() {
  106. // isMergeIng = true
  107. // PQPlayerViewModel.mergeAudios(urls: recorderPart.cacheRecorderFiles) { [weak self] completURL in
  108. //
  109. // BFLog(message: "completURL is \(String(describing: completURL))")
  110. //
  111. // if completURL?.absoluteString.count ?? 0 > 0 {
  112. // self?.recorderPart.compliteMP3AudioFile = completURL?.relativePath ?? ""
  113. // self?.isMergeIng = false
  114. // }
  115. // }
  116. }
  117. }