Creating a global array of Audioplayers - iphone

How does one create a global array of audioplayers so that I can call the players from other classes.
The Java equivalent to static?
I want to play multiple sounds. Is a different player needed for each, or can one player, just play random sounds throughout?
Thanks

You dont need an array of players. one player is enough
Declare this in your appDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:resourcePath] error:&err];
}
-(void)playAudio:(NSString *)fileName
{
NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
NSString* file=[NSString stringWithFormat:#"/%#",fileName];
resourcePath = [resourcePath stringByAppendingString:file];
NSLog(#"Path : %#",resourcePath);
NSError* err;
//Declare the player in your didFinishLaunching Dont declare here
player.delegate = self;
[player play];
}
Now wherever you want to play any file create a object to appDelegate
yourAppDelegate *yourAppDelegateObject= (yourAppDelegate *)[[UIApplication sharedApplication] delegate];
Use that object to call that function
[yourAppDelegateObject playAudio:filenameOftheAudioFile];

You can create an array(NSMutableArray) in the AppDelegate class and you can add different players objects to that array, and you can access that array from any class
in App Delegate file,
in .h file
NSMutableArray *playersArray;
#property (nonatomic,retain)NSMutableArray *playersArray;
in .m file initialize that array
playersArray = [[NSMutableArray alloc] init];
in some other file you can access that array as follows
AppDelegateClassName *appDelegate = (AppDelegateClassName *)[[UIApplication sharedApplication] delegate];
[appDelegate.playersArray addObject:onePlayerObject];
similarly you can access that player object by reading from that array.
Regards,
Satya.

Related

How to subclass or store extra information in AVAudioPlayer player objects

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.

incompatible pointer types assigning to nsarray from nsdictionary

I'm new to iPhone development and have had great success with with answers from here so I am hoping to receive help directly. I am reading data into a tableview from a plist. The application works fine but I get 2 warnings when I compile. I know why I get the errors but I have been unsuccessful with resolving the issues. Although this app works I really would like to resolve the warnings efficiently. When I tried changing the NSDictionary to NSArray the warning goes away but the table is no longer populated.
Any help would be greatly appreciated.
Staff and Data are defined as NSArray in the Delegate .h file. The warnings show in the delegate .m file below.
My Delegate has the following:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Add the tab bar controller's current view as a subview of the window
NSString *Path = [[NSBundle mainBundle] bundlePath];
NSString *DataPath = [Path stringByAppendingPathComponent:#"Data.plist"];
NSString *SPath = [[NSBundle mainBundle] bundlePath];
NSString *StaffPath = [SPath stringByAppendingPathComponent:#"Staff.plist"];
NSDictionary *tempDict = [[NSDictionary alloc] initWithContentsOfFile:DataPath];
**self.data = tempDict;**
[tempDict release];
NSDictionary *staffDict = [[NSDictionary alloc]initWithContentsOfFile:StaffPath];
**self.staff = staffDict;**
[staffDict release];
In my staff ViewController I have the following:
if(CurrentLevel == 0) {
//Initialize our table data source
NSArray *staffDict = [[NSArray alloc] init];
self.tableDataSource = staffDict;
[staffDict release];
Midwest_DigestiveAppDelegate *AppDelegate = (Midwest_DigestiveAppDelegate *)[[UIApplication sharedApplication] delegate];
self.tableDataSource = [AppDelegate.staff valueForKey:#"Rows"];
}
else
self.navigationItem.title = CurrentTitle;
An NSArray holds a one dimensional list of items where an NSDictionary maps keys to values.
Array:
[a, b, c]
Dictionary:
{#"a" = #"first item", #"b" = #"second item"}
Could you declare data as NSDictionary *data; and populate it as data = [[NSDictionary alloc] initWithContentsOfFile:DataPath];
You then access values in the dictionary with [data valueForKey:#"key"]
Everything in your code suggests that the staff and data properties are NSDictionary instances. You initialize them to dictionary objects and you reference them as dictionary objects. Why then are you declaring them as NSArray objects?
You should change how they are declared so they are NSDictionary in your header file rather than NSArray. That seems to me the most logical way to remove your warnings.
This should still work assuming the contents of your "staff" NSDictionary has a key named "Rows" whose value is an NSArray. The code you have to initialize self.tableDataSource with an empty NSArray seems redundant, as you immediately overwrite the value with the
self.tableDataSource = [AppDelegate.staff valueForKey:#"Rows"];
line in your code

NSURL becomes null when passed as a parameter iphone

I have set up a couple of very simple methods that play with NSURLs and NSStrings.
the first method that gets called is getAudio. I want to set the variable audioPath equal to cleanAudio so that it can be used later in the encryptWithAudioFile. The problem is somewhere along the way when it is being passed as a parameter the object becomes null. I have no idea why and I have tried every method I can think of in terms of memory management to try and keep the object. The logs I have used work result as follows
NSLog(#"Clean Audio1: %#", audioPath); Perfect, works as it should NSLog(#"Clean Audio2: %#", audioPath); NSLog(#"Clean Audio3: %#", audioPath); NSLog(#"Clean Audio4: %#", audioPath);
#implementation Stub
#synthesize audioPath,
userText;
-(NSURL *)retrieveAudio:(NSURL *)cleanAudio{
self.audioPath = cleanAudio;
NSLog(#"Clean Audio1: %#", audioPath);
return cleanAudio;
}
-(NSString *)retrieveText:(NSString *)message{
self.userText = message;
NSLog(#"Text: %#", self.userText);
NSLog(#"Clean Audio2: %#", audioPath);
return message;
}
-(void)startEncrption{
NSLog(#"Clean Audio3: %#", self.audioPath);
[self encrypterWithAudioFile:self.audioPath withString:self.userText];
}
-(NSURL *)encrypterWithAudioFile:(NSURL *)audio withString:(NSString *)text{
NSLog(#"DIRTY AUDIO and TEXT: %#, %#", audio, text);
return audio;
}
-(void)dealloc{
[super dealloc];
[audioPath release];
[userText release];
}
#end
Edit: Here's the .h
#interface Stub : NSObject {
NSURL *audioPath;
NSString *userText;
}
-(NSURL *)retrieveAudio:(NSURL *)cleanAudio;
-(NSString *)retrieveText:(NSString *)message;
-(void)startEncrption;
-(NSURL *)encrypterWithAudioFile:(NSURL *)audio withString:(NSString *)text;
#property(nonatomic, retain) NSURL *audioPath;
#property(nonatomic, retain) NSString *userText;
I'm calling the methods in other classes as follows:
stubObj = [[Stub alloc] init];
[stubObj retrieveAudio:recordedTmpFile];
and
stubObj2 = [[Stub alloc] init];
[stubObj2 retrieveText:textView.text];
[stubObj2 startEncrption];
Edit: I should probably have mentioned that I have three views, all with different controllers running on a UIScrollView that are calling the [Stub] interface.
I would look into your memory management and make sure you are not over releasing that URL some where after you've passed it into this methods. What I can see from your current code is that (assuming your audioPath property is marked retain) you are actually over retaining it within this snipped when you are assigning it:
self.audioPath = [[NSURL alloc] initWithString:#"TEST"];
Should be:
self.audioPath = [NSURL urlWithString:#"TEST"];
It looks as if you are allocating the NSURL only in -[Stub getAudio:], right? It also looks like you are creating multiple Stub objects. Are you calling the getAudio: method on each of those objects before trying to use the audioPath property on that object? That could be your problem.
In your latest update of the post, you include 2 different examples of how you use this class. In the first one you have this:
stubObj = [[Stub alloc] init];
[stubObj retrieveAudio:recordedTmpFile];
I assume that this is working, and that your "Clean Audio1:" log shows the expected URL, is that correct?
In your next example you have this:
stubObj2 = [[Stub alloc] init];
[stubObj2 retrieveText:textView.text];
[stubObj2 startEncrption];
Since this is a new instance and you never call retrieveAudio on this instance, the audioPath property for this instance is never set so it is nil. When you call startEncrption it calls encrypterWithAudioFile and passes self.audioPath, which is nil. Isn't this what you are expecting or am I missing something here?
Finally figured out a fix. I ended up moving all my variables to my app delegate and referenced them from other classes by pointing to the delegate::
RonnieD1AppDelegate *appDelegate = (RonnieD1AppDelegate *)[[UIApplication sharedApplication] delegate];
I removed the startEncryption method altogether and called the method encryptWithAudioFile like this:
[stubObj2 encrypterWithAudioFile:appDelegate.audioPath withString:appDelegate.userText];
My final code ended up looking like this:
#implementation Stub
-(NSURL *)retrieveAudio:(NSURL *)cleanAudio{
RonnieD1AppDelegate *appDelegate = (RonnieD1AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.audioPath = cleanAudio;
NSLog(#"Clean Audio (retrieveAudio): %#", appDelegate.audioPath);
return cleanAudio;
}
-(NSString *)retrieveText:(NSString *)message{
// self.audioPath = [NSURL URLWithString:#"TEST"];
RonnieD1AppDelegate *appDelegate = (RonnieD1AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.userText = message;
NSLog(#"Text: %#", appDelegate.userText);
NSLog(#"Clean Audio (retireveText): %#", appDelegate.audioPath);
return message;
}
//-(void)startEncrption{
// NSLog(#"Clean Audio (startEncryption): %# TEXT: %#", self.audioPath, self.userText);
// //self.audioPath = [NSURL URLWithString:#"TEST"];
// [self encrypterWithAudioFile:self.audioPath withString:self.userText];
//
//}
-(NSURL *)encrypterWithAudioFile:(NSURL *)audio withString:(NSString *)text{
NSLog(#"DIRTY AUDIO and TEXT: %#, %#", audio, text);
return audio;
}
-(void)dealloc{
[super dealloc];
}
#end
I know there are still some memory management issues with this but its working finally so I can figured that out myself. Thanks for all your help.

Struggling with memory management and create array method

I'm struggling to find the correct way to release an array after my method has been called. I wonder if there is a better way to achieve what I'm trying to acheive with my method:
- (NSArray *) setupDetailArray : (NSString *) selectedCategory {
// Load .plist file
NSString *path = [[NSBundle mainBundle] pathForResource:#"data" ofType:#"plist"];
// Load .plist into a new dictionary
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
// Drill down to next level
NSArray *faceSelection = [[NSArray alloc] initWithArray:[dict objectForKey:detailTitle]];
[dict release], dict = nil;
// Set up link to App Delegate
UltimateRageAppDelegate *dataCenter = (UltimateRageAppDelegate *) [[UIApplication sharedApplication] delegate];
dataCenter.faces = [[NSMutableArray alloc] init];
// Set app delegate faces to array
dataCenter.faces = faceSelection;
[dataCenter.faces release];
return faceSelection;
// [faceSelection release], faceSelection = nil; ??????
}
And I call my method in viewDidLoad
// If faceArray is empty, create it
if (faceArray == nil)
faceArray = [self setupDetailArray:detailTitle];
...
My application is leaking memory here, and I'm really looking for a way to release everything once I'm done.
Your method should return an autoreleased array which is then retained by the method that calls it if it wants/needs to keep it.
- (NSArray *) setupDetailArray : (NSString *) selectedCategory {
...
// Create the array, but don't own it
NSArray *faceSelection = [[[NSArray alloc] initWithArray:[dict objectForKey:detailTitle]] autorelease];
...
return facesSelected;
}
Now the code that calls this method should retain the object if it needs it. So, in your viewDidLoad
if (faceArray == nil)
faceArray = [[self setupDetailArray:detailTitle] retain];
...
If faceArray is an instance variable in your class, then you can just release it in your dealloc method.
You are also leaking memory here
// Set up link to App Delegate
UltimateRageAppDelegate *dataCenter = (UltimateRageAppDelegate *) [[UIApplication sharedApplication] delegate];
dataCenter.faces = [[NSMutableArray alloc] init];
// Set app delegate faces to array
dataCenter.faces = faceSelection;
[dataCenter.faces release];
This should be
// Set up link to App Delegate
UltimateRageAppDelegate *dataCenter = (UltimateRageAppDelegate *) [[UIApplication sharedApplication] delegate];
dataCenter.faces = faceSelection;
I'd suggest you read (and re-read and re-read) the docs on memory management and read up on properties, setters and the dot notation.
Apple Objective-C Memory Management
dataCenter.faces = [[NSMutableArray alloc] init];
You allocate a non-autoreleased array and assign it to the property faces (I bet it has the retain modifier).
dataCenter.faces = faceSelection;
Now you assign as new array to the faces property, but you haven't properly release the previous NSMutableArray.
[dataCenter.faces release];
You now indirectly release your faceSelection array.
You leak at least one NSMutableArray every time you run that method. You should do it like this instead:
// Drill down to next level
NSArray *faceSelection = [[dict objectForKey:detailTitle] copy];
[dict release], dict = nil;
// Set up link to App Delegate
UltimateRageAppDelegate *dataCenter = (UltimateRageAppDelegate *) [[UIApplication sharedApplication] delegate];
// Set app delegate faces to array
dataCenter.faces = faceSelection;
return [faceSelection autorelease];
Your method should return an autoreleased object. The only methods that should return retained objects are methods whose name:
starts with alloc
starts with new
contains copy
All other methods should return autoreleased objects.
Other way for doing this.
//Declare method as follows.
- (void) setupDetailArray : (NSString *) selectedCategory arrFaceArray:(NSArray *)faceArray
{
}
And I call my method in viewDidLoad
if (!faceArray)
{
faceArray = [[NSArray alloc] init]; //Alloc in ViewDidLoad and release in ViewDidUnload or dealloc.
faceArray = [self setupDetailArray:detailTitle arrFaceArray:faceArray];
}
Also consider the #DarkDust answer for maintaining autoreleased objects. Both are the possible ways.

Can you see anything wrong in this code?

NSString *path = [[NSBundle mainBundle] pathForResource:[arraySub objectAtIndex:indexPathHere.row] ofType:#"mp3"];
NSURL *file = [[NSURL alloc] initFileURLWithPath:path];
AVAudioPlayer *myPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:file error:nil];
self.player = myPlayer;
[player prepareToPlay];
[player setDelegate:self];
[self.player play];
NSTimeInterval lenghtMusic = player.duration;
if (player.currentTime == lenghtMusic) {
NSLog(#"It worked");
[tableThing deselectRowAtIndexPath:indexPathHere animated:YES];
[myPlayer autorelease];
[file autorelease];
}
Can you see anything wrong?
Apparently, the "if"-statement never gets called...
If I read your code correctly you are trying to find out when the player has finished playing and then you want to deselect the current rode and release the player. Your problem is that you start the player and then immediately checks where it is currently playing. Unless you have an almost 0 length file the currentTime and length will NEVER be equal.
If that is what you are trying to do you should use the AVAudioPlayerDelegate Protocol and especially the:
– audioPlayerDidFinishPlaying:successfully:
method that is called when the player is finished.
So you need to change your class that controls the player by editing the .h file to use the AVAudioPlayerDelegate Protocol. Obviously keep extending whatever you were before.
#interface YourClass <AVAudioPlayerDelegate>
#end
In your .m file:
Then when you have created and assigned your player instance:
[player setDelegate:self];
In the .m file also add the method
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
// Your success code goes here
}
You are comparing double's with ==, which is almost always false due to round-off errors.
Compare with a range e.g.
if (fabs(player.currentTime - lenghtMusic) < 0.0001) {
...
}
I'm a noob iPhone dev, but are you sure that player.currentTime is a NSTimeInterval? That looks like the most obvious candidate.
Also, I'd log out what your player.currentTime is and what lenghtMusic is before your if and that'll probably let you know what's going on.