How to Stop AVAudioPlayer - ios

I am making a noiseMaker application for iOS in xCode where there are different buttons that play different sounds. Each sound is given a tag of 0-3 along with each button in the storyboard. And the randomize button has a tag of 4, Here is the code so far:
func play (index: Int) {
if !player.isEmpty && index >= 0 && index < player.count {
player[index].play()
}
if index == 4{
let randomNumber = Int(arc4random_uniform (4) + 0)
player[randomNumber].play()
}
However, what I want is for the current song to stop playing when I press on another button or even on the same button again. I believe the stop AVAudioPlayer code would go right when the function "play" begins so that it stops the player before any other song starts playing.
All help is appreciated. Thanks in advance...

You can check that currently player is playing or not.
Check the playing property returns status of player is playing or not.
if (aPlayer.playing) {
aPlayer.stop()
//Then play again by aPlayer.play()
}
else {
aPlayer.play()
}

Related

How to stop currently playing audio automatically when another play button in different UITableView cells are pressed? - Swift

I am a swift beginner currently working on my first iOS app.
I have a TableViewController which has several cells and each cell contains a play button. When a few play buttons are pressed, a few different audios are played at the same time, but I would like to stop a currently playing audio when another playing play button is pressed.
Here are the sample codes I created.
Please give me an advice. Thank you so much!
The problem is that every single cell has its own independent player. You want just one player, so that you can stop it and start a new sound. For example, make the one player a property of the view controller. Every cell can see it but there is just one.
create an extension for AVPlayer because AVPlayer does not own condition for playing. extension given below.
extension AVPlayer {
var isPlaying: Bool {
return rate != 0 && error == nil
}
}
use it while tap on play like this.
if player?.isPlaying == true {
player?.pause()
player?.seek(to: CMTime(seconds: 0, preferredTimescale: 60))
}
//set up you audio file to player
player?.play()

When I tap the "next" button it pauses the music instead of automatically going to the next song

Hi I recently implemented a play/pause button which works perfectly using MPMusicPlayerController I have now added a "next song" button. But when I click this "next song" button it pauses the music and when I tap the "play" button then it plays the next song. I was wondering if there was a way where as I click the "next song" button it would automatically play the next song instead of pausing it. Heres my code for the 2 buttons:
var musicPlayer = MPMusicPlayerController.systemMusicPlayer
#IBAction func nextMusicTapped(_ sender: UIButton) {
musicPlayer.skipToNextItem()
musicPlayer.prepareToPlay()
musicPlayer.play()
}
#IBAction func playPauseMusicTapped(_ sender: UIButton) {
let state = musicPlayer.playbackState.rawValue
if (state == 1) { //playing
musicPlayer.pause()
musicPlayPauseButton.setImage(UIImage(systemName: "play.fill"), for: .normal)
musicNextButton.isHidden = true
} else if (state == 2) { //paused
musicPlayer.prepareToPlay()
musicPlayer.play()
musicPlayPauseButton.setImage(UIImage(systemName: "pause.fill"), for: .normal)
musicNextButton.isHidden = false
}
}
First off, according to Apple's documentation, skipToNextItem() does the following:
Starts playback of the next media item in the playback queue; or, if the music player is not playing, designates the next media item as the next to be played.
So in other words, if the music player is already playing a song, you shouldn't have to tell it to play the next item, it should automatically start playing. So you wouldn't need the following lines:
musicPlayer.prepareToPlay()
musicPlayer.play()
This also leads me to believe that somehow your music player is being paused prior to that skipToNextItem() being called. Try putting print(musicPlayer.playbackState.rawValue) before line 4. If it prints 2, then your music player seems to pause by clicking the "next song" button, so that would mean something weird is going on with your IBAction connections. I've done this before where I accidentally call two different IBAction methods with the same button (due to an error in setting up the outlets in the first place). In your particular case, you could be accidentally calling both the playPauseMusicTapped (which pauses the music), and the nextMusicTapped() method to switch the songs, but that's just a random thought. Let me know what that playback state before line 4 is, regardless. That would be good to know.

AVAudioPlayer audio not playing when its set and played from different methods

I have an app with different button press sounds. I'm trying to use two functions to set the next button press sound and then play it. The idea is to load the audio file for when the next button is pressed, and then finally play it through a different function when a button is pressed. Using prepareToPlay(), this should make playback faster.
The problem is that when I press a button, I hear 'silence'. I say 'silence' because my speakers actually receive some signal, but its too week to be heard.
Here's my code:
func loadNextButtonPressSound() {
let randomSoundIndex = Int(arc4random_uniform(UInt32(pressSounds.count)))
pressSoundPlayer = try! AVAudioPlayer(contentsOfURL: pressSounds[randomSoundIndex])
pressSoundPlayer.prepareToPlay()
pressSoundPlayer.delegate = self
}
func playButtonPressSound() { // called when a button is pressed
if(StoredSettings.instance.getEnableSoundsSetting()) {
pressSoundPlayer.play()
loadNextButtonPressSound()
}
}
If I instead merge the codes to form this:
internal func playButterPressSound() {
if(StoredSettings.instance.getEnableSoundsSetting()) {
let randomSoundIndex = Int(arc4random_uniform(UInt32(pressSounds.count)))
pressSoundPlayer = try! AVAudioPlayer(contentsOfURL: pressSounds[randomSoundIndex])
pressSoundPlayer.play()
}
}
It starts working (i.e. I hear button presses), but it defeats the purpose of using prepareToPlay() to preload the next sound.
Any suggestions on how to make the first code above work ?

Swift AVAudioPlayer Restart from Beginning

I have an AVAudioPlayer stored in a property called "player". When the user clicks a button, it triggers this code:
#IBAction func restartPressed(sender: AnyObject) {
player.play()
}
The problem is happening when the user clicks the button twice in a row very quickly.
If the sound from the first click is still playing, it seems like the second call is ignored.
Instead, I'd like to either:
a) restart the player from the beginning when the button is clicked a second time; or
b) have two "instances" of the sound playing at the same time.
How would I go about achieving either or both of these options?
Answering to "either" part (rather than "both") of these options: (a) how to stop and play the sound from beginning:
#IBAction func restartPressed(sender: AnyObject) {
if player.playing {
player.pause()
}
player.currentTime = 0
player.play()
}
Regarding (b): from the Language Ref for AVAudioPlayer:
Using an audio player you can:
...
Play multiple sounds simultaneously, one sound per audio player, with precise synchronization.
So you'd need two separate players to simultaneously (off-sync) play two separate sounds, even if both players use the same sound. I'd stick with (a).
dfri's answer updated for Swift 4
#IBAction func restartPressed(sender: UIButton) {
if negativePlayer.isPlaying
{
negativePlayer.pause()
}
negativePlayer.currentTime = 0
negativePlayer.play()
}
player.seek(to: .zero)
player.play()

Notification when AVQueuePlayer rewinds to the beginning of an item

I have 5 AVPlayerItems in my AVQueuePlayer, which is set to AVPlayerActionAtItemEndAdvance. I hit play on my UI, and play the first, the second and then start playing the third. Then I hit my rewind button. What I want to happen is that the third video rewinds to its start, and I then get a notification that allows me to stop. What I'm seeing is that I get a status of ready to play for the 4th item, followed by a current item changed to the 4th item - then the 4th item plays.
Why does the 4th item become the current item after the 3rd item has rewound to its start
Is the only way I can stop this to set the player to not auto advance (AVPlayerActionAtItemEndPause), observe the end of each item, and hope I get an "end of play" notification for the rewinding of the 3rd item as well as when it plays to its end naturally. Then in my end observer code, I can check the rate of the player, and if rewinding, not advance to the next item.
The way I handled this was in the begin seeking code, set the actionAtItemEnd to AVPlayerActionAtItemEndNone, and then reset it back to AVPlayerActionAtItemEndAdvance when the seeking ends. On iOS6, it appears that one can seek past the start of the track. In the "end seeking" code, I reset the rate and current time before beginning normal playback.
The following method is called by a long press gesture recognizer.
- (IBAction)fastRewind:(id)sender
{
UIGestureRecognizer *recog = (UIGestureRecognizer*)sender;
if (recog.state == UIGestureRecognizerStateBegan) {
if (_player.rate == 1) {
NSLog(#"fastRewind begin\n%#", sender);
_player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
_player.rate = -2;
}
} else if (recog.state != UIGestureRecognizerStateChanged) {
// Ended/Canceled
NSLog(#"fastRewind end\n%#", sender);
if (_player.rate < 0) {
_player.rate = 0;
if (CMTimeGetSeconds(_player.currentTime) < 0) {
[_player seekToTime:CMTimeMake(0, 1)];
}
_player.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
_player.rate = 1;
}
}
}
One may wish to speed up the seek if it is held for a longer period of time.

Resources