home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 497a.lha / ComSMUS_v2.2 / src / smusinterface.c < prev   
Encoding:
C/C++ Source or Header  |  1991-04-07  |  18.7 KB  |  800 lines

  1. /* interface library for Lehenbauer SMUS Server -- Aztec C version */
  2.  
  3. /* Copyright 1989 Karl Lehenbauer, All Rights Reserved
  4.  *
  5.  * This code was made available under license to a customer of Hackercorp.
  6.  * It is considered a trade secret of Hackercorp and the customer is expected
  7.  * to take reasonable measures to insure the security of this code.
  8.  *
  9.  * Hackercorp
  10.  * 3918 Panorama
  11.  * Missouri City, TX  77459
  12.  * (713) 438-4964
  13.  * Usenet: uunet!sugar!karl   
  14.  * Internet/BITNET: karl@sugar.hackercorp.com
  15.  *
  16.  */
  17.  
  18. #include <exec/types.h>
  19. #include <exec/memory.h>
  20. #include <exec/nodes.h>
  21. #include <exec/lists.h>
  22. #include <functions.h>
  23. #include <devices/audio.h>
  24. #include <workbench/workbench.h>
  25. #include <workbench/startup.h>
  26. #include <fcntl.h>
  27. #include <stdio.h>
  28.  
  29. #include "smus_requests.h"
  30.  
  31. extern short panic_in_progress;
  32.  
  33. char *server_died = "one of the smus server programs died";
  34.  
  35. /*
  36.  * This file contains the routines a program needs to include (and call)
  37.  * in order to load and play songs with the Lehenbauer SMUS Server
  38.  *
  39.  * There is a message-based interface and a RPC (Remote Procedure Call)
  40.  * interface.  Most people find the RPC interface easier to understand,
  41.  * although in some circumstances the message-based interface may provide
  42.  * certain advantages.  
  43.  * 
  44.  */
  45.  
  46. static struct PlayerRequest player_request;
  47.  
  48. static struct PlayerRequest loader_request;
  49.  
  50. /* we find the player and loader request ports or launch the
  51.  * loader and player
  52.  */
  53. static struct MsgPort *player_request_port = NULL;
  54.  
  55. static struct MsgPort *loader_request_port = NULL;
  56.  
  57. /* we get replies to out requests through the reply request port
  58.  * and launch replies through a launch reply port 
  59.  */
  60.  
  61. static struct MsgPort *reply_request_port = NULL;
  62.  
  63. static struct MsgPort *launch_reply_port = NULL;
  64.  
  65. static struct WBStartup *player_wbstartup = NULL, *loader_wbstartup = NULL;
  66.  
  67. #define YES 1
  68. #define NO 0
  69.  
  70. static short launched_player = NO;
  71. static short launched_loader = NO;
  72.  
  73. /* where the player and loader reside */
  74. static char player_name[] = "ksmu:playsmus";
  75. static char *player_launch_argv[] = {player_name, NULL};
  76.  
  77. static char loader_name[] = "ksmu:loadsmus";
  78. static char *loader_launch_argv[] = {loader_name, NULL};
  79.  
  80. /* the SMUS equivalent of Unix errno, numbers are in smusrequest.h,
  81.  * conversion to text included in this file 
  82.  */
  83. int es_errno;
  84.  
  85. static short terminate_in_progress = NO;
  86.  
  87.  
  88. /* ------------ workbench launch ---------------------------- */
  89.  
  90. /* The workbench launch and free code, below, is used to start up and
  91.  * recognize the termination of the player and loader servers 
  92.  */
  93.  
  94. /* Launch(rport, name, argv, argc, pri, win, stack) -- launch a program.
  95.  *
  96.  * Launch builds a startup message, loads a program, and launches it.
  97.  *
  98.  * Arguments:
  99.  *    rport -- reply port to receive startup message back when the program
  100.  *             finishes.
  101.  *    name  -- File name for the program to load.
  102.  *    argv  -- list of filenames to pass to loaded program.
  103.  *    argc  -- length of argument list.
  104.  *    pri   -- priority of the new process.
  105.  *    win   -- ToolWindow for the new process, or NULL.
  106.  *    stack -- Stack size for the new process.
  107.  *
  108.  * argv[0] should be the same as the program. pri should normally be 0.
  109.  * stack should normally be at least 4K.
  110.  */
  111. static struct WBStartup *
  112. Launch(rport, name, argv, argc, pri, win, stack)
  113. struct MsgPort *rport;
  114. char *name;
  115. char **argv;
  116. int argc;
  117. long pri;
  118. char *win;
  119. ULONG stack;
  120. {
  121.     ULONG flock;
  122.     struct WBStartup *msg;
  123.     char *s, *namep;
  124.     int i;
  125.  
  126.     if(!rport)
  127.         return 0;
  128.  
  129.     /* Get some space to work in -- the startup message */
  130.     msg = (struct WBStartup *)
  131.         AllocMem(sizeof(struct WBStartup), MEMF_PUBLIC|MEMF_CLEAR);
  132.     if(!msg)
  133.         return 0;
  134.  
  135.     /* Now load the program */
  136.     msg->sm_Segment = (long)LoadSeg(name);
  137.     if(!msg->sm_Segment) 
  138.     {
  139.         FreeStartup(msg);
  140.         return 0;
  141.     }
  142.  
  143.     /* Allocate and link in the window description */
  144.     if(win) 
  145.     {
  146.         msg->sm_ToolWindow = (char *)AllocMem(strlen(win)+1, MEMF_PUBLIC);
  147.         if(!msg->sm_ToolWindow) 
  148.         {
  149.             FreeStartup(msg);
  150.             return 0;
  151.         }
  152.         strcpy(msg->sm_ToolWindow, win);
  153.     } 
  154.     else
  155.         msg->sm_ToolWindow = 0;
  156.  
  157.     /* Allocate the arg list */
  158.     msg->sm_ArgList = (struct WBArg *)
  159.         AllocMem((long)(sizeof(struct WBArg) * argc), MEMF_PUBLIC | MEMF_CLEAR);
  160.     if(!msg->sm_ArgList) 
  161.     {
  162.         FreeStartup(msg);
  163.         return 0;
  164.     }
  165.  
  166.     /* Empty out all args, just in case this aborts, so cleanup
  167.      * can clean it up
  168.      */
  169.     msg->sm_NumArgs = argc;
  170.     for(i = 0; i < argc; i++) 
  171.     {
  172.         msg->sm_ArgList[i].wa_Lock = 0;
  173.         msg->sm_ArgList[i].wa_Name = 0;
  174.     }
  175.  
  176.     /* Get lock and name for wbargs */
  177.     for(i = 0; i < argc; i++) 
  178.     {
  179.         flock = (long)Lock(argv[i],ACCESS_READ);
  180.         if(!flock) 
  181.         {
  182.             FreeStartup(msg);
  183.             return 0;
  184.         }
  185.         msg->sm_ArgList[i].wa_Lock = (long)ParentDir(flock);
  186.         UnLock(flock);
  187.         if(!msg->sm_ArgList[i].wa_Lock) 
  188.         {
  189.             FreeStartup(msg);
  190.             return 0;
  191.         }
  192.  
  193.         /* Get the tail end of the file name. I could also do an Examine
  194.          * on the lock, but didn't want to bother allocating a File Info
  195.          * Block that wouldn't fit under the tracking via the startup
  196.          * message.
  197.          */
  198.         namep = argv[i];
  199.  
  200.         for(s = argv[i]; *s; s++)
  201.             if(*s=='/' || *s==':')
  202.                 namep = s+1;
  203.  
  204.         msg->sm_ArgList[i].wa_Name =
  205.             (char *)AllocMem(strlen(namep)+1, MEMF_PUBLIC);
  206.  
  207.         if(!msg->sm_ArgList[i].wa_Name) 
  208.         {
  209.             FreeStartup(msg);
  210.             return 0;
  211.         }
  212.         strcpy(msg->sm_ArgList[i].wa_Name, namep);
  213.     }
  214.  
  215.     /* Create the process. It is now running, but will wait for the
  216.      * startup message.
  217.      */
  218.     msg->sm_Process = (struct MsgPort *)
  219.         CreateProc(msg->sm_ArgList[0].wa_Name, pri, msg->sm_Segment, stack);
  220.  
  221.     if(!msg->sm_Process) 
  222.     {
  223.         FreeStartup(msg);
  224.         return 0;
  225.     }
  226.  
  227.     /* Initialise the message part of the startup message, and pass it to
  228.      * the process. At this point it will actually start ding work
  229.      */
  230.     msg->sm_Message.mn_ReplyPort = rport;
  231.     msg->sm_Message.mn_Length = sizeof(struct WBStartup);
  232.     msg->sm_Message.mn_Node.ln_Type = NT_MESSAGE;
  233.  
  234.     PutMsg(msg->sm_Process, msg);
  235.  
  236.     /* return message. Not very useful, but it's as meaningful a response as
  237.      * any.
  238.      */
  239.     return msg;
  240. }
  241.  
  242. static FreeStartup(msg)
  243. struct WBStartup *msg;
  244. {
  245.     /* Free the files. Assumes they have been allocated via Launch, so
  246.      * they're just AllocMemmed. UnLock all the Locks, and Free the
  247.      * names.
  248.      */
  249.     if(msg->sm_ArgList) 
  250.     {
  251.         int i;
  252.  
  253.         for(i = 0; i < msg->sm_NumArgs; i++) 
  254.         {
  255.             if(msg->sm_ArgList[i].wa_Lock)
  256.                 UnLock(msg->sm_ArgList[i].wa_Lock);
  257.  
  258.             if(msg->sm_ArgList[i].wa_Name)
  259.                 FreeMem(msg->sm_ArgList[i].wa_Name,
  260.                     (long)strlen(msg->sm_ArgList[i].wa_Name)+1);
  261.         }
  262.  
  263.         FreeMem(msg->sm_ArgList,
  264.             (long)(sizeof(struct WBArg) * msg->sm_NumArgs));
  265.     }
  266.  
  267.     /* Here goes the program...
  268.      */
  269.     if(msg->sm_Segment)
  270.         UnLoadSeg(msg->sm_Segment);
  271.  
  272.     /* And the name of the window. Again, assuming it's allocated by
  273.      * Launch, so it just fits in its AllocMemmed buffer.
  274.      */
  275.     if(msg->sm_ToolWindow)
  276.         FreeMem(msg->sm_ToolWindow,
  277.             (long)(strlen(msg->sm_ToolWindow)+1));
  278.  
  279.     /* And finally the message itself */
  280.     FreeMem(msg, (long)sizeof(struct WBStartup));
  281. }
  282.  
  283. /* ------------ message interface --------------------------- */
  284.  
  285. /* given a port to one of the SMUS server programs, command from requests.h,
  286.  * pointer to data and port to reply to, tell one of the SMUS programs to 
  287.  * do somthing.
  288.  *
  289.  * Note that RPC (Remote Procedure Call) users need not use this routine
  290.  * as it is called directly by the RPC stuff.
  291.  *
  292.  * If the replyport is not NULL, allocate a player request on-the-fly and 
  293.  * uses that for the request as multiple requests may be pending.
  294.  * If the replyport is not NULL, the message is sent and the subroutine
  295.  * returns, then when the server program has completed processing the 
  296.  * command, it will reply to that replyport.  When using it this way,
  297.  * the user is expected to check for the reply message as part of their
  298.  * big event loop.
  299.  *
  300.  * If the replyport is NULL, this signifies that the user wants to wait,
  301.  * it uses an internal request buffer of which there is only one but that's
  302.  * OK as no more than one request can be pending at a time with that model.
  303.  *
  304.  * The RPC interfaces uses TellSMUSProgram in "wait" mode.  You don't
  305.  * have to wait for the song to play if you don't want to, though, because
  306.  * the song player can be told to start playing but reply to the message
  307.  * immediately via it's play-with-quick-return capabilities.
  308.  *
  309.  */
  310. struct PlayerRequest *TellSMUSProgram(port,command,data,replyport)
  311. struct MsgPort *port;
  312. int command;
  313. void *data;
  314. struct MsgPort *replyport;
  315. {
  316.     struct PlayerRequest *msg;
  317.     struct PlayerRequest *rp;
  318.     struct MsgPort *msgp;    /* pointer to reply port we'll use, ours or user's */
  319.     long sigmask, signals;
  320.  
  321.     if (CheckServerShutdown() && !panic_in_progress && !terminate_in_progress)
  322.         panic(server_died);
  323.  
  324.     /* set up pointers and get up a request message if there is a reply
  325.      * port in case there's concurrency */
  326.     if (replyport)
  327.     {
  328.         rp = AllocMem((long)(sizeof(struct PlayerRequest)),MEMF_PUBLIC);
  329.         if (!rp)
  330.             panic("can't alloc SMUS request message");
  331.         msgp = replyport;
  332.     }
  333.     else
  334.     {
  335.         rp = &player_request;
  336.         msgp = reply_request_port;
  337.     }
  338.  
  339.     /* set up the message */
  340.     rp->command = command;
  341.     rp->status = ES_PENDING;
  342.     rp->data.song_pointer = data;
  343.     rp->message.mn_Length = sizeof(player_request);
  344.     rp->message.mn_ReplyPort = msgp;
  345.  
  346.     /* send it */
  347.     PutMsg(port,rp);
  348.  
  349.     /* if they didn't specify a reply port, do a mini Wait loop, being
  350.      * good neighbors by only looking at our signal bits and control-C
  351.      */
  352.     if (!replyport)
  353.     {
  354.         sigmask = (1 << launch_reply_port->mp_SigBit)
  355.             | (1 << msgp->mp_SigBit) 
  356.             | SIGBREAKF_CTRL_C;
  357.  
  358.         do
  359.         {
  360.             signals = Wait(sigmask);
  361.  
  362.             if (signals & SIGBREAKF_CTRL_C)
  363.                 panic("^C abort");
  364.  
  365.             if (CheckServerShutdown() && !panic_in_progress && !terminate_in_progress)
  366.                     panic(server_died);
  367.  
  368.             while (msg = (struct PlayerRequest *)GetMsg(reply_request_port))
  369.             {
  370.                 if (msg == rp)
  371.                 {
  372.                     break;
  373.                 }
  374.             }
  375.         } while (msg != &player_request);
  376.     }
  377.  
  378. #ifdef DEBUG
  379.     printf("reply status %d\n",rp->status);
  380. #endif
  381.  
  382.     if ((rp->status == ES_SERVER_ABORTED) && (!panic_in_progress))
  383.         panic(server_died);
  384.  
  385.     es_errno = rp->status;
  386.     PrintSMUSError(es_errno);
  387.     return(rp);
  388. }
  389.  
  390. static int CheckServerShutdown()
  391. {
  392.     struct PlayerRequest *msg;
  393.  
  394.     /* this is static 'cuz one a server did shutdown, a server always
  395.      * did shutdown */
  396.     static short one_did_shutdown = NO;
  397.  
  398.     while (msg = (struct PlayerRequest *)GetMsg(launch_reply_port))
  399.     {
  400.         if (player_wbstartup && (struct WBStartup *)msg == player_wbstartup)
  401.         {
  402.  
  403. #ifdef DEBUG
  404.             printf("got the player's termination message\n");
  405. #endif
  406.  
  407.             FreeStartup(msg);
  408.             player_wbstartup = NULL;
  409.             one_did_shutdown = YES;
  410.         }
  411.         else if (loader_wbstartup && (struct WBStartup *)msg == loader_wbstartup)
  412.         {
  413.  
  414. #ifdef DEBUG
  415.             printf("got the loader's termination message\n");
  416. #endif
  417.  
  418.             FreeStartup(msg);
  419.             loader_wbstartup = NULL;
  420.             one_did_shutdown = YES;
  421.         }
  422.     }
  423.     return(one_did_shutdown);
  424. }
  425.  
  426. static void LocatePlayerPort()
  427. {
  428.     player_request_port = FindPort(PLAYER_PORTNAME);
  429.     if (!player_request_port)
  430.     {
  431. #ifdef DEBUG
  432.         printf("launching smus player\n");
  433. #endif
  434.         player_wbstartup = Launch(launch_reply_port,player_name,player_launch_argv,1L,0L,(char *)NULL,4096L);
  435.  
  436.         if (player_wbstartup != (struct WBStartup *)NULL)
  437.         {
  438.             Delay(50);
  439.  
  440.             player_request_port = FindPort(PLAYER_PORTNAME);
  441.  
  442.             if (!player_request_port)
  443.                 panic("didn't find request port after launching player");
  444.  
  445.             launched_player = YES;
  446.         }
  447.         else
  448.             panic("launch of player failed");
  449.     }
  450.  
  451. }
  452.  
  453. static void LocateLoaderPort()
  454. {
  455.     loader_request_port = FindPort(LOADER_PORTNAME);
  456.     if (!loader_request_port)
  457.     {
  458.  
  459. #ifdef DEBUG
  460.         printf("launching smus loader\n");
  461. #endif
  462.  
  463.         loader_wbstartup = Launch(launch_reply_port,loader_name,loader_launch_argv,1,0,NULL,4096);
  464.  
  465.         if (loader_wbstartup != (struct WBStartup *)NULL)
  466.         {
  467.             Delay(50);
  468.  
  469.             loader_request_port = FindPort(LOADER_PORTNAME);
  470.             if (!loader_request_port)
  471.                 panic("didn't find request port after launching loader");
  472.  
  473.             launched_loader = YES;
  474.         }
  475.         else
  476.             panic("launch of loader failed");
  477.     }
  478.  
  479. }
  480.  
  481. static void SMUSCleanupPorts()
  482. {
  483.     struct Message *msg;
  484.     long sigmask;
  485.  
  486.  
  487.     if (launch_reply_port)
  488.     {
  489.  
  490.         /* if a task shut down, cheat and set panic in progress
  491.          * so my TellSMUSProgram calls below won't be terminated */
  492.         if (CheckServerShutdown())
  493.             panic_in_progress = YES;
  494.  
  495.         /* if there isn't a terminate in progress and we didn't launch
  496.          * the player but we do have a request port for it (meaning
  497.          * it's running) or we did launch it, send a terminate message
  498.          * to it & ditto for the loader */
  499.  
  500.         if (!terminate_in_progress && ((!launched_player && player_request_port) || player_wbstartup))
  501.             TellSMUSProgram(player_request_port,PLEASE_TERMINATE_IMMEDIATELY,NULL,NULL);
  502.  
  503.         if (!terminate_in_progress && ((!launched_loader && loader_request_port) || loader_wbstartup))
  504.             TellSMUSProgram(loader_request_port,PLEASE_TERMINATE_IMMEDIATELY,NULL);
  505.  
  506.         while (player_wbstartup || loader_wbstartup)
  507.         {
  508. #ifdef DEBUG
  509.             printf("waiting for player and loader to terminate\n");
  510. #endif
  511.             sigmask = (1 << launch_reply_port->mp_SigBit);
  512.  
  513.             Wait(sigmask);
  514.             CheckServerShutdown();
  515.         }
  516.         DeletePort(launch_reply_port);
  517.     }
  518.  
  519.     if (reply_request_port)
  520.     {
  521.         while (GetMsg(reply_request_port)) ;
  522.  
  523.         DeletePort(reply_request_port);
  524.     }
  525.  
  526. }
  527.  
  528. SMUSInitServer()
  529. {
  530.     add_cleanup(SMUSCleanupPorts);
  531.  
  532.     launch_reply_port = CreatePort(0L,0L);
  533.     if (!launch_reply_port)
  534.         panic("can't create launch reply port");
  535.  
  536.     reply_request_port = CreatePort(0L,0L);
  537.     if (!reply_request_port)
  538.         panic("can't create request reply port");
  539.  
  540.     LocateLoaderPort();
  541.  
  542.     LocatePlayerPort();
  543. }
  544.  
  545.  
  546.  
  547. /* ------------ RPC calls ----------------------------------- */
  548.  
  549.  
  550. /* RPC Remote Procedure Call interface to the SMUS loader and player */
  551.  
  552. /* loads a song, does not insure all samples are loaded */
  553. void *LoadSong(songname)
  554. char *songname;
  555. {
  556.     struct PlayerRequest *rp;
  557.  
  558.     rp = TellSMUSProgram(loader_request_port,PLEASE_LOAD_SONG,songname,NULL);
  559.     if (rp->status == ES_OK)
  560.         return(rp->data.song_pointer);
  561.     else
  562.         return(NULL);
  563. }
  564.  
  565. /* loads any samples necessary to insure all samples are loaded for songid */
  566. int AttachSamples(songptr)
  567. void *songptr;
  568. {
  569.     struct PlayerRequest *rp;
  570.  
  571.     rp = TellSMUSProgram(loader_request_port,PLEASE_ATTACH_SAMPLES,songptr,NULL);
  572.     return(rp->status);
  573. }
  574.  
  575. /* unloads song but does not remove song's samples */
  576. UnloadSong(songptr)
  577. void *songptr;
  578. {
  579.     struct PlayerRequest *rp;
  580.  
  581.     rp = TellSMUSProgram(loader_request_port,PLEASE_UNLOAD_SONG,songptr,NULL);
  582.     return(rp->status);
  583. }
  584.  
  585. /* unloads all samples that aren't referenced by a song or "tagged" */
  586. PurgeUntaggedSamples()
  587. {
  588.     struct PlayerRequest *rp;
  589.  
  590.     rp = TellSMUSProgram(loader_request_port,PLEASE_PURGE_UNTAGGED_SAMPLES,NULL,NULL);
  591.     return(rp->status);
  592. }
  593.  
  594.  
  595. /* loads all samples and songs found in CAT of samples and songs */
  596. LoadCATArchive(catname)
  597. char *catname;
  598. {
  599.     struct PlayerRequest *rp;
  600.  
  601.     rp = TellSMUSProgram(loader_request_port,PLEASE_LOAD_CAT_SAMPLES,catname,NULL);
  602.     return(rp->status);
  603. }
  604.  
  605.  
  606. /* loads any samples necessary to insure all samples are loaded for all 
  607.  * songs */
  608. AttachAllSamples(replyport)
  609. struct MsgPort *replyport;
  610. {
  611.     struct PlayerRequest *rp;
  612.  
  613.     rp = TellSMUSProgram(loader_request_port,PLEASE_ATTACH_ALL_SAMPLES,NULL,NULL);
  614.     return(rp->status);
  615. }
  616.  
  617. /* clears "tag" bits for all samples */
  618. ClearSampleTags()
  619. {
  620.     struct PlayerRequest *rp;
  621.  
  622.     rp = TellSMUSProgram(loader_request_port,PLEASE_CLEAR_SAMPLE_TAGS,NULL,NULL);
  623.     return(rp->status);
  624. }
  625.  
  626. /* set "tag" bit for samplename */
  627. SetSampleTag(samplename)
  628. char *samplename;
  629. {
  630.     struct PlayerRequest *rp;
  631.  
  632.     rp = TellSMUSProgram(loader_request_port,PLEASE_TAG_NAMED_SAMPLE,samplename,NULL);
  633.     return(rp->status);
  634. }
  635.  
  636. /* play a song, don't return until it's finished */
  637. PlaySong(songptr)
  638. void *songptr;
  639. {
  640.     struct PlayerRequest *rp;
  641.  
  642.     rp = TellSMUSProgram(player_request_port,PLEASE_PLAY_SONG,songptr,NULL);
  643.     return(rp->status);
  644. }
  645.  
  646. /* shut down the server, free everything, exit */
  647. SMUSTerminateServer()
  648. {
  649.     CheckServerShutdown();
  650.  
  651.     terminate_in_progress = YES;
  652.  
  653.     /* if we didn't launch the loader but the loader_request_port is
  654.      * non-null, we did find the loader, or if loader_wbstartup is
  655.      * non-null, we did launch the loader and it is apparently still
  656.      * alive, tell it to shutdown */
  657.  
  658.     if ((!launched_loader && loader_request_port) || loader_wbstartup)
  659.         TellSMUSProgram(loader_request_port,PLEASE_TERMINATE_IMMEDIATELY,NULL,NULL);
  660.  
  661.     /* same for the player */
  662.  
  663.     if ((!launched_player && player_request_port) || player_wbstartup)
  664.         TellSMUSProgram(player_request_port,PLEASE_TERMINATE_IMMEDIATELY,NULL,NULL);
  665. }
  666.  
  667. /* start playing a song but return immediately */
  668. StartSong(songptr)
  669. void *songptr;
  670. {
  671.     struct PlayerRequest *rp;
  672.  
  673.     rp = TellSMUSProgram(player_request_port,PLEASE_PLAY_SONG_IMMEDIATE_REPLY,songptr,NULL);
  674.     return(rp->status);
  675. }
  676.  
  677. SetSampleTagsOfSong(songptr)
  678. void *songptr;
  679. {
  680.     struct PlayerRequest *rp;
  681.  
  682.     rp = TellSMUSProgram(loader_request_port,PLEASE_TAG_SONGS_SAMPLES,songptr,NULL);
  683.     return(rp->status);
  684. }
  685.  
  686. /* terminate the loader now and terminate the player when it's finished
  687.  * the current song (terminates immediately if no song is playing) */
  688. TerminateServerWhenFinished()
  689. {
  690.     terminate_in_progress = YES;
  691.  
  692.     TellSMUSProgram(player_request_port,PLEASE_TERMINATE_WHEN_FINISHED,NULL,NULL);
  693.     TellSMUSProgram(loader_request_port,PLEASE_TERMINATE_IMMEDIATELY,NULL,NULL);
  694. }
  695.  
  696. /* abort the currently playing song but don't terminate the server */
  697. AbortSongImmediately()
  698. {
  699.     struct PlayerRequest *rp;
  700.  
  701.     rp = TellSMUSProgram(player_request_port,PLEASE_ABORT_IMMEDIATELY,NULL,NULL);
  702.     return(rp->status);
  703. }
  704.  
  705. /* start a fade out which will eventually abort the song, but don't terminate
  706.  * the server */
  707. StartFadeOut()
  708. {
  709.     struct PlayerRequest *rp;
  710.  
  711.     rp = TellSMUSProgram(player_request_port,PLEASE_START_FADEOUT,NULL,NULL);
  712.     return(rp->status);
  713. }
  714.  
  715.  
  716. /* don't return until the currently playing song has finished.  Returns
  717.  * immediately if no song is playing */
  718. WaitTilSongFinishes()
  719. {
  720.     struct PlayerRequest *rp;
  721.  
  722.     rp = TellSMUSProgram(player_request_port,PLEASE_REPLY_WHEN_SONG_FINISHES,NULL,NULL);
  723.     return(rp->status);
  724. }
  725.  
  726.  
  727. /* ------------------------- error text --------------------------- */
  728.  
  729.  
  730. /* given a SMUS errno, return text of what that error is */
  731. char *SMUSErrorText(errno)
  732. int errno;
  733. {
  734.     char *text;
  735.  
  736.     switch(errno)
  737.     {
  738.  
  739.         case ES_INVALID_COMMAND:
  740.             text = "invalid command";
  741.             break;
  742.  
  743.         case ES_SERVER_ABORTED:
  744.             text = "server aborted";
  745.             break;
  746.  
  747.         case ES_NOT_IMPLEMENTED:
  748.             text = "command not implemented";
  749.             break;
  750.  
  751.         case ES_ALREADY_PLAYING:
  752.             text = "player is already playing";
  753.             break;
  754.  
  755.         case ES_LOAD_FAILED:
  756.             text = "load failed";
  757.             break;
  758.  
  759.         case ES_NOT_ALL_SAMPLES_PRESENT:
  760.             text = "not all samples present";
  761.             break;
  762.  
  763.         case ES_SONG_IS_PLAYING:
  764.             text = "song is playing";
  765.             break;
  766.  
  767.         case ES_BAD_SONG_ADDRESS:
  768.             text = "address of song is invalid";
  769.             break;
  770.  
  771.         case ES_OK:
  772.             text = "OK";
  773.             break;
  774.  
  775.         case ES_PENDING:
  776.             text = "request pending";
  777.             break;
  778.  
  779.         case ES_PROCESSING:
  780.             text = "request being processed";
  781.             break;
  782.  
  783.         case ES_NOT_FOUND:
  784.             text = "couldn't find specified item";
  785.             break;
  786.  
  787.         default:
  788.             text = "unknown error type";
  789.             break;
  790.  
  791.     }
  792.     return(text);
  793. }
  794.  
  795. PrintSMUSError(errno)
  796. int errno;
  797. {
  798.     puts(SMUSErrorText(errno));
  799. }
  800.