Audio Queues

Moderator
Posts: 3,577
Joined: 2003.06
Post: #76
Are you sure that the selector you specified in your notification registration is identical to the method being called? Example:

Code:
[[NSNotificationCenter defaultCenter] addObserver:self
        selector:@selector(trackFinishedPlaying:) // <-- make sure it matches method
        name:GBMusicTrackFinishedPlayingNotification
        object:nil];

and then:

Code:
- (void)trackFinishedPlaying:(NSNotification *)notification
{
    NSLog(@"trackFinishedPlaying");
}

I can't imagine why the notification wouldn't be received otherwise.
Quote this message in a reply
Member
Posts: 50
Joined: 2008.06
Post: #77
bruss14 Wrote:Yeah, I had that thought too.. I commented out the line where my class adds as a listener and then it doesn't crash. My app then (obviously) doesn't know the track is done playing and so the play animation continues. Then, after a small amount of time (20 seconds or so), The app closes and XCode tells me that the app closed normally.

Edit: Ug, never mind on the crashing anyway when that line is commented out. It only crashes if my class adds itself as a listener. I have a UIViewController that has an instance of my class that is the one controlling GBMusicTrack. Though now that I type all that out, I can probably just simplify and remove that class since it was there before my switch to GBMusicTrack... I'll give that a try and see what happens.

Wow, I feel a bit more than stupid... Well, when I was cleaning up my code, I realized I had left off the :(NSNotification *)notification parameter on my callback function for that listener... That has fixed the crash that I was getting with your new files. Thanks again!
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #78
Heh... Don't feel too bad, I've been bitten by that one a few times myself!
Quote this message in a reply
Member
Posts: 50
Joined: 2008.06
Post: #79
AnotherJake Wrote:Are you sure that the selector you specified in your notification registration is identical to the method being called? Example:

Code:
[[NSNotificationCenter defaultCenter] addObserver:self
        selector:@selector(trackFinishedPlaying:) // <-- make sure it matches method
        name:GBMusicTrackFinishedPlayingNotification
        object:nil];

and then:

Code:
- (void)trackFinishedPlaying:(NSNotification *)notification
{
    NSLog(@"trackFinishedPlaying");
}

I can't imagine why the notification wouldn't be received otherwise.

Nope, I wasn't. Wink I really appreciate the help a lot.

Edit: Darn! should have checked the forum a while before! You had this about 40 minutes before I did Wink
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #80
Apple sent me an email saying they think they fixed the Audio Queues memory leaks issue [with iPhone OS 2.2]. It certainly seems that they've improved the leak situation, but it seems I'm still getting this one persistent leak when I start a new track. It looks like it's 3.50 Kb and traces, beginning at [GBMusicTrack initWithPath:]->AudioQueueNewOutput ... and ends with pthread_create->malloc

Again, this appears to be pretty minor, but still, it appears to leak 3.5K sometimes when I start a new track.

I'm not sure what to update the report with just yet. The leaks don't appear to be resolved to me, but maybe this is a different leak? Can anybody else confirm that they're getting the same leak? Any other leaks?
Quote this message in a reply
Apprentice
Posts: 8
Joined: 2008.09
Post: #81
Any ideas how to fix the 2.2 shutoff bug on iphone. The sound stops when the phone goes into sleep mode.. anyone know how to still use this class and keep the sound playing?

I found this code but I am not sure where to implement it.

Code:
UInt32    sessionCategory = kAudioSessionCategory_MediaPlayback;
        AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory), &sessionCategory);
        AudioSessionSetActive(YES);

thanks
Quote this message in a reply
Apprentice
Posts: 8
Joined: 2008.09
Post: #82
Anyone has this problem? when the iphone goes to sleep the sound stops playing? it worked fine with 2.1 version and in 2.2 it stops playing.
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #83
I haven't had a chance to check this out yet (very busy developing other stuff), but I suspect that it is now correct behavior for audio to stop playing when the device goes to sleep, at least for default behavior. They should've done that before. I hope they still allow playback during sleep though.

You can set the audio session category whenever you want, but you'll also need to make sure you've inited the audio session first (only once) with something like AudioSessionInitialize(nil, nil, nil, nil);
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #84
mediashock, did you ever get this to work for you? I just noticed that Apple has a Technical Q&A (QA1626) which addresses this, and confirms that you now need to specifically set your session category to kAudioSessionCategory_MediaPlayback to continue to play audio when the device is put to sleep (or locked, or whatever they call it).
Quote this message in a reply
Apprentice
Posts: 11
Joined: 2008.09
Post: #85
Hey there AnotherJake. First off thank you very much for the GBMusicTrack code. It certainly got me further along quickly than when I was trying to figure out all this stuff myself. However, I was trying to make a modification and can't seem to get anything to work. I only need one sound loaded up at any one time (when I need to load up a new one it's after a bunch of user input so there's no way more than one sound would need to be loaded at once) and what I'm trying to do is add user enabled replay of the sound without have to destroy/reload all the queues, bufferes, files etc. For example, the way things work now is that once the buffer packet read returns 0, you asynchronously stop the Queue (I know you know this because you wrote it but I'm trying to focus on the stuff I've been paying most attention to Wink )

Code:
-(void)callbackForBuffer:(AudioQueueBufferRef)buffer
{
...
...
   if( repeat )
   {...}
   else
   {  //no repeat
      AudioQueueStop(queue, NO);
      trackEnded = YES;
   }
}

Then up in the propertyListenerCallback method a state change method is called and all the stuff is gotten rid of

Code:
- (void)playBackIsRunningStateChanged
{
    if (trackEnded)
    {
        // go ahead and close the track now
        trackClosed = YES;
        AudioQueueDispose(queue, YES);
        AudioFileClose(audioFile);
        gThereIsAnActiveTrack = NO;
        
        // we're not in the main thread during this callback, so enqueue a message on the main thread to post notification
        // that we're done, or else the notification will have to be handled in this thread, making things more difficult
        [self performSelectorOnMainThread:@selector(postTrackFinishedPlayingNotification:) withObject:nil waitUntilDone:NO];
    }
}

What i've been trying to do is combine the repeat code and not destroy all of the queues upon state change...so now the code looks like:

Code:
-(void)callbackForBuffer:(AudioQueueBufferRef)buffer
{
...
...
   if( repeat )
   {...}
   else
   {  //no repeat
      AudioQueueStop(queue, NO);
      packetIndex = 0;
      [self readPacketsIntoBuffer:buffer];

      trackEnded = YES;
   }
}

Then back up in the playBackIsRunningStateChanged I handle all of the local boolean variables and dont destroy any of the Queues. When I try to replay off a button press, the AudioQueueStart method doesn't return an error message, however nothing is heard. With no error message it's hard to figure out exactly what is going on but I'll assume it's another one of those tricky AudioQueue things that the docs aren't that helpful with. Is there anyway to do user repeat (not necessarily waiting for the song to end, but during the middle) the song without destroying everything? The delay and memory leaks worry me.

Thank you. Let me know if there's anything I need to clarify.
-Mo
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #86
I wouldn't worry about the Audio Queue leaks, as Apple has fixed most of that in 2.2, and I've heard no reports of app rejections from them.

The gap in between loops of compressed audio seems to be unavoidable, as far as I can tell.

Yeah, you should be able to repeat without losing your audio queue. I am not 100% sure I understand what you're trying to do, but that obviously isn't what I had in mind when I put it together. Wink

I'm not sure off-hand how I'd handle the track ending for what you're doing, but yeah, I wouldn't close it in the property listener callback if you want to keep it around (as you already know).

You might want to rearrange some things and maybe prime the buffers during play, rather than init. Here's an idea for you to work with, which I think should work to restart the song anywhere and start it playing again:

Code:
- (void)rewindAndPlay
{
    if (trackClosed)
        return;
    
    AudioQueueStop(queue, YES);
    packetIndex = 0;
    for (int i = 0; i < NUM_QUEUE_BUFFERS; i++)
    {
        if ([self readPacketsIntoBuffer:buffers[i]] == 0)
            break;
    }
    AudioQueueStart(queue, nil);
}
Quote this message in a reply
Apprentice
Posts: 11
Joined: 2008.09
Post: #87
Hey there AnotherJake, thanks for the reply. I apologize if I wasn't all clear about what I was up to but I've just discovered the joy of AudioQueues recently Wink. Here's exactly what's going on with the app and what I'm trying to do. The user is looking at a picture of some kind of animal with a button for listening to the animal's "sound effect" (bark, chirp, whatever). Every time the user presses that button I need to start the sound right away. What I want to avoid is having to reload up the GBMusicTrack object and queues, etc, every time the user presses the button. The files aren't that big but I'm trying to avoid delays and memory fragging (even if Apple doesn't kick for them heh). It'll have to be loaded once, but that's fine because it gets loaded up with all the rest of the animal data before a transition screen. The sound won't be looped because it's annoying to hear the sound more than once. But I assumed if the sound could be looped it could be started and stopped at will without having to reload, despite the fact that the AudioQueues docs tell you to unload everything after each play (seems like a silly design issue). I put the code you added into the but it didn't seem to take care of the issue. One time it played the sound correctly, but every other time I would just hear nothing, sometimes getting an error message of -50 ( paramErr = -50, /*error in user parameter list*/) at the AudioQueueStart method call. Our app is due next Tuesday, so if I have to I can implement the whole destroy/reload version but I'm worried about delays. And we can't use OpenAL because there are over 120 animals in this app and if we used uncompressed formats that OpenAL supports our app would be, lets say, FREAKIN' HUGE!! Smile
Anyway, I hope that makes more sense. Hopefully i'm not alone in needing this functionality heh. Thanx again
Mo
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #88
OpenAL would obviously be ideal, but yeah, I don't know how to decompress off the disk either. It seems like some of the newer audio API calls I've seen include some utilities which might work for that but I haven't had an ounce of time to explore there.

I'd say I'm surprised that little snippet I posted to rewind and play doesn't work right for you (seems to work for me, go figure), but I guess that's the way it is with this stuff.

I wish I had some time to help out on this right now, but I'm totally tied down, darn it... Sad
Quote this message in a reply
Apprentice
Posts: 11
Joined: 2008.09
Post: #89
Yeah, OpenAl would be ideal for sure, but the client wants 30 second+ audio clips, which multiplied by over 100 is insane. I'm surprised that the code snipped didn't work either because I've seen similiar stuff elsewhere. Maybe it would only work on the device, not the simulator? Either way if I have to just destroy everything and reload it we'll survive, it's not like it's a video game sound effect. Still, it almost feels like a matter of principle to get this stuff working the way it should. I appreciate any help you can give. I'll let you know if I actually come up with anything as well.
Thanx again
MoP
Quote this message in a reply
Apprentice
Posts: 5
Joined: 2008.09
Post: #90
Hi AnotherJake,

Thanks for this code. We found a tricky bug though, maybe you can help ( next time you aren't so busy Wink ), or can confirm that it doesn't only happen to us.

It seems to come from selectively allowing the iPod music to play over the top of the App. The steps to produce it are: start the iPod music, then start the app (detect the music and allow it to keep playing instead of starting the apps own AudioQueue music), then exit from the app, turn off the iPod music, and then restart the app. Using the GBMusicTrack as is, when the app restarts and tries to play it's own music, it freezes the device.

It can be made less nasty by adding some code to trap an error when priming the buffer (this is the only stage of the AudioQueue initialization code that seems to give any warning or error). All that happens then is that the music doesn't start, the device doesn't lock up anymore... But you do still seem to need to restart the device to get back the ability for the app to start it's own AudioQueue music.

Something like this should trap the priming error:-

Code:
-(void)play
{
    OSStatus result = AudioQueuePrime(queue, 1, nil);    
    if (result)
    {
        // Error priming AudioQueue
        // undocumented result
        return;
    }
    AudioQueueStart(queue, nil);
}

Any thoughts or suggestions? I've spent hours messing with this, but this is the best improvement I've managed so far.

Cheers.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Core Audio mutlichannel audio tachyon 2 6,294 Mar 18, 2005 01:04 AM
Last Post: tachyon