When an frame hits another frame it plays a sound so i use this code in the did loader so the sound is loaded
i use the framework
#import <AVFoundation/AVFoundation.h>
- (void)viewDidLoad
NSString *path1 = [[NSBundle mainBundle] pathForResource:#"ballbounce" ofType:#"mp3"];
theaudio1 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path1] error: NULL];
then use this to play the sound
[audio1 play];
but some times the sound doesn't play, the sound clip is only 0.7 seconds long and just makes a simple noise.
like
enemy.center = CGPointMake(enemy.center.x+pos.x,enemy.center.y+pos.y);
if (enemy.center.x > 308 || enemy.center.x < 10)
{
[theaudio1 play];
pos.x = -pos.x;
}
if (enemy.center.y > 400 || enemy.center.y < 100)
{
[theaudio1 play];
pos.y = -pos.y ;
}
the sound sometimes plays in quick succession and sometimes the sound doesn't play is there a better way that i can write this code?
Call the prepareToPlay method on your AVAudioPlayer instance before you call play. It preloads the audio player's buffer.
Also, init your player to play from memory if you can, since it's only a very short sound. See the initWithData:error method description in the AVAudioPlayer class documentation.
Related
I create the AVAudioPlayer object and play the music within the scene, if i transition through to another scene and then go back to the original scene it creates another object and plays the same soundtrack twice. How do i overcome this ?
NSURL *url = [[NSBundle mainBundle] URLForResource:#"backgroundMusic" withExtension:#"mp3"];
self.backgroundMusic = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
self.backgroundMusic.numberOfLoops = -1;
[self.backgroundMusic prepareToPlay];
if (self.backgroundMusic.playing == NO) {
[self.backgroundMusic play];
}
Well what do you want to do?
Do you want to play music only in that scene? Well then before you transition, stop the avaudioplayer.
[audioplayer stop];
Or, maybe you want to start music in the scene, and let it just continue playing if you leave the scene or go back to the scene?
Well then you would need a seperate wrapper class for your music. Create a class, and just put a call to it to play music. You can check the .playing property to know if music is already playing or not.
So I have an application and I want to keep it working even if the screen is turned off.
Previously when I wanted to do that I used this hack/trick - I play a silent/empty sound in a loop in the background (AudioServicesPlaySystemSound) so if user presses the on/off button the application still works in the background - so it never allow iPhone to go to sleep mode - it just turned off the screen and maybe things like wifi or bluetooth (and on the iPod Touch accelerometers as far as I remember). And it worked. I wanted to use the same trick in my new application but when I was testing it now it seems it doesn't work anymore. The sound in the background plays (when I replace "empty" audio file with some sound I can hear it play) even with screen turned off but the sound it should play (using AVAudioPlayer) doesn't play (even when I turn the screen on again).
I don't know at which point it stopped working (it worked on 3.x OS for sure). Am I doing something wrong? Did Apple changed/fixed the "hack" that allowed your app to work even with screen turned off? Is there another way to allow the device to go to sleep (and drain the battery less) but continue to work?
This is the code I use to play background/silent sound:
-(void) playSilentSound
{
CFBundleRef mainBundle = CFBundleGetMainBundle ();
CFURLRef silentUrl = CFBundleCopyResourceURL (mainBundle, CFSTR ("silence"), CFSTR ("aiff"), NULL);
AudioServicesCreateSystemSoundID (silentUrl, &silentSound);
silentTimer = [NSTimer scheduledTimerWithTimeInterval: 2.0 target: self selector:#selector(playSilence) userInfo: nil repeats: YES];
}
-(void) playSilence
{
AudioServicesPlaySystemSound (silentSound);
}
And this is how I play the sound that should play even if the screen is turned off:
-(BOOL) playSound: (NSString *) path withLoops: (BOOL) loops stopAfter: (int) seconds
{
NSError *error;
player = [[AVAudioPlayer alloc] initWithContentsOfURL: [NSURL fileURLWithPath: path] error: &error];
player.delegate = self;
player.numberOfLoops = 0;
player.volume = volume;
secondsPlayed = 0;
loop = loops;
BOOL played = [player play];
if(played && seconds > 0)
{
timer = [[NSTimer scheduledTimerWithTimeInterval: 0.5 target: self selector: #selector(stopPlaying:) userInfo: [NSNumber numberWithInt: seconds] repeats: YES] retain];
secondsLimit = seconds;
} else {
secondsLimit = -1;
}
}
-(void) stopPlaying:(NSTimer*)theTimer
{
if(secondsLimit > 0 && (secondsPlayed + [player currentTime]) >= secondsLimit)
{
[player stop];
[timer release];
timer = nil;
}
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)sender successfully:(BOOL)flag
{
if(sender == player)
{
if(flag) { secondsPlayed += sender.duration; }
if(loop) { [player play]; }
}
}
The code is a bit complicated maybe - it could be just the first few lines - it's made that way so you can play only X seconds of sound (if sound is shorter than X it will play few loops until the total time is >= X). And of course everything is working fine when the screen is left on.
Also - if you find the code useful in your projects (like playSound:withLoops:stopAfter:) - feel free to use it (but it would be cool if you send me a message so I would know that I helped :)).
So the answer is to set session category. Some categories just turn off sound when the device is being locked. For me the best option was AVAudioSessionCategoryPlayback
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];
I don't know about Apple fixing stuff, but in iOS 4 there are now official ways to continue doing things in the background which you should take advantage of.
Alright I have two problems. I'm using AVAudioPlayer to play a simple audio file in .caf format. What I'm trying to do is have a play and pause button. Here is a sample of a simple IBAction to play the audio from a button.
- (IBAction) clear {
NSString *path = [[NSBundle mainBundle] pathForResource:#"clear" ofType:#"caf"];
AVAudioPlayer* myAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
myAudio.delegate = self;
myAudio.volume = 1.0;
myAudio.numberOfLoops = 0;
[myAudio play];
}
So when I go ahead and create another IBAction with the method...
- (IBAction) stopaudio {
[myAudio stop];
audioPlayer.currentTime = 0;
}
I compile and xcode tells me that myAudio in the stopaudio IBAction is not defined. How do I fix it so I'm able to access myAudio in another IBAction?
My Second question is that I'm using NavigationController to setup a TableView, when I make a selection it takes me to a UIView with a back button to go back to the TableView. What I'm trying to do is to kill any audio that I have playing in the UIView when I go back to the TableView. The code above is part of the code that I'm going to have in the UIView.
Also one quick question. Lets say I have an audio file that is 1 minute long. Clicking play again will overlap a new instance of that audio before the old instance is done playing. How can I check to see if myAudio is playing and not allow it to be played again until myAudio is finished playing?
Thank You!
First of all, you should read the documentation. It will remove almost all of your questions. Nevertheless, to have access to your instance of AVAudioPlayer from all methods of your class, you should define it in your .h file. To stop your player on returning to the previous screen, call [myAudio stop] in your viewWillDisappear:(BOOL)animated method. About your last question. If you define myAudio in your .h file, you will be able to check if it is playing now. smth like
- (IBAction) clear {
if(![myAudio isPlaying]){
[myAudio play];
}
}
It appears that AVAudioPlayer is not reentent. So in the following would soundfx play to completion, delay 1 second, then play again, rather then the 1 second delay - and resultant overlapping playback I desire:
// ...
AVAudioPlayer* soundfx = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:nil];
(void) makeSomeNoise {
[soundfx play];
sleep(1);
[soundfx play];
}
Must I then resort to NSOperation - threading - to achieve my goal of overlapping playback?
Note: I am using IMA4/ADPCM format which is apparently the correct format for layered sound playback.
Cheers,
Doug
It's not that AVAudioPlayer is not reentrant. It is that AVAudioPlayer starts to play your sound after the runloop has ended, and not in your makeSomeNoise function. If you want to play your sound with a one second delay, you can do this:
(void) makeSomeNoise {
[soundfx play];
[soundfx performSelector:play withObject: nil afterDelay:1.0];
}
I am implementing a sound effect that plays while a user is dragging a UISlider.
In a previous question, I used AudioServicesPlaySystemSound() but that type of sound can't be halted. I need the sound to halt when the user is not actively dragging the slider but has not released it.
Now I am creating a sound using an AVAudioPlayer object. I initialize it in my View Controller like this:
#interface AudioLatencyViewController : UIViewController <AVAudioPlayerDelegate> {
AVAudioPlayer *player1;
}
#implementation AudioLatencyViewController
#property (nonatomic, retain) AVAudioPlayer *player1;
#synthesize player1;
-(void)viewDidLoad {
[super viewDidLoad];
// the sound file is 1 second long
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"tone1" ofType:#"caf"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:soundFilePath];
AVAudioPlayer *newPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
[fileURL release];
self.player1 = newPlayer;;
[newPlayer release];
[self.player1 prepareToPlay];
[self.player1 setDelegate:self];
}
I have two methods to control the sound. The first one plays the sound whenever the slider is being moved. It is connected to the UISlider's Value Changed event in Interface Builder.
-(IBAction)sliderChanged:(id)sender;
{
// only play if previous sound is done. helps with stuttering
if (![self.player1 isPlaying]) {
[self.player1 play];
}
}
The second method halts the sound when the user releases the slider button. It's connected to the UISlider's Touch Up Inside event.
-(IBAction)haltSound;
{
[self.player1 stop];
}
This works when the slider button is moved then released quickly, but if you hold down the button longer than two or three seconds, the sound repeats (good) then breaks up (bad). From then on, the sound won't play again (super bad).
I have tried setting the sound to loop using:
[self.player1 setNumberOfLoops:30];
...but that leads to the app freezing (plus locking up the Simulator).
What can I do to debug this problem?
When a user is holding the UISlider, Value Changed gets called MANY MANY times (any time the user moves just a slight amount). I would use Touch Down instead.
You might want to still try using the loop for this route...
I like the idea of playing a sound while the user is moving the slider though. Let me know how it works out.
deggstand#gmail.com