Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
8547 maxcodehac 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:  none
20
//
21
//-----------------------------------------------------------------------------
22
 
23
 
24
static const char
25
rcsid[] = "$Id: s_sound.c,v 1.6 1997/02/03 22:45:12 b1 Exp $";
26
 
27
 
28
 
29
#include 
30
#include 
31
 
32
#include "i_system.h"
33
#include "i_sound.h"
34
#include "sounds.h"
35
#include "s_sound.h"
36
 
37
#include "z_zone.h"
38
#include "m_random.h"
39
#include "w_wad.h"
40
 
41
#include "doomdef.h"
42
#include "p_local.h"
43
 
44
#include "doomstat.h"
45
 
46
 
47
// Purpose?
48
const char snd_prefixen[]
49
= { 'P', 'P', 'A', 'S', 'S', 'S', 'M', 'M', 'M', 'S', 'S', 'S' };
50
 
51
#define S_MAX_VOLUME		127
52
 
53
// when to clip out sounds
54
// Does not fit the large outdoor areas.
55
#define S_CLIPPING_DIST		(1200*0x10000)
56
 
57
// Distance tp origin when sounds should be maxed out.
58
// This should relate to movement clipping resolution
59
// (see BLOCKMAP handling).
60
// Originally: (200*0x10000).
61
#define S_CLOSE_DIST		(160*0x10000)
62
 
63
 
64
#define S_ATTENUATOR		((S_CLIPPING_DIST-S_CLOSE_DIST)>>FRACBITS)
65
 
66
// Adjustable by menu.
67
#define NORM_VOLUME    		snd_MaxVolume
68
 
69
#define NORM_PITCH     		128
70
#define NORM_PRIORITY		64
71
#define NORM_SEP		128
72
 
73
#define S_PITCH_PERTURB		1
74
#define S_STEREO_SWING		(96*0x10000)
75
 
76
// percent attenuation from front to back
77
#define S_IFRACVOL		30
78
 
79
#define NA			0
80
#define S_NUMCHANNELS		2
81
 
82
 
83
// Current music/sfx card - index useless
84
//  w/o a reference LUT in a sound module.
85
extern int snd_MusicDevice;
86
extern int snd_SfxDevice;
87
// Config file? Same disclaimer as above.
88
extern int snd_DesiredMusicDevice;
89
extern int snd_DesiredSfxDevice;
90
 
91
 
92
 
93
typedef struct
94
{
95
    // sound information (if null, channel avail.)
96
    sfxinfo_t*	sfxinfo;
97
 
98
    // origin of sound
99
    void*	origin;
100
 
101
    // handle of the sound being played
102
    int		handle;
103
 
104
} channel_t;
105
 
106
 
107
// the set of channels available
108
static channel_t*	channels;
109
 
110
// These are not used, but should be (menu).
111
// Maximum volume of a sound effect.
112
// Internal default is max out of 0-15.
113
int 		snd_SfxVolume = 15;
114
 
115
// Maximum volume of music. Useless so far.
116
int 		snd_MusicVolume = 15;
117
 
118
 
119
 
120
// whether songs are mus_paused
121
static boolean		mus_paused;
122
 
123
// music currently being played
124
static musicinfo_t*	mus_playing=0;
125
 
126
// following is set
127
//  by the defaults code in M_misc:
128
// number of channels available
129
int			numChannels;
130
 
131
static int		nextcleanup;
132
 
133
 
134
 
135
//
136
// Internals.
137
//
138
int
139
S_getChannel
140
( void*		origin,
141
  sfxinfo_t*	sfxinfo );
142
 
143
 
144
int
145
S_AdjustSoundParams
146
( mobj_t*	listener,
147
  mobj_t*	source,
148
  int*		vol,
149
  int*		sep,
150
  int*		pitch );
151
 
152
void S_StopChannel(int cnum);
153
 
154
 
155
 
156
//
157
// Initializes sound stuff, including volume
158
// Sets channels, SFX and music volume,
159
//  allocates channel buffer, sets S_sfx lookup.
160
//
161
void S_Init
162
( int		sfxVolume,
163
  int		musicVolume )
164
{
165
  int		i;
166
 
167
  fprintf( stderr, "S_Init: default sfx volume %d\n", sfxVolume);
168
 
169
  // Whatever these did with DMX, these are rather dummies now.
170
  I_SetChannels();
171
 
172
  S_SetSfxVolume(sfxVolume);
173
  // No music with Linux - another dummy.
174
  S_SetMusicVolume(musicVolume);
175
 
176
  // Allocating the internal channels for mixing
177
  // (the maximum numer of sounds rendered
178
  // simultaneously) within zone memory.
179
  channels =
180
    (channel_t *) Z_Malloc(numChannels*sizeof(channel_t), PU_STATIC, 0);
181
 
182
  // Free all channels for use
183
  for (i=0 ; i
184
    channels[i].sfxinfo = 0;
185
 
186
  // no sounds are playing, and they are not mus_paused
187
  mus_paused = 0;
188
 
189
  // Note that sounds have not been cached (yet).
190
  for (i=1 ; i
191
    S_sfx[i].lumpnum = S_sfx[i].usefulness = -1;
192
}
193
 
194
 
195
 
196
 
197
//
198
// Per level startup code.
199
// Kills playing sounds at start of level,
200
//  determines music if any, changes music.
201
//
202
void S_Start(void)
203
{
204
  int cnum;
205
  int mnum;
206
 
207
  // kill all playing sounds at start of level
208
  //  (trust me - a good idea)
209
  for (cnum=0 ; cnum
210
    if (channels[cnum].sfxinfo)
211
      S_StopChannel(cnum);
212
 
213
  // start new music for the level
214
  mus_paused = 0;
215
 
216
  if (gamemode == commercial)
217
    mnum = mus_runnin + gamemap - 1;
218
  else
219
  {
220
    int spmus[]=
221
    {
222
      // Song - Who? - Where?
223
 
224
      mus_e3m4,	// American	e4m1
225
      mus_e3m2,	// Romero	e4m2
226
      mus_e3m3,	// Shawn	e4m3
227
      mus_e1m5,	// American	e4m4
228
      mus_e2m7,	// Tim 	e4m5
229
      mus_e2m4,	// Romero	e4m6
230
      mus_e2m6,	// J.Anderson	e4m7 CHIRON.WAD
231
      mus_e2m5,	// Shawn	e4m8
232
      mus_e1m9	// Tim		e4m9
233
    };
234
 
235
    if (gameepisode < 4)
236
      mnum = mus_e1m1 + (gameepisode-1)*9 + gamemap-1;
237
    else
238
      mnum = spmus[gamemap-1];
239
    }
240
 
241
  // HACK FOR COMMERCIAL
242
  //  if (commercial && mnum > mus_e3m9)
243
  //      mnum -= mus_e3m9;
244
 
245
  S_ChangeMusic(mnum, true);
246
 
247
  nextcleanup = 15;
248
}
249
 
250
 
251
 
252
 
253
 
254
void
255
S_StartSoundAtVolume
256
( void*		origin_p,
257
  int		sfx_id,
258
  int		volume )
259
{
260
 
261
  int		rc;
262
  int		sep;
263
  int		pitch;
264
  int		priority;
265
  sfxinfo_t*	sfx;
266
  int		cnum;
267
 
268
  mobj_t*	origin = (mobj_t *) origin_p;
269
 
270
 
271
  // Debug.
272
  /*fprintf( stderr,
273
  	   "S_StartSoundAtVolume: playing sound %d (%s)\n",
274
  	   sfx_id, S_sfx[sfx_id].name );*/
275
 
276
  // check for bogus sound #
277
  if (sfx_id < 1 || sfx_id > NUMSFX)
278
    I_Error("Bad sfx #: %d", sfx_id);
279
 
280
  sfx = &S_sfx[sfx_id];
281
 
282
  // Initialize sound parameters
283
  if (sfx->link)
284
  {
285
    pitch = sfx->pitch;
286
    priority = sfx->priority;
287
    volume += sfx->volume;
288
 
289
    if (volume < 1)
290
      return;
291
 
292
    if (volume > snd_SfxVolume)
293
      volume = snd_SfxVolume;
294
  }
295
  else
296
  {
297
    pitch = NORM_PITCH;
298
    priority = NORM_PRIORITY;
299
  }
300
 
301
 
302
  // Check to see if it is audible,
303
  //  and if not, modify the params
304
  if (origin && origin != players[consoleplayer].mo)
305
  {
306
    rc = S_AdjustSoundParams(players[consoleplayer].mo,
307
			     origin,
308
			     &volume,
309
			     &sep,
310
			     &pitch);
311
 
312
    if ( origin->x == players[consoleplayer].mo->x
313
	 && origin->y == players[consoleplayer].mo->y)
314
    {
315
      sep 	= NORM_SEP;
316
    }
317
 
318
    if (!rc)
319
      return;
320
  }
321
  else
322
  {
323
    sep = NORM_SEP;
324
  }
325
 
326
  // hacks to vary the sfx pitches
327
  if (sfx_id >= sfx_sawup
328
      && sfx_id <= sfx_sawhit)
329
  {
330
    pitch += 8 - (M_Random()&15);
331
 
332
    if (pitch<0)
333
      pitch = 0;
334
    else if (pitch>255)
335
      pitch = 255;
336
  }
337
  else if (sfx_id != sfx_itemup
338
	   && sfx_id != sfx_tink)
339
  {
340
    pitch += 16 - (M_Random()&31);
341
 
342
    if (pitch<0)
343
      pitch = 0;
344
    else if (pitch>255)
345
      pitch = 255;
346
  }
347
 
348
  // kill old sound
349
  S_StopSound(origin);
350
 
351
  // try to find a channel
352
  cnum = S_getChannel(origin, sfx);
353
 
354
  if (cnum<0)
355
    return;
356
 
357
  //
358
  // This is supposed to handle the loading/caching.
359
  // For some odd reason, the caching is done nearly
360
  //  each time the sound is needed?
361
  //
362
 
363
  // get lumpnum if necessary
364
  if (sfx->lumpnum < 0)
365
    sfx->lumpnum = I_GetSfxLumpNum(sfx);
366
 
367
#ifndef SNDSRV
368
  // cache data if necessary
369
  if (!sfx->data)
370
  {
371
    fprintf( stderr,
372
	     "S_StartSoundAtVolume: 16bit and not pre-cached - wtf?\n");
373
 
374
    // DOS remains, 8bit handling
375
    //sfx->data = (void *) W_CacheLumpNum(sfx->lumpnum, PU_MUSIC);
376
    // fprintf( stderr,
377
    //	     "S_StartSoundAtVolume: loading %d (lump %d) : 0x%x\n",
378
    //       sfx_id, sfx->lumpnum, (int)sfx->data );
379
 
380
  }
381
#endif
382
 
383
  // increase the usefulness
384
  if (sfx->usefulness++ < 0)
385
    sfx->usefulness = 1;
386
 
387
  // Assigns the handle to one of the channels in the
388
  //  mix/output buffer.
389
  channels[cnum].handle = I_StartSound(sfx_id,
390
				       /*sfx->data,*/
391
				       volume,
392
				       sep,
393
				       pitch,
394
				       priority);
395
}
396
 
397
void
398
S_StartSound
399
( void*		origin,
400
  int		sfx_id )
401
{
402
#ifdef SAWDEBUG
403
    // if (sfx_id == sfx_sawful)
404
    // sfx_id = sfx_itemup;
405
#endif
406
 
407
    S_StartSoundAtVolume(origin, sfx_id, snd_SfxVolume);
408
 
409
 
410
    // UNUSED. We had problems, had we not?
411
#ifdef SAWDEBUG
412
{
413
    int i;
414
    int n;
415
 
416
    static mobj_t*      last_saw_origins[10] = {1,1,1,1,1,1,1,1,1,1};
417
    static int		first_saw=0;
418
    static int		next_saw=0;
419
 
420
    if (sfx_id == sfx_sawidl
421
	|| sfx_id == sfx_sawful
422
	|| sfx_id == sfx_sawhit)
423
    {
424
	for (i=first_saw;i!=next_saw;i=(i+1)%10)
425
	    if (last_saw_origins[i] != origin)
426
		fprintf(stderr, "old origin 0x%lx != "
427
			"origin 0x%lx for sfx %d\n",
428
			last_saw_origins[i],
429
			origin,
430
			sfx_id);
431
 
432
	last_saw_origins[next_saw] = origin;
433
	next_saw = (next_saw + 1) % 10;
434
	if (next_saw == first_saw)
435
	    first_saw = (first_saw + 1) % 10;
436
 
437
	for (n=i=0; i
438
	{
439
	    if (channels[i].sfxinfo == &S_sfx[sfx_sawidl]
440
		|| channels[i].sfxinfo == &S_sfx[sfx_sawful]
441
		|| channels[i].sfxinfo == &S_sfx[sfx_sawhit]) n++;
442
	}
443
 
444
	if (n>1)
445
	{
446
	    for (i=0; i
447
	    {
448
		if (channels[i].sfxinfo == &S_sfx[sfx_sawidl]
449
		    || channels[i].sfxinfo == &S_sfx[sfx_sawful]
450
		    || channels[i].sfxinfo == &S_sfx[sfx_sawhit])
451
		{
452
		    fprintf(stderr,
453
			    "chn: sfxinfo=0x%lx, origin=0x%lx, "
454
			    "handle=%d\n",
455
			    channels[i].sfxinfo,
456
			    channels[i].origin,
457
			    channels[i].handle);
458
		}
459
	    }
460
	    fprintf(stderr, "\n");
461
	}
462
    }
463
}
464
#endif
465
 
466
}
467
 
468
 
469
 
470
 
471
void S_StopSound(void *origin)
472
{
473
 
474
    int cnum;
475
 
476
    for (cnum=0 ; cnum
477
    {
478
	if (channels[cnum].sfxinfo && channels[cnum].origin == origin)
479
	{
480
	    S_StopChannel(cnum);
481
	    break;
482
	}
483
    }
484
}
485
 
486
 
487
 
488
 
489
 
490
 
491
 
492
 
493
 
494
//
495
// Stop and resume music, during game PAUSE.
496
//
497
void S_PauseSound(void)
498
{
499
    if (mus_playing && !mus_paused)
500
    {
501
	I_PauseSong(mus_playing->handle);
502
	mus_paused = true;
503
    }
504
}
505
 
506
void S_ResumeSound(void)
507
{
508
    if (mus_playing && mus_paused)
509
    {
510
	I_ResumeSong(mus_playing->handle);
511
	mus_paused = false;
512
    }
513
}
514
 
515
 
516
//
517
// Updates music & sounds
518
//
519
void S_UpdateSounds(void* listener_p)
520
{
521
    int		audible;
522
    int		cnum;
523
    int		volume;
524
    int		sep;
525
    int		pitch;
526
    sfxinfo_t*	sfx;
527
    channel_t*	c;
528
 
529
    mobj_t*	listener = (mobj_t*)listener_p;
530
 
531
 
532
 
533
    // Clean up unused data.
534
    // This is currently not done for 16bit (sounds cached static).
535
    // DOS 8bit remains.
536
    /*if (gametic > nextcleanup)
537
    {
538
	for (i=1 ; i
539
	{
540
	    if (S_sfx[i].usefulness < 1
541
		&& S_sfx[i].usefulness > -1)
542
	    {
543
		if (--S_sfx[i].usefulness == -1)
544
		{
545
		    Z_ChangeTag(S_sfx[i].data, PU_CACHE);
546
		    S_sfx[i].data = 0;
547
		}
548
	    }
549
	}
550
	nextcleanup = gametic + 15;
551
    }*/
552
 
553
    for (cnum=0 ; cnum
554
    {
555
	c = &channels[cnum];
556
	sfx = c->sfxinfo;
557
 
558
	if (c->sfxinfo)
559
	{
560
	    if (I_SoundIsPlaying(c->handle))
561
	    {
562
		// initialize parameters
563
		volume = snd_SfxVolume;
564
		pitch = NORM_PITCH;
565
		sep = NORM_SEP;
566
 
567
		if (sfx->link)
568
		{
569
		    pitch = sfx->pitch;
570
		    volume += sfx->volume;
571
		    if (volume < 1)
572
		    {
573
			S_StopChannel(cnum);
574
			continue;
575
		    }
576
		    else if (volume > snd_SfxVolume)
577
		    {
578
			volume = snd_SfxVolume;
579
		    }
580
		}
581
 
582
		// check non-local sounds for distance clipping
583
		//  or modify their params
584
		if (c->origin && listener_p != c->origin)
585
		{
586
		    audible = S_AdjustSoundParams(listener,
587
						  c->origin,
588
						  &volume,
589
						  &sep,
590
						  &pitch);
591
 
592
		    if (!audible)
593
		    {
594
			S_StopChannel(cnum);
595
		    }
596
		    else
597
			I_UpdateSoundParams(c->handle, volume, sep, pitch);
598
		}
599
	    }
600
	    else
601
	    {
602
		// if channel is allocated but sound has stopped,
603
		//  free it
604
		S_StopChannel(cnum);
605
	    }
606
	}
607
    }
608
    // kill music if it is a single-play && finished
609
    // if (	mus_playing
610
    //      && !I_QrySongPlaying(mus_playing->handle)
611
    //      && !mus_paused )
612
    // S_StopMusic();
613
}
614
 
615
 
616
void S_SetMusicVolume(int volume)
617
{
618
    if (volume < 0 || volume > 127)
619
    {
620
	I_Error("Attempt to set music volume at %d",
621
		volume);
622
    }
623
 
624
    I_SetMusicVolume(127);
625
    I_SetMusicVolume(volume);
626
    snd_MusicVolume = volume;
627
}
628
 
629
 
630
 
631
void S_SetSfxVolume(int volume)
632
{
633
 
634
    if (volume < 0 || volume > 127)
635
	I_Error("Attempt to set sfx volume at %d", volume);
636
 
637
    snd_SfxVolume = volume;
638
 
639
}
640
 
641
//
642
// Starts some music with the music id found in sounds.h.
643
//
644
void S_StartMusic(int m_id)
645
{
646
    S_ChangeMusic(m_id, false);
647
}
648
 
649
void
650
S_ChangeMusic
651
( int			musicnum,
652
  int			looping )
653
{
654
    musicinfo_t*	music;
655
    char		namebuf[9];
656
 
657
    if ( (musicnum <= mus_None)
658
	 || (musicnum >= NUMMUSIC) )
659
    {
660
	I_Error("Bad music number %d", musicnum);
661
    }
662
    else
663
	music = &S_music[musicnum];
664
 
665
    if (mus_playing == music)
666
	return;
667
 
668
    // shutdown old music
669
    S_StopMusic();
670
 
671
    // get lumpnum if neccessary
672
    if (!music->lumpnum)
673
    {
674
	sprintf(namebuf, "d_%s", music->name);
675
	music->lumpnum = W_GetNumForName(namebuf);
676
    }
677
 
678
    // load & register it
679
    music->data = (void *) W_CacheLumpNum(music->lumpnum, PU_MUSIC);
680
    music->handle = I_RegisterSong(music->data);
681
 
682
    // play it
683
    I_PlaySong(music->handle, looping);
684
 
685
    mus_playing = music;
686
}
687
 
688
 
689
void S_StopMusic(void)
690
{
691
    if (mus_playing)
692
    {
693
	if (mus_paused)
694
	    I_ResumeSong(mus_playing->handle);
695
 
696
	I_StopSong(mus_playing->handle);
697
	I_UnRegisterSong(mus_playing->handle);
698
	Z_ChangeTag(mus_playing->data, PU_CACHE);
699
 
700
	mus_playing->data = 0;
701
	mus_playing = 0;
702
    }
703
}
704
 
705
 
706
 
707
 
708
void S_StopChannel(int cnum)
709
{
710
 
711
    int		i;
712
    channel_t*	c = &channels[cnum];
713
 
714
    if (c->sfxinfo)
715
    {
716
	// stop the sound playing
717
	if (I_SoundIsPlaying(c->handle))
718
	{
719
#ifdef SAWDEBUG
720
	    if (c->sfxinfo == &S_sfx[sfx_sawful])
721
		fprintf(stderr, "stopped\n");
722
#endif
723
	    I_StopSound(c->handle);
724
	}
725
 
726
	// check to see
727
	//  if other channels are playing the sound
728
	for (i=0 ; i
729
	{
730
	    if (cnum != i
731
		&& c->sfxinfo == channels[i].sfxinfo)
732
	    {
733
		break;
734
	    }
735
	}
736
 
737
	// degrade usefulness of sound data
738
	c->sfxinfo->usefulness--;
739
 
740
	c->sfxinfo = 0;
741
    }
742
}
743
 
744
 
745
 
746
//
747
// Changes volume, stereo-separation, and pitch variables
748
//  from the norm of a sound effect to be played.
749
// If the sound is not audible, returns a 0.
750
// Otherwise, modifies parameters and returns 1.
751
//
752
int
753
S_AdjustSoundParams
754
( mobj_t*	listener,
755
  mobj_t*	source,
756
  int*		vol,
757
  int*		sep,
758
  int*		pitch )
759
{
760
    fixed_t	approx_dist;
761
    fixed_t	adx;
762
    fixed_t	ady;
763
    angle_t	angle;
764
 
765
    // calculate the distance to sound origin
766
    //  and clip it if necessary
767
    adx = abs(listener->x - source->x);
768
    ady = abs(listener->y - source->y);
769
 
770
    // From _GG1_ p.428. Appox. eucledian distance fast.
771
    approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1);
772
 
773
    if (gamemap != 8
774
	&& approx_dist > S_CLIPPING_DIST)
775
    {
776
	return 0;
777
    }
778
 
779
    // angle of source to listener
780
    angle = R_PointToAngle2(listener->x,
781
			    listener->y,
782
			    source->x,
783
			    source->y);
784
 
785
    if (angle > listener->angle)
786
	angle = angle - listener->angle;
787
    else
788
	angle = angle + (0xffffffff - listener->angle);
789
 
790
    angle >>= ANGLETOFINESHIFT;
791
 
792
    // stereo separation
793
    *sep = 128 - (FixedMul(S_STEREO_SWING,finesine[angle])>>FRACBITS);
794
 
795
    // volume calculation
796
    if (approx_dist < S_CLOSE_DIST)
797
    {
798
	*vol = snd_SfxVolume;
799
    }
800
    else if (gamemap == 8)
801
    {
802
	if (approx_dist > S_CLIPPING_DIST)
803
	    approx_dist = S_CLIPPING_DIST;
804
 
805
	*vol = 15+ ((snd_SfxVolume-15)
806
		    *((S_CLIPPING_DIST - approx_dist)>>FRACBITS))
807
	    / S_ATTENUATOR;
808
    }
809
    else
810
    {
811
	// distance effect
812
	*vol = (snd_SfxVolume
813
		* ((S_CLIPPING_DIST - approx_dist)>>FRACBITS))
814
	    / S_ATTENUATOR;
815
    }
816
 
817
    return (*vol > 0);
818
}
819
 
820
 
821
 
822
 
823
//
824
// S_getChannel :
825
//   If none available, return -1.  Otherwise channel #.
826
//
827
int
828
S_getChannel
829
( void*		origin,
830
  sfxinfo_t*	sfxinfo )
831
{
832
    // channel number to use
833
    int		cnum;
834
 
835
    channel_t*	c;
836
 
837
    // Find an open channel
838
    for (cnum=0 ; cnum
839
    {
840
	if (!channels[cnum].sfxinfo)
841
	    break;
842
	else if (origin &&  channels[cnum].origin ==  origin)
843
	{
844
	    S_StopChannel(cnum);
845
	    break;
846
	}
847
    }
848
 
849
    // None available
850
    if (cnum == numChannels)
851
    {
852
	// Look for lower priority
853
	for (cnum=0 ; cnum
854
	    if (channels[cnum].sfxinfo->priority >= sfxinfo->priority) break;
855
 
856
	if (cnum == numChannels)
857
	{
858
	    // FUCK!  No lower priority.  Sorry, Charlie.
859
	    return -1;
860
	}
861
	else
862
	{
863
	    // Otherwise, kick out lower priority.
864
	    S_StopChannel(cnum);
865
	}
866
    }
867
 
868
    c = &channels[cnum];
869
 
870
    // channel is decided to be cnum.
871
    c->sfxinfo = sfxinfo;
872
    c->origin = origin;
873
 
874
    return cnum;
875
}
876
 
877
 
878
 
879