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