[comp.sources.mac] Asynchronous Sound Code

lsr@Apple.COM (Larry Rosenstein) (05/10/89)

[Asynchronous Sound Code]

Enclosed is the source for an MPW Pascal unit that shows how to play
asynchronous sounds with the Sound Manager.  I have tried this unit only
on System 6.0.2; supposdly there are bugs in earlier versions of the
Sound Manager.  This unit also doesn't check for the existence of the
Sound Manager, I assume that you do this at a higher level.

I used this in a simple MacApp program that will open any file and
allow you to play any snd resource in the file ansynchronously.  (I
started this with the idea of allowing copy & paste, but haven't gotten
that far yet.  If there is interest, I can post that program.)

 Larry Rosenstein,  Object Specialist
 Apple Computer, Inc.  20525 Mariani Ave, MS 46-B  Cupertino, CA 95014
    AppleLink:Rosenstein1    domain:lsr@Apple.COM
UUCP:{sun,voder,nsc,decwrl}!apple!lsr

---
(*
A simple unit that demonstrates how to produce asynchronous sound with
the Macintosh Sound Manager.  Although I am pretty confident about this code,
I don't guarantee that this code demonstrates the correct way to do things.  It
does seem to work reliably.

This unit doesn't solve any of the tricky issues about using the Sound Manager.
Primarily, sound channels should be disposed of as soon as they are no longer
needed.  This code does just that, but it doesn't prevent your program or a
background program from trying to make sound.

Changes:

2/16/89  Lock the sound resource; state restored in call back
gSoundPlaying is the actual handle.

Larry Rosenstein
Apple Computer, Inc.
lsr@Apple.COM

Copyright 1988-1989 Apple Computer Inc.  All Rights Reserved.
*)


UNIT UAsynchSnd;

INTERFACE

USES
MemTypes, Quickdraw, OSIntf, ToolIntf;

{ call this before any other routine }
PROCEDURE InitUAsynchSnd;

{ returns TRUE if an asynchronous sound is playing }
FUNCTION  IsSoundPlaying: BOOLEAN;

{ equivalent to SndPlay, but does it asynchronously; if you call this
while another sound is playing, the first one will be stopped }
FUNCTION  ASynchSndPlay(sndHandle: Handle): OSErr;

{ stop the sound from playing; may be called even if no sound is
currently playing }
PROCEDURE StopAsynchSound;

{ should be called when your program exits }
PROCEDURE TerminateUAsynchSnd;

IMPLEMENTATION

VAR
gSoundPlaying:		Handle;
gSoundState:		SignedByte;
gSndChannel:		SndChannelPtr;

PROCEDURE ChanCallBack(chan: SndChannelPtr; cmd: SndCommand); FORWARD;
FUNCTION  GetA5: LONGINT; INLINE $2E8D; {MOVE.L A5,(A7)}
FUNCTION  LoadA5(newA5: LONGINT): LONGINT;
    					INLINE $2F4D,$0004,$2A5F;

(********************)

PROCEDURE InitUAsynchSnd;
BEGIN
gSndChannel := NIL;
gSoundPlaying := NIL;
END;


FUNCTION  ASynchSndPlay(sndHandle: Handle): OSErr;
VAR	err:			OSErr;
aCommand:		SndCommand;

BEGIN
StopAsynchSound;	{ kill the current sound & channel }

err := noErr; { default value }

{ gSndChannel should be NIL now }
err := SndNewChannel(gSndChannel, 0, 0, @ChanCallBack);
{ We don't specify a synthesizer, since we are assuming that
the snd resource specifies one.  For example, the
standard Clink-Klank snd resource doesn't use the 
sampled synthesizer. }

gSoundState := HGetState(sndHandle);
MoveHHi(sndHandle);
HLock(sndHandle);
gSoundPlaying := sndHandle;

IF err = noErr THEN
err := SndPlay(gSndChannel, sndHandle, TRUE);

WITH aCommand DO BEGIN
cmd := callBackCmd;
param1 := 0;
param2 := GetA5;
END;
IF err = noErr THEN
err := SndDoCommand(gSndChannel, aCommand, FALSE);

IF err <> noErr THEN
StopAsynchSound;		{ flush channel; unlock sound }

AsynchSndPlay := err;
END;


PROCEDURE ChanCallBack(chan: SndChannelPtr; cmd: SndCommand);
VAR	oldA5:	LONGINT;
BEGIN
oldA5 := LoadA5(cmd.param2);	{ get the application's A5 and set it }

HSetState(gSoundPlaying, gSoundState);
gSoundPlaying := NIL;

oldA5 := LoadA5(oldA5);			{ restore old A5 }
END;


FUNCTION  IsSoundPlaying: BOOLEAN;
BEGIN
IsSoundPlaying := gSoundPlaying <> NIL;
END;


PROCEDURE StopAsynchSound;
BEGIN
IF gSndChannel <> NIL THEN BEGIN
IF SndDisposeChannel(gSndChannel, TRUE) = noErr THEN { nothing };
gSndChannel := NIL;
END;
IF gSoundPlaying <> NIL THEN
BEGIN
HSetState(gSoundPlaying, gSoundState);
gSoundPlaying := NIL;
END;
END;

PROCEDURE TerminateUAsynchSnd;
BEGIN
StopAsynchSound;
END;


END.
---