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?
Related
In iOS 7, after my audio interruption listener gets called, any attempt to restore the audio session seems to fail silently.
My interruption listener calls
NSError *activationError = nil;
[[AVAudioSession sharedInstance] setActive:YES error:&activationError];
But the app's audio session is dead as soon as the alarm clock rings. The listener gets called with appropriate begin and end states.
It worked just fine on iOS 6.
I have heard that this is a bug in iOS 7 and that there is a workaround, but can't find it.
Does anyone know a link to a workaround or Technical Note from Apple?
EDIT: I found that I HAVE to use AVAudioSessionCategoryPlayback instead of kAudioSessionCategory_AmbientSound. Now it works. But it is not the category I wanted.
Based on Apple's Audio session programming guide, you should listen and react to changes in your interruption handler. This means that your code can/should handle the end of the interruptions too, based on the received parameter interruptionState.
Check out "Audio Interruption Handling Techniques" on this link, I think it will help you a lot: https://developer.apple.com/library/ios/documentation/Audio/Conceptual/AudioSessionProgrammingGuide/HandlingAudioInterruptions/HandlingAudioInterruptions.html
Good luck,
Z.
You can see my code below. But First,
You should select this bit target | Background Modes | Audio, Airplay and Picture in Picture from Capabilities.
// ViewController.m
// AVAudioPlayer
#import "ViewController.h"
#import AVFoundation;
#import MediaPlayer;
#interface ViewController ()
#property (strong, nonatomic) AVAudioPlayer *audioPlayer;
#property (weak, nonatomic) IBOutlet UISlider *volumeSlider;
#property (weak, nonatomic) IBOutlet UISlider *rateSlider;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self setupAudio];
}
- (void) setupAudio {
NSError *error;
[[AVAudioSession sharedInstance] setActive:YES error:&error];
if (error != nil) {
NSAssert(error == nil, #"");
}
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
if (error != nil) {
NSAssert(error == nil, #"");
}
NSURL *soundURL = [[NSBundle mainBundle] URLForResource:#"SoManyTimes" withExtension:#"mp3"];
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:&error];
if (error != nil) {
NSAssert(error == nil, #"");
}
//[self.audioPlayer setVolume:0.8];
self.audioPlayer.enableRate = YES;
[self.audioPlayer prepareToPlay];
[self.audioPlayer setVolume:self.volumeSlider.value];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(audioInterrupt:) name:AVAudioSessionInterruptionNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(audioRouteChanged:) name:AVAudioSessionRouteChangeNotification object:nil];
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.playCommand addTarget:self action:#selector(playButtonPressed:)];
[commandCenter.stopCommand addTarget:self action:#selector(stopButtonPressed:)];
[commandCenter.pauseCommand addTarget:self action:#selector(stopButtonPressed:)];
}
- (void) audioRouteChanged:(NSNotification*)notification {
NSNumber *reason = (NSNumber*)[notification.userInfo valueForKey:AVAudioSessionRouteChangeReasonKey];
switch ([reason integerValue]) {
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
[self stopButtonPressed:nil];
break;
default:
break;
}
}
- (void) audioInterrupt:(NSNotification*)notification {
NSNumber *interruptionType = (NSNumber*)[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey];
switch ([interruptionType integerValue]) {
case AVAudioSessionInterruptionTypeBegan:
[self stopButtonPressed:nil];
break;
case AVAudioSessionInterruptionTypeEnded:
{
if ([(NSNumber*)[notification.userInfo valueForKey:AVAudioSessionInterruptionOptionKey] intValue] == AVAudioSessionInterruptionOptionShouldResume) {
[self playButtonPressed:nil];
}
break;
}
default:
break;
}
}
- (IBAction)playButtonPressed:(id)sender {
BOOL played = [self.audioPlayer play];
if (!played) {
NSLog(#"Error");
}
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:#"CoverArt"]];
NSDictionary *songInfo = #{
MPMediaItemPropertyTitle:#"So Many Times",
MPMediaItemPropertyArtist:#"The Jellybricks",
MPMediaItemPropertyAlbumTitle:#"Soap Opera",
MPMediaItemPropertyArtwork:albumArt,
MPMediaItemPropertyPlaybackDuration:#(self.audioPlayer.duration),
MPNowPlayingInfoPropertyElapsedPlaybackTime:#(self.audioPlayer.currentTime),
};
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
}
- (IBAction)stopButtonPressed:(id)sender {
[self.audioPlayer stop];
}
- (IBAction)volumeSliderChanged:(UISlider*)sender {
[self.audioPlayer setVolume:sender.value];
}
- (IBAction)rateSliderChanged:(UISlider*)sender {
[self.audioPlayer setRate:sender.value];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#end
I'm also using the AVAudioSessionCategoryPlayback category but interruptions do not automatically resume playback. Check out Detecting active AVAudioSessions on iOS device. The chosen answer has detailed instructions on how to handle session interrupts.
I am developing an iOS app that has a button with a microphone on it (along with other features). When the user presses the microphone, it gets highlighted and the app should now start recording sound from the deviceĀ“s microphone and send to a server (a server dedicated to the app, developed by people that I know, so I can affect its design).
I am looking for the simplest yet sturdiest approach to do this, i.e. I have no need to develop a complicated streaming solution or VoIP functionality, unless it is as simple to do as anything else.
The main problem is that we have no idea for how long the user will be recording sound, but we want to make sure that sounds are sent to the server continuously, we do not wish to wait until the user has finished recording. It is okay if the data arrives to the server in chunks however we do not wish to miss any information that the user may be recording, so one chunk must continue where the previous one ended and so on.
Our first thought was to create "chunks" of sound clips of for example 10 seconds and send them continuously to the server. Is there any streaming solution that is better/simpler that I am missing out on?
My question is, what would be the most simple but still reliable approach on solving this task on iOS?
Is there a way to extract chunks of sound from a running recording by AVAudioRecorder, without actually stopping the recording?
look at this
in this tutorial, the sound recorded will be saved at soundFileURL, then you will just have to create an nsdata with that content, and then send it to your server.
hope this helped.
EDIT :
I just created a version that contain 3 buttons, REC, SEND and Stop :
REC : will start recording into a file.
SEND : will save what was recorded on that file in a NSData, and send it to a server, then will restart recording.
and STOP : will stop recording.
here is the code :
in your .h file :
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface ViewController : UIViewController <AVAudioRecorderDelegate>
#property (nonatomic, retain) AVAudioRecorder *audioRecorder;
#property (nonatomic, retain) IBOutlet UIButton *recordButton;
#property (nonatomic, retain) IBOutlet UIButton *stopButton;
#property (nonatomic, retain) IBOutlet UIButton *sendButton;
#property BOOL stoped;
- (IBAction)startRec:(id)sender;
- (IBAction)sendToServer:(id)sender;
- (IBAction)stop:(id)sender;
#end
and in the .m file :
#import "ViewController.h"
#implementation ViewController
#synthesize audioRecorder;
#synthesize recordButton,sendButton,stopButton;
#synthesize stoped;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
sendButton.enabled = NO;
stopButton.enabled = NO;
stoped = YES;
NSArray *dirPaths;
NSString *docsDir;
dirPaths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
NSString *soundFilePath = [docsDir
stringByAppendingPathComponent:#"tempsound.caf"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
NSDictionary *recordSettings = [NSDictionary
dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:AVAudioQualityMin],
AVEncoderAudioQualityKey,
[NSNumber numberWithInt:16],
AVEncoderBitRateKey,
[NSNumber numberWithInt: 2],
AVNumberOfChannelsKey,
[NSNumber numberWithFloat:44100.0],
AVSampleRateKey,
nil];
NSError *error = nil;
audioRecorder = [[AVAudioRecorder alloc]
initWithURL:soundFileURL
settings:recordSettings
error:&error];
audioRecorder.delegate = self;
if (error)
{
NSLog(#"error: %#", [error localizedDescription]);
} else {
[audioRecorder prepareToRecord];
}
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (BOOL) sendAudioToServer :(NSData *)data {
NSData *d = [NSData dataWithData:data];
//now you'll just have to send that NSData to your server
return YES;
}
-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{
NSLog(#"stoped");
if (!stoped) {
NSData *data = [NSData dataWithContentsOfURL:recorder.url];
[self sendAudioToServer:data];
[recorder record];
NSLog(#"stoped sent and restarted");
}
}
- (IBAction)startRec:(id)sender {
if (!audioRecorder.recording)
{
sendButton.enabled = YES;
stopButton.enabled = YES;
[audioRecorder record];
}
}
- (IBAction)sendToServer:(id)sender {
stoped = NO;
[audioRecorder stop];
}
- (IBAction)stop:(id)sender {
stopButton.enabled = NO;
sendButton.enabled = NO;
recordButton.enabled = YES;
stoped = YES;
if (audioRecorder.recording)
{
[audioRecorder stop];
}
}
#end
Good Luck.
It might actually be easier to have fixed-size chunks, instead of fixed-time. Then you can have two buffers, one that you currently fill with sample data. When the active buffer is full, then switch to fill the other buffer, while sending a signal to a sender-thread that takes the first buffer and sends it to the server.
You can of course use fixed-time instead, but then you need to make sure that the buffer is large enough to keep all samples, or use a dynamic buffer that can increase in size when needed. The double-buffering and sending thread can still be the same though.
I need to create a new NSNumber or integer property called primaryKey to be included with all AVAudioPlayer objects that I create so that I can read the value of that property within the audioPlayerDidFinishPlaying callback and know exactly which database record was played.
The reason I need to do this is: I can't use the player URL property to determine which database record it was since the same sound file can be used multiple times within the playlist.
How can I add a new property to an existing iOS class like this?
Example:
AVAudioPlayer *newAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
self.theAudio = newAudio; // automatically retain audio and dealloc old file if new file is loaded
if (theAudio != nil) [audioPlayers addObject:theAudio];
[newAudio release];
[theAudio setDelegate: theDelegate];
[theAudio setNumberOfLoops: 0];
[theAudio setVolume: callVolume];
// This is the new property that I want to add
[theAudio setPrimaryKey: thePrimaryKey];
[theAudio play];
Then I'd retrieve it in the callback like this:
- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
NSNumber *finishedSound = [NSNumber numberWithInt:[player primaryKey]];
// Do something with this information now...
}
You can create a subclass and add your property just like you would when subclassing anything.
Interface
#interface MyAudioPlayer : AVAudioPlayer
#property (nonatomic) int primaryKey;
#end
Implementation
#implementation MyAudioPlayer
#synthesize primaryKey = _primaryKey;
#end
Creating
MyAudioPlayer *player = [[MyAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
player.primaryKey = thePrimaryKey;
...
Delegate
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
if ([player isKindOfClass:[MyAudioPlayer class]]) {
MyAudioPlayer *myPlayer = (MyAudioPlayer *)player;
NSNumber *primaryKeyObject = [NSNumber numberWithInt:myPlayer.primaryKey];
...
}
}
A simple approach might be to create an NSMutableDictionary and use the AVAudioPlayers you create as KEYS with the primary key (or an entire dictionary) as the corresponding VALUE. Then when a player stops playing (or errors) you can look it up in your dictionary and recover anything you like.
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];
}
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];