summaryrefslogtreecommitdiff
path: root/apps/plugins/doom/i_sound.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/doom/i_sound.c')
-rw-r--r--apps/plugins/doom/i_sound.c607
1 files changed, 607 insertions, 0 deletions
diff --git a/apps/plugins/doom/i_sound.c b/apps/plugins/doom/i_sound.c
new file mode 100644
index 0000000000..471b0ee193
--- /dev/null
+++ b/apps/plugins/doom/i_sound.c
@@ -0,0 +1,607 @@
1/* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25 * 02111-1307, USA.
26 *
27 * DESCRIPTION:
28 * System interface for sound.
29 *
30 *-----------------------------------------------------------------------------
31 */
32
33#include "z_zone.h"
34
35#include "i_system.h"
36#include "i_sound.h"
37#include "m_argv.h"
38#include "m_misc.h"
39#include "w_wad.h"
40#include "m_swap.h"
41#include "d_main.h"
42#include "doomdef.h"
43#include "rockmacros.h"
44
45// The number of internal mixing channels,
46// the samples calculated for each mixing step,
47// the size of the 16bit, 2 hardware channel (stereo)
48// mixing buffer, and the samplerate of the raw data.
49
50// Needed for calling the actual sound output.
51#define SAMPLECOUNT 512
52
53#define NUM_CHANNELS 16
54// It is 2 for 16bit, and 2 for two channels.
55#define BUFMUL 4
56#define MIXBUFFERSIZE (SAMPLECOUNT*BUFMUL)
57
58#define SAMPLERATE 11025 // 44100 22050 11025
59#define SAMPLESIZE 2 // 16bit
60
61// The global mixing buffer.
62// Basically, samples from all active internal channels
63// are modifed and added, and stored in the buffer
64// that is submitted to the audio device.
65signed short mixbuffer[MIXBUFFERSIZE];
66
67typedef struct {
68 // SFX id of the playing sound effect.
69 // Used to catch duplicates (like chainsaw).
70 int id;
71 // The channel step amount...
72 unsigned int step;
73 // ... and a 0.16 bit remainder of last step.
74 unsigned int stepremainder;
75 unsigned int samplerate;
76 // The channel data pointers, start and end.
77 const unsigned char* data;
78 const unsigned char* enddata;
79 // Time/gametic that the channel started playing,
80 // used to determine oldest, which automatically
81 // has lowest priority.
82 // In case number of active sounds exceeds
83 // available channels.
84 int starttime;
85 // Hardware left and right channel volume lookup.
86 int *leftvol_lookup;
87 int *rightvol_lookup;
88} channel_info_t;
89
90channel_info_t channelinfo[NUM_CHANNELS];
91
92int *vol_lookup; // Volume lookups.
93
94int steptable[256]; // Pitch to stepping lookup. (Not setup properly right now)
95
96//
97// This function loads the sound data from the WAD lump for single sound.
98// It is used to cache all the sounddata at startup.
99//
100void* getsfx( const char* sfxname )
101{
102 unsigned char* sfx;
103 unsigned char* paddedsfx;
104 int size;
105 char name[20];
106 int sfxlump;
107
108 // Get the sound data from the WAD, allocate lump
109 // in zone memory.
110 snprintf(name, sizeof(name), "ds%s", sfxname);
111
112 // Now, there is a severe problem with the sound handling, in it is not
113 // (yet/anymore) gamemode aware. That means, sounds from DOOM II will be
114 // requested even with DOOM shareware.
115 // The sound list is wired into sounds.c, which sets the external variable.
116 // I do not do runtime patches to that variable. Instead, we will use a
117 // default sound for replacement.
118 if ( W_CheckNumForName(name) == -1 )
119 sfxlump = W_GetNumForName("dspistol");
120 else
121 sfxlump = W_GetNumForName(name);
122
123 size = W_LumpLength( sfxlump );
124
125 sfx = (unsigned char*)W_CacheLumpNum( sfxlump);
126
127 paddedsfx = (unsigned char*)malloc( size ); // Allocate from memory.
128 memcpy(paddedsfx, sfx, size ); // Now copy and pad.
129 W_UnlockLumpNum(sfxlump); // Remove the cached lump.
130
131 return (void *) (paddedsfx); // Return allocated data.
132}
133
134/* cph
135 * stopchan
136 * Stops a sound
137 */
138static void stopchan(int i)
139{
140 channelinfo[i].data=NULL;
141}
142
143//
144// This function adds a sound to the
145// list of currently active sounds,
146// which is maintained as a given number
147// (eight, usually) of internal channels.
148// Returns a handle.
149//
150int addsfx( int sfxid, int channel)
151{
152 stopchan(channel);
153
154 // We will handle the new SFX.
155 // Set pointer to raw data.
156 {
157 int lump = S_sfx[sfxid].lumpnum;
158 size_t len = W_LumpLength(lump);
159
160 /* Find padded length */
161 len -= 8;
162 channelinfo[channel].data = S_sfx[sfxid].data;
163
164 /* Set pointer to end of raw data. */
165 channelinfo[channel].enddata = channelinfo[channel].data + len - 1;
166 channelinfo[channel].samplerate = (channelinfo[channel].data[3]<<8)+channelinfo[channel].data[2];
167 channelinfo[channel].data += 8; /* Skip header */
168 }
169
170 channelinfo[channel].stepremainder = 0;
171 // Should be gametic, I presume.
172 channelinfo[channel].starttime = gametic;
173
174 // Preserve sound SFX id,
175 // e.g. for avoiding duplicates of chainsaw.
176 channelinfo[channel].id = sfxid;
177
178 return channel;
179}
180
181static void updateSoundParams(int handle, int volume, int seperation, int pitch)
182{
183 int rightvol;
184 int leftvol;
185 int slot = handle;
186 int step = steptable[pitch];
187#ifdef RANGECHECK
188 if (handle>=NUM_CHANNELS)
189 I_Error("I_UpdateSoundParams: handle out of range");
190#endif
191 // Set stepping
192 // MWM 2000-12-24: Calculates proportion of channel samplerate
193 // to global samplerate for mixing purposes.
194 // Patched to shift left *then* divide, to minimize roundoff errors
195 // as well as to use SAMPLERATE as defined above, not to assume 11025 Hz
196 if (pitched_sounds)
197 channelinfo[slot].step = step + (((channelinfo[slot].samplerate<<16)/SAMPLERATE)-65536);
198 else
199 channelinfo[slot].step = ((channelinfo[slot].samplerate<<16)/SAMPLERATE);
200
201 // Separation, that is, orientation/stereo.
202 // range is: 1 - 256
203 seperation += 1;
204
205 // Per left/right channel.
206 // x^2 seperation,
207 // adjust volume properly.
208 leftvol = volume - ((volume*seperation*seperation) >> 16);
209 seperation = seperation - 257;
210 rightvol= volume - ((volume*seperation*seperation) >> 16);
211
212 // Sanity check, clamp volume.
213 if (rightvol < 0 || rightvol > 127)
214 I_Error("rightvol out of bounds");
215
216 if (leftvol < 0 || leftvol > 127)
217 I_Error("leftvol out of bounds");
218
219 // Get the proper lookup table piece
220 // for this volume level???
221 channelinfo[slot].leftvol_lookup = &vol_lookup[leftvol*256];
222 channelinfo[slot].rightvol_lookup = &vol_lookup[rightvol*256];
223}
224
225void I_UpdateSoundParams(int handle, int volume, int seperation, int pitch)
226{
227 updateSoundParams(handle, volume, seperation, pitch);
228}
229
230//
231// SFX API
232// Note: this was called by S_Init.
233// However, whatever they did in the
234// old DPMS based DOS version, this
235// were simply dummies in the Linux
236// version.
237// See soundserver initdata().
238//
239void I_SetChannels()
240{
241 // Init internal lookups (raw data, mixing buffer, channels).
242 // This function sets up internal lookups used during
243 // the mixing process.
244 int i;
245 int j;
246 int* steptablemid = steptable + 128;
247
248 // Okay, reset internal mixing channels to zero.
249 for (i=0; i<NUM_CHANNELS; i++)
250 memset(&channelinfo[i],0,sizeof(channel_info_t));
251
252 // This table provides step widths for pitch parameters.
253 for (i=-128 ; i<128 ; i++)
254 steptablemid[i]=2;
255// steptablemid[i] = (int)(pow(1.2, ((double)i/(64.0*SAMPLERATE/11025)))*65536.0);
256
257 // Generates volume lookup tables
258 // which also turn the unsigned samples
259 // into signed samples.
260 for (i=0 ; i<128 ; i++)
261 for (j=0 ; j<256 ; j++)
262 vol_lookup[i*256+j] = 3*(i*(j-128)*256)/191;
263}
264
265void I_SetSfxVolume(int volume)
266{
267 // Identical to DOS.
268 // Basically, this should propagate
269 // the menu/config file setting
270 // to the state variable used in
271 // the mixing.
272 snd_SfxVolume = volume;
273}
274
275// MUSIC API - dummy. Some code from DOS version.
276void I_SetMusicVolume(int volume)
277{
278 // Internal state variable.
279 snd_MusicVolume = volume;
280 // Now set volume on output device.
281 // Whatever( snd_MusciVolume );
282}
283
284//
285// Retrieve the raw data lump index
286// for a given SFX name.
287//
288int I_GetSfxLumpNum(sfxinfo_t* sfx)
289{
290 char namebuf[9];
291 snprintf(namebuf, sizeof(namebuf), "ds%s", sfx->name);
292 return W_GetNumForName(namebuf);
293}
294
295//
296// Starting a sound means adding it
297// to the current list of active sounds
298// in the internal channels.
299// As the SFX info struct contains
300// e.g. a pointer to the raw data,
301// it is ignored.
302// As our sound handling does not handle
303// priority, it is ignored.
304// Pitching (that is, increased speed of playback)
305// is set, but currently not used by mixing.
306//
307int I_StartSound(int id, int channel, int vol, int sep, int pitch, int priority)
308{
309 (void)priority;
310 int handle;
311
312 // Returns a handle (not used).
313 handle = addsfx(id,channel);
314
315#ifdef RANGECHECK
316 if (handle>=NUM_CHANNELS)
317 I_Error("I_StartSound: handle out of range");
318#endif
319 updateSoundParams(handle, vol, sep, pitch);
320
321 return handle;
322}
323
324void I_StopSound (int handle)
325{
326#ifdef RANGECHECK
327 if (handle>=NUM_CHANNELS)
328 I_Error("I_StopSound: handle out of range");
329#endif
330 stopchan(handle);
331}
332
333int I_SoundIsPlaying(int handle)
334{
335#ifdef RANGECHECK
336 if (handle>=NUM_CHANNELS)
337 I_Error("I_SoundIsPlaying: handle out of range");
338#endif
339 return channelinfo[handle].data != NULL;
340}
341
342//
343// This function loops all active (internal) sound
344// channels, retrieves a given number of samples
345// from the raw sound data, modifies it according
346// to the current (internal) channel parameters,
347// mixes the per channel samples into the given
348// mixing buffer, and clamping it to the allowed
349// range.
350//
351// This function currently supports only 16bit.
352//
353
354bool swap=0;
355bool lastswap=1;
356 // Pointers in global mixbuffer, left, right, end.
357 signed short* leftout;
358 signed short* rightout;
359 signed short* leftend;
360
361void I_UpdateSound( void )
362{
363 // Mix current sound data.
364 // Data, from raw sound, for right and left.
365 register unsigned char sample;
366 register int dl;
367 register int dr;
368
369 // Step in mixbuffer, left and right, thus two.
370 int step;
371
372 // Mixing channel index.
373 int chan;
374
375 if(lastswap==swap)
376 return;
377 lastswap=swap;
378
379 // Left and right channel
380 // are in global mixbuffer, alternating.
381 leftout = (swap ? mixbuffer : mixbuffer + SAMPLECOUNT*2);
382 rightout = (swap ? mixbuffer : mixbuffer + SAMPLECOUNT*2)+1;
383 step = 2;
384
385 // Determine end, for left channel only
386 // (right channel is implicit).
387 leftend = (swap ? mixbuffer : mixbuffer + SAMPLECOUNT*2) + SAMPLECOUNT*step;
388
389 // Mix sounds into the mixing buffer.
390 // Loop over step*SAMPLECOUNT,
391 // that is 512 values for two channels.
392 while (leftout != leftend)
393 {
394 // Reset left/right value.
395 dl = 0;
396 dr = 0;
397
398 // Love thy L2 chache - made this a loop.
399 // Now more channels could be set at compile time
400 // as well. Thus loop those channels.
401 for ( chan = 0; chan < NUM_CHANNELS; chan++ )
402 {
403 // Check channel, if active.
404 if (channelinfo[chan].data)
405 {
406 // Get the raw data from the channel.
407 sample = (((unsigned int)channelinfo[chan].data[0] * (0x10000 - channelinfo[chan].stepremainder))
408 + ((unsigned int)channelinfo[chan].data[1] * (channelinfo[chan].stepremainder))) >> 16;
409 // Add left and right part
410 // for this channel (sound)
411 // to the current data.
412 // Adjust volume accordingly.
413 dl += channelinfo[chan].leftvol_lookup[sample];
414 dr += channelinfo[chan].rightvol_lookup[sample];
415 // Increment index ???
416 channelinfo[chan].stepremainder += channelinfo[chan].step;
417 // MSB is next sample???
418 channelinfo[chan].data += channelinfo[chan].stepremainder >> 16;
419 // Limit to LSB???
420 channelinfo[chan].stepremainder &= 0xffff;
421
422 // Check whether we are done.
423 if (channelinfo[chan].data >= channelinfo[chan].enddata)
424 stopchan(chan);
425 }
426 }
427
428 // Clamp to range. Left hardware channel.
429 // Has been char instead of short.
430 // if (dl > 127) *leftout = 127;
431 // else if (dl < -128) *leftout = -128;
432 // else *leftout = dl;
433
434 if (dl > 0x7fff)
435 *leftout = 0x7fff;
436 else if (dl < -0x8000)
437 *leftout = -0x8000;
438 else
439 *leftout = (signed short)dl;
440
441 // Same for right hardware channel.
442 if (dr > 0x7fff)
443 *rightout = 0x7fff;
444 else if (dr < -0x8000)
445 *rightout = -0x8000;
446 else
447 *rightout = (signed short)dr;
448
449 // Increment current pointers in mixbuffer.
450 leftout += step;
451 rightout += step;
452 }
453}
454
455//
456// This would be used to write out the mixbuffer
457// during each game loop update.
458// Updates sound buffer and audio device at runtime.
459// It is called during Timer interrupt with SNDINTR.
460// Mixing now done synchronous, and
461// only output be done asynchronous?
462//
463
464void get_more(unsigned char** start, size_t* size)
465{
466 // This code works fine, the only problem is that doom runs slower then the sound
467 // updates (sometimes). This code forces the update if the sound hasn't been
468 // remixed.
469 if(lastswap!=swap)
470 I_UpdateSound(); // Force sound update (We don't want stutters)
471
472 *start = (unsigned char*)((swap ? mixbuffer : mixbuffer + SAMPLECOUNT*2));
473 *size = SAMPLECOUNT*2*sizeof(short);
474 swap=!swap;
475}
476
477
478void I_SubmitSound(void)
479{
480 if (nosfxparm)
481 return;
482
483#if !defined(SIMULATOR)
484 rb->pcm_play_data(&get_more, NULL, 0);
485#endif
486}
487
488void I_ShutdownSound(void)
489{
490#if !defined(SIMULATOR)
491 rb->pcm_play_stop();
492 rb->pcm_set_frequency(44100); // 44100
493#endif
494}
495
496void I_InitSound()
497{
498 int i;
499
500 // Initialize external data (all sounds) at start, keep static.
501 printf( "I_InitSound: ");
502#if !defined(SIMULATOR)
503 rb->pcm_play_stop();
504 rb->pcm_set_frequency(SAMPLERATE);
505#endif
506
507 vol_lookup=malloc(128*256*sizeof(int));
508
509 for (i=1 ; i<NUMSFX ; i++)
510 {
511 if (!S_sfx[i].link) // Alias? Example is the chaingun sound linked to pistol.
512 S_sfx[i].data = getsfx( S_sfx[i].name); // Load data from WAD file.
513 else
514 S_sfx[i].data = S_sfx[i].link->data; // Previously loaded already?
515 }
516
517 printf( " pre-cached all sound data\n");
518
519 // Now initialize mixbuffer with zero.
520 for ( i = 0; i< MIXBUFFERSIZE; i++ )
521 mixbuffer[i] = 0;
522
523 // Finished initialization.
524 printf("I_InitSound: sound module ready\n");
525}
526
527//
528// MUSIC API.
529// Still no music done.
530// Remains. Dummies.
531//
532void I_InitMusic(void) {
533}
534void I_ShutdownMusic(void) {
535}
536
537static int looping=0;
538static int musicdies=-1;
539
540void I_PlaySong(int handle, int looping)
541{
542 // UNUSED.
543 handle = looping = 0;
544 musicdies = gametic + TICRATE*30;
545}
546
547void I_PauseSong (int handle)
548{
549 // UNUSED.
550 handle = 0;
551}
552
553void I_ResumeSong (int handle)
554{
555 // UNUSED.
556 handle = 0;
557}
558
559void I_StopSong(int handle)
560{
561 // UNUSED.
562 handle = 0;
563
564 looping = 0;
565 musicdies = 0;
566}
567
568void I_UnRegisterSong(int handle)
569{
570 // UNUSED.
571 handle = 0;
572}
573
574int I_RegisterSong(const void *data)
575{
576 // UNUSED.
577 data = NULL;
578
579 return 1;
580}
581
582// Is the song playing?
583int I_QrySongPlaying(int handle)
584{
585 // UNUSED.
586 handle = 0;
587 return looping || musicdies > gametic;
588}
589
590// Interrupt handler.
591void I_HandleSoundTimer( int ignore )
592{
593 (void)ignore;
594}
595
596// Get the interrupt. Set duration in millisecs.
597int I_SoundSetTimer( int duration_of_tick )
598{
599 (void)duration_of_tick;
600 // Error is -1.
601 return 0;
602}
603
604// Remove the interrupt. Set duration to zero.
605void I_SoundDelTimer(void)
606{
607}