Mute My App When Other Apps are Playing Music in Swift - ios

I'm writing an app that plays music. However, I'd like to make it so that my app's music pauses when other music is playing, such as music from iTunes. How would I accomplish this? Here's my code:
var alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Ambler", ofType: "mp3")!)
// Removed deprecated use of AVAudioSessionDelegate protocol
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
var error:NSError?
audioPlayer = AVAudioPlayer(contentsOfURL: alertSound, error: &error)
audioPlayer.prepareToPlay()
audioPlayer.play()
audioPlayer.numberOfLoops = -1

To do this I believe you are going to need to check if the music player is playing, here is how I would do that in Objective-C.
First #import <MediaPlayer/MediaPlayer.h> and add the library to your build phases.
- (BOOL)isPlaying
{
MPMusicPlayerController *musicPlayer = [MPMusicPlayerController systemMusicPlayer];
if (musicPlayer.playbackState == MPMusicPlaybackStatePlaying) {
return YES;
}
return NO;
}
When initializing musicPlayer it may cause the music to stop, if so I would
look into
+ (MPMusicPlayerController *)applicationMusicPlayer
Swift
func isPlaying() -> Bool {
let musicPlayer = MPMusicPlayerController.systemMusicPlayer()
if musicPlayer.playbackState == MPMusicPlaybackState.Playing {
return true
}
return false
}

Related

iOS: audio files used in app sound harsher than when played outside the app

The audio files for our iOS app sound much worse when played through the app than when played outside the app. The sounds seem harsher and more "ecohy".
Here's our code for playing audio. Are we somehow altering the playback or natural sound of the audio?
Two of the audio files we're using can be found here:
private func createAudioPlayer(filename: String) -> AVAudioPlayer {
// Define file URL
let path = Bundle.main.path(forResource: filename, ofType: nil)
let url = URL(fileURLWithPath: path!)
// Create player
let audioPlayer: AVAudioPlayer!
do {
audioPlayer = try AVAudioPlayer(contentsOf: url)
} catch {
audioPlayer = nil
printError("Error creating audio player for \(url): \(error)")
logEvent("Audio Error", userData: nil)
}
// Print status
print("Created audio player for \(filename)")
// Return player
return audioPlayer
}
func play(file: AudioFileEnum) {
if let player = audioPlayers[file] {
if player.isPlaying {
player.pause()
}
player.currentTime = 0
player.play()
} else {
printError("Error finding audio player for \(file)")
}
}
Have you tried setting the volume on the audioPlayer object to something smaller, like 0.05f, and adjusting from there?

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.

Background Music Restarting

I am programming a game which switches between SKScenes. When I start playing the background music from the start screen, switch to a new screen and back to the start screen the music starts playing again.
I can't find out how to run the music once and not restarting again and again. I could make a workaround with storing a bool in NSUserDefaults to check if the music is running but that seems like an awful workaround. There must be a more simple solution. I have attempted with static but that doesn't work.
Create a new empty swift file and add this code into it:
var backgroundMusicPlayer: AVAudioPlayer!
var musicOnPlayScene = Bool()
func playBackgroundMusic(filename: String) {
let url = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
if (url == nil) {
print("Could not find file: \(filename)")
return
}
var error: NSError? = nil
do {
backgroundMusicPlayer = try AVAudioPlayer(contentsOfURL: url!)
} catch let error1 as NSError {
error = error1
backgroundMusicPlayer = nil
}
if backgroundMusicPlayer == nil {
print("Could not create audio player: \(error!)")
return
}
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
}
Now play your background music into start scene this way:
playBackgroundMusic("yourSong.mp3")
and when you switch a scene add this code into next scene's didMoveToView:
backgroundMusicPlayer.play()
This will resume your background music when you switch any scene.
Hope it will help.

How to let background music continue?

I want to allow a player to use their music in my game, so I want to know how to detect if the player is playing music from his music library so I can let his music continue playing and if not let my music play in the background.
I use this code to play music in my GameViewController:
let bgMusicURL: NSURL = NSBundle.mainBundle().URLForResource("Squart-GameMusic", withExtension: "wav")!
do {
Data.GameMusic = try AVAudioPlayer(contentsOfURL: bgMusicURL, fileTypeHint: nil)
} catch {
return print("no music file")
}
Data.GameMusic.numberOfLoops = 1
Data.GameMusic.prepareToPlay()
Data.GameMusic.play()
The problem is that when I try to play music from Music Library the sound stops and it lets my app play music instead of the Music Library.
Check whether the music is playing or not in the background like this
if MPMusicPlayerController.systemMusicPlayer().playbackState == .Playing {
print("Music is playing")
} else {
print("Play your music")
}
Don't forget to import:
import MediaPlayer
And if you want to play the sound simultaneously then use this code to play the sound file
func playSound {
var soundID: SystemSoundID = 0
let soundURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), "myFileName", "mp3", nil)
AudioServicesCreateSystemSoundID(soundURL, &soundID)
AudioServicesPlaySystemSound(soundID)
}

iOS, swift: play background music and sound effects without delay

I am trying to create a game in iOS without using SpriteKit.
I am stuck in getting the sound effects to play in a timely manner. I've been using the following code which I have found online and the background music plays great. However, when I use the
"playSoundEffect" method it plays ok the first time but then starts to lag behind and becomes out of sync. I guess that happens because it initializes an AVAudioPlayer every time.
Anyone have a good idea in how to play sound effects in a timely manner, while also playing background music? Thanks!
import AVFoundation
public class SKTAudio: NSObject, AVAudioPlayerDelegate {
public var backgroundMusicPlayer: AVAudioPlayer?
public var soundEffectPlayer: AVAudioPlayer?
private var mainLoopFileName:String! {
let randomSong = Int(arc4random_uniform(3))
switch randomSong {
//case 0: return "Test.mp3"
//case 1: return "Test2.mp3"
case 0: return "SneakySnitch.mp3"
case 1: return "FasterDoesIt.mp3"
case 2: return "MonkeysSpinningMonkeys.mp3"
default:
break
}
return "SneakySnitch.mp3"
}
public class func sharedInstance() -> SKTAudio {
return SKTAudioInstance
}
public func playBackgroundMusic() {
let filename = mainLoopFileName
let url = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
if (url == nil) {
println("Could not find file: \(filename)")
return
}
var error: NSError? = nil
backgroundMusicPlayer = AVAudioPlayer(contentsOfURL: url, error: &error)
if let player = backgroundMusicPlayer {
player.numberOfLoops = 0
player.delegate = self
player.prepareToPlay()
player.play()
} else {
println("Could not create audio player: \(error!)")
}
}
public func pauseBackgroundMusic() {
if let player = backgroundMusicPlayer {
if player.playing {
player.pause()
}
}
}
public func resumeBackgroundMusic() {
if let player = backgroundMusicPlayer {
if !player.playing {
player.play()
}
}
}
public func playSoundEffect(filename: String) {
let url = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
if (url == nil) {
println("Could not find file: \(filename)")
return
}
var error: NSError? = nil
soundEffectPlayer = AVAudioPlayer(contentsOfURL: url, error: &error)
if let player = soundEffectPlayer {
player.numberOfLoops = 0
player.prepareToPlay()
player.play()
} else {
println("Could not create audio player: \(error!)")
}
}
// MARK: AVAudioPlayerDelegate
public func audioPlayerDidFinishPlaying(player: AVAudioPlayer!, successfully flag: Bool) {
println("finished playing \(flag)")
delay(5.0, {
self.playBackgroundMusic()
})
}
public func audioPlayerDecodeErrorDidOccur(player: AVAudioPlayer!, error: NSError!) {
println("\(error.localizedDescription)")
}
}
You could using AVPlayer to play your sound file. Keep one player, but change its AVPlayerItem to a new item when you need to play a new sound. It might be faster than recreating the player every time.
While AVAudioPlayer/AVPlayer is the simplest option, it will not give you the shortest delay or perfect synchronization when playing audio files. You should look into Audio Queues or Audio Units within Core Audio for more accurate sound playback.
The problem is that you are playing the second sound effect before the first one is finished. You are killing the reference to the first when you set the new AVAudioPlayer to your soundEffectPlayer variable and the first will stop playing.
If you don't mind loosing the availability of controlling the volume of the sound you could use this:
var mySound: SystemSoundID = 0
AudioServicesCreateSystemSoundID(url, &mySound)
// Play
AudioServicesPlaySystemSound(mySound)
Otherwise you can use AVAudioPlayer if you add each sound that you create into an array, keeping the reference. Then you can delete it when the sound is done by implementing AVAudioPlayer delegate.
func playSound(){
let path : NSString? = NSBundle.mainBundle().pathForResource("sound", ofType: "wav")!
let url = NSURL(fileURLWithPath: path!)
var error : NSError?
let sound = AVAudioPlayer(contentsOfURL: url, error: &error)
sound.delegate = self
sound.volume = 0.5
self.soundsEffectsArray.addObject(sound)
sound.prepareToPlay()
sound.play()
}
func audioPlayerDidFinishPlaying(player: AVAudioPlayer!, successfully flag: Bool) {
self.soundsEffectsArray.removeObject(player)
}

Resources