Forráskód Böngészése

Merge branch 'dev'

harry 3 éve
szülő
commit
22e41e315a
28 módosított fájl, 2509 hozzáadás és 386 törlés
  1. 1 0
      BFRecordScreenKit.podspec
  2. 21 0
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/music_icon.imageset/Contents.json
  3. BIN
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/music_icon.imageset/music_icon@3x.png
  4. 23 0
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/stuckPoint_edit_loading.imageset/Contents.json
  5. BIN
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/stuckPoint_edit_loading.imageset/stuckPoint_edit_loading.png
  6. BIN
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/stuckPoint_edit_loading.imageset/stuckPoint_edit_loading@2x.png
  7. BIN
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/stuckPoint_edit_loading.imageset/stuckPoint_edit_loading@3x.png
  8. BIN
      BFRecordScreenKit/Assets/stuckPoint_edit_loading.gif
  9. 16 2
      BFRecordScreenKit/Classes/BFRecordExport.swift
  10. 144 0
      BFRecordScreenKit/Classes/RecordScreen/Controller/BFMusicManager.swift
  11. 372 0
      BFRecordScreenKit/Classes/RecordScreen/Controller/BFMusicSearchController.swift
  12. 71 3
      BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenBaseManager.swift
  13. 30 130
      BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenCameraManager.swift
  14. 303 142
      BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenController.swift
  15. 3 2
      BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenImageManager.swift
  16. 5 1
      BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenVideoManager.swift
  17. 71 70
      BFRecordScreenKit/Classes/RecordScreen/View/BFAudioSettingView.swift
  18. 522 10
      BFRecordScreenKit/Classes/RecordScreen/View/BFChooseMusicView.swift
  19. 83 0
      BFRecordScreenKit/Classes/RecordScreen/View/BFCustomMusicBtn.swift
  20. 29 24
      BFRecordScreenKit/Classes/RecordScreen/View/BFLoadingView.swift
  21. 378 0
      BFRecordScreenKit/Classes/RecordScreen/View/BFMusicCutView.swift
  22. 4 1
      BFRecordScreenKit/Classes/RecordScreen/View/BFSubtitleSettingView.swift
  23. 4 1
      BFRecordScreenKit/Classes/RecordScreen/View/BFVoiceSettingView.swift
  24. 11 0
      BFRecordScreenKit/Classes/RecordScreen/View/Cell/BFCameraCoverViewCell.swift
  25. 61 0
      BFRecordScreenKit/Classes/RecordScreen/View/Cell/BFCollectionViewFlowLayout.swift
  26. 225 0
      BFRecordScreenKit/Classes/RecordScreen/View/Cell/BFMuicInfoCell.swift
  27. 99 0
      BFRecordScreenKit/Classes/RecordScreen/View/Cell/BFMusicCategoryCell.swift
  28. 33 0
      BFRecordScreenKit/Classes/RecordScreen/View/Cell/BFMusicInfoSearchCell.swift

+ 1 - 0
BFRecordScreenKit.podspec

@@ -47,6 +47,7 @@ TODO: Add long description of the pod here.
   s.dependency 'BFNetRequestKit'
   s.dependency 'BFMaterialKit'
   s.dependency 'BFMediaKit'
+  s.dependency "BFUploadKit"
   s.dependency 'BFUIKit'
   s.dependency 'GPUImage'
 end

+ 21 - 0
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/music_icon.imageset/Contents.json

@@ -0,0 +1,21 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "music_icon@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/music_icon.imageset/music_icon@3x.png


+ 23 - 0
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/stuckPoint_edit_loading.imageset/Contents.json

@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "filename" : "stuckPoint_edit_loading.png",
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "stuckPoint_edit_loading@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "stuckPoint_edit_loading@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/stuckPoint_edit_loading.imageset/stuckPoint_edit_loading.png


BIN
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/stuckPoint_edit_loading.imageset/stuckPoint_edit_loading@2x.png


BIN
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/stuckPoint_edit_loading.imageset/stuckPoint_edit_loading@3x.png


BIN
BFRecordScreenKit/Assets/stuckPoint_edit_loading.gif


+ 16 - 2
BFRecordScreenKit/Classes/BFRecordExport.swift

@@ -43,6 +43,9 @@ public class BFRecordExport {
         }
     }
 
+    //选择的背景音乐信息
+    public var bgmModel:PQVoiceModel?
+    
     var count = 0
 
     var stickerRanges = [CMTimeRange]()
@@ -444,7 +447,7 @@ public class BFRecordExport {
 
         // 有录音操作或者多个视频,就会进入合成步骤,否则就是一个没有处理的素材,直接导出就行了
         if voiceCount > 0 || videoStickers.count > 1 {
-            let (audioMix, composition) = mergeAudio(videoStickers: videoStickers, audios: voiceList, synthesisAll: synthesisAll)
+            let (audioMix, composition) = mergeAudio(videoStickers: videoStickers, audios: voiceList, synthesisAll: synthesisAll, totalDur: totalDuration)
 
             let outputSize: CGSize = CGSize(width: 1080, height: Int(1080 * CGFloat(UIScreen.main.bounds.size.height / UIScreen.main.bounds.size.width)))
             BFLog(message: "输出视频大小:\(outputSize)")
@@ -540,7 +543,7 @@ public class BFRecordExport {
 }
 
 extension BFRecordExport {
-    func mergeAudio(videoStickers: [PQEditVisionTrackMaterialsModel], audios: [PQEditVisionTrackMaterialsModel]?, synthesisAll: Bool) -> (AVMutableAudioMix, AVMutableComposition) {
+    func mergeAudio(videoStickers: [PQEditVisionTrackMaterialsModel], audios: [PQEditVisionTrackMaterialsModel]?, synthesisAll: Bool, totalDur:Double) -> (AVMutableAudioMix, AVMutableComposition) {
         let composition = AVMutableComposition()
         let audioMix = AVMutableAudioMix()
         var tempParameters = [AVMutableAudioMixInputParameters]()
@@ -563,7 +566,17 @@ extension BFRecordExport {
                 tempParameters += mergeRecordVoiceOnly(voices: voices, composition)
             }
         }
+        //add by ak  处理背景音乐 如果选择了背景音乐添加背景音乐音轨
+        BFLog(message: "合成视频总时长:\(composition.duration.seconds)")
+        if bgmModel != nil, bgmModel?.wavFilePath != nil {
+            bgmModel?.startTime = bgmModel?.currentTime ?? 0.0
+            let bgmParameters = PQPlayerViewModel.dealWithBGMTrack(bgmData: bgmModel!, totalDuration: totalDur, composition: composition)
+            if bgmParameters.count > 0 {
+                tempParameters += bgmParameters
+            }
+        }
         audioMix.inputParameters = tempParameters
+        
         return (audioMix, composition)
     }
 
@@ -593,6 +606,7 @@ extension BFRecordExport {
                 continue
             }
             tempParameters += PQPlayerViewModel.dealWithMaterialTrack(stickerModel: model, composition: composition)
+            totalDur += model.aptDuration
         }
 
         return tempParameters

+ 144 - 0
BFRecordScreenKit/Classes/RecordScreen/Controller/BFMusicManager.swift

@@ -0,0 +1,144 @@
+//
+//  BFMusicManager.swift
+//  BFRecordScreenKit
+//
+//  Created by Harry on 2022/3/4.
+//
+
+import Foundation
+import BFMediaKit
+import BFCommonKit
+import BFNetRequestKit
+import Alamofire
+
+
+class BFMusicManager {
+    
+    var playStatusCallback : ((AVPlayerItem?, BFMuicInfoCellState) -> Void)?
+    var needDeteactiveStatus = false
+    var currPlayUrlPath: String?
+    // 试听音乐
+    let player:AVPlayer = {
+        let p = AVPlayer(playerItem: nil)
+        return p
+    }()
+    
+    var loadedTimeRangesObserver : NSKeyValueObservation?
+    var retaRangesObserver : NSKeyValueObservation?
+
+    init() {
+        loadedTimeRangesObserver = player.observe(\AVPlayer.currentItem?.loadedTimeRanges, options: [.new, .initial]) { [weak self] (player, change) in
+
+            DispatchQueue.main.async {[weak self] in
+                guard let wself = self else { return }
+                
+                guard let ranges = change.newValue as? [CMTimeRange] else { return }
+                if let totalDur = player.currentItem?.duration, totalDur.isValid,  CMTimeCompare(ranges.first?.duration ?? .zero, totalDur) >= 0{
+                    wself.playStatusCallback?(wself.player.currentItem, .playing)
+                }
+            }
+        }
+        
+    }
+    
+    func play() {
+        player.play()
+        
+        needDeteactiveStatus = true
+        deteactiveStaus()
+    }
+    
+    func deteactiveStaus() {
+        DispatchQueue.global().asyncAfter(deadline: .now() + 0.05) {[weak self] in
+            guard let wself = self else { return }
+            
+            if wself.player.currentItem?.isPlaybackLikelyToKeepUp ?? false{
+                wself.playStatusCallback?(wself.player.currentItem, .playing)
+            }else if (wself.needDeteactiveStatus){
+                wself.deteactiveStaus()
+            }
+        }
+    }
+    
+    func pause(_ status:BFMuicInfoCellState = .pause) {
+        player.pause()
+        needDeteactiveStatus = false
+        playStatusCallback?(player.currentItem, status)
+    }
+    
+    func stop() {
+        playStatusCallback?(player.currentItem, .normal)
+
+    }
+    
+    func replaceCurrentItem(musicPath: String?) {
+        if currPlayUrlPath == musicPath {
+            return
+        }
+        currPlayUrlPath = musicPath
+        
+        if player.currentItem != nil {
+            NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
+        }
+        
+        if let musicPath = musicPath, let url = URL(string: musicPath) {
+            player.replaceCurrentItem(with: AVPlayerItem(url: url))
+        }else {
+            player.replaceCurrentItem(with: nil)
+        }
+        NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: player.currentItem, queue: .main) {[weak self] notice in
+            guard let wself = self else { return }
+            
+            wself.player.currentItem?.seek(to: wself.player.currentItem?.reversePlaybackEndTime ?? .zero, completionHandler: nil)
+            wself.play()
+        }
+    }
+    
+    deinit {
+        NotificationCenter.default.removeObserver(self)
+        loadedTimeRangesObserver?.invalidate()
+        loadedTimeRangesObserver = nil
+    }
+
+}
+
+
+// class method
+extension BFMusicManager {
+    public class func downloadMusic(model: PQVoiceModel, complete:(@escaping () -> Void)) {
+
+        if let path = model.musicPath, let url = URL(string: path) {
+            
+            let request = URLRequest(url: url)
+            let session = URLSession.shared
+            let downloadTask = session.downloadTask(with: request,
+                   completionHandler: { (location:URL?, response:URLResponse?, error:Error?)
+                    -> Void in
+                print("location:\(location)")
+                if let locationPath = location?.path {
+                    BFMusicManager.moveLocation(locationPath: locationPath, model:model)
+                }
+                
+                complete()
+            })
+            downloadTask.resume()
+        }
+        
+        
+    }
+    
+    fileprivate class func moveLocation(locationPath: String, model: PQVoiceModel) {
+        let fileManager = FileManager.default
+        let fileName = (model.musicPath! as NSString).lastPathComponent
+        let documnets = bgMusicDirectory + fileName + ".mp3"
+        createDirectory(path: bgMusicDirectory)
+        do {
+            try fileManager.moveItem(atPath: locationPath, toPath: documnets)
+            model.wavFilePath = documnets
+        } catch {
+            BFLog(1, message: "移动失败, \(error)")
+        }
+        print("new location:\(documnets)")
+
+    }
+}

+ 372 - 0
BFRecordScreenKit/Classes/RecordScreen/Controller/BFMusicSearchController.swift

@@ -0,0 +1,372 @@
+//
+//  BFMusicSearchController.swift
+//  BFRecordScreenKit
+//
+//  Created by Harry on 2022/3/3.
+//
+
+import Foundation
+import BFUIKit
+import BFMediaKit
+import BFCommonKit
+import BFNetRequestKit
+import Alamofire
+import MJRefresh
+
+class BFMusicSearchController: BFBaseViewController {
+    
+    var choseAction : ((PQVoiceModel?) -> Void)?
+    
+    var cutActionCallback: ((PQVoiceModel) -> Void)?
+    
+    var loadedTimeRangesObserver : NSKeyValueObservation?
+
+    var chosedIndexPath : IndexPath?
+
+    var chosedCellStatu : BFMuicInfoCellState = .pause
+    
+    var searchResults = [PQVoiceModel]()
+    
+    var currPage = 1
+    
+    var musicVolume = 0 {
+        didSet{
+            slidV.value = Float(musicVolume) / 100.0
+            uiForChangeVolume()
+        }
+    }
+    var keywork : String?
+
+    // 试听音乐
+    let playerManager = BFMusicManager()
+    
+    lazy var searchTF : UITextField = {
+        let searchTF = UITextField()
+        let leftv = UIView(frame: CGRect(x: 0, y: 0, width: 30, height: 20))
+        let iv = UIImageView(frame: CGRect(x: 6, y: 0, width: 20, height: 20))
+        iv.contentMode = .scaleAspectFit
+        iv.image = imageInRecordScreenKit(by: "search")
+        leftv.addSubview(iv)
+        searchTF.leftView = leftv
+        searchTF.leftViewMode = .always
+        searchTF.clearButtonMode = .never
+       
+        let redPlaceholderText = NSAttributedString(string: "歌名/歌手名",
+                                                    attributes: [NSAttributedString.Key.foregroundColor:  UIColor.hexColor(hexadecimal: "#BDBDBD"),NSAttributedString.Key.font:UIFont.systemFont(ofSize: 14,weight: .regular)])
+        searchTF.attributedPlaceholder = redPlaceholderText
+        searchTF.font = UIFont.systemFont(ofSize: 14,weight: .regular)
+        searchTF.delegate = self
+        searchTF.returnKeyType = .search
+        searchTF.textColor = UIColor.white
+        searchTF.backgroundColor = UIColor.hexColor(hexadecimal: "#1d1d1d")
+        searchTF.layer.cornerRadius = 18
+        
+        return searchTF
+    }()
+    
+    lazy var progressL : UILabel = {
+        let l = UILabel()
+        l.textColor = UIColor.hexColor(hexadecimal: "#9d9d9d")
+        l.font = UIFont.systemFont(ofSize: 13, weight: .regular)
+        l.textAlignment = .center
+        l.text = "0%"
+        return l
+    }()
+    
+    lazy var slidV : UISlider = {
+        let v = UISlider()
+        v.addTarget(self, action: #selector(valuChange(slid:)), for: .valueChanged)
+        return v
+    }()
+    
+    lazy var musicTb : UITableView = {
+        let tb = UITableView(frame: .zero)
+        tb.delegate = self
+        tb.dataSource = self
+        tb.separatorColor = UIColor.hexColor(hexadecimal: "#272727")
+        tb.register(BFMusicInfoSearchCell.self, forCellReuseIdentifier: "BFMusicInfoSearchCell")
+        tb.tableFooterView = UIView()
+        tb.backgroundColor = .clear
+//        tb.mj_footer = MJRefreshAutoFooter.init(refreshingBlock: {[weak self] in
+//            guard let wself = self else { return }
+//            if wself.searchResults.count > 0 {
+//                wself.requestSearchMusic()
+//            }
+//        })
+        return tb
+    }()
+    
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+        
+        searchTF.becomeFirstResponder()
+    }
+    
+    override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        uiForChangeVolume()
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        let closeBtn = UIButton()
+        closeBtn.setImage(imageInRecordScreenKit(by: "search_close"), for: .normal)
+        closeBtn.frame = CGRect(x: 12, y: statusBarHeight + 2, width: 40, height: 40)
+        closeBtn.addTarget(self, action: #selector(closeAction), for: .touchUpInside)
+        view.addSubview(closeBtn)
+        
+        searchTF.frame = CGRect(x: 62, y: statusBarHeight + 4, width: cScreenWidth - 62 - 18, height: 36)
+        view.addSubview(searchTF)
+
+        view.addSubview(musicTb)
+        musicTb.snp.makeConstraints { make in
+            make.left.right.equalToSuperview()
+            make.top.equalTo(statusBarHeight + 44)
+            make.bottom.equalTo(-100)
+        }
+        
+        let soundIV = UIImageView(image: imageInRecordScreenKit(by: "soundBtn"))
+        soundIV.contentMode = .scaleAspectFit
+        view.addSubview(soundIV)
+        soundIV.snp.makeConstraints { make in
+            make.left.equalTo(18)
+            make.top.equalTo(musicTb.snp.bottom).offset(36)
+            make.width.height.equalTo(28)
+        }
+        
+        view.addSubview(slidV)
+        slidV.frame = CGRect(x: 56, y: cScreenHeigth - 59, width: cScreenWidth - 56 - 18, height: 18)
+
+        progressL.frame = CGRect(x: slidV.x - 7, y: slidV.y - 24, width: 36, height: 16)
+        view.addSubview(progressL)
+        
+//        musicVolume = 0.2
+        
+        playerManager.playStatusCallback = {[weak self] playerItem, playStatus in
+            guard let wself = self else { return }
+            
+            if let indx = wself.chosedIndexPath, let cell = wself.musicTb.cellForRow(at: indx) as? BFMuicInfoCell, (playerItem?.asset as? AVURLAsset)?.url.absoluteString ?? "b" == cell.data?.musicPath ?? "a" {
+                DispatchQueue.main.async {
+                    cell.status = playStatus                    
+                }
+            }
+            wself.chosedCellStatu = playStatus
+        }
+        
+    }
+    
+    func choseCell() -> BFMuicInfoCell? {
+        if let indpx = chosedIndexPath, let cell = musicTb.cellForRow(at: indpx) as? BFMuicInfoCell {
+            return cell
+        }
+        return nil
+    }
+    
+    @objc func closeAction(){
+        self.dismiss(animated: true, completion: nil)
+    }
+    
+    @objc func valuChange(slid:UISlider) {
+        musicVolume = Int(slid.value * 100)
+    }
+    
+    func uiForChangeVolume() {
+
+        progressL.text = String(format: "%d%%", musicVolume)
+        
+        var frame = progressL.frame
+        frame.origin.x = slidV.x - 7 + ((slidV.width - 28) / 100.0) * CGFloat(musicVolume)
+        progressL.frame = frame
+        
+        playerManager.player.volume = Float(musicVolume) / 100.0
+
+    }
+}
+
+extension BFMusicSearchController : UITableViewDelegate, UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return searchResults.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: "BFMusicInfoSearchCell")
+        
+        if let cell = cell as? BFMusicInfoSearchCell{
+            cell.data = searchResults[indexPath.row]
+
+            cell.useCallback = {[weak self, weak cell] in
+                guard let wself = self else { return }
+                wself.playerManager.pause()
+                cell?.status = .pause
+                cell?.data?.volume = (Int)(wself.slidV.value * 100.0)
+                wself.choseAction?(cell?.data)
+                wself.dismiss(animated: true, completion: nil)
+            }
+            cell.cutCallBack = {[weak self, weak cell] in
+                guard let wself = self else { return }
+                wself.playerManager.pause()
+                cell?.status = .pause
+                cell?.data?.volume = (Int)(wself.slidV.value * 100.0)
+
+                if let data = cell?.data {
+                    wself.dismiss(animated: true) {[weak self] in
+                        guard let wself = self else { return }
+                        wself.cutActionCallback?(data)
+                    }
+                }
+            }
+
+            if cell.data?.isSelected ?? false {
+                chosedIndexPath = indexPath
+                cell.status = chosedCellStatu
+            }else{
+                cell.status = .normal
+            }
+        }
+        
+        cell?.selectionStyle = .none
+        
+        return cell!
+    }
+    
+    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        return 64
+    }
+    
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        
+        if let cell = tableView.cellForRow(at: indexPath) as? BFMusicInfoSearchCell {
+            if chosedIndexPath == indexPath {
+                if cell.status != .pause{
+                    cell.status = .pause
+                    playerManager.pause()
+                }else{
+                    cell.status = .loading
+                    playerManager.play()
+                }
+                return
+            }
+            cell.changeSelected(true)
+            
+            chosedIndexPath = indexPath
+            
+            if cell.status == .normal {
+                if let urlStr = cell.data?.musicPath, let url = URL(string: urlStr){
+                    BFLog(1, message: "歌曲地址: \(url)")
+                    if url.absoluteString != (playerManager.currPlayUrlPath ?? "b") {
+                        playerManager.replaceCurrentItem(musicPath: urlStr)
+                        cell.status = .loading
+                    }else {
+                        cell.status = .playing
+                    }
+                    playerManager.play()
+                }
+            }else if cell.status == .pause{
+                playerManager.play()
+            } else{
+                playerManager.pause()
+                cell.status = .pause
+            }
+            chosedCellStatu = cell.status
+
+        }
+    }
+    
+    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
+        searchResults[indexPath.row].isSelected = false
+        let cell = tableView.cellForRow(at: indexPath) as? BFMusicInfoSearchCell
+        cell?.status = .normal
+        cell?.changeSelected(false)
+        
+        chosedIndexPath = nil
+        playerManager.pause()
+    }
+    
+    // MARK: - 搜素接口
+    func requestSearchMusic() {
+        if keywork?.count ?? 0 < 1 {
+            cShowHUB(superView: nil, msg: "搜索不能为空")
+            return
+        }
+
+        BFNetRequestAdaptor.postRequestData(url: PQENVUtil.shared.materialSearchApi + searchBGMMaterialUrl, parames: ["keyWord": keywork, "pageNo": 1, "pageSize": 200], commonParams: commonParams(), encoding: JSONEncoding.default, isJsonEncodingNormal: true) { [weak self] response, _, error, _ in
+            DispatchQueue.global().async {[weak self] in
+                guard let wself = self else { return }
+                
+                var bgmList = [PQVoiceModel]()
+                if response is NSNull || response == nil {
+                    DispatchQueue.main.async {
+                        cShowHUB(superView: nil, msg: "网络连接失败,请检查网络后重试")
+                    }
+                    return
+                }
+                let tempArr = (response as? [String: Any])?["entityList"] as? [[String: Any]]
+                if tempArr != nil {
+                    for item in tempArr! {
+                        let tempModel = PQVoiceModel(jsonDict: item)
+                        tempModel.volume = 30
+                        tempModel.voiceType = VOICETYPT.BGM.rawValue
+                        bgmList.append(tempModel)
+                    }
+                }
+                if wself.currPage == 1 {
+                    wself.searchResults.removeAll()
+                }
+                wself.searchResults.append(contentsOf: bgmList)
+                wself.currPage += 1
+                DispatchQueue.main.async {[weak self] in
+                    guard let wself = self else { return }
+                    
+                    if wself.searchResults.count > 0 {
+//                        wself.musicTb.tableFooterView = UIView()
+                    }else{
+                        wself.musicTb.tableFooterView = wself.createNoDataView()
+                    }
+                    wself.musicTb.reloadData()
+                }
+            }
+        }
+    }
+    
+    func createNoDataView() -> UIView {
+        let back = UIView(frame: CGRect(x: 0, y: 0, width: cScreenWidth, height: 200))
+        
+        let l = UILabel(frame: CGRect(x: 40, y: 60, width: cScreenWidth - 80, height: 80))
+        l.text = "没有搜索到相关音乐\n换个词再试试吧"
+        l.textColor = UIColor.hexColor(hexadecimal: "#787878")
+        l.textAlignment = .center
+        l.numberOfLines = 2
+        l.font = UIFont.systemFont(ofSize: 17)
+        back.addSubview(l)
+        
+        return back
+    }
+    
+    func resetRearch() {
+        playerManager.pause()
+        chosedCellStatu = .normal
+        chosedIndexPath = nil
+        currPage = 1
+    }
+}
+
+extension BFMusicSearchController : UITextFieldDelegate {
+    
+    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
+        if let keyword = textField.text, keyword.replacingOccurrences(of: " ", with: "", options: .literal, range: nil).count > 0 {
+            resetRearch()
+            keywork = keyword
+            requestSearchMusic()
+        }else{
+            cShowHUB(superView: nil, msg: "搜索不能为空")
+        }
+        
+        textField.resignFirstResponder()
+        
+        
+        return true
+    }
+    
+    
+}

+ 71 - 3
BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenBaseManager.swift

@@ -28,6 +28,14 @@ class BFRecordScreenBaseManager : NSObject{
         }
     }
     
+    //播放背景音乐
+    lazy var playBGMPlayer : BFAudioPlayer = {
+         let player = BFAudioPlayer.init()
+         player.delegate = self
+         return player
+        
+    }()
+    
     var playView : GPUImageView?
     var filter = GPUImageFilter()
     
@@ -39,6 +47,7 @@ class BFRecordScreenBaseManager : NSObject{
     }
 
     var recording = false
+    //录制的音频或变音是否正在播放
     var isPlaying = false
     // MARK: -
     func resetEnv(){
@@ -60,9 +69,15 @@ class BFRecordScreenBaseManager : NSObject{
     
     func play(){
         
+//        playBGM()
     }
     
     func pause(){
+
+        playBGMPlayer.pause()
+        
+        // add by ak 暂停时 isPlaying 设置为 false
+        isPlaying = false
         
     }
     
@@ -91,6 +106,8 @@ class BFRecordScreenBaseManager : NSObject{
         DispatchQueue.global().async {
             GPUImageContext.sharedFramebufferCache().purgeAllUnassignedFramebuffers()
         }
+        NotificationCenter.default.removeObserver(self)
+
     }
     
     func checkCurrentPlayStatus(){
@@ -112,14 +129,40 @@ class BFRecordScreenBaseManager : NSObject{
                     wself.recordPlayer?.play()
 
                 })
-            }else{
-                BFLog(message: "this is run isPlaying\(isPlaying) su:\(su)")
             }
             
         }else{
             
-            BFLog(message: "voice is nill");
+            BFLog(message: "voice is nill currentAssetProgress is\(currentAssetProgress)");
+        }
+    }
+    
+    
+    /// 播放背景音乐方法
+    func playBGM(){
+        if(dele?.bgmModel != nil){
+            
+            if(dele?.bgmModel?.endCMTime.seconds == 0){
+                BFLog(message: "当前素材的结束时间有问题播放不成功.");
+                return
+            }
+            
+            playBGMPlayer.configPlayer(audioPathURL: URL(fileURLWithPath: dele?.bgmModel?.wavFilePath ?? ""))
+            playBGMPlayer.player?.volume = Float(dele?.bgmModel?.volume ?? 100) / 100.0
+            //原背景音乐可使用时长
+            let bgmSeconds = ((dele?.bgmModel?.endCMTime ?? .zero) - (dele?.bgmModel?.startCMTime ?? .zero)).seconds
+            //余数
+            let step = currentAssetProgress.seconds.truncatingRemainder(dividingBy: bgmSeconds)
+            var seekTime = (dele?.bgmModel?.startCMTime ?? .zero).seconds + step
+            if(step == 0){//当时间小于音乐时长
+                seekTime += currentAssetProgress.seconds
+            }
+            BFLog(message: "seekTime is: \(seekTime) step is:\(step)")
+           
+            playBGMPlayer.seekTime(seekTime)
+            playBGMPlayer.play()
         }
+        
     }
     
     deinit {
@@ -127,3 +170,28 @@ class BFRecordScreenBaseManager : NSObject{
         BFLog(1, message: "\(self) release")
     }
 }
+
+extension BFRecordScreenBaseManager: BFAudioPlayerDelegate{
+    
+    // 播放完成
+    func audioPlayerDidFinish(audioPlayer:BFAudioPlayer){
+        BFLog(message: "audioPlayerDidFinish")
+        //背景音乐播放完成后,素材还在播放,再次调用播放背景音乐,开始播放的起点在playBGM已处理
+        if(isPlaying){
+            playBGM()
+            BFLog(message: "进行下一次播放背景音乐")
+        }
+       
+
+    }
+    
+    // 播放开始
+    func audioPlayerStart(audioPlayer:BFAudioPlayer){
+        
+    }
+    // 播放进度
+    func playProgress(audioPlayer:BFAudioPlayer,currentTime: TimeInterval, progress: Double){
+
+    }
+    
+}

+ 30 - 130
BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenCameraManager.swift

@@ -61,7 +61,7 @@ class BFRecordScreenCameraManager: BFRecordScreenBaseManager {
     var hadDeleteVideoOfFailed = false
 
     var movieWrite: GPUImageMovieWriter?
-
+    
     lazy var camera: GPUImageStillCamera? = {
         let camera = GPUImageStillCamera(sessionPreset: AVCaptureSession.Preset.hd1920x1080.rawValue, cameraPosition: AVCaptureDevice.Position.back)
         camera?.outputImageOrientation = UIInterfaceOrientation.portrait
@@ -76,15 +76,12 @@ class BFRecordScreenCameraManager: BFRecordScreenBaseManager {
     }()
 
     var groupCount = 0
-    
-    // 录音文件是否正在播放
-    var audioPlayerIsPlaying:Bool = false
-
-    lazy var avplayer: AVPlayer = {
-        let p = AVPlayer(playerItem: nil)
-        
-        return p
-    }()
+ 
+//    lazy var assetPlayer: AVPlayer = {
+//        let p = AVPlayer(playerItem: nil)
+//
+//        return p
+//    }()
 
     lazy var playerCoverIV: UIImageView = {
         let iv = UIImageView()
@@ -96,7 +93,7 @@ class BFRecordScreenCameraManager: BFRecordScreenBaseManager {
     }()
 
     lazy var playerLayer: AVPlayerLayer = {
-        let layer = AVPlayerLayer(player: avplayer)
+        let layer = AVPlayerLayer(player: assetPlayer)
         layer.frame = UIScreen.main.bounds
         return layer
     }()
@@ -416,6 +413,7 @@ class BFRecordScreenCameraManager: BFRecordScreenBaseManager {
 
     // 使用avplayer进行播放视频和音频
     override func play() {
+        super.play()
         guard playView != nil else {
             return
         }
@@ -428,99 +426,29 @@ class BFRecordScreenCameraManager: BFRecordScreenBaseManager {
         if (recordItem?.materialDuraion.seconds)! - currentAssetProgress.seconds < 0.1 {
             currentAssetProgress = .zero
             locationTo(time: currentAssetProgress)
-            avplayer.replaceCurrentItem(with: nil)
+            assetPlayer?.replaceCurrentItem(with: nil)
         }
         
         startTimer(isPlay: true)
     }
-
-    func playRecordVoice(needPlay: Bool = true) {
-
-        // 三种状态值ff
-//        case paused = 0
-//        case waitingToPlayAtSpecifiedRate = 1
-//        case playing = 2
-        if (BFAudioPlayer.shared.player?.isPlaying ?? false || audioPlayerIsPlaying){
-//            BFLog(2, message: "is playering 本次查找无效,有播放任务")
-            return
-        }
-//        BFLog(2,message: "开始查找!!!!当前进度:\(currentAssetProgress.seconds)")
-
-        // 播放音频
-        var useVoiceStickers = [PQVoiceModel]()
-        if (recordItem?.voiceChangeStickers.count ?? 0) > 0 {
-            useVoiceStickers = recordItem?.voiceChangeStickers ?? [PQVoiceModel]()
-        } else {
-            useVoiceStickers = recordItem?.voiceStickers ?? [PQVoiceModel]()
-        }
-
-        if let mod = useVoiceStickers.first(where: { m in
-            CMTimeCompare(m.startCMTime, currentAssetProgress) <= 0 && CMTimeCompare(m.endCMTime, currentAssetProgress) > 0
-
-        }) {
-            BFLog(message: "找到音频开始时间:\(mod.startCMTime.seconds)结束 \(mod.endCMTime.seconds) currentAssetProgress is \(currentAssetProgress.seconds) wavFilePath:\(mod.wavFilePath ?? "")")
-
-            // 当前播放的文件
-            if playingVoiceMode == mod {
-                BFLog(2,message: "找查到mod的已经播放完成同一个 wavFilePath:\(mod.wavFilePath ?? "")")
-                return
-            }
-            playingVoiceMode = mod
-            
-            let cureentPalyURlString = BFAudioPlayer.shared.currentAudioPathURL?.absoluteString ?? ""
-            if cureentPalyURlString.replacingOccurrences(of: "file://", with: "") == mod.wavFilePath && audioPlayerIsPlaying{
-                BFLog(2, message: "命中的和播放的文件路径一样? wavFilePath:\(mod.wavFilePath ?? "")")
-//                audioPlayerIsPlaying = true
-//                BFAudioPlayer.shared.playOrPause()
-                
-//                if !(audioPlayerIsPlaying) {
-////                        audioPlayerIsPlaying = true
-                    BFAudioPlayer.shared.setPlayingTime((currentAssetProgress - mod.startCMTime).seconds)
-////                        BFAudioPlayer.shared.playOrPause()
-//                }
-
-
-                return
-            }
- 
-            BFAudioPlayer.shared.delegate = self
-            BFAudioPlayer.shared.configPlayer(audioPathURL: URL(fileURLWithPath: mod.wavFilePath))
- 
-            let tt = currentAssetProgress - mod.startCMTime
-            BFLog(1, message: "record - tt: \(tt.seconds), curr:\(currentAssetProgress.seconds) \(needPlay) rate:\(BFAudioPlayer.shared.player?.rate  ?? -1)   \(mod.wavFilePath ?? "")")
-            if CMTimeCompare(tt, .zero) == 0 {
-                if needPlay {
-                    audioPlayerIsPlaying = true
-                    BFAudioPlayer.shared.playOrPause()
-                }
-                return
-            }
-            audioPlayerIsPlaying = true
-            BFAudioPlayer.shared.setPlayingTime(tt.seconds)
-            BFAudioPlayer.shared.playOrPause()
- 
-        } else {
-            BFLog(message: "没有找到即将播放的录制音频文件?\(currentAssetProgress.seconds)")
-        }
-    }
-
+    
     func prepareToPlayNext(needPlay: Bool = true) {
         
         if let mod = recordItem?.videoStickers.first(where: { mode in
             CMTimeCompare(mode.timelineCMIn, currentAssetProgress) <= 0 && CMTimeCompare(currentAssetProgress, mode.timelineCMOut) < 0
         }) {
             // true 代表需要换源
-            let su = (avplayer.currentItem?.asset as? AVURLAsset)?.url.lastPathComponent != (mod.locationPath as NSString?)?.lastPathComponent
+            let su = (assetPlayer?.currentItem?.asset as? AVURLAsset)?.url.lastPathComponent != (mod.locationPath as NSString?)?.lastPathComponent
             
             if !su {
-                if avplayer.timeControlStatus == .paused { // 同源且暂停的时候,需要修改进度
+                if assetPlayer?.timeControlStatus == .paused { // 同源且暂停的时候,需要修改进度
                     setCoverImage(currentAssetProgress)
                     
                     let tt = currentAssetProgress - mod.timelineCMIn
-                    avplayer.currentItem?.seek(to: tt, toleranceBefore: CMTime(value: 1, timescale: 1_000_000), toleranceAfter: CMTime(value: 1, timescale: 1_000_000), completionHandler: { [weak avplayer] _ in
+                    assetPlayer?.currentItem?.seek(to: tt, toleranceBefore: CMTime(value: 1, timescale: 1_000_000), toleranceAfter: CMTime(value: 1, timescale: 1_000_000), completionHandler: { [weak assetPlayer] _ in
 
                         if needPlay {
-                            avplayer?.play()
+                            assetPlayer?.play()
                         }
                     })
                     
@@ -537,15 +465,15 @@ class BFRecordScreenCameraManager: BFRecordScreenBaseManager {
             let tt = currentAssetProgress - mod.timelineCMIn
             BFLog(1, message: "asset - tt: \(tt.seconds), curr:\(currentAssetProgress.seconds)")
             
-            if CMTimeCompare(tt, (avplayer.currentItem?.duration ?? .zero)) == 0 {
+            if CMTimeCompare(tt, (assetPlayer?.currentItem?.duration ?? .zero)) == 0 {
                 if needPlay {
-                    avplayer.play()
+                    assetPlayer?.play()
                 }
             } else {
-                avplayer.currentItem?.seek(to: tt, toleranceBefore: CMTime(value: 1, timescale: 1_000_000), toleranceAfter: CMTime(value: 1, timescale: 1_000_000), completionHandler: { [weak avplayer] _ in
+                assetPlayer?.currentItem?.seek(to: tt, toleranceBefore: CMTime(value: 1, timescale: 1_000_000), toleranceAfter: CMTime(value: 1, timescale: 1_000_000), completionHandler: { [weak assetPlayer] _ in
 
                     if needPlay {
-                        avplayer?.play()
+                        assetPlayer?.play()
                     }
                 })
             }
@@ -559,29 +487,26 @@ class BFRecordScreenCameraManager: BFRecordScreenBaseManager {
     }
 
     func avplayerReplaceItem(newItem: AVPlayerItem?) {
-        if let item = avplayer.currentItem {
+        if let item = assetPlayer?.currentItem {
             if (item.asset as? AVURLAsset)?.url == (newItem?.asset as? AVURLAsset)?.url {
                 return
             }
             NotificationCenter.default.removeObserver(self as Any, name: .AVPlayerItemDidPlayToEndTime, object: item)
         }
 
-        avplayer.replaceCurrentItem(with: newItem)
+        assetPlayer?.replaceCurrentItem(with: newItem)
         if newItem == nil {
             return
         }
 
-        NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: avplayer.currentItem, queue: .main) { [weak self] _ in
+        NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: assetPlayer?.currentItem, queue: .main) { [weak self] _ in
             guard let wself = self else {
                 return
             }
 
-            BFLog(1, message: "endPlay:\(wself.currentAssetProgress.seconds), 总时长:\(wself.recordItem!.materialDuraion.seconds), currplayer:\(wself.avplayer.currentItem?.currentTime().seconds ?? 0)")
+//            BFLog(1, message: "endPlay:\(wself.currentAssetProgress.seconds), 总时长:\(wself.recordItem!.materialDuraion.seconds), currplayer:\(wself.assetPlayer.currentItem?.currentTime().seconds ?? 0)")
 
-            // 判断录音是否还在播放,如果在播放停止
-            if BFAudioPlayer.shared.player?.isPlaying ?? false {
-                wself.recordPlayer?.pause()
-            }
+    
 //            // 如果离结束大于0.7s,代表还有下一段视频
 //            if ((wself.recordItem?.materialDuraion ?? .zero) - wself.currentAssetProgress).seconds > 0.7 {
 ////                wself.findNextModel()
@@ -599,13 +524,10 @@ class BFRecordScreenCameraManager: BFRecordScreenBaseManager {
     }
 
     override func pause() {
+        super.pause()
         dispatchWorkItemIsCancel = true
-        avplayer.pause()
-       
-        BFAudioPlayer.shared.pause()
-
-        audioPlayerIsPlaying = false
-
+        assetPlayer?.pause()
+        recordPlayer?.pause()
         playingVoiceMode = nil
 
         timerr?.invalidate()
@@ -667,9 +589,9 @@ class BFRecordScreenCameraManager: BFRecordScreenBaseManager {
         filter.removeTarget(movieWrite)
         filter.removeTarget(rendView)
         camera?.removeTarget(filter)
-
         camera?.stopCapture()
 
+        
         super.clean()
     }
 
@@ -718,11 +640,9 @@ class BFRecordScreenCameraManager: BFRecordScreenBaseManager {
         }
 
         // 检测录音的
-//        checkCurrentPlayStatus()
+        checkCurrentPlayStatus()
         // 检测视频的
         prepareToPlayNext()
-        // 新的检测录音的
-        playRecordVoice()
     }
 
     func timerForRecord(currTime _: CMTime) {
@@ -794,27 +714,7 @@ class BFRecordScreenCameraManager: BFRecordScreenBaseManager {
         movieWrite = GPUImageMovieWriter(movieURL: URL(fileURLWithPath: vpath), size: videoSize)
         filter.addTarget(movieWrite)
     }
-}
-
-extension BFRecordScreenCameraManager: BFAudioPlayerDelegate{
-    
-    // 播放完成
-    func audioPlayerDidFinish(){
-        BFLog(message: "audioPlayerDidFinish")
-        audioPlayerIsPlaying = false
-        BFAudioPlayer.shared.pause()
-    }
-    
-    // 播放开始
-    func audioPlayerStart(){
-        
-    }
-    // 播放进度
-    func playProgress(currentTime: TimeInterval, progress: Double){
-        
-//        BFLog(message: "currentTime is \(currentTime) progress\(progress) \(BFAudioPlayer.shared.currentAudioPathURL?.absoluteString ?? "")")
-    }
-    
+  
 }
 
 extension BFRecordScreenCameraManager: GPUImageVideoCameraDelegate {

+ 303 - 142
BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenController.swift

@@ -17,7 +17,10 @@ import Foundation
 import GPUImage
 import Photos
 import UIKit
-
+import BFUploadKit
+import Kingfisher
+import BFAnalyzeKit
+import BFCommonKit
 struct WithDrawModel {
     var type: Int // 0:拖动; 1:预览播放暂停 2: 录音结束  3: 删除录音
     var timestamp: CMTime
@@ -48,6 +51,7 @@ public class BFRecordScreenController: BFBaseViewController {
     public var nextActionHandle: (() -> Void)?
     public var closeActionHandle: (() -> Void)?
     public var changeItemHandle: ((_ index: Int) -> Void)?
+    public var cleanScreen: ((Bool) -> Void)?
     // 当前录制结束的上报
     public var recordEndHandle: ((_ currentRecord: PQVoiceModel?) -> Void)?
     // 字幕按钮点击上报
@@ -55,6 +59,8 @@ public class BFRecordScreenController: BFBaseViewController {
 //    public var currentRecordId: String? // 当前录制Id
     //选了发音人点了确定
     public var voiceBtnClickHandle: ((_ voice: PQVoiceModel?) -> Void)?
+    //选择的背景音乐信息
+    public var bgmModel:PQVoiceModel?
     // MARK: - 素材参数
     lazy var rsvmanager : BFRecordScreenVideoManager = {
         let m = BFRecordScreenVideoManager()
@@ -73,6 +79,9 @@ public class BFRecordScreenController: BFBaseViewController {
     
     lazy var rscmanager : BFRecordScreenCameraManager = {
         let m = BFRecordScreenCameraManager()
+        m.assetPlayer = assetPlayer
+
+        
         m.recordProgressCallBack = {[weak self] progress in
             guard let wself = self else { return }
             wself.rscurrentManager.currentAssetProgress = progress
@@ -126,6 +135,11 @@ public class BFRecordScreenController: BFBaseViewController {
             let itemModel = itemModels[currItemModelIndex]
             currMediaType = itemModel.mediaType ?? .Video
             isEndPlay = false
+            
+            assetPlayer.pause()
+            assetPlayer.replaceCurrentItem(with: nil)
+            recordPlayer.pause()
+            recordPlayer.replaceCurrentItem(with: nil)
 
             switch itemModel.mediaType {
             case .Image:
@@ -213,6 +227,7 @@ public class BFRecordScreenController: BFBaseViewController {
             voiceSettingBtn.isHidden = isRecording
             subtitleBtn.isHidden = isRecording
             soundSettingBtn.isHidden = isRecording
+            addMusicBtn.isHidden = isRecording
             recordBtn.setTitle(isRecording ? "record_pause".BFLocale : (currMediaType == .Camera ? "record_lz".BFLocale : "record_ly".BFLocale), for: .normal)
             recordBtn.backgroundColor = UIColor.hexColor(hexadecimal: "#389AFF", alpha: isRecording ? 0.6 : 1)
             cameraFlipBtn.isHidden = !(!isRecording && (currMediaType == .Camera))
@@ -243,6 +258,7 @@ public class BFRecordScreenController: BFBaseViewController {
             soundSettingBtn.isHidden = isNormalPlaying
             voiceSettingBtn.isHidden = isNormalPlaying
             withDrawBtn.isHidden = isNormalPlaying
+            addMusicBtn.isHidden = isNormalPlaying
             updateRecordBtnStatus(needHidden: isNormalPlaying)
             cameraFlipBtn.isHidden = !(!isNormalPlaying && (currMediaType == .Camera) && !isDragingProgressSlder)
         }
@@ -260,8 +276,9 @@ public class BFRecordScreenController: BFBaseViewController {
     var currenStartPlayTime: CMTime = .zero
     var pauseTime: Double = 0 // 停止无操作的时间点
 
-    var assetPlayer: AVPlayer = AVPlayer(playerItem: nil) // 原视频音频播放器
-    var recordPlayer: AVPlayer = AVPlayer(playerItem: nil) // 录音音频播放器
+    var assetPlayer     : AVPlayer = AVPlayer(playerItem: nil)   // 原视频音频播放器
+    var recordPlayer    : AVPlayer = AVPlayer(playerItem: nil)   // 录音音频播放器
+    var bgmPlayer = BFMusicManager()   // 背景音乐音频播放器
 
     var hadPrepareToPlayRecord = false // 录音播放器准备
     var playView: GPUImageView? // 视频展示视图
@@ -297,8 +314,8 @@ public class BFRecordScreenController: BFBaseViewController {
                                          AVEncoderBitDepthHintKey: 16, // 位深
                                          AVEncoderAudioQualityKey: AVAudioQuality.medium.rawValue] // 音频质量
 
-    public var haveSpeakVolume: Float = 0.15
-    public var noSpeakVolume: Float = 0.30
+    //原素材音量
+    public var originalVolume: Float = 0.2
 
     // 录音管理器
     var recorderManager: BFVoiceRecordManager?
@@ -393,7 +410,7 @@ public class BFRecordScreenController: BFBaseViewController {
         btn.isEnabled = false
         btn.titleLabel?.font = UIFont.systemFont(ofSize: 12)
         btn.contentVerticalAlignment = UIControl.ContentVerticalAlignment.center
-        btn.addTarget(self, action: #selector(withdrawAction), for: .touchUpInside)
+        btn.addTarget(self, action: #selector(withdrawAction), for: .touchDown)
         return btn
     }()
 
@@ -412,6 +429,28 @@ public class BFRecordScreenController: BFBaseViewController {
         return toolV
 
     }()
+    
+    lazy var musicCutView: BFMusicCutView = {
+        let musicCutView = BFMusicCutView(frame: CGRect(x: 0, y: 0, width: cScreenWidth, height: cScreenHeigth))
+        musicCutView.isHidden = true
+        musicCutView.cutTimeHandle = {[weak self] iscancel, endTime, musicInfo in
+            guard let wself = self else { return }
+            
+            if(!iscancel){
+                wself.hadChoosed(music:musicInfo)
+            }else{
+                musicInfo?.isSelected = false
+                let btn = UIButton()
+                btn.tag = 1001
+                wself.choseMusicPanel.btnAction(btn: btn)
+            }
+        
+            //add by ak
+            wself.showOptBtn(isShow: true)
+        }
+        return musicCutView
+    }()
+
 
     // 头像  add by ak
     lazy var avatarView: BFRecordAvatarView = {
@@ -493,36 +532,96 @@ public class BFRecordScreenController: BFBaseViewController {
         let btn = UIButton(type: .custom)
         btn.setImage(imageInRecordScreenKit(by: "flip"), for: .normal)
         btn.titleLabel?.font = UIFont.systemFont(ofSize: 10)
-        btn.addTarget(self, action: #selector(cameraFlipAction), for: .touchUpInside)
+        btn.addTarget(self, action: #selector(cameraFlipAction), for: .touchDown)
         return btn
     }()
     
     // 添加音乐设置
-    lazy var addMusicBtn: UIButton = {
-        let btn = UIButton(type: .custom)
-        btn.backgroundColor = UIColor.black
+    lazy var addMusicBtn: BFCustomMusicBtn = {
+//        let btn = UIButton(type: .custom)
+//        btn.setImage(imageInRecordScreenKit(by: "addmusic"), for: .normal)
+//        btn.setTitle("选择音乐", for: .normal)
+//        btn.titleLabel?.font = UIFont.systemFont(ofSize: 13)
+//        btn.addTarget(self, action: #selector(addMusicAction), for: .touchDown)
+        
+        let btn = BFCustomMusicBtn(frame: CGRect.zero)
+        btn.backgroundColor = UIColor.init(white: 0, alpha: 0.6)
         btn.layer.cornerRadius = 16
-        btn.setImage(imageInRecordScreenKit(by: "addmusic"), for: .normal)
-        btn.setTitle("选择音乐", for: .normal)
-        btn.titleLabel?.font = UIFont.systemFont(ofSize: 13)
-        btn.addTarget(self, action: #selector(addMusicAction), for: .touchUpInside)
+        btn.clickAction = {[weak self] in
+            guard let wself = self else { return }
+            
+            wself.addMusicAction()
+        }
         return btn
     }()
     
     lazy var choseMusicPanel: BFChooseMusicView = {
-        let vv = BFChooseMusicView(frame: CGRect(x: 0, y: cScreenHeigth, width: cScreenWidth, height: cScreenHeigth - 200))
-        vv.clickBtnAction = { [weak self] type in
+        let vv = BFChooseMusicView(frame: CGRect(x: 0, y: cScreenHeigth, width: cScreenWidth, height: cScreenHeigth))
+        vv.clickBtnAction = { [weak self, weak vv] type in
             guard let wself = self else { return }
             
+            var nextAction : ((Bool) -> Void)?
             if type == BFChooseMusicViewClickType.sure {
+                wself.hadChoosed(music: vv?.chosedMusic)
+                wself.audioSettingView.musicSlider.value = Float(vv?.musicVolume ?? 0)
+
+                BFLog(1, message: "volume1 sure: \(wself.choseMusicPanel.musicVolume)")
+            }else if type == .cancle{
+                wself.choseMusicPanel.musicVolume = Int(wself.audioSettingView.musicSlider.value)
+                BFLog(1, message: "volume1 cancel: \(wself.choseMusicPanel.musicVolume)")
+            }else if type == .search{
+                nextAction = {[weak self] isFinished in
+                    guard let wself = self else { return }
+                    BFLog(1, message: "volume1 search: \(wself.choseMusicPanel.musicVolume)")
+
+                    if isFinished {
+                        let vc = BFMusicSearchController()
+                        vc.modalPresentationStyle = .fullScreen
+                        vc.musicVolume = wself.choseMusicPanel.musicVolume
+                        vc.choseAction = {[weak self] voiceModel in
+                            guard let wself = self else { return }
+                            wself.hadChoosed(music: voiceModel)
+                            wself.choseMusicPanel.searchModel = voiceModel
+                        }
+                        
+                        vc.cutActionCallback = {[weak self] voiceModel in
+                            guard let wself = self else { return }
+                            wself.showOptBtn(isShow: false)
+                            wself.showCutView(voiceModel)
+                        }
+                        wself.present(vc, animated: true, completion: nil)
+                    }
+                }
+            }
+            
+            let hiddAnimation = {[weak self] in
+                guard let wself = self else { return }
                 
-            }else {
+                wself.choseMusicPanel.frame = CGRect(x: 0, y: cScreenHeigth, width: cScreenWidth, height: cScreenHeigth)
                 
+                if(type != .search){
+                    wself.showOptBtn(isShow: true)
+                }
+                wself.cleanScreen?(false)
             }
-            UIView.animate(withDuration: 0.25) {
-                wself.choseMusicPanel.frame = CGRect(x: 0, y: cScreenHeigth, width: cScreenWidth, height: cScreenHeigth - 200)
+            
+            UIView.animate(withDuration: 0.25, animations: hiddAnimation, completion: nextAction)
+        }
+        
+        vv.cutActionCallback = {[weak self] voiceModel in
+            
+            UIView.animate(withDuration: 0.25) {[weak self] in
+                guard let wself = self else { return }
+                
+                wself.choseMusicPanel.frame = CGRect(x: 0, y: cScreenHeigth, width: cScreenWidth, height: cScreenHeigth)
+            
+            } completion: {[weak self] _ in
+                guard let wself = self else { return }
+                wself.showCutView(voiceModel)
             }
+
         }
+        
         return vv
     }()
 
@@ -574,24 +673,22 @@ public class BFRecordScreenController: BFBaseViewController {
     // 音量设置
     lazy var audioSettingView: BFAudioSettingView = {
         let audioSettingView = BFAudioSettingView(frame: CGRect(x: 0, y: 0, width: cScreenWidth, height: cScreenHeigth))
+        audioSettingView.originalMSlider.value = 20
+        audioSettingView.musicSlider.value = 20
         audioSettingView.isHidden = true
-        audioSettingView.haveSpeakSlider.valueIsInt = true
-        audioSettingView.noSpeakSlider.valueIsInt = true
-        audioSettingView.callBack = { [weak self] haveSpeak, noHaveSpeak in
+        audioSettingView.musicSlider.valueIsInt = true
+        audioSettingView.originalMSlider.valueIsInt = true
+        audioSettingView.callBack = { [weak self] originalVolume, musicVolume in
 //            BFLog(1, message: "haveSpeak is:\(haveSpeak),noHaveSpeak is:\(noHaveSpeak)")
             guard let wself = self else { return }
-
-            wself.haveSpeakVolume = haveSpeak / 100.0
-            wself.noSpeakVolume = noHaveSpeak / 100.0
-            if !wself.isNormalPlaying, !wself.isRecording {
-                if wself.deleteRecordBtn.isHidden {
-//                    wself.recordPlayer.volume = 0
-                    wself.assetPlayer.volume = wself.noSpeakVolume
-                } else {
-//                    wself.recordPlayer.volume = 1.0
-                    wself.assetPlayer.volume = wself.haveSpeakVolume
-                }
-            }
+            wself.showOptBtn(isShow: true)
+            wself.bgmModel?.volume = Int(musicVolume)
+            wself.originalVolume = originalVolume / 100.0
+            wself.choseMusicPanel.musicVolume = Int(musicVolume)
+            wself.assetPlayer.volume = wself.originalVolume
+//            if !wself.isNormalPlaying, !wself.isRecording {
+//
+//            }
         }
 
         return audioSettingView
@@ -679,12 +776,24 @@ public class BFRecordScreenController: BFBaseViewController {
         assetPlayer.pause()
         recordPlayer.pause()
     }
+    
+    ///  显示或隐藏功能 btn
+    /// - Parameter isShow: <#isShow description#>
+    func showOptBtn(isShow:Bool){
+        
+        soundSettingBtn.isHidden = !isShow
+        cameraFlipBtn.isHidden = !isShow
+        voiceSettingBtn.isHidden = !isShow
+        subtitleBtn.isHidden = !isShow
+        addMusicBtn.isHidden = !isShow
+        
+        cleanScreen?(!isShow)
+      
+      
+    }
 
     @objc func didBecomeActive() {
         BFLog(message: "进入活跃状态")
-        
-        loadingView.startGIF()
-        
     }
 
     @objc func willResignActive() {
@@ -733,11 +842,6 @@ public class BFRecordScreenController: BFBaseViewController {
     override public func viewDidLoad() {
         super.viewDidLoad()
         _ = disablePopGesture()
-
-        let doubleTapGes = UITapGestureRecognizer(target: self, action: #selector(doubleTapAction(tap:)))
-        doubleTapGes.numberOfTapsRequired = 2
-        doubleTapGes.numberOfTouchesRequired = 1
-        view.addGestureRecognizer(doubleTapGes)
         
         // 进入活跃状态
         PQNotification.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
@@ -772,10 +876,10 @@ public class BFRecordScreenController: BFBaseViewController {
                 
         view.addSubview(bottomeView)
 //        view.addSubview(subtitleLabel)
-//        view.addSubview(addMusicBtn)
-        view.addSubview(choseMusicPanel)
-        
         view.addSubview(playBtn)
+        
+        view.addSubview(choseMusicPanel)
+        view.addSubview(addMusicBtn)
 //        view.addSubview(avatarView)
 //        view.addSubview(openCameraBtn)
 //        view.addSubview(drawPinBtn)
@@ -785,10 +889,9 @@ public class BFRecordScreenController: BFBaseViewController {
             view.addSubview(voiceSettingBtn)
             voiceSettingBtn.addSubview(voiceIconView)
         }
-   
+
         view.addSubview(cameraFlipBtn)
         
-        
         view.addSubview(subtitleSettingView)
         view.addSubview(audioSettingView)
         //??? add key windows?
@@ -810,22 +913,25 @@ public class BFRecordScreenController: BFBaseViewController {
 
         // 字幕设置回调
         subtitleSettingView.subtitleSettingCallBack = { [weak self] subtitileModel in
-
+            
             if let wself = self{
                 
-                //1,设置字幕样式
-                wself.setSubtitleStyle(settingModel: subtitileModel.setting)
-                
-                //2,显示设置文字,不显示设置为空
-                if(subtitileModel.setting.subtitleIsShow){
-                    wself.updateSubtitle(time: wself.currentAssetProgress)
-                }else{
-                    wself.subtitleLabel.text = ""
-                }
-        
-                // mdf by ak 这里是设置字幕开关回调
-                if wself.subTitleBtnClickHandle != nil {
-                    wself.subTitleBtnClickHandle!(subtitileModel.setting.subtitleIsShow)
+                wself.showOptBtn(isShow: true)
+                if(subtitileModel != nil){
+                    //1,设置字幕样式
+                    wself.setSubtitleStyle(settingModel: subtitileModel!.setting)
+                    
+                    //2,显示设置文字,不显示设置为空
+                    if(subtitileModel!.setting.subtitleIsShow){
+                        wself.updateSubtitle(time: wself.currentAssetProgress)
+                    }else{
+                        wself.subtitleLabel.text = ""
+                    }
+            
+                    // mdf by ak 这里是设置字幕开关回调
+                    if wself.subTitleBtnClickHandle != nil {
+                        wself.subTitleBtnClickHandle!(subtitileModel!.setting.subtitleIsShow)
+                    }
                 }
             }
          
@@ -902,7 +1008,7 @@ public class BFRecordScreenController: BFBaseViewController {
         }
         voiceSettingView.voiceSettingCallBack = {[weak self] actionType ,selectVoice ,isSame in
             guard let wself = self else { return }
-          
+            wself.showOptBtn(isShow: true)
             if actionType == .VoiceSettingActionConfirm{//要生成真实音频文件
                 BFLog(2, message: "确认选择发音人操作")
                 if(wself.voiceBtnClickHandle != nil && selectVoice != nil){
@@ -928,7 +1034,7 @@ public class BFRecordScreenController: BFBaseViewController {
                     //显示 UI
                     wself.voiceIconView.setNetImage(url: "\(wself.mSelectVoiced?.avatarUrl ?? "")")
                     if(!wself.voiceChangeStickerFinish(itemIndex: wself.currItemModelIndex)){
-
+                        wself.loadingView.isDownloadMusic = false
                         wself.loadingView.loadShow()
                     }
                     //静默转换语音
@@ -970,6 +1076,8 @@ public class BFRecordScreenController: BFBaseViewController {
             }
             
         }
+        
+        view.addSubview(musicCutView);
          
         UIApplication.shared.keyWindow?.addSubview(loadingView)
 }
@@ -980,12 +1088,11 @@ public class BFRecordScreenController: BFBaseViewController {
             make.height.equalTo(adapterWidth(width: 180))
         }
         
-//        addMusicBtn.snp.makeConstraints { make in
-//            make.centerX.equalToSuperview()
-//            make.top.equalTo(soundSettingBtn).offset(-8)
-//            make.width.equalTo(100)
-//            make.height.equalTo(32)
-//        }
+        addMusicBtn.snp.makeConstraints { make in
+            make.centerX.equalToSuperview()
+            make.top.equalTo(soundSettingBtn).offset(-12)
+            make.height.equalTo(32)
+        }
 
         progreddL.snp.makeConstraints { make in
             make.width.equalTo(100)
@@ -1385,6 +1492,23 @@ public class BFRecordScreenController: BFBaseViewController {
     //    }
 
     // MARK: - 按钮事件响应
+    
+    func showCutView(_ model:PQVoiceModel) {
+
+        DispatchQueue.main.async { [weak self] in
+            guard let wself = self else { return }
+            wself.musicCutView.bgmData = model
+            wself.musicCutView.currentProgressLab.text = model.startCMTime.seconds.formatDurationToHMS()
+            wself.musicCutView.resetWave()
+            wself.musicCutView.show()
+            
+            wself.musicCutView.frame = CGRect(x: 0, y: cScreenHeigth + 220, width: cScreenWidth, height: cScreenHeigth)
+            UIView.animate(withDuration: 0.1) {
+                // 显示裁剪视图
+                wself.musicCutView.frame = CGRect(x: 0, y:0, width: cScreenWidth, height: cScreenHeigth)
+            }
+        }
+    }
 
     override public func backBtnClick() {
         //add by ak 删除变音文件
@@ -1444,20 +1568,21 @@ public class BFRecordScreenController: BFBaseViewController {
     @objc func subTitleClick() {
         BFLog(message: "subTitle Click ")
         subtitleSettingView.isHidden = !subtitleSettingView.isHidden
-
+        showOptBtn(isShow: false)
         updateSubtitle(time: currentAssetProgress)
     }
 
     // 声音设置
     @objc func soundSetting() {
         BFLog(message: "设置声音")
-
+        showOptBtn(isShow: false)
         audioSettingView.isHidden = !audioSettingView.isHidden
     }
     
     //变声设置
     @objc func voiceSetting() {
         BFLog(message: "设置变音")
+        showOptBtn(isShow: false)
         if(mSelectVoiced != nil){
             voiceSettingView.selectVoice = mSelectVoiced
             voiceSettingView.flushSelectVoiceStatus(voiceStatue: .isSelected)
@@ -1468,17 +1593,6 @@ public class BFRecordScreenController: BFBaseViewController {
       
     }
     
-    @objc func doubleTapAction(tap:UITapGestureRecognizer) {
-        if !(bottomeView.bounds.contains(tap.location(in: bottomeView))
-             || navHeadImageView!.bounds.contains(tap.location(in: navHeadImageView!))
-             || !voiceSettingView.isHidden
-             || !subtitleSettingView.isHidden
-             || !audioSettingView.isHidden
-             || recordBtn.isHidden){
-            cameraFlipAction()
-        }
-    }
-    
     @objc func cameraFlipAction() {
         if currMediaType == .Camera && !recordBtn.isHidden  {
             rscmanager.cameraFlip()
@@ -1486,24 +1600,20 @@ public class BFRecordScreenController: BFBaseViewController {
     }
     
     @objc func addMusicAction() {
-        cShowHUB(superView: nil, msg: "选择音乐")
         
         soundSettingBtn.isHidden = true
         cameraFlipBtn.isHidden = true
         voiceSettingBtn.isHidden = true
         subtitleBtn.isHidden = true
         addMusicBtn.isHidden = true
-        
+        cleanScreen?(true)
         UIView.animate(withDuration: 0.25) {[weak self] in
             guard let wself = self else { return }
-            wself.soundSettingBtn.isHidden = false
-            wself.cameraFlipBtn.isHidden = false
-            wself.voiceSettingBtn.isHidden = false
-            wself.subtitleBtn.isHidden = false
-            wself.addMusicBtn.isHidden = false
             
-            wself.choseMusicPanel.frame = CGRect(x: 0, y: 210, width: cScreenWidth, height: cScreenHeigth - 200)
+            wself.choseMusicPanel.frame = CGRect(x: 0, y: 0, width: cScreenWidth, height: cScreenHeigth)
         }
+        choseMusicPanel.refreshCurrSelectModel(mod: bgmModel)
+        choseMusicPanel.musicVolume = Int(audioSettingView.musicSlider.value)
     }
 
     /// 删除指定段落的所有字幕 数据
@@ -1647,6 +1757,7 @@ public class BFRecordScreenController: BFBaseViewController {
 
     @objc func endRecord() {
         // 存储录音
+        
         if !isRecording {
             return
         }
@@ -1928,7 +2039,7 @@ public class BFRecordScreenController: BFBaseViewController {
             recordBtn.isHidden = isEndPlay ? false : su
         }
         
-        deleteRecordBtn.isHidden = isEndPlay ? true : !(recordBtn.isHidden)
+        deleteRecordBtn.isHidden = isEndPlay ? true : ((isRecording || isNormalPlaying) ? true : !(recordBtn.isHidden))
     }
     
     func recordFinished(_ isFinished:Bool) {
@@ -2053,33 +2164,26 @@ public class BFRecordScreenController: BFBaseViewController {
     }
 
     // MARK: - 音视频处理
-
+    
+    /// 查找录音播放
+    /// - Parameters:
+    ///   - currentT: 当前播放进度时刻
+    ///   - periodicTimeObserver: 每次刷新要执行的block
+    ///   - didPlayToEndTime: 录音播放结束后的block
+    ///   - _: 播放失败的block
     func playRecord(at currentT: CMTime, periodicTimeObserver: @escaping (_ time: CMTime, _ currentItem: AVPlayerItem) -> Void, didPlayToEndTime: @escaping (_ recordedInfo: (Int, PQVoiceModel)?, _ currentItem: AVPlayerItem?) -> Void, playFailed _: @escaping (_ recordedInfo: (Int, PQVoiceModel)?, _ currentItem: AVPlayerItem?) -> Void) {
-//        if currentPlayRecordIndex >= 0 {
-//            if assetPlayer.volume != haveSpeakVolume{
-//                assetPlayer.volume = haveSpeakVolume
-//            }
-//        }else{
-//            if assetPlayer.volume != noSpeakVolume {
-//                assetPlayer.volume = noSpeakVolume
-//            }
-//        }
-        
+ 
         //add by ak 使用的音频数据
         let  useVoiceStickers = itemModels[currItemModelIndex].getUsedVoices()
         
         //音量设置判断使用原录音数据
-        if itemModels[currItemModelIndex].voiceStickers.first(where: { m in
-            CMTimeCompare(m.startCMTime, currentT) <= 0 && CMTimeCompare(currentT, m.endCMTime) <= 0
-        }) != nil {
-            if assetPlayer.volume != haveSpeakVolume {
-                assetPlayer.volume = haveSpeakVolume
-            }
-        } else {
-            if assetPlayer.volume != noSpeakVolume {
-                assetPlayer.volume = noSpeakVolume
-            }
-        }
+//        if itemModels[currItemModelIndex].voiceStickers.first(where: { m in
+//            CMTimeCompare(m.startCMTime, currentT) <= 0 && CMTimeCompare(currentT, m.endCMTime) <= 0
+//        }) != nil {
+//            if assetPlayer.volume != originalVolume {
+//                assetPlayer.volume = originalVolume
+//            }
+//        }
 //        BFLog(1, message: "volume:\(assetPlayer.volume ?? -1)")
 
         if currentPlayRecordIndex == -3 { // 刚录音完,不需要播放
@@ -2100,7 +2204,6 @@ public class BFRecordScreenController: BFBaseViewController {
             return
         }
         BFLog(1, message: "当前时间:\(CMTimeGetSeconds(currentT)), 找到的音频:\(recordedAudio.startCMTime.seconds) ~ \(recordedAudio.endCMTime.seconds), \(recordedAudio.wavFilePath ?? "")")
-
         // 创建播放器
         if (recordPlayer.currentItem?.asset as? AVURLAsset)?.url.lastPathComponent != (recordedAudio.wavFilePath as NSString?)?.lastPathComponent {
             let newItem = AVPlayerItem(url: URL(fileURLWithPath: recordedAudio.wavFilePath))
@@ -2126,20 +2229,20 @@ public class BFRecordScreenController: BFBaseViewController {
                 }
                 wself.hadPrepareToPlayRecord = false
                 wself.currentPlayRecordIndex = -1
-//                wself.recordPlayer.volume = 0
-//                wself.assetPlayer.volume = wself.noSpeakVolume
+                
+                wself.assetPlayer.volume = wself.originalVolume
+                
                 BFLog(3, message: "播放结束")
                 didPlayToEndTime((shouldPlayRecordIndex, recordedAudio), newItem)
             }
 //            recordPlayer.removeTimeObserver(recordPlayerTimeObserver as Any)
 //            recordPlayerTimeObserver?.invalidate()
             //MARK: 声音播放器播放进度回调
-            recordPlayerTimeObserver = recordPlayer.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 1000), queue: DispatchQueue.global()) { [weak self, weak recordPlayer] time in
-                guard let wself = self, let rPlay = recordPlayer else {
-                    BFLog(3, message: "wself为空")
-                    return
+            recordPlayerTimeObserver = recordPlayer.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 1000), queue: DispatchQueue.global()) { [weak self] time in
+                guard let wself = self else { return }
+                if time.seconds > 0 {
+                    wself.assetPlayer.volume = wself.originalVolume * 0.6
                 }
-//                BFLog(1, message: "当前播放--- \(time.seconds), \(rPlay.currentItem?.duration.seconds ?? 0)")
                 if CMTimeGetSeconds(wself.currenStartPlayTime) <= 0 {
                     BFLog(message: "重新更新开始播放进度\(#function)-\(wself.currenStartPlayTime.seconds)")
                     wself.currenStartPlayTime = time
@@ -2147,14 +2250,11 @@ public class BFRecordScreenController: BFBaseViewController {
                 let progressTime = CMTime(seconds: CMTimeGetSeconds(time) - CMTimeGetSeconds(wself.currenStartPlayTime), preferredTimescale: 1000)
                 BFLog(message: "progressTime进度\(#function)-\(progressTime.seconds)")
                 periodicTimeObserver(progressTime, newItem)
-//                if (rPlay.currentItem?.currentTime().seconds ?? 0) > (rPlay.currentItem?.duration.seconds ?? 0) - 0.1 {
-//                    didPlayToEndTime((shouldPlayRecordIndex, recordedAudio), newItem)
-//                }
             } as? NSKeyValueObservation
         }
         if currMediaType == .Video {
             videoMaterialRecordPlay(at: currentT, shouldPlayRecordIndex: shouldPlayRecordIndex, recordedAudio: recordedAudio)
-        } else {
+        } else if currMediaType == .Image {
             imageMaterialRecordPlay(at: currentT, shouldPlayRecordIndex: shouldPlayRecordIndex, recordedAudio: recordedAudio)
         }
         BFLog(1, message: "应当播放:\(shouldPlayRecordIndex), 当前播放:\(currentPlayRecordIndex)")
@@ -2198,8 +2298,6 @@ public class BFRecordScreenController: BFBaseViewController {
                             
                             if finished, wself.isNormalPlaying {
                                 wself.recordPlayer.play()
-//                                wself.recordPlayer.volume = 1
-//                                wself.assetPlayer.volume = wself.haveSpeakVolume ?? 0
                                 BFLog(1, message: "录音开始播放 playing, \(second), \(CMTimeGetSeconds(wself.recordPlayer.currentItem?.duration ?? .zero)), \(wself.recordPlayer.currentItem?.currentTime().seconds ?? 0)")
                             }
                         })
@@ -2253,7 +2351,7 @@ public class BFRecordScreenController: BFBaseViewController {
         BFLog(1, message: "开始播放 \(currentAssetProgress.seconds)")
         recorderManager?.voiceModel = nil
         events.append(WithDrawModel(type: 1, timestamp: currentAssetProgress, recordItem: rscurrentManager.recordItem!))
-
+ 
         itemModels[currItemModelIndex].titleStickers.sort { m1, m2 in
             CMTimeCompare(m1.timelineIn, m2.timelineIn) < 0
         }
@@ -2280,16 +2378,21 @@ public class BFRecordScreenController: BFBaseViewController {
 
             rscurrentManager.currentAssetProgress = .zero
         }
+
+        bgmPlayer.player.currentItem?.reversePlaybackEndTime = CMTime(seconds: bgmModel?.currentTime ?? 0.0, preferredTimescale: 1000)
+        bgmPlayer.player.volume = Float(choseMusicPanel.musicVolume) / 100.0
+        assetPlayer.volume = originalVolume
         
-        rscurrentManager.play()
         
-        subtitleBtn.isHidden = true
-        voiceSettingBtn.isHidden = true
-        soundSettingBtn.isHidden = true
-        withDrawBtn.isHidden = true
-        recordBtn.isHidden = true
-        deleteRecordBtn.isHidden = true
+        if currItemModelIndex == 0 && currentAssetProgress == .zero{
+            bgmPlayer.player.seek(to: bgmPlayer.player.currentItem?.reversePlaybackEndTime ?? .zero ) {[weak self] _ in
+                self?.bgmPlayer.play()
+            }
+        }else{
+            bgmPlayer.play()
+        }
         
+        rscurrentManager.play()
     }
 
     public func pause() {
@@ -2298,6 +2401,9 @@ public class BFRecordScreenController: BFBaseViewController {
         
         assetPlayer.pause()
         recordPlayer.pause()
+        bgmPlayer.pause()
+        rscurrentManager.pause()
+        
         recordStartPlayTime = CMTime.zero
         pauseTime = currentAssetProgress.seconds
         currentPlayRecordIndex = -1
@@ -2305,7 +2411,9 @@ public class BFRecordScreenController: BFBaseViewController {
 
         // 暂停状态
         updatePlayBtnStatus()
-        rscurrentManager.pause()
+        updateRecordBtnStatus()
+        
+        
     }
 
     public func fetchMaterial(_ phAsset:[PHAsset]) {
@@ -2425,10 +2533,10 @@ public class BFRecordScreenController: BFBaseViewController {
             if !wself.isRecording, wself.currItemModelIndex < (wself.itemModels.count - 1) {
             } else {} //
 
-            wself.isNormalPlaying = false
             wself.isEndPlay = true
-            wself.updatePlayBtnStatus()
-
+            wself.pause()
+            
+            //add by ak 导入视频素材播放结束后主动调用一次基类的pause 停止背景音乐
 
             if wself.isRecording {
                 wself.endRecord()
@@ -2452,7 +2560,7 @@ public class BFRecordScreenController: BFBaseViewController {
                 DispatchQueue.main.async { [weak self] in
                     guard let wself = self else { return }
                     
-                    BFLog(message: "更新录音进度\(#function)-\(wself.currentAssetProgress.seconds )")
+                    BFLog(message: "更新录音进度\(#function) - \(wself.currentAssetProgress.seconds )")
                     wself.progreddL.text = String(format: "%@", CMTimeGetSeconds(time).formatDurationToHMS())
                     let su = !wself.isDragingProgressSlder || wself.isRecording || wself.isNormalPlaying
                     if su { // 不拖动,正常播放和录音时更新进度条
@@ -2528,7 +2636,7 @@ public class BFRecordScreenController: BFBaseViewController {
                 DispatchQueue.main.async { [weak self] in
                     guard let wself = self else { return }
                     
-                    BFLog(message: "更新录音进度\(#function)- \(wself.currentAssetProgress.seconds)")
+                    BFLog(message: "更新录音进度\(#function) - \(wself.currentAssetProgress.seconds)")
                     wself.progreddL.text = String(format: "%@", CMTimeGetSeconds(wself.currentAssetProgress).formatDurationToHMS())
                 }
                 assetPlayer.seek(to: currentAssetProgress, toleranceBefore: CMTime(value: 1, timescale: 1_000_000), toleranceAfter: CMTime(value: 1, timescale: 1_000_000)) { _ in
@@ -2591,6 +2699,52 @@ public class BFRecordScreenController: BFBaseViewController {
             imageRecordProgress(isRecord: true, progress: progress)
         }
     }
+    
+    func hadChoosed(music:PQVoiceModel?) {
+        bgmModel = music
+        bgmPlayer.replaceCurrentItem(musicPath: bgmModel?.musicPath)
+
+        if let model = music {
+            
+            BFEventTrackAdaptor.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_musicChangerButton, pageSource: .sp_speekPage,extParams: ["musicID":bgmModel?.musicId ?? ""],commonParams: commonParams())
+     
+            if let title = model.musicName {
+//                addMusicBtn.setTitle(title, for: .normal)
+//                addMusicBtn.kf.setImage(with: URL(string: model.avatarUrl), for: .normal)
+                
+                addMusicBtn.config(title: title, imgUrl: URL(string: model.avatarUrl))
+            }else{
+//                addMusicBtn.setTitle("选择音乐", for: .normal)
+//                addMusicBtn.setImage(imageInRecordScreenKit(by: "addmusic"), for: .normal)
+                addMusicBtn.config()
+            }
+            // 设置music为背景音乐
+            hadDowonloadMusic(model: model)
+        }else {
+            addMusicBtn.config()
+            BFLog(1, message: "无效音乐")
+        }
+    }
+    
+    func hadDowonloadMusic(model: PQVoiceModel) {
+        loadingView.isDownloadMusic = true
+        loadingView.loadShow()
+        if let path = model.musicPath {
+            PQDownloadManager.downLoadFile(url: path) { [weak self ]musicUrl, err in
+                guard let wself = self else { return }
+                
+                if err == nil, musicUrl != nil {
+                    model.wavFilePath = musicUrl
+                    wself.loadingView.removeLoading()
+                }else{
+                    cShowHUB(superView: wself.view, msg: "音乐下载失败,请重新尝试")
+                    wself.bgmModel = nil
+//                    wself.addMusicBtn.setTitle("选择音乐", for: .normal)
+                    wself.addMusicBtn.config()
+                }
+            }
+        }
+    }
 }
 
 extension BFRecordScreenController: GPUImageMovieDelegate {
@@ -2632,6 +2786,11 @@ extension BFRecordScreenController: UICollectionViewDelegate, UICollectionViewDa
             cell = BFVideoCoverViewCell.gpuVideoViewCell(collectionView: collectionView, indexPath: indexPath)
         case .Camera:
             cell = BFCameraCoverViewCell.gpuCamraViewCell(collectionView: collectionView, indexPath: indexPath)
+            (cell as? BFCameraCoverViewCell)?.cameraFlip = {[weak self] in
+                guard let wself = self else { return }
+                
+                wself.cameraFlipAction()
+            }
         default:
             break
         }
@@ -2727,10 +2886,11 @@ extension BFRecordScreenController: UICollectionViewDelegate, UICollectionViewDa
 
     func endScrollItem(page: Int) {
         BFLog(1, message: "endScrollItem")
+        rscurrentManager.playBGMPlayer.pause()
         // 切换素材时先把录制状态切为不可用,延迟可点,避免在缩略图未加载出来时即刻录制
+        pause()
         currItemModelIndex = page
         // 暂停
-        pause()
         //add by ak  BFRecordScreenCameraManager 中的播放器也要暂停那里的进度回调也会刷新 UI 导致出现闪烁 BUG
         rscmanager.pause()
  
@@ -3074,6 +3234,7 @@ extension BFRecordScreenController: BFTTSManagerDelegte{
         
         if(isShowLoadView && !isFinish){
             loadingView.loadShow()
+            loadingView.isDownloadMusic = false
         }
      
         return isFinish

+ 3 - 2
BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenImageManager.swift

@@ -26,6 +26,7 @@ class BFRecordScreenImageManager : BFRecordScreenBaseManager{
     }
     
     override func play() {
+        super.play()
         // 找到当前要播放的音频
         timerr?.invalidate()
         
@@ -59,10 +60,10 @@ class BFRecordScreenImageManager : BFRecordScreenBaseManager{
     }
     
     override func pause() {
+        super.pause()
         timerr?.invalidate()
         timerr = nil
-        // add by ak 暂停时 isPlaying 设置为 false
-        isPlaying = false
+     
     }
     
     override func deleteRecord(at: CMTime) {

+ 5 - 1
BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenVideoManager.swift

@@ -57,12 +57,16 @@ class BFRecordScreenVideoManager : BFRecordScreenBaseManager{
         super.startRecord()
     }
     
+    override func pause() {
+        super.pause()
+    }
+      
     override func play() {
+        super.play()
         if !movieIsProcessing {
             movie?.startProcessing()
             movieIsProcessing = true
         }
-        assetPlayer?.volume = 0
         assetPlayer?.play()
     }
     

+ 71 - 70
BFRecordScreenKit/Classes/RecordScreen/View/BFAudioSettingView.swift

@@ -10,7 +10,8 @@ import BFMediaKit
 import BFUIKit
 import Foundation
 
-typealias BFAudioSettingViewCallBack = (_ haveSpeak: Float, _ noHaveSpeak: Float) -> Void
+// 1,原声音量  2,音乐音量
+typealias BFAudioSettingViewCallBack = (_ originalVolume: Float, _ musicVolume: Float) -> Void
 
 class BFAudioSettingView: UIView {
     var callBack: BFAudioSettingViewCallBack?
@@ -18,75 +19,75 @@ class BFAudioSettingView: UIView {
     // 操作板背景
     let backView = UIView()
     // 标题
-    public lazy var haveSpeakLab: UILabel = {
-        let haveSpeakLab = UILabel()
-        haveSpeakLab.font = UIFont.boldSystemFont(ofSize: 16)
-        haveSpeakLab.textAlignment = .left
-        haveSpeakLab.text = "sound_record_ture".BFLocale
-        haveSpeakLab.textColor = .white
-        haveSpeakLab.backgroundColor = .clear
-        return haveSpeakLab
+    public lazy var originalMLab: UILabel = {
+        let originalMLab = UILabel()
+        originalMLab.font = UIFont.boldSystemFont(ofSize: 16)
+        originalMLab.textAlignment = .left
+        originalMLab.text = "原声"
+        originalMLab.textColor = .white
+        originalMLab.backgroundColor = .clear
+        return originalMLab
     }()
 
     // 有录音
-    lazy var haveSpeakSlider: BFUISlider = {
-        let haveSpeakSlider = BFUISlider()
+    lazy var originalMSlider: BFUISlider = {
+        let originalMSlider = BFUISlider()
+        originalMSlider.isMovePoint = false
         let thbImage = UIImage.moduleImage(named: BFConfig.shared.silderPinUsedImageName, moduleName: "BFFramework", isAssets: false)
-        haveSpeakSlider.setMinimumTrackImage(thbImage, for: .normal)
-        haveSpeakSlider.setMaximumTrackImage(thbImage, for: .normal)
-        haveSpeakSlider.setThumbImage(thbImage, for: .highlighted)
-        haveSpeakSlider.setThumbImage(thbImage, for: .normal)
-        haveSpeakSlider.maximumTrackTintColor = UIColor.hexColor(hexadecimal: "#E6E8E8")
-        haveSpeakSlider.minimumTrackTintColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
-        haveSpeakSlider.tag = 10
-        haveSpeakSlider.unit = "%"
+        originalMSlider.setMinimumTrackImage(thbImage, for: .normal)
+        originalMSlider.setMaximumTrackImage(thbImage, for: .normal)
+        originalMSlider.setThumbImage(thbImage, for: .highlighted)
+        originalMSlider.setThumbImage(thbImage, for: .normal)
+        originalMSlider.maximumTrackTintColor = UIColor.hexColor(hexadecimal: "#E6E8E8")
+        originalMSlider.minimumTrackTintColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
+        originalMSlider.tag = 10
+        originalMSlider.unit = "%"
 //        speechSlidView.addTarget(self, action: #selector(sliderTouchEnded(sender:)), for: .touchUpInside)
-        haveSpeakSlider.maximumValue = 100
-        haveSpeakSlider.minimumValue = 0
-        haveSpeakSlider.isMovePoint = false
-        haveSpeakSlider.valueTextColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
-        haveSpeakSlider.value = 15
-        haveSpeakSlider.valueChangedHandle = { [weak self] sender in
+        originalMSlider.maximumValue = 100
+        originalMSlider.minimumValue = 0
+        originalMSlider.valueIsInt = true
+        originalMSlider.valueTextColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
+        originalMSlider.valueChangedHandle = { [weak self] sender in
             if self?.callBack != nil {
-                self?.callBack!(sender.value, self?.noSpeakSlider.value ?? 0)
+                self?.callBack!(sender.value, self?.musicSlider.value ?? 0)
             }
         }
-        return haveSpeakSlider
+        return originalMSlider
     }()
 
     // 标题
-    public lazy var noSpeakLab: UILabel = {
-        let noSpeakLab = UILabel()
-        noSpeakLab.font = UIFont.boldSystemFont(ofSize: 16)
-        noSpeakLab.textAlignment = .left
-        noSpeakLab.text = "sound_record_false".BFLocale
-        noSpeakLab.textColor = .white
-        return noSpeakLab
+    public lazy var musicLab: UILabel = {
+        let musicLab = UILabel()
+        musicLab.font = UIFont.boldSystemFont(ofSize: 16)
+        musicLab.textAlignment = .left
+        musicLab.text = "音乐"
+        musicLab.textColor = .white
+        return musicLab
     }()
 
-    lazy var noSpeakSlider: BFUISlider = {
-        let noSpeakSlider = BFUISlider()
+    lazy var musicSlider: BFUISlider = {
+        let musicSlider = BFUISlider()
         let thbImage = UIImage.moduleImage(named: BFConfig.shared.silderPinUsedImageName, moduleName: "BFFramework", isAssets: false)
-        noSpeakSlider.setMinimumTrackImage(thbImage, for: .normal)
-        noSpeakSlider.setMaximumTrackImage(thbImage, for: .normal)
-        noSpeakSlider.setThumbImage(thbImage, for: .highlighted)
-        noSpeakSlider.setThumbImage(thbImage, for: .normal)
-        noSpeakSlider.maximumTrackTintColor = UIColor.hexColor(hexadecimal: "#E6E8E8")
-        noSpeakSlider.minimumTrackTintColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
-        noSpeakSlider.tag = 20
-        noSpeakSlider.isMovePoint = false
+        musicSlider.setMinimumTrackImage(thbImage, for: .normal)
+        musicSlider.setMaximumTrackImage(thbImage, for: .normal)
+        musicSlider.setThumbImage(thbImage, for: .highlighted)
+        musicSlider.setThumbImage(thbImage, for: .normal)
+        musicSlider.maximumTrackTintColor = UIColor.hexColor(hexadecimal: "#E6E8E8")
+        musicSlider.minimumTrackTintColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
+        musicSlider.tag = 20
+        musicSlider.isMovePoint = false
 //        speechSlidView.addTarget(self, action: #selector(sliderTouchEnded(sender:)), for: .touchUpInside)
-        noSpeakSlider.maximumValue = 100
-        noSpeakSlider.minimumValue = 0
-        noSpeakSlider.unit = "%"
-        noSpeakSlider.valueTextColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
-        noSpeakSlider.value = 30
-        noSpeakSlider.valueChangedHandle = { [weak self] sender in
+        musicSlider.maximumValue = 100
+        musicSlider.minimumValue = 0
+        musicSlider.unit = "%"
+        musicSlider.valueIsInt = true
+        musicSlider.valueTextColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
+        musicSlider.valueChangedHandle = { [weak self] sender in
             if self?.callBack != nil {
-                self?.callBack!(self?.haveSpeakSlider.value ?? 0, sender.value)
+                self?.callBack!(self?.originalMSlider.value ?? 0, sender.value)
             }
         }
-        return noSpeakSlider
+        return musicSlider
     }()
 
     override init(frame: CGRect) {
@@ -104,31 +105,31 @@ class BFAudioSettingView: UIView {
             make.height.equalTo(220)
         }
 
-        backView.addSubview(haveSpeakSlider)
-        backView.addSubview(noSpeakSlider)
-        backView.addSubview(haveSpeakLab)
-        backView.addSubview(noSpeakLab)
+        backView.addSubview(originalMSlider)
+        backView.addSubview(musicSlider)
+        backView.addSubview(originalMLab)
+        backView.addSubview(musicLab)
 
-        haveSpeakLab.snp.makeConstraints { make in
+        originalMLab.snp.makeConstraints { make in
             make.left.equalToSuperview().offset(16)
-            make.right.equalToSuperview().offset(-16)
-            make.top.equalToSuperview().offset(25)
+            make.top.equalToSuperview().offset(60)
+            make.width.equalTo(40)
+            make.height.equalTo(20)
         }
-        haveSpeakSlider.snp.makeConstraints { make in
-            make.left.equalToSuperview().offset(16)
+        originalMSlider.snp.makeConstraints { make in
+            make.left.equalTo(originalMLab.snp.right).offset(10)
             make.right.equalToSuperview().offset(-16)
-            make.top.equalTo(haveSpeakLab).offset(24)
+            make.top.height.equalTo(originalMLab)
         }
 
-        noSpeakLab.snp.makeConstraints { make in
-            make.left.equalToSuperview().offset(16)
-            make.right.equalToSuperview().offset(-16)
-            make.top.equalTo(haveSpeakSlider).offset(42)
+        musicLab.snp.makeConstraints { make in
+            make.left.width.height.equalTo(originalMLab)
+            make.top.equalTo(originalMSlider).offset(70)
         }
-        noSpeakSlider.snp.makeConstraints { make in
-            make.left.equalToSuperview().offset(16)
+        musicSlider.snp.makeConstraints { make in
+            make.left.equalTo(musicLab.snp.right).offset(10)
             make.right.equalToSuperview().offset(-16)
-            make.top.equalTo(noSpeakLab).offset(24)
+            make.top.height.equalTo(musicLab)
         }
     }
 
@@ -145,7 +146,7 @@ class BFAudioSettingView: UIView {
         if !backView.bounds.contains(tap.location(in: backView)) {
             isHidden = true
             if callBack != nil {
-                callBack!(haveSpeakSlider.value, noSpeakSlider.value)
+                callBack!(originalMSlider.value, musicSlider.value)
             }
         }
     }

+ 522 - 10
BFRecordScreenKit/Classes/RecordScreen/View/BFChooseMusicView.swift

@@ -6,24 +6,175 @@
 //
 
 import Foundation
+import UIKit
+import BFUIKit
+import BFMediaKit
+import BFCommonKit
+import BFNetRequestKit
+import Alamofire
+import MJRefresh
+
 
 enum BFChooseMusicViewClickType {
-    case sure, cancle
+    case sure, cancle, search
+}
+
+struct MusicCategoryModel {
+    var id : Int64
+    var name: String?
+    var page = 1
+    mutating func changePage() {
+        page += 1
+    }
 }
 
+
 class BFChooseMusicView: UIView {
     
     var clickBtnAction: ((BFChooseMusicViewClickType) -> Void)?
+    var cutActionCallback: ((PQVoiceModel) -> Void)?
+
+    // 各类别音乐列表,以类别id为key
+    var musicDic = [Int64 : [PQVoiceModel]]()
+    
+    var playManager = BFMusicManager()
+    
+    var searchModel : PQVoiceModel?{
+        willSet{
+            if searchModel != nil {
+                if let catogory = categories.first(where: { mod in
+                    mod.name == "热门"
+                }){
+                    if let _ = musicDic[catogory.id] {
+                        // 找到热门的数组
+                        musicDic[catogory.id]?.removeFirst()
+                    }
+                }
+            }
+        }
+        didSet {
+            if let searchModel = searchModel {
+                if let catogory = categories.first(where: { mod in
+                    mod.name == "热门"
+                }){
+                    if let list = musicDic[catogory.id] {
+                        var arr = [PQVoiceModel]()
+                        arr.append(searchModel)
+                        if list.first?.musicId != searchModel.musicId {
+                            arr.append(contentsOf: list)
+                            musicDic[catogory.id] = arr
+                            searchModel.isSelected = true
+                            chosedMusic?.isSelected = false
+                            chosedMusic = searchModel
+                            
+                            musicTb.reloadData()
+                        }
+                    }
+                }
+            }
+        }
+    }
+    // 选择的音乐类别
+    var categorySelectIndex : Int = 1
+    
+    var currPage : Int64 = 1
+    // 音乐类别
+    var categories = [MusicCategoryModel]()
+    
+    var musicVolume = 0 {
+        didSet{
+            slidV.value = Float(musicVolume) / 100.0
+            uiForChangeVolume()
+        }
+    }
+    
+    // 选中的音乐
+    var chosedMusic : PQVoiceModel?
+    
+    var chosedIndexPath : IndexPath?
+    
+    var chosedCellStatu : BFMuicInfoCellState = .pause
+    
+    lazy var categoryView : UICollectionView = {
+        let layout = BFCollectionViewFlowLayout()
+        layout.dele = self
+        layout.scrollDirection = .horizontal
+        layout.minimumLineSpacing = 25
+//        layout.sectionInset = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
+        layout.minimumInteritemSpacing = 2
+        let vv = UICollectionView(frame: CGRect(x: 60, y: 0, width: cScreenWidth - 60, height: 40), collectionViewLayout: layout)
+        vv.isPagingEnabled = false
+        vv.showsHorizontalScrollIndicator = false
+        vv.showsVerticalScrollIndicator = false
+        vv.register(BFMusicCategoryCell.self, forCellWithReuseIdentifier: "BFMusicCategoryCell")
+        vv.backgroundColor = UIColor.clear
+
+        vv.delegate = self
+        vv.dataSource = self
+        if #available(iOS 11.0, *) {
+            vv.contentInsetAdjustmentBehavior = .never
+        } else {
+            // Fallback on earlier versions
+        }
+        vv.backgroundColor = .clear
+
+        return vv
+    }()
+    
+    lazy var progressL : UILabel = {
+        let l = UILabel()
+        l.textColor = UIColor.hexColor(hexadecimal: "#9d9d9d")
+        l.font = UIFont.systemFont(ofSize: 13, weight: .regular)
+        l.textAlignment = .center
+        l.text = "0%"
+        return l
+    }()
+    
+    lazy var slidV : UISlider = {
+        let v = UISlider()
+        v.addTarget(self, action: #selector(valuChange(slid:)), for: .valueChanged)
+        return v
+    }()
+    
+    lazy var musicTb : UITableView = {
+        let tb = UITableView(frame: .zero)
+        tb.delegate = self
+        tb.dataSource = self
+        tb.separatorColor = UIColor.hexColor(hexadecimal: "#272727")
+        tb.register(BFMuicInfoCell.self, forCellReuseIdentifier: "BFMuicInfoCell")
+        tb.backgroundColor = .clear
+//        tb.mj_footer = MJRefreshAutoFooter.init(refreshingBlock: {[weak self] in
+//            guard let wself = self else { return }
+//
+//            let cate = wself.categories[wself.categorySelectIndex]
+//            if (wself.musicDic[cate.id]?.count ?? 0) > 0 {
+//                wself.getMusicsForCategory()
+//            }
+//        })
+        return tb
+    }()
     
     override init(frame: CGRect) {
         super.init(frame: frame)
         
-        backgroundColor = UIColor.black
-        layer.cornerRadius = 10
+        backgroundColor = UIColor.clear
+        
+        let dismisBtn = UIButton()
+        dismisBtn.tag = 1004
+        dismisBtn.frame = CGRect(x: 0, y: 0, width: width, height: 200)
+        dismisBtn.addTarget(self, action: #selector(btnAction(btn:)), for: .touchUpInside)
+        addSubview(dismisBtn)
+        
+        let backV = UIView()
+        backV.backgroundColor = .black
+        backV.isUserInteractionEnabled = true
+        backV.layer.cornerRadius = 10
+        backV.frame =  CGRect(x: 0, y: 200, width: cScreenWidth, height: cScreenHeigth - 190)
+        addSubview(backV)
         
         let cancelBtn = UIButton()
         cancelBtn.tag = 1001
-        cancelBtn.frame = CGRect(x: 18, y: 17, width: 35, height: 24)
+        cancelBtn.frame = CGRect(x: 18, y: 227, width: 35, height: 24)
         cancelBtn.setTitle("取消", for: .normal)
         cancelBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#616161"), for: .normal)
         cancelBtn.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular)
@@ -32,26 +183,387 @@ class BFChooseMusicView: UIView {
         
         let sureBtn = UIButton()
         sureBtn.tag = 1002
-        sureBtn.frame = CGRect(x: width - 35 - 18, y: 17, width: 35, height: 24)
-        sureBtn.setTitle("取消", for: .normal)
-        sureBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#616161"), for: .normal)
+        sureBtn.frame = CGRect(x: width - 35 - 18, y: 227, width: 35, height: 24)
+        sureBtn.setTitle("确定", for: .normal)
+        sureBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#389AFF"), for: .normal)
         sureBtn.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular)
         sureBtn.addTarget(self, action: #selector(btnAction(btn:)), for: .touchUpInside)
         addSubview(sureBtn)
         
+        let line1 = UIView()
+        line1.backgroundColor = UIColor.hexColor(hexadecimal: "#272727")
+        line1.frame = CGRect(x: 0, y: 260, width: width, height: 1)
+        addSubview(line1)
+
+        categoryView.frame = CGRect(x: 0, y: line1.bottomY, width: width - 50, height: 40)
+        addSubview(categoryView)
+        
+        let searchBtn = UIButton()
+        searchBtn.tag = 1003
+        searchBtn.frame  = CGRect(x: categoryView.rightX+2, y: line1.bottomY, width: 40, height: 40)
+        searchBtn.setImage(imageInRecordScreenKit(by: "search"), for: .normal)
+        searchBtn.addTarget(self, action: #selector(btnAction(btn:)), for: .touchUpInside)
+        addSubview(searchBtn)
+        
+        let line2 = UIView()
+        line2.backgroundColor = UIColor.hexColor(hexadecimal: "#272727")
+        line2.frame = CGRect(x: 0, y: categoryView.bottomY, width: width, height: 1)
+        addSubview(line2)
+        
+        musicTb.frame = CGRect(x: 0, y: line2.bottomY, width: width, height: height - line2.bottomY - 100)
+        addSubview(musicTb)
+        
+        let soundIV = UIImageView(image: imageInRecordScreenKit(by: "soundBtn"))
+        soundIV.frame = CGRect(x: 16, y: musicTb.bottomY + 36, width: 28, height: 28)
+        soundIV.contentMode = .scaleAspectFit
+        addSubview(soundIV)
+        
+        slidV.frame = CGRect(x: soundIV.rightX + 20, y: soundIV.y+5, width: cScreenWidth - soundIV.rightX - 20 - 18, height: 18)
+        addSubview(slidV)
+
+        progressL.frame = CGRect(x: slidV.x - 7, y: slidV.y - 24, width: 36, height: 16)
+        addSubview(progressL)
+        
+        self.musicVolume = 20
+
+        playManager.playStatusCallback = {[weak self] playerItem, status in
+            guard let wself = self else { return }
+            
+            if let indx = wself.chosedIndexPath, let cell = wself.musicTb.cellForRow(at: indx) as? BFMuicInfoCell, (playerItem?.asset as? AVURLAsset)?.url.absoluteString ?? "b" == cell.data?.musicPath ?? "a" {
+                DispatchQueue.main.async {
+                    cell.status = status
+                }
+            }
+            wself.chosedCellStatu = status
+        }
+        
+        prepareCatoryList()
+        
     }
     
     required init?(coder: NSCoder) {
         fatalError("init(coder:) has not been implemented")
     }
+
+    //MARK: - 请求数据
+    func prepareCatoryList() {
+        let categoryListUrl = "produce-center/bgm/app/getAllBgmCates"
+        
+        BFNetRequestAdaptor.postRequestData(url: onlinePQTvApi + categoryListUrl, parames: [:], commonParams: commonParams(), encoding: JSONEncoding.default, isJsonEncodingNormal: false, timeoutInterval: 15) {[weak self] response, _, error, _ in
+            guard let wself = self else { return }
+            
+            var tagsList = [MusicCategoryModel]()
+            if response is NSNull || response == nil {
+                cShowHUB(superView: nil, msg: "音乐分类获取失败")
+                return
+            }
+            let tagsTempArr = response as? [[String: Any]]
+            if tagsTempArr != nil, (tagsTempArr?.count ?? 0) > 0 {
+                for dict in tagsTempArr! {
+                    let cid = (dict["cateId"] as? Int64 ?? 0)
+                    if cid > 0 {
+                        let tempMusic = MusicCategoryModel(id: cid, name: dict["cateName"] as? String)
+                        tagsList.append(tempMusic)
+                    }
+                }
+            }
+            
+            let tempMusic = MusicCategoryModel(id: -1, name: "")
+            tagsList.insert(tempMusic, at: 0)
+            wself.categories.append(contentsOf: tagsList)
+            wself.categoryView.reloadData()
+            if wself.categories.count > 1 {
+                wself.categorySelectIndex = 1
+                wself.getMusicsForCategory()
+            }
+        }
+    }
     
+    func getMusicsForCategory() {
+        let musicListUrl = "produce-center/bgm/pageCateBgm"
+        let cate = categories[categorySelectIndex]
+        BFNetRequestAdaptor.postRequestData(url: onlinePQTvApi + musicListUrl, parames: ["cateId": cate.id, "pageNum": cate.page, "pageSize": 100], commonParams: commonParams(), encoding: JSONEncoding.default) {[weak self] response, _, error, _ in
+            guard let wself = self else { return }
+            
+            wself.musicTb.mj_footer?.endRefreshing()
+            
+            if response is NSNull || response == nil {
+                cShowHUB(superView: nil, msg: "该分类下无音乐")
+                return
+            }
+            
+            if wself.musicDic[cate.id] == nil {
+                wself.musicDic[cate.id] = [PQVoiceModel]()
+            }
+
+            var musicPageList = [PQVoiceModel]()
+            let oldDataMusic : [PQVoiceModel] = wself.musicDic[cate.id]!
+            
+            if let dict = response as? [String: Any] {
+                if let tempArr = dict["objs"] as? [[String: Any]] , tempArr.count > 0 {
+                    for (_, dic) in tempArr.enumerated() {
+                        let tempMusic = PQVoiceModel(jsonDict: dic)
+                        tempMusic.cacheTagID = cate.id
+                        if tempMusic.endTime <= tempMusic.startTime {
+                            tempMusic.endTime = tempMusic.startTime + 40
+                        }
+                        
+                        let haveIndex = oldDataMusic.firstIndex(where: { (music) -> Bool in
+                            music.musicId == tempMusic.musicId
+                        })
+                        if haveIndex == nil {
+                            musicPageList.append(tempMusic)
+                        }
+                    }
+                }
+                
+                if musicPageList.count > 0 {
+                    if let ind = wself.categories.firstIndex(where: {$0.id == cate.id}) {
+                        wself.categories[ind].changePage()
+                        
+                    }
+                    wself.musicDic[cate.id]!.append(contentsOf: musicPageList)
+                    if cate.id == wself.categories[wself.categorySelectIndex].id{
+                        wself.musicTb.reloadData()
+                    }
+                }
+                if (dict["totalSize"] as? Int64 ?? 0) <= (dict["offset"] as? Int64 ?? -1) {
+                    // 不再需要数据拉新
+                }
+            }
+            
+            
+        }
+    }
+    
+    @objc func valuChange(slid:UISlider) {
+        musicVolume = Int(slid.value * 100)
+    }
+    
+    func uiForChangeVolume() {
+
+        progressL.text = String(format: "%d%%", musicVolume)
+        
+        var frame = progressL.frame
+        frame.origin.x = slidV.x - 7 + ((slidV.width - 28) / 100.0) * CGFloat(musicVolume)
+        progressL.frame = frame
+        
+        playManager.player.volume = Float(musicVolume) / 100.0
+        chosedMusic?.volume = musicVolume
+
+    }
+    
+    func configCategoriesView(){
+        categoryView.reloadData()
+    }
+    
+    func choseCell() -> BFMuicInfoCell? {
+        if let indpx = chosedIndexPath, let cell = musicTb.cellForRow(at: indpx) as? BFMuicInfoCell {
+            return cell
+        }
+        return nil
+    }
     
     @objc func btnAction(btn:UIButton) {
-        if btn.tag == 1001 {
-            //取消
+        
+        playManager.pause()
+ 
+        choseCell()?.status = .pause
+
+        switch btn.tag{
+        case 1001:
             clickBtnAction?(.cancle)
-        }else {
+            chosedMusic?.isSelected = false
+            chosedMusic = nil
+            choseCell()?.status = .normal
+            chosedIndexPath = nil
+            chosedCellStatu = .normal
+        case 1002, 1004:
             clickBtnAction?(.sure)
+        case 1003:
+            clickBtnAction?(.search)
+        default:break
+        }
+    }
+    
+    func reloadView() {
+        
+    }
+    
+    // 禁用音乐
+    func inhibitChooseMusic() {
+        chosedMusic = nil
+        playManager.pause(.normal)
+//        choseCell()?.status = .normal
+        choseCell()?.changeSelected(false)
+        chosedIndexPath = nil
+    }
+    
+    
+    func refreshCurrSelectModel(mod:PQVoiceModel?) {
+        chosedMusic = mod
+        if let mod = mod {
+            mod.isSelected = true
+        }
+        musicTb.reloadData()
+    }
+}
+
+extension BFChooseMusicView:UITableViewDelegate, UITableViewDataSource {
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: "BFMuicInfoCell", for: indexPath) as! BFMuicInfoCell
+        cell.selectionStyle = .none
+        cell.cutCallBack = {[weak self, weak cell] in
+            guard let wself = self else { return }
+            wself.playManager.pause()
+            if let data = cell?.data {
+                wself.cutActionCallback?(data)
+            }
+        }
+        let tagid = categories[categorySelectIndex].id
+        if let arr = musicDic[tagid] {
+            cell.data = arr[indexPath.row]
+            if cell.data?.isSelected ?? false {
+                chosedIndexPath = indexPath
+                chosedMusic = cell.data
+//                cell.status = chosedCellStatu
+                tableView.selectRow(at: indexPath, animated: false, scrollPosition: UITableView.ScrollPosition.none)
+                cell.status = .pause
+                playManager.replaceCurrentItem(musicPath: chosedMusic?.musicPath)
+            }else{
+                cell.status = .normal
+            }
+        }
+        
+        return cell
+    }
+    
+    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        return 64
+    }
+    
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        if categories.count > 1 {
+            return musicDic[categories[categorySelectIndex].id]?.count ?? 0
+        }
+        return 0
+    }
+    
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+
+        if let cell = tableView.cellForRow(at: indexPath) as? BFMuicInfoCell {
+            if chosedIndexPath == indexPath {
+                if cell.status != .pause{
+                    cell.status = .pause
+                    playManager.pause()
+                }else{
+                    BFLog(1, message: "status: 2")
+                    cell.status = .loading
+                    playManager.play()
+                }
+                return
+            }
+            
+            chosedMusic?.isSelected = false
+            chosedMusic = cell.data
+            chosedMusic?.volume = musicVolume
+            cell.changeSelected(true)
+            
+            chosedIndexPath = indexPath
+            
+            if cell.status == .normal {
+                if let urlStr = cell.data?.musicPath, let url = URL(string: urlStr){
+                    BFLog(1, message: "歌曲地址: \(url)")
+                    if url.absoluteString != (playManager.player.currentItem?.asset as? AVURLAsset)?.url.absoluteString ?? "b" {
+                        cell.status = .loading
+                        playManager.replaceCurrentItem(musicPath: urlStr)
+                    }else {
+                        cell.status = .playing
+                    }
+                    playManager.play()
+                }
+            }else if cell.status == .pause{
+                BFLog(1, message: "status: 2")
+
+                playManager.play()
+            } else{
+                playManager.pause()
+                cell.status = .pause
+            }
+            chosedCellStatu = cell.status
         }
     }
+    
+    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
+        
+        let cell = tableView.cellForRow(at: indexPath) as? BFMuicInfoCell
+        
+        cell?.status = .normal
+        cell?.changeSelected(false)
+        
+        chosedIndexPath = nil
+        playManager.pause()
+    }
 }
+
+extension BFChooseMusicView : UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return categories.count
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BFMusicCategoryCell", for: indexPath) as! BFMusicCategoryCell
+        cell.addData(dic: categories[indexPath.row])
+        cell.isChoosed = (indexPath.row == categorySelectIndex)
+     
+        return cell
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        let oldCell = collectionView.cellForItem(at: IndexPath(row: categorySelectIndex, section: 0)) as? BFMusicCategoryCell
+        let newCell = collectionView.cellForItem(at: indexPath) as? BFMusicCategoryCell
+        if indexPath.row > 0 {
+            categorySelectIndex = indexPath.row
+            musicTb.reloadData()
+            
+            oldCell?.isChoosed = false
+            newCell?.isChoosed = true
+            
+            getMusicsForCategory()
+            
+        }else {
+            inhibitChooseMusic()
+        }
+    }
+    
+    
+}
+
+extension BFChooseMusicView: BFFlowLayoutDelegate{
+    
+    func flowLayout(_ flowLayout: BFCollectionViewFlowLayout, itemHeight indexPath: IndexPath) -> CGFloat {
+        
+        var itemWidth = 30.0
+        
+        if let title = categories[indexPath.row].name, title.count > 0{
+            let ww = title.textAutoWidth(height: 20, font: UIFont.systemFont(ofSize: 15, weight: .regular))
+            itemWidth = max(30.0, ww)
+        }
+        
+        return itemWidth
+    }
+}
+
+extension String{
+    func textAutoWidth(height:CGFloat, font:UIFont) ->CGFloat{
+        let string = self as NSString
+        let origin = NSStringDrawingOptions.usesLineFragmentOrigin
+        let lead = NSStringDrawingOptions.usesFontLeading
+        let rect = string.boundingRect(with:CGSize(width:0, height: height), options: [origin,lead],
+                                       attributes: [NSAttributedString.Key.font:font],
+                                       context:nil)
+        return rect.width
+        
+    }
+}
+

+ 83 - 0
BFRecordScreenKit/Classes/RecordScreen/View/BFCustomMusicBtn.swift

@@ -0,0 +1,83 @@
+//
+//  BFCustomMusicBtn.swift
+//  BFRecordScreenKit
+//
+//  Created by Harry on 2022/3/4.
+//
+
+import Foundation
+import Kingfisher
+
+
+class BFCustomMusicBtn : UIView {
+    var title : String = ""
+    var img : UIImage?
+    
+    var clickAction : (() -> Void)?
+    
+    fileprivate var titleL = UILabel()
+    fileprivate var imageV = UIImageView(image: imageInRecordScreenKit(by: "addmusic"))
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        
+        titleL.font = UIFont.systemFont(ofSize: 13)
+        titleL.text = "选择音乐"
+        titleL.textColor = UIColor.white
+        titleL.textAlignment = .left
+        titleL.lineBreakMode = .byTruncatingTail
+        
+        imageV.layer.cornerRadius = 13
+        imageV.layer.masksToBounds = true
+        
+        addSubview(imageV)
+        addSubview(titleL)
+        
+        
+        imageV.snp.makeConstraints { make in
+            make.left.equalTo(6)
+            make.right.equalTo(titleL.snp.left).offset(-4)
+            make.top.equalTo(3)
+            make.bottom.equalTo(-3)
+            make.width.equalTo(imageV.snp.height)
+            make.centerY.equalToSuperview()
+        }
+        
+        titleL.snp.makeConstraints { make in
+            make.left.equalTo(imageV.snp.right).offset(4)
+            make.right.equalTo(-8)
+            make.top.equalTo(2)
+            make.bottom.equalTo(-2)
+            make.centerY.equalToSuperview()
+            make.width.lessThanOrEqualTo(200)
+        }
+        
+        let btn = UIButton()
+        btn.addTarget(self, action: #selector(click), for: .touchUpInside)
+        addSubview(btn)
+        btn.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    @objc func click() {
+        clickAction?()
+    }
+    
+    func config(title:String? = "选择音乐", imgUrl:URL? = nil){
+        titleL.text = title ?? "选择音乐"
+        if imgUrl == nil {
+            imageV.image = imageInRecordScreenKit(by: "addmusic")
+        }else {
+            imageV.kf.setImage(with: imgUrl)
+        }
+        
+        setNeedsLayout()
+        layoutIfNeeded()
+        
+    }
+}

+ 29 - 24
BFRecordScreenKit/Classes/RecordScreen/View/BFLoadingView.swift

@@ -12,17 +12,21 @@ import UIKit
 
 class BFLoadingView: UIView {
     var cancelHandle: (() -> Void)?
-
-    // gif每一帧图
-    public var gifImages: [UIImage]?
-    // gif播放时长
-    public var duration: Double?
-
+    
     public lazy var loadingImage: UIImageView = {
         let loadingImage = UIImageView()
-        loadingImage.tintColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
+        loadingImage.image = imageInRecordScreenKit(by: "stuckPoint_edit_loading")
         return loadingImage
     }()
+    
+    //是否是下载音乐的模式,不发出回调不显示进度
+    public var isDownloadMusic:Bool = false{
+        didSet {
+            if isDownloadMusic {
+                titleL.text = ""
+            }
+        }
+    }
 
     lazy var closedBtn: UIButton = {
         let closedBtn = UIButton(type: .custom)
@@ -49,13 +53,6 @@ class BFLoadingView: UIView {
         addSubview(closedBtn)
         addSubview(titleL)
 
-        let data = try? Data(contentsOf: URL(fileURLWithPath: currentBundle()!.path(forResource: "stuckPoint_edit_loading", ofType: ".gif")!))
-        if data != nil {
-            PQPHAssetVideoParaseUtil.parasGIFImage(data: data!, isRenderingColor: UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)) { [weak self] _, images, duration in
-                self?.gifImages = images
-                self?.duration = duration
-            }
-        }
     }
 
     required init?(coder _: NSCoder) {
@@ -76,25 +73,33 @@ class BFLoadingView: UIView {
     public func loadShow() {
     
         isHidden = false
-        loadingImage.displayGIF(data: nil, images: gifImages, repeatCount: .max, duration: duration ?? 2)
-    }
-    
-    //启动动画
-    public func startGIF(){
-        loadingImage.displayGIF(data: nil, images: gifImages, repeatCount: .max, duration: duration ?? 2)
+        loadingImage.layer.removeAllAnimations()
+        loadingImage.isHidden = false
+        // 1.创建动画
+        let rotationAnim = CABasicAnimation(keyPath: "transform.rotation.z")
+        // 2.设置动画的属性
+        rotationAnim.fromValue = 0
+        rotationAnim.toValue = Double.pi * 2
+        rotationAnim.repeatCount = MAXFLOAT
+        rotationAnim.duration = 1
+        // 这个属性很重要 如果不设置当页面运行到后台再次进入该页面的时候 动画会停止
+        rotationAnim.isRemovedOnCompletion = false
+        // 3.将动画添加到layer中
+        loadingImage.layer.add(rotationAnim, forKey: nil)
     }
-    
-    
+ 
     public func removeLoading(){
         isHidden = true
-        loadingImage.removePlayGIF()
+        
+        loadingImage.layer.removeAllAnimations()
+        loadingImage.isHidden = true
         
     }
 
     // 隐藏动画
     @objc public func loadHidden() {
         removeLoading()
-        if cancelHandle != nil {
+        if cancelHandle != nil,!isDownloadMusic {
             cancelHandle!()
         }
         

+ 378 - 0
BFRecordScreenKit/Classes/RecordScreen/View/BFMusicCutView.swift

@@ -0,0 +1,378 @@
+//
+//  BFMusicCutView.swift
+//  BFRecordScreenKit
+//
+//  Created by ak on 2022/3/4.
+//  功能:音乐裁剪界面,设置开始播放位置
+
+import BFCommonKit
+import BFMediaKit
+import BFUIKit
+import Foundation
+import UIKit
+
+// 操作动作类型
+public enum MusiceCutActionType: Int {
+    case MusiceCutActionCancel = 1 // 取消
+    case MusiceCutActionConfirm = 2 // 确认
+}
+
+class BFMusicCutView: UIView, UIGestureRecognizerDelegate {
+    var waveLayers: [CAShapeLayer] = Array<CAShapeLayer>.init()
+    // 裁剪时间回调
+    var cutTimeHandle: ((_ isCancel: Bool, _ endTime: Float64, _ bgmData: PQVoiceModel?) -> Void)?
+
+    let normalMargin: CGFloat = cDefaultMargin * 2
+
+    // 记录设置的起点, 在点击确认后会设置 bgmdata 的 开始时间为本值。
+    var startCMTime: Float64 = 0.0 // 开始时间
+
+    // 播放音乐
+    lazy var avPlayer: AVPlayer = {
+        let avPlayer = AVPlayer()
+        PQNotification.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: avPlayer.currentItem, queue: .main) { [weak self] notify in
+            BFLog(message: "AVPlayerItemDidPlayToEndTime = \(notify)")
+            guard let wself = self else { return }
+
+            if(!wself.isHidden){
+                wself.avPlayer.seek(to: CMTime(value: CMTimeValue((wself.bgmData?.currentTime ?? 0.0)) * Int64(playerTimescale), timescale: CMTimeScale(playerTimescale)))
+                wself.resetWave()
+
+                wself.avPlayer.play()
+            }
+       
+        }
+        PQNotification.addObserver(forName: .AVPlayerItemNewErrorLogEntry, object: avPlayer.currentItem, queue: .main) { notify in
+            BFLog(message: "AVPlayerItemNewErrorLogEntry = \(notify)")
+        }
+        PQNotification.addObserver(forName: .AVPlayerItemFailedToPlayToEndTime, object: avPlayer.currentItem, queue: .main) { notify in
+            BFLog(message: "AVPlayerItemFailedToPlayToEndTime = \(notify)")
+        }
+        PQNotification.addObserver(forName: .AVPlayerItemPlaybackStalled, object: avPlayer.currentItem, queue: .main) { notify in
+            BFLog(message: "AVPlayerItemPlaybackStalled = \(notify)")
+        }
+        avPlayer.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: CMTimeScale(playerTimescale)), queue: .main) { [weak self] _ in
+            guard let wself = self else { return }
+
+            let currentTime = CMTimeGetSeconds(avPlayer.currentItem?.currentTime() ?? CMTime.zero)
+            wself.configPlayProgress(currentTime: currentTime)
+        }
+        return avPlayer
+    }()
+
+    // 放指针的背景图
+    lazy var panCutBackView: UIView = {
+        let panCutBackView = UIView()
+        panCutBackView.backgroundColor = .clear
+        let panGes = UIPanGestureRecognizer(target: self, action: #selector(panClick(ges:)))
+        panCutBackView.addGestureRecognizer(panGes)
+        return panCutBackView
+    }()
+
+    // 指针
+    lazy var cutRemindView: UIView = {
+        let cutRemindView = UIView()
+        cutRemindView.backgroundColor = UIColor.hexColor(hexadecimal: "#389AFF")
+        cutRemindView.addCorner(corner: 2)
+        cutRemindView.frame = CGRect(x: (panCutBackView.frame.width - 2) / 2, y: 0, width: 2, height: panCutBackView.frame.height)
+        return cutRemindView
+    }()
+
+    // 水波纹的背景
+    lazy var progressImage: UIImageView = {
+        let progressImage = UIImageView()
+        progressImage.frame = CGRect(x: 20, y: currentProgressLab.frame.maxY + 2, width: cScreenWidth - 20 * 2, height: cDefaultMargin * 6)
+        return progressImage
+    }()
+
+    // 显示进度
+    lazy var currentProgressLab: UILabel = {
+        let currentProgressLab = UILabel(frame: CGRect(x: cDefaultMargin * 3, y: cDefaultMargin * 6, width: cDefaultMargin * 6, height: cDefaultMargin * 2))
+        currentProgressLab.textColor = UIColor.hexColor(hexadecimal: "#616161")
+        currentProgressLab.font = UIFont.systemFont(ofSize: 13)
+        currentProgressLab.text = "00:00"
+        return currentProgressLab
+    }()
+
+    // 操作板背景
+    let backView = UIButton()
+
+    // 当前选择的音乐
+    public var bgmData: PQVoiceModel? {
+        didSet {
+            BFLog(message: "设置的音乐开始时间currentTime:\(bgmData?.currentTime ?? 0) 总时长\(bgmData?.duration ?? "") 音量是:\(bgmData?.volume ?? 0)")
+            resetWave()
+            // 默认进来播放音乐
+            playBGM()
+            avPlayer.volume = Float(bgmData?.volume ?? 0) / 100.0
+            addCutViewLayout()
+        }
+    }
+
+    lazy var titleL: UILabel = {
+        let l = UILabel()
+        l.text = "起播点"
+        l.textAlignment = .center
+        l.textColor = .white
+        l.font = UIFont.systemFont(ofSize: 17, weight: .medium)
+
+        return l
+    }()
+
+    required init?(coder _: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    override func layoutSubviews() {
+        super.layoutSubviews()
+        backView.addCorner(roundingCorners: [.topLeft, .topRight], corner: 10)
+        addCutViewLayout()
+    }
+
+    func addCutViewLayout() {
+        let totalWidth = (frame.width - normalMargin * 2 - 6)
+        let itemWidth = totalWidth / CGFloat(Double("\(bgmData?.duration ?? "1")") ?? 0.0)
+        panCutBackView.frame = CGRect(x: (normalMargin - cDefaultMargin * 1.5) + CGFloat(bgmData?.currentTime ?? 0) * itemWidth, y: currentProgressLab.frame.maxY + 2, width: cDefaultMargin * 3, height: cDefaultMargin * 6)
+        cutRemindView.frame = CGRect(x: (panCutBackView.frame.width - 2) / 2, y: 0, width: 2, height: panCutBackView.frame.height)
+        currentProgressLab.centerX = panCutBackView.centerX + cDefaultMargin
+    }
+
+    func playBGM() {
+        if (bgmData?.musicPath ?? "").count > 0 {
+            avPlayer.pause()
+            avPlayer.replaceCurrentItem(with: AVPlayerItem(url: URL(string: (bgmData?.musicPath ?? ""))!))
+            avPlayer.seek(to: CMTime(value: CMTimeValue((bgmData?.currentTime ?? 0.0) * playerTimescale), timescale: CMTimeScale(playerTimescale)))
+
+            avPlayer.play()
+        }
+    }
+
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+
+        backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
+        backView.backgroundColor = UIColor.hexColor(hexadecimal: "#121212")
+        addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(okBtnAction)))
+
+        addSubview(backView)
+        backView.snp.makeConstraints { make in
+            make.right.equalTo(self.snp.right)
+            make.bottom.equalTo(self.snp.bottom)
+            make.width.equalTo(cScreenWidth)
+            make.height.equalTo(220)
+        }
+        backView.setNeedsUpdateConstraints()
+
+        // 取消
+        let cancelBtn = UIButton()
+        cancelBtn.backgroundColor = .clear
+        cancelBtn.setTitle("取消", for: .normal)
+        cancelBtn.setTitleColor(.white, for: .normal)
+        cancelBtn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 17)
+        cancelBtn.addTarget(self, action: #selector(cancelAction), for: .touchDown)
+        backView.addSubview(cancelBtn)
+
+        cancelBtn.snp.makeConstraints { make in
+            make.left.equalToSuperview().offset(18)
+            make.width.equalTo(35)
+            make.height.equalTo(24)
+            make.top.equalToSuperview().offset(18)
+        }
+
+        backView.addSubview(titleL)
+        titleL.snp.makeConstraints { make in
+            make.centerX.equalToSuperview()
+            make.width.equalTo(54)
+            make.height.equalTo(24)
+            make.top.equalTo(cancelBtn)
+        }
+
+        // 确认
+        let okBtn = UIButton()
+        okBtn.backgroundColor = .clear
+        okBtn.setTitle("确认", for: .normal)
+        okBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#389AFF"), for: .normal)
+        okBtn.titleLabel?.font = UIFont.systemFont(ofSize: 17)
+        okBtn.addTarget(self, action: #selector(okBtnAction), for: .touchUpInside)
+        backView.addSubview(okBtn)
+        okBtn.snp.makeConstraints { make in
+            make.right.equalToSuperview().offset(-18)
+            make.width.equalTo(35)
+            make.height.equalTo(24)
+            make.top.equalToSuperview().offset(18)
+        }
+
+        backView.addSubview(panCutBackView)
+        panCutBackView.addSubview(cutRemindView)
+        backView.addSubview(currentProgressLab)
+        backView.addSubview(progressImage)
+
+        if (progressImage.layer.sublayers?.count ?? 0) > 0 {
+            let totalCount = progressImage.layer.sublayers?.count ?? 0
+            for index in 0 ..< Int(totalCount) {
+                (progressImage.layer.sublayers?[index] as? CAShapeLayer)?.strokeColor = UIColor.white.cgColor
+                (progressImage.layer.sublayers?[index] as? CAShapeLayer)?.setNeedsDisplay()
+            }
+        } else {
+            let totalCount = Int(cScreenWidth - normalMargin * 2) / (cFrequency.count * 2)
+            let remainder = Int(cScreenWidth - normalMargin * 2) % (cFrequency.count * 2)
+            var totalWave: [CGFloat] = Array<CGFloat>.init()
+            for _ in 0 ..< totalCount {
+                totalWave = totalWave + cFrequency
+            }
+            if remainder > 0 {
+                totalWave = totalWave + cFrequency[0 ... (remainder / 2)]
+            }
+            createWave(waveArr: totalWave)
+        }
+    }
+
+    /// 生成波纹
+    /// - Parameter waveArr: <#waveArr description#>
+    /// - Returns: <#description#>
+    func createWave(waveArr: [CGFloat]) {
+        for (i, power) in waveArr.enumerated() {
+            // 画布高度
+            let hight: CGFloat = progressImage.frame.height
+            // 开始 Y 值
+            var startY: CGFloat = (hight - power) / 2.0
+            if startY < 0 { startY = 0 }
+            // 结束 Y 值
+            var endY: CGFloat = startY + power
+            if endY > CGFloat(hight) { endY = hight }
+            // 线的路径
+            let linePath = UIBezierPath()
+
+            // 起点
+            let originX: CGFloat = CGFloat(i * 2)
+            linePath.move(to: CGPoint(x: originX, y: startY))
+            // 终点
+            linePath.addLine(to: CGPoint(x: originX, y: endY))
+
+            let lineLayer = CAShapeLayer()
+
+            lineLayer.lineWidth = 1
+
+            lineLayer.strokeColor = UIColor.white.cgColor
+
+            lineLayer.path = linePath.cgPath
+            lineLayer.fillColor = UIColor.black.cgColor
+
+            waveLayers.append(lineLayer)
+            progressImage.layer.addSublayer(lineLayer)
+        }
+    }
+    
+    func setProgress() {
+        
+    }
+
+    @objc func panClick(ges: UIPanGestureRecognizer) {
+        if ges.state == .changed {
+            let translation = ges.translation(in: backView)
+            let totalWidth = progressImage.frame.width + (cutRemindView.frame.width / 2)
+            let itemWidth = totalWidth / CGFloat(Double("\(bgmData?.duration ?? "1")") ?? 0.0)
+            let maxX = progressImage.frame.maxX
+            let minX = cDefaultMargin * 2
+            let maxWidth = maxX - minX + itemWidth
+            var gesCenx = (ges.view?.center.x ?? 0) + translation.x
+            if gesCenx < minX {
+                gesCenx = minX
+            } else if gesCenx > maxX {
+                gesCenx = maxX
+            }
+            ges.view?.center.x = gesCenx
+            currentProgressLab.center.x = panCutBackView.center.x + cDefaultMargin
+            ges.setTranslation(CGPoint.zero, in: ges.view)
+            let cenx = (ges.view!.center.x - minX)
+            currentProgressLab.text = (Float64(cenx / maxWidth) * (Float64("\(bgmData?.duration ?? "0")") ?? 0.0)).formatDurationToHMS()
+            let startTime = Float64(Int((cenx / maxWidth) * CGFloat(Double("\(bgmData?.duration ?? "0")") ?? 0.0)))
+            let endTime = Float64(Double("\(bgmData?.duration ?? "0")") ?? 0.0)
+            BFLog(message: "裁剪背景音乐 duration = \(bgmData?.duration ?? "0"),startTime = \(startTime),endTime = \(endTime)")
+            bgmData?.currentTime = startTime
+
+            avPlayer.seek(to: CMTime(value: CMTimeValue(startTime * playerTimescale), timescale: CMTimeScale(playerTimescale)))
+            avPlayer.play()
+            // 重置波纹
+            resetWave()
+        }
+    }
+
+    /// 重置波纹
+    /// - Returns: <#description#>
+    func resetWave() {
+        let startTotal = (Float64(waveLayers.count) / (Float64("\(bgmData?.duration ?? "0")") ?? 1)) * Float64(bgmData?.currentTime ?? 0)
+        if startTotal.isNaN {
+            return
+        }
+        let totalCount = progressImage.layer.sublayers?.count ?? 0
+        if totalCount > 0 {
+            for index in 0 ..< Int(totalCount) {
+                if index < Int(startTotal) {
+                    (progressImage.layer.sublayers?[index] as? CAShapeLayer)?.strokeColor = UIColor.hexColor(hexadecimal: "#202020").cgColor
+                } else {
+                    (progressImage.layer.sublayers?[index] as? CAShapeLayer)?.strokeColor = UIColor.hexColor(hexadecimal: "#505050").cgColor
+                }
+                (progressImage.layer.sublayers?[totalCount - index - 1] as? CAShapeLayer)?.setNeedsDisplay()
+            }
+        }
+    }
+
+    func configPlayProgress(currentTime: Float64) {
+        let totalCount = progressImage.layer.sublayers?.count ?? 0
+        if currentTime >= 0 {
+            // 播放的开始点
+            let startPoint = (Float64(waveLayers.count) / (Float64("\(bgmData?.duration ?? "0")") ?? 1)) * Float64(bgmData?.currentTime ?? 0)
+            // 已经播放到的点
+            let endPoint = (Float64(waveLayers.count) / (Float64("\(bgmData?.duration ?? "0")") ?? 1)) * currentTime
+
+            if endPoint < startPoint {
+                BFLog(message: "startPoint: \(startPoint) endPoint:\(endPoint)")
+                return
+            }
+            if totalCount > 0 {
+                for index in Int(startPoint) ... Int(endPoint) {
+                    var tempIndex = index
+                    if tempIndex < 0 {
+                        tempIndex = 0
+                    }
+                    if tempIndex >= totalCount - 1 {
+                        tempIndex = totalCount - 1
+                    }
+                    (progressImage.layer.sublayers?[tempIndex] as? CAShapeLayer)?.strokeColor = UIColor.hexColor(hexadecimal: "#1B5692").cgColor
+                    (progressImage.layer.sublayers?[tempIndex] as? CAShapeLayer)?.setNeedsDisplay()
+                }
+            }
+        }
+    }
+
+    func show() {
+        isHidden = false
+        startCMTime = bgmData?.currentTime ?? 0.0
+    }
+
+    @objc func hidden() {
+        isHidden = true
+        avPlayer.pause()
+    }
+
+    @objc func cancelAction() {
+        hidden()
+        bgmData?.currentTime = startCMTime
+        if cutTimeHandle != nil, bgmData != nil {
+            cutTimeHandle!(true, 0, bgmData)
+        }
+    }
+
+    @objc func okBtnAction() {
+        hidden()
+
+        bgmData?.startCMTime = CMTime(value: CMTimeValue((bgmData?.currentTime ?? 0.0) * playerTimescale), timescale: CMTimeScale(playerTimescale))
+
+        BFLog(message: "最后设置的开始时间是\(bgmData?.startCMTime.seconds ?? 0.0)")
+
+        if cutTimeHandle != nil, bgmData != nil {
+            cutTimeHandle!(false, 0, bgmData)
+        }
+    }
+}

+ 4 - 1
BFRecordScreenKit/Classes/RecordScreen/View/BFSubtitleSettingView.swift

@@ -11,7 +11,7 @@ import BFUIKit
 import Foundation
 import UIKit
 
-typealias SubtitleSettingCallBack = (_ subtitileModel: PQEditSubTitleModel) -> Void
+typealias SubtitleSettingCallBack = (_ subtitileModel: PQEditSubTitleModel?) -> Void
 
 class BFSubtitleSettingView: UIView {
     required init?(coder _: NSCoder) {
@@ -218,6 +218,9 @@ class BFSubtitleSettingView: UIView {
 
     @objc func hidden() {
         isHidden = true
+        if subtitleSettingCallBack != nil {
+            subtitleSettingCallBack!(subtitle)
+        }
     }
 
     @objc func styleSetting(sender: UIButton) {

+ 4 - 1
BFRecordScreenKit/Classes/RecordScreen/View/BFVoiceSettingView.swift

@@ -148,7 +148,10 @@ class BFVoiceSettingView: UIView {
     override init(frame: CGRect) {
         super.init(frame: frame)
 
-        backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)
+        backgroundColor = .clear
+        let v = UIView.init(frame: CGRect.init(x: 0, y: 0, width: cScreenWidth, height: cScreenHeigth))
+        addSubview(v)
+        v.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(okBtnAction)))
         backView.backgroundColor = UIColor.hexColor(hexadecimal: "#121212")
 
         addSubview(backView)

+ 11 - 0
BFRecordScreenKit/Classes/RecordScreen/View/Cell/BFCameraCoverViewCell.swift

@@ -11,12 +11,23 @@ import GPUImage
 open class BFCameraCoverViewCell: BFImageCoverViewCell {
     
     public var initCameraAfterData:(()->())?
+    public var cameraFlip:(() -> Void)?
     
     @objc public class func gpuCamraViewCell(collectionView: UICollectionView, indexPath: IndexPath) -> BFCameraCoverViewCell {
         let cell: BFCameraCoverViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: BFCameraCoverViewCell.self), for: indexPath) as! BFCameraCoverViewCell
+        
+        let doubleTapGes = UITapGestureRecognizer(target: cell, action: #selector(doubleTapAction(tap:)))
+        doubleTapGes.numberOfTapsRequired = 2
+        doubleTapGes.numberOfTouchesRequired = 1
+        cell.contentView.addGestureRecognizer(doubleTapGes)
+        
         return cell
     }
     
+    @objc func doubleTapAction(tap:UITapGestureRecognizer) {
+        cameraFlip?()
+    }
+    
     public override var recordItem: BFRecordItemModel? {
         didSet {
 //            addData()

+ 61 - 0
BFRecordScreenKit/Classes/RecordScreen/View/Cell/BFCollectionViewFlowLayout.swift

@@ -0,0 +1,61 @@
+//
+//  BFCollectionViewFlowLayout.swift
+//  BFRecordScreenKit
+//
+//  Created by 胡志强 on 2022/3/1.
+//
+
+import Foundation
+
+
+protocol BFFlowLayoutDelegate: NSObjectProtocol {
+    func flowLayout(_ flowLayout: BFCollectionViewFlowLayout, itemHeight indexPath: IndexPath) -> CGFloat
+}
+
+class BFCollectionViewFlowLayout: UICollectionViewFlowLayout {
+    weak var dele : BFFlowLayoutDelegate?
+    var totalWidth = 0.0
+
+    var layoutAttributeArray = [UICollectionViewLayoutAttributes]()
+    
+    override func prepare() {
+        super.prepare()
+        // 计算每个 Cell 的宽度
+        let itemHeight = 40.0
+        // Cell 数量
+        let itemCount = collectionView!.numberOfItems(inSection: 0)
+
+        // 遍历 item 计算并缓存属性
+        totalWidth = 0.0
+        
+        for i in layoutAttributeArray.count ..< itemCount {
+            let indexPath = IndexPath(item: i, section: 0)
+            
+            let attr = UICollectionViewLayoutAttributes(forCellWith: indexPath)
+            
+            // 获取动态高度
+            let itemWidth = dele?.flowLayout(self, itemHeight: indexPath) ?? 0.0
+
+            // 赋值新的位置信息
+            attr.frame = CGRect(x: totalWidth, y: 0, width: itemWidth + minimumLineSpacing, height: CGFloat(itemHeight))
+            // 缓存布局属性
+            layoutAttributeArray.append(attr)
+            totalWidth = attr.frame.maxX
+        }
+    }
+    
+    
+}
+
+extension BFCollectionViewFlowLayout {
+    
+    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
+        return layoutAttributeArray.filter {
+            $0.frame.intersects(rect)
+        }
+    }
+    
+    override var collectionViewContentSize: CGSize {
+        return CGSize(width: totalWidth , height: collectionView!.bounds.height)
+    }
+}

+ 225 - 0
BFRecordScreenKit/Classes/RecordScreen/View/Cell/BFMuicInfoCell.swift

@@ -0,0 +1,225 @@
+//
+//  BFMuicInfoCell.swift
+//  BFRecordScreenKit
+//
+//  Created by 胡志强 on 2022/3/1.
+//
+
+import Foundation
+import BFMediaKit
+import BFCommonKit
+import BFMaterialKit
+
+enum BFMuicInfoCellState {
+    case normal, playing, pause, loading
+}
+
+class BFMuicInfoCell: UITableViewCell {
+    var cutCallBack : (() -> Void)?
+    var useCallback : (() -> Void)?
+
+    var data:PQVoiceModel?{
+        didSet{
+            addData()
+        }
+    }
+    
+    var status : BFMuicInfoCellState = .normal {
+        didSet {
+            switch status {
+            case .normal:
+                maskIV.image = nil
+                audioStateView.isHidden = true
+
+            case .playing:
+                maskIV.image = imageInRecordScreenKit(by: "markPlaying")
+                stopLoading()
+                audioStateView.isHidden = false
+                audioStateView.kf.setImage(with: URL(fileURLWithPath: currentBundle()!.path(forResource: "stuckPoint_music_playing", ofType: ".gif")!))
+                
+            case .pause:
+                maskIV.image = imageInRecordScreenKit(by: "markSeleced")
+                stopLoading()
+                audioStateView.isHidden = false
+                audioStateView.image = imageInRecordScreenKit(by: "stuckPoint_music_pause")
+
+            case .loading:
+                maskIV.image = imageInRecordScreenKit(by: "markSeleced")
+                audioStateView.image = imageInRecordScreenKit(by: "audioLoding")?.withRenderingMode(.alwaysTemplate)
+                loadingAnimation()
+                audioStateView.isHidden = false
+            }
+        }
+    }
+    
+    lazy var iconIV : UIImageView = {
+        let iv = UIImageView()
+        iv.layer.cornerRadius = 21
+        iv.layer.masksToBounds = true
+        
+        return iv
+    }()
+    
+    lazy var maskIV : UIImageView = {
+        let iv = UIImageView()
+        iv.layer.masksToBounds = true
+        iv.layer.cornerRadius = 21
+        iv.layer.masksToBounds = true
+        iv.backgroundColor = UIColor.init(white: 0, alpha: 0.5)
+        
+        return iv
+    }()
+
+    // 语音播放动画和暂停播放
+    lazy var audioStateView: UIImageView = {
+        let iv = UIImageView()
+        iv.image = imageInRecordScreenKit(by: "audioLoding")
+        iv.contentMode = .scaleAspectFit
+        iv.isHidden = true
+        return iv
+    }()
+    
+    lazy var titleL : UILabel = {
+        let l = UILabel()
+        l.text = "歌曲"
+        l.textColor = .white
+        l.font = UIFont.systemFont(ofSize: 16, weight: .regular)
+        l.textAlignment = .left
+        return l
+    }()
+    
+    lazy var cutBtn : UIButton = {
+        let cutBtn = UIButton()
+        cutBtn.tag = 2001
+        cutBtn.setImage(imageInRecordScreenKit(by: "clip"), for: .normal)
+        cutBtn.addTarget(self, action: #selector(cutAction), for: .touchUpInside)
+        return cutBtn
+    }()
+    
+    lazy var useBtn : UIButton = {
+        let useBtn = UIButton()
+        useBtn.tag = 2002
+        useBtn.setTitle("使用", for: .normal)
+        useBtn.backgroundColor = UIColor.hexColor(hexadecimal: "#389AFF")
+        useBtn.layer.cornerRadius = 4
+        useBtn.titleLabel?.font = UIFont.systemFont(ofSize: 14)
+        useBtn.addTarget(self, action: #selector(useBtnAction), for: .touchUpInside)
+        return useBtn
+    }()
+    
+    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+        super.init(style: style, reuseIdentifier: reuseIdentifier)
+        self.backgroundColor = .clear
+        contentView.backgroundColor = .clear
+        
+        iconIV.frame = CGRect(x: 18, y: 11, width: 42, height: 42)
+        maskIV.frame = iconIV.bounds
+        audioStateView.frame = CGRect(x: 8, y: 8, width: 26, height: 26)
+        
+        iconIV.addSubview(maskIV)
+        iconIV.addSubview(audioStateView)
+        contentView.addSubview(iconIV)
+        
+
+        titleL.frame = CGRect(x: iconIV.rightX + 10, y: 12, width: width - iconIV.rightX - 10 - 90, height: 40)
+        contentView.addSubview(useBtn)
+        useBtn.snp.makeConstraints { make in
+            make.right.equalTo(-16)
+            make.width.equalTo(0)
+            make.height.equalTo(40)
+            make.top.equalTo(12)
+        }
+                
+        contentView.addSubview(cutBtn)
+        cutBtn.snp.makeConstraints { make in
+            make.right.equalTo(useBtn.snp.left).offset(-2)
+            make.width.equalTo(0)
+            make.height.equalTo(40)
+            make.top.equalTo(12)
+        }
+        
+        contentView.addSubview(titleL)
+        titleL.snp.makeConstraints { make in
+            make.left.equalTo(iconIV.snp.right).offset(10)
+            make.top.equalTo(12)
+            make.height.equalTo(40)
+            make.right.equalTo(cutBtn.snp.left).offset(-2)
+        }
+        
+
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    func addData() {
+        if let data = data{
+            let plImg = imageInRecordScreenKit(by: "music_icon") ?? UIImage()
+            iconIV.setNetImage(url: data.avatarUrl, placeholder: plImg)
+            titleL.text = data.musicName
+            
+            changeSelected(data.isSelected)
+            
+        
+        }
+    }
+    
+    func changeSelected(_ selected: Bool){
+        data?.isSelected = selected
+
+        if selected{
+            titleL.textColor = UIColor.hexColor(hexadecimal: "#389AFF")
+            titleL.font = UIFont.systemFont(ofSize: 16, weight: .medium)
+            showFunctionBtn()
+        }else {
+            titleL.textColor = UIColor.white
+            titleL.font = UIFont.systemFont(ofSize: 16, weight: .regular)
+            hiddenFunctionBtn()
+        }
+    }
+
+    
+    func showFunctionBtn() {
+        cutBtn.snp.updateConstraints { make in
+            make.width.equalTo(40)
+        }
+    }
+    
+    func hiddenFunctionBtn() {
+        cutBtn.snp.updateConstraints { make in
+            make.width.equalTo(0)
+        }
+    }
+    
+    @objc func cutAction() {
+        cutCallBack?()
+    }
+    
+    
+    @objc func useBtnAction() {
+        useCallback?()
+    }
+    
+    func loadingAnimation(){
+        let rotationAnim = CABasicAnimation(keyPath: "transform.rotation.z")
+        // 2.设置动画的属性
+        rotationAnim.fromValue = 0
+        rotationAnim.toValue = Double.pi * 2
+        rotationAnim.repeatCount = MAXFLOAT
+        rotationAnim.duration = 1
+        // 这个属性很重要 如果不设置当页面运行到后台再次进入该页面的时候 动画会停止
+        rotationAnim.isRemovedOnCompletion = false
+        // 3.将动画添加到layer中
+        audioStateView.layer.add(rotationAnim, forKey: nil)
+
+    }
+    
+    func stopLoading(){
+        audioStateView.layer.removeAllAnimations()
+    }
+    
+    deinit {
+        stopLoading()
+    }
+}

+ 99 - 0
BFRecordScreenKit/Classes/RecordScreen/View/Cell/BFMusicCategoryCell.swift

@@ -0,0 +1,99 @@
+//
+//  BFMusicCategoryCell.swift
+//  BFRecordScreenKit
+//
+//  Created by 胡志强 on 2022/3/1.
+//
+
+import Foundation
+import BFMediaKit
+
+class BFMusicCategoryCell: UICollectionViewCell {
+    
+    var isChoosed : Bool = false {
+        didSet{
+            if titleL.text?.count ?? 0 > 0{
+                if isChoosed {
+                    titleL.textColor = UIColor.white
+                    line.isHidden = false
+                }else {
+                    titleL.textColor = UIColor.hexColor(hexadecimal: "#616161")
+                    line.isHidden = true
+                }
+            }
+            
+        }
+    }
+    lazy var titleL : UILabel = {
+        let l = UILabel()
+        l.textColor = UIColor.white
+        l.font = UIFont.systemFont(ofSize: 15, weight: .regular)
+        l.textColor = UIColor.hexColor(hexadecimal: "#616161")
+        l.textAlignment = .center
+        
+        return l
+    }()
+    
+    lazy var icon : UIImageView = {
+        let iv = UIImageView()
+        iv.contentMode = .scaleAspectFit
+        
+        return iv
+    }()
+    
+    lazy var line : UIView = {
+        let v = UIView()
+        v.backgroundColor = .white
+        v.isHidden = true
+        return v
+    }()
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        backgroundColor = UIColor.clear
+ 
+        contentView.addSubview(titleL)
+        titleL.snp.makeConstraints { make in
+            make.left.right.top.equalToSuperview()
+            make.bottom.equalTo(-8)
+        }
+        
+        contentView.addSubview(line)
+        line.snp.makeConstraints { make in
+            make.left.equalTo(3)
+            make.right.equalTo(-3)
+            make.height.equalTo(2)
+            make.bottom.equalToSuperview()
+        }
+        
+        contentView.addSubview(icon)
+        icon.snp.makeConstraints { make in
+            make.center.equalToSuperview()
+            make.width.height.equalTo(24)
+        }
+        
+        
+//
+//        contentView.addSubview(line1);
+ 
+    }
+
+    required init?(coder _: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    func addData(dic:MusicCategoryModel) {
+        icon.isHidden = true
+        titleL.isHidden = true
+        if let title = dic.name, title.count > 0 {
+            titleL.isHidden = false
+            titleL.text = title
+            icon.image = nil
+        }else if dic.id == -1 {
+            icon.isHidden = false
+            titleL.text = ""
+            icon.image = imageInRecordScreenKit(by: "voiceDeleteS")
+        }
+    }
+    
+}

+ 33 - 0
BFRecordScreenKit/Classes/RecordScreen/View/Cell/BFMusicInfoSearchCell.swift

@@ -0,0 +1,33 @@
+//
+//  BFMusicInfoSearchCell.swift
+//  BFRecordScreenKit
+//
+//  Created by Harry on 2022/3/3.
+//
+
+import Foundation
+
+
+class BFMusicInfoSearchCell: BFMuicInfoCell {
+    
+    
+    override func showFunctionBtn() {
+        cutBtn.snp.updateConstraints { make in
+            make.width.equalTo(40)
+        }
+        useBtn.snp.updateConstraints { make in
+            make.width.equalTo(40)
+        }
+        
+    }
+    
+    override func hiddenFunctionBtn() {
+        cutBtn.snp.updateConstraints { make in
+            make.width.equalTo(0)
+        }
+        useBtn.snp.updateConstraints { make in
+            make.width.equalTo(0)
+        }
+    }
+    
+}