home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 June / SIMTEL_0692.cdr / msdos / turbo_c / tcmusic.arc / SOUND.C < prev   
C/C++ Source or Header  |  1987-06-20  |  12KB  |  340 lines

  1. /*
  2. **  These routines demonstrate the use of TURBO C inline assembly.
  3. **  The routines are almost verbatim copies of similarly named
  4. **  assembler routines found in:
  5. **
  6. **      'THE BLUEBOOK of ASSEMBLY ROUTINES FOR THE PC & XT'
  7. **         by Christopher L. Morgan
  8. **
  9. **  NOTE: I am not a micro assembler programmer!
  10. **        Perhaps I'll get around to it one of
  11. **        these days.
  12. **
  13. */
  14.  
  15. extern  void    toneinit( void );
  16. extern  void    toneset( unsigned period );
  17. extern  void    toneon( void );
  18.  
  19. #include        <music.h>
  20. #include        <stdio.h>
  21. #pragma         inline
  22.  
  23. /*
  24. **  TONEINIT: initialize speaker timer
  25. **
  26. **  This routine initializes the portion of the 8253 timer chip used by
  27. **  the speaker system. In particular, it sets up channel 2 of this
  28. **  timer as a square wave generator. This routine does not set the
  29. **  frequency nor turn on the tone. Use TONESET to select the frequency,
  30. **  TONEON to turn the tone on, and TONEOFF to turn it off.
  31. **
  32. */
  33.  
  34. static  void    toneinit( void )
  35. {
  36.     asm     mov     al,10110110b;       /*  setup 8253 control word */
  37.     asm     out     43h,al;             /*  send to timer-mode port */
  38. }
  39.  
  40. /*
  41. **  TONESET: set the tone on the speaker
  42. **
  43. **  This routine selects the frequency of the square wave tone to the
  44. **  speaker. The input to this routine is an integer, n, which determines
  45. **  the frequency f according to the formula:
  46. **
  47. **      f = F/n
  48. **
  49. **  where f is 1,193,182, the frequency of a clock signal which feeds the
  50. **  timer. The value n is the number of cycles of the clock signal per
  51. **  cycle of the resulting square wave. This routine does not actually
  52. **  turn on the tone. Use TONEON to turn the tone on and TONEOFF to turn
  53. **  it off. This routine assumes that the speaker timer has already been
  54. **  properly initialized. This happens after during normal boot-up of the
  55. **  computer, or you can use TONEINIT to initialize this timer.
  56. **
  57. **  COPIER'S NOTE: 1,193,182 won't work on machines not running
  58. **                 at 4.77 Mhz !
  59. **
  60. */
  61.  
  62. static  void    toneset( unsigned _CX )
  63. {
  64.     asm     mov     al,cl;              /*  move lower byte */
  65.     asm     out     42h,al;             /*  out to frequency control port    */
  66.     asm     mov     al,ch;              /*  move lower byte */
  67.     asm     out     42h,al;             /*  out to frequency control port    */
  68.  
  69. }
  70.  
  71. /*
  72. **  TONEON: turn on tone
  73. **
  74. **  Turns on the timer and speaker to produce a tone. The frequency
  75. **  of the tone must have already been selected on the timer.
  76. **
  77. */
  78.  
  79.  
  80. static  void    toneon( void )
  81. {
  82.     asm     in      al,61h;             /*  get contents of speaker port    */
  83.     asm     or      al,3;               /*  turn speaker and timer on       */
  84.     asm     out     61h,al;             /*  send new values to speaker port */
  85. }
  86.  
  87. /*
  88. **  TONEOFF: turn off tone
  89. **
  90. **  This routine turns off the timer and speaker.
  91. **
  92. */
  93.  
  94.  
  95. void    toneoff( void )
  96. {
  97.     asm     in      al,61h;             /*  get contents of speaker port    */
  98.     asm     and     al,11111100b;       /*  turn speaker and timer          */
  99.     asm     out     61h,al;             /*  off                             */
  100. }
  101.  
  102.  
  103. /*
  104. **  DELAY: delay for specified number of milliseconds
  105. **
  106. */
  107.  
  108.  
  109.  
  110. void    delay( unsigned milliseconds )
  111. {
  112.     _CX = milliseconds;                 /*  get ready for 'loop'        */
  113. delay1:
  114.     asm     push    cx;
  115.     asm     mov     cx,260;             /*  timing constant             */
  116. delay2:
  117.     asm     loop    delay2;             /*  small   loop                */
  118.     asm     pop     cx;
  119.     asm     loop    delay1;             /*  loop to count milliseconds  */
  120. }
  121.  
  122. /*
  123. **  FREQ: conversion from frequency to period
  124. **
  125. **  This routine converts from frequency to the number required by
  126. **  TONESET to set the frequency. The routine performs the following
  127. **  formula:
  128. **      n = F / f
  129. **
  130. **  where f is the frequency input to this routine, n is the number
  131. **  output by this routine, and F is 1,193,182. In other words this
  132. **  routine divides the specified frequency f into the 1,193,182 hertz
  133. **  clock frequency that drives the timer. Use this routine just
  134. **  before TONESET.
  135. **
  136. **  COPIER'S NOTE: 1,193,182 won't work on machines not running
  137. **                 at 4.77 Mhz !
  138. **
  139. */
  140.  
  141. unsigned    freq( unsigned  frequency )
  142. {
  143.     asm     mov     dx,12h;             /*  upper part of numerator     */
  144.     asm     mov     ax,34DEh;           /*  lower part of numerator     */
  145.     asm     mov     cx,frequency;
  146.     asm     div     cx;                 /*  divide by frequency         */
  147.     asm     mov     cx,ax;              /*  move converted frequency    */
  148.     return( _CX );
  149. }
  150.  
  151. /*
  152. **  TONE: make a tone
  153. **
  154. **  This routine makes a tone of a given frequency and given length.
  155. **
  156. */
  157.  
  158. void    tone( unsigned frequency, unsigned length )
  159. {
  160.     unsigned    period;
  161.     period = freq( frequency );
  162.     toneset( period );
  163.     toneon();
  164.     delay( length );
  165.     toneoff();
  166. }
  167.  
  168. /*
  169. **  PITCH: Convert from pitch to frequency
  170. **
  171. **  This routine converts from pitch number to the value required by
  172. **  TONESET to set the frequency. The pitch numbers refer to an ext-
  173. **  ended chromatic scale. The notes of this scale are numbered from
  174. **  0 to 95 with 12 notes per octave. 0 corresponds to a C at 16.35
  175. **  Hertz.
  176. **
  177. */
  178.  
  179. unsigned    pitch(unsigned pitch_no )
  180. {
  181.     static      int     notes[] =
  182.         {
  183.             4186,       /*  C                   */
  184.             4435,       /*  C sharp/ D flat     */
  185.             4699,       /*  D                   */
  186.             4978,       /*  D sharp/ E flat     */
  187.             5274,       /*  E                   */
  188.             5588,       /*  F                   */
  189.             5920,       /*  F sharp/ G flat     */
  190.             6272,       /*  G                   */
  191.             6645,       /*  G sharp/ A flat     */
  192.             7040,       /*  A                   */
  193.             7459,       /*  A sharp/ B flat     */
  194.             7902        /*  B                   */
  195.         };
  196.  
  197.     asm     mov     ax,pitch_no;
  198.     asm     mov     ah,0;
  199.     asm     mov     cl,12;          /*  divisor of 12 notes per octave      */
  200.     asm     div     cl;
  201.     asm     mov     dl,al;          /*  move quotient octave                */
  202.     asm     mov     al,ah;          /*  move remainder pitch                */
  203.     asm     cbw;                    /*  convert to word                     */
  204.     asm     mov     bx,ax;          /*  setup index into note table         */
  205.     _CX = notes[ _BX ];             /*  get a note                          */
  206.  
  207.     asm     push    dx;             /*  save dx                             */
  208.     _CX = freq( _CX );              /*  convert to frequency                */
  209.     asm     pop     dx;             /*  restore dx                          */
  210.  
  211.     asm     xchg    cx,dx;          /*  octave in cl, period in dx          */
  212.     asm     neg     cl;             /*  8 - octave = shift count            */
  213.     asm     add     cl,8;
  214.     asm     sal     dx,cl;
  215.  
  216.     return( _DX );
  217.  
  218. }
  219.  
  220. /*
  221. **  PLAY: Play music
  222. **
  223. **  This routine plays music. It reads a 'play list' which contains
  224. **  instructions to make the tune. This list consists of a series of
  225. **  music instructions. In this particular implementation, there are
  226. **  four instructions: Tempo, Note, Rest, and End. The syntax is given
  227. **  in MUSIC.DOC.
  228. **
  229. */
  230.  
  231.  
  232. void    play( char *play_list )
  233. {
  234.     unsigned            whole;
  235.     unsigned            acount;
  236.     unsigned            rcount;
  237.  
  238.     asm     mov     whole,2000;             /*  2 seconds for a whole note  */
  239.     asm     cld;                            /*  move forward                */
  240.     asm     mov     si,word ptr play_list;  /*  load play_list adr          */
  241.  
  242. /*
  243. **  Main loop
  244. */
  245.  
  246. play1:
  247.  
  248.     asm     lodsb;                  /*  get char from command line          */
  249.  
  250.     asm     cmp     al,'X';         /*  END command ?                       */
  251.     asm     jne     chktempo;       /*  no. check for tempo                 */
  252.     asm     jmp     playexit;       /*  yes, get out.                       */
  253.  
  254.     /*
  255.     **  check for Tempo command
  256.     */
  257.  
  258. chktempo:
  259.     asm     cmp     al,'T';         /*  TEMPO command ?                     */
  260.     asm     jne     chkpitch;       /*  no, check for pitch                 */
  261.  
  262. /*
  263. **  process tempo command
  264. */
  265.  
  266.     asm     lodsb;                  /*  get tempo                           */
  267.     asm     mov     cl,al;          /*  setup as divisor                    */
  268.     asm     mov     ch,0;           /*  clear hiorder of divisor            */
  269.     asm     mov     ax,60000;       /*  number of milliseconds/minute       */
  270.     asm     mov     dx,0;           /*  clear hiorder part of dividend      */
  271.     asm     div     cx;             /*  divide into time                    */
  272.     asm     mov     whole,ax;       /*  number of milliseconds/whole note   */
  273.     asm     jmp     play1;          /*  go process more commands            */
  274.  
  275. /*
  276. **  check for Pitch
  277. */
  278.  
  279. chkpitch:
  280.  
  281.     asm     cmp     al,'N';         /*  Pitch command ?                     */
  282.     asm     jne     chkrest;        /*  no. check for rest                  */
  283.  
  284. /*
  285. **  process pitch command
  286. */
  287.  
  288.     asm     lodsb;                  /*  get the pitch                       */
  289.  
  290.     _CX = pitch( _AX );             /*  convert pitch number                */
  291.  
  292.     toneset( _CX );                 /*  set frequency                       */
  293.     toneon();                       /*  turn tone on                        */
  294.  
  295.     asm     mov     cx,whole;       /*  number of milliseconds/whole note   */
  296.     asm     lodsb;                  /*  get duration                        */
  297.     asm     mov     ah,al;          /*  setup duration as multiplier        */
  298.     asm     mov     al,0;           /*  clear                               */
  299.     asm     sal     cx,1;           /*  scale factor 1                      */
  300.     asm     mul     cx;
  301.     asm     mov     cx,dx;          /*  total count  for the note           */
  302.     asm     lodsb;                  /*  get style                           */
  303.     asm     mov     ah,al;          /*  setup style as multiplier           */
  304.     asm     mov     al,0;           /*  clear rest of multiplier            */
  305.     asm     mul     cx;             /*  multiply by style                   */
  306.     asm     mov     acount,dx;      /*  store count for note                */
  307.     asm     sub     cx,dx;          /*  calculate count for rest            */
  308.     asm     mov     rcount,cx;      /*  save count for rest                 */
  309.  
  310.     delay( acount );                /*  audible count of note               */
  311.     toneoff();                      /*  turn tone off                       */
  312.     delay( rcount );                /*  inaudible part of note              */
  313.  
  314.     asm     jmp     play1;          /*  process more commands               */
  315.  
  316. chkrest:
  317.  
  318.     asm     cmp     al,'R';         /*  REST command ?                      */
  319.     asm     jne     playexit;       /*  nope... get out                     */
  320.  
  321. /*
  322. **  process rest command
  323. */
  324.  
  325.     asm     mov     cx,whole;       /*  number of milliseconds/whole note   */
  326.     asm     lodsb;                  /*  get duration                        */
  327.     asm     mov     ah,al;          /*  setup duration as multiplier        */
  328.     asm     mov     al,0;           /*  clear rest of multiplier            */
  329.     asm     sal     cx,1;           /*  scale factor of 1                   */
  330.     asm     mul     cx;
  331.  
  332.     delay( _DX );
  333.  
  334.     asm     jmp     play1;          /*  back for more...                    */
  335.  
  336. playexit:
  337.  
  338.     return;
  339. }
  340.