问题
I created the 3 UIButtons on the Main StoryBoard.
When I press "Button1" or "Button2" each plays an audio file ( "player 1" , "player 2".) and while the audio is playing the UIButton is set to selected
.
I would like "Button3" to implement multiple functions in a row. That means when I press "Button 3", "player 3" should be played first, after that "player 1" and after that "player 2".
However, when I press "Button3", "player 1" and "player 2" are played at same time as there is no delay for the previous to finish.
I found out setting the delegate of the AVAudioPlayer or using AVQueuePlayer would solve the problem, but I find it difficult to make changes.
fileprivate var player1:AVAudioPlayer?
fileprivate var player2:AVAudioPlayer?
fileprivate var player3:AVAudioPlayer?
@IBAction func pushButton1(sender: UIButton) {
audioPlayer1(url: url1)
}
@IBAction func pushButton2(sender: UIButton) {
audioPlayer2(url: url2)
}
@IBAction func pushButton3(_ sender: UIButton) {
audioPlayer3(url: url1, url2: url2, url3: url3)
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
if (player === player1) {
yourButton1.isSelected = false
} else if (player === player2) {
yourButton2.isSelected = false
} else if (player === player3) {
//"player 1" and "player 2" are played at same time
yourButton3.isSelected = false
player1!.play()
yourButton1.isSelected = true
player2!.play()
yourButton2.isSelected = false
}
}
func audioPlayer1(url: URL) {
do {
try player1 = AVAudioPlayer(contentsOf:url)
player1!.play()
yourButton1.isSelected = true
player1!.delegate = self
} catch {
print(error)
}
}
func audioPlayer2(url: URL) {
do {
try player2 = AVAudioPlayer(contentsOf:url)
player2!.play()
yourButton2.isSelected = true
player2!.delegate = self
} catch {
print(error)
}
}
func audioPlayer3(url: URL, url2: URL, url3: URL) {
do {
try player3 = AVAudioPlayer(contentsOf:url3)
try player1 = AVAudioPlayer(contentsOf: url1)
try player2 = AVAudioPlayer(contentsOf: url2)
player3!.play()
yourButton3.isSelected = true
player3!.delegate = self
player1!.delegate = self
} catch {
print(error)
}
}
}
Changed code
var queue = AVQueuePlayer()
var items = [AVPlayerItem]()
override func viewDidLoad() {
super.viewDidLoad()
player1?.delegate = self
player2?.delegate = self
player3?.delegate = self
let asset1 = AVPlayerItem(url: url1)
let asset2 = AVPlayerItem(url: url2)
let asset3 = AVPlayerItem(url: url3)
let asset4 = AVPlayerItem(url: url4)
items = [asset1, asset2, asset3, asset4]
queue = AVQueuePlayer(items: items)
for item in queue.items() {
NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidReachEnd(notification:)), name: .AVPlayerItemDidPlayToEndTime, object: item)
}
}
@IBAction func pushButton3(_ sender: UIButton) {
//audioPlayer3(url: url1, url2: url2)
sender.isSelected = true
queue.play()
}
func playerItemDidReachEnd(notification: NSNotification) {
if notification.object as? AVPlayerItem == items[0] {
yourButton3.isSelected = false
yourButton1.isSelected = true
}
if notification.object as? AVPlayerItem == items[1] {
yourButton1.isSelected = false
yourButton2.isSelected = true
}
if notification.object as? AVPlayerItem == items[2] {
yourButton2.isSelected = false
yourButton4.isSelected = true
//yourButton1.isSelected = true
}
if notification.object as? AVPlayerItem == items[3] {
yourButton4.isSelected = false
print("item 3")
}
回答1:
EDIT: This is the method for AVAudioPlayer
So basically:
1: Add only one player, remove the rest of the players.
2: Create an NSMutableArray
with your objects that you want to play.
3: Depending on what button gets pressed, you can re-arrange the NSMutableArray
objects to set the correct order as you wish. (Simply recreate the array if you want to make it easy) And start playing the firstObject
(url) in the array adding it accordingly to your player.
4: When the player finished playing, you can start playing the next object adding it to your player from the NSMutableArray
the same way as done above.
Following above steps will avoid having multiple players playing simultaniously, together with other benefits (like avoid loading multiple URLs at the same time etc)
You may need some variable to check the currentPlayingObject to determine which one is next, maybe an int
, and check that you don't try to load a new item from the array if it is the last item playing, to avoid crash on accessing objectAtIndex that does not exist.
来源:https://stackoverflow.com/questions/42324487/playing-multiple-audio-files-using-avaudioplayer