Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audio duplication error #62

Open
dinhthiet2702 opened this issue Jul 13, 2022 · 9 comments
Open

Audio duplication error #62

dinhthiet2702 opened this issue Jul 13, 2022 · 9 comments
Labels
question Further information is requested

Comments

@dinhthiet2702
Copy link

dinhthiet2702 commented Jul 13, 2022

I'm using your library but still there is a bug where when pressing next repeatedly or once, sometimes the same song plays 2 times in parallel.

@dinhthiet2702 dinhthiet2702 changed the title Control center support and fix duplicate music Audio duplication error Jul 13, 2022
@dinhthiet2702
Copy link
Author

RPReplay_Final1657727160.MP4

@dinhthiet2702
Copy link
Author

var songs:[Song] = [] {
didSet {
if index >= songs.count{
index = 0
}
// let metaData = RxMusicPlayerItem(url: <#T##URL#>, meta: RxMusicPlayerItem.Meta(duration: <#T##CMTime?#>, lyrics: <#T##String?#>, title: <#T##String?#>, album: <#T##String?#>, artist: <#T##String?#>, artwork: <#T##UIImage?#>, skipDownloading: <#T##Bool#>))
let items = songs.map({ RxMusicPlayerItem(url: URL(string: $0.filename?.queryURL ?? "")!) })
self.player = RxMusicPlayer(items: Array(items[index ..< songs.count]))

        setupPlayer()
    }
}

var index = 0 {
    didSet{

// setupSong()
}
}

var player: RxMusicPlayer?

public override func viewDidLoad() {
    super.viewDidLoad()
    
    setupUI()

// setupRemoteCommandCenter()

}

func setupPlayer() {

    guard let player = player else {
        return
    }



    // 2) Control views
    player.rx.canSendCommand(cmd: .play)
        .do(onNext: { [weak self] canPlay in
            self?.isPlaying = canPlay
        })
        .drive()
        .disposed(by: disposeBag)

    player.rx.canSendCommand(cmd: .next)
        .drive(btnNext.rx.isEnabled)
        .disposed(by: disposeBag)

    player.rx.canSendCommand(cmd: .previous)
        .drive(btnPre.rx.isEnabled)
        .disposed(by: disposeBag)

    player.rx.canSendCommand(cmd: .seek(seconds: 0, shouldPlay: false))
        .drive(onTime.rx.isUserInteractionEnabled)
        .disposed(by: disposeBag)

    player.rx.currentItemTitle()
        .drive(lbName.rx.text)
        .disposed(by: disposeBag)

    player.rx.currentItemArtwork()
            .drive(onNext: {[weak self] image in
                self?.imv.image = image
                self?.imv.image = image
            })
            .disposed(by: disposeBag)

// player.rx.currentItemLyrics()
// .distinctUntilChanged()
// .do(onNext: { [weak self] _ in
// self?.tableView.reloadData()
// })
// .drive(lyricsLabel.rx.text)
// .disposed(by: disposeBag)

    player.rx.currentItemRestDurationDisplay()
        .map {
            guard let rest = $0 else { return "--:--" }
            return "-\(rest)"
        }
        .drive(lbEnd.rx.text)
        .disposed(by: disposeBag)

    player.rx.currentItemTimeDisplay()
        .drive(lbStart.rx.text)
        .disposed(by: disposeBag)

    player.rx.currentItemDuration()
        .map { Float($0?.seconds ?? 0) }
        .do(onNext: { [weak self] in
            self?.onTime.maximumValue = $0
        })
        .drive()
        .disposed(by: disposeBag)

    let seekValuePass = BehaviorRelay<Bool>(value: true)
    player.rx.currentItemTime()
        .withLatestFrom(seekValuePass.asDriver()) { ($0, $1) }
        .filter { $0.1 }
        .map { Float($0.0?.seconds ?? 0) }
        .drive(onTime.rx.value)
        .disposed(by: disposeBag)
    onTime.rx.controlEvent(.touchDown)
        .do(onNext: {
            seekValuePass.accept(false)
        })
        .subscribe()
        .disposed(by: disposeBag)
    onTime.rx.controlEvent(.touchUpInside)
        .do(onNext: {
            seekValuePass.accept(true)
        })
        .subscribe()
        .disposed(by: disposeBag)

    player.rx.currentItemLoadedProgressRate()
        .drive(onTime.rx.playableProgress)
        .disposed(by: disposeBag)

    player.rx.shuffleMode()
        .do(onNext: { [weak self] mode in
            self?.isShuffled = mode == .songs
        })
        .drive()
        .disposed(by: disposeBag)

    player.rx.repeatMode()
        .do(onNext: { [weak self] mode in
            var title = ""
            switch mode {
            case .none: title = "Repeat"
            case .one: title = "Repeat(All)"
            case .all: title = "No Repeat"
            @unknown default:
                break
            }
            self?.btnRepeat.setTitle(title, for: .normal)
        })
        .drive()
        .disposed(by: disposeBag)

    player.rx.playerIndex()
        .do(onNext: { index in
            if index == player.queuedItems.count - 1 {
                // You can remove the comment-out below to confirm the append().
                // player.append(items: items)
            }
        })
        .drive()
        .disposed(by: disposeBag)

    // 3) Process the user's input
    let cmd = Driver.merge(
        btnPlay.rx.tap.asDriver().map { [weak self] in
            if self?.isPlaying == true {
                return RxMusicPlayer.Command.play
            }
            return RxMusicPlayer.Command.pause
        },
        btnNext.rx.tap.asDriver().map { RxMusicPlayer.Command.next },
        btnPre.rx.tap.asDriver().map { RxMusicPlayer.Command.previous },
        onTime.rx.controlEvent(.valueChanged).asDriver()
            .map { [weak self] _ in
                RxMusicPlayer.Command.seek(seconds: Int(self?.onTime.value ?? 0),
                                           shouldPlay: false)
            }
            .distinctUntilChanged()
    )
    .startWith(.prefetch)
    .debug()

    // You can remove the comment-out below to confirm changing the current index of music items.
    // Default is 0.
    // player.playIndex = 1

    player.run(cmd: cmd)
        .do(onNext: { status in
            UIApplication.shared.isNetworkActivityIndicatorVisible = status == .loading
        })
        .flatMap { [weak self] status -> Driver<()> in
            guard let weakSelf = self else { return .just(()) }

            switch status {
            case let RxMusicPlayer.Status.failed(err: err):
                print(err)
                return Wireframe.promptOKAlertFor(src: weakSelf,
                                                  title: "Error",
                                                  message: err.localizedDescription)

            case let RxMusicPlayer.Status.critical(err: err):
                print(err)
                return Wireframe.promptOKAlertFor(src: weakSelf,
                                                  title: "Critical Error",
                                                  message: err.localizedDescription)
            default:
                print(status)
            }
            return .just(())
        }
        .drive()
        .disposed(by: disposeBag)

    btnShuffe.rx.tap.asDriver()
        .drive(onNext: {
            switch player.shuffleMode {
            case .off: player.shuffleMode = .songs
            case .songs: player.shuffleMode = .off
            }
        })
        .disposed(by: disposeBag)

    btnRepeat.rx.tap.asDriver()
        .drive(onNext: {
            switch player.repeatMode {
            case .none: player.repeatMode = .one
            case .one: player.repeatMode = .all
            case .all: player.repeatMode = .none
            }
        })
        .disposed(by: disposeBag)

// rateButton.rx.tap.asDriver()
// .flatMapLatest { [weak self] _ -> Driver<()> in
// guard let weakSelf = self else { return .just(()) }
//
// return Wireframe.promptSimpleActionSheetFor(
// src: weakSelf,
// cancelAction: "Close",
// actions: PlaybackRateAction.allCases.map {
// ($0.rawValue, player.desiredPlaybackRate == $0.toFloat)
// })
// .do(onNext: { [weak self] action in
// if let rate = PlaybackRateAction(rawValue: action)?.toFloat {
// player.desiredPlaybackRate = rate
// self?.rateButton.setTitle(action, for: .normal)
// }
// })
// .map { _ in }
// }
// .drive()
// .disposed(by: disposeBag)

// appendButton.rx.tap.asDriver()
// .do(onNext: {
// let newItems = Array(items[4 ..< 6])
// player.append(items: newItems)
// })
// .drive(onNext: { [weak self] _ in
// self?.appendButton.isEnabled = false
// })
// .disposed(by: disposeBag)

// changeButton.rx.tap.asObservable()
// .flatMapLatest { [weak self] _ -> Driver<()> in
// guard let weakSelf = self else { return .just(()) }
//
// return Wireframe.promptSimpleActionSheetFor(
// src: weakSelf,
// cancelAction: "Close",
// actions: items.map {
// ($0.url.lastPathComponent, player.queuedItems.contains($0))
// })
// .asObservable()
// .do(onNext: { action in
// if let idx = player.queuedItems.map({ $0.url.lastPathComponent }).firstIndex(of: action) {
// try player.remove(at: idx)
// } else if let idx = items.map({ $0.url.lastPathComponent }).firstIndex(of: action) {
// for i in (0 ... idx).reversed() {
// if let prev = player.queuedItems.firstIndex(of: items[i]) {
// player.insert(items[idx], at: prev + 1)
// break
// }
// if i == 0 {
// player.insert(items[idx], at: 0)
// }
// }
// }
//
// self?.appendButton.isEnabled = !(player.queuedItems.contains(items[4])
// || player.queuedItems.contains(items[5]))
// })
// .asDriver(onErrorJustReturn: "")
// .map { _ in }
// }
// .asDriver(onErrorJustReturn: ())
// .drive()
// .disposed(by: disposeBag)
}

public override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    imv.radius(radius: 10)
    btnShuffe.centerVertically(padding: 8)
    btnLike.centerVertically(padding: 8)
    btnRepeat.centerVertically(padding: 8)
}

func setupUI() {

    setupContraint()
    vStack.setCustomSpacing(100, after: vStack.arrangedSubviews[0])

    customBtnPre.setImage(Images.PREMUSIC_POP.imageOriginal, for: .normal)

    customBtnPause.setImage(Images.PAUSE_POP.imageOriginal, for: .normal)
    pauseBtn = UIBarButtonItem(customView: customBtnPause)

    customBtnNext.setImage(Images.NEXT_POP.imageOriginal, for: .normal)
    nextBtn = UIBarButtonItem(customView: customBtnNext)

    popupItem.barButtonItems = [preBtn, pauseBtn, nextBtn]

}

func setupContraint() {
    view.addSubview(imvBg)
    view.addSubview(vStack)
    [stackView1, stackView2, stackView3, stackView4].forEach {vStack.addArrangedSubview($0)}
    NSLayoutConstraint.activate([
        imvBg.topAnchor.constraint(equalTo: view.topAnchor, constant: 0),
        imvBg.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0),
        imvBg.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0),
        imvBg.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0),
        vStack.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 70),
        vStack.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 15),
        vStack.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -15)
    ])
    [imv, lbName].forEach {stackView1.addArrangedSubview($0)}
    [onTime, stackView5].forEach {stackView2.addArrangedSubview($0)}
    [lbStart, lbEnd].forEach {stackView5.addArrangedSubview($0)}
    [btnPre, btnPlay, btnNext].forEach {stackView3.addArrangedSubview($0)}
    [btnShuffe, btnRepeat, btnLike].forEach {stackView4.addArrangedSubview($0)}
    onTime.widthAnchor.constraint(equalTo: vStack.widthAnchor, multiplier: 1).isActive = true

}

@yoheimuta
Copy link
Owner

@dinhthiet2702 Thank you for reaching out.
Can you reproduce it when you use our example projects hosted under this repository?

Because I couldn't encounter the issues while playing example projects.
And see also https://github.com/yoheimuta/RxMusicPlayer#bug-report.

@yoheimuta yoheimuta added the question Further information is requested label Jul 14, 2022
@dinhthiet2702
Copy link
Author

I fixed it, does the current library support playing songs at specified index?
ex: click cell tableview -> Play music at indexPath.row

@rastaman111
Copy link

Yes

@dinhthiet2702
Copy link
Author

Yes

Can you show me that function?

@rastaman111
Copy link

Yes

Can you show me that function?

player?.playIndex = playIndex

@dinhthiet2702
Copy link
Author

Yes

Can you show me that function?

player?.playIndex = playIndex

thanks sir hehehe

@rastaman111
Copy link

👌🏼👍🏼😃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants