I am calling this function playSound() in viewWillAppear, the audio starts playing and then suddenly volume increases with a minor break in playback.
here is my code
func playSound() {
let url = Bundle.main.url(forResource: "music", withExtension: "mp3")!
do {
try self.player = AVAudioPlayer(contentsOf: url)
player?.prepareToPlay()
player?.numberOfLoops = -1
player?.play()
} catch let error as NSError {
KBLog.log(object: error.description)
}}
Edit 1 :
I am using this code for an iPad app, so ipad only have speaker, no earpiece, if this has something to do with the issue.
The problem was i was playing this audio and at the same time i was initialising my publisher view for starting the video call, thus an instance of AVPlayer was initialised to take audio input i.e. two instances of AVPlayer were getting initialised causing the break in playback.
Related
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.
I am experiencing a slight delay between UIButton press and sound output (it seems to average about 0.05 seconds). I am currently implementing AVAudioPlayer. Is there a best way to play a sound instantaneously? I scoured the Internet but can't find a concrete answer.
For reference, my (simplified) code is as follows:
// Get sound url
let soundURL = URL(fileURLWithPath: Bundle.main.path(forResource: file, ofType: type)!)
// Audio Player
var audioPlayer: AVAudioPlayer!
do {
// Create audio player
audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
}
catch let errMsg as NSError {
print("AVAudioPlayer error: \(errMsg.localizedDescription)")
}
// Prepare audio player to play
audioPlayer.prepareToPlay()
//...
#IBAction func btnPressed(_ sender: UIButton) {
audioPlayer.play()
}
I am using xcode 9 and swift 4 for my app. In my app i have music playing in the viewDidLoad. When i exit the view controller to go to another View, it continues to play like it should. How ever, when i return to that view controller the song starts to play again. This song is overlapping the song that first loaded. Do you guys have any ideas on how to stop this from happening?
do
{
let audioPath = Bundle.main.path(forResource: "APP4", ofType: "mp3")
try player = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL)
}
catch
{
//catch error
}
let session = AVAudioSession.sharedInstance()
do
{
try session.setCategory(AVAudioSessionCategoryPlayback)
}
catch
{
}
player.numberOfLoops = -1
player.play()
It starts playing again, because your viewDidLoad is called again, which asks it to play it again. A simplest fix would be to keep a static bool variable to keep track if you have already made this call.
static var isMusicPlaying: Bool = false
In your viewDidLoad, you can put code before the code that calls the play.
guard !isMusicPlaying else {
return
}
isMusicPlaying = true
I have a single view application with a few view each having their own view controller class. I also have a game game and a game view controller, I was wondering how I could play a game music starting from the start screen view controller and have it continue to play through out all of my views and game scene? So that way I don't have to say play when x-screen is loaded so it doesn't restart every time the player changes views.
You can play the music form your AppDelegate. Put this code in your applicationDidFinishLaunching()
let url = Bundle.main.url(forResource: "soundName", withExtension: "mp3")!
var player: AVAudioPlayer!
do {
player = try AVAudioPlayer(contentsOf: url)
guard let player = player else { return }
player.prepareToPlay()
player.play()
} catch let error {
print(error.localizedDescription)
}
This will play the music as soon as your app is launched and continue until told to stop or the app closes.
I have built an app that records an audio clip, and saves it to a file called xxx.m4a.
Once the recording is made, I can find this xxx.m4a file on my system and play it back - and it plays back fine. The problem isn't recording this audio track.
My problem is playing this audio track back from within the app.
// to demonstrate how I am building the recordedFileURL
let currentFileName = "xxx.m4a"
let dirPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let docsDir: AnyObject = dirPaths[0]
let recordedFilePath = docsDir.stringByAppendingPathComponent(currentFileName)
var recordedFileURL = NSURL(fileURLWithPath: recordedFilePath)
// quick check for my own sanity
var checkIfExists = NSFileManager.defaultManager()
if checkIfExists.fileExistsAtPath(recordedFilePath){
println("audio file exists!")
}else{
println("audio file does not exist")
}
// play the recorded audio
var error: NSError?
let player = AVAudioPlayer(contentOfURL: recordedFileURL!, error: &error)
if player == nil {
println("error creating avaudioplayer")
if let e = error {
println(e.localizedDescription)
}
}
println("about to play")
player.delegate = self
player.prepareToPlay()
player.volume = 1.0
player.play()
println("told to play")
// -------
// further on in this class I have the following delegate methods:
func audioPlayerDidFinishPlaying(player: AVAudioPlayer!, successfully flag: Bool){
println("finished playing (successfully? \(flag))")
}
func audioPlayerDecodeErrorDidOccur(player: AVAudioPlayer!, error: NSError!){
println("audioPlayerDecodeErrorDidOccur: \(error.localizedDescription)")
}
My console output when I run this code is like this:
audio file exists!
about to play
told to play
I dont get anything logged into the console from either audioPlayerDidFinishPlaying or audioPlayerDecodeErrorDidOccur.
Can someone explain to me why I my audio clip isnt playing?
Many thanks.
You playing code is fine; your only problem is with the scope of your AVAudioPlayer object. Basically, you need to keep a strong reference to the player around all the time it's playing, otherwise it'll be destroyed by ARC, as it'll think you don't need it any more.
Normally you'd make the AVAudioPlayer object a property of whatever class your code is in, rather than making it a local variable in a method.
In the place where you want to use the Playback using speakers before you initialize your AVAudioPlayer, add the following code:
let recordingSession = AVAudioSession.sharedInstance()
do{
try recordingSession.setCategory(AVAudioSessionCategoryPlayback)
}catch{
}
and when you are just recording using AVAudioRecorder use this before initializing that:
do {
recordingSession = AVAudioSession.sharedInstance()
try recordingSession.setCategory(AVAudioSessionCategoryRecord)
}catch {}