show an image and link it to a sound - ios

I'm trying to make a simple app for some kids. I want to show a photo and to link that photo to a sound.
When the kid click on "Play Sound" it should play an audio that represents the image.
so far I've done only showing the images, that I've put in Assets.xcassets, but my question is..
How can I link those photos to some audio files and play them by clicking on "Play Sound"
Here is my code in ViewController.swift :
#IBOutlet weak var viewImage: UIImageView!
var images: [UIImage] = [
UIImage(named: "photo1.png")!,
UIImage(named: "photo2.png")!,
UIImage(named: "photo3.png")!,
UIImage(named: "photo4.png")!,
UIImage(named: "photo5.png")!,
UIImage(named: "photo6.png")!,
UIImage(named: "photo7.png")!,
UIImage(named: "photo8.png")!
]
var currentImagesIndex = 0
#IBAction func nextImage(_ sender: Any) {
currentImagesIndex += 1
let numberOfImages = images.count
let nextImagesIndex = currentImagesIndex % numberOfImages
viewImage.image = images[nextImagesIndex]
}
So far, when I click on Next Photo it shows me a new photo, and it's working ok. All I want to do for now, it's to link the "photo1-8" to an audio and play it.
I'll appreciate any kind of help,
Radu

You should first add all your sound files to your project. I assume you know how to do this. After adding the files, your project navigator should look a bit like this:
For convenience, I will call your 8 sound files sound1.mp3, sound2.mp3, sound3.mp3 ... sound8.mp3.
Create a property in the class called soundFiles:
let soundFiles: [String] = [
"sound1",
"sound2",
"sound3",
"sound4",
"sound5",
"sound6",
"sound7",
"sound8"
]
Also create a property called player to play the sounds:
var player: AVAudioPlayer!
In the #IBAction of the "Play Sound" button, get the file path of the sound file and initialise the player:
let numberOfImages = images.count
let nextImagesIndex = currentImagesIndex % numberOfImages
let soundFilePath = Bundle.main.url(forResource: soundFiles[nextImagesIndex], withExtension: ".mp3")!
player = try! AVAudioPlayer(contentsOf: soundFilePath)
Then you can play the sound by calling prepareToPlay() and play() consecutively:
player.prepareToPlay()
player.play()

Import AV Foundation by adding this line in top: import AVFoundation
Drag MP3, WAV file in main project directory
Check "Copy items if needed" & App Name in "Add to Targets"
Use this code to play sound
func playSound() {
var btnSound: AVAudioPlayer!
if let audioFileURL = Bundle.main.url(forResource: "fileName", withExtension: ".wav") {
do{
try btnSound = AVAudioPlayer(contentsOf: audioFileURL)
btnSound.prepareToPlay()
btnSound.play()
} catch let err as NSError {
print(err.debugDescription)
}
}
}
//call playSound function inside nextImage method
Tested with Swift 3.1 in xCode 8.3.2

Related

How can I play a sound on iOS?

I have built a random number generator, I want it to play a sound when the button is pressed. Xcode doesn't give me any errors and when I simulate my app it generates numbers no problem but there is no sound. I have my sound in a sound folder.
Here is my code.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player: AVAudioPlayer!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBOutlet weak var operations: UIImageView!
#IBOutlet weak var bottomRightNumber: UIImageView!
#IBOutlet weak var bottomMiddleNumber: UIImageView!
#IBOutlet weak var bottomLeftNumber: UIImageView!
#IBOutlet weak var topRightNumber: UIImageView!
#IBOutlet weak var topMiddleNumber: UIImageView!
#IBOutlet weak var topLeftNumber: UIImageView!
#IBAction func buttonPressed(_ sender: UIButton) {
let numberArray = [#imageLiteral(resourceName: "RN1"), #imageLiteral(resourceName: "RN2"), #imageLiteral(resourceName: "RN3"), #imageLiteral(resourceName: "RN4"),#imageLiteral(resourceName: "RN5"), #imageLiteral(resourceName: "RN6"), #imageLiteral(resourceName: "RN7"), #imageLiteral(resourceName: "RN8"), #imageLiteral(resourceName: "RN9"), #imageLiteral(resourceName: "RN0")]
bottomRightNumber.image = numberArray.randomElement()
bottomLeftNumber.image = numberArray.randomElement()
bottomMiddleNumber.image = numberArray.randomElement()
topRightNumber.image = numberArray.randomElement()
topMiddleNumber.image = numberArray.randomElement()
topLeftNumber.image = numberArray.randomElement()
let operationArray = [#imageLiteral(resourceName: "-"), #imageLiteral(resourceName: "+")]
operations.image = operationArray.randomElement()
func playSound() {
guard let url = Bundle.main.url(forResource: "A", withExtension: "wav") else { return }
do { /* 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)
player.play()
}
}
}
}
Looks like you just copied and pasted someone else’s code. But you pasted it into some other code, so the problem now is that your code that plays sound has ended up wrapped in a nested function declaration, inside your func buttonPressed code:
func playSound() {
guard let url = Bundle.main.url(forResource: "A", withExtension: "wav") else { return }
do {
player = try! AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.wav.rawValue)
player.play() // this is where sound _would_ play
}
}
Nothing ever calls that function, so it never runs.
Just delete those two lines that wrap the code, and now the code can run as part of what buttonPressed does. I would also suggest removing the pointless do construct as well, so you will end up with this:
guard let url = Bundle.main.url(forResource: "A", withExtension: "wav") else { return }
player = try! AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.wav.rawValue)
player.play() // this is where sound _would_ play
Your sound still might not play, or you might crash, but at least the code will now actually run!
Having said all that, let me change the code a little more to demonstrate best practices:
guard let url = Bundle.main.url(forResource: "A", withExtension: "wav") else {
print("the URL was not valid") // tell yourself what went wrong
return
}
do {
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.wav.rawValue)
player.play()
} catch { print(error) } // tell yourself what went wrong
In that version, I've put the do construct back, but this time it actually does something useful.
you can do 2 things:
1. Take the function playSound() out of the buttonPressed action and call the playSound() function - because I see that you have not called the function to play the sound.
2. you can just take the audio code out of the function playSound() if you are eventually going to use it there itself.
Here is the sample class to play sounds,
import Foundation
import AudioToolbox
import AVKit
class SoundManager {
static var objPlayer: AVAudioPlayer?
class func playAppLaunchSound() {
registerAndPlaySoundForFile(fileName: "AppLaunchSound", fileExtension: "mp3")
}
class func playEnterButtonSound() {
registerAndPlaySoundForFile(fileName: "EnterButtonSound", fileExtension: "mp3")
}
private class func registerAndPlaySoundForFile(fileName: String, fileExtension: String, withVibration: Bool = true) {
let bundle = Bundle.main
guard let url = bundle.url(forResource: fileName, withExtension: fileExtension) else {
return
}
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.ambient)
try AVAudioSession.sharedInstance().setActive(true)
objPlayer = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)
guard let aPlayer = objPlayer else { return }
aPlayer.play()
} catch let error {
print(error.localizedDescription)
}
}
}
registerAndPlaySoundForFile is the main method responsible to play sound!
You can use it like SoundManager.playEnterButtonSound(). You can create multiple methods in this class like this.
If you want to play sounds in your iOS application, stop what you are doing. Throw away the manual on swifts built in audio services and use a framework.
AudioKit is an incredible powerful framework built by some very competent audio programmers. This library has a nice API, is well documented and is very powerful. They have plenty of example apps, including sample code to help get you started. If you just want to play a sound, you will be covered. If you wanted to build a granular synth, you will also be covered.
Especially if you are new to programming, usually someone has done the hard work for you. Don't reinvent the wheel!

generate random sounds on button click swift 4

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)
}
}

Play local GIF using AVPlayer

We are trying to play local GIF using AVPlayer. Does AVPlayer supports GIF format? This is what we tried:
guard let gifUrl = Bundle.main.url(forResource: "myGif", withExtension: "gif") else {
return
}
let gifPlayer = AVPlayer(url: gifUrl)
self.videoContainer.player = gifPlayer
self.videoContainer.player?.play()
Is it possible to play GIF with AVPlayer?
Any help or advice will be highly appreciated. Thank you.
Best Regards, Roi
You can try these steps to play local video using AVPlayer in swift:-
1) First of all add the video into your xcode project.
2) Check your added video in Bundle. Select your project your Target -> Build Phases -> Copy Bundle Resources.
If video is not available in Copy Bundle Resources then you can add by tapping on Plus button.
3) Use the following code in you project.
import UIKit
import AVKit
import AVFoundation
class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
playVideo()
}
private func playVideo() {
guard let path = Bundle.main.path(forResource: "video", ofType:"m4v") else {
debugPrint("video.m4v not found")
return
}
let player = AVPlayer(url: URL(fileURLWithPath: path))
let playerController = AVPlayerViewController()
playerController.player = player
present(playerController, animated: true) {
player.play()
}
}
}
Hope this will help you :)

Sliding across piano keys (swift/iOS)

I'm working on an app (piano) that has a series of buttons that each has a different mp3. The screen shows 12 buttons (piano keys) and I want the user to be able to play an individual sound or swipe across a couple to hear multiple. Just like a real piano. I've seen many apps do this but mine seems to have a problem when the user slides across multiple buttons quickly. At the same speed, other apps will play all the notes, but mine will skip a few. Thank you for any help! This will make all the difference in my app!
A couple quick notes about this code:
-I just have the bare bones here to save space
-I just showed 6 audio players, but you get the idea
-the locationInNote1...Note2...Note3 is just showing 6 here to save place, but you get the idea
-"note1" in the button action is a string that can be changed when the user selects different octaves to play from, but its just a #, so the audio files ultimately are 1.mp3, 2.mp3, etc.
-the button action playNote1 is the same as the other button actions so i didn't repeat them all there.
var audioPlayer = AVAudioPlayer()
var audioPlayer2 = AVAudioPlayer()
var audioPlayer3 = AVAudioPlayer()
var audioPlayer4 = AVAudioPlayer()
var audioPlayer5 = AVAudioPlayer()
var audioPlayer6 = AVAudioPlayer()
func playNote(for locationInView: CGPoint) {
let locationInNote1 = note1Button.convert(locationInView, from: view)
let locationInNote2 = note2Button.convert(locationInView, from: view)
let locationInNote3 = note3Button.convert(locationInView, from: view)
let locationInNote4 = note4Button.convert(locationInView, from: view)
let locationInNote5 = note5Button.convert(locationInView, from: view)
let locationInNote6 = note6Button.convert(locationInView, from: view)
if note1Button.point(inside: locationInButton1, with: nil) {
playNote1(self)
}
if note2Button.point(inside: locationInButton2, with: nil) {
playNote2(self)
}
if note3Button.point(inside: locationInButton3, with: nil) {
playNote3(self)
}
if note4Button.point(inside: locationInButton4, with: nil) {
playNote4(self)
}
if note5Button.point(inside: locationInButton5, with: nil) {
playNote5(self)
}
if note6Button.point(inside: locationInButton6, with: nil) {
playNote6(self)
}
}
#IBAction func playNote1(_ sender: Any) {
let note1mp3 = note1
if let path = Bundle.main.path(forResource: note1mp3, ofType: "mp3") {
let url = URL(fileURLWithPath: path)
do {
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer.prepareToPlay()
audioPlayer.play()
}
catch {
print(error)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
view.addGestureRecognizer(panGesture)
Here is a way to hopefully:
Improve your code
Make your code simpler
Fix the problem you're having
In this example, I'll just put 3 audio players.
I'll try to make it as straight forward as possible...
import UIKit
class MyViewController: UIViewController {
var audioPlayer = AVAudioPlayer()
var audioPlayer2 = AVAudioPlayer()
var audioPlayer3 = AVAudioPlayer()
#IBOutlet var notes: [UIButton]!
let references = [note1, note2, note3]
#IBAction func notePressed(_ sender: UIButton) {
play(note: notes.index(of: sender)! + 1)
}
func play(note: Int) {
let reference = references[note - 1]
if let path = Bundle.main.path(forResource: reference, ofType: "mp3") {
let url = URL(fileURLWithPath: path)
do {
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer.prepareToPlay()
audioPlayer.play()
}
catch {
print(error)
}
}
}
}
Explanation
#IBOutlet var notes: [UIButton]!
This is an Outlet Collection of buttons. To make this, connect a button but select Outlet Collection instead of just Outlet. Make sure you connect each button in order (1, 2, 3), otherwise it will break!
let references = [note1, note2, note3]
This is an array of references to each of the note files. This is so that we only need one function to play a note.
#IBAction func notePressed(_ :sender:)
This function gets called by the buttons. For each button, connect the Touch Drag Enter (for sliding along notes) and Touch Down actions. You can add other ones if you want. The functions compares the sender and notes Outlet Collection to find out which note was pressed, then passes it to the func play(:note:) function.
func play(:note:)
This function takes the note number, and plays the corresponding file. It's almost identical to your original one, but instead of having a fixed note, it has one that is passed by the #IBAction func notePressed(_ :sender:) method.
I hope this is helpful, good luck!

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