Sound is not playing when using AVFoundation in Swift - ios

I am trying to play some background music for my game. I am not getting an error, but the music itself is not playing at all. I have checked the sound on the iOS Simulator and on my MacBook but I cannot hear anything, so I was wondering if anyone knew what is wrong with my code. I have even tried declaring var musicPlayer: AVAudioPlayer! and var musicPlayer: AVAudioPlayer? but it still does not work. I am currently using Xcode version 13.1.
import AVFoundation
class GameViewController: UIViewController {
var musicPlayer = AVAudioPlayer()
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
// Build the menu scene
let menuScene = MenuScene()
let skView = self.view as? SKView
// Ignore drawing order of child nodes in order to increase performance
skView?.ignoresSiblingOrder = true
// Size the scene to fit the view exactly
menuScene.size = view.bounds.size
// Show the menu
skView?.presentScene(menuScene)
// Start background music
if let musicPath = Bundle.main.path(forResource: "backgroundMusic.m4a", ofType: nil) {
let url = URL(fileURLWithPath: musicPath)
do {
musicPlayer = try AVAudioPlayer(contentsOf: url)
musicPlayer.numberOfLoops = -1
musicPlayer.prepareToPlay()
musicPlayer.play()
}
catch {/* Couldn't load music file */}
}
}

move your audio player code into SKScene.didMove(to view: SKView) - i.e. in MenuScene not in your view controller

Related

AVPlayerLooper not looping my local video

I'm trying to get a video to play in my macOS project and I want it to continuously loop. I was able to get it to play once but I can't figure out how to get it to loop. I'm using the AVPlayerLooper but seem to be messing up somewhere. Here is my code:
import Cocoa
import AVKit
import AVFoundation
class ViewController: NSViewController {
#IBOutlet weak var videoPlayer: AVPlayerView!
override func viewDidLoad() {
//var loop: AVPlayerLooper?
super.viewDidLoad()
guard let path = Bundle.main.path(forResource: "Background", ofType:"mp4") else {
debugPrint("Not found")
return
}
let asset = AVAsset(url: URL(fileURLWithPath: path))
let item = AVPlayerItem(asset: asset)
let queuePlayer = AVQueuePlayer(playerItem: item)
let loop = AVPlayerLooper(player: queuePlayer, templateItem: item)
videoPlayer.player = queuePlayer
videoPlayer.player?.play()
}
}
I also have an AVPlayerView in my storyboard that I have connected to my viewcontroller.
It builds and runs fine but when I run it nothing shows up. I just get a black screen.
Any help is appreciated. Thanks!
AVPlayerLoop is sort of a "top level" object that manages both the player and the player item. So you have to keep it around, e.g. in a custom property of the view controller, or it will be released before it can really do anything
Since AVPlayerLoop manages the items in the AVQueuePlayer, initialize the queue player without passing in the player item

Play music if not playing, but don't if it is already playing (Xcode,Swift4)

I got a question about the AVFoundation and AVAudioPlayer.
In my project I have a view where you start in and a second on where the action happens. When I start the app I told the first view to start playing my background music and it has to continue when the user is in the second view. But whenever you come back to the first view it starts the song again, even though it is already playing. So I end up with a lot of echos.
How can I prevent that from happening?
Here is my code that I have yet:
import UIKit
import AVFoundation
class StartViewController: UIViewController {
var backgroundPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
//background music file
let sound = Bundle.main.path(forResource: "background_music", ofType: "mp3")
do {
backgroundPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: sound!))
}
catch {
print(error)
}
playBackgorundMusic()
}
func playBackgorundMusic() {
if backgroundPlayer.isPlaying {
//do nothing
} else {
backgroundPlayer.play()
}
}
}
You should make a singleton class for Music player to maintain state like play, pause, stop etc.
class MusicPlayerManager{
static let shared = MusicPlayerManager()
var backgroundPlayer = AVAudioPlayer()
init(){}
func playMusic(){
if backgroundPlayer.isPlaying {
//do nothing
} else {
backgroundPlayer.play()
}
}
}

Thread1:EXC_BAD_ACCESS(code=1, address = 0X48)

I'm new in Swift. I am trying to Run this code but got an error:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x48) a.
Error appears here:
self.player.play()
Can anyone help in this issue?
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
do {
if let audioPath = Bundle.main.path(forResource: "music", ofType: "mp3") {
try player = AVAudioPlayer(contentsOf: URL(fileURLWithPath: audioPath))
}
} catch {
print("ERROR")
}
self.player.play()
}
What you doing with your code is:
Creating an instance of AVAudioPlayer without any data
Trying to create a new instance with some data from music.mp3 file
Playing it
If the audioPath is not correct, then the player is not correctly created: application will use the one without valid data, leading to a crash during playback.
Your code can be written making the player optional, which helps preventing the crash:
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player: AVAudioPlayer?
override func viewDidLoad() {
super.viewDidLoad()
player = initializePlayer()
player?.play()
}
private func initializePlayer() -> AVAudioPlayer? {
guard let path = Bundle.main.path(forResource: "music", ofType: "mp3") else {
return nil
}
return try? AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
}
}
Some changes performed to the code:
player is now optional: there is no need to initialize it twice. Plus if the initialization goes well you can use it without problems, otherwise your app won't crash.
The play method is now in an optional chain
I've moved the initialization of the player in a separate private methods, to maintain the code readable. Feel free to change it, it is just a suggestion.
I've got it. The issue is that the file isn't being coping to my app bundle. To fix it:
Click your project
Click your target
Select Build Phases
Expand Copy Bundle Resources
Click '+' and select your file.
Thanks Denis Hennessy

Stopping Music in SpriteKit when leaving a GameScene

I have implemented background music in my game using AVFoundation. However, when leaving the GameScene the music continues playing. I know that in viewControllers you can use viewWillDisappear but I can't find anything similar for GameScenes.
var audioPlayer = AVAudioPlayer()
let audioPath = Bundle.main.path(forResource: "electroDrive", ofType: "wav")
//.......//
//.......//
do {
try audioPlayer = AVAudioPlayer(contentsOf: URL(fileURLWithPath: audioPath!))
}
catch {
// process error
}
audioPlayer.play()
//Code to go to other scene
if viewController != nil {
self.viewController?.performSegue(withIdentifier: "push", sender: viewController)
}
Stop the AVAudioPlayer before segueing to the next Scene
Insert this before your segue code
audioPlayer.pause()

iOS Swift AVAudioPlayer playAtTime method doesn't work

My code is very simple like this:
import UIKit
import AVFoundation
class VC: UIViewController {
var player:AVAudioPlayer!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let url = NSURL.fileURLWithPath(NSBundle.mainBundle().pathForResource("audioName", ofType: "mp3")!)
do{
try player = AVAudioPlayer(contentsOfURL: url)
print("duration", player.duration)// duration 200
player.prepareToPlay()
player.playAtTime(50.0)
}catch{
}
}
}
when player.play() is called, the audio can play normally.
I don't know why the playAtTime function doesn't work.
Please help!
playAtTime plays the sound at a time in the future, specified relative to the device's current time. So the time parameter needs to be the current device time plus your required delay (in seconds):
player.playAtTime(player.currentDeviceTime + 50)
player.currentTime will be 0 when you initialise the player so this isn't the property to use.
Apple doc is here
func playAtTime(time: NSTimeInterval) -> Bool
This function is meant to play the sound some time in the future, based on and greater than deviceCurrentTime, i would suggest to try:
player.playAtTime(player.currentTime + 50.0)

Resources