How Can I repeat a System Sound - ios

I want to repeat the sounds that are send to this method?
- (void) playSound:(CFURLRef)soundFileURLRef {
SystemSoundID soundID;
OSStatus errorCode = AudioServicesCreateSystemSoundID(soundFileURLRef, &soundID);
if (errorCode != 0) {
}else{
AudioServicesPlaySystemSound(soundID);
}
}
I've seen answers on how this is made using numberOfRepeats. But I can't fit it in my code.

Try using AVAudioPlayer:
- (void) playSound:(CFURLRef)soundFileURLRef {
NSError* sndErr;
AVAudioPlayer *player = [[ AVAudioPlayer alloc ] initWithContentsOfURL:(NSURL *)soundFileURLRef error:(&sndErr) ];
// at this point player has a retain count of 1. you should save it
// somewhere like a class member variable so you can stop the playing
// and release the object later. Under ARC, you must have a strong reference
// to the object so that it doesn't get released when this function goes out
// of scope.
if (sndErr != nil) {
player.numberOfLoops = -1; // infinite number of loops. call stop when you want to stop.
[player play];
}
}

Related

Should AVAudioPlayer be used to play sound in background or main thread?

Should AVAudioPlayer be used to play sound in background or main thread? Is it a better practice to use background thread to play sound to not block the UI?
play your sound on a separate thread and I suspect your blocking issues will go away.
The easiest way to make this happen is to do:
- (void)playingAudioOnSeparateThread: (NSString *) path
{
if(_audioPlayer)
{
_audioPlayer = nil; // doing this while a sound is playing might crash...
}
NSLog(#"start playing audio at path %#", path);
NSError *error = nil;
NSData *audioData = [NSData dataWithContentsOfFile:path];
_audioPlayer = [[AVAudioPlayer alloc] initWithData:audioData error:&error];
if (error == nil)
{
[_audioPlayer play];
}
}
- (void)playAudio:(NSString *)path
{
[NSThread detachNewThreadSelector: #selector(playingAudioOnSeparateThread:) toTarget: self withObject: path];
}

Play/Pause/Stop multiple audio file which are stored locally

I have 20 audio tracks, which are stored locally in my project.
I want to play all that files sequentially . For that I have used AVQueuePlayer. Please check below code for reference.
Declaration :-
#interface ViewController ()
{
AVAudioPlayer *player;
NSMutableArray *arrPlayData;
AVPlayerItem *item;
}
#property (nonatomic, retain) AVQueuePlayer *queuePlayer;
Button Clicks as below,
-(IBAction)click_Play:(id)sender {
for (int j = 0; j < arrPlayData.count; j++) {
path =[[NSBundle mainBundle] pathForResource:[[arrPlayData objectAtIndex:j] valueForKey:#"AudioName"] ofType:#"wav"];
item = [[AVPlayerItem alloc] initWithURL:[NSURL fileURLWithPath:path]];
if (_queuePlayer == nil) {
_queuePlayer = [[AVQueuePlayer alloc] initWithPlayerItem:item];
} else{
[_queuePlayer insertItem:item afterItem:nil];
}
}
[_queuePlayer setVolume:1.0];
[_queuePlayer play];
}
Then on pause button I have written below code,
-(IBAction)click_Pause:(id)sender {
[_queuePlayer pause];
}
And on Stop button I have written below code,
-(IBAction)click_Stop:(id)sender {
[_queuePlayer removeAllItems];
}
Everything is working well, the problem I am facing is with PAUSE button. When I click on pause button it stops playing. Then again when I click on play button it plays from the same place where I pause it. Till here everything looks good. But on playing the audio queue after pausing, it plays remaining part of audio queue and then again it plays one more time the whole audio queue. Don't know how to resolve it. Why it is playing whole audio queue again?
Anyone have idea?
Thanks in advance!
whole audio queue is playing again because you are inserting item from arrPlayData every time click_Play is clicked.
So, in click_Play check count of items and handle accordingly like insert item only if count of items _queuePlayer is Zero.
#interface ViewController ()
{
BOOL FromPause;
}
-(IBAction)click_Stop:(id)sender {
[_queuePlayer removeAllItems];
}
-(IBAction)click_Pause:(id)sender {
[_queuePlayer pause];
FromPause=YES;
}
-(IBAction)click_Play:(id)sender {
for (int j = 0; j < arrPlayData.count; j++) {
path =[[NSBundle mainBundle] pathForResource:[[arrPlayData objectAtIndex:j] valueForKey:#"AudioName"] ofType:#"wav"];
item = [[AVPlayerItem alloc] initWithURL:[NSURL fileURLWithPath:path]];
if (_queuePlayer == nil) {
_queuePlayer = [[AVQueuePlayer alloc] initWithPlayerItem:item];
} else{
if(FromPause){
//don't add again
FromPause=NO;
}
else{
[_queuePlayer insertItem:item afterItem:nil];
}
}
}
[_queuePlayer setVolume:1.0];
[_queuePlayer play];
}
Because of you are initialize AVPlayerItem again in click_Play.
Please take one bool value and insert arrPlayData if AVPlayerItem not allocated.

Make my background music player stop? Objective C

I have several views, one of them is an "options" view, in this view there will be a button that will allow the user to click a button to "stop" the background music, or click it to turn it back on. Im pretty sure I have the general idea of how to do this, the problem is, my code isnt working and I am not sure why.
this is the code from the implementation file for the "options" class:
- (IBAction)musicIO:(id)sender {
MenuViewController *myMusicPlayer = [[MenuViewController alloc] init];
[myMusicPlayer backgroundMusicStart];
}
it calls the method in my menu code(a different class)
This is my menu code:
-(IBAction) backgroundMusicStart{
if( i != 1){
NSURL * URL = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/backgroundMusic.wav", [[NSBundle mainBundle] resourcePath]]];
NSError *error;
backgroundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:URL error:&error];
backgroundPlayer.numberOfLoops = -1;
[backgroundPlayer play];
i = 1;
NSLog(#"In start music");
}
else if ( i == 1 ) {
[backgroundPlayer stop];
i = 0;
NSLog(#"In stop music");
}
}
"i" is a globally declared variable.
The problem with the code:
it runs, NSLog outputs that it gets into both if statements fine, the problem is when it is in the [backgroundPlayer stop] bracket, it doesnt stop the music, and I cant figure out why.
You're initialising backgroundPlayer every time this method is invoked and in only one of the cases. What you should be doing is make backgroundPlayer an instance variable and act on the shared instance of it... Example follows:
// static AVPlayer *backgroundPlayer;
- (IBAction) backgroundMusicStart {
if (backgroundPlayer == nil) {
backgroundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:URL error:&error];
backgroundPlayer.numberOfLoops = -1;
}
if (i == 1) { [backgroundPlayer stop]; }
else if (i == 0) { backgroundPlayer play]; }
}

AVAudioPlayer audioPlayerDidFinishPlaying called when the file is played second time

I am using AVAudioPlayer class to play audio but the problem is that when I play the audio I do not receive the callback in audioPlayerDidFinishPlaying when the file is finish playing and the file starts again even when the number of loops is set to 1
Here is the code.
.h
#interface AudioController : NSObject <AVAudioPlayerDelegate>
-(void) playBigGameTheme{
-(void) playMusic:(NSString*) fileName isLooping: (BOOL) isLooping;
#end
.m
#implementation AudioController{
AVAudioPlayer *audioPlayer;
}
-(void) playMusic:(NSString*) fileName isLooping: (BOOL) isLooping{
int loop = 1;
if(isLooping){
loop = -1;
}
[audioPlayer stop];
NSURL* soundUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:fileName ofType:#"mp3"]];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundUrl error:nil];
audioPlayer.numberOfLoops = loop;
[audioPlayer setDelegate:self];
NSLog(#"===Number of loops : %d", audioPlayer.numberOfLoops);
[audioPlayer play];
}
- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
NSLog(#"HERE");
}
-(void) playBigGameTheme{
[self playMusic:BGAME_MAIN_MUSIC_FILE_NAME isLooping:NO];
}
#end
when I call playBigGameTheme the audio starts playing correctly. but it restarts when the sound is finished and when the audio finishes the second time then only the audioPlayerDidFinishPlaying method is called.
I am stuck and do not know whats going wrong..
even when the number of loops is set to 1
That's exactly why. The name and the behavior of this property is a bit counter-intuitive, but if you had read the documentation, it would have been clear that it sets the number of repetitions, and not the number of playbacks. Quote from the docs:
The value of 0, which is the default, means to play the sound once.
So set it to 0 (or don't set it, zero is the default value) and it will play the audio file only once:
audioPlayer.numberOfLoops = 0;

Playing midi files with MusicPlayer & Music Sequence

I've successfully gotten iOS to play a .mid (midi) file with a soundfont sample using the following code:
-(void) playMusic:(NSString*) name
{
NSString *presetURLPath = [[NSBundle mainBundle] pathForResource:#"GortsMiniPianoJ1" ofType:#"SF2"];
NSURL * presetURL = [NSURL fileURLWithPath:presetURLPath];
[self loadFromDLSOrSoundFont: (NSURL *)presetURL withPatch: (int)3];
NSString *midiFilePath = [[NSBundle mainBundle] pathForResource:name ofType:#"mid"];
NSURL * midiFileURL = [NSURL fileURLWithPath:midiFilePath];
NewMusicPlayer(&musicPlayer);
if (NewMusicSequence(&musicSequence) != noErr)
{
[NSException raise:#"play" format:#"Can't create MusicSequence"];
}
if(MusicSequenceFileLoad(musicSequence, (CFURLRef)midiFileURL, 0, 0 != noErr))
{
[NSException raise:#"play" format:#"Can't load MusicSequence"];
}
MusicPlayerSetSequence(musicPlayer, musicSequence);
MusicSequenceSetAUGraph(musicSequence, _processingGraph);
MusicPlayerPreroll(musicPlayer);
MusicPlayerStart(musicPlayer);
}
However, the problem comes when I then try to play a second file when the first is still playing.
I've tried many variations. Firstly, the above code will play both tracks simultaneously. Or, I've tried:
DisposeMusicPlayer(musicPlayer);
DisposeMusicSequence(musicSequence);
Before the NewMusicPlayer(&musicPlayer), but this produces a weird version of the tune with only sporadic notes being played.
I'd love to simply call this method, and the next track to be played.
Ok, I found the answer on how to properly dispose of a MusicPlayer and MusicSequence.
-(void) stop
{
OSStatus result = noErr;
result = MusicPlayerStop(musicPlayer);
UInt32 trackCount;
MusicSequenceGetTrackCount(musicSequence, &trackCount);
MusicTrack track;
for(int i=0;i<trackCount;i++)
{
MusicSequenceGetIndTrack (musicSequence, i, &track);
result = MusicSequenceDisposeTrack(musicSequence, track);
}
result = DisposeMusicPlayer(musicPlayer);
result = DisposeMusicSequence(musicSequence);
result = DisposeAUGraph(_processingGraph);
}

Resources