home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD 2.1 / Amiga Developer CD v2.1.iso / Reference / Amiga_Mail_Vol1 / Audio / DTMF < prev    next >
Encoding:
Text File  |  1999-10-27  |  21.2 KB  |  733 lines

  1. (c)  Copyright 1989-1999 Amiga, Inc.   All rights reserved.
  2. The information contained herein is subject to change without notice, and 
  3. is provided "as is" without warranty of any kind, either expressed or implied.  
  4. The entire risk as to the use of this information is assumed by the user.
  5.  
  6.  
  7.  
  8.  
  9.                   DTMF - A Program for Generating 
  10.                         Touch-Tone Signals
  11.  
  12.                            by Adam Levin
  13.  
  14.  
  15.      Although the use of touch-tones as a method of dialing the telephone
  16. is an international standard, not everyone has a telephone capable of
  17. generating touch-tone signals.  The program presented here will allow
  18. you to create touch-tone signals with your Amiga's audio hardware which 
  19. you can amplify and use to dial your telephone.
  20.  
  21.      With the DTMF program you can enter alternate-carrier account numbers,
  22. access voice-mail systems, dial alphabetic "numbers" and retrieve messages 
  23. from your telephone answering machine (if it uses touch-tones).  In addition 
  24. DTMF can generate the four tones 12-key keypads cannot: 'A', 'B', 'C' and 'D'.
  25.  
  26.  
  27.      DTMF stands for Dual Tone Multi-Frequency which is the method of using 
  28. pairs of tones from a palette of eight frequencies to generate sixteen unique,
  29. machine-recognizable signals.  For each touch-tone key that you press, two 
  30. tones are generated, mixed together and sent down the wires to your phone 
  31. company where they are interpreted as the key you have pressed.  
  32.  
  33.      Since the phone company's decoding machine is "listening" to your line 
  34. while you are dialing, any audible source of touch-tones can be used to dial:
  35. an audio recording, a well-trained voice, or, as in this case, the Amiga.  
  36. Pairs of tones are used, rather than single tones, so that outside noises 
  37. are less likely to be interpreted as key presses.
  38.  
  39.      To use DTMF to dial, you will need to be able to hear the audio output 
  40. of your Amiga.  The built-in speaker in your monitor is fine for this.
  41. You may also connect the Amiga's audio output to amplified speakers such as 
  42. the Panasonic RX-C36, Sony APM-007AV or Bose Roommates.
  43.  
  44.      Set the volume to a comfortable listening level and hold the telephone's
  45. mouthpiece about 1-10 inches from the speaker.  Experiment with the volume 
  46. level and handset distance to get consistent results.
  47.  
  48.  
  49. DTMF runs only from the CLI, and has the following syntax:
  50.  
  51.   DTMF NUMBER "(800) 555-1212" SIDE left|right|either SPEED slow|norm|fast
  52.  
  53.      To enter a phone number from the command line, type the keyword
  54. NUMBER followed by the number you want to dial.  If the phone number has an
  55. imbedded blank, it must be surrounded by quotes.
  56.  
  57.      The SIDE option allows you to specify which audio jack will be used 
  58. for DTMF's output.  If you don't specify which side, DTMF will try to use 
  59. the left side.  If the left side is in use, DTMF will try to use the right 
  60. side.  You may omit the SIDE keyword, and simply type left, right or either.
  61.  
  62.      The SPEED option allows you to specify how quickly the number will
  63. be dialed.  A SPEED of "fast" will dial as quickly as the Dual Tone Multi-
  64. Frequency standard allows.  A SPEED of "norm" will dial at one-half full
  65. speed and "slow" will dial at one-third full speed.  You may omit the 
  66. SPEED keyword and type just fast, norm or slow.
  67.  
  68.      If you don't enter a number on the command line,  DTMF will take its
  69. input from the standard input device.  This means that you can start up DTMF 
  70. and enter more than one phone-number pressing the RETURN key when you want
  71. the number dialed.  When you are done, just type control-backslash to quit:
  72.  
  73.    1> DTMF
  74.    (800) 555-1212
  75.    555-1212
  76.    Ctrl-\
  77.    1>
  78.  
  79. Because DTMF uses standard input when no number is specified on the command
  80. line, you can send a file containing phone numbers to DTMF with the 
  81. redirection symbol "<".  For example, if you have a file called Info which 
  82. contains:
  83.  
  84.    1 (800) 555-1212
  85.  
  86. and you type "DTMF <Info SIDE right SPEED norm", DTMF will dial the number
  87. in Info.
  88.  
  89. DTMF will recognize the following symbols in a phone number:
  90.  
  91.    o  The usual touch-tone characters: 0-9, * and #.
  92.  
  93.    o  The four other characters in the DTMF standard: A, B, C and D.
  94.  
  95.    o  The alphabetic characters found on most touch-tone keypads,
  96.       A-P  and  R-Y.  Letters Q and Z do not appear.  This means that 
  97.       you can dial those easy-to-remember but hard-to-dial mnemonic phone 
  98.       numbers many companies use.  Use lower case when dialing alphabetic
  99.       numbers to avoid confusion with the 4 extra DTMF tones A-D.
  100.  
  101.    o  A comma will make a two-second pause in the dialing sequence.
  102.  
  103. All other characters are ignored.
  104.  
  105.     Each character which generates touch-tones is converted to audio.device 
  106. period and cycle values by look-up tables.  These values are then put into 
  107. two IOAudio request blocks - one for the lower of the two tones, one for 
  108. the higher.  Since the Amiga has two audio channels per side the low and 
  109. high tones can be played on separate channels and the hardware will combine 
  110. them into one audio output.
  111.  
  112.      The calculations performed to obtain the period and cycle values
  113. are dependent upon the Amiga's clock and are therefore different for
  114. NTSC and PAL machines.  DTMF supports both versions.   Be sure and include
  115. the line "#define NTSC" in the source code for NTSC machines and 
  116. "#undef NTSC" for PAL machines.
  117.  
  118.  
  119.  
  120.  
  121. /*  DTMF - DualToneMulti-Frequency tone generator.
  122.  
  123.     v1.2  Adam Levin  Commodore-Amiga Technical Support
  124.  
  125.     Written with Manx's Aztec C v3.6a
  126.       compile with:  cc DTMF.c +L
  127.       link with:     ln DTMF.o -lm32 -lc32
  128.     Note that the math library is required.
  129. */
  130.  
  131. #include <exec/types.h>
  132. #include <exec/memory.h>
  133. #include <devices/audio.h>
  134. #include <libraries/dos.h>
  135. #include <hardware/dmabits.h>
  136. #include <graphics/gfxbase.h>
  137. #include <intuition/intuition.h>
  138. #include <stdio.h>
  139.  
  140. #define NUM_PAIRS   16  /*  Number of pairs of periods.  */
  141.  
  142.  
  143. #define DTMF_PRI    90    /*  Priority of the DTMF sounds.  */
  144. #define NUM_LEN     63    /*  Maximum number of chars in CLI "NUMBER".  */
  145.  
  146. #define SQUARE_WAVE_LEN    8L    /*  Length of audio sample.  */
  147. #define LO_TONES    DMAF_AUD0|DMAF_AUD1    /*  Audio channel masks.  */
  148. #define HI_TONES    DMAF_AUD2|DMAF_AUD3
  149. #define LEFT_SIDE   DMAF_AUD0|DMAF_AUD3
  150. #define RIGHT_SIDE  DMAF_AUD1|DMAF_AUD2
  151. #define ERROR_CANNOT_CREATE_PORT    ERROR_BAD_STREAM_NAME
  152. #define SLOW_SPEED    3    /*  Scaling factors for tone duration.  */
  153. #define NORM_SPEED    2
  154. #define FAST_SPEED    1
  155.  
  156. struct IntuitionBase *IntuitionBase;
  157. struct GfxBase *GfxBase;
  158.  
  159. BYTE *square_wave;    /*  Audio sample.  */
  160. UBYTE side[] = {LEFT_SIDE, RIGHT_SIDE};    /*  Audio channel choices.  */
  161. char number[NUM_LEN+1];    /*  CLI "NUMBER" argument.  */
  162. int speed = NORM_SPEED, side_cnt = sizeof(side);    /*  Scaling, # unit choices.  */
  163. struct Remember *remember;    /*  AllocRemember anchor.  */
  164. /*  Audio request blocks.  One for control, one each for low and high tones.  */
  165. struct IOAudio *cmd_ioA, *lo_ioA, *hi_ioA;
  166.  
  167.  
  168. /*
  169.     Valid keypad symbols.
  170.     '0'-'9', '*' and '#' are standard.
  171.     The keys 'A', 'B', 'C' and 'D' appear on 16-key keypads.
  172. */
  173.  
  174. char keypad[] = "0123456789*#ABCD";
  175.  
  176.  
  177. /*
  178.     Tone periods.
  179.     There is a pair of tones for each of the 16 keys on a DTMF keypad.
  180.     Each tone has been converted to an audio.device period with
  181.     the formula:
  182.  
  183.             1,000,000 / SQUARE_WAVE_LEN / (1 / CLOCK) / freq
  184.  
  185.     where SQUARE_WAVE_LEN is the length of the square wave sample,
  186.     CLOCK is the clock constant (NTSC = 3.579545, PAL = 3.546895), and
  187.     freq is the frequency of the tone.
  188.  
  189.     The order of the following pairs (0, 1, 2...) must match
  190.     the order of the symbols in the keypad string.
  191. */
  192.  
  193.        /*  N T S C   P E R I O D S  */
  194. USHORT periods[][2] = 
  195. {
  196.     { 475, 335 },    /*  0  */
  197.     { 642, 370 },    /*  1  */
  198.     { 642, 335 },    /*  2  */
  199.     { 642, 303 },    /*  3  */
  200.     { 581, 370 },    /*  4  */
  201.     { 581, 335 },    /*  5  */
  202.     { 581, 303 },    /*  6  */
  203.     { 525, 370 },    /*  7  */
  204.     { 525, 335 },    /*  8  */
  205.     { 525, 303 },    /*  9  */
  206.     { 475, 370 },    /*  *  */
  207.     { 475, 303 },    /*  #  */
  208.     { 642, 274 },    /*  A  */
  209.     { 581, 274 },    /*  B  */
  210.     { 525, 274 },    /*  C  */
  211.     { 475, 274 },    /*  D  */
  212.        /*   P A L   P E R I O D S  */
  213.     { 471, 332 },    /*  0  */
  214.     { 636, 367 },    /*  1  */
  215.     { 636, 332 },    /*  2  */
  216.     { 636, 300 },    /*  3  */
  217.     { 576, 367 },    /*  4  */
  218.     { 576, 332 },    /*  5  */
  219.     { 576, 300 },    /*  6  */
  220.     { 520, 367 },    /*  7  */
  221.     { 520, 332 },    /*  8  */
  222.     { 520, 300 },    /*  9  */
  223.     { 471, 367 },    /*  *  */
  224.     { 471, 300 },    /*  #  */
  225.     { 636, 271 },    /*  A  */
  226.     { 576, 271 },    /*  B  */
  227.     { 520, 271 },    /*  C  */
  228.     { 471, 271 }     /*  D  */
  229. };
  230.  
  231.  
  232. /*
  233.     Tone cycles.
  234.     Each tone pair must sound for at least 40 mS, according to DTMF
  235.     specifications.  A scaling factor (SPEED) is used to increase
  236.     this duration, when needed.  The formulas used were:
  237.  
  238.     lo_cycles = ceil((mS_DELAY / 1000.) / ( 1. / hi_freq))
  239.     hi_cycles = ceil(((float)hi_freq / (float)lo_freq) * (float)lo_cycles)
  240.  
  241.     where mS_DELAY was 40, and lo_freq and hi_freq were the frequencies
  242.     specified for the given tone pair.
  243. */
  244.  
  245. USHORT cycles[][2] = 
  246. {
  247.     { 53,  75 },    /*  0  */
  248.     { 48,  83 },    /*  1  */
  249.     { 53, 102 },    /*  2  */
  250.     { 59, 125 },    /*  3  */
  251.     { 48,  75 },    /*  4  */
  252.     { 53,  92 },    /*  5  */
  253.     { 59, 113 },    /*  6  */
  254.     { 48,  68 },    /*  7  */
  255.     { 53,  83 },    /*  8  */
  256.     { 59, 102 },    /*  9  */
  257.     { 48,  62 },    /*  *  */
  258.     { 59,  93 },    /*  #  */
  259.     { 65, 152 },    /*  A  */
  260.     { 65, 138 },    /*  B  */
  261.     { 65, 125 },    /*  C  */
  262.     { 65, 113 }     /*  D  */
  263. };
  264.  
  265.  
  266. /*
  267.     T O L O W E R
  268.     Converts upper case letters to their lower case counterparts.
  269.     Properly converts international ASCII characters.
  270.     This is a 'C' version of Bob "Kodiak" Burns' deCase assembler code.
  271. */
  272.  
  273. char
  274. tolower(x)
  275. char x;
  276. {
  277. UBYTE u;
  278.     u = x;
  279.     if ((u >= 0x41 && u <= 0x5a) ||
  280.         (u >= 0xc0 && u <= 0xd6) || 
  281.         (u >= 0xd8 && u <= 0xde)) x |= 0x20;
  282.  
  283.     return(x);
  284.  
  285. }    /* end tolower() */
  286.  
  287.  
  288. /*
  289.     S T R C M P I
  290.     String compare which ignores case.
  291. */
  292.  
  293. int
  294. strcmpi(s1, s2)
  295. char s1[], s2[];
  296. {
  297. int i = 0;
  298.  
  299.     while ( tolower(s1[i]) == tolower(s2[i]) )
  300.         if ( s1[i++] == '\0' ) return(0);
  301.     return((int) (s1[i] - s2[i]) );
  302.  
  303. }    /* end strcmpi() */
  304.  
  305.  
  306. /*
  307.     I S I N
  308.     Searches for a word in a list of words.
  309. */
  310.  
  311. int
  312. isin(word, words, numwords)
  313. char *word, *words[];
  314. int numwords;
  315. {
  316. int i;
  317.     for (i=0; i<numwords; i++)
  318.         if (strcmpi(word, words[i]) == 0) return(i);
  319.     return(-1);
  320. }    /* end isin() */
  321.  
  322.  
  323. /*
  324.     A R G S
  325.     Parse the CLI argument list.
  326. */
  327.  
  328. #define NUMBER_I        0
  329. #define SIDE_I          1
  330. #define     LEFT_I      2
  331. #define     RIGHT_I     3
  332. #define     EITHER_I    4
  333. #define SPEED_I         5
  334. #define     FAST_I      6
  335. #define     NORM_I      7
  336. #define     SLOW_I      8
  337.  
  338. #define NUM_FLAGS       9
  339.  
  340. BOOL
  341. args(argc, argv)
  342. int argc;
  343. char *argv[];
  344. {
  345. char *pointer, side_chr[6], speed_chr[5];
  346. static char *flags[NUM_FLAGS] =
  347.     { "NUMBER","SIDE","LEFT","RIGHT","EITHER","SPEED","FAST","NORM","SLOW" };
  348. int i, index;
  349.  
  350.     if (argc > 1 && argv[argc-1][0] == '?')
  351.     {
  352.         (void)fprintf(stderr, "Usage:\n%s NUMBER \"555-1212\" SIDE left|right|either SPEED slow|norm|fast\n", argv[0]);
  353.         return(FALSE);
  354.     }
  355.  
  356.     for (i=1; i<argc; i++)
  357.     {
  358.         if ((index = isin(argv[i], flags, NUM_FLAGS)) >= 0)
  359.         {
  360.             pointer = NULL;
  361.             switch (index)
  362.             {
  363.                 /*  Option flag: copy pointer into variable "pointer".  */
  364.                 case (NUMBER_I):
  365.                     pointer = number;
  366.                     break;
  367.                 case (SIDE_I):
  368.                     pointer = side_chr;
  369.                     break;
  370.                 /*  Argument given without flag; take it directly.  */
  371.                 case (LEFT_I):
  372.                 case (RIGHT_I):
  373.                 case (EITHER_I):
  374.                     (void)strcpy(side_chr, flags[index]);
  375.                     break;
  376.                 case (SPEED_I):
  377.                     pointer = speed_chr;
  378.                     break;
  379.                 case (FAST_I):
  380.                 case (NORM_I):
  381.                 case (SLOW_I):
  382.                     (void)strcpy(speed_chr, flags[index]);
  383.                     break;
  384.                 default:
  385.                     break;
  386.             }
  387.             /*  If this is an option flag, copy the following argument into
  388.                 the string (which is now pointed to by "pointer").
  389.             */
  390.             if (pointer != NULL)
  391.             {
  392.                 if (i < argc-1)
  393.                     (void)strcpy(pointer, argv[++i]);
  394.                 else
  395.                 {
  396.                     (void)fprintf(stderr, "%s: \"%s\" requires an argument.\n", argv[0], argv[i]);
  397.                     return(FALSE);
  398.                 }
  399.             }
  400.         }
  401.         else
  402.         {
  403.             (void)fprintf(stderr, "%s: Unknown option \"%s\".\n", argv[0], argv[i]);
  404.             return(FALSE);
  405.         }
  406.     }
  407.  
  408.     if (strlen(side_chr))
  409.     {
  410.         if (strcmpi(side_chr, "LEFT") == 0)
  411.         {
  412.             side[0] = LEFT_SIDE;
  413.             side_cnt = 1;
  414.         }
  415.         else if (strcmpi(side_chr, "RIGHT") == 0)
  416.         {
  417.             side[0] = RIGHT_SIDE;
  418.             side_cnt = 1;
  419.         }
  420.         else if (strcmpi(side_chr, "EITHER") == 0);  /*  Use default.  */
  421.         else
  422.         {
  423.             (void)fprintf(stderr, "%s: Unknown SIDE argument \"%s\".\n", argv[0], side_chr);
  424.             return(FALSE);
  425.         }
  426.     }
  427.  
  428.     if (strlen(speed_chr))
  429.     {
  430.         if (strcmpi(speed_chr, "FAST") == 0) speed = FAST_SPEED;
  431.         else if (strcmpi(speed_chr, "NORM") == 0);  /*  Use default.  */
  432.         else if (strcmpi(speed_chr, "SLOW") == 0) speed = SLOW_SPEED;
  433.         else
  434.         {
  435.             (void)fprintf(stderr, "%s: Unknown SPEED argument \"%s\".\n", argv[0], speed_chr);
  436.             return(FALSE);
  437.         }
  438.     }
  439.  
  440.     return(TRUE);
  441.  
  442. }    /* end args() */
  443.  
  444.  
  445. /*
  446.     S E T U P
  447.     Opens libraries & devices, allocates memory, initializes structures.
  448. */
  449.  
  450. int
  451. setup()
  452. {
  453.     if ((IntuitionBase = (struct IntuitionBase *)
  454.         OpenLibrary("intuition.library", 0L)) == NULL)
  455.         return(ERROR_INVALID_RESIDENT_LIBRARY);
  456.  
  457.     if ((GfxBase = (struct GfxBase *)
  458.         OpenLibrary("graphics.library", 0L)) == NULL)
  459.         return(ERROR_INVALID_RESIDENT_LIBRARY);
  460.  
  461.     remember = NULL;
  462.  
  463.     if ((cmd_ioA = (struct IOAudio *)
  464.         AllocRemember(&remember, (long)sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR))==NULL)
  465.         return(ERROR_NO_FREE_STORE);
  466.  
  467.     if ((lo_ioA = (struct IOAudio *)
  468.         AllocRemember(&remember, (long)sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR))==NULL)
  469.         return(ERROR_NO_FREE_STORE);
  470.  
  471.     if ((hi_ioA = (struct IOAudio *)
  472.         AllocRemember(&remember, (long)sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR))==NULL)
  473.         return(ERROR_NO_FREE_STORE);
  474.  
  475.     if ((square_wave = (BYTE *)
  476.         AllocRemember(&remember, SQUARE_WAVE_LEN, MEMF_CHIP|MEMF_PUBLIC|MEMF_CLEAR))==NULL)
  477.         return(ERROR_NO_FREE_STORE);
  478.  
  479.     if ((cmd_ioA->ioa_Request.io_Message.mn_ReplyPort = (struct MsgPort *)
  480.         CreatePort("DTMF", 0L)) == NULL)
  481.         return(ERROR_CANNOT_CREATE_PORT);
  482.  
  483.     if (OpenDevice(AUDIONAME, 0L, cmd_ioA, 0L)!=NULL)
  484.         return(ERROR_OBJECT_IN_USE);
  485.  
  486.     /*  Initialize cmd audio request block.  */
  487.     cmd_ioA->ioa_Request.io_Message.mn_Node.ln_Pri = DTMF_PRI;
  488.     cmd_ioA->ioa_Request.io_Command = ADCMD_ALLOCATE;
  489.     cmd_ioA->ioa_Request.io_Flags = ADIOF_NOWAIT;
  490.     cmd_ioA->ioa_Data = side;
  491.     cmd_ioA->ioa_Length = side_cnt;
  492.  
  493.     /*  Must use immediately; return if requested channel(s) in use.  */
  494.     BeginIO(cmd_ioA);
  495.     if (WaitIO(cmd_ioA))
  496.         return(ERROR_OBJECT_IN_USE);
  497.  
  498.     /*  Copy cmd values to lo request block; make needed changes.  */
  499.     *lo_ioA = *cmd_ioA;
  500.     lo_ioA->ioa_Request.io_Command = CMD_WRITE;
  501.     lo_ioA->ioa_Request.io_Unit =
  502.         (ULONG)cmd_ioA->ioa_Request.io_Unit & LO_TONES;
  503.     lo_ioA->ioa_Volume = 64;
  504.     lo_ioA->ioa_Data = (UBYTE *)square_wave;
  505.     lo_ioA->ioa_Length = SQUARE_WAVE_LEN;
  506.  
  507.     /*  Copy lo values to hi request block; make needed changes.  */
  508.     *hi_ioA = *lo_ioA;
  509.     hi_ioA->ioa_Request.io_Unit =
  510.         (ULONG)cmd_ioA->ioa_Request.io_Unit & HI_TONES;
  511.  
  512.     /*
  513.         Construct a square wave.  Having it begin and end at the same
  514.         level gives a smooth transition when cycling.  Having it begin
  515.         and end at zero avoids "POP"s when starting and stopping.
  516.  
  517.              __         | _ 127      L
  518.             |  |        |            E
  519.            _|  |__    _ | _ 0        V
  520.                   |  |  |            E
  521.                   |__|  | _ -127     L
  522.           ______________|
  523.  
  524.            0 12 34 56 7
  525.  
  526.           SQUARE_WAVE[x]
  527.     */
  528.  
  529.     square_wave[1] = square_wave[2] =  127;
  530.     square_wave[5] = square_wave[6] = -127;
  531.  
  532.     return(RETURN_OK);
  533.  
  534. }    /* end setup() */
  535.  
  536.  
  537. /*
  538.     K E Y C O N V
  539.     Given a valid DTMF key, returns the low & high periods and cycles.
  540. */
  541.  
  542. BOOL
  543. keyConv(key, lo_period, hi_period, lo_cycles, hi_cycles)
  544. int key, *lo_period, *hi_period, *lo_cycles, *hi_cycles;
  545. {
  546. char *button;
  547. int index = -1;
  548.  
  549.     if (key != '\0')
  550.     {
  551.         /*  If key is alphabetic, convert into corresponding numeral.  */
  552.         if (key >= 'a' && key < 'z' && key != 'q') 
  553.         {
  554.             /*  Fold keys greater than 'q' down 1 slot to fill q's gap.  */
  555.             if (key > 'q') --key;
  556.             /*  Slide to the range of 1 to 24.  */
  557.             key = key - ('a' - 1);
  558.             /*
  559.             Map groups of three onto the digits 2 to 9.
  560.  
  561.                  a b c d e f      w  x  y
  562.  
  563.                  1 2 3 4 5 6 ... 22 23 24
  564.                  \ | / \ | /      \ | /
  565.                    2     3   ...    9
  566.             */
  567.             index = 1 + (int)(key/3. + .9);
  568.         }
  569.         else
  570.         {
  571.             /*  If key is one of the characters in the keypad string...  */
  572.             if (button = strchr(keypad, (char)key))
  573.             {
  574.                 /*  ...calculate index by the difference in pointers.  */
  575.                 index = (int)(button-keypad);
  576.             }
  577.         }
  578.         /*  If key was valid...  */
  579.         if (index >= 0)
  580.         {
  581.             /*  ...index into matrix for the periods and cycles.  */
  582.  
  583.             *lo_cycles = cycles[index][0] * speed;
  584.             *hi_cycles = cycles[index][1] * speed;
  585.  
  586.             /*  If running on a PAL Amiga, offset the index by NUM_PAIRS.  */
  587.             if (GfxBase->DisplayFlags & PAL) index += NUM_PAIRS;
  588.  
  589.             *lo_period = periods[index][0];
  590.             *hi_period = periods[index][1];
  591.  
  592.             return(TRUE);
  593.         }
  594.     }
  595.     return (FALSE);
  596. }    /* end keyConv() */
  597.  
  598.  
  599. /*
  600.     T O N E S
  601.     Generates the tones, one key at a time.
  602. */
  603.  
  604. void
  605. tones()
  606. {
  607. int key;
  608. int lo_period, hi_period, lo_cycles, hi_cycles;
  609. int i=0, s;
  610.  
  611.     s = strlen(number);
  612.  
  613.     do
  614.     {
  615.         /*  If CLI "NUMBER", get next char.  */
  616.         if (s)
  617.             key = ( i < s ) ? number[i++] : EOF;
  618.         /*  Otherwise, use stdin.  */
  619.         else
  620.             key = getchar();
  621.  
  622.         if (keyConv(key, &lo_period, &hi_period, &lo_cycles, &hi_cycles))
  623.         {
  624.             lo_ioA->ioa_Period = lo_period;
  625.             lo_ioA->ioa_Cycles = lo_cycles;
  626.  
  627.             hi_ioA->ioa_Period = hi_period;
  628.             hi_ioA->ioa_Cycles = hi_cycles;
  629.  
  630.             lo_ioA->ioa_Request.io_Flags =
  631.                 hi_ioA->ioa_Request.io_Flags = ADIOF_PERVOL;
  632.  
  633.             BeginIO(lo_ioA);
  634.             BeginIO(hi_ioA);
  635.             (void) WaitIO(lo_ioA);
  636.             (void) WaitIO(hi_ioA);
  637.  
  638.             /*  Insure there is a delay between successive tones.  */
  639.             Delay(2L * speed);
  640.         }
  641.         /*  A comma in the input delays for two seconds.  */
  642.         else if (key == ',') Delay(2L * TICKS_PER_SECOND);
  643.  
  644.     } while (key != EOF);
  645. }    /* end tones() */
  646.  
  647.  
  648. /*
  649.     E R R O R M S G
  650.     Prints an error message to stdout.
  651. */
  652.  
  653. void
  654. errormsg(errval, progname)
  655. int errval;
  656. char *progname;
  657. {
  658.     switch(errval)
  659.     {
  660.         case (ERROR_INVALID_RESIDENT_LIBRARY):
  661.             (void)fprintf(stderr, "%s: Could not open one of either intuition or graphics libraries.\n", progname);
  662.             break;
  663.         case (ERROR_NO_FREE_STORE):
  664.             (void)fprintf(stderr, "%s: Not enough memory.\n", progname);
  665.             break;
  666.         case (ERROR_CANNOT_CREATE_PORT):
  667.             (void)fprintf(stderr, "%s: Could not create port.\n", progname);
  668.             break;
  669.         case (ERROR_OBJECT_IN_USE):
  670.             (void)fprintf(stderr, "%s: Object in use.\n", progname);
  671.             break;
  672.         default:
  673.             (void)fprintf(stderr, "%s: Unknown error.\n", progname);
  674.             break;
  675.     }
  676. }
  677.  
  678.  
  679. /*
  680.     S H U T D O W N
  681.     Clean up.
  682. */
  683.  
  684. void
  685. shutdown()
  686. {
  687.     if (cmd_ioA->ioa_Request.io_Device)
  688.         CloseDevice(cmd_ioA);
  689.  
  690.     if (cmd_ioA->ioa_Request.io_Message.mn_ReplyPort)
  691.         DeletePort(cmd_ioA->ioa_Request.io_Message.mn_ReplyPort);
  692.  
  693.     FreeRemember(&remember, TRUE);
  694.  
  695.     if (GfxBase) CloseLibrary(GfxBase);
  696.  
  697.     if (IntuitionBase) CloseLibrary(IntuitionBase);
  698.  
  699. }    /* end shutdown() */
  700.  
  701.  
  702. /*
  703.     M A I N
  704.     Controlling routine.
  705. */
  706.  
  707. main(argc, argv)
  708. int argc;
  709. char *argv[];
  710. {
  711. int errval = RETURN_OK;
  712.  
  713.     if (args(argc, argv))
  714.     {
  715.         errval = setup();
  716.         if (errval == RETURN_OK)
  717.             tones();
  718.         else
  719.         {
  720.             errormsg(errval, argv[0]);
  721.             errval = (RETURN_FAIL);
  722.         }
  723.         shutdown();
  724.         exit(errval);
  725.     }
  726.     else
  727.         /*  Usage errors.  */
  728.         exit(RETURN_WARN);
  729. }    /* end main() */
  730.  
  731.  
  732.  
  733.