AVAudioPlayer Object Sound Not Playing - Swift - ios

I have created an object class of AVAudioPlayer below in order to play a noise:
class MusicAudio: NSObject, AVAudioPlayerDelegate {
var bassAudio : AVAudioPlayer! = AVAudioPlayer()
override init() {
super.init()
let bassAudioPath = NSBundle.mainBundle().pathForResource("Raw", ofType:"mp3")
let fileURL = NSURL(fileURLWithPath: bassAudioPath!)
bassAudio = AVAudioPlayer(contentsOfURL: fileURL, error: nil)
bassAudio.currentTime = 0
bassAudio.volume = 1.0
bassAudio.delegate = self
}
func adjustAudioLayerState(volumeOn:Bool, layer:AudioLayerState) {
if layer == AudioLayerState.Bass {
self.bassAudio.prepareToPlay()
self.bassAudio.play()
}
}
}
To play the noise, I call the class via:
#IBAction func testSound() {
var sound:MusicAudio = MusicAudio()
sound.adjustAudioLayerState(true, layer: AudioLayerState.Bass)
}
However I am not getting any audio played back, can anyone spot a problem with my implementation?
Edit: After rdelmar answer
var bassAudio : AVAudioPlayer! = AVAudioPlayer()
class MusicAudio: NSObject, AVAudioPlayerDelegate {
override init() {
super.init()
let bassAudioPath = NSBundle.mainBundle().pathForResource("Raw", ofType:"mp3")
let fileURL = NSURL(fileURLWithPath: bassAudioPath!)
bassAudio = AVAudioPlayer(contentsOfURL: fileURL, error: nil)
bassAudio.currentTime = 0
bassAudio.volume = 1.0
bassAudio.delegate = self
}
func adjustAudioLayerState(volumeOn:Bool, layer:AudioLayerState) {
if layer == AudioLayerState.Bass {
self.bassAudio.prepareToPlay()
self.bassAudio.play()
}
}
}

You don't get any sound because your MusicAudio class is being deallocated. You need to make the var, sound, a property, not a local variable.
class ViewController: UIViewController {
var sound = MusicAudio()
#IBAction func testSound() {
self.sound.adjustAudioLayerState(true, layer: AudioLayerState.Bass)
}
}

Related

AVFragmentedAssetMinder not working on iPhone device but works on simulator

We have fragmented movie data that comes through WebSecureSocket (wss://). We are writing that into a temp.mp4 and simultaneously playing the fragments that got written into the file. So, we used AVFragmentedAsset and AVFragmentedAssetMinder for this. It works as expected in the simulator. But in the device, doesn't update the duration of the asset and doesn't post the .AVAssetDurationDidChange notification. Not sure what could be the issue. Already spent 2 days figuring out this but no luck. Can someone help me with this please,
Implementation..
public class WssPlayerSource: WebSocketDelegate {
static let ASSET_MINDER = AVFragmentedAssetMinder()
private let socketClient: WebSocketStreamingClient
private var tempMovieFile:FileHandle? = nil
private var startedPlay = false
private var fragmentedAsset: AVFragmentedAsset? = nil
let player = AVPlayer()
init(withUrl url: URL) {
socketClient = WebSocketStreamingClient(wssUrl: url)
socketClient.delegate = self
do {
self.tempMovieFile = try getTemporaryMovieFileWriter()
}catch {
print("Error opening file")
}
NotificationCenter.default.addObserver(self, selector: #selector(onVideoUpdate), name: .AVAssetDurationDidChange, object: nil)
socketClient.connect()
}
// Socket delegate
public func didReceive(event: WebSocketEvent, client: WebSocket) {
switch event {
case .binary(let data):
self.tempMovieFile?.write(data)
if !startedPlay {
startedPlay = true
DispatchQueue.global().async { [weak self] in
self?.didReceivedInitialData()
}
}
break;
default:
break;
}
}
func didReceivedInitialData() {
fragmentedAsset = AVFragmentedAsset(url: getTemporaryMovieFile()!)
fragmentedAsset?.loadValuesAsynchronously(forKeys: ["duration", "containsFragments", "canContainFragments"], completionHandler: {
WssPlayerSource.ASSET_MINDER.mindingInterval = 1
WssPlayerSource.ASSET_MINDER.addFragmentedAsset(self.fragmentedAsset!)
self.player.replaceCurrentItem(with: AVPlayerItem(asset: self.fragmentedAsset!))
self.player.play()
})
}
#objc
func onVideoUpdate() {
print("Video duration updated..")
// This is not called in device.. but in simulator it works as expected
}
func stop() {
socketClient.forceDisconnect()
NotificationCenter.default.removeObserver(self)
if let asset = self.fragmentedAsset {
WssPlayerSource.ASSET_MINDER.addFragmentedAsset(asset)
}
}
}
And
class ViewController: UIViewController {
#IBOutlet var playerView:PlayerView!
private static let SOCKET_URL = "wss://..."
private var playerSource:WssPlayerSource? = nil
private var playerLayer:AVPlayerLayer? = nil
override func viewDidLoad() {
super.viewDidLoad()
self.playerSource = WssPlayerSource(withUrl: URL(string: ViewController.SOCKET_URL)!)
self.playerLayer = AVPlayerLayer(player: self.playerSource!.player)
self.playerLayer?.videoGravity = .resize
self.playerView.layer.addSublayer(self.playerLayer!)
}
}

1 Button triggering random sounds (from an array?) in iOS

I've figured out how to trigger a sound when the button is pressed. I'm stuck on triggering a random sound when that same button is pressed. Since the audio player will take a string, I've generated a random number, but don't know how to insert that random number in the player. The code below is really broken so please bear with me there, I'm just stuck.
import UIKit
import AVFoundation
class ViewController: UIViewController {
#IBOutlet weak var mainButton: UIButton!
var sound1: AVAudioPlayer?
var sound2: AVAudioPlayer?
var sound3: AVAudioPlayer?
var sound4: AVAudioPlayer?
//There are many more sounds, but this is short for the example
var audioPlayer: AVAudioPlayer = AVAudioPlayer()
func setupAudioPlayerWithFile(file: NSString, type: NSString) -> AVAudioPlayer? {
let path = NSBundle.mainBundle().pathForResource(file as String, ofType: type as String)
let url = NSURL.fileURLWithPath(path!)
var audioPlayer : AVAudioPlayer?
do {
try audioPlayer = AVAudioPlayer(contentsOfURL: url)
} catch {
print("Player not available")
}
return audioPlayer
}
override func viewDidLoad() {
super.viewDidLoad()
if let sound1 = self.setupAudioPlayerWithFile("sound1", type: "aif")
{self.sound1 = sound1}
if let sound2 = self.setupAudioPlayerWithFile("sound2", type: "aif")
{self.sound2 = sound2}
if let sound3 = self.setupAudioPlayerWithFile("sound3", type: "aif")
{self.sound3 = sound3}
if let sound4 = self.setupAudioPlayerWithFile("sound4", type: "aif")
{self.sound4 = sound4}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func buttonPressed(sender: AnyObject){
let audioArray: NSArray = ["1, 2, 3, 4"]
let range: UInt32 = UInt32(audioArray.count)
number = Int(arc4random_uniform(range))
sound(number)?.play()
}
}
class ViewController: UIViewController {
#IBOutlet weak var mainButton: UIButton!
// PUT SOUNDS AS STRINGS IN ARRAY
var arrayOfSounds = ["sound1", "sound2", "sound3", "sound4"]
var audioPlayer: AVAudioPlayer = AVAudioPlayer()
func setupAudioPlayerWithFile(file: NSString, type: NSString) -> AVAudioPlayer? {
let path = NSBundle.mainBundle().pathForResource(file as String, ofType: type as String)
let url = NSURL.fileURLWithPath(path!)
var audioPlayer : AVAudioPlayer?
do {
try audioPlayer = AVAudioPlayer(contentsOfURL: url)
} catch {
print("Player not available")
}
return audioPlayer
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func buttonPressed(sender: AnyObject){
let range: UInt32 = UInt32(arrayOfSounds.count)
number = Int(arc4random_uniform(range))
//FIND OUT WHICH SOUND HERE
let sound = self.setupAudioPlayerWithFile(arrayOfSounds[number], type: "aif")
sound.play()
}
}

Xcode 7/Swift 2 Extra argument 'error' in call [duplicate]

I just opened my old project in Xcode 7 beta. The code works perfectly fine in Xcode 6, but now it's showing many error. I don"t know what those are. Can anybody explain why this happened, and how to fix it? Thank you! Here is the Code
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player: AVAudioPlayer = AVAudioPlayer()
#IBOutlet weak var firstCardImageView: UIImageView!
#IBOutlet weak var secondCardImageView: UIImageView!
#IBOutlet weak var label: UILabel!
var cardNamesArray:[String] = ["dice1","dice2","dice3","dice4","dice5","dice6"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func rollaction(sender: AnyObject) {
updateAction()
}
func updateAction(){
var firstRandomNumber = Int(arc4random_uniform(5))
var firstCardString:String = String(self.cardNamesArray[firstRandomNumber])
var secondRandomNumber = Int(arc4random_uniform(5))
var secondCardString:String = String(self.cardNamesArray[secondRandomNumber])
self.firstCardImageView.image = UIImage(named: firstCardString)
self.secondCardImageView.image = UIImage(named: secondCardString)
var fileLocation = NSBundle.mainBundle().pathForResource("sound", ofType: ".mp3")
var error: NSError? = nil
player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: fileLocation!), error: &error) //Error: Cannot find an initializer for type 'AVAudioPlayer' that accepts an argument list of type '(contentsOfURL: NSURL, error: inout NSError?)'
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient, error: nil) //Error:Extra argument 'error' in call
AVAudioSession.sharedInstance().setActive(true, error: nil) //Error:Extra argument 'error' in call
player.play()
let num = firstRandomNumber + secondRandomNumber + 2
self.label.text = "The sum is \(num)"
}
override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent) {
if event.subtype == UIEventSubtype.MotionShake { //Error:Method does not override any method from its superclass
updateAction()
}
}
}
Here's your updateAction() function with Swift 2.0's do/try/catch implementation:
func updateAction(){
var firstRandomNumber = Int(arc4random_uniform(5))
var firstCardString:String = String(self.cardNamesArray[firstRandomNumber])
var secondRandomNumber = Int(arc4random_uniform(5))
var secondCardString:String = String(self.cardNamesArray[secondRandomNumber])
self.firstCardImageView.image = UIImage(named: firstCardString)
self.secondCardImageView.image = UIImage(named: secondCardString)
let fileLocation = NSBundle.mainBundle().pathForResource("sound", ofType: ".mp3")
do {
player = try AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: fileLocation!))
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)
try AVAudioSession.sharedInstance().setActive(true)
}
catch {
print("Something bad happened. Try catching specific errors to narrow things down")
}
player.play()
let num = firstRandomNumber + secondRandomNumber + 2
self.label.text = "The sum is \(num)"
}

Swift 2 migration problems

I just opened my old project in Xcode 7 beta. The code works perfectly fine in Xcode 6, but now it's showing many error. I don"t know what those are. Can anybody explain why this happened, and how to fix it? Thank you! Here is the Code
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player: AVAudioPlayer = AVAudioPlayer()
#IBOutlet weak var firstCardImageView: UIImageView!
#IBOutlet weak var secondCardImageView: UIImageView!
#IBOutlet weak var label: UILabel!
var cardNamesArray:[String] = ["dice1","dice2","dice3","dice4","dice5","dice6"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func rollaction(sender: AnyObject) {
updateAction()
}
func updateAction(){
var firstRandomNumber = Int(arc4random_uniform(5))
var firstCardString:String = String(self.cardNamesArray[firstRandomNumber])
var secondRandomNumber = Int(arc4random_uniform(5))
var secondCardString:String = String(self.cardNamesArray[secondRandomNumber])
self.firstCardImageView.image = UIImage(named: firstCardString)
self.secondCardImageView.image = UIImage(named: secondCardString)
var fileLocation = NSBundle.mainBundle().pathForResource("sound", ofType: ".mp3")
var error: NSError? = nil
player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: fileLocation!), error: &error) //Error: Cannot find an initializer for type 'AVAudioPlayer' that accepts an argument list of type '(contentsOfURL: NSURL, error: inout NSError?)'
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient, error: nil) //Error:Extra argument 'error' in call
AVAudioSession.sharedInstance().setActive(true, error: nil) //Error:Extra argument 'error' in call
player.play()
let num = firstRandomNumber + secondRandomNumber + 2
self.label.text = "The sum is \(num)"
}
override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent) {
if event.subtype == UIEventSubtype.MotionShake { //Error:Method does not override any method from its superclass
updateAction()
}
}
}
Here's your updateAction() function with Swift 2.0's do/try/catch implementation:
func updateAction(){
var firstRandomNumber = Int(arc4random_uniform(5))
var firstCardString:String = String(self.cardNamesArray[firstRandomNumber])
var secondRandomNumber = Int(arc4random_uniform(5))
var secondCardString:String = String(self.cardNamesArray[secondRandomNumber])
self.firstCardImageView.image = UIImage(named: firstCardString)
self.secondCardImageView.image = UIImage(named: secondCardString)
let fileLocation = NSBundle.mainBundle().pathForResource("sound", ofType: ".mp3")
do {
player = try AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: fileLocation!))
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)
try AVAudioSession.sharedInstance().setActive(true)
}
catch {
print("Something bad happened. Try catching specific errors to narrow things down")
}
player.play()
let num = firstRandomNumber + secondRandomNumber + 2
self.label.text = "The sum is \(num)"
}

Using Swift to display title of currently playing .MP3

I was wondering if there was a straight forward way of displaying the image and title of the currently playing MP3 file my music player is playing. My code is as follows and is very simple for this example. I am only wanting to test using one .MP3 that I've included in my project.
class ViewController: UIViewController {
let audioPath:NSURL! = NSBundle.mainBundle().URLForResource("SippinOnFire", withExtension: "mp3")
#IBOutlet var sliderValue: UISlider!
var player:AVAudioPlayer = AVAudioPlayer()
#IBAction func play(sender: AnyObject) {
player.play()
//println("Playing \(audioPath)")
}
#IBAction func pause(sender: AnyObject) {
player.pause()
}
#IBAction func stop(sender: AnyObject) {
player.stop()
player.currentTime = 0;
}
#IBAction func sliderChanged(sender: AnyObject) {
player.volume = sliderValue.value
}
override func viewDidLoad() {
super.viewDidLoad()
var error:NSError? = nil
player = AVAudioPlayer(contentsOfURL: audioPath!, error: &error)
player.volume = 0.5;
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
So far I am able to println the full path to the MP3 file but have not been able to use the display the image that is showing as the cover art for my MP3 in the project or to display only the title of the track playing. Could someone help me out with this?
you need to add the import statement for Media player and set the
General Media Item Property Keys
for nowPlayingInfo property. For the MPMediaItemPropertyArtwork you need to take a look at this MPMediaItemArtwork
import MediaPlayer
let audioInfo = MPNowPlayingInfoCenter.defaultCenter()
let audioName = audioPath.lastPathComponent!.stringByDeletingPathExtension
audioInfo.nowPlayingInfo = [ MPMediaItemPropertyTitle: audioName, MPMediaItemPropertyArtist:"artistName"]
To extract the metadata from your mp3 you need to create an AVPlayerItem and access its asset commonMetadata property
let playerItem = AVPlayerItem(URL: audioPath)
let metadataList = playerItem.asset.commonMetadata as! [AVMetadataItem]
for item in metadataList {
if item.commonKey == "title" {
println("Title = " + item.stringValue)
}
if item.commonKey == "artist" {
println("Artist = " + item.stringValue)
}
}
Note: You will have to invoke beginReceivingRemoteControlEvents() otherwise it will not work on the actual device.
override func viewDidLoad() {
super.viewDidLoad()
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
}
With Leonardo's help I was able to accomplish this using the following code:
#IBAction func play(sender: AnyObject) {
let audioInfo = MPNowPlayingInfoCenter.defaultCenter()
println(audioInfo)
player.play()
//println("Playing \(audioPath)")
let playerItem = AVPlayerItem(URL: audioPath)
let metadataList = playerItem.asset.commonMetadata as! [AVMetadataItem]
for item in metadataList {
if let stringValue = item.value as? String {
println(item.commonKey)
if item.commonKey == "title" {
trackLabel.text = stringValue
}
if item.commonKey == "artist" {
artistLabel.text = stringValue
}
}
}
}
You can selectively pick which commonkeys you would like to use with if statements like these and print the text to the labels like this:
if item.commonKey == "title" {
trackLabel.text = stringValue
}
if item.commonKey == "artist" {
artistLabel.text = stringValue
}
Thank you Leonardo for your assistance!

Resources