home *** CD-ROM | disk | FTP | other *** search
/ Kids Cube / 2_Music.iso / mel / soundsys.c < prev    next >
Text File  |  1992-07-27  |  14KB  |  254 lines

  1. /* ------------------------------------------------------------------- */
  2. /* SOUNDSYS.C - Background sound system for the IBM-PC and compatibles */
  3. /* ------------------------------------------------------------------- */
  4. /*              Written by Juan Jimenez                                */
  5. /*              Micro Consulting Associates                            */
  6. /*              868 Ashford 6B                                         */
  7. /*              San Juan, P.R. 00907-1018                              */
  8. /*              (809) 725-9470 FAX (809) 721-8470 GEmail: J.JIMENEZ    */
  9. /*              For Turbo C 1.5 or higher, though can be ported...     */
  10. /*              Released on 3/7/89                                     */
  11. /* ------------------------------------------------------------------- */
  12. /* This code is hereby placed in the PUBLIC DOMAIN. This means that I  */
  13. /* am giving it to you at no charge, and you can do with it whatever   */
  14. /* you want, without any restrictions or requests for donations,       */
  15. /* (though if you feel like it I'd be happy if you sent me $10, but if */
  16. /* you don't feel like it that's fine with me too). I'm, putting it    */
  17. /* the public domain because I've had a good response to some of my    */
  18. /* shareware stuff (like GSETUP), and I feel I should give something   */
  19. /* nice in return to the community, for free. The only thing that I    */
  20. /* -DO- ask if that if you use it, please give me credit for this      */
  21. /* implementation, and NEUROMANCER for the original one...             */
  22. /* ------------------------------------------------------------------- */
  23. /* This file contains the C source code for a background sound system  */
  24. /* that can be used in games and other types of applications that want */
  25. /* to produce sounds and noises in the background without interfering  */
  26. /* with the current foreground tasks (like the game play code...)      */
  27. /* ------------------------------------------------------------------- */
  28. /* The reason I coded this was because I couldn't find anything that   */
  29. /* would do the job. This code is loosely based on NEUROMANCER's code  */
  30. /* which he graciously sent to me as a base to start off with. His     */
  31. /* played a predefine sound set, I allow free-form input to produce    */
  32. /* just about any kinds of sounds for all kinds of purposes as long as */
  33. /* the sounds are not overly complex. The sampling rate here is 500hz  */
  34. /* which means that the lowest period of time that a particular note   */
  35. /* can be played is 1/500th of a second. This should be more than      */
  36. /* sufficient for most complex applications and games.                 */
  37. /* ------------------------------------------------------------------- */
  38. /* The way this code works is as follows:                              */
  39. /*                                                                     */
  40. /* 1) Before you do anything with this sound system, you have to make  */
  41. /* sure that you initialize it. This is done with one simple call to   */
  42. /* init_sound(), with no arguments. init_sound redirects interrupt 8,  */
  43. /* the hardware timer interrupt (clock tick) from it's destination     */
  44. /* inside DOS to ourselves. We save that vector 'cause we'll need it   */
  45. /* later on, as you shall soon see. It then modifies the system timer  */
  46. /* to issue 500 ticks per second instead of 18.2. This clock signal    */
  47. /* is the heart of the sound system. We use 500 clock ticks because    */
  48. /* it is A) a nice round number and B) about as slow a tick as you can */
  49. /* have to produce smooth sound.                                       */
  50. /*                                                                     */
  51. /* 2) Once you call init_sound(), the system is running and ready to   */
  52. /* receive input. This is done with the submit_sound() routine. This   */
  53. /* routine takes two arguments, both integer values. The first is the  */
  54. /* frequency of the sound to be generated, the second is the duration  */
  55. /* of the sound in 1/500th's of a second. submit_sound keeps a queue   */
  56. /* which can hold up to 8k submissions. It has two possible return     */
  57. /* values. If the queue had room left and the submission was posted,   */
  58. /* it returns an integer value of 0. If the queue was full, the entry  */
  59. /* you sent to it is thrown away and you get back a value of -1. That  */
  60. /* is about as simple as it can get.                                   */
  61. /*                                                                     */
  62. /* 3) Every time a clock tick is generated (500 times a second), the   */
  63. /* soundsystem() interrupt service routine takes over. It first checks */
  64. /* to see how many clock ticks have been issued. If 27 ticks have been */
  65. /* issued (count is kept in tickcount), we make a quick call to the    */
  66. /* DOS timer interrupt service routine. This effectively emulates the  */
  67. /* standard DOS clock. If we did not do this, the clock would never be */
  68. /* updated while the sound system is running, or your clock would go   */
  69. /* to Warp 10 and by the time you finished your clock would be set to  */
  70. /* some date in the next century... If 27 ticks have gone by, we reset */
  71. /* that counter as well. Now, the variable backduration is the one     */
  72. /* that keeps track of a sound which is currently being played. If     */
  73. /* backduration is greater than 0, that means a sound is in progress.  */
  74. /* We simply decrement backduration by one, and return to your program.*/
  75. /* If backduration is 0, we check to see if there are no sounds in the */
  76. /* queue. If there are, we start the sound going with the specified    */
  77. /* frequency, set backduration to the number of ticks it should last,  */
  78. /* and return. If there are no sounds in the queue, we set the sound   */
  79. /* to a random tone limited by the value in the global variable        */
  80. /* "frequency". The background random noise is controlled by the var   */
  81. /* "back_sound". If you set it to "ON" the background noise is on, if  */
  82. /* you set it to "OFF" the noise is turned off.                        */
  83. /*                                                                     */
  84. /* 4) Once your program is finished, you MUST remember to call the     */
  85. /* restore_sound() routine. This gives back the timer to DOS and       */
  86. /* resets the system timer back to the normal 18.2 ticks per second.   */
  87. /* ------------------------------------------------------------------- */
  88. /*           LIMITATIONS AND CAVEATS! READ THIS CAREFULLY!             */
  89. /* ------------------------------------------------------------------- */
  90. /* 1) This routine CANNOT be active while debugging your code in the   */
  91. /* REMOTE DEBUG mode of Turbo Debugger. It WILL crash the debugger.    */
  92. /* By remote debugging I mean the use of TDREMOTE to debug code that   */
  93. /* is running on one machine with another machine via a serial port.   */
  94. /*                                                                     */
  95. /* 2) This routine will most likely interfere with network adapters,   */
  96. /* though I don't know why. I just tell people not to run my programs  */
  97. /* that use this stuff on a network workstation.                       */
  98. /*                                                                     */
  99. /* 3) This code was written on 80286 and 80386 boxes. On 4.77 mhz 8088 */
  100. /* machines it does not do as well I would like it to. You can try     */
  101. /* making the soundsystem() routine smaller, but the only way I can    */
  102. /* think of that is going to assembly language, and even then this     */
  103. /* is pretty small as it is, so... You can also try and reduce the     */
  104. /* sampling rate, which is set in init_sound(). You do have to know    */
  105. /* how the system timer chip runs to program it. If you need help, do  */
  106. /* call or send me e-mail on GEnie.                                    */
  107. /* ------------------------------------------------------------------- */
  108. /* That's it. How's that for documentation, eh? hehe...                */
  109. /* ------------------------------------------------------------------- */
  110.  
  111. #include <stdio.h>
  112. #include <stdlib.h>
  113. #include <dos.h>
  114. #include <time.h>
  115. #include "soundsys.h"
  116.  
  117. noise background[noisemax];   /* The sound queue itself */
  118. int   frequency=0;            /* Random frequency base value */
  119. int   back_sound;          /* Flag for background sound on or off */
  120. int   topindex=0;             /* Marks the top of the queue */
  121. int   backduration=0;         /* Keeps track of duration of sound */
  122. int   backindex=0;            /* Marks bottom of queue */
  123. int   tickcount=0;            /* Keeps track of clock ticks for INT 8 */
  124. int   sound_in_queue = 0;     /* Tells if sound in queue */
  125. int   insound;                /* Prevent reentrancy */
  126.  
  127. void  interrupt (*oldint8)(void); /* Holds old INT 8 vector */
  128.  
  129. /* ---------------------------------------------------------- */
  130. /* Must be called prior to use of sound variables, sets up a  */
  131. /* int 8h ISR with the interrupt function soundsystem.        */
  132. /* If init_sound is called, then program must call            */
  133. /* restore_sound before it exits (or bad things will happen)! */
  134. /* ---------------------------------------------------------- */
  135.  
  136. void init_sound(void)
  137. {
  138.   backduration = 5;                /* Init backduration       */
  139.   randomize();                     /* Seed random number gen. */
  140.   backindex = topindex = 0;        /* Reset queue pointers    */
  141.   insound = FALSE;                 /* Init insound            */
  142.   back_sound = OFF;           /* No background sound     */
  143.   oldint8 = getvect(TimerTick);    /* get the original vector */
  144.   disable();                       /* Ints off to set timer   */
  145.   outportb(0x043,0x034);           /* Counter 0, read lsb-msb */
  146.                                    /* mode 2, rate generator, */
  147.                                    /* divide by N (0x04a9h)   */
  148.   outportb(0x040,0x052);           /* Low  byte of 0x004A9    */
  149.   outportb(0x040,0x009);           /* High byte of 0x004A9    */
  150.                                    /* Clock tick is now 1000  */
  151.                                    /* ticks per second...     */
  152.   setvect(TimerTick,soundsystem);  /* set up our ISR.         */
  153.   enable();                        /* ...and off we go...     */
  154.  
  155. }
  156.  
  157. /* -------------------------------------------- */
  158. /* Call this routine before returning to DOS... */
  159. /* -------------------------------------------- */
  160.  
  161. void restore_sound(void)
  162. {
  163.   nosound();                      /* Turn off sound          */
  164.   disable();                      /* Interrupts off          */
  165.   outportb(0x043,0x034);          /* Counter 0, read lsb-msb */
  166.                                   /* mode 2, rate generator, */
  167.                                   /* divide by N             */
  168.   outportb(0x040,0x000);          /* Low  byte of 0x00000    */
  169.   outportb(0x040,0x000);          /* High byte of 0x00000    */
  170.                                   /* Clock tick is now 18.2  */
  171.                                   /* ticks/sec (normal rate) */
  172.   setvect(TimerTick,oldint8);     /* Restore timer ISR       */
  173.   enable();                       /* ...and off we go...     */
  174. }
  175.  
  176. #pragma warn -par  /* Turn off warnings that I know about here */
  177. #pragma warn -aus
  178. #pragma warn -use
  179.  
  180. /* --------------------------------------- */
  181. /* The heart of the background soundsystem */
  182. /* --------------------------------------- */
  183.  
  184. void interrupt soundsystem(RegIntList)
  185. {
  186.   unsigned temp[3];             /* temporary storage area */
  187.   register int dummy1;          /* Stop use of register vars */
  188.   register int dummy2;          /* ...ditto...               */
  189.  
  190.   outportb(0x020,0x020);   /* Send EOI to 8259 PIC  */
  191.   if (!insound)            /* Prevent reentrancy */
  192.   {
  193.     insound = TRUE;        /* Ok, we're in, lock the door... */
  194.     if (backduration) backduration--;  /* Sound is in progress... */
  195.     else
  196.     {
  197.           if (sound_in_queue)  /* Current sound finished, more? */
  198.           {
  199.                backduration = (background[backindex].duration); /* Yes, setup */
  200.                sound(background[backindex++].freq);             /* Start sound */
  201.                if (backindex == topindex)               /* If last sound reset */
  202.                   backindex=topindex=sound_in_queue=0;  /* queue pointers      */
  203.           }
  204.           else if (back_sound) sound(random(frequency));  /* No sounds, do background noise */
  205.                       else nosound();
  206.     }
  207.     insound = FALSE;        /* Open the door! */
  208.   }
  209.   tickcount++;             /* Increment tick count  */
  210.   if (tickcount>27)        /* Have we had 27 ticks? */
  211.   {
  212.         tickcount=0;       /* Yes, reset count to 0 */
  213.                            /* chain to clock ISR */
  214.         temp[0] = bp;      /* Save top 3 values */
  215.         temp[1] = di;
  216.         temp[2] = si;
  217.         bp = ds;           /* Move all others up by 3 */
  218.         di = es;
  219.         si = dx;
  220.         ds = cx;
  221.         es = bx;
  222.         dx = ax;
  223.         cx = FP_OFF(oldint8); /* and put a new ip/cs/flags */
  224.         bx = FP_SEG(oldint8); /* on the stack */
  225.         ax = flags & ~0x200;
  226.         _BP -= 6;          /* Modify BP by three words        */
  227.          return;           /* NOTE: after changing _BP, don't */
  228.                            /* plan on using an local variables, */
  229.                            /* other than those here.           */
  230.   }
  231. }
  232.  
  233. #pragma warn +use  /* Turn the warnings back on! */
  234. #pragma warn +aus
  235. #pragma warn +par
  236.  
  237. /* --------------------------------------------------------- */
  238. /* This routine is used to submit sounds to the sound queue. */
  239. /* ----------------------------------------------------------*/
  240.  
  241. int submit_sound(int freq,int delay)
  242. {
  243.         if (backindex < noisemax)   /* Queue full? */
  244.         {
  245.                 background[topindex].freq = freq;  /* No, put it in queue */
  246.                 background[topindex++].duration = delay;
  247.                 sound_in_queue=1;                 /* Note sound in queue */
  248.                 return(0);                        /* Return OK value */
  249.         }
  250.         else return(-1);   /* Queue is full, return fail result */
  251. }
  252.  
  253. /* Finis... */
  254.