home *** CD-ROM | disk | FTP | other *** search
- /* interface library for Lehenbauer SMUS Server -- Aztec C version */
-
- /* Copyright 1989 Karl Lehenbauer, All Rights Reserved
- *
- * This code was made available under license to a customer of Hackercorp.
- * It is considered a trade secret of Hackercorp and the customer is expected
- * to take reasonable measures to insure the security of this code.
- *
- * Hackercorp
- * 3918 Panorama
- * Missouri City, TX 77459
- * (713) 438-4964
- * Usenet: uunet!sugar!karl
- * Internet/BITNET: karl@sugar.hackercorp.com
- *
- */
-
- #include <exec/types.h>
- #include <exec/memory.h>
- #include <exec/nodes.h>
- #include <exec/lists.h>
- #include <functions.h>
- #include <devices/audio.h>
- #include <workbench/workbench.h>
- #include <workbench/startup.h>
- #include <fcntl.h>
- #include <stdio.h>
-
- #include "smus_requests.h"
-
- extern short panic_in_progress;
-
- char *server_died = "one of the smus server programs died";
-
- /*
- * This file contains the routines a program needs to include (and call)
- * in order to load and play songs with the Lehenbauer SMUS Server
- *
- * There is a message-based interface and a RPC (Remote Procedure Call)
- * interface. Most people find the RPC interface easier to understand,
- * although in some circumstances the message-based interface may provide
- * certain advantages.
- *
- */
-
- static struct PlayerRequest player_request;
-
- static struct PlayerRequest loader_request;
-
- /* we find the player and loader request ports or launch the
- * loader and player
- */
- static struct MsgPort *player_request_port = NULL;
-
- static struct MsgPort *loader_request_port = NULL;
-
- /* we get replies to out requests through the reply request port
- * and launch replies through a launch reply port
- */
-
- static struct MsgPort *reply_request_port = NULL;
-
- static struct MsgPort *launch_reply_port = NULL;
-
- static struct WBStartup *player_wbstartup = NULL, *loader_wbstartup = NULL;
-
- #define YES 1
- #define NO 0
-
- static short launched_player = NO;
- static short launched_loader = NO;
-
- /* where the player and loader reside */
- static char player_name[] = "ksmu:playsmus";
- static char *player_launch_argv[] = {player_name, NULL};
-
- static char loader_name[] = "ksmu:loadsmus";
- static char *loader_launch_argv[] = {loader_name, NULL};
-
- /* the SMUS equivalent of Unix errno, numbers are in smusrequest.h,
- * conversion to text included in this file
- */
- int es_errno;
-
- static short terminate_in_progress = NO;
-
-
- /* ------------ workbench launch ---------------------------- */
-
- /* The workbench launch and free code, below, is used to start up and
- * recognize the termination of the player and loader servers
- */
-
- /* Launch(rport, name, argv, argc, pri, win, stack) -- launch a program.
- *
- * Launch builds a startup message, loads a program, and launches it.
- *
- * Arguments:
- * rport -- reply port to receive startup message back when the program
- * finishes.
- * name -- File name for the program to load.
- * argv -- list of filenames to pass to loaded program.
- * argc -- length of argument list.
- * pri -- priority of the new process.
- * win -- ToolWindow for the new process, or NULL.
- * stack -- Stack size for the new process.
- *
- * argv[0] should be the same as the program. pri should normally be 0.
- * stack should normally be at least 4K.
- */
- static struct WBStartup *
- Launch(rport, name, argv, argc, pri, win, stack)
- struct MsgPort *rport;
- char *name;
- char **argv;
- int argc;
- long pri;
- char *win;
- ULONG stack;
- {
- ULONG flock;
- struct WBStartup *msg;
- char *s, *namep;
- int i;
-
- if(!rport)
- return 0;
-
- /* Get some space to work in -- the startup message */
- msg = (struct WBStartup *)
- AllocMem(sizeof(struct WBStartup), MEMF_PUBLIC|MEMF_CLEAR);
- if(!msg)
- return 0;
-
- /* Now load the program */
- msg->sm_Segment = (long)LoadSeg(name);
- if(!msg->sm_Segment)
- {
- FreeStartup(msg);
- return 0;
- }
-
- /* Allocate and link in the window description */
- if(win)
- {
- msg->sm_ToolWindow = (char *)AllocMem(strlen(win)+1, MEMF_PUBLIC);
- if(!msg->sm_ToolWindow)
- {
- FreeStartup(msg);
- return 0;
- }
- strcpy(msg->sm_ToolWindow, win);
- }
- else
- msg->sm_ToolWindow = 0;
-
- /* Allocate the arg list */
- msg->sm_ArgList = (struct WBArg *)
- AllocMem((long)(sizeof(struct WBArg) * argc), MEMF_PUBLIC | MEMF_CLEAR);
- if(!msg->sm_ArgList)
- {
- FreeStartup(msg);
- return 0;
- }
-
- /* Empty out all args, just in case this aborts, so cleanup
- * can clean it up
- */
- msg->sm_NumArgs = argc;
- for(i = 0; i < argc; i++)
- {
- msg->sm_ArgList[i].wa_Lock = 0;
- msg->sm_ArgList[i].wa_Name = 0;
- }
-
- /* Get lock and name for wbargs */
- for(i = 0; i < argc; i++)
- {
- flock = (long)Lock(argv[i],ACCESS_READ);
- if(!flock)
- {
- FreeStartup(msg);
- return 0;
- }
- msg->sm_ArgList[i].wa_Lock = (long)ParentDir(flock);
- UnLock(flock);
- if(!msg->sm_ArgList[i].wa_Lock)
- {
- FreeStartup(msg);
- return 0;
- }
-
- /* Get the tail end of the file name. I could also do an Examine
- * on the lock, but didn't want to bother allocating a File Info
- * Block that wouldn't fit under the tracking via the startup
- * message.
- */
- namep = argv[i];
-
- for(s = argv[i]; *s; s++)
- if(*s=='/' || *s==':')
- namep = s+1;
-
- msg->sm_ArgList[i].wa_Name =
- (char *)AllocMem(strlen(namep)+1, MEMF_PUBLIC);
-
- if(!msg->sm_ArgList[i].wa_Name)
- {
- FreeStartup(msg);
- return 0;
- }
- strcpy(msg->sm_ArgList[i].wa_Name, namep);
- }
-
- /* Create the process. It is now running, but will wait for the
- * startup message.
- */
- msg->sm_Process = (struct MsgPort *)
- CreateProc(msg->sm_ArgList[0].wa_Name, pri, msg->sm_Segment, stack);
-
- if(!msg->sm_Process)
- {
- FreeStartup(msg);
- return 0;
- }
-
- /* Initialise the message part of the startup message, and pass it to
- * the process. At this point it will actually start ding work
- */
- msg->sm_Message.mn_ReplyPort = rport;
- msg->sm_Message.mn_Length = sizeof(struct WBStartup);
- msg->sm_Message.mn_Node.ln_Type = NT_MESSAGE;
-
- PutMsg(msg->sm_Process, msg);
-
- /* return message. Not very useful, but it's as meaningful a response as
- * any.
- */
- return msg;
- }
-
- static FreeStartup(msg)
- struct WBStartup *msg;
- {
- /* Free the files. Assumes they have been allocated via Launch, so
- * they're just AllocMemmed. UnLock all the Locks, and Free the
- * names.
- */
- if(msg->sm_ArgList)
- {
- int i;
-
- for(i = 0; i < msg->sm_NumArgs; i++)
- {
- if(msg->sm_ArgList[i].wa_Lock)
- UnLock(msg->sm_ArgList[i].wa_Lock);
-
- if(msg->sm_ArgList[i].wa_Name)
- FreeMem(msg->sm_ArgList[i].wa_Name,
- (long)strlen(msg->sm_ArgList[i].wa_Name)+1);
- }
-
- FreeMem(msg->sm_ArgList,
- (long)(sizeof(struct WBArg) * msg->sm_NumArgs));
- }
-
- /* Here goes the program...
- */
- if(msg->sm_Segment)
- UnLoadSeg(msg->sm_Segment);
-
- /* And the name of the window. Again, assuming it's allocated by
- * Launch, so it just fits in its AllocMemmed buffer.
- */
- if(msg->sm_ToolWindow)
- FreeMem(msg->sm_ToolWindow,
- (long)(strlen(msg->sm_ToolWindow)+1));
-
- /* And finally the message itself */
- FreeMem(msg, (long)sizeof(struct WBStartup));
- }
-
- /* ------------ message interface --------------------------- */
-
- /* given a port to one of the SMUS server programs, command from requests.h,
- * pointer to data and port to reply to, tell one of the SMUS programs to
- * do somthing.
- *
- * Note that RPC (Remote Procedure Call) users need not use this routine
- * as it is called directly by the RPC stuff.
- *
- * If the replyport is not NULL, allocate a player request on-the-fly and
- * uses that for the request as multiple requests may be pending.
- * If the replyport is not NULL, the message is sent and the subroutine
- * returns, then when the server program has completed processing the
- * command, it will reply to that replyport. When using it this way,
- * the user is expected to check for the reply message as part of their
- * big event loop.
- *
- * If the replyport is NULL, this signifies that the user wants to wait,
- * it uses an internal request buffer of which there is only one but that's
- * OK as no more than one request can be pending at a time with that model.
- *
- * The RPC interfaces uses TellSMUSProgram in "wait" mode. You don't
- * have to wait for the song to play if you don't want to, though, because
- * the song player can be told to start playing but reply to the message
- * immediately via it's play-with-quick-return capabilities.
- *
- */
- struct PlayerRequest *TellSMUSProgram(port,command,data,replyport)
- struct MsgPort *port;
- int command;
- void *data;
- struct MsgPort *replyport;
- {
- struct PlayerRequest *msg;
- struct PlayerRequest *rp;
- struct MsgPort *msgp; /* pointer to reply port we'll use, ours or user's */
- long sigmask, signals;
-
- if (CheckServerShutdown() && !panic_in_progress && !terminate_in_progress)
- panic(server_died);
-
- /* set up pointers and get up a request message if there is a reply
- * port in case there's concurrency */
- if (replyport)
- {
- rp = AllocMem((long)(sizeof(struct PlayerRequest)),MEMF_PUBLIC);
- if (!rp)
- panic("can't alloc SMUS request message");
- msgp = replyport;
- }
- else
- {
- rp = &player_request;
- msgp = reply_request_port;
- }
-
- /* set up the message */
- rp->command = command;
- rp->status = ES_PENDING;
- rp->data.song_pointer = data;
- rp->message.mn_Length = sizeof(player_request);
- rp->message.mn_ReplyPort = msgp;
-
- /* send it */
- PutMsg(port,rp);
-
- /* if they didn't specify a reply port, do a mini Wait loop, being
- * good neighbors by only looking at our signal bits and control-C
- */
- if (!replyport)
- {
- sigmask = (1 << launch_reply_port->mp_SigBit)
- | (1 << msgp->mp_SigBit)
- | SIGBREAKF_CTRL_C;
-
- do
- {
- signals = Wait(sigmask);
-
- if (signals & SIGBREAKF_CTRL_C)
- panic("^C abort");
-
- if (CheckServerShutdown() && !panic_in_progress && !terminate_in_progress)
- panic(server_died);
-
- while (msg = (struct PlayerRequest *)GetMsg(reply_request_port))
- {
- if (msg == rp)
- {
- break;
- }
- }
- } while (msg != &player_request);
- }
-
- #ifdef DEBUG
- printf("reply status %d\n",rp->status);
- #endif
-
- if ((rp->status == ES_SERVER_ABORTED) && (!panic_in_progress))
- panic(server_died);
-
- es_errno = rp->status;
- PrintSMUSError(es_errno);
- return(rp);
- }
-
- static int CheckServerShutdown()
- {
- struct PlayerRequest *msg;
-
- /* this is static 'cuz one a server did shutdown, a server always
- * did shutdown */
- static short one_did_shutdown = NO;
-
- while (msg = (struct PlayerRequest *)GetMsg(launch_reply_port))
- {
- if (player_wbstartup && (struct WBStartup *)msg == player_wbstartup)
- {
-
- #ifdef DEBUG
- printf("got the player's termination message\n");
- #endif
-
- FreeStartup(msg);
- player_wbstartup = NULL;
- one_did_shutdown = YES;
- }
- else if (loader_wbstartup && (struct WBStartup *)msg == loader_wbstartup)
- {
-
- #ifdef DEBUG
- printf("got the loader's termination message\n");
- #endif
-
- FreeStartup(msg);
- loader_wbstartup = NULL;
- one_did_shutdown = YES;
- }
- }
- return(one_did_shutdown);
- }
-
- static void LocatePlayerPort()
- {
- player_request_port = FindPort(PLAYER_PORTNAME);
- if (!player_request_port)
- {
- #ifdef DEBUG
- printf("launching smus player\n");
- #endif
- player_wbstartup = Launch(launch_reply_port,player_name,player_launch_argv,1L,0L,(char *)NULL,4096L);
-
- if (player_wbstartup != (struct WBStartup *)NULL)
- {
- Delay(50);
-
- player_request_port = FindPort(PLAYER_PORTNAME);
-
- if (!player_request_port)
- panic("didn't find request port after launching player");
-
- launched_player = YES;
- }
- else
- panic("launch of player failed");
- }
-
- }
-
- static void LocateLoaderPort()
- {
- loader_request_port = FindPort(LOADER_PORTNAME);
- if (!loader_request_port)
- {
-
- #ifdef DEBUG
- printf("launching smus loader\n");
- #endif
-
- loader_wbstartup = Launch(launch_reply_port,loader_name,loader_launch_argv,1,0,NULL,4096);
-
- if (loader_wbstartup != (struct WBStartup *)NULL)
- {
- Delay(50);
-
- loader_request_port = FindPort(LOADER_PORTNAME);
- if (!loader_request_port)
- panic("didn't find request port after launching loader");
-
- launched_loader = YES;
- }
- else
- panic("launch of loader failed");
- }
-
- }
-
- static void SMUSCleanupPorts()
- {
- struct Message *msg;
- long sigmask;
-
-
- if (launch_reply_port)
- {
-
- /* if a task shut down, cheat and set panic in progress
- * so my TellSMUSProgram calls below won't be terminated */
- if (CheckServerShutdown())
- panic_in_progress = YES;
-
- /* if there isn't a terminate in progress and we didn't launch
- * the player but we do have a request port for it (meaning
- * it's running) or we did launch it, send a terminate message
- * to it & ditto for the loader */
-
- if (!terminate_in_progress && ((!launched_player && player_request_port) || player_wbstartup))
- TellSMUSProgram(player_request_port,PLEASE_TERMINATE_IMMEDIATELY,NULL,NULL);
-
- if (!terminate_in_progress && ((!launched_loader && loader_request_port) || loader_wbstartup))
- TellSMUSProgram(loader_request_port,PLEASE_TERMINATE_IMMEDIATELY,NULL);
-
- while (player_wbstartup || loader_wbstartup)
- {
- #ifdef DEBUG
- printf("waiting for player and loader to terminate\n");
- #endif
- sigmask = (1 << launch_reply_port->mp_SigBit);
-
- Wait(sigmask);
- CheckServerShutdown();
- }
- DeletePort(launch_reply_port);
- }
-
- if (reply_request_port)
- {
- while (GetMsg(reply_request_port)) ;
-
- DeletePort(reply_request_port);
- }
-
- }
-
- SMUSInitServer()
- {
- add_cleanup(SMUSCleanupPorts);
-
- launch_reply_port = CreatePort(0L,0L);
- if (!launch_reply_port)
- panic("can't create launch reply port");
-
- reply_request_port = CreatePort(0L,0L);
- if (!reply_request_port)
- panic("can't create request reply port");
-
- LocateLoaderPort();
-
- LocatePlayerPort();
- }
-
-
-
- /* ------------ RPC calls ----------------------------------- */
-
-
- /* RPC Remote Procedure Call interface to the SMUS loader and player */
-
- /* loads a song, does not insure all samples are loaded */
- void *LoadSong(songname)
- char *songname;
- {
- struct PlayerRequest *rp;
-
- rp = TellSMUSProgram(loader_request_port,PLEASE_LOAD_SONG,songname,NULL);
- if (rp->status == ES_OK)
- return(rp->data.song_pointer);
- else
- return(NULL);
- }
-
- /* loads any samples necessary to insure all samples are loaded for songid */
- int AttachSamples(songptr)
- void *songptr;
- {
- struct PlayerRequest *rp;
-
- rp = TellSMUSProgram(loader_request_port,PLEASE_ATTACH_SAMPLES,songptr,NULL);
- return(rp->status);
- }
-
- /* unloads song but does not remove song's samples */
- UnloadSong(songptr)
- void *songptr;
- {
- struct PlayerRequest *rp;
-
- rp = TellSMUSProgram(loader_request_port,PLEASE_UNLOAD_SONG,songptr,NULL);
- return(rp->status);
- }
-
- /* unloads all samples that aren't referenced by a song or "tagged" */
- PurgeUntaggedSamples()
- {
- struct PlayerRequest *rp;
-
- rp = TellSMUSProgram(loader_request_port,PLEASE_PURGE_UNTAGGED_SAMPLES,NULL,NULL);
- return(rp->status);
- }
-
-
- /* loads all samples and songs found in CAT of samples and songs */
- LoadCATArchive(catname)
- char *catname;
- {
- struct PlayerRequest *rp;
-
- rp = TellSMUSProgram(loader_request_port,PLEASE_LOAD_CAT_SAMPLES,catname,NULL);
- return(rp->status);
- }
-
-
- /* loads any samples necessary to insure all samples are loaded for all
- * songs */
- AttachAllSamples(replyport)
- struct MsgPort *replyport;
- {
- struct PlayerRequest *rp;
-
- rp = TellSMUSProgram(loader_request_port,PLEASE_ATTACH_ALL_SAMPLES,NULL,NULL);
- return(rp->status);
- }
-
- /* clears "tag" bits for all samples */
- ClearSampleTags()
- {
- struct PlayerRequest *rp;
-
- rp = TellSMUSProgram(loader_request_port,PLEASE_CLEAR_SAMPLE_TAGS,NULL,NULL);
- return(rp->status);
- }
-
- /* set "tag" bit for samplename */
- SetSampleTag(samplename)
- char *samplename;
- {
- struct PlayerRequest *rp;
-
- rp = TellSMUSProgram(loader_request_port,PLEASE_TAG_NAMED_SAMPLE,samplename,NULL);
- return(rp->status);
- }
-
- /* play a song, don't return until it's finished */
- PlaySong(songptr)
- void *songptr;
- {
- struct PlayerRequest *rp;
-
- rp = TellSMUSProgram(player_request_port,PLEASE_PLAY_SONG,songptr,NULL);
- return(rp->status);
- }
-
- /* shut down the server, free everything, exit */
- SMUSTerminateServer()
- {
- CheckServerShutdown();
-
- terminate_in_progress = YES;
-
- /* if we didn't launch the loader but the loader_request_port is
- * non-null, we did find the loader, or if loader_wbstartup is
- * non-null, we did launch the loader and it is apparently still
- * alive, tell it to shutdown */
-
- if ((!launched_loader && loader_request_port) || loader_wbstartup)
- TellSMUSProgram(loader_request_port,PLEASE_TERMINATE_IMMEDIATELY,NULL,NULL);
-
- /* same for the player */
-
- if ((!launched_player && player_request_port) || player_wbstartup)
- TellSMUSProgram(player_request_port,PLEASE_TERMINATE_IMMEDIATELY,NULL,NULL);
- }
-
- /* start playing a song but return immediately */
- StartSong(songptr)
- void *songptr;
- {
- struct PlayerRequest *rp;
-
- rp = TellSMUSProgram(player_request_port,PLEASE_PLAY_SONG_IMMEDIATE_REPLY,songptr,NULL);
- return(rp->status);
- }
-
- SetSampleTagsOfSong(songptr)
- void *songptr;
- {
- struct PlayerRequest *rp;
-
- rp = TellSMUSProgram(loader_request_port,PLEASE_TAG_SONGS_SAMPLES,songptr,NULL);
- return(rp->status);
- }
-
- /* terminate the loader now and terminate the player when it's finished
- * the current song (terminates immediately if no song is playing) */
- TerminateServerWhenFinished()
- {
- terminate_in_progress = YES;
-
- TellSMUSProgram(player_request_port,PLEASE_TERMINATE_WHEN_FINISHED,NULL,NULL);
- TellSMUSProgram(loader_request_port,PLEASE_TERMINATE_IMMEDIATELY,NULL,NULL);
- }
-
- /* abort the currently playing song but don't terminate the server */
- AbortSongImmediately()
- {
- struct PlayerRequest *rp;
-
- rp = TellSMUSProgram(player_request_port,PLEASE_ABORT_IMMEDIATELY,NULL,NULL);
- return(rp->status);
- }
-
- /* start a fade out which will eventually abort the song, but don't terminate
- * the server */
- StartFadeOut()
- {
- struct PlayerRequest *rp;
-
- rp = TellSMUSProgram(player_request_port,PLEASE_START_FADEOUT,NULL,NULL);
- return(rp->status);
- }
-
-
- /* don't return until the currently playing song has finished. Returns
- * immediately if no song is playing */
- WaitTilSongFinishes()
- {
- struct PlayerRequest *rp;
-
- rp = TellSMUSProgram(player_request_port,PLEASE_REPLY_WHEN_SONG_FINISHES,NULL,NULL);
- return(rp->status);
- }
-
-
- /* ------------------------- error text --------------------------- */
-
-
- /* given a SMUS errno, return text of what that error is */
- char *SMUSErrorText(errno)
- int errno;
- {
- char *text;
-
- switch(errno)
- {
-
- case ES_INVALID_COMMAND:
- text = "invalid command";
- break;
-
- case ES_SERVER_ABORTED:
- text = "server aborted";
- break;
-
- case ES_NOT_IMPLEMENTED:
- text = "command not implemented";
- break;
-
- case ES_ALREADY_PLAYING:
- text = "player is already playing";
- break;
-
- case ES_LOAD_FAILED:
- text = "load failed";
- break;
-
- case ES_NOT_ALL_SAMPLES_PRESENT:
- text = "not all samples present";
- break;
-
- case ES_SONG_IS_PLAYING:
- text = "song is playing";
- break;
-
- case ES_BAD_SONG_ADDRESS:
- text = "address of song is invalid";
- break;
-
- case ES_OK:
- text = "OK";
- break;
-
- case ES_PENDING:
- text = "request pending";
- break;
-
- case ES_PROCESSING:
- text = "request being processed";
- break;
-
- case ES_NOT_FOUND:
- text = "couldn't find specified item";
- break;
-
- default:
- text = "unknown error type";
- break;
-
- }
- return(text);
- }
-
- PrintSMUSError(errno)
- int errno;
- {
- puts(SMUSErrorText(errno));
- }
-