Implementing sound

Moderator
Posts: 434
Joined: 2002.09
Post: #16
Quote:Originally posted by kelvin
well... since I don't really have any qualms about CBMovie (as it's not really integral to CB) I'll post the source here for you Smile as a favor though, I would like for you to write up a more professional looking tutorial perhaps. Something that is easier to read and more permanent than a forum post. Maybe even an iDG article... You can use my source files, just put credit where it's due.

CBMovie source

watch those fixed precision values!


Thanks.

Before I saw your reply I wrote my own QT callback, based on Apple source code. Everything seems to work except the callback never gets called! I was trying to trigger on end of movie, and no errors are reported by any of the setup calls, but I get nothing.

Looking at your source I noticed that CBMovie is triggering by duration. I then read somewhere that duration was more reliable because it wouldn't trigger if the track stopped somewhere other than the end. Not an issue for me, nonetheless I gave it a go and my callback STILL isn't triggered!

Another evening down the drain. Mad Tomorrow I suppose I'll try using instantiating a CBMovie and using it.

Two minor comments on CBMovie's code: you are calculating the end time twice in a row, the call to GetTimeBaseStopTime replacing the previously computed value. Unintentional but no harm done. Slightly odder to me is that my documentation suggests that you are supposed to create a UPP for the (pascal) callback but instead you are passing your © callback function directly. I tried it both ways, doesn't seem to have anything to do with my problem though.

Quote:You know, I looked at your code last night but for some reason I put it away again.
Now I remember why I put it down... looping isn't implemented yet as far as I can see. So no matter which way I did it I still needed to implement looping.

Hopefully this will all make a nice FAQ entry one day. I'll be happy to set that up myself, if I ever get this bloody thing working.

I'll post an extract from my code on the off-chance that someone sees a possible reason for the lack of callback. (I say there's no callback because my breakpoint there is never reached and the NSLog command I put in there doesn't fire.) This is in an Obj-C++ (.mm) file if that matters.
The if-def'd out version is my end-of-track version of the callback.
Code:
pascal void LoopCurrentTrackCallBack(QTCallBack cb, long refCon);

OSErr SetUpTriggerAtStopCallBack(GameSoundNSMovie *myMovie)
{
    OSErr err = paramErr;

    TimeValue endTime;
    Movie theMovie = myMovie->GetQTMovie();
    
    endTime = GetTimeBaseStopTime (GetMovieTimeBase ( theMovie ),
    GetMovieTimeScale ( theMovie ), nil);
    
    myMovie->myQTCallBack = NewCallBack(GetMovieTimeBase(theMovie),
                                        callBackAtTime);
    if ( myMovie->myQTCallBack )
    {
        myMovie->myQTCallBackUPP = NewQTCallBackUPP(LoopCurrentTrackCallBack);
        err = CallMeWhen(myMovie->myQTCallBack, myMovie->myQTCallBackUPP,
                         (long)myMovie, triggerTimeFwd, endTime,
                         GetMovieTimeScale ( theMovie ));
        
    }
    return err;
}

#if 0
OSErr SetUpTriggerAtStopCallBack(GameSoundNSMovie *myMovie)
{
    OSErr err = paramErr;

    Movie theMovie = myMovie->GetQTMovie();
    
    myMovie->myQTCallBack = NewCallBack(GetMovieTimeBase(theMovie),
                                        callBackAtExtremes);
    if ( myMovie->myQTCallBack )
    {
        myMovie->myQTCallBackUPP = NewQTCallBackUPP(LoopCurrentTrackCallBack);
        err = CallMeWhen(myMovie->myQTCallBack, myMovie->myQTCallBackUPP,
                         (long)myMovie, triggerAtStop, 0, 0);

    }
    return err;
}
#endif 0


The setup code is definitely being run, and not triggering any detectable errors. So why no callback? Sigh.

Thanks for listening...

Measure twice, cut once, curse three or four times.
Quote this message in a reply
Moderator
Posts: 434
Joined: 2002.09
Post: #17
Kelvin, my hat is off to you. It took me a mere 10 minutes to rip out my sound code and replace it with CBMovie, and it worked right first time.

I still don't know what was wrong with my callback code. I played with it a little more but what I'd have to do is to slowly convert my code to yours until I spot the mistake, and that will have to wait.

Eventually I'll turn this into an article for the FAQ. Thanks again for the save; I was about to give up on the music for my game.

Measure twice, cut once, curse three or four times.
Quote this message in a reply
Member
Posts: 469
Joined: 2002.10
Post: #18
Quote:Originally posted by MattDiamond
Kelvin, my hat is off to you. It took me a mere 10 minutes to rip out my sound code and replace it with CBMovie, and it worked right first time.

I still don't know what was wrong with my callback code. I played with it a little more but what I'd have to do is to slowly convert my code to yours until I spot the mistake, and that will have to wait.

Eventually I'll turn this into an article for the FAQ. Thanks again for the save; I was about to give up on the music for my game.
Smile That's good to hear.

---Kelvin--
15.4" MacBook Pro revA
1.83GHz/2GB/250GB
Quote this message in a reply
Member
Posts: 156
Joined: 2002.11
Post: #19
Quote:Now I remember why I put it down... looping isn't implemented yet as far as I can see. So no matter which way I did it I still needed to implement looping.


The channel struct comes with a flag for looping. You just need to set it to TRUE to loop your sound until you set the flag to FALSE again, or load another sound to play into that channel. Wink

Code:
//----------------------------------------------------------------------
        // Check if reached the end of the sound (or the frame count set for this channel):

        if ( !frameCount )    // Loop sound.
        {
        if ( channel_ref->isLoop )
        {
            channel_ref->isBusy            = TRUE;
            channel_ref->ptrFramesCurrPos    = channel_ref->currLPCM_ref->frames;
            channel_ref->framesRemain        = channel_ref->currLPCM_ref->frameCount;
        }
        else        // Stop sound.
        {
            channel_ref->isBusy            = FALSE;
        }
        }
Quote this message in a reply
Moderator
Posts: 434
Joined: 2002.09
Post: #20
Thought I searched the source code for all instances of the word "loop". So why didn't I spot that channel flag? (Shakes head sadly.) I'm glad this is almost over, I'm starting to lose it.

Measure twice, cut once, curse three or four times.
Quote this message in a reply
Member
Posts: 156
Joined: 2002.11
Post: #21
I've uploaded a newer version with a slightely enhanced PlaySound function: it asks you to provide the sound ID, the channel number, and a TRUE/FALSE "loop" parameter. It can't get much easier than that. Smile

http://home.earthlink.net/~yexirocks/MyGameSource.dmg
Quote this message in a reply
Member
Posts: 100
Joined: 2006.05
Post: #22
Quote:It can't get much easier than that. Smile

In my sound engine, you just pass the sound data (A SndListHandle ) and it plays it automatically. It has an internally allocated vector of channels and plays it on any available channel. If you wanted to make things simpler, you could make it so you just pass the resource ID, but then you'd be loading the sound every time you play it.
Quote this message in a reply
Member
Posts: 156
Joined: 2002.11
Post: #23
All paths lead to Rome. Wink

Yeah, it's not a big deal to scan the channels to see which one is not busy. I did that for a previous game, but I want the new one to play a specific type of sound on each channel; I need the new sound to cut off the previous, so they don't create a flanging effect.
Quote this message in a reply
Moderator
Posts: 434
Joined: 2002.09
Post: #24
The fun continues! Last minute problems with Asteroid Rally, all music-related. Kelvin, I hope that my reporting these bugs saves you time in the future, and partly pays you back for the help you've given me!

- CBMusic doesn't handle bad URL's robustly. Give it a filename that doesn't exist, you get a weird crash where NSMovie calls CBMovie's dealloc method, and that method crashes because it's trying to interrogate a non-existent movie to set the playback mode.

- CBMusic's loop timer has some funkiness that I haven't completely tracked down. If I loop through my musical tracks, calling [stop] on each one, the second time around things get nutty; music plays only partially, and doesn't loop. I noticed that your NSTimer probably needs to be retain'ed. That helped a little, but I still got weird behavior that I haven't figured out. Eventually I hacked around it by releasing the timer every time it is invalidated, and setting the timer instance variable to nil (so that a new timer will get allocated the next time the loop is started.)

I now have a few new gray hairs but music seems to finally be working reliably (famous last words?)

I'll just comment that this is a benefit of releasing the CBMovie source code that maybe you and I didn't expect; I was able to work around my problems instead of ripping out the module, and hopefully I'll save you or someone else some time with my feedback. (I'd send you my hacked version of CBMovie but I think you can do better!)

Regards,
Matt

Measure twice, cut once, curse three or four times.
Quote this message in a reply
Member
Posts: 469
Joined: 2002.10
Post: #25
I actually ran into the CBMovie bugs while I was putting together Volcanis.

here's the fix; drop-in replace the following:
Code:
- (void)stop:(id)sender { StopMovie( [self QTMovie] ); if (moviesTaskTimer) [moviesTaskTimer invalidate]; moviesTaskTimer = nil;}

and

- (void)setRate:(float)rate {
    if (rate) [self scheduleMoviesTaskTimer];
    else {
        if (moviesTaskTimer) [moviesTaskTimer invalidate];
        moviesTaskTimer = nil;
    }
    SetMovieRate( [self QTMovie], (int)(rate*(1<<16)) );
}

I've always hated timers and their funky retain policy.Mad

---Kelvin--
15.4" MacBook Pro revA
1.83GHz/2GB/250GB
Quote this message in a reply
Moderator
Posts: 434
Joined: 2002.09
Post: #26
Quote:Originally posted by kelvin
I actually ran into the CBMovie bugs while I was putting together Volcanis.

I've always hated timers and their funky retain policy.Mad


My feedback was too late to help you then. Drat.

Your solution is close to mine, but I thought I had to retain and release them. They retain until the timer is invalidated? Yuck. I googled them for a while and didn't manage to come up with that little tidbit. (Documentation, Apple! Please!)

Measure twice, cut once, curse three or four times.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Implementing audio in games sammyitch 3 2,502 Dec 21, 2005 06:27 AM
Last Post: unknown
  need advice implementing AI in game. NYGhost 9 3,417 Aug 27, 2004 01:28 PM
Last Post: NYGhost