generate random sounds on button click swift 4 - ios

I have a list of .wav files starting from sound1.wav,sound2.wav,sound3.wav.... till sound20.wav
I want to play a random sound whenever user touches a button. What methods should I use and how ?

You can use the arc4random_uniform() and the AudioServicesPlaySystemSound() methods to implement what you want.
First of all you need to import AudioToolbox
make a function to generate the sounds and call it inside the #IBAction function
This is is how you do it :
#IBAction func buttonPressed(_ sender: UIButton){
playSound() //calling the function
}
func playSound(){
//select random number i.e sound1,sound2,sound3...sound[n]
let randomNumber = Int(arc4random_uniform(TotalSoundFiles))
//create the sound
if let soundURL = Bundle.main.url(forResource: "sound\(randomNumber + 1)", withExtension: ".wav"){
var mySound: SystemSoundID = 0
AudioServicesCreateSystemSoundID(soundURL as CFURL, &mySound)
//Play the sound
AudioServicesPlaySystemSound(mySound)
}
}

Whenever the user presses the button, run the following code:
In swift 4.2 it is as simple as:
let sound = arrayOfSounds.randomElement()
In earlier versions:
let randIndex = Int(arc4random_uniform(20)) // Random number between 0 and 20
let sound = arrayOfSounds[randIndex]
Then play the sound using AVAudioPlayer or any other method you want (such as SpriteKit if you are making a game).

Here is a simple solution: a playSound() function that you call when the button is pressed
import Foundation
import AVFoundation
var player: AVAudioPlayer?
func playSound() {
//generate a random Int between 0 and 19, and then add 1
let random = Int(arc4random_uniform(20)) + 1
//Construct the sound file name
let randomSoundName = "sound" + String(random) + ".wav"
//Then check that the file is in the app bundle
guard let url = Bundle.main.url(forResource: randomSoundName, withExtension: "wav") else { return }
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
/* The following line is required for the player to work on iOS 11. Change the file type accordingly*/
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.wav.rawValue)
/* iOS 10 and earlier require the following line:
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeWAVE) */
guard let player = player else { return }
player.play()
} catch let error {
print(error.localizedDescription)
}
}

Related

How to run a function in a for loop sequentially

I have a set of buttons in a stack view. Each button when pressed plays a different sound. I have a separate button (loop button) that when pressed calls the loopButtonPressed function. My goal is that when this loop button is pressed, it will loop through the subviews that are buttons in this stack view and play each of the sounds sequentially in order using the soundButtonPressed function. I saw a method that I implemented below using the run() function which sets each consecutive function to run after a given amount of time. Although this kind of works it is not a great solution because the sound files are of varying length. I was thinking there may be a way to do this using dispatch groups, which I don't fully understand. If I take away the run function, it will only play the sound of the last button in the stack view. I am using AVFoundation to play the wav files as well. I appreciate any advice or direction, thanks.
func run(after seconds: Int, completion: #escaping () -> Void) {
let deadline = DispatchTime.now() + .milliseconds(seconds)
DispatchQueue.main.asyncAfter(deadline: deadline) {
completion()
}
}
#objc func loopButtonPressed(_ sender: UIButton) {
var i = 1
for case let button as UIButton in self.colorBubblesStackView.subviews {
run(after: 800*i) {
self.soundButtonPressed(sender: button)
}
i += 1
}
}
My soundButtonPressed function is just a switch statement where each case calls the function playSound() with the correct sound file name. Here is the playSound function:
func playSound(_ soundFileName: String) {
guard let url = Bundle.main.url(forResource: soundFileName, withExtension: "wav") else { return }
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.wav.rawValue)
guard let player = player else { return }
player.play()
} catch let error {
print(error.localizedDescription)
}
}
func playSound(name: String ) {
guard let url = Bundle.main.url(forResource: name, withExtension: "mp3") else {
print("url not found")
return
}
do {
/// this codes for making this app ready to takeover the device audio
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
/// change fileTypeHint according to the type of your audio file (you can omit this)
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3)
player?.delegate = self
// no need for prepareToPlay because prepareToPlay is happen automatically when calling play()
player!.play()
} catch let error as NSError {
print("error: \(error.localizedDescription)")
}
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
print("finished")//It is working now! printed "finished"!
}
confirm to protocol
class ViewController: UIViewController,AVAudioPlayerDelegate {
and instead of looping add each button a Tag and start from first tag sat 100 . Then when the call back obtained for player finished playing play the next File with new icremented Tag say 101
Try AVQueuePlayer
A player used to play a number of items in sequence.
#objc func loopButtonPressed(_ sender: UIButton) {
let allUrls = allSongs.compactMap { Bundle.main.url(forResource: $0, withExtension: "wav") }
let items = allUrls.map { AVPlayerItem(url: $0) }
let queuePlayer = AVQueuePlayer(items: items)
queuePlayer.play()
}

How do I synchronize background music and buttons?

An application writes. Music plays on the background of the application. And I want to stop and resume this music from every page of the application
But after pressing the music stop button on one page, the buttons on the other pages do not change.
And when I go back to the first page, the music starts all over again.
import Foundation
import AVFoundation
class Music {
static var audioPlayer : AVAudioPlayer?
enum SoundEffect {
case background
}
static func playSound(effect : SoundEffect) {
var soundFileName = ""
switch effect {
case.background:
soundFileName = "background"
}
let bundlePath = Bundle.main.path(forResource: soundFileName, ofType: "wav")
guard bundlePath != nil else {
print("Couldn't find sound file \(soundFileName) in the bundle")
return
}
do {
let soudURL = URL(fileURLWithPath: bundlePath!)
audioPlayer = try AVAudioPlayer(contentsOf: soudURL)
audioPlayer!.numberOfLoops = -1
audioPlayer?.prepareToPlay()
audioPlayer?.play()
} catch {
}
}
}
Al first check AVAudioPlayer is already initialize or not and after that check player is playing or stop.
if let audioPlayer = Music.audioPlayer, audioPlayer.playing {
audioPlayer.stop()
}

Swift 4: How to have a sound play over itself when a button is pushed?

So my current situation is that whenever I press a button, the sound will restart from the beginning and cut off the already playing sound. What I want is the sound to play over itself when I push the button, so if I push it a second time right after the first time, the second sound would play over the first one and not cut it off. Please let me know if you need clarification on what I need. Here is my code so far:
#IBAction func bull(_ sender: Any) {
let url = Bundle.main.url(forResource: "Bull", withExtension: "mp3")!
do
{
player = try AVAudioPlayer(contentsOf: url)
guard let player = player else { return }
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
player.prepareToPlay()
player.play()
}
catch let error
{
print(error.localizedDescription)
}
}
Thanks.
One player can play one sound at a time only. You probably have to do following
Create an array of players
var arrPlayer: [AVAudioPlayer] = []
And then inside your method do the following
let url = Bundle.main.url(forResource: "Bull", withExtension: "mp3")!
do
{
player = try AVAudioPlayer(contentsOf: url)
arrPlayer.append(player)
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)
try AVAudioSession.sharedInstance().setActive(true)
arrPlayer.last?.prepareToPlay()
arrPlayer.last?.play()
}
catch let error
{
print(error.localizedDescription)
}

Background Music Stop/Mute on iOS

I'm creating my first app. I have an app with music playing in the background with the following code:
var backgroundMusicPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
//background Music
func playBackgroundMusic(filename: String) {
let url = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
guard let newURL = url else {
print("Could not find file: \(filename)")
return
}
do {
backgroundMusicPlayer = try AVAudioPlayer(contentsOfURL: newURL)
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
} catch let error as NSError {
print(error.description)
}
}
playBackgroundMusic("Starship.wav")
}
So what should I do in order to stop/mute the background music when I switch to another ViewController? Should I do this my FirstViewController or SecondViewController?
Obviously, I don't want the sound to be off in the SecondViewController as I have other stuff that will be playing there.
To mute sound I simply mute the volume.
backgroundMusicPlayer.volume = 0
and set it to normal if I want sound
backgroundMusicPlayer.volume = 1
If you just want to pause music you can call
backgroundMusicPlayer.pause()
To resume you call
backgroundMusicPlayer.resume()
If you want to stop music and reset it to the beginning you say this
backgroundMusicPlayer.stop()
backgroundMusicPlayer.currentTime = 0
backgroundMusicPlayer.prepareToPlay()
Did you also consider putting your music into a singleton class so its easier to play music in your different viewControllers.
Not sure this is what you are looking for as your question is a bit vague.

How to stop background music with button and if/else in Swift?

In my game I added a button to the GameScene and played music:
let url = NSBundle.mainBundle().URLForResource("Happy Background Music", withExtension: "mp3")
let bgMusic = AVAudioPlayer(contentsOfURL: url, error: nil)
#IBAction func SoundOnOff(sender: UIButton) {
bgMusic.numberOfLoops = -1
bgMusic.play()
}
and the music played, but I want to add an if so when the button is pressed the music will stop. What if should I write?
Change your code with this,
let url = NSBundle.mainBundle().URLForResource("Happy Background Music", withExtension: "mp3")
do{
let bgMusic = try AVAudioPlayer(contentsOfURL: url!)
}catch{
print(error)
}
#IBAction func SoundOnOff(sender: UIButton) {
if bgMusic.playing {
bgMusic.stop()
} else {
self.bgMusic.play()
}
}
Edit: Description
playing- Property
-A Boolean value that indicates whether the audio player is playing (true) or not (false). (read-only)
AVAudioPlayer has a built-in property playing, which you can use to decide whether to play or pause the music:
#IBAction func SoundOnOff(sender: UIButton) {
if bgMusic.playing {
bgMusic.pause()
} else {
bgMusic.numberOfLoops = -1
bgMusic.play()
}
}
Just seen that this is only available in iOS9 and later. So if you're targeting iOS9 then this is possible.
If you're using SpriteKit to write your game I would recommend using the SKAudioNode class to play your BGMusic.
let url = NSBundle.mainBundle().URLForResource("Happy Background Music", withExtension: "mp3")
let audioNode = SKAudioNode(URL:url)
audioNode.positional = false
audioNode.autoplayLooped = true
self.addChild(audioNode)
To pause the music you create an SKAction ...
let pauseAction = SKAction.pause()
audioNode.runAction(pauseAction)
You can resume it also but just trying to find the code for that...

Resources