AVFoundation code produces no ID3 metadata - iphone

This is my code which is not working for any mp3 files:
//define AVassets
NSURL *assetUrl2 = [[NSURL fileURLWithPath:
[assetLoc stringByAppendingPathComponent:title]]
URLByAppendingPathExtension:ext]
AVAsset *asset2 = [AVURLAsset URLAssetWithURL:assetUrl2
options:nil];
//print asset url
NSString *encodedString = [assetUrl2 absoluteString];
NSLog(#"song name%#",encodedString); //this prints the valid url
NSArray *origMetadata =
[asset2 metadataForFormat:AVMetadataFormatID3Metadata];
for (AVMetadataItem *item in origMetadata) {
NSLog(#"*** ID3 tags found");
NSString *keys = (NSString *)[item key];
NSString *value = [item stringValue];
NSLog(#"value for tag:%# is:%#", keys, value);
}
However, if I swap...
`NSArray *origMetadata =
[asset2 metadataForFormat:AVMetadataFormatID3Metadata];`
with...
`NSArray *origMetadata =
[asset2 metadataForFormat:AVMetadataFormatiTunesMetadata];`
and load an AAC file rather than an MP3 file, it works. Obviously I am missing something here. Any help would be appreciated. Thank you.

Related

I tried to export audio using RenderToFile, but when I set scheduledTime, it doesn't work

I merged two audios,one of which I set the scheduledTime. When I just play it, it works. But when I use RenderToFile to export and start to play the audio which I exported, the scheduledTime doesn't work.
NSString *path1 = [[NSBundle mainBundle]pathForResource:#"test" ofType:#"mp4"];
NSString *path2 = [[NSBundle mainBundle]pathForResource:#"music_1" ofType:#"mp3"];
AKAudioFile *file1 = [[AKAudioFile alloc]initForReading:[NSURL fileURLWithPath:path1] error:nil];
AKAudioFile *file2 = [[AKAudioFile alloc]initForReading:[NSURL fileURLWithPath:path2] error:nil];
AKAudioPlayer *player1 = [[AKAudioPlayer alloc]initWithFile:file1 looping:NO lazyBuffering:YES error:nil completionHandler:nil];
AKAudioPlayer *player2 = [[AKAudioPlayer alloc]initWithFile:file2 looping:NO lazyBuffering:YES error:nil completionHandler:nil];
player2.scheduledTime = player1.duration;
AKMixer *mixer = [[AKMixer alloc]init:#[player1,player2]];
AudioKit.output = mixer;
AVAudioFormat *format = [[AVAudioFormat alloc]initWithCommonFormat:AVAudioPCMFormatFloat64 sampleRate:44100 channels:2 interleaved:NO];
NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithDictionary:format.settings];
[settings setObject:#(kAudioFormatMPEG4AAC) forKey:AVFormatIDKey];
AKAudioFile *file = [[AKAudioFile alloc]initForWriting:[NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/justTest.aac",MineAudioURL]] settings:settings error:nil];
[AudioKit renderToFile:file duration:player1.duration+player2.duration error:nil prerender:^{
[player1 start];
[player2 start];
}];
When I play it player2 will play after player1 has finished. But when I use RenderToFile to export and start to play the audio which I exported, both play at the same time. And this is not what I want. I want audio2 to play when audio1 has finished.

How to separate JSON response in iphone

I have an application in which I am having a json response like this.
{"success":"true","message":"You have logged in","pserial":"1"} and I am separating with ":".
And I am getting data like this pSerial:"1"} but I want only 1 value.
NSURL *url = [NSURL URLWithString:strUrl];
NSData *respData = [NSData dataWithContentsOfURL:url];
NSString *strResp = [[NSString alloc]initWithData:respData encoding:NSUTF8StringEncoding];
NSString *approvalString = [[strResp componentsSeparatedByString:#":"] objectAtIndex:3];
NSLog(#"pSerial:%#",approvalString);
for Example :
SBJsonParser *jsonPar = [[SBJsonParser alloc] init];
NSError *error = nil;
NSArray *jsonObj = [jsonPar objectWithString:jsonString error:&error];
id jsonObj = [jsonPar objectWithString:jsonString error:&error];
if ([jsonObj isKindOfClass:[NSDictionary class]])
// treat as a dictionary, or reassign to a dictionary ivar
else if ([jsonObj isKindOfClass:[NSArray class]])
// treat as an array or reassign to an array ivar.
Then get the value :
NSMutableArrary *userMutArr = [NSMutableArray array];
for (NSDictionary *dict in jsonObj)
{
User *userObj = [[[User alloc] init] autorelease];
[userObj setFirstName:[dict objectForKey:#"firstName"]];
[userObj setLastName:[dict objectForKey:#"lastName"]];
[userObj setAge:[[dict objectForKey:#"age"] intValue]];
[userObj setAddress:[dict objectForKey:#"address"]];
[userObj setPhoneNumbers:[dict objectForKey:#"phoneNumber"]];
[userMutArr addObject:userObj];
}
Hope you will understand. and read some Documents. it will help you.
It looks like you are in need of JSON Parsing. Here, what you want is JSON Parsing, not separating data from the JSON Response. JSON is the format of the data in which data is formatted in Key-Value pairs. You can fetch the "Value" of any object using the "Key".
Your first two lines are correct.
NSURL *url = [NSURL URLWithString:strUrl];
NSData *respData = [NSData dataWithContentsOfURL:url];
Now, you can parse JSON Response like this :
NSError* error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:respData options:kNilOptions error:&error];
NSString *pSerial = [json objectForKey:#"pserial"];
This will give you the value of "pserial" from your response. Similarly, you can get the values for "success" and "message". You can check it using this line :
NSLog(#"pserial :: %#",pserial);
You need to parse the JSON Response String, you can use any JSON parser like:
https://github.com/stig/json-framework/
And in your code do:
NSString *strResp = [[NSString alloc]initWithData:respData encoding:NSUTF8StringEncoding];
NSDictionary *ResponseDictionary = [strResp JSONValue];
NSString * pSerial = (NSString*)[ResponseDictionary objectForKey:#"pserial"];
Dont separation by ":" just use JSONValue your response is like
// {"success":"true","message":"You have logged in","pserial":"1"}
// with SBJsonParser parse your object like this
NSDictionary *responseJson = [YOUR-OBJECT JSONValue];
Note: dont forget to add Json header file
Its better you use any OpenSource Json Parser
Here is a stack post Comparison of different Json Parser for iOS

XCODE NSXML changing the element value

I have a very simple xml file by name options.xml
<Dat>
<Name>Tom</Name>
<Option>1</Option>
</Dat>
Using NSXML I am trying to change "Tom" to "Jim" and save the file. How can I do that. I read many document and there is no straight forward solution. Can some one help me with the code ?
update: I ended up in trying with Gdatasxml
-(void)saveToXML
{
NSString* path = [[NSBundle mainBundle] pathForResource:#"options" ofType:#"xml"];
NSData *xmlData = [[NSMutableData alloc] initWithContentsOfFile:path];
NSError *error;
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:&error];
GDataXMLElement *rootElement = [GDataXMLElement elementWithName:#"Dat"];
NSArray *mySettings = [doc.rootElement elementsForName:#"Dat"];
for (GDataXMLElement *mySet in mySettings)
{
NSString *name;
NSArray *names = [mySet elementsForName:#"Name"];
if (names.count > 0)
{
GDataXMLElement *childElement = (GDataXMLElement *) [names objectAtIndex:0];
name = childElement.stringValue;
NSLog(childElement.stringValue);
[childElement setStringValue:#"Jim"];
}
}
[xmlData writeToFile:path atomically:YES];
}
But this is not saving the data. Help.
Editing XML is a little difficult in iOS. You need to parse the original xml to a model and then form the xml.
You can make use of 3rd party library such as GDataXML for forming XML from a data source.
//Edited user info saved in a dictionary
NSDictionary *dictionary = #{#"Name": #"Jim", #"Option":#"1"};
GDataXMLElement *rootElement = [GDataXMLElement elementWithName:#"Dat"];
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
GDataXMLElement *element = [GDataXMLElement elementWithName:key stringValue:obj];
[rootElement addChild:element];
}];
//xml document is formed
GDataXMLDocument *document = [[GDataXMLDocument alloc]
initWithRootElement:rootElement];
NSData *xmlData = document.XMLData;
NSString *filePath = [self savedXMLPath];
//XML Data is written back to a filePath
[xmlData writeToFile:filePath atomically:YES];
Create a class that is essentially an XML "node". Then in your parser setup a system of these XML nodes in the same fashion as you read them. Then search through that body and find the element that you would like to change. Change it. Then write a function that goes through these "node" objects and writes a new NSString in XML format and save that string to file. There is no real easy way that I know of to write XML files. I'm sure someone has a library out there to do it, but I had very complex XML's to deal with so I wrote my own. If you would like specific code let me know and I can try to give you parts of what you may need.
You Can use GDATAXML for changing XML node
Here is Working Code snippet
NSString *XMLString = #"<Dat><Name>Tom</Name><Option>1</Option></Dat>";
NSError *error = nil;
GDataXMLElement *newElement = [[GDataXMLElement alloc] initWithXMLString: XMLString error: &error];
NSLog(#"New element: %# error: %#", newElement, error);
if(nil == error)
{
GDataXMLElement *childElement = [[newElement elementsForName: #"Name"] objectAtIndex: 0];
[childElement setStringValue:#"Jim"];
childElement = [[newElement elementsForName: #"Option"] objectAtIndex: 0];
[childElement setStringValue:#"2"];
}
NSLog(#"New element now: %#", newElement);
Check by using this code snippet

Extracting MP3 Album artwork in iOS

I have been trying to extract metadata information from a .mp3 file for an iPhone app. I tried using AVAsset like this.It didn't work,the common meta data is empty. But audacity and another iOS app from app store could retrieve the meta data details. I don't know why?.
So, I tried to extract the same with below code using AudioToolBox framework
CFDictionaryRef piDict = nil;
UInt32 piDataSize = sizeof(piDict);
// Populates a CFDictionary with the ID3 tag properties
err = AudioFileGetProperty(fileID, kAudioFilePropertyInfoDictionary, &piDataSize, &piDict);
if(err != noErr) {
NSLog(#"AudioFileGetProperty failed for property info dictionary");
return nil;
}
// Toll free bridge the CFDictionary so that we can interact with it via objc
NSMutableDictionary* nsDict = [(__bridge NSDictionary*)piDict mutableCopy];
This returned everything except album art. When I tried to extract the album art using kAudioFilePropertyAlbumArtwork , I got osstatus error(The operation couldn't be completed).
So at last, I tried my luck with ObjC wrapper for libId3(found here). It worked perfectly well. I could get the artwork.
My question is, why AVAsset could not retrieve the data?. What am I missing there?. somebody managed to to get it work?. A sample will be appreciated.
Why kAudioFilePropertyAlbumArtwork extraction couldn't be completed?. Both the issues happened with all the .mp3 files I had.
Solution Update:
AVAsset didn't work for me because I made my URL using
[NSURL URLWithString:[filePath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]
rather than
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
To use AVAsset to extract metadata informations, this post is useful. The following code is what you need:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"filename" ofType:#"mp3"];
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
AVAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
NSArray *keys = [NSArray arrayWithObjects:#"commonMetadata", nil];
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
NSArray *artworks = [AVMetadataItem metadataItemsFromArray:asset.commonMetadata
withKey:AVMetadataCommonKeyArtwork
keySpace:AVMetadataKeySpaceCommon];
for (AVMetadataItem *item in artworks) {
if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) {
NSDictionary *dict = [item.value copyWithZone:nil];
self.imageView.image = [UIImage imageWithData:[dict objectForKey:#"data"]];
} else if ([item.keySpace isEqualToString:AVMetadataKeySpaceiTunes]) {
self.imageView.image = [UIImage imageWithData:[item.value copyWithZone:nil]];
}
}
}];
NSURL *fileURL1 = [NSURL fileURLWithPath:url];
AVAsset *asset = [AVAsset assetWithURL:fileURL1];
for (AVMetadataItem *metadataItem in asset.commonMetadata) {
if ([metadataItem.commonKey isEqualToString:#"artwork"]){
NSDictionary *imageDataDictionary = (NSDictionary *)metadataItem.value;
NSData *imageData = [imageDataDictionary objectForKey:#"data"];
UIImage *image = [UIImage imageWithData:imageData];
imageThumb.image = image;
}
}

How can i share a sound file, to facebook or Twitter in iphone

Hi i am working on application in which , i have to upload a sound file to Facebook.
Please ,provide me a better solution, whether it is possible to share a sound file on Facebook or not.
Thanks in advance
Facebook does not have sound uploading. You could always upload the sound file elsewhere and use Facebook to share the link to it.
if you check the webApps for twitter/facebook, they does not provide any means to UPLOAD an audio file.
Twittier allows only text post and on the other hand, Facebook allow Image/Video to be uploaded.
In the light of these facts, I do not think it is possible without a url share.
It is not possible to upload audio files to Facebook, only photos and videos are allowed. However, another solution would be to upload the audio file somewhere else and then use the Facebook API to post a link using that reference. One place you may wish to look to upload audio is http://developers.soundcloud.com/
Use AVAssetExportSession, create a movie with the sound file and then upload it to Facebook.
This is possible to do but it is a bit of a pain. To do this you must convert the audio file into a video file and then post it to Facebook as a video.
First we need to have access to our audioFile, you should already have this, if not then there are a lot of Stackoverflow questions devoted to this, I won't complicate matters by going off track. We then create a NSURL to a video in our documents. In this case we have an video named video_base.mp4 which has been designed to be a nice background for our audio track. Finally we merge the files before sharing the returned file to Facebook.
- (IBAction)shareToFacebook:(id)sender {
// You should already have your audio file saved
NSString * songFileName = [self getSongFileName];
NSArray * searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentPath = [searchPaths objectAtIndex:0];
NSString * file = [documentPath stringByAppendingPathComponent:songFileName];
NSURL * audioFileURL = [NSURL fileURLWithPath: audioFile];
NSURL * videoFileURL = [NSURL fileURLWithPath:[NSFileManager getFilePath:#"video_base.mp4" withFolder:#""]];
[self mergeAudio:audioFileURL andVideo:videoFileURL withSuccess:^(NSURL * url) {
// Now we have the URL of the video file
[self shareVideoToFacebook:url];
}];
}
Credit to #dineshprasanna for this part of the code which can be found here. We want to merge our audio and video and then save them to a path. We then return the exportURL in the completion block.
- (void)mergeAudio: (NSURL *)audioURL andVideo: (NSURL *)videoURL withSuccess:(void (^)(NSURL * url))successBlock {
AVURLAsset* audioAsset = [[AVURLAsset alloc]initWithURL:audioURL options:nil];
AVURLAsset* videoAsset = [[AVURLAsset alloc]initWithURL:videoURL options:nil];
AVMutableComposition * mixComposition = [AVMutableComposition composition];
AVMutableCompositionTrack * compositionCommentaryTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionCommentaryTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset.duration)
ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]
atTime:kCMTimeZero error:nil];
AVMutableCompositionTrack *compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo
preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]
atTime:kCMTimeZero error:nil];
AVAssetExportSession* _assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition
presetName:AVAssetExportPresetHighestQuality];
NSString * videoName = #"export.mov";
NSString * exportPath = [NSTemporaryDirectory() stringByAppendingPathComponent:videoName];
NSURL * exportUrl = [NSURL fileURLWithPath:exportPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:exportPath]) {
[[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil];
}
_assetExport.outputFileType = #"com.apple.quicktime-movie";
_assetExport.outputURL = exportUrl;
_assetExport.shouldOptimizeForNetworkUse = YES;
[_assetExport exportAsynchronouslyWithCompletionHandler: ^(void ) {
if(successBlock) successBlock(exportUrl);
}];
}
Finally we want to save our return videoURL to Facebook. It is worth noting that we need a few libraries to be added for this functionality to work:
#import <AssetsLibrary/AssetsLibrary.h>
#import <FBSDKCoreKit/FBSDKCoreKit.h>
#import <FBSDKShareKit/FBSDKShareKit.h>
We then share the merged file to Facebook:
- (void)shareVideoToFacebook: (NSURL *)videoURL {
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
ALAssetsLibraryWriteVideoCompletionBlock videoWriteCompletionBlock = ^(NSURL *newURL, NSError *error) {
if(error) {
NSLog( #"Error writing image with metadata to Photo Library: %#", error );
} else {
NSLog( #"Wrote image with metadata to Photo Library %#", newURL.absoluteString);
FBSDKShareDialog *shareDialog = [[FBSDKShareDialog alloc]init];
NSURL *videoURL = newURL;
FBSDKShareVideo *video = [[FBSDKShareVideo alloc] init];
video.videoURL = videoURL;
FBSDKShareVideoContent *content = [[FBSDKShareVideoContent alloc] init];
content.video = video;
[FBSDKShareDialog showFromViewController:self
withContent:content
delegate:nil];
}
};
if([library videoAtPathIsCompatibleWithSavedPhotosAlbum:videoURL]) {
[library writeVideoAtPathToSavedPhotosAlbum:videoURL
completionBlock:videoWriteCompletionBlock];
}
}
This should open up the Facebook app and then allow the user to share their audio file on their wall with a background of the video stored in your app.
Obviously everyone's project is different, this means you might not be able to copy paste this code exactly into your project. I have tried to split up the process meaning it should be easy to extrapolate to get audio messages uploading successfully.