Making a Mute Toggle Button Using NSUserDefaults and AVAudioPlayer - iphone

I've been working on this project for a while, but I've encountered a problem that I can't figure out.
First, I have a checkbox button that saves to NSDefaultUser as BOOL value. It simply saves the value YES when it's pressed once and it saves NO if pressed again and so on... This checkbox button works fine like a normal custom checkbox button would.
I would like to make an option to mute all of my sounds in my app by using this checkbox button.
I'm playing my sounds by calling method such as:
- (void)startMusic1
{
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/music1.mp3", [[NSBundle mainBundle] resourcePath]]];
NSError *error;
AVAudioPlayer *audioPlayer;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
audioPlayer.numberOfLoops = -1;
if (audioPlayer == nil)
NSLog(#"Error: %#", [error description]);
else
[audioPlayer play];
}
So for every sound I want to play in my app, I could repeat something like:
- (IBAction)playButton:(id)sender
{
NSUserDefaults *default = [NSUserDefaults standardUserDefaults];
if(![default boolForKey:#"isMuted"])
{
[self startMusic1];
}
}
But, this gets repetitive especially because I have to manage a lot of different sounds (and different buttons that play sounds) for my project, and it seems irrelevant to repeat these steps.
I tried making a new class with a subclass of AVAudioPlayer and messed around with -(BOOL)play method for a few days, but I couldn't manage to get the results I wanted. I researched and found posts like Disable in App sounds but this still wouldn't do it.
I'm pretty new to programming overall, so it'll be great if someone could enlighten me a little.

I tackle this (rightly or wrongly, but it works for me) by using a global model singleton. This is a class that effectively maintains state across the entire application.
The way to do that is with a class that has a shared static property like this:
//// Interface
#import <Foundation/Foundation.h>
#interface AudioModel
#property (nonatomic) BOOL playAudio;
+(id)sharedInstance;
#end
//// Implementation
#import "AudioModel.h"
#implementation AudioModel
// property for toggling audio on or off
#synthesize playAudio = _playAudio;
// singleton model variable
static AudioModel* audio = nil;
-(BOOL)playAudio {
return _playAudio;
}
-(void)setPlayAudio:(BOOL)playAudio {
_playAudio = playAudio;
}
// static
+(AudioModel*)sharedInstance {
if(audio == nil)
{
audio = [[AudioModel alloc]init];
}
return audio;
}
-(AudioModel*)init {
self = [super init];
if(self){
// set up default sounds on
// this may read from your stored value
_playAudio = YES;
}
}
#end
Then when you want to read or write to this globally available singleton model, you set a variable in your controller like this:
AudioModel *volumeControl = [AudioModel sharedInstance];
if(volumeControl.playAudio){
// method to play audio passing audio file name...
}
You could also have a reference to your AVAudioPlayer instance in this class, init it on creation of the shared instance and pass files to it to play when required.

Related

How to control audio sound through UIswtch of on and off

Playing sound for click sound when date is selected in the date picker. Now i need to set my setting module for control ON and OFF using UISWitch.
How to set my control of sound to uiswitch .
-(void) switchforSounds:(id)sender
{
if ([sender isOn])
{
[Appdelegate.infoObj playback];
}
else
{
}
}
-(void) playback
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"click" ofType:#"caf"];
if (path)
{
NSURL *url = [NSURL fileURLWithPath: path];
SystemSoundID soundID;
AudioServicesCreateSystemSoundID ((CFURLRef)url, &soundID);
AudioServicesPlaySystemSound(soundID);
}
}
Nice and easy:
if(myUISwitch.on){
[self playback];
}
If that wasn't what you were looking for, I can always revise it.
if the question is how to bind the switch in IB to your function
-(void) switchforSounds:(id)sender the answer is
declare it like this:
- (IBAction) switchforSounds:(id) sender in your .h and .m file respectively.
right click on the switch in IB, you will see all events it can respond to. Choose changeValue event. Click in the hook near it and drag to the File's Owner element in your IB'c controller file. When you leave the mouse it's supposed to show you all IBAction selectors you've defined in your controller class. Choose switchforSounds.
Done.
Hopefully i understood your question right.

Sound playback delay (hiccup) when OpenAL is implemented on iphone

I implemented OpenAL code to my iphone game. When I starts the game, it runs for 1 sec and stalls for 2 sec then resumes (hiccup effect). I believe its delayed due to the sound files loading. What is the solution? Can anyone recommend any book, site or sources code (not the iphone reference, please)? Is there a loading process and where should I initialize the loading process? Would that help?
Below, I have included the related components of the OpenAL code that I have implemented. The sound file will be played and invoked by a "if" statement in the gameloop. The OpenALController class is for the sound sources and buffers creation and the InitOpenAL method is invoked in OpenALController. MyView is a customized subclass of UIView and connected to the main view (I didn't use the default view).
// MyView.m
// A customized UIView as the main view.
#import "OpenALSoundController.h"
- (void)startPlaying{
...
[self initializeValuables];
...
[self initializeTimer];
}
- (void)initializeTimer {
if (theTimer == nil) {
theTimer = [CADisplayLink displayLinkWithTarget:self selector:#selector)gameLoop)];
theTimer.frameInterval = 2;
[theTimer addToRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
}
}
- (void)gameLoop {
...
If something = true
// Play the sound
[[OpenALSoundController sharedSoundController] playSound1];
...
}
...
#end
// OpenALSoundController.h
#interface OpenALSoundController : NSObject {
...}
...
+ (OpenALSoundController*) sharedSoundController
...
#end
// OpenALSoundController.m
// Singleton accessor
{
static OpenALSoundController* shared_sound_controller;
#synchronized(self)
{
if(nil == shared_sound_controller)
{
shared_sound_controller = [[OpenALSoundController alloc] init];
}
return shared_sound_controller;
}
return shared_sound_controller;
}
- (void) initOpenAL{
...
file_url = [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:#"fire" ofType:#"wav"]];
firePcmData = MyGetOpenALAudioDataAll((CFURLRef)file_url, &data_size, &al_format,&sample_rate);
alBufferData(fireOutputBuffer, al_format, firePcmData, data_size, sample_rate);
[file_url release];
...
alSourcei(outputSourceFire, AL_BUFFER, fireOutputBuffer);
...
}
You might be interested in Finch, an OpenAL sound engine for iOS. It’s very well suited to games. It’s usually better to reuse some already existing code than develop and maintain your own.
First its better to use mp3, as wav files are huge and loading from disk takes time. Mp3 files are smaller on disk, loaded into memory and decompressed there for playing. Try experimenting by reducing mp3 bitrate/encoding quality too.
Also you need to preload sounds to avoid hiccups, otherwise you will have a delay the first time a sound is played.

Audio plays well in iPhone simulator but not in iPhone

I have an app that is nearly finished but encountered an annoying problem. In the app, I want to play a sound when I tap on some object, then the object disappears. This is the piece of code I used:
In Object.h
#import <UIKit/UIKit.h>
#import <AudioToolbox/AudioServices.h>
#interface Object: UIView {
UIImageView *image;
CGPoint location;
SystemSoundID soundFileID;
}
In Object.m
- (void)initWithSound {
//a bunch of code to define the image and location
NSString *soundPath = [[NSBundle mainBundle] pathForResource:#"Sound" ofType:#"caf"];
CFURLRef soundURL = (CFURLRef)[NSURL fileURLWithPath:soundPath];
AudioServicesCreateSystemSoundID(soundURL, &soundFileID);
return self;
}
Then in my mainViewController, several objects will be created upon user's action and added to the screen at different locations with different images. Upon tapping on the object itself, it will create a sound and disappear.
In Object.m
- (void)tapped {
AudioServicesPlaySystemSound(soundFileID);
image.image = nil;
[self removeFromSuperview];
}
Everything works find in the simulator. But when I tried that in iPhone, the object disappears on tapping as expected but the sound just doesn't play. Tried on a 3G and a 3GS, both don't play the sound. I think this should work. Am I making any mistakes? Thanks for any help.
EDIT: Just think of something that I don't know related or not. I'm also using the microphone to detect sound input from the user by AVAudioRecorder. Not sure if this would affect the audio output.
Finally found where the problem is!!
It turns out that I have to I have to set up an audio session. It's just that! Obviously I need more study on the audio and video things.
Just for someone who may run into the same situation as me, here is the code I added in the viewDidLoad method:
AVAudioSession * audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error: &error];
[audioSession setActive:YES error: &error];

Help stopping audio using AVAudioPlayer

Alright I have two problems. I'm using AVAudioPlayer to play a simple audio file in .caf format. What I'm trying to do is have a play and pause button. Here is a sample of a simple IBAction to play the audio from a button.
- (IBAction) clear {
NSString *path = [[NSBundle mainBundle] pathForResource:#"clear" ofType:#"caf"];
AVAudioPlayer* myAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
myAudio.delegate = self;
myAudio.volume = 1.0;
myAudio.numberOfLoops = 0;
[myAudio play];
}
So when I go ahead and create another IBAction with the method...
- (IBAction) stopaudio {
[myAudio stop];
audioPlayer.currentTime = 0;
}
I compile and xcode tells me that myAudio in the stopaudio IBAction is not defined. How do I fix it so I'm able to access myAudio in another IBAction?
My Second question is that I'm using NavigationController to setup a TableView, when I make a selection it takes me to a UIView with a back button to go back to the TableView. What I'm trying to do is to kill any audio that I have playing in the UIView when I go back to the TableView. The code above is part of the code that I'm going to have in the UIView.
Also one quick question. Lets say I have an audio file that is 1 minute long. Clicking play again will overlap a new instance of that audio before the old instance is done playing. How can I check to see if myAudio is playing and not allow it to be played again until myAudio is finished playing?
Thank You!
First of all, you should read the documentation. It will remove almost all of your questions. Nevertheless, to have access to your instance of AVAudioPlayer from all methods of your class, you should define it in your .h file. To stop your player on returning to the previous screen, call [myAudio stop] in your viewWillDisappear:(BOOL)animated method. About your last question. If you define myAudio in your .h file, you will be able to check if it is playing now. smth like
- (IBAction) clear {
if(![myAudio isPlaying]){
[myAudio play];
}
}

AVAudioPlayer sound on iPhone stops looping unexpectedly: How to debug?

I am implementing a sound effect that plays while a user is dragging a UISlider.
In a previous question, I used AudioServicesPlaySystemSound() but that type of sound can't be halted. I need the sound to halt when the user is not actively dragging the slider but has not released it.
Now I am creating a sound using an AVAudioPlayer object. I initialize it in my View Controller like this:
#interface AudioLatencyViewController : UIViewController <AVAudioPlayerDelegate> {
AVAudioPlayer *player1;
}
#implementation AudioLatencyViewController
#property (nonatomic, retain) AVAudioPlayer *player1;
#synthesize player1;
-(void)viewDidLoad {
[super viewDidLoad];
// the sound file is 1 second long
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"tone1" ofType:#"caf"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:soundFilePath];
AVAudioPlayer *newPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
[fileURL release];
self.player1 = newPlayer;;
[newPlayer release];
[self.player1 prepareToPlay];
[self.player1 setDelegate:self];
}
I have two methods to control the sound. The first one plays the sound whenever the slider is being moved. It is connected to the UISlider's Value Changed event in Interface Builder.
-(IBAction)sliderChanged:(id)sender;
{
// only play if previous sound is done. helps with stuttering
if (![self.player1 isPlaying]) {
[self.player1 play];
}
}
The second method halts the sound when the user releases the slider button. It's connected to the UISlider's Touch Up Inside event.
-(IBAction)haltSound;
{
[self.player1 stop];
}
This works when the slider button is moved then released quickly, but if you hold down the button longer than two or three seconds, the sound repeats (good) then breaks up (bad). From then on, the sound won't play again (super bad).
I have tried setting the sound to loop using:
[self.player1 setNumberOfLoops:30];
...but that leads to the app freezing (plus locking up the Simulator).
What can I do to debug this problem?
When a user is holding the UISlider, Value Changed gets called MANY MANY times (any time the user moves just a slight amount). I would use Touch Down instead.
You might want to still try using the loop for this route...
I like the idea of playing a sound while the user is moving the slider though. Let me know how it works out.
deggstand#gmail.com