How to stop audio after finishing loop in iOS - ios

I'm new to iPhone programming. Using below code I'm displaying image with audio. Images and audio I have stored in array.After audio completed in audioPlayerDidFinishPlaying method I'm using this code I'm displaying next image with audio.
Every thing is work fine. But problem is after audio and images are finish, again its coming from starting. May be some problem in below code can any one correct it plz..
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
for (NSString* path in array000)
{
[blaukypath2 addObject:[UIImage imageWithContentsOfFile:path]];
NSLog(#"%#",path);
}
currentImage++;
NSError *error;
if (currentImage >= blaukypath2.count) currentImage = 0;
UIImage *blaukyyimag = [blaukypath2 objectAtIndex:currentImage];
[img10 setImage:blaukyyimag];
NSLog(#"%icurrentImage++ DIDF",currentImage);
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[audiopath2 objectAtIndex:currentImage] error:&error] ;
audioPlayer.delegate = self;
[audioPlayer prepareToPlay];
[audioPlayer play];
}

If you dont want to repeat the image and audio than remove this line in the code..
if (currentImage >= blaukypath2.count) currentImage = 0;
Because this line will make your currentImage 0. So the loop will start again.
UPDATE
Do it like this
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
for (NSString* path in array000)
{
[blaukypath2 addObject:[UIImage imageWithContentsOfFile:path]];
NSLog(#"%#",path);
}
if (currentImage < blaukypath2.count)
{
currentImage++;
NSError *error;
UIImage *blaukyyimag = [blaukypath2 objectAtIndex:currentImage];
[img10 setImage:blaukyyimag];
NSLog(#"%icurrentImage++ DIDF",currentImage);
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[audiopath2 objectAtIndex:currentImage] error:&error] ;
audioPlayer.delegate = self;
[audioPlayer prepareToPlay];
[audioPlayer play];
}
}

befor plying next audio, stop previous one.
[audioPlayer stop];
Hopes it work for you.

Make a instance variable int arrayIndex; initialize with = 0
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
if([array000 count] > arrayIndex) {
NSString* path = [arrayIndex objectAtIndex:arrayIndex];
[blaukypath2 addObject:[UIImage imageWithContentsOfFile:path]];
NSLog(#"%#",path);
currentImage++;
NSError *error;
if (currentImage >= blaukypath2.count) currentImage = 0;
UIImage *blaukyyimag = [blaukypath2 objectAtIndex:currentImage];
[img10 setImage:blaukyyimag];
NSLog(#"%icurrentImage++ DIDF",currentImage);
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[audiopath2 objectAtIndex:currentImage] error:&error] ;
audioPlayer.delegate = self;
[audioPlayer prepareToPlay];
[audioPlayer play];
}
arrayIndex++;
} else {
[audioPlayer stop];
}
try this approach and let me know if it work

Related

Black Screen when attempting to use MPMoviePlayer

I am using MPMoviePlayer to display a video from an external URL onto my iPhone App, however when I run the App a black screen is all that shows.
Here is the URL I am using:
2015-04-27 00:11:29.655 Floadt[21069:2598414] https://scontent.cdninstagram.com/hphotos-xaf1/t50.2886-16/11179443_819874424728492_389701720_n.mp4
Here is my code to try to setup MPMoviePlayer:
if (entry[#"videos"] != nil) {
NSLog(#"There is a Video: %#", entry[#"videos"]);
NSString *urlString = entry[#"videos"][#"standard_resolution"][#"url"];
NSLog(urlString);
NSURL *url = [NSURL URLWithString:urlString];
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL: url];
[player prepareToPlay];
[player.view setFrame: CGRectMake(10, 65, 299, 299)];
[cell.contentView addSubview: player.view];
player.shouldAutoplay = YES;
[player play];
}
You need to retain your instance to MPMoviePlayerController i.e. as a property or an instance variable. The reference to the movie player is lost if you do not retain.
When we try to load the video from URL initially it will display blank screen only. MPMoviePlayerController will take some time to load the video from url.So we can display first frame of the video till the video loads. For this need to import two frameworks.
1.AVFoundation
2.AssetsLibrary
Using these two we can display first frame of video into UIImageView as follows:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
url=[NSURL URLWithString:#"https://scontent.cdninstagram.com/hphotos-xaf1/t50.2886-16/11179443_819874424728492_389701720_n.mp4"];
AVAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
if ([[avAsset tracksWithMediaType:AVMediaTypeVideo] count] > 0)
{
AVAssetImageGenerator *imageGenerator =[AVAssetImageGenerator assetImageGeneratorWithAsset:avAsset];
Float64 durationSeconds = CMTimeGetSeconds([avAsset duration]);
CMTime midpoint = CMTimeMakeWithSeconds(durationSeconds/2.0, 600);
NSError *error;
CMTime actualTime;
CGImageRef halfWayImage = [imageGenerator copyCGImageAtTime:kCMTimeZero actualTime:&actualTime error:&error];
if (halfWayImage != NULL)
{
NSString *actualTimeString = (NSString *)CFBridgingRelease(CMTimeCopyDescription(NULL, actualTime));
NSString *requestedTimeString = (NSString *)CFBridgingRelease(CMTimeCopyDescription(NULL, midpoint));
NSLog(#"Got halfWayImage: Asked for %#, got %#", requestedTimeString, actualTimeString);
UIImage *img=[UIImage imageWithCGImage:halfWayImage];
_imgVw.image=img;
}
}
UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(tapped)];
[_imgVw addGestureRecognizer:tap];
}
-(void)tapped
{
MPMoviePlayerController *movPlayer=[[MPMoviePlayerController alloc] init];
[movPlayer setContentURL:url];
[movPlayer setMovieSourceType:MPMovieSourceTypeFile];
[movPlayer.view setFrame:CGRectMake(0, 0, _imgVw.frame.size.width, 250)];
[movPlayer prepareToPlay];
movPlayer.controlStyle = MPMovieControlStyleNone;
movPlayer.fullscreen = NO;
movPlayer.shouldAutoplay=YES;
[movPlayer setScalingMode:MPMovieScalingModeAspectFill];
[_imgVw addSubview:movPlayer.view];
[movPlayer play];
}
Here i am taking UIImageView view for playing the video. In viewDidLoad i am loading the 1st frame and giving tap gesture to the UIImageView. When i tapped the ImageView then i am playing the video.

Memory Management - Loading Sound Effects - Cocoa Xcode

So it has been brought to my attention that I should optimise the way I load and use sounds in a small game that I am developing for iOS.
I load a "boing" sound and play it every time I tap the screen, making the sprite jump (like mario).
I want to be able to play the sound every time, even if the sound is already playing from the previous jump...
Below are the 2 ways I'm currently using:
1st implementation:
//load the music from file
-(void)LoadMusic{
jumpSound = [[NSBundle mainBundle] pathForResource:#"boing" ofType:#"mp3"];
}
//call in viewDidLoad
- (void)viewDidLoad{
[self LoadMusic];
...
[super viewDidLoad];
}
//play sound when called
-(void)playSound{
jumpAffect = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:jumpSound] error:NULL];
[jumpAffect play];
}
//tap/touch to jump (& play sound)
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event{
[self playSound];
jumpUp = 16;
}
2nd implementation:, this is similar except I load the same file 5 times, and loop through to the next one (so the same sound affect can be called even if it's already in previous session).
int soundStage = 1;
//load the music from file
-(void)LoadMusic{
jumpSound = [[NSBundle mainBundle] pathForResource:#"Boing" ofType:#"mp3"];
jumpAffect = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:jumpSound] error:NULL];
jumpAffect.delegate = self;
jumpAffect2 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:jumpSound] error:NULL];
jumpAffect2.delegate = self;
jumpAffect3 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:jumpSound] error:NULL];
jumpAffect3.delegate = self;
jumpAffect4 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:jumpSound] error:NULL];
jumpAffect4.delegate = self;
jumpAffect5 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:jumpSound] error:NULL];
jumpAffect5.delegate = self;
}
//call in viewDidLoad
- (void)viewDidLoad{
[self LoadMusic];
...
[super viewDidLoad];
}
//tap/touch to jump (& play sound)
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event{
jumpUp = 16;
if(soundStage == 1){
[jumpAffect play];
soundStage = 2;
}
else if(soundStage == 2){
[jumpAffect2 play];
soundStage = 3;
}
else if(soundStage == 3){
[jumpAffect3 play];
soundStage = 4;
}
else if(soundStage == 4){
[jumpAffect4 play];
soundStage = 5;
}
else if(soundStage == 5){
[jumpAffect5 play];
soundStage = 1;
}
I'm wondering which why is the better way? I'm hoping to avoid memory leaks and just optimise it by being able to continuously have the same sound play when the screen is tapped. Thanks.
AVAudioPlayer plays multiple sounds simultaneously, one sound per audio player. So better is to load/play your quick sound inside the touchesBegan method without keeping the reference:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event
{
NSString *jumpSound = [[NSBundle mainBundle] pathForResource:#"boing" ofType:#"mp3"];
AVAudioPlayer *jumpAffect = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:jumpSound] error:NULL];
[jumpAffect prepareToPlay];
[jumpAffect play];
jumpUp = 16;
}

How to stop AVPlayer Ios and remove the periodicTimeObserverForInterval

I'm having a little difficulty stopping AVPlayer.
I have a method that records and plays music simultaneously. I'm using AVPlayer to play the music because I want to use the addPeriodicTimeObserverForInterval Function. I have it set up as follows:
- (IBAction) recordVoice:(id)sender {
if(!recorder.isRecording){
//set up the file name to record to
NSString *recordingLocation = [self createFileName];
recordingName = recordingLocation;
NSArray *pathComponents = [NSArray arrayWithObjects:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject],
recordingLocation, nil];
NSURL *outputFileURL = [NSURL fileURLWithPathComponents:pathComponents];
recordingURL = outputFileURL;
// Setup audio session
session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker
error:nil];
// Define the recording settings to record as m4a
NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];
[recordSetting setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
// initiate and prepare the recorder
recorder = [[AVAudioRecorder alloc] initWithURL:outputFileURL settings:recordSetting error:NULL];
recorder.delegate = self;
recorder.meteringEnabled = YES;
[recorder prepareToRecord];
[session setActive:YES error:nil];
[recorder record];
// find which song to play and initiate an AVPlayer to play it
NSString *playerLocation = self.TitleLabel.text;
NSString *path = [[NSBundle mainBundle] pathForResource:playerLocation ofType:#"m4a"];
player = [[AVPlayer alloc] initWithURL:[NSURL fileURLWithPath:path]];
lastTime = nil;
//check where the player is at and update the song lines accordingly
[player addPeriodicTimeObserverForInterval:CMTimeMake(3, 10) queue:NULL usingBlock:^(CMTime time){
NSTimeInterval seconds = CMTimeGetSeconds(time);
for (NSDictionary *item in robotR33) {
NSNumber *time = item[#"time"];
if ( seconds > [time doubleValue] && [time doubleValue] >= [lastTime doubleValue] ) {
lastTime = #(seconds);
NSString *str = item[#"line"];
[self nextLine:str];
};
}
}];
[player play];
[_recordButton setImage:[UIImage imageNamed:#"micRecording.gif"] forState:UIControlStateNormal];
}
else{
[recorder stop];
player = nil;
[session setActive:NO error:nil];
}
}
If the recorder is not recording I set up both a new recorder AVAudioRecorder and an AVPlayer. In the AVPlayer I set up an AddPeriodicTimeObserverForInterval which updates the UI based on the position of the player.
If the recorder is recording I stop the recorder and I set the player to nil. This stops the audio from playing but I notice that the addPeriodicTimeObserverInterval is still running because the UI continues to update. Should I destroy the AVPlayer altogether and if so how should I do that? Many thanks in advance.
Also as an aside, I have a warning inside the addPeriodicTimeObserverForInterval block. I am looping over an Array called robotR33. Xcode tells me that 'Capturing self strongly in this block is likely to lead to a retain cycle". Could this be part of my problem?
When finished playing the observer needs to be removed from the player.
Adding [player removeTimeObserver:self.timeObserver] works.

How can I use this code to play more sounds?

//Action to play Audio//
-(IBAction)playAudio:(id)sender {
[self.loopPlayer play];
}
//Action to stop Audio//
-(IBAction)stopAudio:(id)sender {
if (self.loopPlayer.isPlaying) {
[self.loopPlayer stop];
self.loopPlayer.currentTime = 0;
self.loopPlayer.numberOfLoops = -1;
[self.loopPlayer prepareToPlay];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//Code that gets audio file "trap synth"//
NSURL* audioFileURL = [[NSBundle mainBundle] URLForResource:#"trapsynth" withExtension:#"wav"];
self.loopPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL error:nil];
}
This is the code i'm using with one button to play the sound when the button is tapped and stop the sound when the button is released. How would I go about adding more sounds to more buttons? I want to have more buttons that play and stop different sounds just like this.
property (nonatomic, strong) AVAudioPlayer *loopPlayer;
This code is also in my ViewController.h file
Ok although the answer provided by Miro is on the write track the code example given has issues.
Should be this in viewDidLoad -
- (void)viewDidLoad {
[super viewDidLoad];
NSURL* audioFileURL1 = [[NSBundle mainBundle] URLForResource:#"trapsynth" withExtension:#"wav"];
self.loopPlayer1 = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL1 error:nil];
NSURL* audioFileURL2 = [[NSBundle mainBundle] URLForResource:#"other_audio_file" withExtension:#"wav"];
self.loopPlayer2 = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL2 error:nil];
}
also stopAudio: method should be this
-(IBAction)stopAudio:(id)sender {
if (self.loopPlayer1.isPlaying && (sender.tag == 1)) {
[self.loopPlayer1 stop];
self.loopPlayer1.currentTime = 0;
self.loopPlayer1.numberOfLoops = -1;
[self.loopPlayer1 prepareToPlay];
}
if (self.loopPlayer2.isPlaying && (sender.tag == 2)) {
[self.loopPlayer2 stop];
self.loopPlayer2.currentTime = 0;
self.loopPlayer2.numberOfLoops = -1;
[self.loopPlayer2 prepareToPlay];
}
}
And finally for playAudio:
-(IBAction)playAudio:(id)sender {
if([sender tag] == 1){
[self.loopPlayer1 play];
}
if([sender tag] == 2){
[self.loopPlayer2 play];
}
}
If you want to play different sounds at the same time you should look into creating separate AVAudioPlayers - if you create a different one for each sound, then you can easily control (play/stop) each of them separately with a specific button.
On the simplest level, you could do something like this, which allows you to use the same button handlers for all your audio. The playAudio checks the tag of the Play button you press (be sure to set the tag value in IB, to 1,2,etc). There really only need be one Stop button.
You could enhance this in many ways, like attempting to reuse the AVAudioPlayer somehow, and loading the audio on the fly instead of all at the beginning. Or storing your audio file info in an array, creating an array of AVAudioPlayers for management, etc. But this is a start.
-(IBAction)playAudio:(id)sender {
// first, stop any already playing audio
[self stopAudio:sender];
if([sender tag] == 1){
[self.loopPlayer1 play];
} else if([sender tag] == 2){
[self.loopPlayer2 play];
}
}
-(IBAction)stopAudio:(id)sender {
if (self.loopPlayer1.isPlaying) {
[self.loopPlayer1 stop];
self.loopPlayer1.currentTime = 0;
self.loopPlayer1.numberOfLoops = -1;
[self.loopPlayer1 prepareToPlay];
} else if (self.loopPlayer2.isPlaying) {
[self.loopPlayer2 stop];
self.loopPlayer2.currentTime = 0;
self.loopPlayer2.numberOfLoops = -1;
[self.loopPlayer2 prepareToPlay];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
NSURL* audioFileURL1 = [[NSBundle mainBundle] URLForResource:#"trapsynth" withExtension:#"wav"];
self.loopPlayer1 = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL error:nil];
NSURL* audioFileURL2 = [[NSBundle mainBundle] URLForResource:#"trapsynth" withExtension:#"wav"];
self.loopPlayer2 = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL error:nil];
}
AND, in the .h file;
property (nonatomic, strong) AVAudioPlayer *loopPlayer1;
property (nonatomic, strong) AVAudioPlayer *loopPlayer2;

Play and Pause function

I have a soundboard app that plays sound and is supposed to play music and if you click the button then it pauses but it doesn't pause it only stops the music.
And once the music finishes the button stays selected.
My code is;
- (IBAction)aint:(id)sender {
UIButton *aint = (UIButton *)sender;
aint.selected = !aint.selected;
if(aint.selected)
{
// Play
NSString *path2 = [[NSBundle mainBundle] pathForResource:#"2" ofType:#"mp3"];
theAudio2 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path2] error:NULL];
theAudio2.delegate = self;
theAudio2.numberOfLoops = 0;
[theAudio2 play];
[[NSUserDefaults standardUserDefaults] setObject:#"-" forKey:#"music"];
}
else
{
// Pause
[theAudio2 pause];
}
}
And I have theAudio2 and AVAudioPlayer declared.
Try this it will work as expected -
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path2 = [[NSBundle mainBundle] pathForResource:#"2" ofType:#"mp3"];
theAudio2 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path2] error:NULL];
theAudio2.delegate = self;
theAudio2.numberOfLoops = 0;
}
- (IBAction)aint:(id)sender
{
UIButton *aint = (UIButton *)sender;
aint.selected = !aint.selected;
if(aint.selected)
{
// Play
[theAudio2 play];
[[NSUserDefaults standardUserDefaults] setObject:#"-" forKey:#"music"];
}
else
{
// Pause
[theAudio2 pause];
}
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
playerBtn.selected = NO;
}

Resources