Sound recorded but not playing in iOS - swift

In an iOS app, I record a sound (.caf): I can see the file is in the Documents directory and I can drag it from iTunes to my desktop and play it (it contains what I record).
I test this thing on a regular device and when I play the sound I can't hear any sound: the device volume is at its maximum.
The code I use to play the sound is the following and returns no error:
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let fullPath = (documentsDirectory as NSString).stringByAppendingPathComponent("docu.caf")
let url = NSURL.fileURLWithPath(fullPath)
do {
let sound = try AVAudioPlayer(contentsOfURL: url)
// sound is printed as <AVAudioPlayer: 0x14ed5bd20>
print(sound)
sound.prepareToPlay()
sound.play()
} catch {
// never gets called
print("couldn't load any file")
}
Any idea is appreciated...

Related

Error in playing some type of audio file in swift

I would like to play audio Files from the directory in swift and I use following code for initializing:
func setAVAudioPlayer(){
let fileName = URL(string: audioFilename)?.lastPathComponent
let fileURL = NSURL(fileURLWithPath: myPath)
let destinationPath = fileURL.appendingPathComponent(fileName!)
let filePath = destinationPath?.path
let fileManager : FileManager = FileManager.default
if fileManager.fileExists(atPath: filePath!){
ChannelViewController.audioPlayerArrayList[indexPathRow] = AVAudioPlayer()
let path: String! = myPath + "/" + fileName!
let mp3URL = NSURL(fileURLWithPath: path)
do
{
ChannelViewController.audioPlayerArrayList[indexPathRow] = try AVAudioPlayer(contentsOf: mp3URL as URL)
}
catch
{
print("An error occurred while trying to extract audio file")
print(error)
}
}
}
It works for some audio type like m4a, mp3, etc. but I get error for some type like 3gp files.
How can I fix this issue and this function works for all audio type.
Error:
2020-11-29 10:16:57.828589+0330 School_iApp[4038:63911] 3531: MP4Parser_PacketProvider->GetASBD() failed
2020-11-29 10:16:57.829053+0330 School_iApp[4038:63911] 101: OpenFromDataSource failed
2020-11-29 10:16:57.829252+0330 School_iApp[4038:63911] 76: Open failed
An error occurred while trying to extract audio file
Error Domain=NSOSStatusErrorDomain Code=2003334207 "(null)"
Thanks in advance.
AVAudioPlayer is a Audio player your tried to play a mp4 data that’s a video. That will not work. But there is a way in AvFoundation to take the audio from a Video.
Here is something to read for you: https://developer.apple.com/documentation/avfoundation/media_playback_and_selection/creating_a_basic_video_player_ios_and_tvos

How to fix 'appendingPathComponent' is unavailable: Use appendingPathComponent on URL error

I'm working on a old Swift tutorial (Swift 2.0) that's posted on Ray Wenderlich's web site (https://www.raywenderlich.com/2185-how-to-make-a-letter-word-game-with-uikit-and-swift-part-3-3) and I'm running into an error when I tried to re-setup a function called "preloadAudioEffects" in Swift 4.2 . The error? appendingPathComponent' is unavailable: Use appendingPathComponent on URL instead.
I've tried to rename the old Swift code [Ex: NSBundle to Bundle , stringByAppendingPathComponent to appendingPathComponent()], but I'm still running into some syntax issues due to my inexperience with Swift.
This is the original code:
func preloadAudioEffects(effectFileNames:[String]) {
for effect in AudioEffectFiles {
//1 get the file path URL
let soundPath = NSBundle.mainBundle().resourcePath!.stringByAppendingPathComponent(effect)
let soundURL = NSURL.fileURLWithPath(soundPath)
//2 load the file contents
var loadError:NSError?
let player = AVAudioPlayer(contentsOfURL: soundURL, error: &loadError)
assert(loadError == nil, "Load sound failed")
//3 prepare the play
player.numberOfLoops = 0
player.prepareToPlay()
//4 add to the audio dictionary
audio[effect] = player
}
}
And this is what I've tried to do via following the suggestions in Xcode:
func preloadAudioEffects(effectFileNames:[String]) {
for effect in AudioEffectFiles {
//1 get the file path URL
let soundPath = Bundle.main.resourcePath!.appendingPathComponent(effect)
let soundURL = NSURL.fileURL(withPath: soundPath)
//2 load the file contents
var loadError:NSError?
let player = AVAudioPlayer(contentsOfURL: soundURL, error: &loadError)
assert(loadError == nil, "Load sound failed")
//3 prepare the play
player.numberOfLoops = 0
player.prepareToPlay()
//4 add to the audio dictionary
audio[effect] = player
}
}
Get the full path to the sound file and convert it to a URL by using NSURL.fileURLWithPath().
Call AVAudioPlayer(contentsOfURL:error:) to load a sound file in an audio player.
Set the numberOfLoops to zero so that the sound won’t loop at all. Call prepareToPlay() to preload the audio buffer for that sound.
Finally, save the player object in the audio dictionary, using the name of the file as the dictionary key.
Just replace resourcePath with resourceURL
let soundURL = Bundle.main.resourceURL!.appendingPathComponent(effect)
and you have to wrap the AVAudioPlayer initializer in a try block
func preloadAudioEffects(effectFileNames:[String]) {
for effect in AudioEffectFiles {
let soundURL = Bundle.main.resourceURL!.appendingPathComponent(effect)
//2 load the file contents
do {
let player = try AVAudioPlayer(contentsOf: soundURL)
//3 prepare the play
player.numberOfLoops = 0
player.prepareToPlay()
//4 add to the audio dictionary
audio[effect] = player
} catch { print(error) }
}
}

Two simultaneous AVAudioSessions

I have an app that uses WebRTC for voice chat.
After a lot of troubleshooting I came to the conclusion that whenever I receive a stream, the WebRTC will set the AVAudioSession shared instance to the following:
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .voiceChat, options: .allowBluetooth)
I want to play an audio file (about a second long) and if I simply load it and do player.play(), it won't play. If I try to set the category to .playback or anything that would make sense for an audio file, the stream's audio will stop playing.
I also tried to create a new AVAudioSession instance but once the audio file plays, the stream's audio will stop.
Is there a way for me to get the audio file to play without affecting the stream's audio and its configuration?
Here's the full code
private func playTapSound() {
guard let url = Bundle.main.url(forResource: App.AudioFile.emojiTapSound, withExtension: "mp3") else { return }
do {
let audioSesson = AVAudioSession()
try audioSesson.setCategory(.playback, mode: .default)
try audioSesson.setActive(true)
audioPlayer = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)
guard let player = audioPlayer else { return }
player.play()
} catch let error {
LogManager.shared.print("Error - playing emoji tap audio -", error.localizedDescription, type: [.Error, .Audio])
}
// var chime1URL: NSURL?
// var chime1ID: SystemSoundID = 0
// let filePath = Bundle.main.path(forResource: "emojiTapSound", ofType: "wav")
// chime1URL = NSURL(fileURLWithPath: filePath!)
// AudioServicesCreateSystemSoundID(chime1URL!, &chime1ID)
// AudioServicesPlaySystemSound(0)
}
I also tried using the AudioToolBox but for some reason it only played on the simulator. I verified that it's not my phone's settings by trying one of Apple's preloaded sounds and it worked.

How can I play a sound from the asset catalog using AVAudioPlayerNode?

I'm trying to use an AVAudioPlayerNode to play sounds from the Assets.xcassets asset catalog, but I can't figure out how to do it.
I've been using AVAudioPlayer, which can be initialized with an NSDataAsset like this:
let sound = NSDataAsset(name: "beep")!
do {
let player = try AVAudioPlayer(data: sound.data, fileTypeHint: AVFileTypeWAVE)
player.prepareToPlay()
player.play()
} catch {
print("Failed to create AVAudioPlayer")
}
I want to use an AVAudioPlayerNode instead (for pitch shifting and other reasons). I can create the engine and hook up the node OK:
var engine = AVAudioEngine()
func playSound(named name: String) {
let mixer = engine.mainMixerNode
let playerNode = AVAudioPlayerNode()
engine.attach(playerNode)
engine.connect(playerNode, to: mixer, format: mixer.outputFormat(forBus: 0))
// play the file (this is what I don't know how to do)
}
It looks like the method to use for playing the file is playerNode.scheduleFile(). It takes an AVAudioFile, so I thought I'd try to make one. But the initializer for AVAudioFile wants a URL. As far as I can tell, assets in the asset catalog are not available by URL. I can get the data directly using NSDataAsset, but there doesn't seem to be any way to use it to populate an AVAudioFile.
Is it possible to play sounds from the asset catalog with an AVAudioPlayerNode? And if so, how?
OK so your problem is that you would like to get a URL from a file in your Asset catalog right?
I've looked around but only found this answer
As it says
It basically just gets image from assets, saves its data to disk and return file URL
You should probably change it to look for MP3 files (or WAV or whatever you prefer, maybe that could be an input parameter)
So you could end up with something like:
enum SoundType: String {
case mp3 = "mp3"
case wav = "wav"
}
class AssetExtractor {
static func createLocalUrl(forSoundNamed name: String, ofType type: SoundType = .mp3) -> URL? {
let fileManager = FileManager.default
let cacheDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let url = cacheDirectory.appendingPathComponent("\(name).\(type)")
guard fileManager.fileExists(atPath: url.path) else {
guard
let image = UIImage(named: name),
let data = UIImagePNGRepresentation(image)
else { return nil }
fileManager.createFile(atPath: url.path, contents: data, attributes: nil)
return url
}
return url
}
}
Maybe a bit far fetched but I haven't found any other options.
Hope that helps you.

Load temp video and play

I need to play continuously a video stored inside the temp directory.
func setVideo(url vid: String!) {
let directory = NSTemporaryDirectory()
let tempURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("tempMovie\(Date().timeIntervalSince1970)").appendingPathExtension("mp4")
let tempFile = NSURL.fileURL(withPathComponents: [directory])
let file = vid.components(separatedBy: ".")
guard let path = Bundle.main.path(forResource: "\(tempURL)", ofType:file[2]) else {
debugPrint( "\(file.joined(separator: ".")) not found with path: \(file[0] + "." + file[1])")
return
}
let player = AVPlayer(url: URL(fileURLWithPath: path))
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.bounds
self.videoView.layer.addSublayer(playerLayer)
player.play()
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: nil, using: { (_) in
DispatchQueue.main.async {
self.player?.seek(to: kCMTimeZero)
self.player?.play()
}
})
}
The video location and file url is:
file:///private/var/mobile/Containers/Data/Application/E7D12401-F147-4905-83BE-72909F91E004/tmp/tempMovie1501672791.33525.mp4
The continuous loop part works from a previous project however I can't seem to get the temp stored video to load inside the player. I've tried to get the temp directory manually but it didn't work.
Moving the video from tmp to Documents directory worked for me.
This is how you do it:
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destinationURL = documentsDirectoryURL.appendingPathComponent("filename.fileExtension")
do {
try FileManager.default.moveItem(at: previousTempLocation, to: destinationURL)
} catch let error as NSError { print(error.localizedDescription)}
let player = AVPlayer(url: destinationURL)
Apple says this on /tmp directory
Use this directory to write temporary files that do not need to
persist between launches of your app. Your app should remove files
from this directory when they are no longer needed; however, the
system may purge this directory when your app is not running.
Apple also recommends this for user data
Put user data in Documents/. User data generally includes any files
you might want to expose to the user—anything you might want the user
to create, import, delete or edit. Video and audio apps may even include
files that the user has downloaded to watch or listen to later.