Getting name of track that is currently being played - objective-c

Is there a way to get the name of the track that is currently being played in iTunes on an iOS device?
I haven't found anything too useful inMPMusicPlayerController nor AVAudioPlayer.
Thanks!

Alright, after some more searching, I found the answer hidden in this semi-related question: Get album artwork from MP3 file/ID3 tag
There are two properties that exist in MPMusicPlayerController that provides us with the Track Name and Track Artist. They are MPMediaItemPropertyTitle and MPMediaItemPropertyArtist, respectively.

The accepted answer's answer contains 5x the code you need to get what you're after. Here's a simpler answer:
if ([MPMusicPlayerController iPodMusicPlayer].playbackState == MPMusicPlaybackStatePlaying) {
MPMediaItem *item = [[MPMusicPlayerController iPodMusicPlayer] nowPlayingItem];
NSString *title = [item valueForProperty:MPMediaItemPropertyTitle];
NSString *artist = [item valueForProperty:MPMediaItemPropertyArtist]; // common
// do something with these
} else {
// nothing is playing!
}

Related

Create a custom MediaPickerController

I was tasked to create a music player application that is sort of like a DJ app, and when the add music button is clicked, it needs to show the list of all the songs just like the native MediaPickerController but with added functionalities like sorting, search and not fullscreen which is not available in the native one.
I tried scouring the internet for an answer but I can't find anything about this, it's all about creating the native media picker controller.
I found about MPMediaQuery which allows you to get the songs list in the phone, but I can't use it in a for-in
Sample:
MPMediaQuery *songs = [MPMediaQuery songsQuery];
for (MPMediaItem *item in songs) {
}
But I get this:
*Collection expression type 'MPMediaQuery ' may not respond to 'countByEnumeratingWithState:objects:count:'
Any suggestions?
About the mediaquery, you need to convert it to nsarray first, try this:
MPMediaQuery *songs = [MPMediaQuery songsQuery];
NSArray *songList = [songs items];
for (MPMediaItem *item in songList) {
}

Play songs by album/artist on iTunes with Scripting Bridge/Obj-C

Given a name of an album or artist I would like to tell iTunes to play all songs by that artist or on that album. How can I do this? I know how to get all the songs to play by filtering an array with all the tracks, but how do I tell iTunes to play only those I want?
EDIT
I know I can use this code to get all the tracks I want to play, but I have no idea how to tell iTunes to play them in sucession. Any ideas?
// Get the app
iTunesApplication* iTunes = [SBApplication applicationWithBundleIdentifier: #"com.apple.iTunes"];
// Get the library
iTunesSource* library;
for (iTunesSource* thisSource in [iTunes sources]) {
if ([thisSource kind] == iTunesESrcLibrary) {
library = thisSource;
break;
}
}
SBElementArray* tracks;
for (iTunesPlaylist* playlist in [library playlists]) {
if ([playlist specialKind] == iTunesESpKMusic) {
tracks = [playlist searchFor: name only: type == 0 ? iTunesESrAAlbums : iTunesESrAArtists];
}
}
// There. Now what? how do I play all the tracks in 'tracks'?
You can play one songs.
iTunesTrack *track = [tracks objectAtIndex:0];
[track playOnce:YES];
If you want play several songs, you should create or use a playlist:
for (iTunesUserPlaylist *thisList in playlists) {
if ([[thisList name] isEqualToString:playlistName]) {
playlist = thisList;
break;
}
}
[track duplicateTo:playlist];
[playlist playOnce:YES];
So it seems iTunes does not have an AppleScript/Scripting Bridge command to play songs by a certain artist/on a certain album. And according to this question, it's not possible to tell iTunes to play a custom list of tracks without creating a new playlist (which I don't want to do). In short, there's really no way to solve this problem...

Mute an HTTP Live Stream in an AVPlayer

I've been trying to work out this problem for a good 48 hours now and haven't come up with anything. I have 2 AVPlayer objects playing different http live streams. Obviously, I don't want them both playing audio at the same time so I need a way to mute one of the videos.
Apple suggests this for muting an audio track playing in AVPlayer...
NSMutableArray *allAudioParams = [NSMutableArray array];
for (AVPlayerItemTrack *track in [_playerItem tracks]) {
if ([track.assetTrack.mediaType isEqualToString:AVMediaTypeAudio]) {
AVMutableAudioMixInputParameters *audioInputParams = [AVMutableAudioMixInputParameters audioMixInputParameters];
[audioInputParams setVolume:0.0 atTime:CMTimeMakeWithSeconds(0,1)];
[audioInputParams setTrackID:[track.assetTrack trackID]];
[allAudioParams addObject:audioInputParams];
// Added to what Apple Suggested
[track setEnabled:NO];
}
}
AVMutableAudioMix *audioZeroMix = [AVMutableAudioMix audioMix];
[audioZeroMix setInputParameters:allAudioParams];
[_playerItem setAudioMix:audioZeroMix];
When this didn't work (after many iterations), I found the enabled property of AVPlayerItemTrack and tried setting that to NO. Also nothing. This doesn't even register as doing anything because when I try an NSLog(#"%x",track.enabled), it still shows up as 1.
I'm at a loss and I can't think of another piece of documentation I can read and re-read to get a good answer. If anyone out there can help, that would be fantastic.
*Update: I got a hold of Apple and according to the AVFoundation team, it is impossible to mute or disable a track of an HLS video. I, personally, feel like this is a bug so I submitted a bug report (You should do the same to tell Apple that this is a problem). You can also
try and submit a feature enhancement request via their feedback page.
New iOS 7 answer: AVPlayer now has 2 new properties 'volume' and 'muted'. Use those!
And here is the original answer for life before iOS 7:
I've been dealing with the same thing. We created muted streams and streams with audio. To mute or unmute you call [player replaceCurrentItemWithPlayerItem:muteStream].
I also submitted a bug report. It looks like AVPlayer has this functionality on MacOS 10.7, but it hasn't made it to iOS yet.
AVAudioMix is documented not to work on URL assets here
Of course I tried it anyway, and like you I found it really doesn't work.
The best solution for this would be to actually embed the stream url feed with two audio tracks! One would be with the normal audio and the other audio track would be the muted audio.
It makes more sense to do it this way rather then the way ComPuff suggested as his way your actually creating two separate URL streams - which is not required.
Here is the code that you could use to switch the audio tracks:
float volume = 0.0f;
AVPlayerItem *currentItem = self.player.currentItem;
NSArray *audioTracks = self.player.currentItem.tracks;
DLog(#"%#",currentItem.tracks);
NSMutableArray *allAudioParams = [NSMutableArray array];
for (AVPlayerItemTrack *track in audioTracks)
{
if ([track.assetTrack.mediaType isEqual:AVMediaTypeAudio])
{
AVMutableAudioMixInputParameters *audioInputParams = [AVMutableAudioMixInputParameters audioMixInputParameters];
[audioInputParams setVolume:volume atTime:kCMTimeZero];
[audioInputParams setTrackID:[track.assetTrack trackID]];
[allAudioParams addObject:audioInputParams];
}
}
if ([allAudioParams count] > 0) {
AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
[audioMix setInputParameters:allAudioParams];
[currentItem setAudioMix:audioMix];
}
The only problem is that my stream url is only display two tracks (one for video and one for audio) when it should actually be three tracks (2 audio tracks). I cant work out if this is a problem with the stream url or my code! Can anyone spot any mistakes in the code?

Is it possible to modify iTunes tracks using Objective-C?

The question is in the title :) I'm playing a bit with Objective-C and Scripting Bridge. I know it is possible to get information (readonly) from iTunes, but i see nowhere a way to modify a track, for exemple change its name. Is it possible with this or another technology ?
Thanks a lot :)
Well, from the Scripting Library in AppleScript Editor, I can see that a file_track inherits from item and an item has the read-write property name. So you should be able to set it just as easily as you can read it.
Edit: Actually, almost every piece of meta-data is part of track (of which file_track inherits aswell) and most are read-write properties...
Doug Adams has one such script which can change titles from song in iTunes. Maybe have a look at it?
As for setting it via Objective-C, perhaps this documention can help you.
Exerpt from the website:
Listing 2-3 Setting the locked property of Finder items
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
FinderApplication *theFinder = [SBApplication applicationWithBundleIdentifier: #"com.apple.finder"];
SBElementArray *trashItems = [[theFinder trash] items];
if ([trashItems count] > 0) {
for (FinderItem *item in trashItems) {
if ([item locked]==YES)
[item setLocked:NO]; // <<<-- Setting the property
}
}
[pool drain];
return 0;
}
Have you tried:
iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
[[iTunes currentTrack] setName:#"The New Song Title"]);

Play specific title in iTunes via ScriptingBridge

I'm trying to write an application that interacts with iTunes via ScriptingBridge. I works well so far, but the options of this method seem to be very limited.
I want to play song with a given name, but it looks like there's no way to do this. I haven't found anything similar in iTunes.h…
In AppleScript it's just three lines of code:
tell application "iTunes"
play (some file track whose name is "Yesterday")
end tell
And then iTunes starts to play a classic Beatles song.
Is there any was I can do this with ScriptingBridge or do I have to run this AppleScript from my app?
It's not as simple as the AppleScript version, but it's certainly possible.
Method one
Get a pointer to the iTunes library:
iTunesApplication *iTunesApp = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
SBElementArray *iTunesSources = [iTunesApp sources];
iTunesSource *library;
for (iTunesSource *thisSource in iTunesSources) {
if ([thisSource kind] == iTunesESrcLibrary) {
library = thisSource;
break;
}
}
Get an array containing all the audio file tracks in the library:
SBElementArray *libraryPlaylists = [library libraryPlaylists];
iTunesLibraryPlaylist *libraryPlaylist = [libraryPlaylists objectAtIndex:0];
SBElementArray *musicTracks = [self.libraryPlaylist fileTracks];
Then filter the array to find tracks with the title you're looking for.
NSArray *tracksWithOurTitle = [musicTracks filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"%K == %#", #"name", #"Yesterday"]];
// Remember, there might be several tracks with that title; you need to figure out how to find the one you want.
iTunesTrack *rightTrack = [tracksWithOurTitle objectAtIndex:0];
[rightTrack playOnce:YES];
Method two
Get a pointer to the iTunes library as above. Then use the Scripting Bridge searchFor: only: method:
SBElementArray *tracksWithOurTitle = [library searchFor:#"Yesterday" only:kSrS];
// This returns every song whose title *contains* "Yesterday" ...
// You'll need a better way to than this to pick the one you want.
iTunesTrack *rightTrack = [tracksWithOurTitle objectAtIndex:0];
[rightTrack playOnce:YES];
Caveat to method two: The iTunes.h file incorrectly claims that the searchFor: only: method returns an iTunesTrack*, when in fact (for obvious reasons) it returns an SBElementArray*. You can edit the header file to get rid of the resulting compiler warning.