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!
Related
I'm new to swift and I have a AVPlayer which is playing fine on a particular screen. My objective is to play the same audio on lock-screen mode. So far this is what I have done and the code as bellow. for some reason I don't see the player widget on lock-screen mood at all. What am I doing wrong here.
class MyAudiosViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,AVAudioPlayerDelegate {
#IBAction func playButtonPressed(_ sender: Any) {
if(currentRowSelected != nil ){
if AudioPlayer.isPlaying() {
AudioPlayer.pause()
playButton.setImage(UIImage(named: "play_button"), for: UIControlState.normal)
} else {
AudioPlayer.play()
playButton.setImage(UIImage(named: "pause_button"), for: UIControlState.normal)
}
setUpBackgroundMode()
}else{
"Track is not selected from list"
}
}
}
extension MyAudiosViewController {
override func remoteControlReceived(with event: UIEvent?) {
if let receivedEvent = event {
if (receivedEvent.type == .remoteControl) {
switch receivedEvent.subtype {
case .remoteControlTogglePlayPause:
playButtonPressed(AudioPlayer.isPlaying())
case .remoteControlPlay:
playButtonPressed(AudioPlayer.player?.play())
case .remoteControlPause:
playButtonPressed(AudioPlayer.player?.pause())
case .remoteControlNextTrack:
print("next pressed")
case .remoteControlPreviousTrack:
print("previous pressed")
default:
print("received sub type \(receivedEvent.subtype) Ignoring")
}
}
}
}
func setUpBackgroundMode() {
if let positionSong = AudioPlayer.getPlayingIndex() {
let song = songDetailsArray[positionSong]
let songData = song.audioDetails
MPNowPlayingInfoCenter.default().nowPlayingInfo = [
MPMediaItemPropertyTitle: songData?.title ?? "",
MPMediaItemPropertyArtist: songData?.healerName ?? "",
MPMediaItemPropertyPlaybackDuration:
self.player?.currentItem?.asset.duration
]
UIApplication.shared.beginReceivingRemoteControlEvents()
becomeFirstResponder()
}
}
}
Try this and check this demo
declare this variable in your playerviewcontroller
var audioSession = AVAudioSession.sharedInstance()
in viewDidiLoad add this code
try! self.audioSession.setCategory(AVAudioSessionCategoryPlayback)
try! self.audioSession.setActive(true)
UIApplication.shared.beginReceivingRemoteControlEvents()
self.becomeFirstResponder()
pass data to this func or yours
var nowPlayingInfoCenter = MPNowPlayingInfoCenter.default()
var remoCommandCenter = MPRemoteCommandCenter.shared()
func updateNowPlayingInfo(trackName:String,artistName:String,img:UIImage) {
var art = MPMediaItemArtwork(image: img)
if #available(iOS 10.0, *) {
art = MPMediaItemArtwork(boundsSize: CGSize(width: 200, height: 200)) { (size) -> UIImage in
return img
}
}
nowPlayingInfoCenter.nowPlayingInfo = [MPMediaItemPropertyTitle: trackName,
MPMediaItemPropertyArtist: artistName,
MPMediaItemPropertyArtwork : art]
remoCommandCenter.seekForwardCommand.isEnabled = false
remoCommandCenter.seekBackwardCommand.isEnabled = false
remoCommandCenter.previousTrackCommand.isEnabled = false
remoCommandCenter.nextTrackCommand.isEnabled = false
remoCommandCenter.togglePlayPauseCommand.isEnabled = false
}
call above function when your load new song.
func loadNewSong() {
//your audio player code.
updateNowPlayingInfo(trackName:"just Like that", artistName:"LeoJam",img:UIImage(string:"img")!)
}
I Have a switch that when turned to "on" will put the music and when the switch is set to "off" the music will resume playing. My problem is that when i leave the view controller the switch will appear as "off" when it is switch "on". The code for my switch is below, I'm not sure what to write in order for the app to remember the switch state, please help.
//
// SecondViewController.swift
// Urban Sphere
//
// Created by Oren Edrich on 9/11/16.
// Copyright © 2016 Oren Edrich. All rights reserved.
//
import Foundation
import UIKit
import SpriteKit
import AVFoundation
var bombSoundEffect: AVAudioPlayer!
var Ghost = SKSpriteNode()
class SecondViewController: UIViewController {
var sw = false
#IBOutlet var mySwitch: UISwitch!
#IBAction func switchpressed(_ sender: AnyObject) {
if mySwitch.isOn{
if bombSoundEffect != nil {
bombSoundEffect.stop()
bombSoundEffect = nil
}
}
else{
let path = Bundle.main.path(forResource: "newmusic.wav", ofType:nil)!
let url = URL(fileURLWithPath: path)
do {
let sound = try AVAudioPlayer(contentsOf: url)
bombSoundEffect = sound
sound.numberOfLoops = -1
sound.play()
} catch {
// couldn't load file :(
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
}
I found the correct answer and decided to post it incase anyone has the same question.
#IBAction func saveSwitchState(sender: AnyObject) {
var defaults = NSUserDefaults.standardUserDefaults()
if bluetoothSwitch.on {
defaults.setBool(true, forKey: "SwitchState")
} else {
defaults.setBool(false, forKey: "SwitchState")
}
}
and...
override func viewDidLoad() {
super.viewDidLoad()
var defaults = NSUserDefaults.standardUserDefaults()
if (defaults.objectForKey("SwitchState") != nil) {
bluetoothSwitch.on = defaults.boolForKey("SwitchState")
}
}
You want know where to insert the code , I guess.
updata
updata2
Then you can run directly. If it's useful , please UP this answer.
import Foundation
import UIKit
import SpriteKit
import AVFoundation
class SecondViewController: UIViewController {
static let bombSoundEffect = {()->(AVAudioPlayer) in
let path = Bundle.main.path(forResource: "newmusic.wav", ofType:nil)!
let url = URL(fileURLWithPath: path)
return try! AVAudioPlayer(contentsOf: url)
}()
var sw = false
var Ghost = SKSpriteNode()
#IBOutlet var mySwitch: UISwitch!
#IBAction func switchpressed() {
if mySwitch.isOn{
SecondViewController.bombSoundEffect.play()
}else{
SecondViewController.bombSoundEffect.stop()
}
//************* save status *************
UserDefaults.standard.set(mySwitch.isOn, forKey: "SwitchStatus");
}
override func viewDidLoad() {
super.viewDidLoad()
mySwitch.addTarget(self, action: #selector(switchpressed), for: .valueChanged)
//************* load status *************
mySwitch.isOn = UserDefaults.standard.bool(forKey: "SwitchStatus");
switchpressed()
}
}
I have a similar situation to yours, and I just use UserDefaults. Here's a step-by-step guide on how to do it.
Create a variable like the following example. This will set the default setting and store the state of the check box for use later:
var musicSetting = UserDefaults().string(forKey: "Music") ?? "On"
In your viewDidLoad, add an if statement that will check whether the Check Box should be On or Off, like this:
if musicSetting == "On" {
theNameOfYourSwitch.isOn = false
} else {
theNameOfYourSwitch.isOn = true
}
In the IBAction property for your check box, add an if statement like the following that will save your Setting, depending on what it is:
if theNameOfYourCheckbox.state == NSOnState {
UserDefaults().set("On", forKey: "Music")
} else {
UserDefaults().set("Off", forKey: "Music")
}
Here's a screenshot that might help:
If you want to save the state of Switch in user default, then can use the
native method
UserDefaults.standard.set(_ value: Bool, forKey defaultName: String)
Like this
UserDefaults.standard.set(mySwitch.isOn, forKey: "SwitchStatus");
UserDefaults.standard.synchronize();
While fetching switch status just use
let status = UserDefaults.standard.bool(forKey: "SwitchStatus");
UPDATE :
#IBAction func switchpressed(_ sender: AnyObject) {
UserDefaults.standard.set(mySwitch.isOn, forKey: "SwitchStatus");
if mySwitch.isOn{
if bombSoundEffect != nil {
bombSoundEffect.stop()
bombSoundEffect = nil
}
}
else{
let path = Bundle.main.path(forResource: "newmusic.wav", ofType:nil)!
let url = URL(fileURLWithPath: path)
do {
let sound = try AVAudioPlayer(contentsOf: url)
bombSoundEffect = sound
sound.numberOfLoops = -1
sound.play()
} catch {
// couldn't load file :(
}
}
}
Hope it helps
Happy coding ...
I'm really new to the Swift and iOS programming scene and I am trying to learn a bunch of things. For this app, I want to save and load just one integer, but I am having problems, as everything I found on stack overflow and the Internet just doesn't seem to work on Swift 3. The app crashes immediately, it breaks on:
#IBOutlet weak var StaticLabel: UILabel!
It says "Thread 1: breakpoint 3.5" . Any help and tips would be very much appreciated :)
import UIKit
import AVFoundation
var player: AVAudioPlayer?
var number = 0
class ViewController: UIViewController {
#IBOutlet weak var StaticLabel: UILabel!
#IBOutlet weak var NumberLabel: UILabel!
let defaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
Load()
}
func Load()
{
number = defaults.integer(forKey: "Save")
NumberLabel.text = String(number)
}
func playSound()
{
let path = Bundle.main.path(forResource: "RightYouAre.mp3", ofType: nil)
let url = URL(fileURLWithPath: path!)
do {
let sound = try AVAudioPlayer(contentsOf: url)
player = sound
sound.play()
}
catch {
// couldn't load file :(
}
}
#IBAction func Minus(_ sender: AnyObject) {
number = number - 1
NumberLabel.text = String(number)
defaults.set(number, forKey: "Save")
}
#IBAction func Plus(_ sender: UIButton) {
playSound()
number = number + 1
NumberLabel.text = String(number)
defaults.set(number, forKey: "Save")
}
}
your line of code
number = defaults.integer(forKey: "Save")
user defaults integer for key Save is nil, actually this key does not exist at all in the user defaults
before you load this integer from user defaults , it has to be set first
if defaults.object(forKey: "Save") != nil { number = defaults.integer(forKey: "Save") }
this will make sure the object exists in the user defaults before getting it's value
finally your load function should be
func Load()
{
if defaults.object(forKey: "Save") != nil
{
number = defaults.integer(forKey: "Save")
NumberLabel.text = String(number)
}
}
You need to add null handling to the load function as first time there
will be no value for the key "save" in your UserDefaults.
func Load()
{
if let number = defaults.integer(forKey: "Save")
{
self.number = number
NumberLabel.text = String(number)
}
}
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()
}
}
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)
}
}