home *** CD-ROM | disk | FTP | other *** search
/ ST-Computer Leser-CD 2000 January / LCD_01_2000.iso / games / doom / pmdoom / src / sound / devaudio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-12-17  |  14.0 KB  |  571 lines

  1. /*  Emacs style mode select   -*- C++ -*-  */
  2. /* ----------------------------------------------------------------------------- */
  3. /*  */
  4. /*  $Id:$ */
  5. /*  */
  6. /*  Copyright (C) 1993-1996 by id Software, Inc. */
  7. /*  */
  8. /*  This source is available for distribution and/or modification */
  9. /*  only under the terms of the DOOM Source Code License as */
  10. /*  published by id Software. All rights reserved. */
  11. /*  */
  12. /*  The source is distributed in the hope that it will be useful, */
  13. /*  but WITHOUT ANY WARRANTY; without even the implied warranty of */
  14. /*  FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License */
  15. /*  for more details. */
  16. /*  */
  17. /*  $Log:$ */
  18. /*  */
  19. /*  DESCRIPTION: */
  20. /*     System interface for sound. */
  21. /*  */
  22. /* ----------------------------------------------------------------------------- */
  23.  
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <stdarg.h>
  27.  
  28. #include <math.h>
  29.  
  30. #include <sys/time.h>
  31. #include <sys/types.h>
  32.  
  33. #ifndef LINUX
  34. #ifndef ATARI
  35. #include <sys/filio.h>
  36. #endif
  37. #endif
  38.  
  39. #include <fcntl.h>
  40. #include <unistd.h>
  41. #include <sys/ioctl.h>
  42.  
  43. /*  Linux voxware output. */
  44. #ifdef LINUX
  45. #include <linux/soundcard.h>
  46. #endif
  47.  
  48. /*  Timer stuff. Experimental. */
  49. #include <time.h>
  50. #include <signal.h>
  51.  
  52. #include "z_zone.h"
  53.  
  54. #include "i_system.h"
  55. #include "i_sound.h"
  56. #include "m_argv.h"
  57. #include "m_misc.h"
  58. #include "w_wad.h"
  59.  
  60. #include "doomdef.h"
  61.  
  62. /* Use timer interrupt for audio device ? */
  63. #define SNDINTR 1
  64.  
  65.  
  66.  
  67. #ifdef SNDINTR
  68.  
  69. /*  Update all 30 millisecs, approx. 30fps synchronized. */
  70. /*  Linux resolution is allegedly 10 millisecs, */
  71. /*   scale is microseconds. */
  72. /*#define SOUND_INTERVAL     500*/
  73. #define SOUND_INTERVAL     (((512/4)*1000000)/11025)
  74.  
  75. /*  Get the interrupt. Set duration in millisecs. */
  76. int I_SoundSetTimer( int duration_of_tick );
  77. void I_SoundDelTimer( void );
  78.  
  79. /*  A quick hack to establish a protocol between */
  80. /*  synchronous mix buffer updates and asynchronous */
  81. /*  audio writes. Probably redundant with gametic. */
  82. static int flag = 0;
  83.  
  84. #endif
  85.  
  86. /*  The number of internal mixing channels, */
  87. /*   the samples calculated for each mixing step, */
  88. /*   the size of the 16bit, 2 hardware channel (stereo) */
  89. /*   mixing buffer, and the samplerate of the raw data. */
  90.  
  91.  
  92. /*  Needed for calling the actual sound output. */
  93. #define SAMPLECOUNT        512
  94. #define NUM_CHANNELS        8
  95. /*  It is 2 for 16bit, and 2 for two channels. */
  96. #define BUFMUL                  4
  97. #define MIXBUFFERSIZE        (SAMPLECOUNT*BUFMUL)
  98.  
  99. #define SAMPLERATE        11025    /*  Hz */
  100. #define SAMPLESIZE        2       /*  16bit */
  101.  
  102. /*  The actual lengths of all sound effects. */
  103. int         lengths[NUMSFX];
  104.  
  105. /*  The actual output device. */
  106. int    audio_fd;
  107.  
  108. /*  The global mixing buffer. */
  109. /*  Basically, samples from all active internal channels */
  110. /*   are modifed and added, and stored in the buffer */
  111. /*   that is submitted to the audio device. */
  112. signed short    mixbuffer[MIXBUFFERSIZE];
  113.  
  114.  
  115. /*  The channel step amount... */
  116. unsigned int    channelstep[NUM_CHANNELS];
  117. /*  ... and a 0.16 bit remainder of last step. */
  118. unsigned int    channelstepremainder[NUM_CHANNELS];
  119.  
  120.  
  121. /*  The channel data pointers, start and end. */
  122. unsigned char*    channels[NUM_CHANNELS];
  123. unsigned char*    channelsend[NUM_CHANNELS];
  124.  
  125.  
  126. /*  Time/gametic that the channel started playing, */
  127. /*   used to determine oldest, which automatically */
  128. /*   has lowest priority. */
  129. /*  In case number of active sounds exceeds */
  130. /*   available channels. */
  131. int        channelstart[NUM_CHANNELS];
  132.  
  133. /*  The sound in channel handles, */
  134. /*   determined on registration, */
  135. /*   might be used to unregister/stop/modify, */
  136. /*   currently unused. */
  137. int         channelhandles[NUM_CHANNELS];
  138.  
  139. /*  SFX id of the playing sound effect. */
  140. /*  Used to catch duplicates (like chainsaw). */
  141. int        channelids[NUM_CHANNELS];            
  142.  
  143. /*  Pitch to stepping lookup, unused. */
  144. int        steptable[256];
  145.  
  146. /*  Volume lookups. */
  147. int        vol_lookup[128*256];
  148.  
  149. /*  Hardware left and right channel volume lookup. */
  150. int*        channelleftvol_lookup[NUM_CHANNELS];
  151. int*        channelrightvol_lookup[NUM_CHANNELS];
  152.  
  153.  
  154.  
  155.  
  156. /*  */
  157. /*  Safe ioctl, convenience. */
  158. /*  */
  159. void
  160. myioctl
  161. ( int    fd,
  162.   int    command,
  163.   int*    arg )
  164. {   
  165.     int        rc;
  166.     extern int    errno;
  167.     
  168.     rc = ioctl(fd, command, arg);  
  169.     if (rc < 0)
  170.     {
  171.     fprintf(stderr, "ioctl(dsp,%d,arg) failed\n", command);
  172.     fprintf(stderr, "errno=%d\n", errno);
  173.     exit(-1);
  174.     }
  175. }
  176.  
  177. /*  */
  178. /*  Starting a sound means adding it */
  179. /*   to the current list of active sounds */
  180. /*   in the internal channels. */
  181. /*  As the SFX info struct contains */
  182. /*   e.g. a pointer to the raw data, */
  183. /*   it is ignored. */
  184. /*  As our sound handling does not handle */
  185. /*   priority, it is ignored. */
  186. /*  Pitching (that is, increased speed of playback) */
  187. /*   is set, but currently not used by mixing. */
  188. /*  */
  189. int
  190. I_StartSound_linux
  191. ( int        id,
  192.   int        vol,
  193.   int        sep,
  194.   int        pitch,
  195.   int        priority )
  196. {
  197.     /*  Debug. */
  198.     /* fprintf( stderr, "starting sound %d", id ); */
  199.     
  200.     /*  Returns a handle (not used). */
  201.     id = addsfx( id, vol, steptable[pitch], sep );
  202.  
  203.     /*  fprintf( stderr, "/handle is %d\n", id ); */
  204.     
  205.     return id;
  206. }
  207.  
  208. /*  */
  209. /*  This function loops all active (internal) sound */
  210. /*   channels, retrieves a given number of samples */
  211. /*   from the raw sound data, modifies it according */
  212. /*   to the current (internal) channel parameters, */
  213. /*   mixes the per channel samples into the global */
  214. /*   mixbuffer, clamping it to the allowed range, */
  215. /*   and sets up everything for transferring the */
  216. /*   contents of the mixbuffer to the (two) */
  217. /*   hardware channels (left and right, that is). */
  218. /*  */
  219. /*  This function currently supports only 16bit. */
  220. /*  */
  221. void I_UpdateSound( void )
  222. {
  223. #ifdef SNDINTR
  224.   /*  Debug. Count buffer misses with interrupt. */
  225.   static int misses = 0;
  226. #endif
  227.  
  228.   
  229.   /*  Mix current sound data. */
  230.   /*  Data, from raw sound, for right and left. */
  231.   register unsigned int    sample;
  232.   register int        dl;
  233.   register int        dr;
  234.   
  235.   /*  Pointers in global mixbuffer, left, right, end. */
  236.   signed short*        leftout;
  237.   signed short*        rightout;
  238.   signed short*        leftend;
  239.   /*  Step in mixbuffer, left and right, thus two. */
  240.   int                step;
  241.  
  242.   /*  Mixing channel index. */
  243.   int                chan;
  244.     
  245.     /*  Left and right channel */
  246.     /*   are in global mixbuffer, alternating. */
  247.     leftout = mixbuffer;
  248.     rightout = mixbuffer+1;
  249.     step = 2;
  250.  
  251.     /*  Determine end, for left channel only */
  252.     /*   (right channel is implicit). */
  253.     leftend = mixbuffer + SAMPLECOUNT*step;
  254.  
  255.     /*  Mix sounds into the mixing buffer. */
  256.     /*  Loop over step*SAMPLECOUNT, */
  257.     /*   that is 512 values for two channels. */
  258.     while (leftout != leftend)
  259.     {
  260.     /*  Reset left/right value.  */
  261.     dl = 0;
  262.     dr = 0;
  263.  
  264.     /*  Love thy L2 chache - made this a loop. */
  265.     /*  Now more channels could be set at compile time */
  266.     /*   as well. Thus loop those  channels. */
  267.     for ( chan = 0; chan < NUM_CHANNELS; chan++ )
  268.     {
  269.         /*  Check channel, if active. */
  270.         if (channels[ chan ])
  271.         {
  272.         /*  Get the raw data from the channel.  */
  273.         sample = *channels[ chan ];
  274.         /*  Add left and right part */
  275.         /*   for this channel (sound) */
  276.         /*   to the current data. */
  277.         /*  Adjust volume accordingly. */
  278.         dl += channelleftvol_lookup[ chan ][sample];
  279.         dr += channelrightvol_lookup[ chan ][sample];
  280.         /*  Increment index ??? */
  281.         channelstepremainder[ chan ] += channelstep[ chan ];
  282.         /*  MSB is next sample??? */
  283.         channels[ chan ] += channelstepremainder[ chan ] >> 16;
  284.         /*  Limit to LSB??? */
  285.         channelstepremainder[ chan ] &= 65536-1;
  286.  
  287.         /*  Check whether we are done. */
  288.         if (channels[ chan ] >= channelsend[ chan ])
  289.             channels[ chan ] = 0;
  290.         }
  291.     }
  292.     
  293.     /*  Clamp to range. Left hardware channel. */
  294.     /*  Has been char instead of short. */
  295.     /*  if (dl > 127) *leftout = 127; */
  296.     /*  else if (dl < -128) *leftout = -128; */
  297.     /*  else *leftout = dl; */
  298.  
  299.     if (dl > 0x7fff)
  300.         *leftout = 0x7fff;
  301.     else if (dl < -0x8000)
  302.         *leftout = -0x8000;
  303.     else
  304.         *leftout = dl;
  305.  
  306.     /*  Same for right hardware channel. */
  307.     if (dr > 0x7fff)
  308.         *rightout = 0x7fff;
  309.     else if (dr < -0x8000)
  310.         *rightout = -0x8000;
  311.     else
  312.         *rightout = dr;
  313.  
  314.     /*  Increment current pointers in mixbuffer. */
  315.     leftout += step;
  316.     rightout += step;
  317.     }
  318.  
  319. #ifdef SNDINTR
  320.     /*  Debug check. */
  321.     if ( flag )
  322.     {
  323.       misses += flag;
  324.       flag = 0;
  325.     }
  326.     
  327.     if ( misses > 10 )
  328.     {
  329. /*
  330.       fprintf( stderr, "I_SoundUpdate: missed 10 buffer writes\n");
  331. */
  332.       misses = 0;
  333.     }
  334.     
  335.     /*  Increment flag for update. */
  336.     flag++;
  337. #endif
  338. }
  339.  
  340. /*   */
  341. /*  This would be used to write out the mixbuffer */
  342. /*   during each game loop update. */
  343. /*  Updates sound buffer and audio device at runtime.  */
  344. /*  It is called during Timer interrupt with SNDINTR. */
  345. /*  Mixing now done synchronous, and */
  346. /*   only output be done asynchronous? */
  347. /*  */
  348. void I_SubmitSound(void)
  349. {
  350.   /*  Write it to DSP device. */
  351.   write(audio_fd, mixbuffer, SAMPLECOUNT*BUFMUL);
  352. }
  353.  
  354.  
  355. void I_ShutdownSound_linux(void)
  356. {    
  357.   /*  Wait till all pending sounds are finished. */
  358.   int done = 0;
  359.   int i;
  360.   
  361.  
  362.   /*  FIXME (below). */
  363.   fprintf( stderr, "I_ShutdownSound: NOT finishing pending sounds\n");
  364.   fflush( stderr );
  365.   
  366.   while ( !done )
  367.   {
  368.     for( i=0 ; i<8 && !channels[i] ; i++);
  369.     
  370.     /*  FIXME. No proper channel output. */
  371.     /* if (i==8) */
  372.     done=1;
  373.   }
  374. #ifdef SNDINTR
  375.   I_SoundDelTimer();
  376. #endif
  377.   
  378.   /*  Cleaning up -releasing the DSP device. */
  379.   close ( audio_fd );
  380. }
  381.  
  382.  
  383.  
  384.  
  385.  
  386.  
  387. void
  388. I_InitSound_linux()
  389.   int i;
  390.  
  391. #ifdef SNDINTR
  392.   fprintf( stderr, "I_SoundSetTimer: %d microsecs\n", SOUND_INTERVAL );
  393.   I_SoundSetTimer( SOUND_INTERVAL );
  394. #endif
  395.     
  396.   /*  Secure and configure sound device first. */
  397.   fprintf( stderr, "I_InitSound: ");
  398.   
  399.   audio_fd = open("/dev/dsp", O_WRONLY);
  400.   if (audio_fd<0)
  401.     fprintf(stderr, "Could not open /dev/dsp\n");
  402.   
  403.                      
  404.   i = 11 | (2<<16);                                           
  405.   myioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &i);
  406.   myioctl(audio_fd, SNDCTL_DSP_RESET, 0);
  407.   
  408.   i=SAMPLERATE;
  409.   
  410.   myioctl(audio_fd, SNDCTL_DSP_SPEED, &i);
  411.   
  412.   i=1;
  413.   myioctl(audio_fd, SNDCTL_DSP_STEREO, &i);
  414.   
  415.   myioctl(audio_fd, SNDCTL_DSP_GETFMTS, &i);
  416.   
  417.   if (i&=AFMT_S16_LE)    
  418.     myioctl(audio_fd, SNDCTL_DSP_SETFMT, &i);
  419.   else
  420.     fprintf(stderr, "Could not play signed 16 data\n");
  421.  
  422.   fprintf(stderr, " configured audio device\n" );
  423.     
  424.   /*  Initialize external data (all sounds) at start, keep static. */
  425.   fprintf( stderr, "I_InitSound: ");
  426.   
  427.   for (i=1 ; i<NUMSFX ; i++)
  428.   { 
  429.     sfxinfo_t *sfx=&S_sfx[i];
  430.     
  431.     sfx->nbusing=0;
  432.  
  433.     /*  Alias? Example is the chaingun sound linked to pistol. */
  434.     if (!sfx->link)
  435.     {
  436.       /*  Load data from WAD file. */
  437.       sfx->data = getsfx( sfx->name, &lengths[i] );
  438.         Z_ChangeTag ( (sfx->data)-8, PU_CACHE);
  439.     }    
  440. #if 0
  441.     else
  442.     {
  443.       /*  Previously loaded already? */
  444.       sfx->data = sfx->link->data;
  445.       lengths[i] = lengths[(sfx->link - S_sfx)/sizeof(sfxinfo_t)];
  446.  
  447.     }
  448. #endif
  449.   }
  450.  
  451.   fprintf( stderr, " pre-cached all sound data\n");
  452.   
  453.   /*  Now initialize mixbuffer with zero. */
  454.   for ( i = 0; i< MIXBUFFERSIZE; i++ )
  455.     mixbuffer[i] = 0;
  456.   
  457.   /*  Finished initialization. */
  458.   fprintf(stderr, "I_InitSound: sound module ready\n");
  459. }
  460.  
  461. void I_SndUpdate_linux(void)
  462. {
  463.     /*  Sound mixing for the buffer is snychronous. */
  464.     I_UpdateSound();
  465.     /*  Synchronous sound output is explicitly called. */
  466. #ifndef SNDINTR
  467.     /*  Update sound output. */
  468.     I_SubmitSound();
  469. #endif
  470. }
  471.  
  472. /*  */
  473. /*  Experimental stuff. */
  474. /*  A Linux timer interrupt, for asynchronous */
  475. /*   sound output. */
  476. /*  I ripped this out of the Timer class in */
  477. /*   our Difference Engine, including a few */
  478. /*   SUN remains... */
  479. /*    */
  480.  
  481. #ifdef SNDINTR
  482.  
  483. #ifdef sun
  484.     typedef     sigset_t        tSigSet;
  485. #else    
  486.     typedef     int             tSigSet;
  487. #endif /* sun */
  488.  
  489.  
  490. /*  We might use SIGVTALRM and ITIMER_VIRTUAL, if the process */
  491. /*   time independend timer happens to get lost due to heavy load. */
  492. /*  SIGALRM and ITIMER_REAL doesn't really work well. */
  493. /*  There are issues with profiling as well. */
  494. static int /*__itimer_which*/  itimer = ITIMER_REAL;
  495.  
  496. static int sig = SIGALRM;
  497.  
  498. /*  Interrupt handler. */
  499. void I_HandleSoundTimer( int ignore )
  500. {
  501.   /*  Debug. */
  502.   /* fprintf( stderr, "%c", '+' ); fflush( stderr ); */
  503.   
  504.   /*  Feed sound device if necesary. */
  505.   if ( flag )
  506.   {
  507.     /*  See I_SubmitSound(). */
  508.     /*  Write it to DSP device. */
  509.     write(audio_fd, mixbuffer, SAMPLECOUNT*BUFMUL);
  510.  
  511.     /*  Reset flag counter. */
  512.     flag = 0;
  513.   }
  514.   else
  515.     return;
  516.   
  517.   /*  UNUSED, but required. */
  518.   ignore = 0;
  519.   return;
  520. }
  521.  
  522. /*  Get the interrupt. Set duration in millisecs. */
  523. int I_SoundSetTimer( int duration_of_tick )
  524. {
  525.   /*  Needed for gametick clockwork. */
  526.   struct itimerval    value;
  527.   struct itimerval    ovalue;
  528.   struct sigaction    act;
  529.   struct sigaction    oact;
  530.  
  531.   int res;
  532.  
  533.   /*  This sets to SA_ONESHOT and SA_NOMASK, thus we can not use it. */
  534.   /*      signal( _sig, handle_SIG_TICK ); */
  535.   
  536.   /*  Now we have to change this attribute for repeated calls. */
  537.   act.sa_handler = I_HandleSoundTimer;
  538. #ifndef sun    
  539.   /* ac    t.sa_mask = _sig; */
  540. #endif
  541.   act.sa_flags = SA_RESTART;
  542.   
  543.   sigaction( sig, &act, &oact );
  544.  
  545.   value.it_interval.tv_sec    = 0;
  546.   value.it_interval.tv_usec   = duration_of_tick;
  547.   value.it_value.tv_sec       = 0;
  548.   value.it_value.tv_usec      = duration_of_tick;
  549.  
  550.   /*  Error is -1. */
  551.   res = setitimer( itimer, &value, &ovalue );
  552.  
  553.   /*  Debug. */
  554.   if ( res == -1 )
  555.     fprintf( stderr, "I_SoundSetTimer: interrupt n.a.\n");
  556.   
  557.   return res;
  558. }
  559.  
  560.  
  561. /*  Remove the interrupt. Set duration to zero. */
  562. void I_SoundDelTimer()
  563. {
  564.   /*  Debug. */
  565.   if ( I_SoundSetTimer( 0 ) == -1)
  566.     fprintf( stderr, "I_SoundDelTimer: failed to remove interrupt. Doh!\n");
  567. }
  568.  
  569. #endif /* SNDINTR */
  570.