AVAudioPlayer meter values still - 160 - objective-c

Trying to implement AVAudioplayer and get some metering data of the played music, but still getting value -160.
It looks easy to use, just enable Meter and then pickup data under a timer, but no results so far.
playerAV = [[AVAudioPlayer alloc] initWithContentsOfURL:outURL error:nil];
playerAV.delegate = self;
playerAV.meteringEnabled = YES;
[playerAV prepareToPlay];
[playerAV play];
NSLog(#"Peak left: %f Avg right: %f", [playerAV peakPowerForChannel:0],[playerAV averagePowerForChannel:1]);
any thoughts are welcome.

Are you calling updateMeters? The docs for both peakPowerForChannel: and averagePowerForChannel: say:
To obtain a current [...] value, you must call the updateMeters method before calling this method.

How do you know the url works, and the data at the end of it is the right format? Can you hear stuff coming from the speakers?
Are you somehow making two playerAV objects, one with the correct URL which plays, the other with a bad url which plays nothing, and is the one that the timer sees when it calls NSLog()?

Related

Issues Playing A Sound Multiple Times

I'm using SpriteKit for my Mac OS project with Objective C and I'm trying to play a certain sound over and over again when contact between two nodes occurs. I don't want the player to wait for the sound to complete before playing it again. The sound is only about 1 second long, but it repeats as fast as every 0.5 seconds. I've tried two different methods and they both have issues. I'm probably not setting something up correctly.
Method #1 - SKAction
I tried getting one of the sprites to play the sound using the following code:
[playBarNode runAction:[SKAction playSoundFileNamed:#"metronome" waitForCompletion:NO]];
The sound plays perfectly on time, but the sound is modified. It sounds like reverb (echo) was applied to it and it has lost a lot of volume as well.
Method #2 - AVAudioPlayer
Here's the code I used to set this up:
-(void) initAudio {
NSString *path = [NSString stringWithFormat:#"%#/metronome.mp3", [[NSBundle mainBundle] resourcePath]];
NSURL *metronomeSound = [NSURL fileURLWithPath:path];
_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:metronomeSound error:nil];
[_audioPlayer prepareToPlay];
}
Later on in code it is called like this:
[_audioPlayer play];
The issue with this one is that it seems to wait until it's completed playing the first time before playing the sound again. Basically it fails to play many times.
Am I setting this up incorrectly? How can I fix this? Thanks in advance.
Retry method 1, but instead of having the sprite play the sound, have the scene play the sound via
[self runAction:[SKAction playSoundFileNamed:#"metronome" waitForCompletion:NO]];
inside the game SKScene class

AVAudioPlayer on background thread

I know very little about using background threads, but this seems to play my sound in the way that I need it to as follows:
1) I need this very short sound effect to play repeatedly even if the sound overlaps.
2) I need the sound to be played perfectly on time.
3) I need the loading of the sound to not affect the on-screen graphics by stuttering.
I am currently just trying out this method with one sound, but if successful, I will roll it out to other sound effects that need the same treatment. My question is this: Am I using the background thread properly? Will there be any sort of memory leaks?
Here's the code:
-(void) playAudio {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSString *path = [NSString stringWithFormat:#"%#/metronome.mp3", [[NSBundle mainBundle] resourcePath]];
NSURL *metronomeSound = [NSURL fileURLWithPath:path];
_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:metronomeSound error:nil];
[_audioPlayer prepareToPlay];
[_audioPlayer play];
});
}
//handles collision detection
-(void) didBeginContact:(SKPhysicsContact *)contact {
uint32_t categoryA = contact.bodyA.categoryBitMask;
uint32_t categoryB = contact.bodyB.categoryBitMask;
if (categoryA == kLineCategory || categoryB == kLineCategory) {
NSLog(#"line contact");
[self playAudio];
}
}
I use the AVAudioPlayer and use it asynchronously and in background threads without any problems and no leaks as far as I can tell. However, I have implemented a singleton class that handles all the allocations and keeps an array of AVAudioPlayer instances that also play asynchronously as needed. If you need to play a sound repeatedly, you should allocate an AVAudioPlayer instance for every time you want to play it. In that case, latency will be negligible and you can even play the same sound simultaneously.
Concerning your strategy I think it needs some refinements, in particular if you want to prevent any delays. The main problem is always reading from disk, which is the slowest operation of all and your limiting step.
Thus, I would also implement an array of AVAudioPlayers each already initialized to play a specific sound, in particular if this sound is played often and repeatedly. You could remove those instances of players that are played less often from the array if memory starts to grow and reload them a few seconds before if you can tell which ones will be needed.
And one more thing... Don't forget to lock and unlock the array, if you are going to access it from multiple threads or better yet, create a GCD queue to handle all accesses to the array.

AVAudioPlayer Number Of Loops only taking effect after being played through once

I'm a little stumped by this weird occurrence:
I have a UIButton, which once tapped either sets a loop for an audio player, or resets it to 0 (no loop). Here is the method -
-(void)changeLoopValueForPlay:(int)tag toValue:(bool)value{
AVAudioPlayer *av = [self.playerArray objectAtIndex:tag];
if(value){
[av setNumberOfLoops:100];
[av prepareToPlay];
}
else{
[av setNumberOfLoops:0];
}
}
Now for some reason, the loop will only take effect after the player plays through the audio one time, meaning that the looping value doesn't take affect immediately, but the "numberOfLoops" value of the player is in fact set to 100 when I check its value before playing. I'm assuming this has something to do with the initialization or loading of the player, but I don't re-initialize it between those two plays (one without loop, the other with). Any idea why this is happening? If you want to see any other code please let me know.
This fixed the problem, however I feel as if this is a work-around instead of a direct solution. What I did is just create a new AVAudioPlayer with the numberOfLoops value set to whatever it is I wanted and replace that player with the existing player, instead of changing the value of the already existing player.
I workaround the issue by abandoning numberOfLoops altogether and doing my own logic instead.
First, set the delegate of the AVAudioPlayer:
self.audioPlayer.delegate = self;
Next, implement -audioPlayerDidFinishPlaying:successfully: of the delegate:
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer*)player successfully:(BOOL)flag
{
if(flag && <#(bool)i_want_to_repeat_playing#>)
{
[self.audioPlayer play];
}
}
Just replace <#(bool)i_want_to_repeat_playing#> with your desired logic, e.g., check if a counter has reached a certain threshold.

Is there any way i can group three classes?

What I want to do is I have many classes first of all, they all have the same music throughout, as i said in the app delegates bool application did finish launching method. But in my last 3 classes, I want different music, fair enough, I put these lines of code:
[(Smart2AppDelegate *)[UIApplication sharedApplication].delegate pauseAudioPlayer];
[(Smart2AppDelegate *)[UIApplication sharedApplication].delegate newAudioPlayer];
And in my app delegate:
-(void)newAudioPlayer {
NSString *music = [[NSBundle mainBundle]
pathForResource:#"win" ofType:#"m4a"];
audio.delegate = self;
self.audio = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:music] error:NULL];
[audio play];
audio.numberOfLoops = -1;
}
-(void)pauseAudioPlayer {
[audio pause];
}
So it works, whenever I go to that view, it changes music, lets call that view, view x. Now, from that view x I can go to and from only to 2 other views, e.g. I can go to the info page and there is a back button that leads back to that view x, and the same with a prize page. But when I go back to the view x, the music starts from the beginning, when in these three classes, i want them to all loop and not go from beginning because it sounds akward. The reason is simple it is because I put it in the viewDidLoad. But how can I do this, I was thinking of a way to actually group classes and put in the avaudioplayer method in there.
Here you have a possible approach:
Refactor out the music+sounds mechanisms into a separate class, i.e. something like Smart2AudioPlayer.
Public the necessary methods: play, pause, resume so you can use the audio player from anywhere.
In each viewDidLoad method, call the audio player and pass along a parameter to indicate who is the sender (who wants the sound to be played) and a preference indicating whether you wish to continue playing the current group song (see bellow) or start it all over again.
Implement the necessary logic in your audio player to allow certain groups of classes to be associated together. This way, when you are playing a song from a class that belongs to a group, and another class of the same group asks for the music to be played, you won't start the song again, you simply do nothing.
Hope this helps

Sound issue exiting

I have a problem where I have sound in Menu > Level one.
However when I exit Level one and go back to menu the sound won't stop!
What code do I need to terminate the sound?
This is the code I'm using:
- (IBAction) playsound {
NSString *path = [[NSBundle mainBundle] pathForResource:#"imsound" ofType:#"wav"];
AVAudioPlayer* myAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
myAudio.delegate = self;
myAudio.volume = 1.0;
myAudio.numberOfLoops = -1;
[myAudio play];
}
As the documentation states, -1 will cause it to play repeatedly until you send it a -stop. So the problem becomes "when should I tell it to stop." It's probably a good idea to do so (if it's playing) when you go back.
Because you don't hold a reference to the player (you're leaking it as soon as you leave the -playSound: method, you have no way to tell it to shut up. You should make it an instance variable / property so you can get to it whenever you need to. You're essentially pressing play then tossing the player into the back of someone's truck, then wanting it back so you can turn it off. Should've tied a string to it. ;-)
Proper memory management techniques and use of instance variables are the key causes of your current woes.