Audio Queues

Moderator
Posts: 3,572
Joined: 2003.06
Post: #121
Always glad to help Smile

Yes, the docs and example code from Apple for Audio Queues weren't easy to weed through. GBMusicTrack is pretty simplified and ties a bunch of concepts from their sample code together, so it should help you see what's going on a little better. Still, there might be a couple subtle things going on, like a notification being passed to the main thread. You should still read the Audio Queue docs carefully if you really want to pick it apart.
Quote this message in a reply
Nibbie
Posts: 2
Joined: 2009.04
Post: #122
I've tried going through the documentation and sample code a couple of times, but still getting used to Cocoa and Objective-C makes it even more difficult. On top of that, my plain C isn't what it used to be (and it didn't used to be all that either Wink). I noticed the notification going on; not a problem, I know how those work. It's great to have a working example with all the extra stuff ripped out.

One reason for using AudioQueue was support for earlier iPhone firmware versions. You say audio is notoriously buggy in earlier versions. Is AudioQueue fixed in the most current firmware? If I'm not going to release for earlier versions, I might as well keep using AVAudioFoundation as long as I don't need low-level features then.

Nevertheless, a great exercise for when I do need it. Smile
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #123
Well, I don't know if Audio Queues really had many problems itself except for memory leaks in earlier versions (which is problematic enough I suppose), but yes, audio in general is more reliable in newer OS versions. Highly recommended to stick to newer versions if you can. Plus, like you said, there's no need to mess with the lower level stuff if you can use AVAudioFoundation.
Quote this message in a reply
Nibbie
Posts: 1
Joined: 2009.05
Post: #124
Hello everyone. I'm writing a game for iphone, and without background music it runs smoothly at 30 fps. But if I add music (using GBMusicTrack or AVAudioPlayer, both give similar effect), framerate periodically drops to 10 (about once per second), and then returns to 30. Music is mp3@128kps, 44kHz. It degrades performance not constantly, but at certain moments in time, which causes very jerky gameplay. Did anyone meet such problem? Is there any way to make cpu load for mp3 decoding/playing back more uniform? I'd rather have constanly 29 fps than 30 fps most of the time and 10 once per second.
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #125
How odd! No, I haven't heard of anything like that happening. The only thing I can imagine which might conflict is perhaps if you might be throttling your application to be running at 30 fps, maybe with a while loop. If you just have a timer set to operate it at 30 fps, or if it's set higher and the app is just bogging down naturally to 30 fps then I really have no idea off-hand how it could be affected by the music loading/decoding.
Quote this message in a reply
Nibbie
Posts: 2
Joined: 2009.08
Post: #126
Hi guys,
thank you for the GREAT code, really helped me a lot.

I just CANNOT stop this mp3. I'd like to have a stop/play btn but when I create the action with

[musica stop];

I get a error: 'musica' undeclared (first use in this function)

This is my code:

-(void)suonaBG
{
GBMusicTrack *musica = [[GBMusicTrack alloc] initWithPath:[[NSBundle mainBundle] pathForResource:@"Greve03" ofType:@"mp3"]];
[musica setRepeat:YES];
[musica setGain: 0.9f];
[musica play];
}

and I start it with: [self suonaBG];


Please help me.

Thank you


Lamberto
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #127
You have GBMusicTrack *musica declared and defined in the suonaBG method. You should declare musica in your interface instead, so you have access to it from elsewhere. Like:

Code:
@class GBMusicTrack;

@interface MyClass : NSObject
{
    GBMusicTrack    *musica;
}

@end

Then:
Code:
-(void)suonaBG
{
    musica = [[GBMusicTrack alloc] initWithPath:[[NSBundle mainBundle] pathForResource:@"Greve03" ofType:@"mp3"]];
    [musica setRepeat:YES];
    [musica setGain: 0.9f];
    [musica play];    
}

To stop:
[musica pause];

To resume:
[musica play];

Then later to stop completely before starting a different track:
[musica close];
[musica release];
Quote this message in a reply
Nibbie
Posts: 2
Joined: 2009.08
Post: #128
And thank you so much for your kind reply, I just got home...

That's what i did following (?) your suggestion:

In a RootViewController.h I put:
@class GBMusicTrack; at the beginning and in the @interface I put GBMusicTrack *musica;

In the RootViewController.m I put:
#import "GBMusicTrack.h"
#import "RootViewController.h"
and in the - (void)viewDidLoad { I put:
musica = [[GBMusicTrack alloc] initWithPath:[[NSBundle mainBundle] pathForResource:@"Greve03" ofType:@"mp3"]];
[musica setRepeat:YES];
[musica setGain: 0.9f];
[musica play];

and then a:
-(void)stoppa{
NSLog( @"stoppa");
[musica pause];
}

AND IT WORKS!

But now I'd like to call [musica pause]; in ANOTHER file (mediaView.m)

In tried in mediaView.m to put:
#import "RootViewController.h"
#import "GBMusicTrack.h"

hoping it would find the way home to what I declared there but without any luck, still says "error:'musica' undeclared (first use in this function)

What am i missing??

Thank you so much, if you ever need some help in Flash just let me know.


Lamberto
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #129
lucciorebus Wrote:What am i missing??

Hmm... There are a few ways to go about doing this. One way is to provide an accessor in RootViewController so that mediaView can call and get the instance of musica. This isn't so easy to explain because mediaView has to know about RootViewController first, which can be done several different ways, depending on your design. It would maintain strict object-orientation though, so would be ideal I suppose.

An alternative, which is not object-oriented, but much easier, would be to make musica a global variable. This would actually make sense to do in this case, since you can only ever have one compressed music track playing at a time on iPhone. Here's one way you can do it:

In RootViewController.m:
Code:
#import "GBMusicTrack.h"

GBMusicTrack *musica;

@implementation RootViewController
...

Then in RootViewController.h:
Code:
#import <UIKit/UIKit.h>

@class GBMusicTrack;

extern GBMusicTrack *musica;

@interface RootViewController : UIViewController {
...

Now all you have to do to access musica from any place in your program is do #import "RootViewController.h". BTW, you can put "extern GBMusicTrack *musica;" anywhere, really, it doesn't have to be in RootViewController.h.

Globals are fun! Smile
Quote this message in a reply
Member
Posts: 93
Joined: 2008.11
Post: #130
Hi All,

My question is mainly for AnotherJake..
I have written a plugable sound engine which supports ogg and others. The "others" are supported through a slightly adjusted version of GBMusicTrack.

Everything looks fine but the propertyListenerCallback won't work as expected.
I receive a notification when I manually call Start on the queue and probably I will get a notification when I will manually call Stop. However I would expect to get a notification when the sound stops playing from the speaker. That's also written in the Audio Queue Service reference for MacOSX. For the iPhone reference I noticed a little change.. they say that you will get a notification on Stop/Start however they don't specify whether it's a manually called one or not.

1) Am I correct that on the iPhone the notification is not working as on MacOSX?
2) What is the right way to get notified exactly on the second when the music stops playing?

Note that looping music is not a big problem for me because that works with GBMusicTrack already. I just need to know when the music playback is actually finished.

Also, does someone know how to remove the gap in a looping mp3? I was thinking about creating two queues (like in a stream example), prime them both with same mp3 data and then switch between them on each loop iteration to prevent the gap. One could also schedule a start time for the second queue so that it would start directly. How about that?

Thanks!
Alex

TapMania - iPhone StepMania // Human knowledge belongs to the world!
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #131
I don't quite recall what the differences in behavior of the property listener callback were between OS X and iPhone. As far as I remember, at least on iPhone, the property listener callback for kAudioQueueProperty_IsRunning happens when the music stops playing from the speakers, but it only gets called at the end of the track. That is useful because there is a slight delay between buffering the last buffer and when it actually ends playing, so it's really the only way to be notified the instant the track actually ends playback (at the end of the track). That notification at the end of the track should work the same on iPhone or OS X. I don't recall if it gets called on stop or any other time on OS X or not, but I'm pretty sure it doesn't on iPhone -- only end of track. If you call stop yourself with the immediate flag set to YES (or close or pause on GBMusicTrack) then it should instantly stop playing from the speakers so you could just as well generate your own notification at that time if you can't get a notification for a manual stop otherwise.

Quote:Also, does someone know how to remove the gap in a looping mp3? I was thinking about creating two queues (like in a stream example), prime them both with same mp3 data and then switch between them on each loop iteration to prevent the gap. One could also schedule a start time for the second queue so that it would start directly. How about that?

You might get that to work on OS X if you could get your timing down, but it won't work on iPhone, since when you touch Audio Queues with any compressed music it grabs the hardware, and two compressed tracks touching hardware on iPhone is a bad thing, and could freeze the device.

I still haven't heard of any effective way to eliminate that mp3 loop gap. Sad
Quote this message in a reply
Member
Posts: 93
Joined: 2008.11
Post: #132
AnotherJake Wrote:That notification at the end of the track should work the same on iPhone or OS X. I don't recall if it gets called on stop or any other time on OS X or not, but I'm pretty sure it doesn't on iPhone -- only end of track. If you call stop yourself with the immediate flag set to YES (or close or pause on GBMusicTrack) then it should instantly stop playing from the speakers so you could just as well generate your own notification at that time if you can't get a notification for a manual stop otherwise.

Thanks. However it doesn't invoke the callback when the sound actually stops playing. It does only when you manually call stop/start queue. That's at least what I'm experiencing.. I didn't had any chance to work on this issue since the initial post but it just seems to be like that. I have to try that again in a simple demo app.

Will let you know if I find a solution.

Regards,
Alex

TapMania - iPhone StepMania // Human knowledge belongs to the world!
Quote this message in a reply
Nibbie
Posts: 1
Joined: 2009.10
Post: #133
I am very glad to know about this post.I'm also kind of waiting to see what happens with the next iPhone OS update, because there are some serious issues (like silent switch problems relating to Audio Queues and incompatibilities with the iPod app (or iTunes, or whatever it's called).Thanks
Quote this message in a reply
Nibbie
Posts: 2
Joined: 2010.11
Post: #134
Hi AnotherJake,

+1 to the big thank you pile from me for GBMusicTrack. I'm still using it because I'm preparing an OS X title. On iOS, I use the newer Apple APIs.

Potential bug fix for you: -

I was getting a crash in propertyListenerCallback due to inUserData (which is of course the pointer to the instance of GBMusicTrack being accessed after the track had been dealloc'ed in my client code. My client code was deleting the GBMusicTrack instance and creating a new one when I wanted to play a new track.

I solved this by removing the property listener callback in your close function, as below: -

(I have marked the added line by surrounding with comments)

Code:
- (void)close
{
    // it is preferrable to call close first, if there is a problem waiting for an autorelease
    if (trackClosed)
        return;
    trackClosed = YES;

    // *************addition begins***********
    AudioQueueRemovePropertyListener(queue, kAudioQueueProperty_IsRunning, propertyListenerCallback, self);
    // *************addition ends*************

    AudioQueueStop(queue, YES); // <-- YES means stop immediately
    AudioQueueDispose(queue, YES);
    AudioFileClose(audioFile);
    gThereIsAnActiveTrack = NO;
}

Since dealloc calls close, removing the listener, this problem was solved.

Cheers
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #135
Hmm... I hadn't thought about that. I assumed Audio Queues would do the house-keeping for the listener behind the scenes. I hadn't kept up with it on the Mac since I now use Ogg/Vorbis through Remote IO there. I still use AVAudioPlayer on iOS though, as everyone should. Incredibly, I still get emails on this once in a while, so I know there are still a few folks out there who use it, even on iOS, for various reasons, so thanks for contributing your potential fix! Smile
Quote this message in a reply
Post Reply 

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