AVAudioPlayer volume from variable - iphone

I'm trying to change the volume of a sound effect via a variable. I'm using AVAudioPlayer and calling the variable to set volume, however when I run the app I get no sound at all, regardless of the variable setting. (.1-1.0) However if I set the number from within the AvAudioPlayer block (player.volume = .5;) then it responds as it should. Any ideas what I'm doing wrong?
Example of my code:
#interface
#property (nonatomic) float setVolume;
#implementation
#synthesize setVolume;
float setVolume = .5;
-(void)countdown
{
//play sound
NSString *musicFilePath = [[NSBundle mainBundle] pathForResource:#"Countdown_beep" ofType:#"wav"];
NSURL *musicURL = [[NSURL alloc] initFileURLWithPath:musicFilePath];
player = [[AVAudioPlayer alloc] initWithContentsOfURL:musicURL error:nil];
player.volume = setVolume;
[player play];
Thanks. :)
EDIT:
I fixed the problem by removing the declaration from the header file and creating the ivar within the implementation.

This line makes no sense and is irrelevant to your program:
float setVolume = .5;
You are already synthesizing a property/ivar called setVolume. This isn't it. So by default your property setVolume is zero and you are doing nothing to change that, so the volume is ending up as zero and no sound is happening.
To set the value of the property, set the value of the property. :) For example, you could say self.setVolume = .5. You could do that earlier in the countdown method, for example, or in some other method (one of the points of a property is that all methods of this object can see it).
Now, another issue with your code is that your property name begins with "set". This is probably a bad idea. If I were you, I'd pick another name. Names of the form "setX" are best used only as the name of a setter method for an instance variable / property X.

Related

Updating an NSUInteger property within a block. Warning: Block will be retained by

I have an NSUInteger defined as a property like so:
#property (nonatomic, assign) NSUInteger wordDisplayed;
I need to have this as a property because I need access to read/write to this variable from other methods and classes.
When trying to change the value of this property within a block, I get the following warning:
Capturing 'self' strongly in this block is likely to lead to a retain cycle
"Block will be retained by an object strongly retained by the captured object"
How can I update this variable property within a block?
The block I'm using is in a modified UIActionSheet that I'm using to make the UIActionSheet accept blocks.
https://github.com/zoul/Lambda-Alert
Here is an example of my code:
sectionHeadersAct = [[LambdaSheet alloc] initWithTitle:#"Book 2 Lesson 1"];
[sectionHeadersAct addButtonWithTitle:#"D. E. F. & G. Teach New Letters" block:^{
wordDisplayed = 15; //This is where I'm trying to change the value
}];
[sectionHeadersAct showInView:self.view];
This post deals with this certain issue: https://stackoverflow.com/a/8159567/656036
To summarize it: You should use a weak pointer to self. If you use ARC, you can solve this by writing this before your block:
__weak MyObject *weakSelf = self;
And using weakSelf instead of self in the block itself.
EDIT Try this:
__weak ViewController *weakSelf = self;
sectionHeadersAct = [[LambdaSheet alloc] initWithTitle:#"Book 2 Lesson 1"];
[sectionHeadersAct addButtonWithTitle:#"D. E. F. & G. Teach New Letters" block:^{
weakSelf.wordDisplayed = 15;
}];
[sectionHeadersAct showInView:self.view];
This is a common occurrence when using blocks. I assume you are accessing the property with the block by something like self.wordDispalyed.
Because blocks can be run asynchronously, they hold a strong reference to objects inside them. This is so that if they run at some time after you have released the object, at least the block can still send messages to it.
In your case, you are referencing a property within the block. So the block retains the the object, in this case self. But, your object (the self) is also retaining the block, since it has to run it. So you have a circular reference. The block retains the object, and the object retains the block. This might cause you problems - and the compiler is warning you of this.
To get around this you need to create a local variable that well be retained by the block, and use that to reference the property. It's a simple as writing:
__weak TheObject *blockSelf = self;
And then within your block you deal with the property as:
blockSelf.wordDisplayed …

Potential leak problem when taking control of setter method of retained object

So here is my code:
-(void)setMovie:(NSURL *)movieLocal {
movie = movieLocal;
[self.movie retain];
...
}
And i get this error:
Potential leak of an object allocated on line 43
Line 43 is [self.movie retain];. Am i doing something wrong, and how can i get rid of this error?
There are a couple issues here:
The old value for movie is never released
'movie' and 'movieLocal' might point to the exact same object. If that is the case, you will call retain on movie/movieLocal without a subsequent balanced release call.
You might want to use the following:
-(void)setMovie:(NSURL *)movieLocal {
if (movie == movieLocal) {
return;
}
[movie release];
movie = [movieLocal retain];
//...
}
Here's the proper setter:
-(void)setMovie:(NSURL *)movieLocal {
if (movie != movieLocal) {
[movie release];
movie = movieLocal;
[movie retain];
}
}
However, if you declared your property (in .h file):
#propert (nonatomic, retain) NSURL *movie;
and synthesized it in .m file, by #synthesize movie;, than there is no need to explicitly override the setter - the code above will be generated automatically for you. So whenever you want to set your movie you'll just call self.movie = newMovie; which will be equivalent to calling [self setMovie:newMovie];.
For more info read a section "Declared Properties" in Learning Objective-C guide.
EDIT: to explain what was wrong with your setter.
-(void)setMovie:(NSURL *)movieLocal {
movie = movieLocal; // first line
[self.movie retain]; // second line
...
}
In 1st line you are assigning movie to point to movieLocal, but you don't release old NSURL object that movie was pointing to before assignment. This was way, your causing a memory leak - you abandon memory, so it can never be relinquished back by your app. In general, abandoning memory is an easy way to get you application terminated by iOS, when objects are big and often leaked.
In 2nd line you are calling you setMovie setter again as self.movie = syntax causes the runtime to call a setter for movie property. This time it'll cause an infinite loop.
I hope my wording was clear for you and my answer helpful.

Accessing NSDictionary problem

I'm only new to iPhone development, so my apologies if this is a silly question. I'm building various apps based on a book I'm reading and one of their suggestion was to build a mini web browser. I thought this would be easy, but while most of it is, I'm seriously struggling with the NSDictionary.
I have a UISegmentedControl used to display various bookmarks. The bookmark name that is displayed on the buttons of the UISegmentedControl is going to be my key and the url is the value associated with it.
I first try to declare an NSDictonary as a private (global variable), but since I could not get it to work, I resorted to declare it in my header file as follows:
#property (nonatomic, retain) NSDictionary *bookmarks;
I synthesize it and I initialized it in the viewDidLoad as follows:
- (void)viewDidLoad
{
bookmarks = [NSDictionary
dictionaryWithObjectsAndKeys:#"http://www.microsoft.com",
#"Microsoft",
#"http://www.google.com",
#"Google",
#"http://www.apple.com",
#"Apple",
#"http://msdn.microsoft.com",
#"MSDN", nil];
[super viewDidLoad];
}
I then associated a control with my segmented control and when the event is triggered and the function is called I've got the following code which is called:
- (IBAction) getShortcut:(id)sender
{
NSString *shortcut;
shortcut = [shortcuts titleForSegmentAtIndex:shortcuts.selectedSegmentIndex];
NSString *url = [bookmarks valueForKey:shortcut];
//[self navigateTo: url];
[url release];
}
When a button from the UISegmentedControl is clicked, I extract the value and stored it into shortcut and then I try to use the shortcut variable as a key to extract the associated value from the NSDictionary "bookmarks" but it keeps crashing on NSString *url = [bookmarks valueForKey:shortcut];
and bombs out of my function and displays the usual error EXC_BAD_ACCESS
Any help would be greatly appreciated.
T.
You have two options. one is to deal with the ivar directly as #Matt S. posted. Note that in this case you need to keep you object with enough retain count. You're using and auto released object and causing the error.
The other option is to use the property you already defined:
self.bookmarks = [[NSDictionary ...]];
And the property retains it.
That dictionary is autoreleased.
Try this:
self.bookmarks = [NSDictionary dictionaryWithObjectsAndKeys...]
You didn't retain the NSDictionary.
Do:
bookmarks = [[NSDictionary
dictionaryWithObjectsAndKeys:#"http://www.microsoft.com",
#"Microsoft", nil] retain];
The problem is, that you do not retain "bookmarks" in the viewDidLoad method. There is an naming convention mentioned somewhere in the Apple docs: If an intialisation method starts with "init..." the returned object is retained, if not you have to do it yourself. The "dictionaryWithObjectsAndKeys" returns and object with retain count 0, which means, that after the scope of assignment (your viewDidLoad method) it is immediatly released again.
So just put a
[bookmarks retain];
after your initalisation and you are done. Another solution which does the retaining for you
bookmarks = [[NSDictionary alloc] initWithObjectsAndKeys ...];
And you shouldn't release the url in your action. It gets released, once you release the dictionary

Filling an NSMutableArray with a Set of Classes and Then Getting them Back

Hopefully I can make this clear, but I am new to Objective-C and to be honest not great with Arrays in the first place.
So, I have a Singleton class (called SingletonState) that I am using to pass variables around my app (please can we leave the whether I should use Singleton classes out of this - I will fix that later). In this class, I have an NSMutableArray (called arrayMyEvents). I have also created a class that I am going to store a list of events (called EventClass). When the user logs in, I call a web service and get back 3 strings. The 3rd string is a comma separated list of value. I parse the data and populate the custom class EventClass. I then add this class to the SingletonState.arrayMyEvents.
I have all of this working. I can go to another ViewController and access the "count" of items in arrayMyEvents.
PROBLEM: I now want to edit one of the ScheduledEventsClass"es" in my array. How do I get access to it and edit some of the properties and update the one in my SingletonState class?
Here is some of the code, that I've tried:
NSString *sWebServiceEvents = [[NSString alloc] initWithFormat:#"%#", [result objectAtIndex:2]];
if ( [ sWebServiceEvents isEqualToString:#"NULL" ] != true ) {
NSArray *arrayEvents = [sWebServiceEvents componentsSeparatedByString:#","];
// If the array has not been initialized they initialize it.
if (sharedState.arrayMyEvents == nil) {
sharedState.arrayMyEvents = [[NSMutableArray alloc ] init ];
}
for (NSString * sEvent in arrayEvents) {
// Set equal to the value of the array (the Event Number) at the same
// position as the row that we are being asked to return a cell/row for.
EventClass *eventClass = [[EventClass alloc] retain];
eventClass.sEvent = sEvent;
[ sharedState.arrayEvents addObject:eventClass ];
}
NSLog(#"LoginView - sharedState.arrayMyEvents Count: %d", [sharedState.arrayMyEvents count]);
}
Here is me trying to access it in another ViewController:
EventClass *eventClass =
[sharedState.arrayMyEvents objectAtIndex:row ];
NSLog(#"eventClass.sEventNumber: ", eventClass.sEventNumber);
eventClass.sLocation = #"Jason's Big Location";
You're going to have some memory leaks from the sEvent loop. [[EventClass alloc]retain] leaves you an uninitialized EventClass object with a reference count of 2. You'll need to change that to [[[EventClass alloc] init] autorelease] to keep it from leaking. The arrayEvents NSMutableArray will retain it during the addObject: call. (Shouldn't that be [sharedState.arrayMyEvents addObject: eventClass] in the loop?)
After that, all you have to do to edit the EventClass object in the second block of code is edit it. The eventClass variable is a pointer to an object in the array. Anything done to that object doesn't affect the pointer referencing it, it affects data referenced by it. The code you have in the second block should change the sLocation of the selected object as you intend.
You have a few more memory leaks in there, too. Use Cmd-Shift-A to build with the static analyzer and it'll tell you where.
Maybe the problem is that you put them in sharedState.arrayEvents but try to take them out of sharedState.arrayMyEvents. Different variables.
Also, lots of memory leaks.
Thanks John and St3fan, your answers and time are appreciated!!!
I think that I figured out my issue:
Basically, the class that I created (EventClass) had the properties setup like this:
#property (nonatomic, assign) NSString *sStudyNumber;
#property (nonatomic, assign) NSString *sTheater;
but, they should be (or at least I got it to work like this):
#property (nonatomic, retain) NSString *sStudyNumber;
#property (nonatomic, retain) NSString *sTheater;
Then, in my second view I was able to do this:
EventClass *eventClass = [sharedState.arrayMyEvents objectAtIndex:row ];
NSLog(#"MyEvents: %#", eventClass.sEventNumber);
eventClass.sLocation = #"Jason's Big Location";
I then checked it in another method of the view using this and it was still there:
EventClass *eventClass = [sharedState.arrayMyEvents objectAtIndex:row ];
NSLog(#"MyEvents: %#", eventClass.sEventNumber);
NSLog(#"MyEvents: %#", eventClass.sLocation);
I also, checked it in yet another view and the value was maintained in the SharedState.arrayMyEvents without issue. :)
In the end, I believe that I boiled down to the difference between "assign" and "retain".
Now, on to the memory leaks :(
Please, let me know if you see any other issues with this.
Thanks,
Jason

I am writing a pretty simple iPhone app that will play a sound depending on which button is pressed

A... as I learn Objective C.
There will be close to 20 button on a view at a time... :)
If I use a if/else loop for each button touched, it works great but I consider that to be inelegant and just plain cumbersome.
SO, I am using the button.tag to allow me to assign a number to each button then evaluate each button based on the .tag property.
As I mentioned before, works great with if/else if loops.
But I want to change the tag to a string and then nest it within a NSURL to play a sound. Each button will generate a new tag and consequently a new sound.
As reported by the debugger, I am getting Not a CFSTRING and 'out of bounds' messages and upon stepping through, crashes. Fun, but not blue screen.
Here is the code in question. I humbly request of the Objective C gurus to lend some insight!
int soundNumber = [sender tag];
//soundPick = [NSString stringWithFormat:#"%d", soundNumber]; //remmed out as an alternative try
NSString *soundPick = [[NSNumber numberWithInt:soundNumber] stringValue];
NSURL *aSoundURL = [NSURL fileURLWithPath:[mainBundle pathForResource:soundPick ofType:#"aif"]];
playSound = [[AVAudioPlayer alloc] initWithContentsOfURL:aSoundURL error:&error];
if (!playSound) {
NSLog(#"no Sound for that button: %#", [error localizedDescription]);
}
[playSound play];
*/
Thanks,
Neil
I would create an array with all 20 file paths in it, then i would use the tag to index the array.
It almost sounds like you're programming behaviour in your buttons.
Why don't you just assign each button to a different IBOutlet/callback method? You won't need any if/else statements at all.
On your IBActions, add something like this:
SystemSoundID theSound;
NSString *pathToSound;
pathToSound = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"<filename>" ofType:#"<filetype>"] isDirectory:NO];
AudioServicesCreateSystemSoundID((CFURLRef)pathToSound, &theSound);
AudioServicesPlaySystemSound (theSound);
Then add this to your .h file and the the AudioToolbox.framework to your project:
#import <AudioToolbox/AudioToolbox.h>
Worked like a charm for me. Hope this helps.
Thanks! I got the code to work and I used the button.tag. Then, I named each sound file against the tag number.. So, tag 1 = 1.aif, tag 2=2.aif etc... I used a paper chart to get started then off to the races. THe issue I had was how to get the button.tag as a string and I struggled until I figured it out: -(IBAction)buttonPressed:(id)sender; int soundTag = [sender tag]; soundPick = [[NSString alloc] initWithFormat: #"%d", soundTag]; Then took soundPick and stuck in the appropriate location in NSURL.. Thanks!