]> icculus.org git repositories - theoddone33/hheretic.git/blob - base/i_sound.c
44b2c949c0e8b07e30fcff539c6773d0fa972178
[theoddone33/hheretic.git] / base / i_sound.c
1 // $Id$
2
3
4 #include <stdio.h>
5 #include <math.h>       // pow()
6 #include <pthread.h>
7 #include "doomdef.h"
8 #include "sounds.h"
9 #include "i_sound.h"
10 #include "audio_plugin.h"
11
12
13 #define SAMPLE_FORMAT   FMT_S16_LE
14 #define SAMPLE_ZERO     0
15 #define SAMPLE_RATE     11025   // Hz
16 #define SAMPLE_CHANNELS 2
17
18 #if 0
19 #define SAMPLE_TYPE     char
20 #else
21 #define SAMPLE_TYPE     short
22 #endif
23
24
25 /*
26  *
27  *                           SOUND HEADER & DATA
28  *
29  *
30  */
31
32
33 int tsm_ID = -1;
34
35 const char snd_prefixen[] = { 'P', 'P', 'A', 'S', 'S', 'S', 'M',
36   'M', 'M', 'S' };
37
38 int snd_Channels;
39 int snd_DesiredMusicDevice, snd_DesiredSfxDevice;
40 int snd_MusicDevice,    // current music card # (index to dmxCodes)
41         snd_SfxDevice,      // current sfx card # (index to dmxCodes)
42         snd_MaxVolume,      // maximum volume for sound
43         snd_MusicVolume;    // maximum volume for music
44 int dmxCodes[NUM_SCARDS]; // the dmx code for a given card
45
46 int     snd_SBport, snd_SBirq, snd_SBdma;       // sound blaster variables
47 int     snd_Mport;                              // midi variables
48
49 extern boolean  snd_MusicAvail, // whether music is available
50                 snd_SfxAvail;   // whether sfx are available
51
52 // Function prototypes (if needed) - DDOI
53 void I_UpdateSoundParams(int handle, int vol, int sep, int pitch);
54
55 void I_PauseSong(int handle)
56 {
57 }
58
59 void I_ResumeSong(int handle)
60 {
61 }
62
63 void I_SetMusicVolume(int volume)
64 {
65 }
66
67 void I_SetSfxVolume(int volume)
68 {
69 }
70
71 /*
72  *
73  *                              SONG API
74  *
75  */
76
77 int I_RegisterSong(void *data)
78 {
79   return 0;
80 }
81
82 void I_UnRegisterSong(int handle)
83 {
84 }
85
86 int I_QrySongPlaying(int handle)
87 {
88   return 0;
89 }
90
91 // Stops a song.  MUST be called before I_UnregisterSong().
92
93 void I_StopSong(int handle)
94 {
95 }
96
97 void I_PlaySong(int handle, boolean looping)
98 {
99 }
100
101 /*
102  *
103  *                                 SOUND FX API
104  *
105  */
106
107
108 typedef struct
109 {
110     unsigned char* begin;           // pointers into Sample.firstSample
111     unsigned char* end;
112
113     SAMPLE_TYPE* lvol_table;               // point into vol_lookup
114     SAMPLE_TYPE* rvol_table;
115
116     unsigned int pitch_step;
117     unsigned int step_remainder;    // 0.16 bit remainder of last step.
118     
119     int pri;
120     unsigned int time;
121 } Channel;
122
123
124 typedef struct
125 {
126     short a;            // always 3
127     short freq;         // always 11025
128     long length;        // sample length
129     unsigned char firstSample;
130 } Sample;
131
132
133 extern OutputPlugin* get_oplugin_info();
134 static OutputPlugin* audioPI = 0;
135 static int audio_exit_thread = 1;
136 static pthread_t audio_thread;
137
138
139 #define CHAN_COUNT        8
140 Channel channel[ CHAN_COUNT ];
141
142 #define MAX_VOL           64        // 64 keeps our table down to 16Kb
143 SAMPLE_TYPE vol_lookup[ MAX_VOL * 256 ];
144
145 int steptable[ 256 ];               // Pitch to stepping lookup
146
147
148 #define BUF_LEN 256*2
149  
150  
151 void* audio_loop( void* arg )
152 {
153     Channel* chan;
154     Channel* cend;
155     char buf[ BUF_LEN ];
156     SAMPLE_TYPE* begin;
157     SAMPLE_TYPE* end;
158 //    int remain;
159     unsigned int sample;
160     register int dl;
161     register int dr;
162  
163     end = (SAMPLE_TYPE*) (buf + BUF_LEN);
164     cend = channel + CHAN_COUNT;
165
166     while( ! audio_exit_thread )
167     {
168         begin = (SAMPLE_TYPE*) buf;
169         while( begin < end )
170         {
171             // Mix all the channels together.
172
173             dl = SAMPLE_ZERO;
174             dr = SAMPLE_ZERO;
175
176             chan = channel;
177             for( ; chan < cend; chan++ )
178             {
179                 // Check channel, if active.
180                 if( chan->begin )
181                 {
182                     // Get the sample from the channel. 
183                     sample = *chan->begin;
184
185                     // Adjust volume accordingly.
186                     dl += chan->lvol_table[ sample ];
187                     dr += chan->rvol_table[ sample ];
188
189                     // Increment sample pointer with pitch adjustment.
190                     chan->step_remainder += chan->pitch_step;
191                     chan->begin += chan->step_remainder >> 16;
192                     chan->step_remainder &= 65535;
193
194                     // Check whether we are done.
195                     if( chan->begin >= chan->end )
196                     {
197                         chan->begin = 0;
198                         //printf( "  channel done %d\n", chan );
199                     }
200                 }
201             }
202             
203 #if 0   //SAMPLE_FORMAT
204             if( dl > 127 ) dl = 127;
205             else if( dl < -128 ) dl = -128;
206
207             if( dr > 127 ) dr = 127;
208             else if( dr < -128 ) dr = -128;
209 #else
210             if( dl > 0x7fff ) dl = 0x7fff;
211             else if( dl < -0x8000 ) dl = -0x8000;
212
213             if( dr > 0x7fff ) dr = 0x7fff;
214             else if( dr < -0x8000 ) dr = -0x8000;
215 #endif
216
217             *begin++ = dl;
218             *begin++ = dr;
219         }
220
221         // This write is expected to block.
222         audioPI->write_audio( buf, BUF_LEN );
223     }
224  
225     pthread_exit(NULL);
226 }
227
228
229 // Gets lump nums of the named sound.  Returns pointer which will be
230 // passed to I_StartSound() when you want to start an SFX.  Must be
231 // sure to pass this to UngetSoundEffect() so that they can be
232 // freed!
233
234
235 int I_GetSfxLumpNum(sfxinfo_t *sound)
236 {
237 //  char namebuf[9]; // unused variable - DDOI
238
239   if (sound->name == 0)
240           return 0;
241   if(sound->link) sound = sound->link;
242   return W_GetNumForName(sound->name);
243 }
244
245
246 // Id is unused.
247 // Data is a pointer to a Sample structure.
248 // Volume ranges from 0 to 127.
249 // Separation (orientation/stereo) ranges from 0 to 255.  128 is balanced.
250 // Pitch ranges from 0 to 255.  Normal is 128.
251 // Priority looks to be unused (always 0).
252
253 int I_StartSound( int id, void* data, int vol, int sep, int pitch, int priority)
254 {
255     // Relative time order to find oldest sound.
256     static unsigned int soundTime = 0;
257     int chanId;
258     Sample* sample;
259     Channel* chan;
260     int oldest;
261     int i;
262     
263
264     // Find an empty channel, the oldest playing channel, or default to 0.
265     // Currently ignoring priority.
266
267     chanId = 0;
268     oldest = soundTime;
269     for( i = 0; i < CHAN_COUNT; i++ )
270     {
271         if( ! channel[ i ].begin )
272         {
273             chanId = i;
274             break;
275         }
276         if( channel[ i ].time < oldest )
277         {
278             chanId = i;
279             oldest = channel[ i ].time;
280         }
281     }
282
283     sample = (Sample*) data;
284     chan = &channel[ chanId ];
285
286     I_UpdateSoundParams( chanId + 1, vol, sep, pitch );
287
288     // begin must be set last because the audio thread will access the channel
289     // once it is non-zero.  Perhaps this should be protected by a mutex.
290
291     chan->pri = priority;
292     chan->time = soundTime;
293     chan->end = &sample->firstSample + sample->length;
294     chan->begin = &sample->firstSample;
295
296     soundTime++;
297
298 #if 0
299     printf( "I_StartSound %d: v:%d s:%d p:%d pri:%d | %d %d %d %d\n",
300             id, vol, sep, pitch, priority,
301             chanId, chan->pitch_step, sample->a, sample->freq );
302 #endif
303
304     return chanId + 1;
305 }
306
307 void I_StopSound(int handle)
308 {
309     handle--;
310     handle &= 7;
311     channel[ handle ].begin = 0;
312 }
313
314 int I_SoundIsPlaying(int handle)
315 {
316     handle--;
317     handle &= 7;
318     return( channel[ handle ].begin != 0 );
319 }
320
321 void I_UpdateSoundParams(int handle, int vol, int sep, int pitch)
322 {
323     int lvol, rvol;
324     Channel* chan;
325
326     // Set left/right channel volume based on seperation.
327
328     sep += 1;       // range 1 - 256
329     lvol = vol - ((vol * sep * sep) >> 16); // (256*256);
330     sep = sep - 257;
331     rvol = vol - ((vol * sep * sep) >> 16);    
332
333
334     // Sanity check, clamp volume.
335     if( rvol < 0 )
336     {
337         //printf( "rvol out of bounds %d, id %d\n", rvol, handle );
338         rvol = 0;
339     }
340     else if( rvol > 127 )
341     {
342         //printf( "rvol out of bounds %d, id %d\n", rvol, handle );
343         rvol = 127;
344     }
345     
346     if( lvol < 0 )
347     {
348         //printf( "lvol out of bounds %d, id %d\n", lvol, handle );
349         lvol = 0;
350     }
351     else if( lvol > 127 )
352     {
353         //printf( "lvol out of bounds %d, id %d\n", lvol, handle );
354         lvol = 127;
355     }
356
357     // Limit to MAX_VOL (64)
358     lvol >>= 1;
359     rvol >>= 1;
360
361     handle--;
362     handle &= 7;
363     chan = &channel[ handle ];
364     chan->pitch_step = steptable[ pitch ];
365     chan->step_remainder = 0;
366     chan->lvol_table = &vol_lookup[ lvol * 256 ];
367     chan->rvol_table = &vol_lookup[ rvol * 256 ];
368 }
369
370 /*
371  *
372  *                                                      SOUND STARTUP STUFF
373  *
374  *
375  */
376
377 // inits all sound stuff
378
379 void I_StartupSound (void)
380 {
381     int ok;
382
383     snd_MusicDevice = snd_SfxDevice = 0;
384
385     if( M_CheckParm( "-nosound" ) )
386     {
387         tprintf("I_StartupSound: Sound Disabled.\n",1);
388         return;
389     }
390
391     if (debugmode)
392         tprintf("I_StartupSound: Hope you hear a pop.\n",1);
393
394     /* Using get_oplugin_info() from oss.c.  In the future this could
395      load from a real shared library plugin. */
396     audioPI = get_oplugin_info();
397     audioPI->init();
398     audioPI->about();
399     
400     ok = audioPI->open_audio( SAMPLE_FORMAT, SAMPLE_RATE, SAMPLE_CHANNELS );
401     if( ok )
402     {
403         audio_exit_thread = 0;
404         pthread_create( &audio_thread, NULL, audio_loop, NULL);
405     }
406     else
407     {
408         fprintf( stderr, "I_StartupSound: failed\n" );
409     }
410 }
411
412 // shuts down all sound stuff
413
414 void I_ShutdownSound (void)
415 {
416     if( audioPI )
417     {
418         if( ! audio_exit_thread )
419         {
420             audio_exit_thread = 1;
421             pthread_join( audio_thread, NULL );
422         }
423         audioPI->close_audio();
424     }
425 }
426
427 void I_SetChannels(int channels)
428 {
429     int v, j;
430     int* steptablemid;
431
432     // We always have CHAN_COUNT channels.
433
434     for( j = 0; j < CHAN_COUNT; j++ )
435     {
436         channel[ j ].begin = 0;
437         channel[ j ].end = 0;
438         channel[ j ].time = 0;
439     }
440
441
442     // This table provides step widths for pitch parameters.
443     steptablemid = steptable + 128;
444     for( j = -128; j < 128; j++ )
445     {
446         steptablemid[ j ] = (int) (pow( 2.0, (j/64.0) ) * 65536.0);
447     }
448
449
450     // Generate the volume lookup tables.
451     for( v = 0 ; v < MAX_VOL ; v++ )
452     {
453         for( j = 0; j < 256; j++ )
454         {
455             //vol_lookup[v*256+j] = 128 + ((v * (j-128)) / (MAX_VOL-1));
456
457             // Turn the unsigned samples into signed samples.
458 #if 0   // SAMPLE_FORMAT
459             vol_lookup[v*256+j] = (v * (j-128)) / (MAX_VOL-1);
460 #else
461             vol_lookup[v*256+j] = (v * (j-128) * 256) / (MAX_VOL-1);
462 #endif
463
464             //printf("vol_lookup[%d*256+%d] = %d\n", v, j, vol_lookup[v*256+j]);
465         }
466     }
467 }
468
469
470 /* EOF */