home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 377b.lha / devices / narrator / narrator.c next >
Encoding:
C/C++ Source or Header  |  1980-02-03  |  18.0 KB  |  606 lines

  1. /* Copyright (c) 1990 Commodore-Amiga, Inc.
  2.  *
  3.  * This example is provided in electronic form by Commodore-Amiga, Inc. for
  4.  * use with the 1.3 revisions of the Addison-Wesley Amiga reference manuals. 
  5.  * The 1.3 Addison-Wesley Amiga Reference Manual series contains additional
  6.  * information on the correct usage of the techniques and operating system
  7.  * functions presented in this example.  The source and executable code of
  8.  * this example may only be distributed in free electronic form, via bulletin
  9.  * board or as part of a fully non-commercial and freely redistributable
  10.  * diskette.  Both the source and executable code (including comments) must
  11.  * be included, without modification, in any copy.  This example may not be
  12.  * published in printed form or distributed with any commercial product.
  13.  * However, the programming techniques and support routines set forth in
  14.  * this example may be used in the development of original executable
  15.  * software products for Commodore Amiga computers.
  16.  * All other rights reserved.
  17.  * This example is provided "as-is" and is subject to change; no warranties
  18.  * are made.  All use is at your own risk.  No liability or responsibility
  19.  * is assumed.
  20.  */
  21.  
  22. /* A Simple program to show speech on the amiga.
  23.  * If you do not define the flag FACE_ON, 
  24.  * ALL code involved with reading mouth shapes is excluded.
  25.  * If you do not define the flag PARSE,  ALL code involved
  26.  * with parsing the command line is excluded, and defaults
  27.  * are used. Code by Dave Lucas.
  28.  *
  29.  * Lattice use lc -b1 -cfist -v -y. Link with lc.lib and amiga.lib. 
  30.  *
  31.  *   FACE_ON      PARSE    ~Lines'o'Source    ~Executeable size
  32.  *
  33.  *      0           0         185                 8.8K
  34.  *      0           1         360                10.7K
  35.  *      1           0         355                10.4K
  36.  *      1           1         540                12.3K
  37.  */
  38.  
  39. #define FACE_ON
  40. #define PARSE
  41.  
  42. #include <exec/types.h>
  43. #include <exec/io.h>
  44. #include <intuition/intuition.h>
  45. #include <devices/narrator.h>
  46. #include <libraries/translator.h>
  47. #include <libraries/dos.h>
  48.  
  49. #include <proto/all.h>
  50. #include <stdio.h>
  51. #include <stdlib.h>
  52. #include <string.h>
  53.  
  54. #ifdef LATTICE
  55. int CXBRK(void) { return(0); }  /* Disable Lattice CTRL/C handling */
  56. int chkabort(void) { return(0); }  /* really */
  57. #endif
  58.  
  59.  
  60. /* The length of the English and phonetic buffers. */
  61. #define ENGLISH_TEXT_LEN 25   /* Just long enough for default */
  62. /* Input line is restricted to nnn chars (AmigaDOS), but */
  63. #define PHONEME_TEXT_LEN 512  /* Phonemes are longer than English. */
  64.  
  65.  
  66. void DrawFace(void);
  67. void DrawMouth(USHORT half_w,USHORT half_h);
  68. extern LONG ParseArgs (int argc, char **argv, UBYTE *show_face, UWORD *sex,
  69.                     UWORD *inflect, UWORD *samp, UWORD *pitch, UWORD *speed,
  70.                     UWORD *vol, char **text);
  71. extern void BadOpt(char *option);
  72. void CleanUp(void);
  73.  
  74. /* Which audio channels to use. */
  75. BYTE audio_chan[] = {3, 5, 10, 12};
  76.  
  77. /* Pointer to translator library vectors. */
  78. struct Library *TranslatorBase = NULL;
  79.  
  80. struct MsgPort write_port;
  81. struct narrator_rb voice_io;
  82.  
  83. /* Indicative of the Open() return. */
  84. UBYTE NarratorOpenError = -1;
  85. /* Indicative of a Translations success. */
  86. UBYTE TranslatorError = 0;
  87.  
  88. UBYTE EnglBuffer[ENGLISH_TEXT_LEN] = "This is amiga speaking.";
  89. UBYTE PhonBuffer[PHONEME_TEXT_LEN] = "DHIHS IHZ AHMIY3GAH SPIY4KIHNX.";
  90.  
  91. struct IntuitionBase *IntuitionBase = NULL;
  92. struct GfxBase *GfxBase = NULL;
  93.  
  94. #ifdef FACE_ON
  95. /* Pen numbers to draw gadget borders/images/text with. */
  96. #define REDP   3        /* Color in register 3 once was red. */
  97. #define BLKP   2        /* Color in register 2 was black. */
  98. #define WHTP   1        /* Color in register 1 was white. */
  99. #define BLUP   0        /* Color in register 0 was blue. */
  100.  
  101. /* These are used for face rendering. */
  102. #define FACE_ET     12 /* top of eyes */
  103. #define FACE_EB     22 /* bottom of eyes */
  104. #define FACE_LIP_T  23 /* top of mouth clear area */
  105. #define FACE_MID_H  42 /* the middle of the mouth, heightwise */
  106. #define FACE_H      80 /* overall window height */
  107. #define FACE_ELL    24 /* left eye left side */
  108. #define FACE_ELR    40 /* left eye right side */
  109. #define FACE_INIT_W 32 /* left point for initial mouth line */
  110. #define FACE_MID_W  56 /* the middle of the mouth, widthwise */
  111. #define FACE_ERL    72 /* right eye left side */
  112. #define FACE_ERR    88 /* right eye right side */
  113. #define FACE_W     120 /* overall window width */
  114.  
  115. struct NewWindow NewFaceWindow =
  116. {
  117.    0, 11,               /* Start LeftEdge, TopEdge. */
  118.    FACE_W, FACE_H,      /* Width, Height. */
  119.    -1, -1,              /* DetailPen, BlockPen. */
  120.    0,                   /* IDCMP FLAGS. */
  121.    WINDOWDRAG | WINDOWDEPTH | GIMMEZEROZERO, /* Flags. */
  122.    NULL,                /* No pointer to FirstGadget. */
  123.    NULL,                /* No pointer to first CheckMark. */
  124.    NULL,                /* No Title. */
  125.    NULL,                /* No pointer to Screen. */
  126.    NULL,                /* No pointer to BitMap. */
  127.    FACE_W, FACE_H,      /* Minimum sizeable to (NA-not sizeable). */
  128.    FACE_W, FACE_H,      /* Maximum sizeable to (NA-not sizeable). */
  129.    WBENCHSCREEN         /* Type of screen window appears in. */
  130. };
  131.  
  132. struct Window *FaceWindow = NULL;
  133. struct IntuiMessage *MyIntuiMessage;
  134.  
  135. struct MsgPort read_port;
  136. struct mouth_rb mouth_io;
  137. #endif /* FACE_ON defined */
  138.  
  139. /** Start of code ***************************/
  140. VOID main(int argc,char **argv)
  141. {
  142.     ULONG Signals;        /* Wait() tells me which to look at. */
  143.     UBYTE *pp_string;
  144.     UWORD rate, pitch, mode, sex, volume, sampfreq;
  145.     UBYTE show_face = 0;
  146.     /* Let CleanUp() know these signals not allocated yet. */
  147.     write_port.mp_SigBit = -1;
  148. #ifdef FACE_ON
  149.     read_port.mp_SigBit = -1;
  150. #endif /* FACE_ON defined */
  151.  
  152.     pp_string = &EnglBuffer[0];
  153.  
  154.     rate = DEFRATE;
  155.     pitch = DEFPITCH;
  156.     mode = DEFMODE;
  157.     sex = DEFSEX;
  158.     volume = DEFVOL;
  159.     sampfreq = DEFFREQ;
  160.  
  161. #ifdef PARSE
  162.     ParseArgs(argc, argv, &show_face, &sex, &mode, &sampfreq, &pitch, &rate,
  163.      &volume, (APTR)&pp_string);
  164. #endif /* PARSE defined */
  165.  
  166.     /* Open those libraries that the program uses directly. */
  167.     if ((IntuitionBase = (struct IntuitionBase *)
  168.      OpenLibrary("intuition.library", 33)) == NULL)
  169.     {
  170.         fprintf(stderr, "Can't open the intuition library\n");
  171.         CleanUp();
  172.         exit(RETURN_WARN);
  173.     }
  174.  
  175.     if ((GfxBase = (struct GfxBase *)
  176.      OpenLibrary("graphics.library", 33)) == NULL)
  177.     {
  178.         fprintf(stderr, "Can't open the graphics library\n");
  179.         CleanUp();
  180.         exit(RETURN_WARN);
  181.     }
  182.  
  183.     if ((TranslatorBase =  (struct Library *)
  184.      OpenLibrary("translator.library", 0L)) == NULL)
  185.     {
  186.         fprintf(stderr, "Can't open the translator library\n");
  187.         CleanUp();
  188.         exit(RETURN_WARN);
  189.     }
  190.  
  191.     if ((TranslatorError = Translate((UBYTE *)pp_string,
  192.      strlen(pp_string), (UBYTE *)PhonBuffer, PHONEME_TEXT_LEN)) != 0)
  193.     {
  194.         fprintf(stderr, "Translator won't. (%lx)\n",TranslatorError);
  195.     }
  196.  
  197.     if ((NarratorOpenError =
  198.      OpenDevice("narrator.device",0, (struct IORequest *) &voice_io, 0))!=0)
  199.     {
  200.         fprintf(stderr, "Can't open the narrator device\n");
  201.         CleanUp();
  202.         exit(RETURN_WARN);
  203.     }
  204.  
  205.     /* Set up the write port, allocate the signal */
  206.     /* and the message. */
  207.     if ((write_port.mp_SigBit = AllocSignal(-1)) == -1)
  208.     {
  209.         fprintf(stderr, "Couldn't Allocate write Signal bit\n");
  210.         CleanUp();
  211.         exit(RETURN_WARN);
  212.     }
  213.     write_port.mp_Node.ln_Name = "speech_write";
  214.     write_port.mp_Node.ln_Type = NT_MSGPORT;
  215.     write_port.mp_Flags = PA_SIGNAL;
  216.     write_port.mp_SigTask = (struct Task *)FindTask(NULL);
  217.  
  218.     NewList(&write_port.mp_MsgList);
  219.  
  220.     /* Set up the write channel information. */
  221.     voice_io.message.io_Command = CMD_WRITE;
  222.     voice_io.message.io_Offset = 0;
  223.     voice_io.message.io_Data = (APTR)PhonBuffer;
  224.     voice_io.message.io_Length = strlen(PhonBuffer);
  225.  
  226.     voice_io.message.io_Message.mn_Node.ln_Type = NT_MSGPORT;
  227.     voice_io.message.io_Message.mn_Length = sizeof(voice_io);
  228.     voice_io.message.io_Message.mn_ReplyPort = &write_port;
  229.     voice_io.message.io_Unit = 0;
  230.  
  231.     voice_io.ch_masks = (UBYTE *)audio_chan;
  232.     voice_io.nm_masks = sizeof(audio_chan);
  233.     voice_io.mouths = show_face;
  234.     voice_io.rate = rate;
  235.     voice_io.pitch = pitch;
  236.     voice_io.mode = mode;
  237.     voice_io.sex = sex;
  238.     voice_io.volume = volume;
  239.     voice_io.sampfreq = sampfreq;
  240.  
  241. #ifdef FACE_ON
  242.     if (show_face)
  243.     {
  244.         /* Set up the read port, allocate the signal */
  245.         /* and the message. */
  246.         read_port.mp_Node.ln_Type = NT_MSGPORT;
  247.         read_port.mp_Flags = PA_SIGNAL;
  248.         if ((read_port.mp_SigBit = AllocSignal(-1)) == -1)
  249.         {
  250.             fprintf(stderr, "Couldn't Allocate read Signal bit\n");
  251.             CleanUp();
  252.             exit(RETURN_WARN);
  253.         }
  254.         read_port.mp_SigTask = (struct Task *)FindTask(NULL);
  255.         NewList(&read_port.mp_MsgList);
  256.  
  257.         /* Set up the read channel information. */
  258.         mouth_io.voice = voice_io;
  259.         mouth_io.width = 0;
  260.         mouth_io.height = 0;
  261.         mouth_io.voice.message.io_Message.mn_ReplyPort = &read_port;
  262.         mouth_io.voice.message.io_Command = CMD_READ;
  263.         mouth_io.voice.message.io_Error = 0;
  264.  
  265.         if ((FaceWindow = OpenWindow(&NewFaceWindow))
  266.          == NULL)
  267.         {
  268.             fprintf(stderr, "Couldn't open the face window.\n");
  269.             CleanUp();
  270.             exit(RETURN_WARN);
  271.         }
  272.         DrawFace();
  273.     }
  274. #endif /* FACE_ON defined */
  275.  
  276.     SendIO((struct IORequest *) &voice_io );
  277.  
  278. #ifdef FACE_ON
  279.     if (show_face)
  280.     {
  281.         SendIO((struct IORequest *) &mouth_io );
  282.     }
  283. #endif /* FACE_ON defined */
  284.  
  285.     /* Wait() lets the rest of the system run while */
  286.     /* this program sleeps. */
  287.     for (;;)
  288.     {
  289. #ifdef FACE_ON
  290.         if (show_face)
  291.             Signals = Wait(
  292.           (1L << voice_io.message.io_Message.mn_ReplyPort->mp_SigBit)
  293.         | (1L << mouth_io.voice.message.io_Message.mn_ReplyPort->mp_SigBit));
  294.         else
  295. #endif /* FACE_ON defined */
  296.             Signals = Wait(
  297.           (1L << voice_io.message.io_Message.mn_ReplyPort->mp_SigBit));
  298.  
  299.         /* A voice SendIO() (Write) has completed. */
  300.         if (Signals & (1L <<
  301.          voice_io.message.io_Message.mn_ReplyPort->mp_SigBit))
  302.         {
  303.             /* Was it Sucessful? filter out the abort error. */
  304.             if (voice_io.message.io_Error == -2)
  305.                 voice_io.message.io_Error = 0;
  306.             if (voice_io.message.io_Error != 0)
  307.             {
  308.                 fprintf(stderr, "Narrator won't. (%ld)\n",
  309.                  voice_io.message.io_Error);
  310.                 voice_io.message.io_Error = 0;
  311.             }
  312.             CleanUp();
  313.             exit(RETURN_OK);
  314.         }
  315.  
  316. #ifdef FACE_ON
  317.         /* A mouth SendIO() (Read) has completed. */
  318.         if (Signals & (1L <<
  319.          mouth_io.voice.message.io_Message.mn_ReplyPort->mp_SigBit))
  320.         {
  321.  
  322.             USHORT LipWidth, LipHeight;
  323.  
  324.             LipWidth = mouth_io.width << 2;
  325.             LipHeight = mouth_io.height;
  326.  
  327.          DrawMouth(LipWidth, LipHeight);
  328.  
  329.             /* On occasion, the first request for a mouth shape is
  330.              * handled before the narrator realizes it's speaking.
  331.              * That's why this error is ignored.
  332.              */
  333.             if (mouth_io.voice.message.io_Error == ND_NoWrite)
  334.                 mouth_io.voice.message.io_Error = 0;
  335.  
  336.             if (mouth_io.voice.message.io_Error == 0)
  337.              SendIO((struct IORequest *) &mouth_io );
  338.             else
  339.             {
  340.                 fprintf(stderr, "Narrator won't mouth. (%ld)\n",
  341.                  mouth_io.voice.message.io_Error);
  342.                 CleanUp();
  343.                 exit(RETURN_WARN);
  344.             }
  345.         }
  346. #endif /* FACE_ON defined */
  347.     } /* The for will never exit through here, no CleanUp() needed. */
  348. }  /* main */
  349.  
  350.     /* handle abort like this !!!! */
  351. /*  AbortIO(&voice_io);
  352.     voice_io.message.io_Error = 0;
  353.     mouth_io.voice.message.io_Error = 0;
  354. */
  355.  
  356. #ifdef FACE_ON
  357. void DrawFace(void)
  358. {
  359.     /* Set pen to White, fill whole window. */
  360.     SetAPen(FaceWindow->RPort, WHTP);
  361.     RectFill(FaceWindow->RPort, 0, 0, FACE_W, FACE_H);
  362.  
  363.     /* Set pen to Blue, do eyes. */
  364.     SetAPen(FaceWindow->RPort, BLUP);
  365.     RectFill(FaceWindow->RPort, FACE_ELL, FACE_ET, FACE_ELR, FACE_EB);
  366.     RectFill(FaceWindow->RPort, FACE_ERL, FACE_ET, FACE_ERR, FACE_EB);
  367.  
  368.     /* Do gob. */
  369.     DrawMouth(FACE_INIT_W, 1);
  370. }
  371. void DrawMouth(USHORT half_w,USHORT half_h)
  372. {
  373.     WaitBOVP(&FaceWindow->WScreen->ViewPort);
  374.  
  375.     /* Clear the entire mouth area. */
  376.     SetAPen(FaceWindow->RPort, WHTP);
  377.     RectFill(FaceWindow->RPort, 0, FACE_LIP_T, FACE_W, FACE_H);
  378.  
  379.     /* Draw a new mouth. */
  380.     SetAPen(FaceWindow->RPort, REDP);
  381.  
  382.     Move(FaceWindow->RPort, FACE_MID_W - half_w, FACE_MID_H);
  383.     Draw(FaceWindow->RPort, FACE_MID_W,          FACE_MID_H - half_h);
  384.     Draw(FaceWindow->RPort, FACE_MID_W + half_w, FACE_MID_H);
  385.     Draw(FaceWindow->RPort, FACE_MID_W,          FACE_MID_H + half_h);
  386.     Draw(FaceWindow->RPort, FACE_MID_W - half_w, FACE_MID_H);
  387. }
  388. #endif /* FACE_ON defined */
  389.  
  390.  
  391. #ifdef PARSE
  392. /** ************************************************************************/
  393. LONG
  394. ParseArgs (int argc,
  395.            char **argv,
  396.            UBYTE *show_face,
  397.            UWORD *sex,
  398.            UWORD *inflect,
  399.            UWORD *samp,
  400.            UWORD *pitch,
  401.            UWORD *speed,
  402.            UWORD *vol,
  403.            char **text)
  404. {
  405.     int len;
  406.  
  407.     argc--;
  408.     argv++;
  409.  
  410.     while (argc > 0 && argv[0][0] == '-')
  411.     {
  412.     len = strlen(*argv);
  413.     switch (argv[0][1])
  414.     {
  415.         case 'm':        /* -Male */
  416.             if ((!strncmp(*argv, "-m", len)) ||
  417.                 (!strncmp(*argv, "-male", len)))
  418.             {
  419.                 *sex = FEMALE;
  420.             }
  421.             else
  422.             {
  423.                 BadOpt(*argv);
  424.                 return(1);
  425.             }
  426.             break;
  427.  
  428.         case 'f':        /* -face -Female */
  429.             if ((!strncmp(*argv, "-f", len)) ||
  430.                      (!strncmp(*argv, "-female", len)))
  431.             {
  432.                 *sex = FEMALE;
  433.             }
  434. #ifdef FACE_ON
  435.             else if (!strncmp(*argv, "-face", len))
  436.             {
  437.                 *show_face = 1;
  438.             }
  439. #endif /* FACE_ON defined */
  440.             else
  441.             {
  442.                 BadOpt(*argv);
  443.                 return(1);
  444.             }
  445.             break;
  446.  
  447.         case 'r':        /* -Robotic */
  448.             if ((!strncmp(*argv, "-r", len)) ||
  449.                 (!strncmp(*argv, "-robotic", len)))
  450.             {
  451.                     *inflect = ROBOTICF0;
  452.             }
  453.             else
  454.             {
  455.                 BadOpt(*argv);
  456.                 return(1);
  457.             }
  458.             break;
  459.  
  460.         case 'n':        /* -Natural */
  461.             if ((!strncmp(*argv, "-n", len)) ||
  462.                 (!strncmp(*argv, "-natural", len)))
  463.             {
  464.                     *inflect = NATURALF0;
  465.             }
  466.             else
  467.             {
  468.                 BadOpt(*argv);
  469.                 return(1);
  470.             }
  471.             break;
  472.  
  473.         case 's':        /* -Speed <frequency> */
  474.             if ((!strncmp(*argv, "-s", len)) ||
  475.                 (!strncmp(*argv, "-speed", len)))
  476.             {
  477.                 argc--; argv++;
  478.                 *speed = atoi(*argv);
  479.             }
  480.             else
  481.             {
  482.                 BadOpt(*argv);
  483.                 return(1);
  484.             }
  485.             break;
  486.  
  487.         case 'p':        /* -pitch <frequency> */
  488.             if ((!strncmp(*argv, "-p", len)) ||
  489.                 (!strncmp(*argv, "-pitch", len)))
  490.             {
  491.                 argc--; argv++;
  492.                 *pitch = atoi(*argv);
  493.             }
  494.             else
  495.             {
  496.                 BadOpt(*argv);
  497.                 return(1);
  498.             }
  499.             break;
  500.  
  501.         case 'v':        /* -vol <level> */
  502.             if ((!strncmp(*argv, "-v", len)) ||
  503.                 (!strncmp(*argv, "-vol", len)) ||
  504.                 (!strncmp(*argv, "-volume", len)))
  505.             {
  506.                 argc--; argv++;
  507.                 *vol = atoi(*argv);
  508.             }
  509.             else
  510.             {
  511.                 BadOpt(*argv);
  512.                 return(1);
  513.             }
  514.             break;
  515.  
  516.         default:
  517.             BadOpt(*argv);
  518.             return(1);
  519.             break;
  520.  
  521.         }
  522.         argc--; argv++;
  523.     }
  524.  
  525.     /* The last arg is the English string to speak. */
  526.     if (argc > 0)
  527.     {
  528.         *text = *argv;
  529.     }
  530.  
  531.     if (argc > 1)
  532.     {
  533.         fprintf(stderr, "Arguments after text ignored.\n");
  534.     }
  535.  
  536.     /* Narrator has it's limits, be sure to abide by them. */
  537.     if (*speed > MAXRATE)
  538.         *speed = MAXRATE;
  539.     if (*speed < MINRATE)
  540.         *speed = MINRATE;
  541.  
  542.     if (*pitch > MAXPITCH)
  543.         *pitch = MAXPITCH;
  544.     if (*pitch < MINPITCH)
  545.         *pitch = MINPITCH;
  546.  
  547.     if (*samp > MAXFREQ)
  548.         *samp = MAXFREQ;
  549.     if (*samp < MINFREQ)
  550.         *samp = MINFREQ;
  551.  
  552.     if (*vol > MAXVOL)
  553.         *vol = MAXVOL;
  554.     if (*vol < MINVOL)
  555.         *vol = MINVOL;
  556.  
  557. }
  558.  
  559. void BadOpt(char *option)
  560. {
  561.     char *whoami;
  562.     whoami = "sayit";
  563.  
  564.     fprintf(stderr, "%s: option \"%s\" not supported.\n", whoami, *option);
  565.     fprintf(stderr, "Usage: %s\n", whoami); 
  566.     fprintf(stderr, "  -m or -male   -f or -female\n");
  567.     fprintf(stderr, "  -r or -robot  -n or -natural\n");
  568.     fprintf(stderr, "  -s or -speed  <WPM 40-400>\n");
  569.     fprintf(stderr, "  -p or -pitch  <65-320>\n");
  570.     fprintf(stderr, "  -v or -volume <0-64>\n");
  571. #ifdef FACE_ON
  572.     fprintf(stderr, "  -face\n");
  573. #endif /* FACE_ON defined */
  574.     fprintf(stderr, "  [\"quoted text\"]\n");
  575.  
  576.     CleanUp();
  577. }
  578. #endif /* PARSE defined */
  579.  
  580. /* Deallocate any memory, and close all of the
  581.  * windows/screens/devices/libraries in reverse order to
  582.  * make things work smoothly. And be sure to check
  583.  * that the open/allocation was successful before
  584.  * closing/deallocating.
  585.  */
  586. void CleanUp(void)
  587. {
  588.    if (write_port.mp_SigBit != -1)
  589.       FreeSignal(write_port.mp_SigBit);
  590. #ifdef FACE_ON
  591.    if (read_port.mp_SigBit != -1)
  592.       FreeSignal(read_port.mp_SigBit);
  593.    if (FaceWindow != NULL)
  594.       CloseWindow(FaceWindow);
  595. #endif /* FACE_ON defined */
  596.    if (NarratorOpenError == 0)
  597.       CloseDevice((struct IORequest *) &voice_io);
  598.    if (TranslatorBase != NULL)
  599.       CloseLibrary(TranslatorBase);
  600.    if (GfxBase != NULL)
  601.       CloseLibrary(GfxBase);
  602.    if (IntuitionBase != NULL)
  603.       CloseLibrary(IntuitionBase);
  604.    return;
  605. }
  606.