i'm new in Objective C, i'm developing an app that play sound on touch event of an uibutton.
When i touch the button, it change is image and audio starts to playing. When audio is finish to play, i want to put the original image of uibutton. How can i do that? How can i catch the event of the audio that is finish?
i'm using that code:
- (SystemSoundID) createSoundID: (NSString*)name
{
NSString *path = [NSString stringWithFormat: #"%#/%#",
[[NSBundle mainBundle] resourcePath], name];
NSURL* filePath = [NSURL fileURLWithPath: path isDirectory: NO];
SystemSoundID soundID;
AudioServicesCreateSystemSoundID((CFURLRef)filePath, &soundID);
return soundID;
}
In
viewDidLoad
i call
freq = [self createSoundID: #"freq.wav"];
and in function that is called on button's touch i use:
AudioServicesPlaySystemSound(freq);
How can i do? Thank you!
Not sure if it's possible with the AudioService API but you can use AVAudioPlayer instead and assign a delegate that implements the AVAudioPlayerDelegate protocol and does something when audioPlayerDidFinishPlaying:successfully: is called.
Remember to add the AVFoundation framework.
Example:
In your interface definition:
#import <AVFoundation/AVFoundation.h>
...
#inteface SomeViewController : UIViewController <AVAudioPlayerDelegate>
...
#propety(nonatomic, retain) AVAudioPlayer *player;
In your implementation:
#synthesize player;
...
// in some viewDidLoad or init method
self.player = [[[AVAudioPlayer alloc] initWithContentsOfURL:soundUrl error:NULL] autorelease];
self.player.delegate = self;
...
- (void)pressButton:(id)sender {
// set play image
[self.player stop];
self.player.currentTime = 0;
[self.player play];
}
...
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
// set finish image
}
...
# in some dealloc or viewDidUnload method
self.player = nil;
It is possible with the AudioService API, you want to use AudioServicesAddSystemSoundCompletion.
Related
I created class for handling sounds. But I've problem to stop sound? I can't send variable from playSound to stopSound AudioServicesDisposeSystemSoundID(soundID).
How to handle this? I really don't want instantiate it or I've to?
#implementation SoundPlayer
+(void)playSound:(NSString *)filename ofType:(NSString *)extension {
SystemSoundID soundID;
NSString *soundFile = [[NSBundle mainBundle] pathForResource:filename ofType:extension];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:soundFile], &soundID);
AudioServicesPlaySystemSound(soundID); // error - soundID is undeclared
}
+(void)stopSound
{
AudioServicesDisposeSystemSoundID(soundID);
}
#end
You'll either have to pass the id to stopSound (the same as when you play sound), or else if your audio player only plays 1 sound at a time, store the soundID as a static param on the class when you play.
You can create soundID as ivar, something like:
#interface SoundPlayer(){
SystemSoundID soundID;
}
#end
Other way use it as global variable
I have a UIButton in a nib (xib) that id like to play a sound when touched, what protocol needs to be followed to make this happen?
** overhauled question** Throughout my time here on Stack Overflow, i've learned a lot, and not just about what i'm usually needing help with (Objective-C, xCode), but with how Stack Overflow works. I find it to be a vast wealth of information and support and i've looked at how i've asked this question before and it made absolutely no sense at all. To help future users, i've edited what I was originally asking for, in a way others can understand like Naveen did below. Apologies
Add this framework
AudioToolBox framework
Include this header file .h file
#include <AudioToolbox/AudioToolbox.h>
CFURLRef soundFileURLRef;
SystemSoundID soundFileObject;
Add this code in viewdidLoad
NSURL *tapSound = [[NSBundle mainBundle] URLForResource: #"Voice035"
withExtension: #"amr"];
// Store the URL as a CFURLRef instance
self.soundFileURLRef = (CFURLRef) [tapSound retain];
// Create a system sound object representing the sound file.
AudioServicesCreateSystemSoundID (
soundFileURLRef,
&soundFileObject
);
On Button Tap, Call this method
AudioServicesPlayAlertSound (soundFileObject);
Do release in dealloc
AudioServicesDisposeSystemSoundID (soundFileObject);
CFRelease (soundFileURLRef);
For more info : http://developer.apple.com/library/ios/#documentation/AudioToolbox/Reference/SystemSoundServicesReference/Reference/reference.html#//apple_ref/c/func/AudioServicesCreateSystemSoundID
I would say, create an 'Observer' class that plays the sound that all buttons are connected to. I'll give an example below, It is a singleton class. It's written on a whim, and not tested but gives you an idea.
//SoundObserver.h
#include <AVFoundation/AVFoundation.h>
#interface SoundObserver {
AVAudioPlayer *audioPlayer;
}
-(void)playSound;
+(SoundObserver*)sharedInstance;
#property (nonatomic, retain) AVAudioPlayer *audioPlayer;
#end
//SoundObserver.m
static SoundObserver *instance = nil;
#implementation SoundObserver
-(id)init {
self = [super init];
if(self) {
//Get path to file.
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"sound"
ofType:#"wav"];
// filePath to URL
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:filePath];
//Initialize the AVAudioPlayer.
self.audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:fileURL error:nil];
// Preloads buffer and prepare for use.
[self.audioPlayer prepareToPlay];
[filePath release];
[fileURL release];
}
}
+(SoundObserver*)sharedInstance {
#synchronized(self) {
if (instance == nil)
instance = [[self alloc] init];
}
return instance;
}
-(void)playSound {
//make sure the audio resets to beginning each activation.
[self.audioPlayer play];
}
-(void)dealloc {
//Clean up
[self.audioPlayer release];
self.audioPlayer = nil;
[super dealloc];
}
// User example.
In applicationDidFinishLaunching:
[SoundObserver sharedInstance];
From here you can connect all buttons to the same function, or call it from anywhere in the App that #import's SoundObserver.h
-(IBOutlet)buttonClick:(id)sender {
[[SoundObserver sharedInstance] playSound];
}
I've searched and I believe my problem is quite unique. I'm aware of the Simulator 5.1 bug when using AVAudioPlayer which isn't my problem. I'm running on a iOS 5.1 device.
Here's my header file:
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <AVFoundation/AVAudioPlayer.h>
-(IBAction)pushBell;
#end
and my implementation file:
#import "BellViewController.h"
#interface BellViewController ()
#end
#implementation BellViewController
-(IBAction)pushBell {
NSString *soundPath =[[NSBundle mainBundle] pathForResource:#"bell1" ofType:#"caf"];
NSURL *soundURL = [NSURL fileURLWithPath:soundPath];
NSError *error;
AVAudioPlayer *theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:&error];
[theAudio setDelegate:self];
[theAudio setNumberOfLoops:0];
[theAudio play];
}
#end
And I have a button in my .xib that plays the file.
To make sure I referenced my audio file correctly, I deliberately type a wrong file name and indeed I got an exception. To make sure the file is playable, I mailed it to myself and played it on the device.
When I tap the button I hear nothing. There are no errors. I don't know what is wrong.
More information
iPhone 4 with iOS 5.1
Xcode 4.3.2
Audio is about 700kbps converted to .caf format using afconvert from a big .wav file.
SOLVED.
1.- Check if your file has been added to "Copy Bundle Resources" in Build Phases.
2.- ViewController.m
#import <AVFoundation/AVAudioPlayer.h>
#interface ViewController ()
#property (nonatomic, strong) AVAudioPlayer *theAudio;
- (IBAction)pushBell;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *soundPath =[[NSBundle mainBundle] pathForResource:#"beep-beep" ofType:#"caf"];
NSURL *soundURL = [NSURL fileURLWithPath:soundPath];
NSError *error = nil;
self.theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:&error];
}
- (IBAction)pushBell {
[self.theAudio play];
}
3.- Connect the IBAction to your button in Interface Builder.
Done.
This was my issue, so I'm just posting it here in case it helps anyone!
Believe it or not, there appears to be a bug (in iOS 5.1 on iPad 2 at least) where if you had the Side Switch set to Mute and it was Muted, then set the Side Switch to Lock Rotation, AVAudioPlayer sounds WON'T PLAY except through headphones!
So I had to go to the Settings app, change the switch back to Mute, then unmute the iPad, switch it back to Lock Rotation and my app started to work properly.
Your theAudio AVAudioPlayer is deallocated before it can even start playing (since it's allocated as a local variable inside pushBell method.
Change your code to:
header file:
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <AVFoundation/AVAudioPlayer.h>
#interface BellViewController:UIViewController
#property (nonatomic, strong) AVAudioPlayer *theAudio;
-(IBAction)pushBell;
#end
implementation file:
#import "BellViewController.h"
#implementation BellViewController
#synthesize theAudio = _theAudio;
- (void)viewDidLoad {
if (!theAudio) {
NSString *soundPath =[[NSBundle mainBundle] pathForResource:#"bell1" ofType:#"caf"];
NSURL *soundURL = [NSURL fileURLWithPath:soundPath];
NSError *error;
_theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:&error];
[self.theAudio setDelegate:self];
[self.theAudio setNumberOfLoops:0];
[self.theAudio prepareToPlay];
}
}
-(IBAction)pushBell {
[self.theAudio play];
}
#end
I'm assuming that BellController is a UIViewController, if it's a different kind of class just modify the code accordingly.
I think that it is a memory issue, you should instantiate the AVAudioPlayer *theAudio as a global class object and initialise it in the init method. Then just run the [theAudio play]; method in the -(IBAction)pushBell method.
I responded do a similar issue related to playing a system sound (which in fact is what you are trying to do), check it out my response is at the bottom:
Couldn't play system sound after switching to iOS 5
By the way consider playing the bell as a system sound, AVAudioPlayer is more appropriate to play sound tracks, not bells and effects.
I hope that it helps.
I agree with hooleyhoop. Try
NSLog(#"%#", error.userInfo)
to see what's coming out.
Another alternative would be to set up the player object in either viewDidLoad or initWithNib and storing it as a property on your object. This way you can use something like
[self.theAudio prepareToPlay];
To avoid loading up the audio each time you press the button, which could save you some time if it's high quality audio. Then just call [self.theAudio play]; in you IBAction method.
Sorry, if this answer is a little sloppy - typing on an iPad so can't double check properly.
You should give it some time to prepare the pronunciation since it's probably buffering from a URL.
The code:
[theAudio prepareToPlay];
[self performSelector:#selector(playMusic) withObject:nil afterDelay:3.0];
I’m trying to make a small iPhone application with some buttons to play WAV sounds.
My buttons works, but I have a small latency (~ 0,5 sec).
This is my .m file :
#import "buttonSoundViewController.h"
#implementation buttonSoundViewController
//#synthesize player;
-(IBAction) playSoundA:(id)sender{
NSString *path = [[NSBundle mainBundle] pathForResource:#"a" ofType:#"wav"];
AVAudioPlayer* theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
theAudio.delegate = self;
[theAudio play];
}
-(IBAction) playSoundB:(id)sender{
NSString *path = [[NSBundle mainBundle] pathForResource:#"b" ofType:#"wav"];
AVAudioPlayer* theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
theAudio.delegate = self;
[theAudio play];
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
[player release];
}
-(void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error {
}
-(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player {
}
-(void)audioPlayerEndInterruption:(AVAudioPlayer *)player {
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
}
- (void)dealloc {
[audioPlayer release];
[super dealloc];
}
#end
How can I avoid this latency between playing different sounds?
Using AudioServices is indeed much quicker for small sounds (less than 30 seconds). The required code isn't very long either (but it requires some good old C).
#import <AudioToolbox/AudioServices.h>
SystemSoundID soundID = 0;
NSString* str = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
CFURLRef soundFileURL = (CFURLRef)[NSURL URLWithString:str ];
OSStatus errorCode = AudioServicesCreateSystemSoundID(soundFileURL, &soundID);
if (errorCode != 0) {
// Handle failure here
}
else
AudioServicesPlaySystemSound(soundID);
Also you can optimize your sounds (reduce their size) with the following terminal command:
afconvert mysound.caf mysoundcompressed.caf -d ima4 -f caff
You could simplify things a lot by using system sounds. Look up: AudioServicesCreateSystemSoundID in the documentation. There is also a "System Sound Services Reference Document" that talks about that and other related functions. This is a simple and efficient way to play short sounds. Not sure if it will solve your latency issues but its a good start. You may also try using some different sound file types. Perhaps there is an issue with how it was or was not compressed.
Maybe you could look at AppSoundEngine. It addresses latency and greatly simplifies using of System Sound Services, because it is objective-c wrapper for SystemSoundID and associated C functions.
You can not get acceptable latency (<10 ms) from AVAudioPlayer. System Sound Services is the way to go.
From the Apple Docs grab the SoundEffect class in project BubbleLevel
SoundEffect is a simple Objective-C wrapper around Audio Services
functions that allow the loading and playing of sound files.
Classes/SoundEffect.m
this will make playing files as easy as
SoundEffect *soundEffect = [SoundEffect soundEffectWithContentsOfFile:#""]
[soundEffect play];
it will also handle memory deallocation. AudioServicesDisposeSystemSoundID(soundID);
One simple fix would just be to do the AVAudioPlayer alloc init for the 2 sounds in your buttonSoundViewController's init method. Then those 2 audio players will already be ready to play in your button delegates.
The fastest way to play sounds is to use the RemoteIO Audio Unit, but that's a far more advanced and complicated looking API.
I have an app I am working on and It uses some of the hardware sensors to provide data on screen, there is a Label that updates with the number. I want a sound to play whenever the number is above 100 or something. For example, say it was reading numbers then all of the sudden it finds a good spot (or whatever), then I would like a sound to play or a light to light up. I am an absolute beginner and it would be nice if the answer would be easy for a absolute beginner to understand.
I am using the system AudioToolbox.framework for playing sounds in my simple game. I added this static function to common MyGame class:
+ (SystemSoundID) createSoundID: (NSString*)name
{
NSString *path = [NSString stringWithFormat: #"%#/%#",
[[NSBundle mainBundle] resourcePath], name];
NSURL* filePath = [NSURL fileURLWithPath: path isDirectory: NO];
SystemSoundID soundID;
AudioServicesCreateSystemSoundID((__bridge CFURLRef)filePath, &soundID);
return soundID;
}
I added the "Morse.aiff" file to project Resources and initialized it in (any) class initialization with the following:
self.mySound = [MyGame createSoundID: #"Morse.aiff"];
And then I played sound with this call:
AudioServicesPlaySystemSound(mySound);
Also, don't forget to import AudioServices.h file.
This audio toolbox can play also different sound formats.
h
#import <AVFoundation/AVFoundation.h>
#interface CMAVSound : NSObject {
AVAudioPlayer *audioPlayer;
}
- (id)initWithPath:(NSString*)fileNameWithExctension;
- (void)play;
#end
m
#import "CMAVSound.h"
#implementation CMAVSound
-(void)dealloc {
[audioPlayer release];
}
- (id)initWithPath:(NSString*)fileNameWithExctension {
if ((self = [super init])) {
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath], fileNameWithExctension]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
if (audioPlayer == nil) {
NSLog(#"%#", [error description]);
}
}
return self;
}
- (void)play {
[audioPlayer play];
}
#end
Check out the documentation for the AVAudioPlayer class. It allows you to play sounds clips. If you have troubles implementing that, show us some code.
If the sounds gonna up to 5 seconds and no stereo output is needed I would recommend you to do that with system sounds. It is easy and better solution then any other.
Apple sample code is provided under name SysSound
EDIT1
Or maybe tutorial could help you more
http://howtomakeiphoneapps.com/2009/08/how-to-play-a-short-sound-in-iphone-code/
Take a look at following links;
http://blog.guvenergokce.com/avaudioplayer-on-iphone-simulator/57/
http://mobileorchard.com/easy-audio-playback-with-avaudioplayer/
Here you can find list of AudioServicesPlaySystemSound
http://iphonedevwiki.net/index.php/AudioServices