I am working on a simple game and after every screen touch, an animation is happening on a small UIImageView and this happen very well and the app is running smoothly with CADisplayLink as the timer.
I added an mp3 file to play after every touch with 1 second lengh as AVAudioPlayer imagine a sound like : Bip
So the first time that I touch the screen first hiccup happens an the app freeze for less than a second that I can say it is ok coz it's the first time that the sound allocate memory.
The problem happens later when u touch the screen again if I touch it earlier than 3 seconds, the app doesn't hiccups but if I wait 4 seconds and more, the app starts to hiccup after every touch.
Every time if I touch again and again earlier than 3 seconds between touches, the app doesn't hiccups, but after 4 seconds between touches, the app hiccups.
Any idea to solve hiccups?
This is some code if needed
#property (nonatomic, strong) AVAudioPlayer *mySound;
- (void)viewDidLoad {
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"bip" ofType:#"mp3"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:soundFilePath];
AVAudioPlayer *newPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
self.mySound = newPlayer;
[mySound prepareToPlay];
[mySound setDelegate:self];
}
and after touch happens
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
if (location.x < viewWidth/2) {
[mySound play];
} else {
[mySound play];
}
}
Make a very short silent sound. Use your timer to play that (with another sound player) every second or so. — You see what the idea is here; we are try to keep the media server alive so that it is ready to go when we want our sound effect. Since you say there's no problem before 3 seconds, that makes me think that after 3 seconds the media server has gone back to sleep. Our goal is to keep "tickling" it so that can't happen.
In general, however, my impression is that AVAudioPlayer is not intended for this sort of thing. You should probably be using AVAudioEngine, which can be kept running and made to play sounds without latency.
Related
I have thoroughly looked through this forum and I have not found a solution to this issue. When my app launches during the splash screen I want a 5 sec sound clip to play then stop when the app is done launching and the home screen is shown.
In the AppDelegate.m
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *musicFile = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:#"bknewsroom2"
ofType:#"caf"]];
AVAudioPlayer *launchClip = [[AVAudioPlayer alloc] initWithContentsOfURL:musicFile error:nil];
[launchClip play];
Now I have a whole bunch of stuff loading within didFinishLaunchingWithOptions this is just one of many things. The sound won't launch in the simulator nor will it launch on my phone. So am I missing something? Again it is a 5 second clip that I want to play when the app launches for the first time. Any advice would be greatly appreciated.
I fixed this issue:
in the AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//------ PLAY SOUND CLIP WHILE LOADING APP -----
NSLog(#"launch banner with clip");
NSURL *clip = [[NSBundle mainBundle] URLForResource: #"soundfile" withExtension:#"caf"];
self.startupPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:clip error:NULL];
[self.startupPlayer play];
Now when the application launches, the sound clip plays. It does hiccup once which I'm looking into, but the function works. Also, I put this at the top of didFinishLaunchingWithOptions if it was in the middle some place, it would work.
I have spent days researching on SO and other websites for the answer to this but without any luck.
Essentially, the challenge I've set for myself is to create an alarm clock app for iOS that will sound no matter where the user may be (foreground or background). This I have already accomplished by using an AVAudioPlayer instance and starting to play an empty sound file when the user sets the alarm in order for the app to keep running in the background. When it is time for the alarm to go off (ie when the NSTimer is fired) a second player, which has already been initiated and prepared to play, starts playing the ringtone to which the user wakes up.
Also, I have managed to handle interruptions by a phone call, system timer or alarm clock by implementing the AVAudioSessionDelegate methods beginInterruption and endInterruptionWithFlags. It works both in background and foreground modes, but the strangest thing happens:
When the interruption ends, the AVAudioPlayer resumes playing BUT I cannot execute any other code in my app unless I bring the app to the foreground again.
To get to the bottom of this, I have experimented with a much simpler project which I am posting below.
What this app does is, as soon as you enter the app, an instance of the AVAudioPlayer class starts looping a certain sound. Then when you bring it to the background, the player continues to loop the sound. When an interruption occurs I pause the player and when it ends I use a dispatch to wait a couple of seconds before it calls two methods, ie (void)playPlayer, a method that contains the code to resume playing the file and (void)tester, a method that contains a timer, which is set to stop the player 5 seconds after the interruption (or 7 seconds to be exact) has ended. Both the methods get called as indicated by the NSLogs I have put in both of them, but the timer never gets fired and the player continues to play indefinitely.
Here is the code for the .h file:
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
#interface InterruptionTest3ViewController : UIViewController <AVAudioSessionDelegate, AVAudioPlayerDelegate>
{
AVAudioSession *mySession;
AVAudioPlayer *myPlayer;
}
-(void) playPlayer;
-(void) pausePlayer;
-(void) tester;
#end
Here is the code for the .m file:
#import "InterruptionTest3ViewController.h"
#interface InterruptionTest3ViewController ()
#end
#implementation InterruptionTest3ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
mySession = [AVAudioSession sharedInstance];
NSError *setActiveError = nil;
[mySession setActive:YES withFlags:AVAudioSessionSetActiveFlags_NotifyOthersOnDeactivation error:&setActiveError];
if (setActiveError) {
NSLog(#"Session failed to activate within viewDidLoad");
}
else {
NSLog(#"Session was activated within viewDidLoad");
}
NSError *setCategoryError = nil;
[mySession setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
if (setCategoryError) {
NSLog(#"Category failed to be set");
}
else {
NSLog(#"Category has been set");
}
[mySession setDelegate:self];
NSString *path = [[NSBundle mainBundle] pathForResource:#"headspin" ofType:#"wav"];
NSError *initMyPlayerError = nil;
myPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&initMyPlayerError];
if (initMyPlayerError) {
NSLog(#"myPlayer failed to initiate");
}
else {
NSLog(#"myPlayer has been initiated");
}
[myPlayer prepareToPlay];
[self playPlayer];
OSStatus propertySetError = 0;
UInt32 allowMixing = true;
propertySetError = AudioSessionSetProperty (
kAudioSessionProperty_OverrideCategoryMixWithOthers,
sizeof (allowMixing),
&allowMixing
);
[myPlayer setNumberOfLoops:-1];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}
-(void) beginInterruption
{
[myPlayer pause];
}
-(void) endInterruptionWithFlags:(NSUInteger)flags
{
if (flags) {
if (AVAudioSessionInterruptionFlags_ShouldResume)
{
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC),dispatch_get_main_queue(), ^{
[self playPlayer];
[self tester];
});
}
}
}
}
-(void) tester
{
[NSTimer timerWithTimeInterval:5.0 target:self selector:#selector(pausePlayer) userInfo:nil repeats:NO];
NSLog(#"tester method has been called");
}
-(void) playPlayer
{
[NSTimer timerWithTimeInterval:5.0 target:myPlayer selector:#selector(stop) userInfo:nil repeats:NO];
[myPlayer play];
NSLog(#"playPlayer method has been called");
}
-(void) pausePlayer
{
[myPlayer pause];
}
//viewDidUnload etc not listed.
So, this is it folks. Again, why is the timer not being fired after an interruption while the app is in the background? Do I need to set something in the applicationDidEnterBackground method?
Thank you very much in advance!
please use "[NSTimer scheduledTimerWithTimeInterval:5.0 target:myPlayer selector:#selector(stop) userInfo:nil repeats:NO]";
Not to avoid the question, but the play-an-empty-sound-in-the-background bit is a hack. The ability to play a sound in the background is provided so that you can play music or other sound for the user's benefit, not to keep a background process alive.
Consider using local notifications instead if you want to play a sound or otherwise alert the user at a particular time.
why is the timer not being fired after an interruption while the app is in the background?
If I remember correctly, timers are either suspended or cancelled when your app enters the background. The documentation explicitly says that you should "stop timers and other periodic tasks" when your app is interrupted, i.e. when it's sent to the background.
I want to play a video in my iPhone app. I used this code:
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *movieUrl = [NSURL fileURLWithPath:
[[NSBundle mainBundle] pathForResource:#"Myvideo"
ofType:#"mp4"]];
//create a new instance of MPMoviePlayerController
MPMoviePlayerController* myMovie=[[MPMoviePlayerController alloc]
initWithContentURL:movieUrl];
//disable scaling of our movie
myMovie.scalingMode = MPMovieScalingModeNone;
//don't show any controls
// myMovie.movieControlMode = MPMovieControlModeHidden;
//you can specify at which time the movie should
//start playing (default is 0.0)
myMovie.initialPlaybackTime = 2.0;
//register a callback method which will be called
//after the movie finished
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieFinished:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:myMovie];
//start the movie (asynchronous method)
[myMovie play];
// Do any additional setup after loading the view from its nib.
}
Only the sound works with this code. Can someone help me please?
You need to allocate a frame to your movie and add it to the view. See here.
So you can add:
[myMoview.view setFrame: self.view.bounds]; // player's frame must match parent's
[self.view addSubview:myMovie.view];
Apple Technical Q&A 1240:
MPMoviePlayerController plays movie audio but not video
Q: I'm able to successfully play movies using MPMoviePlayerController on iOS 3.1.3. When I run this same code on the iPad and on the iPhone with iOS 4 I can hear the movie audio, but the video is no longer displayed. What's going on?
A: Starting with iPhone iOS 3.2, calling the -play: method still starts playback of the movie but it does not ensure that the movie is visible. In order to display a movie, you must get the new view property from your MPMoviePlayerController object and add that view to your view hierarchy. Here's a code snippet:
Listing 1 How to add the MPMoviePlayerController view property to your view hierarchy to play a movie.
#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
[[player view] setFrame:[myView bounds]]; // size to fit parent view exactly
[myView addSubview:[player view]];
[player play];
See Important Porting Tip for Using the Media Player Framework and the MPMoviePlayerController Class Reference for more information.
Howdy! I'm writing an iPad app, and I need to be able to play a video when a UIView loads. However, I was getting a BAD_EXC_ACCESS if I try to message my MPMoviePlayerController anywhere after I initialize it. I removed the MPMediaPlayerController from my *.h file, then declared it entirely in the implementation file, and now I'm getting the message at the bottom below my code. There are no issues in Build and Analyze about memory leaks (or any issues, for that matter), and I cannot find any posts about this. Here's my code:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
NSString *url = [[NSBundle mainBundle] pathForResource:#"p0600c0100cmpintro" ofType:#"m4v"];
MPMoviePlayerController *movie = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL fileURLWithPath:url]];
NSLog(#"%#", movie);
movie.view.frame = CGRectMake(5, 0, 1035, 768);
movie.view.contentMode = UIViewContentModeScaleToFill;
[[movie view] setCenter:CGPointMake(movie.view.center.x-10, movie.view.center.y)];
[movie setControlStyle:MPMovieControlStyleNone];
[movie setShouldAutoplay:YES];
[[self view] addSubview:[movie view]];
return self;
}
The NSLog of "movie" gives "MPMoviePlayerController: 0x1b77f0", but then the error message upon crash is "* -[MPMoviePlayerController playbackState]: message sent to deallocated instance 0x1473a0". Help?
According to the documentation it looks like the frame of the movie view needs to match the view of its parent. Also try moving your code out of the initWithNibName:bundle: into viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *movieContainer = [[UIView alloc] initWithFrame:CGRectMake(5, 0, 300, 400)];
//Do any other positioning of the view you would like
NSString *path = [[NSBundle mainBundle] pathForResource:#"p0600c0100cmpintro" ofType:#"m4v"];
NSURL *url = [NSURL fileURLWithPath:path];
MPMoviePlayerController *movie = [[MPMoviePlayerController alloc] initWithContentURL:url];
movie.view.frame = movieContainer.bounds; //Make sure this is the bounds of its parent view
movie.scalingMode = MPMovieScalingModeFill;
movie.controlStyle = MPMovieControlStyleNone;
movie.shouldAutoplay = YES;
[movieContainer addSubview:movie.view];
[self.view addSubview:movieContainer];
[movieContainer release];
}
One last suggestion would be to keep a reference of the movie so that you can dealloc it once the view controller gets deallocated
I would suggest creating your MoviePlayer in viewDidLoad, then in viewDidAppear make the movie play, for best results.
Alright, so I have another MPMoviePlayerController instance which was being deallocated earlier, but when I try to create another instance of MPMoviePlayerController, all the messages sent to this one were being sent to the deallocated instance, resulting in the memory problem. So I just removed the part where I released the first instance, and it works perfectly fine. My question now is this: is there a way to deallocate this first instance so that it isn't a burden on memory when it isn't needed? I feel that there should be a more elegant solution to this problem. I will need to play videos frequently in this application.
I found the solution a number of weeks ago and forgot about this post. I wasn't successfully releasing the MPMoviePlayerController. For those curious, in order to release an MPMoviePlayerController, we must first remove the notification from the NSNotificationCenter (if one was set), stop the movie (even if it's done playing), THEN release it. I wasn't doing this earlier in the application with my first MPMoviePlayerController, so it was trying to reference the deallocated instance. When the movie is done playing, here is the code to release the movie successfully:
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackStateDidChangeNotification object:movie];
[movie.view removeFromSuperview];
[movie stop];
[movie release];
I have this code trying to run a video on the iPhone 4 Simulator.
The problem is that looks like it loads the player but half a second later loads a back screen on top of the whole application disabling touch and everything, and it looks like it does not play the video also, because I can't hear anything.
Any ideas?!
MPMoviePlayerViewController *mp =
[[MPMoviePlayerViewController alloc] initWithContentURL:videoUrl];
if (mp) {
mp.moviePlayer.scalingMode = MPMovieScalingModeFill;
mp.moviePlayer.movieSourceType = MPMovieSourceTypeFile;
[mp.moviePlayer play];
[self presentMoviePlayerViewControllerAnimated:mp];
[mp release];
}
I believe the problem is caused by releasing the MPMoviePlayerViewController. Simply retain the controller until you are done with it.
Prior to the "[mp release];" add this line to save the value away.
self.moviePlayerViewController = mp;
Then update your dealloc method to do the release:
- (void)dealloc {
[_moviePlayerViewController release], _moviePlayerViewController = nil;
[super dealloc];
}
Add the synthesize to the top of your .m file:
#synthesize moviePlayerViewController = _moviePlayerViewController;
Add the defination to the #interface of your .h file:
MPMovieViewController *_moviePlayerViewController;
Add the property to your .h file:
#property (readwrite, retain) MPMovieViewController *moviePlayerViewController;
You may need some headers in your header:
#import <MediaPlayer/MediaPlayer.h>
#import <MediaPlayer/MPMoviePlayerViewController.h>
You may also need to balance your "presentMoviePlayer" call with the dismiss somewhere:
[self dismissMoviePlayerViewControllerAnimated];
Phew, code everywhere. Anyway, if you are finished with the resource early, you may be able to release it sooner by using NotificationManager to watch for MPMoviePlayerPlaybackDidFinishNotification. There are many examples of that, so I won't repeat it.
Hope this helps.
This is the code I'm using:
MPMoviePlayerViewController *movieViewController = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL URLWithString:contentUrl]];
movieViewController.moviePlayer.movieSourceType = MPMovieSourceTypeFile;
[self presentMoviePlayerViewControllerAnimated:movieViewController];
[movieViewController release];
It seems to work fine for me. Two notes:
Some simulators (like the current iOS 5.0) crash when playing a movie, but it works on a real device
if you leave out the movieSourceType part, a black screen is shown for about a second before the movie starts