AVAudioPlayerNode playing unexpectedly multiple times

£可爱£侵袭症+ 提交于 2019-12-13 03:51:12

问题


On top of a background audio file, I want to play a sequence of 8 audio files.
By touching a segmented control, the user has the possibility to choose which sounds will be played on top of the background file.

 playCountOneAndFive(index: index)   
 playCountOneToSeven(index: index)
 playCountOneToEight(index: index)

The problem I am facing is that when I try to switch playing from one function to another, some of the nodes end up playing simultaneously several times. For example instead of playing 1, 2, 3, it plays 1,1,2,2,2,3. and so on.
It looks like some of the nodes continue playing, even though the user has touched to another segment on the segmented control.
Here's a video showing the app in action https://youtu.be/HGm1hJX1Oiw

//  AudioPlayer.swift


import Foundation
import AVFoundation


protocol CustomAudioPlayerDelegate: class {
    func present(beatCount: Int)
    func present(currentTimeOfPlayer: CMTime, songDuration:CMTime)
}

class CustomAudioPlayer {


    var playStepOneAndFive: Bool = true
    var playStepOneToSeven: Bool = false
    var playStepOneToEight: Bool = false

    var delegate: CustomAudioPlayerDelegate?

    var playerRate: Float
    var soundForCountIsOn:Bool = true //initially, sound must be played for all counts
    var timeSincePlayerStarted: TimeInterval!
    var timeIndex = 0 // keeps track of the index of the item transversed within `timesToTransverse`  by `player?.addBoundaryTimeObserver`

    var audioNodeIndex = 0 //keeps track of which countdown file should be played from topAudioFiles

    var topAudioFiles: [AVAudioFile] = []
    var engine:AVAudioEngine

    var topAudioAudioNodes = [AVAudioPlayerNode]()
    var mixer: AVAudioMixerNode

    var urls: [URL] = []
    var player: AVPlayer!
    var timesToTransverse = [NSValue]() //contains timeValues in seconds such as ["1.54",2.64, 67.20]. These mark the time when the corresponding count down file should be played over the background file


    fileprivate var timeObserverToken: Any?//for addBoundaryTimeObserver

    init (backgroundURL: URL, urls: [URL] = [], timesToTransverse: [NSValue], newPlayer: AVPlayer, playerRate:Float) {

        self.urls = urls
        self.topAudioFiles = urls.map { try! AVAudioFile(forReading: $0) }
        self.timesToTransverse = timesToTransverse
        self.player = newPlayer
        self.playerRate = playerRate
        self.engine = AVAudioEngine()
        self.mixer = AVAudioMixerNode()

        self.engine.attach(mixer)
        self.engine.connect(mixer, to: engine.outputNode, format: nil)

        initTopAudioNodes()
        try! engine.start()
    }


    func initTopAudioNodes() {
        for _ in topAudioFiles {
            topAudioAudioNodes += [AVAudioPlayerNode()]
        }

        for node in topAudioAudioNodes {
            engine.attach(node)
            engine.connect(node, to: mixer, format: nil)
        }
    }


    func playWithAudioPlayerAndNodes() {

        player.playImmediately(atRate: self.playerRate)


     timeObserverToken =  player.addBoundaryTimeObserver(forTimes: timesToTransverse, queue: DispatchQueue.main) {
        [weak self] in

        guard let strongSelf = self else {return}

        //using the reminder operator get the index of the file to be played
           let index = strongSelf.audioNodeIndex % strongSelf.topAudioAudioNodes.count
           let node = strongSelf.topAudioAudioNodes[index]

            node.scheduleFile(strongSelf.topAudioFiles[index], at: nil, completionHandler: nil)

        func playCountOneAndFive(index: Int) {

            switch index {

            case 0: node.play()
            case 1: node.pause()
            case 2: node.pause()
            case 3: node.pause()
            case 4: node.play()
            case 5: node.pause()
            case 6: node.pause()
            case 7: node.pause()
            default:
                printsNow(message: "unexpected case in playCountOneAndFive with index \(index)")
            }
        }


        func playCountOneToSeven(index: Int) {

            switch index {

            case 0: node.play()
            case 1: node.play()
            case 2: node.play()
            case 3: node.pause()
            case 4: node.play()
            case 5: node.play()
            case 6: node.play()
            case 7: node.pause()
            default:
                printsNow(message: "unexpected case in playCountOneToSeven with index \(index)")
            }
        }

            func playCountOneToEight(index: Int) {
                node.play()
            }


        //if sound is ON, only one of the three functions will be called
        if strongSelf.soundForCountIsOn == true {

            if strongSelf.playStepOneAndFive == true &&
                strongSelf.playStepOneToSeven == false &&
                strongSelf.playStepOneToEight == false {
                    playCountOneAndFive(index: index)

            }else if strongSelf.playStepOneToSeven == true &&
                      strongSelf.playStepOneAndFive == false &&
                      strongSelf.playStepOneToEight == false {
                        playCountOneToSeven(index: index)

            } else if strongSelf.playStepOneToEight == true &&
                        strongSelf.playStepOneToSeven == false &&
                        strongSelf.playStepOneAndFive == false {
                         playCountOneToEight(index: index)
            }

        }else {
            node.pause()
        }






        //reset or increment audioNodeIndex to obtain the correct index when searching the countdown file to play
        //There is a total of 8 audio files to be played over the background file
        if strongSelf.audioNodeIndex == 7 {
            strongSelf.audioNodeIndex = 0
        }else {
            strongSelf.audioNodeIndex += 1
        }


        //because there are no time signature changes, we can simply increment  timeIndex with + 1 every time `addBoundaryTimeObserver` completion handler is called and subscript timesToTransverse with timeIndex in order to get the subsequent timeInSeconds
        guard strongSelf.timeIndex < strongSelf.timesToTransverse.count else {return}


        //use reminder operator to determine the beat count
        let beat = (strongSelf.timeIndex + 1) % 8 == 0 ? 8 : ((strongSelf.timeIndex + 1) % 8)

        print("Beat would be: ", beat)
         strongSelf.delegate?.present(beatCount: beat)

        /*
         0: (0 + 1) % 8 = 1
         1: (1 + 1) % 8 = 2
         6: (6 + 1) % 8 = 7
         7: (7 + 1) % 8 = 0
         */

        strongSelf.timeIndex += 1

        }
    }



}//end class

来源:https://stackoverflow.com/questions/56126570/avaudioplayernode-playing-unexpectedly-multiple-times

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!