iOS+Swift: Why is music not played on iPad? - ios

I am working on a SpriteKit game and trying to play a looped music file. Here's my code:
var musicPlayer = AVAudioPlayer()
override func didMoveToView(view: SKView) {
self.playBackgroundMusic("gravity")
}
private func playBackgroundMusic(filename: String) {
do {
let url = NSBundle.mainBundle().URLForResource(filename, withExtension: "m4a")
print(url)
self.musicPlayer = try AVAudioPlayer(contentsOfURL: url!)
self.musicPlayer.numberOfLoops = -1
self.musicPlayer.prepareToPlay()
self.musicPlayer.play()
} catch let error as NSError {
print("ERROR: \(error.description)")
}
}
This works when testing on my iPhone 4S, but not on the iPad Air (there's just no music playing, but works fine with apps like Youtube etc.). Strangely enough, when connecting my Bluetooth head phones to the iPad, it works when listening with the head phones.
My iOS versions are both 9.3.4 for the iPad Air and the iPhone 4S.
Any ideas? Is this a bug on Apple's side ?

Okay I found a fix. Try to call the following code before playing any audio with AVAudioPlayer:
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, withOptions: AVAudioSessionCategoryOptions.DefaultToSpeaker)
} catch let error as NSError {
print("ERROR: \(error.description)")
}
Hope this helps somebody.

Related

AVAudioPlayer still playing ambient sound even when iPhone is on silent

I have an observable object which provides functionality to my app to play the same chime sound whenever I need (repeatedly).
The issue is that it plays when the iPhone is on silent and I cannot stop it. I Googled as much as I could and found that people were advising to set the audio category to 'ambient', however, this still does not appear to fix the issue for me.
Here's my Chime class:
class Chime: ObservableObject {
let chimeSoundUrl = URL(fileURLWithPath: Bundle.main.path(forResource: "chime.wav", ofType: nil)!)
private var audioPlayer: AVAudioPlayer?
init() {
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(.ambient, options: .duckOthers)
try audioSession.setActive(true)
} catch {
print("Failed to activate audio session: \(error.localizedDescription)")
}
do {
audioPlayer = try AVAudioPlayer(contentsOf: chimeSoundUrl)
audioPlayer?.prepareToPlay()
audioPlayer?.volume = 1.0
} catch {
print("Failed to prepare audio player: \(error.localizedDescription)")
}
}
func play() {
audioPlayer?.currentTime = 0
audioPlayer?.play()
}
}
I have tried setting the audio category in the Chime class' init function, at the top-level of the app in the onAppear of the view playing the chime, and in the Chime class' 'play' method (which actually causes app performance issues) - none of these seem to work.
I'd really appreciate any help. Thanks
Not sure whether you've tried this on a real device, but here's what I found:
Using the simulator, if the silent switch is already active when the app is opened it behaves as if it isn't on silent and the sound plays. If silent switch is activated after the app is opened it works as expected.
When I tried running it on a real device it worked correctly in both cases, so maybe it's a bug in the simulator.
If you're just playing short alert-type sounds you should probably use Audio Services rather than an AVAudioPlayer. That might look like this:
class Chime {
let chimeSoundUrl = URL(fileURLWithPath: Bundle.main.path(forResource: "alert.mp3", ofType: nil)!)
let chimeSoundID: SystemSoundID
init(){
var soundID:SystemSoundID = 0
AudioServicesCreateSystemSoundID(chimeSoundUrl as CFURL, &soundID)
chimeSoundID = soundID
}
func play(){
AudioServicesPlaySystemSound(chimeSoundID)
}
}
Running the above in the simulator the silent switch has no effect and sound always plays, on a real device it works as expected.

How to play custom sound while app is in background AVAudioSession?

My iOS app receives notification to refresh its state from our main service. Right now, we are fetching the latest state and we play a continues sound when there is updated data. Our devices are locked in guided mode to stop them from turning off.
We are making changes such that the device can go to sleep after xx minutes of inactivity. However, we are noticing that the sound doesn't play when the app is in background and it always seems to fail on AVAudioSession.sharedInstance().setActive(true). Interestingly, if put breakpoints and run it through the debugger, it works fine but when running normally, it fails with this error:
Error Domain=NSOSStatusErrorDomain Code=561015905
We have the "Audio, AirPlay, and PnP" enabled under background modes under capabilities. Here is the code for playing the sound:
func playSound(shouldPlay: Bool) {
guard let url = Bundle.main.url(forResource: "sms_alert_circles", withExtension: "caf") else { return }
let audioSession = AVAudioSession.sharedInstance()
do {
try self.audioSession.setCategory(.playback, mode: .default, options: .mixWithOthers)
try self.audioSession.setActive(true)
/* The following line is required for the player to work on iOS 11. Change the file type accordingly*/
self.player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.caf.rawValue)
guard let player = self.player else { return }
if shouldPlay == true {
player.volume = 1.0
player.numberOfLoops = 1
player.play()
} else {
player.stop()
try self.audioSession.setActive(false)
}
} catch let error {
print("Error playing sounds")
print(error.localizedDescription)
}
}
I am hoping someone can point out the issue.

Audio live streaming Swift

I need to build an application that plays an audio live streaming URL for a radio station.
The URL is available but i can't find a good way to implement it in my application.
I've tried this way but the player won't provide any sound, although i am sure it is loading the URL (it plays the stream when trying on a simulator but on a real device it does not)
This is my code:
var player : AVPlayer!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let url = "http://listen.shoutcast.com/radiodeltalebanon"
player = AVPlayer(url: URL(string: url)!)
player.volume = 1.0
player.rate = 1.0
player.play()
}
The indicator beside the 4G or the Wifi icon in the status bar is showing while in app and if i turn the volume of my phone up, the Volume is up and not the Ringer.
This means that it is actually playing but no sound is available.
Can anyone help me please ?
Thanks.
Turned out that i missed this :
override func viewDidLoad() {
super.viewDidLoad()
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
print("AVAudioSession Category Playback OK")
do {
try AVAudioSession.sharedInstance().setActive(true)
print("AVAudioSession is Active")
} catch let error as NSError {
print(error.localizedDescription)
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
For Swift 4.2:
override func viewDidLoad() {
super.viewDidLoad()
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print(error)
}
}

AVAudioPlayer not responding in the background

i'm playing audio in the background when my app receive voip notification.
But AVAudioPlayer acting weird and sometimes play the audio and sometimes not without any error.
thats how i play:
func playAudio() {
// Set the sound file name & extension
let alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("testAudio", ofType: "wav")!)
// Play the sound
do {
try audioPlayer = AVAudioPlayer(contentsOfURL: alertSound)
audioPlayer.delegate = self
if (audioPlayer.prepareToPlay()) {
audioPlayer.play()
}
else {NSLog("there is error")}
} catch {
NSLog("there is \(error)")
}
}
I initialize AVAudioSession in my AppDelegate application function:
do
{
audioPlayer = AVAudioPlayer()
audioSession = AVAudioSession()
try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, withOptions: [AVAudioSessionCategoryOptions.MixWithOthers])
try audioSession.setActive(true)
try audioSession.overrideOutputAudioPort(AVAudioSessionPortOverride.Speaker)
}
catch {
NSLog("there is \(error)")
}
and of course var audioPlayer: AVAudioPlayer! as a class verb
Its working for the first minute or two and then AVAudioPlayer stop responding without any error.
I even try to use AVAudioPlayerDelegate to receive events, but nothing happen.
can someone explain this behavior? is it a bug in ios? or i'm doing somthing wrong?
tested on Ios 9.2, iphone 6 device, xcode 7.2.
Thanks.

MPMoviePlayerViewController only display video but not playing the audio iOS 8

Forgive me if this question has been asked already, but I've looked everywhere and all I can find is old questions from iOS 5.0 and people have the opposite problem.
My problem is I have created a video I would like to use in my application, and I recently added audio to it. The video plays fine with no problems, but the audio does not play. If I play the video in any video player in my computer, the sound is there so I cant figure out what the problem is.
Here is my code:
var movieViewController: MPMoviePlayerViewController?
func playVideoUsingViewController() {
if let
path = NSBundle.mainBundle().pathForResource("wakyIntro", ofType: "mp4"),
url = NSURL(fileURLWithPath: path),
movieViewController = MPMoviePlayerViewController(contentURL: url) {
self.movieViewController = movieViewController
movieViewController.moviePlayer.fullscreen = true
movieViewController.moviePlayer.controlStyle = .None
movieViewController.moviePlayer.scalingMode = .AspectFill
movieViewController.view.userInteractionEnabled = false
self.showViewController(movieViewController, sender: self)
println("Movie loaded")
} else {
println("Video failed")
}
}
Then I just call it from my ViewController:
func mainMenuViewControllerDidPressPlay(mainMenuViewController: MainMenuViewController) {
game?.runAction(pressButton)
if !videoPlayed{
playVideoUsingViewController()
} else if !gameStarted {
gameStarted = true
game?.bird.startAnimation(game!)
game?.bird.startPhysics()
}
self.hideViewController(mainMenuViewController)
}
Any ideas or suggestions would be greatly appreciated!
This is a solution in swift based on #user2709666's response
var session: AVAudioSession = AVAudioSession.sharedInstance()
session.setCategory("AVAudioSessionCategoryPlayback", withOptions: AVAudioSessionCategoryOptions.DefaultToSpeaker, error: nil)
Call this before playing the video.
Edit for Swift 2.2 :
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
} catch let error as NSError {
print(error)
}
Make a session of AVAudioSession.
Following is the code:
var session = AVAudioSession.SharedInstance();
session.SetCategory(new NSString("AVAudioSessionCategoryPlayback"), AVAudioSessionCategoryOptions.DefaultToSpeaker, out error);
if (error != null)
{
Console.WriteLine(error);
}
and add this before your video player. I am sure this will work for you, I got the same issue before.

Resources