123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- //
- // 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: ((_ startTime: Float64, _ endTime: Float64, _ bgmData: PQVoiceModel?) -> Void)?
- let normalMargin: CGFloat = cDefaultMargin * 2
- // 记录设置的起点, 在点击确认后会设置 bgmdata 的 开始时间为本值。
- var startCMTime: CMTime = .zero // 开始时间
- // 播放音乐
- 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
- }
- 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")
- 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)
- }
- }
- @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 - itemWidth - 3
- let minX = cDefaultMargin * 2 - (cutRemindView.frame.width / 2) - 3
- 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.white.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: "#389AFF").cgColor
- (progressImage.layer.sublayers?[tempIndex] as? CAShapeLayer)?.setNeedsDisplay()
- }
- }
- }
- }
- func show() {
- isHidden = false
- }
- @objc func hidden() {
- isHidden = true
- avPlayer.pause()
- }
- @objc func cancelAction() {
- hidden()
- }
- @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!(0, 0, bgmData)
- }
- }
- }
|