I have piano keyboard.When I press the key I want the previous key not to be interrupted before calling func pianoKeyUp.So I created another player in pianoKeyDown.
The problem is: When simultaneously press the key created AudioPlayers is not deleted or simultaneous deletion occurs and gives an error about the missing element in AudioPlayers array and app crashes.What is the better way to play piano sound multiple?
var audioPlayers = [KeyAudio]()
There is a struct for each piano key that init in ViewDidLoad()
in for key in cycle
struct KeyAudio {
let audioPlayer : AVAudioPlayer
var playersArray : [AVAudioPlayer]
init(audioPlayer: AVAudioPlayer) {
self.audioPlayer = audioPlayer
var array = [AVAudioPlayer]()
array.append(audioPlayer)
self.playersArray = array
}
}
ViewDidLoad()
Prepare each player to play and append to audioPlayers
array with init of KeyAudio
for key in 1...61 {
do {
let pianoSoundURL = URL(fileURLWithPath: Bundle.main.path(forResource: "\(key).wav", ofType: nil)!)
let audioPlayer = try AVAudioPlayer(contentsOf: pianoSoundURL, fileTypeHint: nil)
audioPlayer.volume = 0.1
audioPlayer.prepareToPlay()
let player = KeyAudio(audioPlayer: audioPlayer) // init
self.audioPlayers.append(player)
} catch(let error) {
print(error)
}
And I have functions from custom piano view
First - keyDown - triggered when piano key pressed
If player.isPlaying
I create another AudioPlayer
and append it to common array of each note
func pianoKeyDown(_ keyNumber: UInt8) {
let number = Int(keyNumber)
audioPlayers[number].audioPlayer.setVolume(0, fadeDuration: 0.05)
if audioPlayers[number].audioPlayer.isPlaying {
let pianoSoundURL = URL(fileURLWithPath: Bundle.main.path(forResource: "\(number+1).wav", ofType: nil)!)
guard let duplicatePlayer = try? AVAudioPlayer(contentsOf: pianoSoundURL) else { return }
audioPlayers[number].playersArray.append(duplicatePlayer)
duplicatePlayer.prepareToPlay()
duplicatePlayer.currentTime = 0
DispatchQueue.global().async {
duplicatePlayer.play()
duplicatePlayer.setVolume(0.8, fadeDuration: 0.05)
}
} else {
guard let firstTimePlayer = audioPlayers[number].playersArray.first else { return }
firstTimePlayer.currentTime = 0
DispatchQueue.global().async {
firstTimePlayer.play()
firstTimePlayer.setVolume(0.8, fadeDuration: 0.05)
}
}
}
And second - keyUp - when finger is released I stop AudioPlayer
created by first tap, then check if another AudioPlayer
created by next tap
and there is the problem
func pianoKeyUp(_ keyNumber: UInt8) {
let number = Int(keyNumber)
if let firstPlayer = audioPlayers[number].playersArray.first, firstPlayer.isPlaying {
audioPlayers[number].audioPlayer.setVolume(0, fadeDuration: 0.75)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.75, execute: {
DispatchQueue.global().async {
if self.audioPlayers[number].audioPlayer.isPlaying {
self.audioPlayers[number].audioPlayer.stop()
}
}
})
}
let isIndexValid = audioPlayers[number].playersArray.indices.contains(1)
if isIndexValid, audioPlayers[number].playersArray[1].isPlaying {
audioPlayers[number].playersArray[1].setVolume(0, fadeDuration: 0.75)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.75, execute: {
if self.audioPlayers[number].playersArray.indices.contains(1) {
self.audioPlayers[number].playersArray[1].stop()
self.audioPlayers[number].playersArray.remove(at: 1)
}
})