Delay background music from starting - ios

I am working my through an app and adding features that I would like but running in to a few issues as I go along.
I was hoping someone would have an answer to this question.
I want some background music on my app and I have achieved this by using this code in my AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
resourcePath = [resourcePath stringByAppendingString:#"/smb1-1.mp3"];
NSLog(#"Path to play: %#", resourcePath);
NSError* err;
//Initialize our player pointing to the path to our resource
player = [[AVAudioPlayer alloc] initWithContentsOfURL:
[NSURL fileURLWithPath:resourcePath] error:&err];
if( err ){
//bail!
NSLog(#"Failed with reason: %#", [err localizedDescription]);
}
else{
//set our delegate and begin playback
player.delegate = self;
[player play];
player.numberOfLoops = -1;
player.currentTime = 0;
player.volume = 0.5;
}
// Override point for customization after application launch.
return YES;
}
So all is good. However what I would like is for the audio to start playing 3 seconds after the app has finished launching and not straight away.
Does anyone know how I can set a delay on this or maybe explain a different way. I don't there to be a delay before every time the music repairs, just for the first initial launch.
Thanks in advance.

Hope this help:
Create an BOOL iVar in your appDelegate:
#implementation aaaAppDelegate
{
BOOL firstTime;
}
Add this to your didFinishLaunchingWithOptions method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
firstTime = YES;
return YES;
}
Add this to your applicationDidBecomeActive method in the appDelegate.
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if (firstTime)
{
firstTime = NO;
[self performSelector:#selector(playMusic) withObject:nil afterDelay:3.0];
}
}
Move your music playing code from didFinishLaunchingWithOptions to a separate method called playMusic:
-(void)playMusic
{
NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
resourcePath = [resourcePath stringByAppendingString:#"/smb1-1.mp3"];
NSLog(#"Path to play: %#", resourcePath);
NSError* err;
//Initialize our player pointing to the path to our resource
player = [[AVAudioPlayer alloc] initWithContentsOfURL:
[NSURL fileURLWithPath:resourcePath] error:&err];
if( err ){
//bail!
NSLog(#"Failed with reason: %#", [err localizedDescription]);
}
else{
//set our delegate and begin playback
player.delegate = self;
[player play];
player.numberOfLoops = -1;
player.currentTime = 0;
player.volume = 0.5;
}
}

Related

Can use of AKAudioFile and replaceWith within AKAudioPlayer lead to distortion?

I'm building AudioKit into an experimental psychology add testing interoception (internal body sense), and it's very simple for now, I just have an AudioManager.m class that declares:
#implementation AudioManager {
AKAudioPlayer* player;
bool canPlay;
}
+(AudioManager *)sharedManager {
static dispatch_once_t pred = 0;
static AudioManager *instance = nil;
dispatch_once(&pred, ^{
instance = [[AudioManager alloc] init];
[instance setup];
});
return instance;
}
little player methods like:
-(NSError*)playHTTIntro {
return [self playWavInResources:#"Htt1"];
}
are then invoked from particular view controllers as:
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[AudioManager sharedManager] playHTTGuess];
}
the weird problem I'm having is that as files get called and played, increasing amplitude and distortion accumulates
by the time I've played 10 or 15 files, the voice audio files are almost impossible to understand
the playWavInResourcesabove is implemented as:
-(NSError*)playWavInResources:(NSString*)wavName {
if(player)
[player stop];
NSError* error;
AKAudioFile* audioFile = [[AKAudioFile alloc] initForReading:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:wavName ofType:#"wav"]] error:&error];
if(!error) {
if(player)
[player replaceWithFile:audioFile error:&error];
else
player = [[AKAudioPlayer alloc] initWithFile:audioFile looping:false lazyBuffering:false error:&error completionHandler:nil];
AudioKit.output = player;
// Start audio engine if not already
NSError* error;
[AudioKit startAndReturnError:&error];
if(!error)
[player start];
}
return error;
}
Has anyone seem this problem before?

Issue with AVAudioPlayer gives error when click at Play button

I am programing an audio player using "AVFoundation.h". I've issues with Updating Progress bar, Hence when I click at play button my app gives error. I am attaching both code samples & error report. can anyone sort this out?
-(void)updateProgress {
NSInteger durationMinutes = [self.audioPlayer duration] / 60;
NSInteger durationSeconds = [self.audioPlayer duration] - durationMinutes * 60;
NSInteger currentTimeMinutes = [self.audioPlayer currentTime] / 60;
NSInteger currentTimeSeconds = [self.audioPlayer currentTime] - currentTimeMinutes * 60;
NSString *progressString = [NSString stringWithFormat:#"%d:%02d / %d:%02d", currentTimeMinutes, currentTimeSeconds, durationMinutes, durationSeconds];
self.timeLabel.text = progressString;
self.progressBar.progress = [self.audioPlayer currentTime] / [self.audioPlayer duration];
NSNumber *numCurrentTimeSeconds = [NSNumber numberWithInt:currentTimeSeconds];
NSNumber *numDurationSeconds = [NSNumber numberWithInt:durationSeconds];
NSString *songTitle = [self.selectedFilePath lastPathComponent];
NSString *artistName = #"MyPlayer";
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:#"placeholder"]];
MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
NSDictionary *infoDict = [NSDictionary dictionaryWithObjects:#[songTitle, artistName, albumArt, numDurationSeconds, numCurrentTimeSeconds] forKeys:#[MPMediaItemPropertyTitle, MPMediaItemPropertyAlbumArtist, MPMediaItemPropertyArtwork, MPMediaItemPropertyPlaybackDuration, MPNowPlayingInfoPropertyElapsedPlaybackTime]];
[infoCenter setNowPlayingInfo:infoDict]; }
When pressed Build & Run app goes successfully started in simulator, I've taken 2 images of active Console
1. before clicking at play button.
After clicking play button. when app goes crash.
enter image description here
now please suggest me what I should do at this point? so that my app start working smoothly...
Thanks
Faiz.
After following instructions by Losiowaty answer last day. those yellow issues are removed but still my programing gives same error when I click at play button.
enter image description here
This time I am uploading complete code and highlighting few things due to which I think error is occurring.
Please take a look at my mainwviewcontroller.m class code.
#interface MainViewController ()
#end
#implementation MainViewController
#synthesize audioPlayer;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib
NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
if (error == nil) {
NSLog(#"audio session initialized successfully");
} else {
NSLog(#"error initializing audio session: %#", [error description]);
}
[audioPlayer setDelegate:self];
MPVolumeView *volumeView = [ [MPVolumeView alloc] init] ;
[volumeView setFrame:self.airPlayView.bounds];
[self.airPlayView addSubview:volumeView];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(caughtInterruption:) name:AVAudioSessionInterruptionNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(routeChanged:) name:AVAudioSessionRouteChangeNotification object:nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(BOOL)canBecomeFirstResponder
{
return YES;
}
-(void)dealloc
{
[self resignFirstResponder];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showFilePicker"]) {
UINavigationController *navigationController = (UINavigationController *)segue.destinationViewController;
FileViewController *fileViewController = (FileViewController *)navigationController.topViewController;
fileViewController.delegate = self;
}
}
#pragma mark - file picker delegate methods
-(void)cancel
{
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)didFinishWithFile:(NSString *)filePath
{
NSError *error = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
self.selectedFilePath = filePath;
NSString *relativeFilePath = [documentsDirectory stringByAppendingPathComponent:filePath];
NSURL *fileURL = [NSURL fileURLWithPath:relativeFilePath];
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error];
self.audioPlayer.delegate = self;
if (error == nil) {
NSLog(#"audio player initialized successfully");
self.titleLabel.text = self.selectedFilePath;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateProgress) userInfo:nil repeats:YES];
NSString *songTitle = [filePath lastPathComponent];
NSString *artistName = #"MyPlayer";
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:#"placeholder"]];
MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
NSDictionary *infoDict = [NSDictionary dictionaryWithObjects:#[songTitle, artistName, albumArt] forKeys:#[MPMediaItemPropertyTitle, MPMediaItemPropertyAlbumArtist, MPMediaItemPropertyArtwork]];
[infoCenter setNowPlayingInfo:infoDict];
[self play:nil];
} else {
NSLog(#"error initializing audio player: %#", [error description]);
}
//dismiss the file picker
[self dismissViewControllerAnimated:YES completion:nil];
}
-(IBAction)play:(id)sender
{
if ([self.audioPlayer isPlaying]) {
[self.audioPlayer pause];
[self.playButton setImage:[UIImage imageNamed:#"play"] forState:UIControlStateNormal];
[self.timer invalidate];
[animation stopAnimating];
} else {
[self.audioPlayer play];
[self.playButton setImage:[UIImage imageNamed:#"pause"] forState:UIControlStateNormal];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateProgress) userInfo:nil repeats:YES];
animation.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"animation1.png"],
[UIImage imageNamed:#"animation2.png"],
[UIImage imageNamed:#"animation3.png"],nil];
[animation setAnimationRepeatCount:2000];
animation.animationDuration = 0.5;
[animation startAnimating];
}
self.playbackInterrupted = NO;
}
-(IBAction)skipForward:(id)sender
{
if ([self.audioPlayer isPlaying]) {
NSTimeInterval desiredTime = self.audioPlayer.currentTime + 15.0f;
if (desiredTime < self.audioPlayer.duration) {
self.audioPlayer.currentTime = desiredTime;
}
}
}
-(IBAction)skipBackward:(id)sender
{
if ([self.audioPlayer isPlaying]) {
NSTimeInterval desiredTime = self.audioPlayer.currentTime - 15.0f;
if (desiredTime < 0) {
self.audioPlayer.currentTime = 0.0f;
} else {
self.audioPlayer.currentTime = desiredTime;
}
}
}
#pragma mark - Timer delegate
-(void)updateProgress
{
NSInteger durationMinutes = [self.audioPlayer duration] / 60;
NSInteger durationSeconds = [self.audioPlayer duration] - durationMinutes * 60;
NSInteger currentTimeMinutes = [self.audioPlayer currentTime] / 60;
NSInteger currentTimeSeconds = [self.audioPlayer currentTime] - currentTimeMinutes * 60;
NSString *progressString = [NSString stringWithFormat:#"%ld:%02ld / %ld:%02ld", currentTimeMinutes,currentTimeSeconds, durationMinutes, durationSeconds];
self.timeLabel.text = progressString;
self.progressBar.progress = [self.audioPlayer currentTime] / [self.audioPlayer duration];
NSNumber *numCurrentTimeSeconds = [NSNumber numberWithInteger:currentTimeSeconds];
NSNumber *numDurationSeconds = [NSNumber numberWithInteger:durationSeconds];
NSString *songTitle = [self.selectedFilePath lastPathComponent];
NSString *artistName = #"MyPlayer";
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:#"placeholder"]];
MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
NSDictionary *infoDict = [NSDictionary dictionaryWithObjects:#[songTitle, artistName, albumArt, numDurationSeconds, numCurrentTimeSeconds] forKeys:#[MPMediaItemPropertyTitle, MPMediaItemPropertyAlbumArtist, MPMediaItemPropertyArtwork, MPMediaItemPropertyPlaybackDuration, MPNowPlayingInfoPropertyElapsedPlaybackTime]];
[infoCenter setNowPlayingInfo:infoDict];
}
#pragma mark - AVAudioPlayer delegate methods
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
if (flag) {
[self.playButton setImage:[UIImage imageNamed:#"play"] forState:UIControlStateNormal];
[self.timer invalidate];
[animation stopAnimating];
}
}
#pragma mark - Remote control
-(void)remoteControlReceivedWithEvent:(UIEvent *)event
{
switch (event.subtype) {
case UIEventSubtypeRemoteControlPlay:
case UIEventSubtypeRemoteControlPause:
case UIEventSubtypeRemoteControlTogglePlayPause:
[self play:nil];
break;
case UIEventSubtypeRemoteControlNextTrack:
[self skipForward:nil];
break;
case UIEventSubtypeRemoteControlPreviousTrack:
[self skipBackward:nil];
break;
default:
break;
}
}
#pragma mark - audio interruption
-(void)caughtInterruption:(NSNotification *)notification
{
NSDictionary *userInfo = notification.userInfo;
NSNumber *type =[userInfo objectForKey:AVAudioSessionInterruptionTypeKey];
if ([type integerValue] == AVAudioSessionInterruptionTypeBegan) {
if (self.audioPlayer.playing) {
[self.audioPlayer pause];
[animation stopAnimating];
self.playbackInterrupted = YES;
}
} else {
if (self.audioPlayer.playing == NO && self.playbackInterrupted == YES) {
[self.audioPlayer play];
[animation startAnimating];
self.playbackInterrupted = NO;
}
}
}
#pragma mark - route changed
-(void)routeChanged:(NSNotification *)notification
{
NSDictionary *userInfo = notification.userInfo;
NSNumber *reason =[userInfo objectForKey:AVAudioSessionRouteChangeReasonKey];
switch ([reason integerValue]) {
case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
[self.audioPlayer stop];
[animation stopAnimating];
break;
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
case AVAudioSessionRouteChangeReasonWakeFromSleep:
[self.audioPlayer pause];
[animation stopAnimating];
break;
default:
break;
}
}
#end
above code is error free & pretty clean, everything is clearly mentioned, I am using 4 buttons,
for Play & Pause
for seeking forward
for seeking backward
for entering into document directory for audio file picking
when I am pressing this fourth button it prepares for entering into another view for picking audio file.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showFilePicker"]) {
UINavigationController *navigationController = (UINavigationController *)segue.destinationViewController;
FileViewController *fileViewController = (FileViewController *)navigationController.topViewController;
fileViewController.delegate = self;
}
}
I needs two things to accomplish
the first things which I needs to accomplish is that,
I don't wants to enter into next view, because I am testing my app into simulator where there is no physical audio file I can place or locate in simulator, hence I needs to avoid this thing just for my owns testing purpose.
Hence I am willing to add an audio mp3 file into NSBundle and wants to play this file when I press play button file starts playing & then Pause when pressing again. code for paying & pause is pretty clean working well. but for initializing file path is I think I've to initialize file path in viewDidload method by replacing above view did load by following code.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *audioFilePath = [[NSBundle mainBundle] pathForResource:#"Nameofflie" ofType:#"mp3"];
NSURL *pathAsURL = [[NSURL alloc] initFileURLWithPath:audioFilePath];
NSError *error = nil;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:pathAsURL error:&error];
if (error == nil) {
NSLog(#"audio session initialized successfully");
} else {
NSLog(#"error initializing audio session: %#", [error description]);
}
[audioPlayer setDelegate:self];
MPVolumeView *volumeView = [ [MPVolumeView alloc] init] ;
[volumeView setFrame:self.airPlayView.bounds];
[self.airPlayView addSubview:volumeView];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(caughtInterruption:) name:AVAudioSessionInterruptionNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(routeChanged:) name:AVAudioSessionRouteChangeNotification object:nil];
}
this code also Runs & compiles pretty well but when pressing play button same error occurring. so please suggest me where to place following lines to play MP3 Music file placed from NSBudle .
NSString *audioFilePath = [[NSBundle mainBundle] pathForResource:#"rabne" ofType:#"mp3"];
NSURL *pathAsURL = [[NSURL alloc] initFileURLWithPath:audioFilePath];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:pathAsURL error:&error];
this point is totally different from just above point number 1 which I am willing to accomplish. when after testing successfully, playing that NSBundle audio MP3 file.
I wants to work with my earlier code again, as the end user is not using my simulator, hence for end users I wants to have same option which already I accomplished in above Mainviewcontroller.m class,i.e. user have to press 4th number button for accessing their device's document directory file path. that works pretty well in my code. but the thing here I wants to point to or needs to sort out is that, if someone directly pressing very 1st play button without pressing 4th button for going & selecting an audio file, an alert view should appear with a message that first select a file by pressing fourth button and then click play button. That's it I am willing to have in my code.
Based on the exception in screenshot #2, it looks that you are trying to insert a nil object into an array. The only place where you insert some objects into array is this line :
NSDictionary *infoDict = [NSDictionary dictionaryWithObjects:#[songTitle, artistName, albumArt, numDurationSeconds, numCurrentTimeSeconds] forKeys:#[MPMediaItemPropertyTitle, MPMediaItemPropertyAlbumArtist, MPMediaItemPropertyArtwork, MPMediaItemPropertyPlaybackDuration, MPNowPlayingInfoPropertyElapsedPlaybackTime]];
The second array, the one with the keys, looks ok, as it consisits of only system provided consts. The first one on the other hand has two objects than could be nil : songTitle and albumArt.
The reasons for these to be nil are :
songTitle may be nil if self.selectedFilePath is nil
albumArt - I'm not entirely sure, but it could end up being nil if your image was not found.
Please make sure that these two are not nil and everything should be working fine.
As to your warnings, these two :
NSNumber *numCurrentTimeSeconds = [NSNumber numberWithInt:currentTimeSeconds];
NSNumber *numDurationSeconds = [NSNumber numberWithInt:durationSeconds];
can be fixed by changing to [NSNumber numberWithInteger:] and are caused by the fact that NSInteger is a typedef for long and not int.
The warning on
NSString *progressString = [NSString stringWithFormat:#"%d:%02d / %d:%02d", currentTimeMinutes, currentTimeSeconds, durationMinutes, durationSeconds];
is caused by virtually the same thing. %d expects an int, and NSInteger is a long. Changing %d to %ld will fix it.
It is still my firm belief that these didn't cause the crash, especially based on the thrown exception which is pretty straightforward in stating what has happend.
The provided code confirms my assumptions - the crash happens because self.selectedFilePath is nil in updateProgress method resulting in songTitle also being nil. Why this happens? The only place in provided code where you set self.selectedFilePath is in didFinishWithFile: method, which I assume is a delegate method of FileViewController. If you don't present it and selected something there, that method is not called.
Now, if you want to setup this for testing, the easiest way would be to add this in your viewDidLoad :
NSError *error;
NSString *audioFilePath = [[NSBundle mainBundle] pathForResource:#"rabne" ofType:#"mp3"];
NSURL *pathAsURL = [[NSURL alloc] initFileURLWithPath:audioFilePath];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:pathAsURL error:&error];
if (error == nil) {
self.selectedFilePath = #"test file"; // <<-- IMPORTANT
self.titleLabel.text = self.selectedFilePath;
} else {
NSLog(#"error initializing audio player: %#", [error description]);
}
just above [audioPlayer setDelegate:self];. This should get everything going.
On a side note : I'd also remove this line self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateProgress) userInfo:nil repeats:YES]; from didFinishWithFile: method - you also setup a timer in play: method, and it seems safer to do it once.
As to point #2 - I can give you a hint, that you know if a file is selected when self.selectedFilePath != nil and to take a look at UIAlertViewController class. The rest of work is left for you, as it was not a part of the original problem and has nothing to do with solving the crash. Also, you wouldn't learn anything that way :)

handleWatchKitExtensionRequest play background audio on iphone

I'm experimenting with an WatchKit app that will trigger an audio alert on the iphone. I've got it setup to do the trigger, repeating every 5 seconds. On the iphone, I have the logic that will play the short audio wav file in the background. It is designed to work even if the app is minimized and play the audio. I've tested that stand-alone on the iphone, to work fine.
The step that is not working is the trigger on the watch to tell the parent Iphone app to play the audio. It mostly works, except that the 2 second audio alert is clipped at perhaps .5 seconds. It plays a chirp of the first part and is cut off. Each time I trigger it, it will do this clipped chirp of the entire wav sound.
The reply from the parent app comes across okay, and I'm wondering if as soon as that reply comes back all background processing is cut off entirely.
I have background-mode for Audio enabled in the application.
How can I get the watch to trigger the app to play the entire audio alert in the background?
// WatchApp InterfaceController
- (void)pingIphone {
[WKInterfaceController openParentApplication:#{#"requestString":#"findphone"} reply:^(NSDictionary *replyInfo, NSError *error) {
NSLog(#"\nReply info: %#\nError: %#",replyInfo, error);
}];
}
// Iphone AppDelegate
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void(^)(NSDictionary *replyInfo))reply {
NSString * request = [userInfo objectForKey:#"requestString"];
NSDictionary * replyContent = #{#"state":(application.applicationState == UIApplicationStateBackground ? #"back" : #"not back")};
if ([request isEqualToString:#"findphone"]){
ViewController *vc = [[ViewController alloc] init];
[vc pingIphone];
}
reply(replyContent);
}
// Iphone ViewController
#property (strong, nonatomic) AVQueuePlayer *player;
- (void)viewDidLoad {
[super viewDidLoad];
// get device's default audio level on start
AudioSessionInitialize(NULL, NULL, NULL, NULL);
AudioSessionSetActive(true);
Float32 volume;
UInt32 dataSize = sizeof(Float32);
AudioSessionGetProperty (
kAudioSessionProperty_CurrentHardwareOutputVolume,
&dataSize,
&volume
);
[[AVAudioSession sharedInstance] setActive:YES error:nil];
[[AVAudioSession sharedInstance] addObserver:self forKeyPath:#"outputVolume" options:NSKeyValueObservingOptionNew context:nil];
}
(void)pingIphone {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
if([userDefaults boolForKey:#"vibrate_bool"] == YES){
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
}
for(int i = 0; i < 5; i++){
[_player insertItem:[AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:#"alarm" withExtension:#"wav"]] afterItem:nil];
}
//NSLog(#"volume: %f",[userDefaults floatForKey:#"volume"]);
if (!(_player.rate > 0 && !_player.error)) {
[self startAudio];
}
}
- (void)startAudio {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[[AVAudioSession sharedInstance] setDelegate: self];
NSError *setCategoryError = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: &setCategoryError];
NSString* path = [[NSBundle mainBundle] pathForResource:#"alarm" ofType:#"wav"];
_player = [[AVQueuePlayer alloc] init];
for(int i = 0; i < 5; i++){
[_player insertItem:[AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:path]] afterItem:nil];
}
_player.volume = [userDefaults floatForKey:#"volume"];
[_player play];
if (setCategoryError)
NSLog(#"Error setting category! %#", setCategoryError);
}
It sounds like your app is being killed off by the OS. Check out this answer for some advice: https://stackoverflow.com/a/29848521/3704092
And yes, if you don't use a background task, processing ends once you call reply().

Checking whether the audio has ended playing in iOS

I would like to check whether the audio has ended playing. If so, I need to change the button to a play button instead of a pause button. How do i do it in iOS?
My code looks like this:
-(void)playAudioFile:(BOOL)status
{
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"audio" ofType:#"mp3"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
NSError *error;
if(status)
{
if(!audioPlayer)
{
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:&error];
audioPlayer.currentTime = currentPlayTime;
audioPlayer.numberOfLoops = 0; //Infinite
audioDuration = audioPlayer.duration;
audioPlaySlider.minimumValue = 0;
audioPlaySlider.maximumValue = audioDuration;
audioPlaySlider.value = currentPlayTime;
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(audioTimer:) userInfo:[NSDictionary dictionaryWithObject:audioPlayer forKey:#"playerObject"] repeats:YES];
}
[audioPlayer play];
if([audioPlayer rate] != 1.0 )
{
[audioPlayer play];
}
else
{
[self->audioPlayBtn setBackgroundImage:[UIImage imageNamed:#"playBtn.png"] forState:UIControlStateNormal];
}
NSLog(#"Play duration : %f",audioDuration);
}
else
{
if(audioPlayer && audioPlayer.playing)
{
[audioPlayer pause];
}
}
NSLog(#"Error for file : %#",error);
}
I tried this:
if([audioPlayer rate] != 1.0 )
{
[audioPlayer play];
}
else
{
[self->audioPlayBtn setBackgroundImage:[UIImage imageNamed:#"playBtn.png"] forState:UIControlStateNormal];
}
But not working.. Need some guidance...
Use the AVAudioPlayerDelegate's method
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
Your viewcontroller/class with the audioplayer instance must be the delegate for the audio player. The method will be called when the audio player finishes playback.
http://developer.apple.com/library/ios/#documentation/AVFoundation/Reference/AVAudioPlayerDelegateProtocolReference/Reference/Reference.html

AVAudio player singleton crashes on a isPlaying check

I am using a singleton for AVAudioPlayer, it works great.
I wish to add a check before initializing the player and stop the player if it already playing something.
I added this code :
SoundPlayer *player = [SoundPlayer sharedInstance];
if([player isPlaying]){
[player stop];
}
But it gives me a EXE_BAD_ACCESS on the if([player isPlaying]) line.
2 Questions:
If this is a singleton and I am getting back the same player then why doesn't it stopes by itself?
Why am I getting the error?
HERE IS THE FULL CODE
#import "SoundPlayer.h"
#implementation SoundPlayer
#synthesize helpMode;
static SoundPlayer *sharedInstance = nil;
+ (SoundPlayer *)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[super allocWithZone:NULL] init];
}
return sharedInstance;
}
+(BOOL)playSound:(NSURL*)url{
NSError *error;
SoundPlayer *player = [SoundPlayer sharedInstance];
if([player isPlaying]){
[player stop];
}
player = [[SoundPlayer sharedInstance] initWithContentsOfURL:url error:&error];
[player play];
if (player == nil){
NSLog(#"error %# \n for file %#",[error description],[url path]);
return NO;
}else {
if (![player helpMode]) {
[player play];
}else{
NSLog(#"help mode");
}
}
return YES;
}
-(void)stopSound{
if ([self isPlaying]) {
[self stop];
}
}
#end
Im not sure if this is whats causing the error, but figured I would post my response to the discussion here to make my point clearer since Ill have more room and code formatting.
No you dont have to override all the methods, i was just asking to make sure I understood everything right.
The other piece of what im saying is that just like in stopSound{}, you should be using self not
SoundPlayer *player = [SoundPlayer sharedInstance];
so change your code to this, run it and see if its fixed, post a comment below to let me know the outcome.
-(BOOL)playSound:(NSURL*)url{
NSError *error;
// SoundPlayer *player = [SoundPlayer sharedInstance];
if([self isPlaying]){
[self stop];
}
//not sure if this should be self or super (i think super, try both)
//if you overrided the init then definitely self
//[self initWithContentsOfURL:url error:&error];
[super initWithContentsOfURL:url error:&error];
[self play];
if (self == nil){
NSLog(#"error %# \n for file %#",[error description],[url path]);
return NO;
}else {
if (![self helpMode]) {
[self play];
}else{
NSLog(#"help mode");
}
}
return YES;
}
Really all your doing is creating a pointer to self with you create a player object since it is a singleton. With that said Im not sure why that would make it crash other then the fact that you "should" be using self instead.
+ (SoundPlayer *)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[super allocWithZone:NULL] init];
}
return sharedInstance;
}
see here that AVAudioPlayer only has 2 init methods and neither are just init hence you are not completely initializing your super class. You need to override initWithContentsOfURL:url method and initialize this object and its super with the url parameter. Just make sure you put the initWithContentsOfURL:url in both the .h and .m. Also idk if it matters but try just alloc not allocWithZone.

Resources