fix remapping behavior. Remapping is only necessary if we are rendering on the workbe...
[AROS-Contrib.git] / Games / Doom / i_sound.c
bloba6e57640d419e29a64138348e29108a2d85bb492
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.
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.
17 // $Log$
18 // Revision 1.1 2000/02/29 18:21:04 stegerg
19 // Doom port based on ADoomPPC. Read README.AROS!
22 // DESCRIPTION:
23 // System interface for sound.
25 //-----------------------------------------------------------------------------
27 static const char
28 rcsid[] = "$Id$";
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
34 #include <math.h>
36 #include <sys/time.h>
37 #include <sys/types.h>
39 #ifndef LINUX
40 #include <sys/filio.h>
41 #endif
43 #include <fcntl.h>
44 #include <unistd.h>
45 #include <sys/ioctl.h>
47 // Linux voxware output.
48 #include <linux/soundcard.h>
50 // Timer stuff. Experimental.
51 #include <time.h>
52 #include <signal.h>
54 #include "z_zone.h"
56 #include "i_system.h"
57 #include "i_sound.h"
58 #include "m_argv.h"
59 #include "m_misc.h"
60 #include "w_wad.h"
62 #include "doomdef.h"
64 // UNIX hack, to be removed.
65 #ifdef SNDSERV
66 // Separate sound server process.
67 FILE* sndserver=0;
68 char* sndserver_filename = "./sndserver ";
69 #elif SNDINTR
71 // Update all 30 millisecs, approx. 30fps synchronized.
72 // Linux resolution is allegedly 10 millisecs,
73 // scale is microseconds.
74 #define SOUND_INTERVAL 500
76 // Get the interrupt. Set duration in millisecs.
77 int I_SoundSetTimer( int duration_of_tick );
78 void I_SoundDelTimer( void );
79 #else
80 // None?
81 #endif
84 // A quick hack to establish a protocol between
85 // synchronous mix buffer updates and asynchronous
86 // audio writes. Probably redundant with gametic.
87 static int flag = 0;
89 // The number of internal mixing channels,
90 // the samples calculated for each mixing step,
91 // the size of the 16bit, 2 hardware channel (stereo)
92 // mixing buffer, and the samplerate of the raw data.
95 // Needed for calling the actual sound output.
96 #define SAMPLECOUNT 512
97 #define NUM_CHANNELS 8
98 // It is 2 for 16bit, and 2 for two channels.
99 #define BUFMUL 4
100 #define MIXBUFFERSIZE (SAMPLECOUNT*BUFMUL)
102 #define SAMPLERATE 11025 // Hz
103 #define SAMPLESIZE 2 // 16bit
105 // The actual lengths of all sound effects.
106 int lengths[NUMSFX];
108 // The actual output device.
109 int audio_fd;
111 // The global mixing buffer.
112 // Basically, samples from all active internal channels
113 // are modifed and added, and stored in the buffer
114 // that is submitted to the audio device.
115 signed short mixbuffer[MIXBUFFERSIZE];
118 // The channel step amount...
119 unsigned int channelstep[NUM_CHANNELS];
120 // ... and a 0.16 bit remainder of last step.
121 unsigned int channelstepremainder[NUM_CHANNELS];
124 // The channel data pointers, start and end.
125 unsigned char* channels[NUM_CHANNELS];
126 unsigned char* channelsend[NUM_CHANNELS];
129 // Time/gametic that the channel started playing,
130 // used to determine oldest, which automatically
131 // has lowest priority.
132 // In case number of active sounds exceeds
133 // available channels.
134 int channelstart[NUM_CHANNELS];
136 // The sound in channel handles,
137 // determined on registration,
138 // might be used to unregister/stop/modify,
139 // currently unused.
140 int channelhandles[NUM_CHANNELS];
142 // SFX id of the playing sound effect.
143 // Used to catch duplicates (like chainsaw).
144 int channelids[NUM_CHANNELS];
146 // Pitch to stepping lookup, unused.
147 int steptable[256];
149 // Volume lookups.
150 int vol_lookup[128*256];
152 // Hardware left and right channel volume lookup.
153 int* channelleftvol_lookup[NUM_CHANNELS];
154 int* channelrightvol_lookup[NUM_CHANNELS];
160 // Safe ioctl, convenience.
162 void
163 myioctl
164 ( int fd,
165 int command,
166 int* arg )
168 int rc;
169 extern int errno;
171 rc = ioctl(fd, command, arg);
172 if (rc < 0)
174 fprintf(stderr, "ioctl(dsp,%d,arg) failed\n", command);
175 fprintf(stderr, "errno=%d\n", errno);
176 exit(-1);
185 // This function loads the sound data from the WAD lump,
186 // for single sound.
188 void*
189 getsfx
190 ( char* sfxname,
191 int* len )
193 unsigned char* sfx;
194 unsigned char* paddedsfx;
195 int i;
196 int size;
197 int paddedsize;
198 char name[20];
199 int sfxlump;
202 // Get the sound data from the WAD, allocate lump
203 // in zone memory.
204 sprintf(name, "ds%s", sfxname);
206 // Now, there is a severe problem with the
207 // sound handling, in it is not (yet/anymore)
208 // gamemode aware. That means, sounds from
209 // DOOM II will be requested even with DOOM
210 // shareware.
211 // The sound list is wired into sounds.c,
212 // which sets the external variable.
213 // I do not do runtime patches to that
214 // variable. Instead, we will use a
215 // default sound for replacement.
216 if ( W_CheckNumForName(name) == -1 )
217 sfxlump = W_GetNumForName("dspistol");
218 else
219 sfxlump = W_GetNumForName(name);
221 size = W_LumpLength( sfxlump );
223 // Debug.
224 // fprintf( stderr, "." );
225 //fprintf( stderr, " -loading %s (lump %d, %d bytes)\n",
226 // sfxname, sfxlump, size );
227 //fflush( stderr );
229 sfx = (unsigned char*)W_CacheLumpNum( sfxlump, PU_STATIC );
231 // Pads the sound effect out to the mixing buffer size.
232 // The original realloc would interfere with zone memory.
233 paddedsize = ((size-8 + (SAMPLECOUNT-1)) / SAMPLECOUNT) * SAMPLECOUNT;
235 // Allocate from zone memory.
236 paddedsfx = (unsigned char*)Z_Malloc( paddedsize+8, PU_STATIC, 0 );
237 // ddt: (unsigned char *) realloc(sfx, paddedsize+8);
238 // This should interfere with zone memory handling,
239 // which does not kick in in the soundserver.
241 // Now copy and pad.
242 memcpy( paddedsfx, sfx, size );
243 for (i=size ; i<paddedsize+8 ; i++)
244 paddedsfx[i] = 128;
246 // Remove the cached lump.
247 Z_Free( sfx );
249 // Preserve padded length.
250 *len = paddedsize;
252 // Return allocated padded data.
253 return (void *) (paddedsfx + 8);
261 // This function adds a sound to the
262 // list of currently active sounds,
263 // which is maintained as a given number
264 // (eight, usually) of internal channels.
265 // Returns a handle.
268 addsfx
269 ( int sfxid,
270 int volume,
271 int step,
272 int seperation )
274 static unsigned short handlenums = 0;
276 int i;
277 int rc = -1;
279 int oldest = gametic;
280 int oldestnum = 0;
281 int slot;
283 int rightvol;
284 int leftvol;
286 // Chainsaw troubles.
287 // Play these sound effects only one at a time.
288 if ( sfxid == sfx_sawup
289 || sfxid == sfx_sawidl
290 || sfxid == sfx_sawful
291 || sfxid == sfx_sawhit
292 || sfxid == sfx_stnmov
293 || sfxid == sfx_pistol )
295 // Loop all channels, check.
296 for (i=0 ; i<NUM_CHANNELS ; i++)
298 // Active, and using the same SFX?
299 if ( (channels[i])
300 && (channelids[i] == sfxid) )
302 // Reset.
303 channels[i] = 0;
304 // We are sure that iff,
305 // there will only be one.
306 break;
311 // Loop all channels to find oldest SFX.
312 for (i=0; (i<NUM_CHANNELS) && (channels[i]); i++)
314 if (channelstart[i] < oldest)
316 oldestnum = i;
317 oldest = channelstart[i];
321 // Tales from the cryptic.
322 // If we found a channel, fine.
323 // If not, we simply overwrite the first one, 0.
324 // Probably only happens at startup.
325 if (i == NUM_CHANNELS)
326 slot = oldestnum;
327 else
328 slot = i;
330 // Okay, in the less recent channel,
331 // we will handle the new SFX.
332 // Set pointer to raw data.
333 channels[slot] = (unsigned char *) S_sfx[sfxid].data;
334 // Set pointer to end of raw data.
335 channelsend[slot] = channels[slot] + lengths[sfxid];
337 // Reset current handle number, limited to 0..100.
338 if (!handlenums)
339 handlenums = 100;
341 // Assign current handle number.
342 // Preserved so sounds could be stopped (unused).
343 channelhandles[slot] = rc = handlenums++;
345 // Set stepping???
346 // Kinda getting the impression this is never used.
347 channelstep[slot] = step;
348 // ???
349 channelstepremainder[slot] = 0;
350 // Should be gametic, I presume.
351 channelstart[slot] = gametic;
353 // Separation, that is, orientation/stereo.
354 // range is: 1 - 256
355 seperation += 1;
357 // Per left/right channel.
358 // x^2 seperation,
359 // adjust volume properly.
360 leftvol =
361 volume - ((volume*seperation*seperation) >> 16); ///(256*256);
362 seperation = seperation - 257;
363 rightvol =
364 volume - ((volume*seperation*seperation) >> 16);
366 // Sanity check, clamp volume.
367 if (rightvol < 0 || rightvol > 127)
368 I_Error("rightvol out of bounds");
370 if (leftvol < 0 || leftvol > 127)
371 I_Error("leftvol out of bounds");
373 // Get the proper lookup table piece
374 // for this volume level???
375 channelleftvol_lookup[slot] = &vol_lookup[leftvol*256];
376 channelrightvol_lookup[slot] = &vol_lookup[rightvol*256];
378 // Preserve sound SFX id,
379 // e.g. for avoiding duplicates of chainsaw.
380 channelids[slot] = sfxid;
382 // You tell me.
383 return rc;
391 // SFX API
392 // Note: this was called by S_Init.
393 // However, whatever they did in the
394 // old DPMS based DOS version, this
395 // were simply dummies in the Linux
396 // version.
397 // See soundserver initdata().
399 void I_SetChannels()
401 // Init internal lookups (raw data, mixing buffer, channels).
402 // This function sets up internal lookups used during
403 // the mixing process.
404 int i;
405 int j;
407 int* steptablemid = steptable + 128;
409 // Okay, reset internal mixing channels to zero.
410 /*for (i=0; i<NUM_CHANNELS; i++)
412 channels[i] = 0;
415 // This table provides step widths for pitch parameters.
416 // I fail to see that this is currently used.
417 for (i=-128 ; i<128 ; i++)
418 steptablemid[i] = (int)(pow(2.0, (i/64.0))*65536.0);
421 // Generates volume lookup tables
422 // which also turn the unsigned samples
423 // into signed samples.
424 for (i=0 ; i<128 ; i++)
425 for (j=0 ; j<256 ; j++)
426 vol_lookup[i*256+j] = (i*(j-128)*256)/127;
430 void I_SetSfxVolume(int volume)
432 // Identical to DOS.
433 // Basically, this should propagate
434 // the menu/config file setting
435 // to the state variable used in
436 // the mixing.
437 snd_SfxVolume = volume;
440 // MUSIC API - dummy. Some code from DOS version.
441 void I_SetMusicVolume(int volume)
443 // Internal state variable.
444 snd_MusicVolume = volume;
445 // Now set volume on output device.
446 // Whatever( snd_MusciVolume );
451 // Retrieve the raw data lump index
452 // for a given SFX name.
454 int I_GetSfxLumpNum(sfxinfo_t* sfx)
456 char namebuf[9];
457 sprintf(namebuf, "ds%s", sfx->name);
458 return W_GetNumForName(namebuf);
462 // Starting a sound means adding it
463 // to the current list of active sounds
464 // in the internal channels.
465 // As the SFX info struct contains
466 // e.g. a pointer to the raw data,
467 // it is ignored.
468 // As our sound handling does not handle
469 // priority, it is ignored.
470 // Pitching (that is, increased speed of playback)
471 // is set, but currently not used by mixing.
474 I_StartSound
475 ( int id,
476 int vol,
477 int sep,
478 int pitch,
479 int priority )
482 // UNUSED
483 priority = 0;
485 #ifdef SNDSERV
486 if (sndserver)
488 fprintf(sndserver, "p%2.2x%2.2x%2.2x%2.2x\n", id, pitch, vol, sep);
489 fflush(sndserver);
491 // warning: control reaches end of non-void function.
492 return id;
493 #else
494 // Debug.
495 //fprintf( stderr, "starting sound %d", id );
497 // Returns a handle (not used).
498 id = addsfx( id, vol, steptable[pitch], sep );
500 // fprintf( stderr, "/handle is %d\n", id );
502 return id;
503 #endif
508 void I_StopSound (int handle)
510 // You need the handle returned by StartSound.
511 // Would be looping all channels,
512 // tracking down the handle,
513 // an setting the channel to zero.
515 // UNUSED.
516 handle = 0;
520 int I_SoundIsPlaying(int handle)
522 // Ouch.
523 return gametic < handle;
530 // This function loops all active (internal) sound
531 // channels, retrieves a given number of samples
532 // from the raw sound data, modifies it according
533 // to the current (internal) channel parameters,
534 // mixes the per channel samples into the global
535 // mixbuffer, clamping it to the allowed range,
536 // and sets up everything for transferring the
537 // contents of the mixbuffer to the (two)
538 // hardware channels (left and right, that is).
540 // This function currently supports only 16bit.
542 void I_UpdateSound( void )
544 #ifdef SNDINTR
545 // Debug. Count buffer misses with interrupt.
546 static int misses = 0;
547 #endif
550 // Mix current sound data.
551 // Data, from raw sound, for right and left.
552 register unsigned int sample;
553 register int dl;
554 register int dr;
556 // Pointers in global mixbuffer, left, right, end.
557 signed short* leftout;
558 signed short* rightout;
559 signed short* leftend;
560 // Step in mixbuffer, left and right, thus two.
561 int step;
563 // Mixing channel index.
564 int chan;
566 // Left and right channel
567 // are in global mixbuffer, alternating.
568 leftout = mixbuffer;
569 rightout = mixbuffer+1;
570 step = 2;
572 // Determine end, for left channel only
573 // (right channel is implicit).
574 leftend = mixbuffer + SAMPLECOUNT*step;
576 // Mix sounds into the mixing buffer.
577 // Loop over step*SAMPLECOUNT,
578 // that is 512 values for two channels.
579 while (leftout != leftend)
581 // Reset left/right value.
582 dl = 0;
583 dr = 0;
585 // Love thy L2 chache - made this a loop.
586 // Now more channels could be set at compile time
587 // as well. Thus loop those channels.
588 for ( chan = 0; chan < NUM_CHANNELS; chan++ )
590 // Check channel, if active.
591 if (channels[ chan ])
593 // Get the raw data from the channel.
594 sample = *channels[ chan ];
595 // Add left and right part
596 // for this channel (sound)
597 // to the current data.
598 // Adjust volume accordingly.
599 dl += channelleftvol_lookup[ chan ][sample];
600 dr += channelrightvol_lookup[ chan ][sample];
601 // Increment index ???
602 channelstepremainder[ chan ] += channelstep[ chan ];
603 // MSB is next sample???
604 channels[ chan ] += channelstepremainder[ chan ] >> 16;
605 // Limit to LSB???
606 channelstepremainder[ chan ] &= 65536-1;
608 // Check whether we are done.
609 if (channels[ chan ] >= channelsend[ chan ])
610 channels[ chan ] = 0;
614 // Clamp to range. Left hardware channel.
615 // Has been char instead of short.
616 // if (dl > 127) *leftout = 127;
617 // else if (dl < -128) *leftout = -128;
618 // else *leftout = dl;
620 if (dl > 0x7fff)
621 *leftout = 0x7fff;
622 else if (dl < -0x8000)
623 *leftout = -0x8000;
624 else
625 *leftout = dl;
627 // Same for right hardware channel.
628 if (dr > 0x7fff)
629 *rightout = 0x7fff;
630 else if (dr < -0x8000)
631 *rightout = -0x8000;
632 else
633 *rightout = dr;
635 // Increment current pointers in mixbuffer.
636 leftout += step;
637 rightout += step;
640 #ifdef SNDINTR
641 // Debug check.
642 if ( flag )
644 misses += flag;
645 flag = 0;
648 if ( misses > 10 )
650 fprintf( stderr, "I_SoundUpdate: missed 10 buffer writes\n");
651 misses = 0;
654 // Increment flag for update.
655 flag++;
656 #endif
661 // This would be used to write out the mixbuffer
662 // during each game loop update.
663 // Updates sound buffer and audio device at runtime.
664 // It is called during Timer interrupt with SNDINTR.
665 // Mixing now done synchronous, and
666 // only output be done asynchronous?
668 void
669 I_SubmitSound(void)
671 // Write it to DSP device.
672 write(audio_fd, mixbuffer, SAMPLECOUNT*BUFMUL);
677 void
678 I_UpdateSoundParams
679 ( int handle,
680 int vol,
681 int sep,
682 int pitch)
684 // I fail too see that this is used.
685 // Would be using the handle to identify
686 // on which channel the sound might be active,
687 // and resetting the channel parameters.
689 // UNUSED.
690 handle = vol = sep = pitch = 0;
696 void I_ShutdownSound(void)
698 #ifdef SNDSERV
699 if (sndserver)
701 // Send a "quit" command.
702 fprintf(sndserver, "q\n");
703 fflush(sndserver);
705 #else
706 // Wait till all pending sounds are finished.
707 int done = 0;
708 int i;
711 // FIXME (below).
712 fprintf( stderr, "I_ShutdownSound: NOT finishing pending sounds\n");
713 fflush( stderr );
715 while ( !done )
717 for( i=0 ; i<8 && !channels[i] ; i++);
719 // FIXME. No proper channel output.
720 //if (i==8)
721 done=1;
723 #ifdef SNDINTR
724 I_SoundDelTimer();
725 #endif
727 // Cleaning up -releasing the DSP device.
728 close ( audio_fd );
729 #endif
731 // Done.
732 return;
740 void
741 I_InitSound()
743 #ifdef SNDSERV
744 char buffer[256];
746 if (getenv("DOOMWADDIR"))
747 sprintf(buffer, "%s/%s",
748 getenv("DOOMWADDIR"),
749 sndserver_filename);
750 else
751 sprintf(buffer, "%s", sndserver_filename);
753 // start sound process
754 if ( !access(buffer, X_OK) )
756 strcat(buffer, " -quiet");
757 sndserver = popen(buffer, "w");
759 else
760 fprintf(stderr, "Could not start sound server [%s]\n", buffer);
761 #else
763 int i;
765 #ifdef SNDINTR
766 fprintf( stderr, "I_SoundSetTimer: %d microsecs\n", SOUND_INTERVAL );
767 I_SoundSetTimer( SOUND_INTERVAL );
768 #endif
770 // Secure and configure sound device first.
771 fprintf( stderr, "I_InitSound: ");
773 audio_fd = open("/dev/dsp", O_WRONLY);
774 if (audio_fd<0)
775 fprintf(stderr, "Could not open /dev/dsp\n");
778 i = 11 | (2<<16);
779 myioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &i);
780 myioctl(audio_fd, SNDCTL_DSP_RESET, 0);
782 i=SAMPLERATE;
784 myioctl(audio_fd, SNDCTL_DSP_SPEED, &i);
786 i=1;
787 myioctl(audio_fd, SNDCTL_DSP_STEREO, &i);
789 myioctl(audio_fd, SNDCTL_DSP_GETFMTS, &i);
791 if (i&=AFMT_S16_LE)
792 myioctl(audio_fd, SNDCTL_DSP_SETFMT, &i);
793 else
794 fprintf(stderr, "Could not play signed 16 data\n");
796 fprintf(stderr, " configured audio device\n" );
799 // Initialize external data (all sounds) at start, keep static.
800 fprintf( stderr, "I_InitSound: ");
802 for (i=1 ; i<NUMSFX ; i++)
804 // Alias? Example is the chaingun sound linked to pistol.
805 if (!S_sfx[i].link)
807 // Load data from WAD file.
808 S_sfx[i].data = getsfx( S_sfx[i].name, &lengths[i] );
810 else
812 // Previously loaded already?
813 S_sfx[i].data = S_sfx[i].link->data;
814 lengths[i] = lengths[(S_sfx[i].link - S_sfx)/sizeof(sfxinfo_t)];
818 fprintf( stderr, " pre-cached all sound data\n");
820 // Now initialize mixbuffer with zero.
821 for ( i = 0; i< MIXBUFFERSIZE; i++ )
822 mixbuffer[i] = 0;
824 // Finished initialization.
825 fprintf(stderr, "I_InitSound: sound module ready\n");
827 #endif
834 // MUSIC API.
835 // Still no music done.
836 // Remains. Dummies.
838 void I_InitMusic(void) { }
839 void I_ShutdownMusic(void) { }
841 static int looping=0;
842 static int musicdies=-1;
844 void I_PlaySong(int handle, int looping)
846 // UNUSED.
847 handle = looping = 0;
848 musicdies = gametic + TICRATE*30;
851 void I_PauseSong (int handle)
853 // UNUSED.
854 handle = 0;
857 void I_ResumeSong (int handle)
859 // UNUSED.
860 handle = 0;
863 void I_StopSong(int handle)
865 // UNUSED.
866 handle = 0;
868 looping = 0;
869 musicdies = 0;
872 void I_UnRegisterSong(int handle)
874 // UNUSED.
875 handle = 0;
878 int I_RegisterSong(void* data)
880 // UNUSED.
881 data = NULL;
883 return 1;
886 // Is the song playing?
887 int I_QrySongPlaying(int handle)
889 // UNUSED.
890 handle = 0;
891 return looping || musicdies > gametic;
897 // Experimental stuff.
898 // A Linux timer interrupt, for asynchronous
899 // sound output.
900 // I ripped this out of the Timer class in
901 // our Difference Engine, including a few
902 // SUN remains...
904 #ifdef sun
905 typedef sigset_t tSigSet;
906 #else
907 typedef int tSigSet;
908 #endif
911 // We might use SIGVTALRM and ITIMER_VIRTUAL, if the process
912 // time independend timer happens to get lost due to heavy load.
913 // SIGALRM and ITIMER_REAL doesn't really work well.
914 // There are issues with profiling as well.
915 static int /*__itimer_which*/ itimer = ITIMER_REAL;
917 static int sig = SIGALRM;
919 // Interrupt handler.
920 void I_HandleSoundTimer( int ignore )
922 // Debug.
923 //fprintf( stderr, "%c", '+' ); fflush( stderr );
925 // Feed sound device if necesary.
926 if ( flag )
928 // See I_SubmitSound().
929 // Write it to DSP device.
930 write(audio_fd, mixbuffer, SAMPLECOUNT*BUFMUL);
932 // Reset flag counter.
933 flag = 0;
935 else
936 return;
938 // UNUSED, but required.
939 ignore = 0;
940 return;
943 // Get the interrupt. Set duration in millisecs.
944 int I_SoundSetTimer( int duration_of_tick )
946 // Needed for gametick clockwork.
947 struct itimerval value;
948 struct itimerval ovalue;
949 struct sigaction act;
950 struct sigaction oact;
952 int res;
954 // This sets to SA_ONESHOT and SA_NOMASK, thus we can not use it.
955 // signal( _sig, handle_SIG_TICK );
957 // Now we have to change this attribute for repeated calls.
958 act.sa_handler = I_HandleSoundTimer;
959 #ifndef sun
960 //ac t.sa_mask = _sig;
961 #endif
962 act.sa_flags = SA_RESTART;
964 sigaction( sig, &act, &oact );
966 value.it_interval.tv_sec = 0;
967 value.it_interval.tv_usec = duration_of_tick;
968 value.it_value.tv_sec = 0;
969 value.it_value.tv_usec = duration_of_tick;
971 // Error is -1.
972 res = setitimer( itimer, &value, &ovalue );
974 // Debug.
975 if ( res == -1 )
976 fprintf( stderr, "I_SoundSetTimer: interrupt n.a.\n");
978 return res;
982 // Remove the interrupt. Set duration to zero.
983 void I_SoundDelTimer()
985 // Debug.
986 if ( I_SoundSetTimer( 0 ) == -1)
987 fprintf( stderr, "I_SoundDelTimer: failed to remove interrupt. Doh!\n");