Does each individual sound require an instance of AVAudioPlayer? - objective-c

I have a few game sounds, such as four different beep tones, and each one is played depending on the action the user performs. Do I need to instantiate four different AVAudioPlayer objects?
Or do I just need one main player? If I just need one, then how do I switch the file URL?
This is what I am doing now:
NSError *error;
soundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:markedSound error:&error];
but it seems to take a few seconds to initialize. Is there a way to preload the files and switch between them quickly?

Continue creating as many instance as you have sounds.
After initialization, use the method prepareToPlay:
Prepares the audio player for playback by preloading its buffers.
Discussion Calling this method preloads buffers and acquires the audio
hardware needed for playback, which minimizes the lag between calling
the play method and the start of sound output.
Calling the stop method, or allowing a sound to finish playing, undoes
this setup.
For example:
NSError *error;
soundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:markedSound error:&error];
[soundPlayer prepareToPlay];
For the moments, you only need 4 songs, so this would be premature optimization. But if later, the number of sounds you need increase a lot, you can wrap all this in a sub-class and do lazy loading. As the documentation states, using the stop method is the equivalent of unloading your file.

You don't necessarily need to instantiate four different AVAudioPlayer objects, although this is one option (that may be simplest if you only have four sounds).
One you could make would be to load the sounds into memory in advance, which you can do by using the initWithData method instead of the initWithContentsOfURL (having loaded all your sound files in advance to NSData objects). This may speed things up slightly, although it also depends on your sound format. Uncompressed files will obviously be loaded much quicker than an MP3, for example.
If this still doesn't work out for you, your best bet would be to look at some of the lower level audio technologies provided, which are more suited for games work (OpenAL, Audio Toolbox, etc), but these are a fair bit more complex than a simple AVVAudioPlayer.

Related

Long delay with NSFileCoordinator coordinateWritingItemAtURL

I'm setting up NSFileCoordinator and NSFilePresenter in my app so I can do file IO from my AppleWatch app safely. There are some places in my code where I write to a file a couple of times in quick succession. This is a problem in and of itself and I'm working to correct it, but I'm noticing some weird behavior in the process.
I wrap my writes like this:
//In a class that implements NSFilePresenter:
NSFileCoordinator *coord = [[NSFileCoordinator alloc]initWithFilePresenter:self];
[coord coordinateWritingItemAtURL:self.presentedItemUrl options:0 error:nil byAccessor:^(NSURL *url)
{
//do my writing here using CFWriteStreamRef or NSOutputStream
}];
On the first write, the write block happens within 1 ms. But after that, there's about a 0.5 second delay between calling coordinateWritingItemAtURL and the write block being executed.
Is this expected behavior?
Some of the documentation for NSFileCoordinator and NSFilePresenter says to use prepareForReadingItemsAtURLs:writingItemsAtURLs:options:error:byAccessor: for batch operations, but it seems weird to get such a long delay when I don't batch.
Update: This happens with reading too.
Update 2: Here is an example project reproducing the problem.
Update 3: Using this API for coordination between an app and its extension is apparently a bad idea. But the question still stands.
Referring to File System Programming Guide , you can read following:
you might want to avoid incorporating changes directly from your file
presenter method. Instead, dispatch a block asynchronously to a
dispatch queue and process the changes at a later time. This lets you
process the changes at your app’s convenience without causing
unnecessary delays to the file coordinator that initiated the change.
Of course, when saving or relinquishing control of a file (such as in
the relinquishPresentedItemToReader:,
relinquishPresentedItemToWriter:, or
savePresentedItemChangesWithCompletionHandler: methods) you should
perform all necessary actions immediately and not defer them.
I think this is your case where you are defering actions.
Possible Solution:
Please read this well , to properly handle multiple successive writing operations , the relinquishPresentedItemToWriter , can do the job , same will work with reading file , relinquishPresentedItemToReader , supposing that multiple different objects are trying to read and write the same file.
P.S :
I dont know what your app does exactly , but i hope you have read this :
If you are implementing a document-based app, you do not need to
incorporate file presenter semantics into your NSDocument subclasses.
The NSDocument class already conforms to the NSFilePresenter protocol
and implements the appropriate methods. Thus, all of your documents
automatically register themselves as presenters of their corresponding
file and do things like save changes and track changes to the
document.
Is it possible to use options NSFileCoordinatorReadingImmediatelyAvailableMetadataOnly for reading and NSFileCoordinatorWritingContentIndependentMetadataOnly for writing in some cases? Looks like this iOS8 options can help you.

Simple Communication between Objects NSNotification/Delegates or similar?

I just started with Objective C and have a question concerning communication between two objects.
Say I have an Object, let's call it aTimcode instantiated from main() from class "Timecode" which is a class that stores a Timecode (01:30:20:10 for example) and has methods to de/increase this timecode and some more methods that do math with Timecodes.
I have a second Object, lets call it aVideo from Class "Video" that does some (openCV) stuff on a video, also created in main().
so my main() looks like:
Timecode *aTimecode = [[Timecode alloc] init];
Video *aVideo = [[Video alloc] init];
[aVideo doFancyOpenCVSTUFF: someparameter];
I now want aVideo to call methods on aTimecode. something like
[aTimecode increaseFramesBy: 5];
I know the easy way to establish communication would be to instantiate aTimecode from within aVideo with:
// somewhere within aVideo
Timecode * aTimceode = [[Timecode alloc] init];
[aTimecode increaseFramesBy: 5];
[aTimecode release];
but of course this will destroy the information stored in aTimecode when its released.
but when the program goes further I want a lot of Video objects (bVideo, cVideo, etc...) to contribute to the values in aTimecode and do math with the previously stored numbers. aTimecode should store them even when aVideo gets released and bVideo starts doing some stuff.
What's the right way to communicate? Delegates? NSNotification?
There could be the situation that for example aVideo sends a lot of messages to aTimecode - 10 times a second or more often. What's the right way for sending so many messages in short time?
From what you've described, it would seem simplest to create an initWithTimecode: method in the Video class.
This assumes that the Video uses Timecode methods as opposed to methods that might be part of Timecode or might be part of something else. If that's wrong and you need to pass in various object types, then you would use a protocol and a delegate.
Notifications would be more appropriate if the Video is just declaring that certain events have happened and doesn't care whether any other object (or many) is taking notice of them.
The Timecode object not necessarily has to be released if it is a parameter of a certain Video object. You might want to create a singleton class that keeps the Timecode object in it and pass a weak reference to it to all the created Video objects, after that you can either use KVO, NSNotifications or delegates, whichever you prefer the most. Delegation, though, would seem to be the clearest solution here.

Best design for assigning CoreLocation & CoreMotion updates through multithreading?

I have a Game class that has a -(void) play method which will be executed when the user clicks on the Play button on the device.
Inside the -(void) play method I have a while loop that will be executed repeatedly until the user clicks on the Quit button. This while loop is basically the core of my code, where all necessary methods are being called, things happen, objects interact etc.
I also have a User class (amongst other classes..) and I create a User* player instance in the -(void) play method of my Game class to store some values and have those interact with other things along the duration of the game..
Now I need to know (at any moment during the game..) the device's deviation from the magnetic North & the acceleration the user is exercising on the device
I've written the code and everything is working fine. However, being new to programming I have a few questions concerning the overall design of my code, which I think is a mess especially when it comes to using the CoreLocation & CoreMotion frameworks..
The -(void) play method of the Game class (which is basically my "main" method) is executed on a separate thread as in [game performSelectorInBackground:#selector(play) withObject:nil]; Is this the right way to do it?
However, I initialise CoreMotion Acceleration updates from inside the -(void) play method as in [motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]withHandler:^(CMDeviceMotion *motion, NSError *error){...} which means that updates will be stored in the main Queue whereas the method initializing these updates is executed from inside a method(-(void) play) that runs on a separate thread. Does that make sense?
I also initialise CoreLocation updates when I initialize an instance of my Game class. Even more weird?
My point is this. Given that I'll be measuring the acceleration the user is exercising on the device and the orientation he/she is giving to the device (degrees) I want to encapsulate all that in my User class and have methods like [player getMyDegrees]; and [player getMyAcceleration]; Isn't this the correct way design-wise? Where exactly should I initialize those updates? From inside which specific class-method? Should everything be running on the same main thread or the same separate thread or on different separate threads? I'm confused..
Threading is a complex issue, as you've no doubt seen.
The -(void) play method of the Game class (which is basically my
"main" method) is executed on a separate thread as in [game
performSelectorInBackground:#selector(play) withObject:nil]; Is this
the right way to do it?
It depends. Is there any work that you are doing in the -play method that absolutely must be taken off the main thread (i.e. crazy number crunching, server requests, loading textures, etc.) so as to make the UI more responsive? That's really the only reason methods are dispatched to the background on iOS, is to maintain a responsive UI, because UIKit classes are not just thread-unsafe, but they will wholeheartedly dive straight off a cliff and take your app with them if you try to do anything in the background.
However, I initialise CoreMotion Acceleration updates from inside the
-(void) play method ... which means... these updates are executed from inside a method(-(void) play) that runs on a separate thread. Does
that make sense?
Well, again it depends here. The ultimate goal of threading is not to have two or more threads attempt to access or mutate one object held by another thread, which is just the start of a whole mess of problems. The one thing that I have to take issue with is the fact that you are using the main queue despite a very clear and ominous warning in the documentation:
Because the processed events might arrive at a high rate, using the
main operation queue is not recommended.
This is an example of one of those high powered calculations that you should be throwing to a background thread, which is NSOperationQueue's forte.
I want to encapsulate all that in my User class and have methods like
[player getMyDegrees]; and [player getMyAcceleration]; Isn't this the
correct way design-wise? Where exactly should I initialize those
updates? From inside which specific class-method?
It depends on how design-wise you're trying to be, and what you consider good design. Good code is usually code that you could hand off to another programmer and have them pick up where you started off with very little effort. That being said, it sounds like your user class is going to be the controller part of MVC. If your classes are too bulky, cut 'em up. Classes in objective-c are surprisingly light-weight creatures. Even arranging one class in multiple categories that describe the general section's implementation are fine (the header of NSString for example).
Should everything be running on the same main thread or the same
separate thread or on different separate threads? I'm confused..
Everything UI and light should be running in the main thread (or main queue if that's how you prefer to look at it), and everything heavy and thread-blocking should be put in a background thread.

Reusing an AVAudioPlayer for a different sound

I have an instance of AVAudioPlayer that will play several sounds. Is there a way to provide an AVAudioPlayer instance with a new sound, or do I have to create a new instance with initWithData:?
There's no API to pass a new file to an existing AVAudioPlayer instance. However, AVAudioPlayer creation isn't expensive, so you shouldn't be concerned about performance, as long as you release instances once you're done with them.
If you have a fixed set of sounds that play over each other, or are likely to play repeatedly, you can create one AVAudioPlayer instance for each sound and re-use those instances by calling play on the appropriate instance.
If only one sound is playing at a time, and you're generating new sounds (for instance with text-to-speech based on user input), I generally have a single player as a property. When a new sound plays, I call [player stop], release the old player, and instantiate a new one with the new sound file.
Reusing objects is usually a bad idea, generally try to have your objects as short-lived as possible. This reduces the number of possible states of your code and makes reasoning about it easier. (“Where did this value come from? Maybe it’s a leftover from the previous instance usage?” Yuck.) Only break this rule once you run into measurable problems.
For further reading along this line of thought you can browse questions tagged “immutable”.

iOS writing to asset library observations

I am attempting to write an app that reads images from the asset library, modifies the image's GPS data and writes it back to the asset library. I store the assets in a mutableArray via the "enumerating assets" methods. Most of the details on how to do the various steps, I got from searching this forum. Thanks!
I have found that when I write the first "asset" via the "writeimagedatatosavedphotosalbum" method, all the elements of the mutableArray associated with the assets' URL became null. Furthermore, I noticed that writing back an image does not replace the original image, but instead creates a second instance of the image.
Just thought I'd pass these results along, in case others had questions. And, of course, I'd be interested in other's comments, observations, etc.
This forum has provided me with great information. Thanks again.
Your ALAsset object is only as good for the amount of time that your ALAssetsLibrary object is around. You either need to do everything you want in the completion block when you get the ALAsset, or store the ALAssetsLibrary in an instance variable so ARC does not deallocate it.
An ALAsset is essentially a Core Data object who can have properties accessed from multiple threads but a NSManagedObject or a subclass of NSManagedObject does not make sense without a parent NSManagedObjectContext much in the same way an ALAsset doesn't make sense without an ALAssetsLibrary.
It is common practice to store the NSManagedObjectContext on the AppDelegate; and while I abstract that functionality into a wrapper/singleton there is a retained reference to the NSManagedObjectContext throughout the app lifecycle. Apply the same logic to the ALAssetsLibrary and everything will works as expected.