iosswiftavaudioengineavaudioplayernode

AVAudioengine play / loop audio , multiple buttons


I have this button to play and loop a wav. But what if I have a second button with another loop? I want this second loop to start playing once pressed, however, the first loop must finish his 'round'. And vice versa (play and loop the second loop, press button first loop and it takes over)

 @IBAction func playButtonTapped(_ sender: Any) {



    guard let filePath: String = Bundle.main.path(forResource: "25loop110", ofType: "wav") else{ return }
          print("\(filePath)")
          let fileURL: URL = URL(fileURLWithPath: filePath)
          guard




              let audioFile = try? AVAudioFile(forReading: fileURL) else{ return }

          let audioFormat = audioFile.processingFormat
          let audioFrameCount = UInt32(audioFile.length)
          guard let audioFileBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: audioFrameCount)  else{ return }
          do{
              try audioFile.read(into: audioFileBuffer)

              timeShift.rate = adjustedBpm/bpm
              playerNode.scheduleFile(audioFile, at: nil, completionHandler: nil)


          } catch{
              print("over")
          }

          try? audioEngine.start()
          playerNode.play()
          playerNode.scheduleBuffer(audioFileBuffer, at: nil, options:.loops,completionHandler: nil)

    }

Solution

  • You can handle this behavior by using the completionHandler parameter of .scheduleBuffer.

    For example, you could do something like this:

    var nextAudioFilePath: String  
    var isPlaying: Bool = false
    
    @IBAction func playLoopA() {  
       guard let path = Bundle.main.path(forResource: "audioFileA", ofType: "wav") else { return }  
       nextAudioFilePath = path  
       guard !isPlaying else { return }  
       play()
    }  
    
    @IBAction func playLoopB() {  
       guard let path = Bundle.main.path(forResource: "audioFileB", ofType: "wav") else { return }  
       nextAudioFilePath = path  
       guard !isPlaying else { return }  
       play()
    }  
    
    private func play() {  
       let fileURL = URL(fileURLWithPath: nextAudioFilePath)  
       ...  
       playerNode.scheduleBuffer(audioFileBuffer, at: nil, options: [], completionHandler: { [weak self] in   
          self?.play()
       })
    }