Play sound repeatedly when tapping a button - ios

I am trying to add sound to my app. I would like it for when I tap a button, it plays a quick sound. However, when the button is tapped quickly and repeatedly, the sound does not work as well (It only plays 1 or 2 times when I tap the button 5 or 6 times). Here is my code in the button
player.play()
I have this outside
var player = AVAudioPlayer()
let audioPath = NSBundle.mainBundle().pathForResource("illuminati", ofType: "wav")
Viewdidload:
do {
try player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: audioPath!))
} catch {}
How can I play the sound repeatedly better? Thanks.

The problem is that you are calling the play multiple time before the previous call finished. You need to keep track of how many times the user click the button and the play song one by one.
This is what you can do:
Use an integer in you class to keep track of number of times that the button is clicked
var numClicks = 0
var buttonClickTime:NSDate? = nil // The last time when the button is clicked
#IBAction func yourbuttonclickfunction() {
numClicks++;
buttonClickTime = NSDate()
player.play()
}
Register the delegate of AVAudioPlayerDelegate
do {
try player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: audioPath!))
// Add this
player.delegate = self
} catch {}
In the delegate function, play the song again when the previous one reach the end:
optional func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer,
successfully flag: Bool)
{
if --numClicks > 0
{
let now = NSDate()
let duration = now.timeIntervalSinceDate(buttonClickTime!)
// If the button click was less than 0.5 seconds before
if duration < 0.5
{
// Play the song again
player.play();
}
}
}

The reason your sound only plays a few time is because the song plays until finished or until you stop it, even if you press the button multiple times in succession.
You could just stop the music manually, so before you press a button that plays a sound you say
player.stop()
and than
player.play()
Is that helping?

Related

Play audio from "paused" point after restarting the track with AVAudioPlayer

I'm working on a music player in Swift. The user can play a track, pause the track, or move a slider to choose a place to start. These three functions work.
However, if a user plays a track, listens for 1:00, and hits pause: when they click play again, it will restart the track from 0:00 instead of respecting where they paused.
I am using AVAudioPlayer as a media player -- according to the documentation, .play() allows you to specify a start-point using atTime.
So I tried to use atTime and have it start where the slider is -- if a listener listens for 1:00, I want atTime to dynamically pick that up. I try to establish this time value in a var.
class MusicViewController: UIViewController {
// removed imports and viewdidload to condense
#IBAction func playDownload(_ sender: Any) {
// removed file download code to condense
do {
audioPlayer = try AVAudioPlayer(contentsOf: destinationUrl)
guard let player = audioPlayer else { return }
//here's where I try to capture a time value from the slider's progress
let checkTime = TimeInterval(Slider.value)
player.prepareToPlay()
player.play(atTime: checkTime)
} catch let error {
print(error.localizedDescription)
}
// updates slider with progress
Slider.value = 0.0
Slider.maximumValue = Float((audioPlayer?.duration)!)
audioPlayer.play()
timer = Timer.scheduledTimer(timeInterval: 0.0001, target: self, selector: #selector(self.updateSlider), userInfo: nil, repeats: true)
}
#IBAction func pause(_ sender: Any) {
if audioPlayer.isPlaying {
audioPlayer.pause()
} else {
audioPlayer.play()
}
}
#IBOutlet var Slider: UISlider!
#objc func updateSlider() {
Slider.value = Float(audioPlayer.currentTime)
print("Changing works")
}
}
So I am trying to capture progress and have .play respect that -- this code will run, but it does not work. No errors but after hitting play, it will go to the end of the track immediately now. No good.
I am new to Swift - any idea where I went wrong?

Music playing over the song when i return to the original VC

I am using xcode 9 and swift 4 for my app. In my app i have music playing in the viewDidLoad. When i exit the view controller to go to another View, it continues to play like it should. How ever, when i return to that view controller the song starts to play again. This song is overlapping the song that first loaded. Do you guys have any ideas on how to stop this from happening?
do
{
let audioPath = Bundle.main.path(forResource: "APP4", ofType: "mp3")
try player = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL)
}
catch
{
//catch error
}
let session = AVAudioSession.sharedInstance()
do
{
try session.setCategory(AVAudioSessionCategoryPlayback)
}
catch
{
}
player.numberOfLoops = -1
player.play()
It starts playing again, because your viewDidLoad is called again, which asks it to play it again. A simplest fix would be to keep a static bool variable to keep track if you have already made this call.
static var isMusicPlaying: Bool = false
In your viewDidLoad, you can put code before the code that calls the play.
guard !isMusicPlaying else {
return
}
isMusicPlaying = true

make play and stop music with same button swift

How to make music play and stop with single button just like in itunes.When the button is pressed and same button pressed again should stop the music.
var audioplayer = AVaudioPlayer
#IBAction func play(_ sender: Any) {
do
{
audioPlayer = try AVAudioPlayer (contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "bensound", ofType: "mp3")!))
audioPlayer.prepareToPlay()
audioPlayer.play()
audioPlayer.stop()
}
catch {
print ("error")
}
}
Obviously, this won't work:
audioPlayer.prepareToPlay()
audioPlayer.play() // this will just start playing the music and immediately stop it.
audioPlayer.stop()
You need an if statement and a variable indicating whether the music is playing.
Luckily, the variable that I was talking about already exists! You don't even need to declare it yourself! It is isPlaying, defined in AVAudioPlayer.
We just need to write the if statement. It is pretty simple,
If the music is playing, stop it, otherwise, start it
if audioplayer.isPlaying {
audioplayer.play()
} else {
audioplayer.stop()
}

Swift 3 Game Music Play

I have a single view application with a few view each having their own view controller class. I also have a game game and a game view controller, I was wondering how I could play a game music starting from the start screen view controller and have it continue to play through out all of my views and game scene? So that way I don't have to say play when x-screen is loaded so it doesn't restart every time the player changes views.
You can play the music form your AppDelegate. Put this code in your applicationDidFinishLaunching()
let url = Bundle.main.url(forResource: "soundName", withExtension: "mp3")!
var player: AVAudioPlayer!
do {
player = try AVAudioPlayer(contentsOf: url)
guard let player = player else { return }
player.prepareToPlay()
player.play()
} catch let error {
print(error.localizedDescription)
}
This will play the music as soon as your app is launched and continue until told to stop or the app closes.

Resume sound in AVAudioPlayer with swift

I am trying to play a clip of sound that can be paused, resumed or stopped. My pause button works, and the action function for this button is shown below. However I cannot get my resume function to work. When I press the resume button no audio is played. I have read around online and the only tips I can find are to use the prepareToPlay() function and to set shortStartTimeDelay to a value greater than 0.0. I have tried both of these to no avail.
timeAtPause is a global variable of type NSTimeInterval
The action function for the pause button is as follows:
#IBAction func pauseAllAudio(sender: UIButton) {
timeAtPause = audioPlayer.currentTime
audioPlayer.pause()
}
The action function for the resume button is as follows:
#IBAction func resumeAllAudio(sender: UIButton) {
let shortStartDelay = 0.01
audioPlayer.prepareToPlay()
audioPlayer.playAtTime(timeAtPause + shortStartDelay)
}
Any tips on how to resume the audio would be really appreciated.
Thank you for your time.
You should not use playAtTime() to resume the AVAudioPlayer, as documentation states:
Plays a sound asynchronously, starting at a specified point in the
audio output device’s timeline.
and
Use this method to precisely synchronize the playback of two or more
AVAudioPlayer objects.
And, even if you use it, it should be used in conjunction with deviceCurrentTime plus the time in seconds to have the delay. In one word, it's not meant to be used to resume the paused player. Instead, just use play() to resume the playback.
You probably did what I did. Upon calling my play() function, I created a new player every time:
// The Wrong Way
#IBAction func playAction(sender: AnyObject) {
// do and catch omitted for brevity
player = try AVAudioPlayer(contentsOf: goodURL)
player.play()
}
However this should be done at some other initialization time, such as when you have the user choose the file to play.
Or, if you're hard-coding the file to play, you could initialize at an earlier time, such as viewDidLoad().
Then, once your AVAudioPlayer is initialized, you can call .play() and pause() on it and they will work correctly. And .play() will resume after a pause.
To simply pause and restart at the same point in the audio file, the following works:
#IBAction func playAction(sender: AnyObject) {
player.play()
}
#IBAction func pauseAction(sender: AnyObject) {
player.pause()
}
I found these to be helpful. If you are doing this in Swift, I did this a little differently. I created an AudioPlayer class after importing AVFoundation and used these two functions to pause and resume the music. In the SwiftUI call, I used an #State boolean that toggled between the button states. Here is the code that I used:
import AVFoundation
class MusicPlayer {
static let shared = MusicPlayer()
var audioPlayer: AVAudioPlayer?
// Pause music
func pauseBackgroundMusic() {
guard let audioPlayer = audioPlayer else { return }
audioPlayer.pause()
}
// Resume music
func resumeBackgroundMusic() {
guard let audioPlayer = audioPlayer else { return }
audioPlayer.play()
}
}
And in the View:
Define an #State variable:
#State var pauseMode = false
And call the music player as follows in the Button action code:
Button(action: {
if pauseMode {
MusicPlayer.shared.resumeBackgroundMusic()
self.pauseMode.toggle()
} else {
MusicPlayer.shared.pauseBackgroundMusic()
self.pauseMode.toggle()
}
})
{
Image(systemName: "playpause")
}
FYI - I posted this in SwiftUI since newer programmers (and Apple) seem to be moving in this direction. I figured this might help someone (like me!) who came to this question seeking a Swift / SwiftUI answer.

Resources