I use AVAudioPlayer for playing audio file and UISlider to show user current time. Firstly, it looked that everything is fine but I noticed that audio player returns wrong file duration. For example it returns me duration equals to 3.5sec but file durations is equal to 6 sec. in reality.
Do you know What can cause this problem?
Below you can see my code which return file duration:
- (NSTimeInterval)audioDurationAtURL:(NSURL *)url
{
NSError *error;
NSData *data = [NSData dataWithContentsOfURL:url];
_audioPlayer = [[AVAudioPlayer alloc] initWithData:data error:&error];
return _audioPlayer.duration;
}
To add a bit to TonyMkenu's answer, AVAsset is an alternative with the ability to give you a more accurate duration.
https://developer.apple.com/library/mac/documentation/AVFoundation/Reference/AVAsset_Class/Reference/Reference.html#//apple_ref/occ/instp/AVAsset/duration
If you specify providesPreciseDurationAndTiming = YES, then AVAsset will decode the file if needed to determine its duration with accuracy. If the decode time is too long for your use, you can disable it.
In my situation, I use the AVURLAsset subclass:
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:localURL options:[NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], AVURLAssetPreferPreciseDurationAndTimingKey, nil]];
float t = CMTimeGetSeconds(asset.duration);
AVAudioPlayer appears to only returns the correct duration of a file when it is ready for play it, so try to check the length of the audio file after [_audioPlayer play];
Under certain compression formats, the audio player may change its estimate of the duration as it learns more and more about the song by playing (and hence decoding) more and more of it - https://stackoverflow.com/a/16265186
In my case, having added an audio file to a project then editing (making it longer or shorter) and then deleting and re-adding the file to the Xcode project was the problem.
Essentially the project is caching the old file. To debug this I renamed the audio file to something else, added the new audio file to the project after which the duration reported by the player was always correct, before and after calling play.
Related
AVAsset *asset = [AVAsset assetWithUrl:#"myUrl.mp4"];
AVPlayerItem * item = [AVPlayerItem itemWithAsset:asset];
AVPlayer * player = [AVPlayer playerWithItem:item];
playerViewController.player = player;
[player play];
How Can i know that asset or item or whatever downloaded video fully.
I need it for future caching it;
Now im using
NSData * videoData = [NSData dataWithUrl:myurl.mp4"];
NSCache *cache = [NSCache new];
[cache setObject:videoData forKey:myUrl];
And when i retrieve data from nscache i write it to file and play
NSData *videoData = [cache objectForKey:myUrl];
[videoData writeToFile:MyPath.mp4 atomically:YES];
And then
NSURL *url = [NSURL fileUrlWithPath:mypath];
AVAsset *asset = =[AVAsset assetWithURL:url];
..etc and play
But it a little bit slow.
if videoData contains in AVAsset , i can store it or AVPlayerItem .But i need to know when it downloaded.
How can i implement caching in a different way or upgrade this one. Help.
we can download any data from cloud using NSURLSession, Download in progress and completion NSURLSession fires delegate methods , we can know download completion events and and progression events by using that delegate methods, you can implement downloading functionality by using NSURLSession as follows
https://www.raywenderlich.com/110458/nsurlsession-tutorial-getting-started
I have an app with vertical collection view of video items, each item should repeat itself continuously, until swiped up or down.
So I need not to stream file every time and not download files that were downloaded earlier.
I came up with slightly different approach:
Stream AVURLAsset,
When reached end I use AVAssetExportSession to save file to disk,
Then I create new AVPLayerItem with new AVAsset using saved file,
AVPlayer's replaceCurrentItem(with: <#T##AVPlayerItem?#>)
But this is lame, I guess. I have to steam file two times. First time on first run, and then then when saving file. I'd like to just cache AVAsset into memory for the current application session, so I don't need to keep files.
I have tried everything I can find to try and get my app to play an mp3. I have a few 2-3 second mp3s that play as sound effects that work, but I am trying to play a 15s mp3 without any luck.
NSError *error;
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[[NSBundle mainBundle] URLForResource: #"test" withExtension: #"mp3"] error:&error];
if (error) {
NSLog(#"Error creating audio player: %#", [error userInfo]);
} else {
if([audioPlayer play] == FALSE) {
NSLog(#"Fail %#", error);
}
}
NSTimer* myTimer = [NSTimer scheduledTimerWithTimeInterval: 15.0 target: self
selector: #selector(stopMusic) userInfo: nil repeats: NO];
I doubt the timer has anything to do with it but thought I would include it just in case. Basically my error checks are not getting triggered at all. It "should" be playing properly but no sound is coming out, and no errors are being displayed.
I have tried a few other things to try and get this working, and I have tried without the timer. I can't find a decent guide. I don't need a full blown media player, just some background music for an UIScreenViewController.
I have also tried the following code, which works good with short MP3s, not with the longer MP3 I want to play.
NSString *path = [[NSBundle mainBundle] pathForResource:#"bgmusic" ofType:#"mp3"];
NSURL *pathURL = [NSURL fileURLWithPath : path];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)pathURL, &audioEffect);
AudioServicesPlaySystemSound(audioEffect);
Please help, I have spent hours trying to sort this. It seems apple re-jig their audio every time they release an OS.
I did a little testing for you: It turned out that music files aren't added to target by default in Xcode 5. Therefore URLForResource:error: likely returns nil.
Try removing the mp3 from your project. Then add it again and this time make shure you add it to your target:
The other thing is: It seems like you're defining the AVAudioPlayer inside a method. You normally set up a #property for your audio player because otherwise its life is limited to the method - and the music stops playing after you reach the end of the method.
This was working fine in iOS 6, but in iOS 7, after I export a portion of a song using AVAssetExportSession to a file, the exported file's duration is wrong in AVAudioPlayer, but correct in AVURLAsset.
AVAudioPlayer incorrectly reports the duration as the whole song duration.
I'm exporting files using steps from https://developer.apple.com/library/ios/qa/qa1730/_index.html
and checking the durations as below:
AVURLAsset* audioAsset = [AVURLAsset URLAssetWithURL:outputURL options:nil];
CMTime audioDuration = audioAsset.duration; // shows correct
and
AVAudioPlayer* avAudioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:outputURL error:nil];
NSTimeInterval duration = avAudioPlayer.duration; // shows wrong
Interestingly, if I play the exported file in iTunes, it also shows the wrong (whole) duration.
I'm not sure how to fix this problem. Could this be a bug in iOS7?
I have developed an app that plays audio files with some voices recordings. When I debug it I have no problems with it, when I download it from the AppStore it works perfectly, my friends use it and they haven't got any problem with the app, but a few people from all over the world have contacted me to tell me that the app doesn't sound.
It is very strange because they tell me that the sound of the bell (mp3 44100Hz, mono, 128kbps) that plays first sounds but the voices (mp3 44100Hz, stereo, 96kbps) don't sound. The people that contact with me has different devices models and different versions of iOS 6.
I use AVAudioPlayer to play the files and I think that it work well.
Have you experienced the same problem?
Thank you
UPDATE
This is the Localizable.strings that I have
I load the file like this:
//
NSString *fileLang = NSLocalizedString(aItem.fileName, nil);
//more code ...
thePlayerURL = [[NSBundle mainBundle] URLForResource:fileLang withExtension:#"mp3"];
//more code ...
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL: aItem.urlAudio error:&error];
player.delegate = self;
[player prepareToPlay];
This is an example of the Localizable.strings with the file keys and values, by default the name of the key is the spanish version of the file:
//AUDIO NAMES
"1_au_es" = "1_au_en";
"2_au_es" = "2_au_en";
"3_au_es" = "3_au_en";
"4_au_es" = "4_au_en";
"5_au_es" = "5_au_en";
"6_au_es" = "6_au_en";
"7_au_es" = "7_au_en";
"8_au_es" = "8_au_en";
"9_au_es" = "9_au_en";
"10_au_es" = "10_au_en";
"11_au_es" = "11_au_en";
"12_au_es" = "12_au_en";
"13_au_es" = "13_au_en";
"14_au_es" = "14_au_en";
"15_au_es" = "15_au_en";
"16_au_es" = "16_au_en";
"17_au_es" = "17_au_en";
"18_au_es" = "18_au_en";
"19_au_es" = "19_au_en";
Have you ensured that their vibrate button isn't flipped on? I wrote a translate app that had the users simply mute/put the vibrate button flipped on. It seems simple, but I've gotten almost 15 emails for that alone, saying the app had no audio.
It seems simple, but that solution is the easiest and quickest fix. Vibrate mode, turns off all audio, even for the apps.
My translate App used AVAudioPlayer, and there was nothing programmatically wrong. Just simply user error.
Maybe the audio bitrate could be the problem? I really don't know...
The bell, than works well has 128kbps and the audio files have 96kbps and sometimes don't work. The strange thing is that the audio works very well for the 90% of the uses but sometimes fail. The bell instead work well the 100% times.
:/
Is very difficult to find the precise solution to this problem without checking your Xcode project configuration. However, by the information that you mentioned in the comments, I suspect that the problem must be related with the Localization strings. Check your localization configuration, check that you are using the localization strings files correctly, check that you wrote a valid filename for each supported localization.
Also, checking your code:
NSString *fileLang = NSLocalizedString(aItem.fileName, nil);
//more code ...
thePlayerURL = [[NSBundle mainBundle] URLForResource:fileLang withExtension:#"mp3"];
//more code ...
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL: aItem.urlAudio error:&error];
player.delegate = self;
[player prepareToPlay];
Why you are not using "thePlayerURL"?. Maybe that is the problem, since is the "Localized URL". I would expect:
NSString *fileLang = NSLocalizedString(aItem.fileName, nil);
//more code ...
thePlayerURL = [[NSBundle mainBundle] URLForResource:fileLang withExtension:#"mp3"];
//more code ...
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:thePlayerURL error:&error];
player.delegate = self;
[player prepareToPlay];
I recently submitted an application that was having the same problems, but it was denied due to the fact that I hadn't setup "Inter-App Audio" in Xcode and in the Certificates pane under the app info. That may help.
In case you are wondering, the application was in violation of Section 2.13 of the App Store Guidelines.
I'm using AVAudioPlayer from iOS SDK for playing short sounds on each click in tableView rows.
I have manually made #selector on button in each row that fires up method playSound:(id)receiver {}. From receiver I get sound url so I can play it up.
This method looks like that:
- (void)playSound:(id)sender {
[audioPlayer prepareToPlay];
UIButton *audioButton = (UIButton *)sender;
[audioButton setImage:[UIImage imageNamed:#"sound_preview.png"] forState:UIControlStateNormal];
NSString *soundUrl = [[listOfItems objectForKey:[NSString stringWithFormat:#"%i",currentPlayingIndex]] objectForKey:#"sound_url"];
//here I get mp3 file from http url via NSRequest in NSData
NSData *soundData = [sharedAppSettingsController getSoundUrl:defaultDictionaryID uri:soundUrl];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithData:soundData error:&error];
audioPlayer.numberOfLoops = 0;
if (error) {
NSLog(#"Error: %#",[error description]);
}
else {
audioPlayer.delegate = self;
[audioPlayer play];
}
}
Everything works fine except for the first play of some sound. The application freezes for about 2 seconds and than sound is played. Second and every other sound play works just right after click on sound button.
I wonder why there is that about 2 seconds freeze on first play when application starts?
From your code snippet, audioPlayer must be an ivar, right?
At the top of the method, you invoke -prepareToPlay on the existing audioPlayer instance (which could be nil, at least on the first pass).
Later in the method, you replace this existing audio player with a brand new AVAudioPlayer instance. The previous -prepareToPlay is wasted. And you're leaking memory with each new AVAudioPlayer.
Instead of caching sound data or URLs, I would try creating a cache of AVAudioPlayer objects, one for each sound. In your -playSound: method, get a reference to the appropriate audio player for the table row, and -play.
You can use -tableView:cellForRowAtIndexPath: as the appropriate point to get the AVAudioPlayer instance for that row, maybe lazily creating the instance and caching it there as well.
You can try -tableView:willDisplayCell:forRowAtIndexPath: as the point where you would invoke -prepareToPlay on the row's AVAudioPlayer instance.
Or you could just do the prepare in -tableView:cellForRowAtIndexPath:. Experiment and see which works best.
Check whether you are getting data asynchronously in function..
NSData *soundData = [sharedAppSettingsController getSoundUrl:defaultDictionaryID uri:soundUrl];
If you are getting asynchronously the execution will be blocked until it will get data.
If your audio is less than 30 seconds long in length and is in linear PCM or IMA4 format, and is packaged as a .caf, .wav, or .aiff you can use system sounds:
Import the AudioToolbox Framework
In your .h file create this variable:
SystemSoundID mySound;
In your .m file implement it in your init method:
-(id)init{
if (self) {
//Get path of VICTORY.WAV <-- the sound file in your bundle
NSString* soundPath = [[NSBundle mainBundle] pathForResource:#"VICTORY" ofType:#"WAV"];
//If the file is in the bundle
if (soundPath) {
//Create a file URL with this path
NSURL* soundURL = [NSURL fileURLWithPath:soundPath];
//Register sound file located at that URL as a system sound
OSStatus err = AudioServicesCreateSystemSoundID((CFURLRef)soundURL, &mySound);
if (err != kAudioServicesNoError) {
NSLog(#"Could not load %#, error code: %ld", soundURL, err);
}
}
}
return self;
}
In your IBAction method you call the sound with this:
AudioServicesPlaySystemSound(mySound);
This works for me, plays the sound pretty damn close to when the button is pressed. Hope this helps you.
This sometimes happens in the simulator for me too. Everything seems to work fine on the device. Have you tested on actual hardware?