Playing sounds with multiple play buttons in iOS - ios

I have to play three types of wav file one at a time. When I click on the Play button in Enter region it plays a sound named "EnterRegion.wav" and clicking on Exit Region play button plays "ExitRegion.wav" audio file.
But for the play button of the Immediate, it plays a sound repeatedly until I press the Stop button.
Both first and second Play buttons are working perfectly for me but when I click on play button in Immediate, it plays sound for the first time but when I click on other play buttons and come back to Immediate play button again, the application throws exceptions. This happens the same when I press stop button and come back to the play button of immediate section. My sample app screenshot and code sample which is in Swift 4 is as below.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var engine: AVAudioEngine!
var enterWavFile: AVAudioFile!
var audioPlayerNode : AVAudioPlayerNode!
var immediaAudioPlayerNode: AVAudioPlayerNode!
var exitWavFile: AVAudioFile!
var flatlineWavFile:AVAudioFile!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
engine = AVAudioEngine()
enterWavFile = try? AVAudioFile(forReading: NSURL.fileURL(withPath:
Bundle.main.path(forResource: "EnterRegion", ofType: "wav")!))
exitWavFile = try? AVAudioFile(forReading: NSURL.fileURL(withPath:
Bundle.main.path(forResource: "ExitRegion", ofType: "wav")!))
flatlineWavFile = try? AVAudioFile(forReading: NSURL.fileURL(withPath:
Bundle.main.path(forResource: "Immediate", ofType: "wav")!))
}
#IBAction func playEnterRegionSound(_ sender: UIButton) {
playSound(audioFile: enterWavFile)
}
#IBAction func playExitRegion(_ sender: UIButton) {
playSound(audioFile: exitWavFile)
}
func playSound(audioFile: AVAudioFile) {
audioPlayerNode = AVAudioPlayerNode()
if(audioPlayerNode.isPlaying){
audioPlayerNode.stop()
}
if(engine.isRunning){
engine.stop()
engine.reset()
}
engine.attach(audioPlayerNode)
engine.connect(audioPlayerNode, to: engine.mainMixerNode, format: audioFile.processingFormat)
audioPlayerNode.scheduleFile(audioFile, at: nil, completionHandler: nil)
// Start the audio engine
engine.prepare()
try! engine.start()
audioPlayerNode.play()
}
#IBAction func playImmediateFlatline(_ sender: UIButton) {
immediaAudioPlayerNode = AVAudioPlayerNode()
if(immediaAudioPlayerNode.isPlaying){
print("immediateAudioPlayerNode Playing")
immediaAudioPlayerNode.stop()
}
if(engine.isRunning){
print("engine Running")
engine.stop()
engine.reset()
}
let audioFileBuffer = AVAudioPCMBuffer(pcmFormat: flatlineWavFile.processingFormat, frameCapacity: AVAudioFrameCount( flatlineWavFile.length))!
try? flatlineWavFile.read(into: audioFileBuffer)
engine.attach(immediaAudioPlayerNode)
engine.connect(immediaAudioPlayerNode, to: engine.mainMixerNode, format: flatlineWavFile.processingFormat)
immediaAudioPlayerNode.scheduleBuffer(audioFileBuffer, at: nil, options: AVAudioPlayerNodeBufferOptions.loops, completionHandler: nil)
// Start the audio engine
engine.prepare()
try! engine.start()
immediaAudioPlayerNode.play()
}
#IBAction func stopImmediateFlatline(_ sender: UIButton) {
if (immediaAudioPlayerNode.isPlaying){
print("immediateAudioPlayerNode playing")
immediaAudioPlayerNode.pause()
}
if(engine.isRunning){
print("engine Running")
engine.stop()
engine.reset()
}
}
}
I am unable to locate why this is happening and could you please help me to fix the issue or any suggestions.

code for playing the sound
var player1: AVAudioPlayer?
var player2: AVAudioPlayer?
var player3: AVAudioPlayer?
func playSound() {
guard let url = Bundle.main.url(forResource: "Music", 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.mp3.rawValue)
/* iOS 10 and earlier require the following line:
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3) */
guard let player = player else { return }
// read this line of code for you to play the wav file you wanted to play ang then trigger it using a button.
player.play()
} catch let error {
print(error.localizedDescription)
}
}

Related

Play random sounds from my projects in Swift 5

I'm new in iOS. I have written this code, which plays only one audio file when tapping a UIButton. I would like to play multiple sound randomly. How to set it? Thank you!
import UIKit
import AVFoundation
class ViewController: UIViewController {
var audioPlayer: AVAudioPlayer!
#IBAction func playButtonPressed(_ sender: UIButton) {
if let soundURL = Bundle.main.url(forResource: "kompilacja", withExtension: "mp3") {
do {
audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
}
catch {
print(error)
}
audioPlayer.play()
}else{
print("Karwasz twarz! Brak pliku audio, Panie!")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
Put all of your sound file names in an array and use the randomElement method to choose your sound.
#IBAction func playButtonPressed(_ sender: UIButton) {
let sounds = ["kompilacja", "another sound", "yet another sound"]
guard let sound = sounds.randomElement(),
let soundURL = Bundle.main.url(forResource: sound, withExtension: "mp3") else { return }
do {
audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
}
catch {
print(error)
}
audioPlayer.play()
}

Multiple Audio Files In One ViewController With Button

Want to play multiple audio files in the same ViewController. When a user clicks on Button1 the file1.mp3 should be loaded into ViewController and when user hit play same should play and for button2 file2.mp3 should load and play.
I know how to do this for a single file but for multiple files I have to create ViewController again and again which is so hard. This is my code for single file
#IBAction func buttonPressed(_ sender: UIButton) {
if player.isPlaying {
player.stop()
sender.setImage(UIImage(named:"play.png"),for: .normal)
} else {
player.delegate = self
player.play()
player.numberOfLoops = 107
sender.setImage(UIImage(named:"pause.png"),for: .normal)
}
}
do {
let audioPlayer = Bundle.main.path(forResource: "chalisa", ofType: "mp3")
try player = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: audioPlayer!) as URL)
}catch {
//ERROR
}
You can use Tableview List all songs Name and Play Button use to Multiple audio files use in one viewcontroller
import AVFoundation
//MARK:- Variable Declaration
var player: AVAudioPlayer?
func playMusic() {
guard let url = Bundle.main.url(forResource: "chalisa", withExtension: "mp3") else { return }
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)
/* iOS 10 and earlier require the following line:
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3) */
guard let player = player else { return }
player.play()
} catch let error {
print(error.localizedDescription)
}
}

MP3 File Not Playing On Button Select

For simplicity, assume this View says alphabet letters in button when you click on said button.
I have been attempting to play the audio files associated with each button, but the files never play. The buttons, individually, are never given outlets, but they are all connected to the function on Touch Up Inside.
class UIAlphabetController: UIViewController, AVAudioPlayerDelegate{
#IBAction func PlaySound(Sender: UIButton) {
let soundname:String = Sender.currentTitle!
var player: AVAudioPlayer?
let url = Bundle.main.url(forResource: soundname, withExtension: "mp3")
do {
player = try AVAudioPlayer(contentsOf: url!)
guard let player = player else { return }
player.prepareToPlay()
player.play()
} catch let error { print(error.localizedDescription) }
}
}
Since I have had this problem, the AQDefaultDevice (188): skipping input stream 0 0 0x0 message has been coming up. On testing with breakpoints, both soundname and url have expected values.
Does anybody have any idea as to how to fix this?
Move your instance of AVAudioPlayer outside of the method. If you do this it will keep player around long enough to play the sound. The reason it's not working is because it trashes the player instance after the method is complete because it thinks you are done using it.
class UIAlphabetController: UIViewController, AVAudioPlayerDelegate {
var player: AVAudioPlayer?
#IBAction func PlaySound(Sender: UIButton) {
let soundname:String = Sender.currentTitle!
let url = Bundle.main.url(forResource: soundname, withExtension: "mp3")
do {
player = try AVAudioPlayer(contentsOf: url!)
guard let player = player else { return }
player.prepareToPlay()
player.play()
} catch let error { print(error.localizedDescription) }
}
}

Remote mp3 file taking a lot of time to play in swift ios

i'm in trouble. i want to play a remote mp3 file in my app. but mp3 file taking a lot of time (approx 5-6 minute) to play. why ?
Anyone can suggest what should i do ?
import UIKit
import AVFoundation
class TestViewController: UIViewController, AVAudioPlayerDelegate {
var player:AVAudioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func play(sender: AnyObject) {
let url = "http://www.example.com/song.mp3"
let fileURL = NSURL(string: url.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()))
let soundData = NSData.dataWithContentsOfURL(fileURL, options: nil, error: nil)
var error: NSError?
self.player = AVAudioPlayer(data: soundData, error: &error)
if player == nil
{
if let e = error
{
println(e.localizedDescription)
}
}
player.volume = 1.0
player.delegate = self
player.prepareToPlay()
player.play()
}
}
Thanks in advance.
Use AVPlayer instead of AVAudioPlayer for streaming audio. It can be as simple as this --
let urlString = "http://www.example.com/song.mp3"
let url = NSURL(string: urlString)
var avPlayer = AVPlayer(URL: url)
avPlayer.play()
You can also set an observer on the AVPlayer.status property to manage its changing status. Check out:
https://stackoverflow.com/a/13156942/2484290 (objective c)
And of course the AVPlayer docs are here:
https://developer.apple.com/LIBRARY/ios/documentation/AVFoundation/Reference/AVPlayer_Class/index.html#//apple_ref/occ/cl/AVPlayer
Try using my example where you can work with the cache and the remote file.
player.automaticallyWaitsToMinimizeStalling = false // by default true, so It is important to solve your problem in a mp3 file remotely
This feature is available iOS 10.0+
var player: AVPlayer!
enum AudioType: String {
case remote
case cache
}
#IBAction func remotePressed(_ sender: Any) {
playAudio(type: .remote, fileURL: "http://www.example.com/song.mp3")
}
#IBAction func cachePressed(_ sender: Any) {
if let fileURL = Bundle.main.path(forResource: "jordan", ofType: "mp3") {
playAudio(type: .cache, fileURL: fileURL)
}
}
private func playAudio(type: AudioType, fileURL: String) {
let url = type == .cache ? URL.init(fileURLWithPath: fileURL) : URL.init(string: fileURL)
let playerItem: AVPlayerItem = AVPlayerItem(url: url!)
player = AVPlayer(playerItem: playerItem)
player.automaticallyWaitsToMinimizeStalling = false//It's important
player.play()
}
Its may be late.
Yes. what #lemon lime pomelo has said. you should use AVPlayer instead of AVAudioPlayer.
Also you can use MediaPlayer.
import UIKit
import MediaPlayer
#IBAction func playSong(sender: AnyObject) {
print("Song")
self.playAudio("yourAudioURL.mp3")
}
func playAudio(URL:String){
let movieurl:NSURL? = NSURL(string: "\(URL)")
if movieurl != nil {
self.movie = MPMoviePlayerViewController(contentURL: movieurl!)
}
if self.movie != nil {
self.presentViewController(self.movie!, animated: true, completion: nil)
self.movie?.moviePlayer.play()
}
}
It is slow because your program downloads all the song before starting playing

AVAudioPlayer produces lag despite prepareToPlay() in Swift

Playing a very short sound (~0.5s) produces a hiccup (like a lag) in my SpriteKit iOS game programmed in Swift. In other questions, I read that I should prepareToPlay() the sound, which I did.
I even used a variable (soundReady) to check if the sound is prepared before playing it. I also re-prepare the sound whenever it is finished playing (audioPlayerDidFinishPlaying()). Here are the relevant parts of the code:
class GameScene: SKScene, AVAudioPlayerDelegate {
var splashSound = NSURL()
var audioPlayer = AVAudioPlayer()
var soundReady = false
override func didMoveToView(view: SKView) {
let path = NSBundle.mainBundle().pathForResource("plopSound", ofType: "m4a")
splashSound = NSURL(fileURLWithPath: path)
audioPlayer = AVAudioPlayer(contentsOfURL: splashSound, error: nil)
audioPlayer.delegate = self
soundReady = audioPlayer.prepareToPlay()
}
func playSound(){
if(soundReady){
audioPlayer.play()
soundReady = false
}
}
func audioPlayerDidFinishPlaying(player: AVAudioPlayer!, successfully flag: Bool){
//Prepare to play after Sound finished playing
soundReady = audioPlayer.prepareToPlay()
}
}
I have no idea where I've gone wrong on this one. I feel like I have tried everything (including, but not limited to: only preparing once, preparing right after playing, not using a variable, but just prepareToPlay()).
Additional information:
The sound plays without delay.
How quickly the sound is played after the last finish does not seem to impact the lag.
I ran into this same problem and played the sound in the backgroundQueue.
This is a good example: https://stackoverflow.com/a/25070476/586204.
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
audioPlayer.play()
})
Just adding a Swift 3 version of the solution from #brilliantairic.
DispatchQueue.global().async {
audioPlayer.play()
}
When I called play multiple times it would cause bad access. I believe the player is being deallocated since this is not thread safe. I created a serial queue to alleviate this problem.
class SoundManager {
static let shared = SoundManager()
private init() {
try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try? AVAudioSession.sharedInstance().setActive(true)
}
private let serialQueue = DispatchQueue(label: "SoundQueue", qos: .userInitiated)
private var player: AVAudioPlayer?
static func play(_ sound: Sound) {
shared.play(sound)
}
func play(_ sound: Sound) {
guard let url = Bundle.main.url(forResource: sound.fileName, withExtension: "mp3")
else { return }
do {
try serialQueue.sync {
self.player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3)
DispatchQueue.main.async {
self.player?.play()
}
}
} catch let error as NSError {
print("error: \(error.localizedDescription)")
}
}
}

Resources