Swift AVAudioPlayer wont play audio recorded with AVAudioRecorder - ios

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

Related

What is the best way to play a sound with the least amount of delay possible, in Swift/Xcode?

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

Music playing over the song when i return to the original VC

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

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?

iOS Xode : AVAudioPlayer is not playing WAV file correctly

I have a WAV file with tones at around 18kHz. The audio is 16-bit PCM mono.
I am using the following function to play the file:
func playSound(name: String) {
let documents = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let path = documents.appending("/").appending(name)
let url = NSURL.fileURL(withPath: path)
do {
player = try AVAudioPlayer(contentsOf: url)
guard let player = player else { return }
player.prepareToPlay()
player.play()
} catch let error {
print(error.localizedDescription)
}
}
When I play it on my iOS 10.2 device I hear a series of tones between 1000Hz to 10000Hz. I've analyzed the rendered audio by capturing it and a frequency plot shows that the original content at 18kHz is there, but there are also tones present between 1000Hz to 10000Hz. When I play the same WAV file with VLC or any other desktop audio player, I don't hear the tones (which is expected since they're located around 18kHz). I suspect that the code above isn't loading the data correctly or that the player isn't properly initialized, so I need a seasoned iOS veteran who can tell me what I'm doing wrong.
Thank you in advance.
Check the following code it might work for you
import UIKit
import AVFoundation
class ViewController: UIViewController {
var audioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
var alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("button-09", ofType: "wav"))
println(alertSound)
// Removed deprecated use of AVAudioSessionDelegate protocol
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
var error:NSError?
audioPlayer = AVAudioPlayer(contentsOfURL: alertSound, error: &error)
audioPlayer.prepareToPlay()
audioPlayer.play()
}
}

Background Music Stop/Mute on iOS

I'm creating my first app. I have an app with music playing in the background with the following code:
var backgroundMusicPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
//background Music
func playBackgroundMusic(filename: String) {
let url = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
guard let newURL = url else {
print("Could not find file: \(filename)")
return
}
do {
backgroundMusicPlayer = try AVAudioPlayer(contentsOfURL: newURL)
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
} catch let error as NSError {
print(error.description)
}
}
playBackgroundMusic("Starship.wav")
}
So what should I do in order to stop/mute the background music when I switch to another ViewController? Should I do this my FirstViewController or SecondViewController?
Obviously, I don't want the sound to be off in the SecondViewController as I have other stuff that will be playing there.
To mute sound I simply mute the volume.
backgroundMusicPlayer.volume = 0
and set it to normal if I want sound
backgroundMusicPlayer.volume = 1
If you just want to pause music you can call
backgroundMusicPlayer.pause()
To resume you call
backgroundMusicPlayer.resume()
If you want to stop music and reset it to the beginning you say this
backgroundMusicPlayer.stop()
backgroundMusicPlayer.currentTime = 0
backgroundMusicPlayer.prepareToPlay()
Did you also consider putting your music into a singleton class so its easier to play music in your different viewControllers.
Not sure this is what you are looking for as your question is a bit vague.

Resources