iOS GameCenter, AVAsset, and Audio Streaming - iphone

I get a song from the device iTunes library and shove it into an AVAsset:
- (void)mediaPicker: (MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection
{
NSArray *arr = mediaItemCollection.items;
MPMediaItem *song = [arr objectAtIndex:0];
AVAsset *songAsset = [AVAsset assetWithURL:[song valueForProperty:MPMediaItemPropertyAssetURL]];
}
Then I have this Game Center method for receiving data:
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID
I'm having a LOT of trouble figuring out how to send this AVAsset via GameCenter and then have it play on the receiving device.
I've read through:
http://developer.apple.com/library/ios/#documentation/MusicAudio/Reference/AudioStreamReference/Reference/reference.html#//apple_ref/doc/uid/TP40006162
http://developer.apple.com/library/ios/#documentation/AudioVideo/Conceptual/MultimediaPG/UsingAudio/UsingAudio.html#//apple_ref/doc/uid/TP40009767-CH2-SW5
http://developer.apple.com/library/mac/#documentation/AVFoundation/Reference/AVAudioPlayerClassReference/Reference/Reference.html
http://developer.apple.com/library/mac/#documentation/MusicAudio/Conceptual/AudioQueueProgrammingGuide/Introduction/Introduction.html
I am just lost. Information overload.
I've implemented Cocoa With Love's Audio Stream code, but I can't figure out how to take the NSData I receive through GameCenter and shove it into his code.
http://cocoawithlove.com/2008/09/streaming-and-playing-live-mp3-stream.html
Can someone please help me figure this out? Thanks!

As far as I know the AVAsset is not the actual song. So if you want to send the actual data of the picked song you need to try something like this:
- (void)mediaPicker: (MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection
{
NSArray *arr = mediaItemCollection.items;
MPMediaItem *song = [arr objectAtIndex:0];
NSData *songData = [NSData dataWithContentsOfURL:[song valueForProperty:MPMediaItemPropertyAssetURL]];
// Send the songData variable trough GameCenter
}
On the other device now you need to write the NSData you receive to the disk somewhere and than create an AVAsset with it's newly URL. Like this:
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID
{
NSString *url = NSTemporaryDirectory();
[url stringByAppendingPathComponent:<#audio_file_name#>];
// Make sure there is no other file with the same name first
if ([[NSFileManager defaultManager] fileExistsAtPath:url]) {
[[NSFileManager defaultManager] removeItemAtPath:url error:nil];
}
[data writeToFile:url atomically:NO];
AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:url] options:nil];
// Do whatever you want with your new asset
}
Let me know if that works!

Related

Saving the Response to a file and reading that file in iOS

Im getting a BSON response from the server.I want to save it to a file and read that file instead of directly reading the response.
This is the method where im getting the response
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
// NSString* path = [[NSBundle mainBundle] pathForResource:#"data"
ofType:#"txt"];
// NSString* content = [NSString stringWithContentsOfFile:path
encoding:NSUTF8StringEncoding
error:NULL];
[self readTillEOF];
NSRange range = NSMakeRange(0, [data length] - 3);
NSData *refinedData = [data subdataWithRange:range];
[delegate didRecieveTillEof:refinedData];
}
can anyone please help me with this.
Thanks in advance.
EDIT : Take a look at this how to convert BSON into NSString
Check if u can convert your data into array or dictionary and that can be used for saving in document directory like this.

How to play audio songs one after other which is placed in resources folder in iphone

I am new to iphone.I am working on audio player here i am struck in the middle because i am facing some issue that is here in my project i have 2 directories in resource folder.In each directory i have 6mp3 audio files.So my issue is when user gives the path to the 3rd mp3 song in first directory in resources folder from that we have to play continuous all the songs like 4th,5th,6th mp3files also which is placed in that directory in resources folder.here is my code for this
NSString *chapterString =#"3";
NSString *string = [[NSBundle mainBundle]pathForResource:chapterString ofType:#"mp3" inDirectory:#"raj"];
NSLog(#"string is %#",string);
url = [NSURL fileURLWithPath:string isDirectory:YES];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
If any body know this please help me..
Put all the songs in an array
Initialize a global variable suppose x
Write the following AVAudioPlayer Delegate
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
if (x<([_downloadedSongArray count]-1))
{
x=x+1;
[self loadSong];
}
}
- (void)loadSong
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
_audioDocumentsDirectory = [paths objectAtIndex:0];
_audioDocumentsDirectory = [_audioDocumentsDirectory stringByAppendingPathComponent:#"Songs"];
NSString *selectedSong = [_downloadedSongArray objectAtIndex:x];
_sharerDownloadedString=selectedSong;
selectedSong = [selectedSong stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
_audioDocumentsDirectory = [_audioDocumentsDirectory stringByAppendingPathComponent:selectedSong];
url = [NSURL fileURLWithPath:_audioDocumentsDirectory];
[_audio stop];
_audio = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
[_audio prepareToPlay];
[_audio play];
[Utility setPlaying:YES];
[_audio setDelegate:self];
}
Where _downloadedSongArray is an array where all song are. And loadSong is a method which load next/prev song from an array based on index and assign to the AVAudioPlayer.
You could get the contents of the directories into an Array via NSFileManager, upon finishing song x..play the next song in the array, song y.

How to cache xml lists downloaded from a server?

I am trying to figure out how to cache a list of comic titles that I want to use in a uitableview, and will be updated roughly every week, so instead of loading the list every time the app is launched from the web-server I want to hold onto a cache.. only problem is I'm finding the documentation hard to come across for caching lists like this.
Any example code or suggestions would be greatly appreciated :)
You fetch the XML
You parse it to a NSDictionary using NSXMLParser
You serialize and store the dictionary.
#implementation NSDictionary(BinaryPlist)
- (BOOL)writeToBinaryFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile {
NSString *errorString = nil;
NSData *data = [NSPropertyListSerialization dataFromPropertyList:self format:NSPropertyListBinaryFormat_v1_0
errorDescription:&errorString];
if (errorString) {
return NO;
}
return [data writeToFile:path atomically:useAuxiliaryFile];
}
#end
Then you can define for how long you should consider the cache fresh, or, alternatively, issue HTTP HEAD request and check the Last-Modified header.
- (BOOL)cacheValid:(NSString*)path {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
NSDictionary *attrs = [fileManager attributesOfItemAtPath:path error:&error];
if (!error) {
NSDate *modDate = [attrs fileModificationDate];
NSTimeInterval delta = - [modDate timeIntervalSinceNow];
if (delta < kCacheTTL) {
return YES;
}
}
return NO;
}

Using MPMediaItems with AVAudioPlayer

Is it possible to pick media items using MPMediaPickerController and then load them into an AVAudioPlayer object?
If MPMusicPlayerController doesn't meet your needs, you can copy the audio to your local bundle so you can use AVAudioPlayer.
EDIT
You basically have three options for playing audio from the user's iPod library: MPMediaPlayer, AVPlayer and AVAudioPlayer.
Here are examples for MPMediaPlayer and AVPlayer:
- (void) mediaPicker: (MPMediaPickerController *) mediaPicker
didPickMediaItems: (MPMediaItemCollection *) collection {
MPMediaItem *item = [[collection items] objectAtIndex:0];
NSURL *url = [item valueForProperty:MPMediaItemPropertyAssetURL];
[self dismissModalViewControllerAnimated: YES];
// Play the item using MPMusicPlayer
MPMusicPlayerController* appMusicPlayer = [MPMusicPlayerController applicationMusicPlayer];
[appMusicPlayer setQueueWithItemCollection:collection];
[appMusicPlayer play];
// Play the item using AVPlayer
AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:url];
AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
[player play];
}
If you need to use AVAudioPlayer for some reason, or you need access to the audio file's actual audio data, you have to first copy the audio file to your app's directory and then work with it there. The AVAsset + AVPlayer stuff is the closest analogy to ALAsset if you're used to working with photos and videos.
Just wanted to say that it appears that in iOS 6 and 7, AVAudioPlayer can play file URLs directly from the iPod without having to copy the audio data into your app directory as Art suggested.
- (void) mediaPicker: (MPMediaPickerController *) mediaPicker didPickMediaItems: (MPMediaItemCollection *) collection {
MPMediaItem *item = [[collection items] objectAtIndex:0];
NSURL *url = [item valueForProperty:MPMediaItemPropertyAssetURL];
// Play the item using AVPlayer
self.avAudioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
[self.avAudioPlayer play];
}
I wanted to add (and I can't comment on SO yet) that it seems like in iOS 8, and perhaps before, songs that are stored on iCloud do not have a value for the property assetURL and return null for [npItem valueForProperty:MPMediaItemPropertyAssetURL].
Here is a sample output:
MPMediaItem *npItem = self.musicPlayerController.nowPlayingItem;
NSLog(#"Song Title: %#\n assetURL: %#\n Cloud Item: %d", npItem.title, [npItem valueForProperty:MPMediaItemPropertyAssetURL], npItem.cloudItem)
// Log Output
Song Title: Time We Had
assetURL: (null)
Cloud Item: 1
Song Title: The Quiet
assetURL: ipod-library://item/item.m4a?id=1529654720874100371
Cloud Item: 0
I think you'll find that the [NSBundle mainBundle] is READ-ONLY. It's not possible to write files into the APP, therefore AVAudioPlayer will not work for iPod-Library MPMediaItems!

AVAudioPlayer -> not working

Audio Player works fine if I initiate it for 20 to 30 times but after that it shows the following error
Error Domain=NSOSStatusErrorDomain Code=-43 "The operation couldn’t be completed. (OSStatus error -43.)"
The code currently I am using is as follows
- (void)playShortSound:(NSString *)fileName type:(NSString *)type
{
if (!isSound) {
return;
}
NSString *soundFilePath =[[NSBundle mainBundle] pathForResource: fileName ofType: type];
NSURL *fileURL = [[[NSURL alloc] initFileURLWithPath: soundFilePath] autorelease];
NSError *error;
AVAudioPlayer *newPlayer =[[AVAudioPlayer alloc] initWithContentsOfURL: fileURL
error: &error];
//newPlayer.numberOfLoops = 0;
newPlayer.volume=1.0;
if (newPlayer == nil){
NSLog(#"%#",[error description]);
//[self playShortSound:fileName type:type];
//return;
}
else{
[newPlayer play];
newPlayer.delegate=self;
}
}
- (void) audioPlayerDidFinishPlaying: (AVAudioPlayer *) player
successfully: (BOOL) completed {
if (completed == YES) {
NSLog(#"Sound Playing complete");
}else {
NSLog(#"error in completion of %#",player.url);
}
}
I was having the same problem and instead of init the player from url i did it from NSData.
NSString *soundFilePath =[[NSBundle mainBundle] pathForResource: fileName ofType: type];
NSData *soundData = [[NSData alloc] initWithContentsOfFile:soundFilePath];
NSError *error;
AVAudioPlayer *newPlayer =[[AVAudioPlayer alloc] initWithData: soundData
error: &error];
It seem to solved my problem hope it does yours.
By calling the method many times you are allocating the instance of AVAudioPlayer newPlayer many times. This will certainly cause you problems because of the memory allocated. Just make sure you release the objects that you own once you are done using them.
Did you set AudioSessionCategory?
{NSError *setCategoryError = nil;
BOOL success = [[AVAudioSession sharedInstance] setCategory: avaudiosessioncategoryplayandrecorderror: &setCategoryError];
}
I was trying to play files in my app by picking them from the iPod library when I got this error code. I found that in my iPod library on the device, I had a duplicate entry for the same song. Music player that comes with iOS was able to play one and not the other. That means one entry was not valid anyway. Probably removed from the device but iTunes/iOS did not do proper housekeeping.
It seems that the reference to the non-playable entry existed within the "Recently Added" playlist which is automatically created by the system. I had picked the same file from "Recently Added" playlist to play in the app.
Side Note: I had removed the file from the manually created playlist in which it existed (removed from the iOS device itself).
In short it seems that the file that I loaded into the AVAudioPlayer instance was not available on the device anymore or its referencing URL did not give back a proper file to play. So common reason for this error would be when the player does not get the file to load based on the URL given to it.