Playing sound samples on the iPhone - ios

I've reached the point where i want to play some samples in my game/app. My instinct says use openAL ... I will have the situation where I will need to play multiple samples at once (however no more than 2 or 3) and the samples will be short (2 or 3 seconds).
My question is what is the best way of playing samples on the iPhone given that criteria?

This is a piece of sample code I am using:
1st option:
I defined this in my .h file
SystemSoundID topClick;
And in my .m file I firest load the sound (aiff file):
NSBundle* bundle = [NSBundle mainBundle];
NSString *topClickFile = [bundle pathForResource:#"top_click" ofType:#"aiff"];
Then, when I want to play the sound, I use:
AudioServicesPlaySystemSound(topClick);
2nd option:
You could also use the AVAudioPlayer (available since firmware 2.2 I think):
NSString *graffitiSprayFile = [bundle pathForResource:#"sound_effect" ofType:#"aiff"];
AVAudioPlayer* sprayAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:graffitiSprayFile] error:NULL];
sprayAudio.delegate = self;
[sprayAudio prepareToPlay];
[sprayAudio setNumberOfLoops:999];
The first option is very usable for relatively short sound effect.

Related

Xcode pre-load many audio files into one AVAudioPLayer?

I managed to load an audio file onto a player and play it via the following method:
- (void) play:(NSString*)loop{
NSString* resourcePath = [[NSBundle mainBundle] pathForResource:loop
ofType:#"mp3"];
player = [[AVAudioPlayer alloc] initWithContentsOfURL:
[NSURL fileURLWithPath:resourcePath]];
player.delegate = self;
[player play];
player.numberOfLoops = 1;
player.currentTime = 0;
player.volume = 1.0;
}
}
(I removed a couple of lines in this post but the method itself works great)
The problem here is, that the sound only gets loaded when the method play() is being called which results in a little delay in between the button click that calls the method and the player actually playing it.
I have about 200 sounds in my app and one way would be to load each sound in its own AVAudioPLayer in the viewDidLoad, but I doubt that this is considered good code.
On Android, I am using the SoundPool for this concern and it works great but I don't think there is an equivalent for this on iOS.
I now managed to do it via AudioServicesPlaySystemSound()
I use a method that loads all the sounds in a giant array
- (void)prepSounds{
NSString *soundPath =
[[NSBundle mainBundle] pathForResource:#"bl3"
ofType:#"mp3"];
NSURL *soundPathURL = [NSURL fileURLWithPath:soundPath];
AudioServicesCreateSystemSoundID(
(__bridge CFURLRef)soundPathURL, &soundID[0]);
AudioServicesPlaySystemSound(soundID[0]);
}
where soundID[] is an array of SystemSoundID declared in the header.
Now, I can just easily fire the sound via AudioServicesPlaySystemSound(soundID[0]) where 0 relates to the first sound I loaded. The latency is close to 0 (I would guess 5ms)
However, this only works for sounds no longer than about 30 seconds.
If you need any assistance with that, just comment here, I'm glad to help.

AVAudioPlayer returns wrong file duration

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.

Audio wont play in objective c

I have the following code that used to work to play a sound in my project however after iv done some playing around over the past few weeks on other aspects of the project it dose not seem to play.
The audio file is in the main directory and works in preview.
Any ideas?
//playsound
AVAudioPlayer *showsound;
NSString *audiopath = [[NSBundle mainBundle] pathForResource:#"mouse1" ofType:#"wav"];
NSURL *audiourl = [NSURL fileURLWithPath:audiopath];
showsound = [[AVAudioPlayer alloc]initWithContentsOfURL:audiourl error:Nil];
[showsound play];
It's because after the line showsound play your method comes to an end and showsound (your AVAudioPlayer) is released. Hence there is nothing to do any playing any more. Solution: retain it! (For example, set an instance variable to hold on to it.)

Creating AVAudioPlayer object much before playing the dynamic audio

Whenever i play an audio file like below, it is taking few seconds to start playing the audio. I have seen from some forums and understand that, AVAudioPlayer will take few seconds to start playing if we allocate the object there itself. I am thinking to allocate this object much earlier (may be Appdelegate itself), before when i want to play, so that when i want to play it, it will play immediately.
NSURL *audioPathURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:audioName ofType:#"wav"]];
audioP = [[AVAudioPlayer alloc]initWithContentsOfURL:audioPathURL error:NULL];
[appDelegate.audioP play];
but, i am passing audio url dynamically at run time, so i would like to know how can i allocate the object earlier and then be able to pass dynamic audio url path later?
Please note, my question is different from this question Slow start for AVAudioPlayer the first time a sound is played
In the existing question, they are telling about one audio file and play it when required. But, i am having many different audio file and url path is generated randomly and at run time to play, so i do not know what the actual url path to set and play. So, the answer mentioned here->Slow start for AVAudioPlayer the first time a sound is played
won't help for my question.
I can't use prepareToPlay, because audio path url is set at run time and not just one audio is being used for all the times to play, there will more than 20 audio file randomly chosen and set to play one at a time. So, i need the right answer for it, it is not a duplicate question.
Thank you!
For a single file here the solution:
in your file.h
#import <AVFoundation/AVFoundation.h>
#interface AudioPlayer : UIViewController <AVAudioPlayerDelegate> {
}
To use with button add - (IBAction)Sound:(id)sender; in your file.h
Now in your file.m
//this is a action but you can use inside a void to start auto
#implementation AudioPlayer {
SystemSoundID soundID;
}
- (IBAction)Sound:(id)sender {
AudioServicesDisposeSystemSoundID(soundID);
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundFileURLRef;
//you can use here extension .mp3 / .wav / .aif / .ogg / .caf / and more
soundFileURLRef = CFBundleCopyResourceURL(mainBundle, (CFStringRef) #"sound" ,CFSTR ("mp3") , NULL);
AudioServicesCreateSystemSoundID(soundFileURLRef, &soundID);
AudioServicesPlaySystemSound(soundID);
}
If you want use your code to play audio from URL you can use this:
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>
#interface AudioPlayer : UIViewController <AVAudioPlayerDelegate> {
AVPlayer *mySound;
}
#property (strong, nonatomic) AVPlayer *mySound;
#end
And file.m
- (IBAction)playPause:(id)sender {
if (mySound.rate == 0){
NSURL *url = [NSURL URLWithString:#"http://link.mp3"];
mySound = [[AVPlayer alloc] initWithURL:url];
[mySound setAllowsExternalPlayback:YES];
[mySound setUsesExternalPlaybackWhileExternalScreenIsActive: YES];
[mySound play];
} else {
[mySound pause];
}
}
Hope this can help you!
One thing you can do to reduce the latency of the first load+play is to "spin up" the underlying audio system before you need it by loading and playing a dummy file. For example, in application:didFinishLaunchingWithOptions: add some code like this:
// this won't effect the problem you're seeing, but it's good practice to explicitly
// set your app's audio session category
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
NSURL *audioURL = [[NSBundle mainBundle] URLForResource:#"dummy.wav" withExtension:nil];
AVAudioPlayer *dplayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioURL error:nil];
// we don't want to hear it (alternatively, the audiofile you use for this purpose can be silence.)
dplayer.volume = 0.f;
[dplayer prepareToPlay];
[dplayer play];
[dplayer stop];
Once this code completes, subsequent instantiations and playback of AVAudioPlayer instances should have pretty low latency (certainly well under 1 second)

Audio doesn't sound in a few iOS devices

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.

Resources