How can I play a sound on the iPhone using MonoTouch? - iphone

I am looking for something like
PlaySound (uint frequency)
Does it exist?

From the HowTo at: http://wiki.monotouch.net/HowTo/Sound/Play_a_Sound_or_Alert
var sound = SystemSound.FromFile (new NSUrl ("File.caf"));
sound.PlaySystemSound ();

I don't know about mono, but in the iPhone SDK it isn't easy that easy to create and play sound. Other alternatives are to provide the sound as a file and play that, or create an array representing a sinusoid, and wrap it in a audio wrapper, and pass it to one of many sound APIs.
If mono proves to be just as limited, then search stackoverflow.com for System Sound Services and AVAudioPlayer as starting points.
Here are two ways to play a sound file:
SoundEffect.c (based on Apple's)
#import "SoundEffect.h"
#implementation SoundEffect
+ (id)soundEffectWithContentsOfFile:(NSString *)aPath {
if (aPath) {
return [[[SoundEffect alloc] initWithContentsOfFile:aPath] autorelease];
}
return nil;
}
- (id)initWithContentsOfFile:(NSString *)path {
self = [super init];
if (self != nil) {
NSURL *aFileURL = [NSURL fileURLWithPath:path isDirectory:NO];
if (aFileURL != nil) {
SystemSoundID aSoundID;
OSStatus error = AudioServicesCreateSystemSoundID((CFURLRef)aFileURL, &aSoundID);
if (error == kAudioServicesNoError) { // success
_soundID = aSoundID;
} else {
NSLog(#"Error %d loading sound at path: %#", error, path);
[self release], self = nil;
}
} else {
NSLog(#"NSURL is nil for path: %#", path);
[self release], self = nil;
}
}
return self;
}
-(void)dealloc {
AudioServicesDisposeSystemSoundID(_soundID);
NSLog(#"Releasing in SoundEffect");
[super dealloc];
// self = nil;
}
-(void)play {
AudioServicesPlaySystemSound(_soundID);
}
-(void)playvibe {
AudioServicesPlayAlertSound(_soundID);
}
+(void)justvibe {
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
}
#end
SoundEffect.h:
#import <AudioToolbox/AudioServices.h>
#interface SoundEffect : NSObject {
SystemSoundID _soundID;
}
+ (id)soundEffectWithContentsOfFile:(NSString *)aPath;
- (id)initWithContentsOfFile:(NSString *)path;
- (void)play;
- (void)playvibe;
+ (void)justvibe;
#end
How to use it:
// load the sound
gameOverSound = [[SoundEffect alloc] initWithContentsOfFile:[mainBundle pathForResource:#"buzz" ofType:#"caf"]];
// play the sound
[gameOverSound playvibe];
This is useful for when you want to play sound at the same volume as the iPhone's volume control setting, and you won't need to stop or pause the sound.
Another way is:
+ (AVAudioPlayer *) newSoundWithName: (NSString *) name;
{
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource: name ofType: #"caf"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
AVAudioPlayer *newPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: fileURL
error: nil];
[fileURL release];
// if the sound is large and you need to preload it:
[newPlayer prepareToPlay];
return (newPlayer);
}
and use it (you can see all the extras when you go with AVAudioPlayer):
timePassingSound = [AVAudioPlayer newSoundWithName:#"ClockTicking"];
[timePassingSound play];
// change the volume
[timePassingSound volume:0.5];
// pause to keep it at the same place in the sound
[timePassingSound pause];
// stop to stop completely, and go to beginning
[timePassingSound stop];
// check to see if sound is still playing
[timePassingSound isPlaying];

Related

Unable to play mp3 file in iPhone game

I want to play background music in my game (ideally i'd like it to fade in / out but I want it playing first)
I have looked at various answers on SO and my code seems to look correct but for some reason the file isnt playing, in fact it crashes (with no useful output) on the AVAudioPlayer *player line. If I continue it crashes again on the [player play] line but error is 0.
Updated... again
GameViewController.h
#property (nonatomic, retain) AVAudioPlayer *player;
GameViewController.m
#synthesize player; // the player object
ViewDidLoad:
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource: #"bgtrack" ofType: #"mp3"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error: nil]; // breaks here
[player prepareToPlay];
I am including
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
Updates:
As per comments I tried copying / pasting the code directly from the Apple Dev center. I am using the latest XCode 4 and iOS 6.1 simulator.
When it crashes I get no information bar the fact it hits a breakpoint. The file is included in the bundle
You need to store a strong reference to player (perhaps in an ivar of your view controller or something). If you just put that code into the -viewDidLoad method or something without making a strong reference to it, what'll happen is that the player will be deallocated before it has a chance to start playing. For example:
#interface MyViewController : UIViewController
#end
#implementation MyViewController
{
AVAudioPlayer* player;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"YourMP3FileName" ofType:#"mp3"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
NSError *error;
player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:&error];
player.numberOfLoops = -1; //infinite
player.currentTime = 139;// 2:19;
[player play];
}
#end
That works fine for me, but if player is not an ivar of the view controller I get no sound.
Alternately, I whipped up the following class that will work as a "one-shot" AVAudioPlayer. This obviously presents a problem for cases where numberOfLoops is -1 (infinite) since that means it will never stop playing or go away, but it might make things slightly easier in the case where you want something to play once and then go away without needing to keep a reference to it around.
OneShotAVAudioPlayer.h
#import <AVFoundation/AVFoundation.h>
#interface OneShotAVAudioPlayer : AVAudioPlayer
#end
OneShotAVAudioPlayer.m
#import "OneShotAVAudioPlayer.h"
#import <AVFoundation/AVFoundation.h>
#import <objc/runtime.h>
#interface OneShotAVAudioPlayer () <AVAudioPlayerDelegate>
#property(weak) id<AVAudioPlayerDelegate> p_exogenousDelegate;
#end
#implementation OneShotAVAudioPlayer
static void * const OneShotAVAudioPlayerKey = (void*)&OneShotAVAudioPlayerKey;
- (id)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError
{
if (self = [super initWithContentsOfURL:url error:outError])
{
// Retain ourself
objc_setAssociatedObject(self, OneShotAVAudioPlayerKey, self, OBJC_ASSOCIATION_RETAIN);
[super setDelegate: self];
}
return self;
}
- (id)initWithData:(NSData *)data error:(NSError **)outError;
{
if (self = [super initWithData:data error:outError])
{
// Retain ourself
objc_setAssociatedObject(self, OneShotAVAudioPlayerKey, self, OBJC_ASSOCIATION_RETAIN);
[super setDelegate: self];
}
return self;
}
- (void)setDelegate:(id<AVAudioPlayerDelegate>)delegate
{
self.p_exogenousDelegate = delegate;
}
- (id<AVAudioPlayerDelegate>)delegate
{
return self.p_exogenousDelegate;
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
#try
{
if ([self.p_exogenousDelegate respondsToSelector: _cmd])
[self.p_exogenousDelegate audioPlayerDidFinishPlaying:player successfully:flag];
}
#finally
{
// Make a strong ref so we stay alive through the scope of this function
typeof(self) keepAlive = self;
// Give up the self retain
objc_setAssociatedObject(keepAlive, OneShotAVAudioPlayerKey, nil, OBJC_ASSOCIATION_RETAIN);
// Push in the "real" (outside) delegate, cause our job is done here.
[super setDelegate: self.p_exogenousDelegate];
}
}
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error
{
#try
{
if ([self.p_exogenousDelegate respondsToSelector: _cmd])
[self.p_exogenousDelegate audioPlayerDecodeErrorDidOccur:player error:error];
}
#finally
{
// Make a strong ref so we stay alive through the scope of this function
typeof(self) keepAlive = self;
// Give up the self retain
objc_setAssociatedObject(keepAlive, OneShotAVAudioPlayerKey, nil, OBJC_ASSOCIATION_RETAIN);
// Push in the "real" (outside) delegate, cause our job is done here.
[super setDelegate: self.p_exogenousDelegate];
}
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
BOOL retVal = [super respondsToSelector: aSelector];
if (!retVal)
{
struct objc_method_description method = protocol_getMethodDescription(#protocol(AVAudioPlayerDelegate), aSelector, YES, YES);
if (method.name)
{
retVal = [self.p_exogenousDelegate respondsToSelector: aSelector];
}
}
return retVal;
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
id retVal = [super forwardingTargetForSelector:aSelector];
if (!retVal)
{
struct objc_method_description method = protocol_getMethodDescription(#protocol(AVAudioPlayerDelegate), aSelector, YES, YES);
if (method.name && [self.p_exogenousDelegate respondsToSelector: aSelector])
{
retVal = self.p_exogenousDelegate;
}
}
return retVal;
}
- (void)setNumberOfLoops:(NSInteger)numberOfLoops
{
if (numberOfLoops < 0)
{
NSLog(#"Warning! You have set an infinite loop count for an instance of %# (%p). This means the instance will effectively be leaked.", NSStringFromClass([self class]), self);
}
[super setNumberOfLoops: numberOfLoops];
}
#end
Posted here as a gist.
Promote the player ivar to a strong property and log the [error description] if player is nil.
If it is crashing something definitely is wrong. Did you add an exception breakpoint?

How to prevent AVAudioPlayer from playing before a view is displayed

Hi there I have an a UIView named HomeViewController that use UINavigationController. On a click of a button, I push another view using the following code:
gameView = [[GameViewControler alloc] init];
[[self navigationController] pushViewController:gameView animated:YES];
[gameView release];
In GameViewController's viewDidLoad, I use the following code to play two audio file one after another:
//handle sound stuff
if([[GameStore defaultStore] currentIndex] == 0)
{
if(![audioPlayer isPlaying])
{
NSString *findPath = [[NSBundle mainBundle] pathForResource:#"findtheword" ofType:#"aiff"];
if(findPath)
{
NSURL *findURL = [NSURL fileURLWithPath:findPath];
NSError *findError;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:findURL error:&findError];
if(audioPlayer == nil)
{
NSLog([findError description]);
}
else{
[audioPlayer play];
}
}
}
}
while ([audioPlayer isPlaying]) {
//do nothing and wait so the sound is not overlap
}
if(![audioPlayer isPlaying] || ([audioPlayer isPlaying] && override)){
Word *currentWord = [[GameStore defaultStore] currentWord];
NSString *soundPath = [[NSBundle mainBundle] pathForResource:[currentWord fileName]
ofType:[currentWord fileType]];
// If this file is actually in the bundle...
if (soundPath) {
// Create a file URL with this path
NSURL *soundURL = [NSURL fileURLWithPath:soundPath];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:&error];
if (audioPlayer == nil)
NSLog([error description]);
else
[audioPlayer play];
}
[FlurryAnalytics logEvent:#"GameViewController - playSound"];
}
else{
[FlurryAnalytics logEvent:#"GameViewController - playSound is still playing"];
}
I can't figure out why the first audio file started to play while the screen is still on HomeViewController even though it is being called from GameViewController's viewDidLoad.
If I remove the following code:
while ([audioPlayer isPlaying]) {
//do nothing and wait so the sound is not overlap
}
The first audio file started to play when GameViewController is loaded but the 2nd audio file overlap.
Thank you very much.
Does the audio file start to play after you click some button or table view cell in HomeViewController? As in, it starts to play while the GameViewController's view is being animated from right to left? If so, then that's because -viewDidLoad is called before the view is actually presented. What you need to do is instead place your view logic in -viewDidAppear:. That will only be called when the view is finished animating and is completely visible.
You could move the code from viewDidLoad to viewDidAppear:.

Voice recorder view. It stops recording when I go to another view, help?

Ok right RecordViewController, allows you to use the voice function and record. It functions properly, but When i press the 'back' button and go to a different view the recording stops. How do I make it so it keeps recording, so the user can record their voice even whilst on different views?
#implementation RecordViewController
#synthesize actSpinner, btnStart, btnPlay;
-(void)countUp {
mainInt += 1;
seconds.text = [NSString stringWithFormat:#"%02d", mainInt];
}
-(IBAction)goBack:(id)sender; {
[self dismissModalViewControllerAnimated:YES];
}
/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
*/
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
//Start the toggle in true mode.
toggle = YES;
btnPlay.hidden = YES;
//Instanciate an instance of the AVAudioSession object.
AVAudioSession * audioSession = [AVAudioSession sharedInstance];
//Setup the audioSession for playback and record.
//We could just use record and then switch it to playback leter, but
//since we are going to do both lets set it up once.
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error: &error];
//Activate the session
[audioSession setActive:YES error: &error];
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (IBAction) start_button_pressed{
if(toggle)
{
toggle = NO;
[actSpinner startAnimating];
[btnStart setImage:[UIImage imageNamed:#"stoprecordingbutton.png"] forState:UIControlStateNormal];
mainInt = 0;
theTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countUp) userInfo:nil repeats:YES];
btnPlay.enabled = toggle;
btnPlay.hidden = !toggle;
//Begin the recording session.
//Error handling removed. Please add to your own code.
//Setup the dictionary object with all the recording settings that this
//Recording sessoin will use
//Its not clear to me which of these are required and which are the bare minimum.
//This is a good resource: http://www.totodotnet.net/tag/avaudiorecorder/
NSMutableDictionary* recordSetting = [[NSMutableDictionary alloc] init];
[recordSetting setValue :[NSNumber numberWithInt:kAudioFormatAppleIMA4] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt: 2] forKey:AVNumberOfChannelsKey];
//Now that we have our settings we are going to instanciate an instance of our recorder instance.
//Generate a temp file for use by the recording.
//This sample was one I found online and seems to be a good choice for making a tmp file that
//will not overwrite an existing one.
//I know this is a mess of collapsed things into 1 call. I can break it out if need be.
recordedTmpFile = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent: [NSString stringWithFormat: #"%.0f.%#", [NSDate timeIntervalSinceReferenceDate] * 1000.0, #"caf"]]];
NSLog(#"Using File called: %#",recordedTmpFile);
//Setup the recorder to use this file and record to it.
recorder = [[ AVAudioRecorder alloc] initWithURL:recordedTmpFile settings:recordSetting error:&error];
//Use the recorder to start the recording.
//Im not sure why we set the delegate to self yet.
//Found this in antother example, but Im fuzzy on this still.
[recorder setDelegate:self];
//We call this to start the recording process and initialize
//the subsstems so that when we actually say "record" it starts right away.
[recorder prepareToRecord];
//Start the actual Recording
[recorder record];
//There is an optional method for doing the recording for a limited time see
//[recorder recordForDuration:(NSTimeInterval) 10]
}
else
{
toggle = YES;
[actSpinner stopAnimating];
[btnStart setImage:[UIImage imageNamed:#"recordbutton.png"] forState:UIControlStateNormal];
btnPlay.enabled = toggle;
btnPlay.hidden = !toggle;
[theTimer invalidate];
NSLog(#"Using File called: %#",recordedTmpFile);
//Stop the recorder.
[recorder stop];
}
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
-(IBAction) play_button_pressed{
//The play button was pressed...
//Setup the AVAudioPlayer to play the file that we just recorded.
AVAudioPlayer * avPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:recordedTmpFile error:&error];
[avPlayer prepareToPlay];
[avPlayer play];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
//Clean up the temp file.
NSFileManager * fm = [NSFileManager defaultManager];
[fm removeItemAtPath:[recordedTmpFile path] error:&error];
//Call the dealloc on the remaining objects.
[recorder dealloc];
recorder = nil;
recordedTmpFile = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
Thanks
Keep a reference to it somewhere in the app (like in the delegate) so it won't get de-allocated.
I would set up the view controller using a singleton approach. I like it better then using the appDelegate and apple recomends it over editting it.
Have a look here for some examples
I use this approach when I want ininterrupt music playing on my apps and works like a charm... be aware of memory usage though as those instances are never released as your app is running...

Touchesbegan always fire when using touchesmoved?

I'm new to iPhone programing.
I'm trying to do a simple window with a cat doing two sounds. When you click at the cat icon it should do "miaau", and when wou drag over window (stroke) it should make "mrrrr".
It works, but always when I try to make cat mrrrrr function TouchesBegan fires and cat also do "miaaau".
What to do to make the interface recognize I want only stroke cat, not to touch it for doing the first option, "miaau"?
I suggest to add a NSTimer to touchesBegan method with small time interval (say 0.1 sec):
BOOL tap_event = NO; //ivar declared in header
-(void) touchesBegan:... {
tap_event = YES;
[NSTimer scheduledTimerWithTimeInterval: 0.1 target: self selector: #selector(checkTap:) userInfo: nil repeats: NO];
}
-(void) checkTap:(NSTimer*) t {
if( tap_event ) //miauu here
tap_event = NO;
}
-(void) touchesMoved:... {
tap_event = NO;
//mrrrr here
}
Or as an option check docs for UIGestureRecognizers
This may help you
When does a touchesBegan become a touchesMoved?
touchesBegin & touchesMove Xcode Obj C Question
Max' solution is correct. It will definitely work. I am having an alternative, see this approach.
Make your player object in an .h file then allocate it in viewDidLoad and release in dealloc.
-(void) touchesBegan:... {
//Play the miauu sound.
}
-(void) touchesMoved:... {
[self.yourPlayer stop];
//Play mrrrr.
}
Edit
.h file,
AVAudioPlayer *avPlayer1;
AVAudioPlayer *avPlayer2;
.m file
-(void) touchesBegan:... {
NSString *path = [[NSBundle mainBundle] pathForResource:#"cat" ofType:#"wav"];
avPlayer1 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[avPlayer1 play];
}
-(void) touchesMoved:... {
[avPlayer1 stop];
NSString *path = [[NSBundle mainBundle] pathForResource:#"cat" ofType:#"wav"];
avPlayer2 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[avPlayer2 play];
}
-(void)dealloc
{
[avPlayer1 release];
[avPlayer2 release];
[super dealloc];
}

AVaudioPlayer regarding song overlapping

I have using AVAudioPlayer in my app but the problem is that when i play the song from list it will played while before stopping the song i am again goes to song list then select another song then both song will start playing simultaneously so please tell me the code which will terminate my first song.
I use flag variable for initializing the song file---
fileURL = [[NSURL alloc] initFileURLWithPath: [[NSBundle mainBundle] pathForResource:#" Om Namo" ofType:#"aac"]];
self._player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
if (self._player)
{
_fileName.text = [NSString stringWithFormat: #"%# (%d ch.)", [[self._player.url relativePath] lastPathComponent], self._player.numberOfChannels, nil];
[self updateViewForPlayerInfo];
[self updateViewForPlayerState];
}
[fileURL release];
break;
Play and push function---
- (void)startPlayback
{
if ([self._player play])
{
//[self stop];
[self updateViewForPlayerState];
self._player.delegate = self;
}
else
NSLog(#"Could not play %#\n", self._player.url);
}
- (void)pausePlayback
{
[self._player pause];
[self updateViewForPlayerState];
}
I also make stop function---
-(void)stop
{
[_player release];
self._player=nil;
}
Please help me .
Is correct my stop function.
and where i put our stop function or any other solution to fix this problem...
I can think of 3 things :
1) You can make code look like code by putting 4 spaces infront of each line. The reason you've gt no answers so far is probably because your question is almost unreadable ;)
2) In your stop method you might want to try this :
- (void) stop {
[_player stop];
self._player = nil;
}
3) Apple highly discourage using an underscore in front of member names. I've never run into problems but I'd rename _player to player.