I am attempting to use Apple's Music Kit within my application to listen to music. My issue is that the first song that plays will continually repeat. The first song plays correctly by using the following code:
let mediaArray = [songsApple?[songNumber!]]
let playableColledction = MPMediaItemCollection.init(items: mediaArray as! [MPMediaItem])
applicationMusicPlayer.setQueue(with: playableCollection)
applicationMusicPlayer.play()
self.activateAudioSession
The activateAudioSession method is:
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayback)
try audioSession.setActive(true)
}
catch let error {
print(error.localizedDescription)
}
To detect the end of the song, I use the following observer from the Apple Music Documentation:
applicationMusicPlayer.beginGeneratingPlaybackNotications()
NotificationCenter.default.addObserver(self, #selector(didFinishSong), name: Notification.Name.MPMusicPlayerControllerNowPlayingItemDidChange, object: nil)
The didFinishSong method is used to get the songNumber of the next song to be played from the songsApple array. This information is then passed back into the method that plays the song to get the mediaArray. However, right now, didFinishSong does not get called when the song playing finishes and instead the song just repeats from the beginning.
Is there something that I am doing wrong for detecting the end of a song? Is there another way to do it? I also tried to use a timer, but for some reason it would stop counting whenever the application entered the background making the count inaccurate.
Thank you in advance for any help that you are able to provide. If you need additional information please to not hesitate to ask and I will clarify more.
Related
I am creating a simple application, and if the user gets the answer correct it plays a sound and if the answer is wrong it plays another sound. I have created a SettingsViewController with a switch button. How do I make it so that when the switch is on, it keeps sound and when it’s off it disables all sound
Follow this tutorial to setup your switch button and read its value. Then when you read its value, mute/unmute the sound.
Save the value to UserDefaults
UserDefaults.standard.set(boolValue, forKey: "gameSound")
Retrieve the sound anywhere in the Application using
let sound = UserDefaults.standard.bool(forKey: "gameSound")
I'm using SPTAudioStreamingController.sharedInstance().metadata.currentTrack?.uri to get the currently playing track, but sometimes it returns the next song on the playlist, instead of what is playing right now.
Edit: I think it may have to do with the playlist it is playing from, since whenever I skip a track, it deletes from the playlist. Is there a way to play from the top song in the playlist every time a skip occurs?
Or even better: Is there a way to refresh a playlist?
You can use a delegate method and locally keep track of what track is playing:
func audioStreaming(_ audioStreaming: SPTAudioStreamingController!, didStartPlayingTrack trackUri: String!) {
self.currentURI = trackURI
}
Whenever a track starts playing, the delegate method will fire and you can locally store the current URI. Just make sure to also utilize the other delegate methods like didStopPlayingTrack in order to ensure you have all the correct information.
I am playing live audio stream using AVPlayer and AVPlayerItem and trying to determine the current bit rate of the stream. I searched in the net and found this help :
Determening MPMovieController bit-rate
Inspired by the above thread, I tried to compute it using the following code:
NSArray *logEvents=playerItem.accessLog.events;
AVPlayerItemAccessLogEvent *event = (AVPlayerItemAccessLogEvent *)[logEvents lastObject];
double bitRate=event.observedBitrate;
But the variable bitRate is always zero when checked inside a timer.
In fact [logEvents count] is also always zero.
Could you please tell me what is wrong with the technique ?
Thanks a lot.
In addition to Ooops's suggestion, it might be wise to register for the AVPlayerItemNewAccessLogEntryNotification notification to check for the bitrate.
Since the access log array isn't KVO compliant, using the notification would allow you to not use a timer to check for updates and you wouldn't have to worry about waiting for the player item to be ready. If the events are being fired too frequently, you could choose to ignore some.
Nothing's wrong with the method. Check if your playerItem is actually loaded. The accessLog is nil until the playerItem is 'access'ed. Try to get the accessLogs after your player becomes AVPlayerStatusReadyToPlay and you'll get the log.
I am using AudioQueue to stream audio from an arbitrary source (the class basically just needs a delegate that provides it with packets), I've made a class that wraps all the functionality lets call is AudioQueueClass, I am using this class to play many songs, in between each song I release my class and create a new AudioQueueClass instance to play the next song, I am seeing two problems which I haven't been able to find the cause maybe some of you have run into these issues and can shed a light on it
1- Every now and the AudioQueue plays a few seconds of a previous song and then comes back to the current song, not sure why this could be happening as I am creating a new queue for each song, and I believe I am disposing of my queues appropriately --some code to follow
2- This one is even worse, sometimes when I'm into the 3rd or 4th song the audio queue stops playing... I believe the problem is that AudioQueueInputCallback inCallbackProc stops being called, which I guess is because the queue stopped playing and processing packets, but cannot find out why...Another thing to note is that this only happens when I stream to the device from an outside source, if I merely get the file data locally and use that as packets I hear a "stutter" in the music but it recovers and plays fine, whereas in the streaming case the sound just stops (pretty weird)
Here is the code I am using to dispose of the audio queue, not posting any more because not sure what relevant parts to post, please let me know if you want to see any of the code and ill post it
AudioQueueFlush(audioQueue);
AudioQueueStop(audioQueue, true);
if (audioFileStream)
{
err = AudioFileStreamClose(audioFileStream);
audioFileStream = nil;
if (err)
{
[self failWithErrorCode:AS_FILE_STREAM_CLOSE_FAILED];
}
}
//
// Dispose of the Audio Queue
//
if (audioQueue)
{
err = AudioQueueDispose(audioQueue, true);
audioQueue = nil;
if (err)
{
[self failWithErrorCode:AS_AUDIO_QUEUE_DISPOSE_FAILED];
}
}
Turns out not surprisingly the error was not in the Audio streamer at all, it was the way i was writing the music to be streamed...after i fixed it everything worked out alright...
Thanks Again
Daniel
I have a UITableView populated with a bunch of music players that I've made using a custom UITableViewCell. (as shown in the screenshot linked here: screenshot).
The problem with the way I have it coded is that since each cell is completely independent, there's no way for one tableviewcell to check if any other the other cells have audio playing. This means that when I run my app, I am able to play more than one audio file at the same time.
Ultimately, how would I go about putting some sort of check in place so that when the user hits the play button, the app first checks to see if any other cell is playing audio, and, if so, stops them before playing its own audio file?
Please let me know if you would like me to post my project.
Thank you!
For starters I can't think of any good reason the cells themselves should be responsible for playing the audio. They should be responsible for telling some other object (like whatever controller is responsible for the UITableView) to play the audio. That controller would obviously then know to stop whatever it was already playing before playing something else.
In general, it's not a great idea to be putting that much logic into a 'view.'
It would probably be easiest to use notifications (NSNotification and NSNotificationCenter).
Use a common notification name for starting and stopping.
MusicPlayerWillPlay
MusicPlayerWillStop
When you create each player, register for both notifications. Then when playerA is going to play, it posts a MusicPlayerWillPlay notification. playerB, playerC and playerD, will get this notification, and if they are playing, will stop.
One caveat is that each player is unaware of the others, so you have to register with a nil object, which means playerA will get it's own notification. So in your notification method, you'll probably want to do something like
{
//...
if (sender == self)
return;
//...
}