Metropoli BBS
VIEWER: demo2.c MODE: TEXT (ASCII)
/*
	DEMO2.C, Low level demonstration program.

	Copyright Ad Lib Inc, 1988.

	17-Oct-88, Dale Glowinski, Marc Savary, Ad Lib Inc.

	This is a demonstration program which shows an alternate method for
	generating sound.  Its general strategy is to start a sound, and then
	adjust it for volume or frequency at regular intervals.  This eliminates
	the need to use a music (.ROL) file which would be played as a piece
	of music.  (However, before coding a sound effect, it is a good idea
	to develop an approximation of the desired sound using the
	Visual Composer.)  This method is only useful for sounds which are
	repetitive or continuous.

	The program uses several routines in ADLIB.C, which is slightly
	different from the original version, but only in that several "static"
	statements have been removed to make several lower level routines
	available to this module.

	Compiled with Microsoft C, V5.0, as follows:
	   cl -AS -Zi -Ox -Gs demo2.c adlib.obj

	Some of the programming was done in a rather arbitrary fashion in order
	to save time and to keep the code as simple as possible:
	- Volumes are set to a value which was based on whatever sounded best.
	  Depending on what you wish to do, variables and passing values might
	  be in order.  Ditto for pitches.
	- A simple counter was used to create the timing, so that the speed
	  of the sounds produced by the executable file will vary with the
	  speed of the processor.  Certainly, one would have to use the real
	  timer in a real application.  (The timing counter can be changed by
	  using the "/t" execution option.)

*/


#include  <stdlib.h>
#include  <fcntl.h>
#include  <sys\types.h>
#include  <sys\stat.h>
#include  <io.h>
#include  <string.h>

#include  "adlib.h"

/* synthesizer modes */
#define  MELODIC     0
#define  PERCUSSIVE  1
#define  MODE        MELODIC

/* computer keyboard scan codes */
#define  LEFT        75
#define  RIGHT       77
#define  UP          72
#define  DOWN        80
#define  PAGEUP      73
#define  PAGEDOWN    81

/* flags used for Set_Instrument() */
#define  RELEASE     1
#define  NO_RELEASE  0

#if (MODE == PERCUSSIVE)
#define  NR_VOICES   11
#else
#define  NR_VOICES   9
#endif

#define  SIREN       0
#define  COPTER      1
#define  PLANEa      2
#define  PLANEb      3
#define  BOMBa       4
#define  BOMBb       5
#define  BOMBc       6
#define  BOMBd       7
#define  JETa        8
#define  JETb        9
#define  JETc        10
#define  JETd        11
#define  JETe        12
#define  FAKE_ID     13
#define  NR_SOUNDS   14

/* These are declared in ADLIB.C */
extern char slotVoice [][2];
extern char slotPerc  [][2];
extern NoteOff (int);
extern NoteOn  (int);

/* Timbre information for each sound */
unsigned char op [NR_SOUNDS][2][30];

/* These arrays contain information for each sound */
unsigned int fnum [NR_SOUNDS];
int octave [NR_SOUNDS];
int volume [NR_SOUNDS];
int myVoice [NR_SOUNDS];        /* voice number being used */

int voices [NR_VOICES];         /* used to allocate voices */

int mode;                       /* card mode */
int start_time = 100;
int timing = 100;

/* These routines are redefined to clarify their appearance in the code. */
#define  SetVoiceVolume(x,y)    SetVoiceVolume(myVoice[x],y)
#define  NoteOn(x)              NoteOn(myVoice[x]);
#define  NoteOff(x)             NoteOff(myVoice[x]);


main( argc, argv)
   int     argc;
   char    *argv[];
{
   int i;
   extern SoundColdInit();

   for ( i = 1, argv++; i < argc; i++, argv++)
   {
	 strupr ( *argv);
	 if ( ! strncmp( "/T", *argv, 2)) start_time = atoi (*argv + 2);
   }

   Instrument_Init ();
   Demo_Init ();

   Choose_Sound ();   /* user interface routine */

   Stifle ();
   SoundColdInit (0x388);
}


/*-----------------------------------------------------------------------*/
float frq_low [12] = {16.352, 17.324, 18.354, 19.445, 20.601, 21.826,
					  23.124, 24.499, 25.956, 27.500, 29.135, 30.867};

unsigned int freq_nums [12];

/*
frq_low is a table of frequencies for the first 12 pitches.  If Note_A is
one octave higher than Note_B, then the frequency of Note_B is twice that
of Note_A.  We can then use this relationship to find the frequency for any
note by multiplying the value in frq_low (pitch % 12) by a power of 2, where
the power of two is the octave (b = pitch / 12 - 1).  This can be expressed
as follows:
	frequency = frq_low [pitch % 12] * (2 ** b)

freq_nums is a table of f-numbers given a pitch (modulo 12).  The card uses
the f-number and the octave bits to determine the pitch it will output.
Note that the f-numbers preserve the same power of 2 relationship between
octaves as for frequency.

The f-numbers can be calculated using a transformed version of the formula
on page 47 of the programmer's manual:
   1.  fnum = freq * (2 ** 20-b) / 50 kHz
   2.  fnum = freq * (2 ** 20) * (2 ** -b) / 50000      (from 1)
   3.  fnum = freq * 20.971 * (2 ** -b)                 (from 2)
   4.  freq = frq_low [pitch % 12] * (2 ** b)
   5.  fnum = frq_low [pitch % 12] * 20.971             (from 3 & 4)
(b is the octave less 1 so that 0 <= b <= 7.)
Thus, the f-numbers are the integer results of multiplying the values in
frq_low by 20.971.

This gives a range of 343 to 685 for the f-numbers.  However, the f-number
register is 9 bits and can hold an unsigned value of 0 to 1023.  The range
outside of 343-685 can be used, but it should be noted that in the lower
ranges, a small change in f-number will yield a larger change in output
frequency.  By always using the range 343-685, one will have the same
incremental degree of control between pitches regardless of the octave.
*/


/*-----------------------------------------------------------------------
This sets up the table of f-numbers for a given pitch.  This actually could
be calculated beforehand and the array initialized to the calculated values,
but it is done here for illustrative purposes.  */

SetUp_FNums ()
{
   int n;
   float f;
   for (n=0; n < 12; n++) {
	  f = frq_low [n] * 20.971;
	  freq_nums [n] = (unsigned int) f;

	  /* Round number up if necessary */
	  f -= (float) freq_nums [n];
	  if (f > 0.5) freq_nums [n]++;
   }
}


/*-----------------------------------------------------------------------
This does a note-on for the passed f-num.  If the note is already on,
only the frequency of the note will change.  */

Out_Freq (sound_ID, fNbr, octave)
	unsigned int sound_ID;
	unsigned int fNbr;              /* f-number for card */
	unsigned int octave;
{
	unsigned int t1;

	octave -= 1;
	while (fNbr > 1023) {
	   fNbr >>= 1;
	   octave++;
	}
	while (fNbr < freq_nums [0]) {
	   fNbr <<= 1;
	   octave--;
	}

	SndOutput (0xA0 + myVoice [sound_ID], fNbr);

	t1 = 0x20 | (octave << 2) | (0x3 & (fNbr >> 8));
	SndOutput (0xB0 + myVoice [sound_ID], t1);
	return (octave);
}


/*-----------------------------------------------------------------------
Initialize the card and set up several variables. */

Demo_Init ()
{
   extern SoundColdInit();
   int n;
   timing = start_time;

   n = SoundColdInit (0x388);
   if (!n) {
	  printf ("\nSound card not found.\n");
	  exit (0);
   }
   mode = MODE;
   SetMode (mode);

   SetUp_FNums ();
   for (n=0; n < NR_VOICES; n++) voices [n] = -1;
   for (n=0; n < NR_SOUNDS; n++) {
	  myVoice [n] = -1;
	  volume [n] = 0x50;
	  fnum [n] = 0;
   }
}


/*-----------------------------------------------------------------------
Given a pitch, return its f-number. */

#define  Pitch_to_Fnum(x)  (freq_nums [x % 12])

/*
unsigned int Pitch_to_Fnum (pitch)
   int pitch;
{
   return (freq_nums [pitch % 12]);
}
*/


/*-----------------------------------------------------------------------
If the release values have been set to zero, a sound will continue to be
heard even if a note-off is done.  This routine resets the release values
for every voice to an arbitrary value in order to shut them off. */

Stifle_Voice (sound_ID)
   int sound_ID;
{
   int voice;
   voice = myVoice [sound_ID];
   if (voice == -1) return;

   SetASlotParam (slotVoice [voice][0], prmRelease, 10);
   SetASlotParam (slotVoice [voice][1], prmRelease, 10);
   NoteOff (sound_ID);
   Free_Voice (sound_ID);
}

/* Silence all voices. */
Stifle ()
{
   int n;
   for (n=0; n < NR_SOUNDS; n++) Stifle_Voice (n);
}


/*-----------------------------------------------------------------------
Finds an available voice and sets the approriate entry in the myVoice table.
Returns 0 if no more voices available else returns 1.*/

Alloc_Voice (sound_ID)
   int sound_ID;
{
   register int n;
   for (n=0; n < NR_VOICES && voices [n] != -1; n++);
   if (n >= NR_VOICES) return (0);
   voices [n] = sound_ID;
   myVoice [sound_ID] = n;
   return (1);
}

/* De-allocate the voice which was allocated in the above routine. */
Free_Voice (sound_ID)
   int sound_ID;
{
   register int n;
   n = myVoice [sound_ID];
   if (n == -1) return;
   voices [n] = -1;
   myVoice [sound_ID] = -1;
}


/*-----------------------------------------------------------------------
Read the .INS file into the appropriate operator buffers. */

Read_Ins_File (name, sound_ID)
   char *name;
   int sound_ID;
{
   unsigned char *op0, *op1;
   int file, n, ok;
   unsigned char temp [20];

   op0 = op [sound_ID][0];
   op1 = op [sound_ID][1];

   file = open (name, O_RDONLY & O_RAW);
   if (file < 0) {
	  printf ("Can't open %s\n", name);
	  return (0);
   }
   read (file, temp, 2);
   for (n=0; n < 13; n++) read (file, &op0 [n], 2);
   for (n=0; n < 13; n++) read (file, &op1 [n], 2);
   ok = read (file, temp, 20);
   if (ok) {
	  read (file, &op0 [prmWaveSel], 2);
	  read (file, &op1 [prmWaveSel], 2);
   }
   close (file);
   return (1);
}


/*-----------------------------------------------------------------------
Given the timbre parameters, initialize a voice.  NOTE: The real voice
number is passed here, not the sound's ID code. */

SetUp_Timbre (voice, op0, op1)
   int voice;
   unsigned char *op0, *op1;
{
   SetCharSlotParam (slotVoice [voice][0], op0, op0 [prmWaveSel]);
   if (mode == MELODIC || voice <= BD)
	  SetCharSlotParam (slotVoice [voice][1], op1, op1 [prmWaveSel]);
}


/*-----------------------------------------------------------------------
Set up an instrument for the passed voice.  Resets the release values
according to the passed flag. This routine is similar to SetVoiceTimbre()
in ADLIB.C.  */

Set_Instrument (sound_ID, rel_flag)
   int sound_ID, rel_flag;
{
   int ok, voice, rel_value;
   unsigned char *op0, *op1;

   ok = Alloc_Voice (sound_ID);
   if (!ok) {
	  printf ("No more voices available\n");
	  return (0);
   }
   voice = myVoice [sound_ID];

   op0 = op [sound_ID][0];
   op1 = op [sound_ID][1];

   /* Setting the release values to zero has the effect of making the note
	  play forever. */
   if (rel_flag) rel_value = 1;
	  else rel_value = 0;
   op0 [prmRelease] = rel_value;
   op1 [prmRelease] = rel_value;

   SetUp_Timbre (voice, op0, op1);

   SetVoiceVolume (sound_ID, 0x60);
   return (1);
}


/*-----------------------------------------------------------------------
Read in all of the required .INS files. */

Instrument_Init ()
{
   Read_Ins_File ("instr\\jet11a.ins", JETa);
   Read_Ins_File ("instr\\jet11b.ins", JETb);
   Read_Ins_File ("instr\\oboe1.ins", JETc);
   Read_Ins_File ("instr\\siren2a.ins", JETd);
   Read_Ins_File ("instr\\siren2a.ins", JETe);

   Read_Ins_File ("instr\\flute.ins", BOMBa);
   Read_Ins_File ("instr\\plane1.ins", BOMBb);
   Read_Ins_File ("instr\\plane1.ins", BOMBc);
   Read_Ins_File ("instr\\plane1.ins", BOMBd);

   Read_Ins_File ("instr\\plane3.ins", PLANEa);
   Read_Ins_File ("instr\\plane3.ins", PLANEb);

   Read_Ins_File ("instr\\siren1.ins", SIREN);
   Read_Ins_File ("instr\\helico3.ins", COPTER);
}


/*------------------------------------------------------------------------
This creates a delay in a rather simple manner in order to create a fake
timer.  In a real application, a scheduling or a timer routine would have
call the Internal_Driver() routine at the appropriate moment. */

delay (n)
   int n;
{
   int i, j = 5;
   for (i=0; i < (n * 100); i++) j %= 23;
}


/*********************************** Jet *********************************/

Drive_Jet ()
{
   static int flag = 1;
   static int n = -0x40;
   if (!fnum [JETa]) return;

   if (flag) {
	  /* raise the volume and pitch */
	  SetVoiceVolume (JETa, 0x50+n);
	  SetVoiceVolume (JETb, 0x60+n);
	  SetVoiceVolume (JETc, 0x30+(n/2));
	  SetVoiceVolume (JETd, 0x60+n);
	  SetVoiceVolume (JETe, 0x45+n);
	  Out_Freq (JETa, fnum [JETa]+n, octave [JETa]);
	  Out_Freq (JETb, fnum [JETb]+n, octave [JETb]);
	  Out_Freq (JETd, fnum [JETd]+n, octave [JETd]);
	  Out_Freq (JETe, fnum [JETe]+n, octave [JETe]);
	  n++;
	  if (n >= 0x1f) flag = 0;
   }
   else /* flag is 0 */ {
	  /* lower the volume and pitch */
	  SetVoiceVolume (JETa, 0x50+n);
	  SetVoiceVolume (JETb, 0x60+n);
	  SetVoiceVolume (JETc, 0x30+(n/2));
	  SetVoiceVolume (JETd, 0x60+n);
	  SetVoiceVolume (JETe, 0x45+n);
	  Out_Freq (JETa, fnum [JETa]+n, octave [JETa]);
	  Out_Freq (JETb, fnum [JETb]+n, octave [JETb]);
	  Out_Freq (JETd, fnum [JETd]+n, octave [JETd]);
	  Out_Freq (JETe, fnum [JETe]+n, octave [JETe]);
	  n--;
	  if (n <= -0x40) {
		 /* "shut off" the jet: set variables to zero */
		 flag = 1;
		 fnum [JETa] = 0;
		 fnum [JETb] = 0;
		 fnum [JETc] = 0;
		 fnum [JETd] = 0;
		 fnum [JETe] = 0;
		 Stifle_Voice (JETa);
		 Stifle_Voice (JETb);
		 Stifle_Voice (JETc);
		 Stifle_Voice (JETd);
		 Stifle_Voice (JETe);
	  }
   }
}


/*-----------------------------------------------------------------------
This needs five voices to get a good effect.  JET11A.INS and JET11B.INS
alone have been described as being like a vacuum cleaner.  But if ever
you need a vacuum cleaner sound... */

Jet ()
{
   int n, ok;
   if (fnum [JETa]) return;

   Set_Instrument (JETa, NO_RELEASE);
   Set_Instrument (JETb, NO_RELEASE);
   Set_Instrument (JETc, NO_RELEASE);
   Set_Instrument (JETd, NO_RELEASE);
   ok = Set_Instrument (JETe, NO_RELEASE);

   if (!ok) {
	  for (n=JETa; n < JETe; n++) Free_Voice (n);
	  return;
   }

   fnum [JETa] = Pitch_to_Fnum (52);
   fnum [JETb] = Pitch_to_Fnum (88);
   fnum [JETc] = Pitch_to_Fnum (16);
   fnum [JETd] = Pitch_to_Fnum (100);
   fnum [JETe] = Pitch_to_Fnum (101);
   octave [JETa] = 52 / 12;
   octave [JETb] = 88 / 12;
   octave [JETc] = 1;
   octave [JETd] = 100 / 12;
   octave [JETe] = 101 / 12;

   /* The pitch of JETc does not vary, so it turned on on here.  The other
	  voices are output in Drive_Jet(). */
   SetVoiceVolume (JETc, 0);
   Out_Freq (JETc, fnum [JETc], octave [JETc]);
   Drive_Jet ();

   printf ("Jet started\n");
}


/*********************************** Bomb ********************************/

unsigned bomb_low;
int wait_time;

Drive_Bomb ()
{
   if (!fnum [BOMBa]) return;

   if (fnum [BOMBa] > bomb_low) {
	  /* Decrease pitch of bomb falling sound */
	  fnum [BOMBa] -= 4;
	  Out_Freq (BOMBa, fnum [BOMBa], octave [BOMBa]);
   }
   else {
	  if (!wait_time) {
		 /* Shut off the flute (bomb falling sound) */
		 Stifle_Voice (BOMBa);

		 /* Turn on the exploding sound. The more voices which can be used
			here, the better the effect, although the minimum is two voices.
			For each voice added, set the pitch a half-tone above the
			previous voice. */
		 Out_Freq (BOMBb, fnum [BOMBb], octave [BOMBb]);
		 Out_Freq (BOMBc, fnum [BOMBc], octave [BOMBc]);
		 Out_Freq (BOMBd, fnum [BOMBd], octave [BOMBd]);
		 wait_time = 160;
	  }
	  else {  /* wait to let sound fade, otherwise it may get cut off */
		 wait_time--;
		 if (!wait_time) {  /* wait has expired, free voices */
			timing += 5;
			fnum [BOMBa] = 0;
			Stifle_Voice (BOMBb);
			Stifle_Voice (BOMBc);
			Stifle_Voice (BOMBd);
		 }
	  }
   }
}

/*-----------------------------------------------------------------------*/

Bomb ()
{
   int new_oct, ok;
   unsigned char c;

   if (!fnum [BOMBa]) {
	  Set_Instrument (BOMBb, RELEASE);
	  Set_Instrument (BOMBc, RELEASE);
	  Set_Instrument (BOMBd, RELEASE);
	  ok = Set_Instrument (BOMBa, NO_RELEASE);
	  if (!ok) {
		 Free_Voice (BOMBb);
		 Free_Voice (BOMBc);
		 Free_Voice (BOMBd);
		 return;
	  }

	  timing -= 5;
	  fnum [BOMBb] = Pitch_to_Fnum (12);
	  fnum [BOMBc] = Pitch_to_Fnum (13);
	  fnum [BOMBd] = Pitch_to_Fnum (14);
	  octave [BOMBb] = 1;
	  octave [BOMBc] = 1;
	  octave [BOMBd] = 1;
	  SetVoiceVolume (BOMBb, 0x7f);
	  SetVoiceVolume (BOMBc, 0x7f);
	  SetVoiceVolume (BOMBd, 0x7f);
	  SetVoiceVolume (BOMBa, volume [BOMBa]);
   }

   octave [BOMBa] = 95 / 12;
   fnum [BOMBa] = Pitch_to_Fnum (95);
   bomb_low = fnum [BOMBa] >> 2;
   wait_time = 0;

   Out_Freq (BOMBa, fnum [BOMBa], octave [BOMBa]);

   /* Read input from keyboard */
   printf ("\n Bomb: press spacebar to exit, 'S' to silence ");
   do {
	  while (! kbhit ())
		 Internal_Driver ();
	  c = getch ();
	  if (c) {
		 c = toupper (c);
		 if (c == 'S') {
			timing += 5;
			Stifle_Voice (BOMBa);
			Stifle_Voice (BOMBb);
			Stifle_Voice (BOMBc);
			Stifle_Voice (BOMBd);
			fnum [BOMBa] = 0;
			c = ' ';
		 }
	  }
	  else {
		 c = getch ();
	  }
   }
   while (c != ' ');
}


/********************************* Airplane ******************************/

Drive_Airplane ()
{
   /* Here, one could adjust the pitch up and down slightly in order to
	  give the sound a more pronounced whine. */
}


/*-----------------------------------------------------------------------*/

Airplane ()
{
   static int inc = 6;
   static pitch = 30;
   int n, new_oct, ok;
   unsigned char c;

   if (!fnum [PLANEb]) {
	  ok = Set_Instrument (PLANEa,  NO_RELEASE);
	  if (!ok) return;
	  ok = Set_Instrument (PLANEb,  NO_RELEASE);
	  if (!ok) {
		 Free_Voice (PLANEa);
		 return;
	  }

	  timing -= 5;
	  volume [PLANEb] = volume [PLANEa] * 3 / 4;
	  octave [PLANEa] = octave [PLANEb] = pitch / 12;
	  fnum [PLANEa] = Pitch_to_Fnum (pitch);
	  fnum [PLANEb] = fnum [PLANEa] + 2;

	  SetVoiceVolume (PLANEa, volume [PLANEa]);
	  SetVoiceVolume (PLANEb, volume [PLANEb]);
	  Out_Freq (PLANEa, fnum [PLANEa], octave [PLANEa]);
   }

   /* Read keyboard input */
   printf ("\nAirplane: press spacebar to exit, 'S' to silence ");
   do {
	  while (! kbhit ())
		 Internal_Driver ();
	  c = getch ();
	  if (c) {
		 c = toupper (c);
		 if (c == 'S') {
			timing += 5;
			Stifle_Voice (PLANEa);
			Stifle_Voice (PLANEb);
			fnum [PLANEa] = 0;
			fnum [PLANEb] = 0;
			c = ' ';
		 }
	  }
	  else {
		 c = getch ();
		 switch (c) {
			case LEFT:
				/* Decrease volume */
				if (volume [PLANEa] == 0) break;
				volume [PLANEa] -= 2;
				volume [PLANEb] = volume [PLANEa] * 3 / 4;
				SetVoiceVolume (PLANEa, volume [PLANEa]);
				SetVoiceVolume (PLANEb, volume [PLANEb]);
				break;
			case RIGHT:
				/* Increase volume */
				if (volume [PLANEa] >= 0x7e) break;
				volume [PLANEa] += 2;
				volume [PLANEb] = volume [PLANEa] * 3 / 4;
				SetVoiceVolume (PLANEa, volume [PLANEa]);
				SetVoiceVolume (PLANEb, volume [PLANEb]);
				break;
			case UP:
				/* Increase pitch */
				fnum [PLANEa] += 2;
				Out_Freq (PLANEa, fnum [PLANEa], octave [PLANEa]);
				fnum [PLANEb] = fnum [PLANEa] + 1;
				break;
			case DOWN:
				/* Decrease pitch */
				if (fnum [PLANEa] <= 18) break;
				fnum [PLANEa] -= 2;
				Out_Freq (PLANEa, fnum [PLANEa], octave [PLANEa]);
				fnum [PLANEb] = fnum [PLANEa] + 1;
				break;
		 }
	  }
   }
   while (c != ' ');
}


/*********************************** Siren *******************************/

unsigned siren_high;
unsigned siren_low;

Drive_Siren () {
   static flag = 1;
   if (!fnum [SIREN]) return;

   if (flag) {
	  /* increase pitch */
	  fnum [SIREN] += 4;
	  Out_Freq (SIREN, fnum [SIREN], octave [SIREN]);
	  if (fnum [SIREN] >= siren_high) flag = 0;
   }
   else {
	  /* decrease pitch */
	  fnum [SIREN] -= 4;
	  Out_Freq (SIREN, fnum [SIREN], octave [SIREN]);
	  if (fnum [SIREN] <= siren_low) flag = 1;
   }
}


/*-----------------------------------------------------------------------*/

Siren ()
{
   int ok;
   unsigned char c;

   if (!fnum [SIREN]) {
	  ok = Set_Instrument (SIREN, NO_RELEASE);
	  if (!ok) return;
	  timing -= 5;
	  volume [SIREN] = 0x60;
	  octave [SIREN] = 67 / 12;
	  fnum [SIREN] = Pitch_to_Fnum (67) >> 1;
	  siren_low = Pitch_to_Fnum (66);
	  siren_high = Pitch_to_Fnum (71);

	  SetVoiceVolume (SIREN, volume [SIREN]);
	  Out_Freq (SIREN, fnum [SIREN], octave [SIREN]);
   }

   /* Read keyboard input */
   printf ("\nSiren: press spacebar to exit, 'S' to silence ");
   do {
	  while (! kbhit ())
		 Internal_Driver ();
	  c = getch ();
	  if (c) {
		 c = toupper (c);
		 if (c == 'S') {
			timing += 5;
			Stifle_Voice (SIREN);
			fnum [SIREN] = 0;
			c = ' ';
		 }
	  }
	  else {
		 c = getch ();
		 switch (c) {
			case LEFT:
				/* decrease volume */
				if (volume [SIREN] == 0) break;
				volume [SIREN] -= 2;
				SetVoiceVolume (SIREN, volume [SIREN]);
				break;
			case RIGHT:
				/* increase volume */
				if (volume [SIREN] == 0x7f) break;
				volume [SIREN] += 2;
				SetVoiceVolume (SIREN, volume [SIREN]);
				break;
			case UP:
				/* increase pitch */
				fnum [SIREN] += 8;
				Out_Freq (SIREN, fnum [SIREN], octave [SIREN]);
				break;
			case DOWN:
				/* decrease pitch */
				fnum [SIREN] -= 8;
				Out_Freq (SIREN, fnum [SIREN], octave [SIREN]);
				break;
		 }
	  }
   }
   while (c != ' ');
}


/******************************** Helicopter *****************************/

int heli_wait;

Drive_Helicopter ()
{
   static int cntr = 0;
   if (!fnum [COPTER]) return;

   cntr++;
   if (cntr >= heli_wait) {
	  NoteOff (COPTER);
	  Out_Freq (COPTER, fnum [COPTER], octave [COPTER]);
	  cntr = 0;
   }
}


/*-----------------------------------------------------------------------*/

Helicopter ()
{
   int pitch, ok;
   unsigned char c;

   if (!fnum [COPTER]) {
	  ok = Set_Instrument (COPTER, RELEASE);
	  if (!ok) return;
	  timing -= 5;
	  heli_wait = 5;
	  pitch = 39;
	  volume [COPTER] = 0x70;
	  octave [COPTER] = pitch / 12;
	  fnum [COPTER] = Pitch_to_Fnum (pitch);

	  SetVoiceVolume (COPTER, volume [COPTER]);
	  Out_Freq (COPTER, fnum [COPTER], octave [COPTER]);
   }

   /* Read keyboard input */
   printf ("\nHelicopter: Pg Up - faster, Pg Dn - slower, spacebar to exit ");
   do {
	  while (! kbhit ()) Internal_Driver ();
	  c = getch ();
	  if (c) {
		 c = toupper (c);
		 if (c == 'S') {
			timing += 5;
			Stifle_Voice (COPTER);
			fnum [COPTER] = 0;
			c = ' ';
		 }
	  }
	  else {
		 c = getch ();
		 switch (c) {
			case LEFT:
				/* decrease volume */
				if (volume [COPTER] == 0) break;
				volume [COPTER] -= 2;
				SetVoiceVolume (COPTER, volume [COPTER]);
				break;
			case RIGHT:
				/* increase volume */
				if (volume [COPTER] == 0x7f) break;
				volume [COPTER] += 2;
				SetVoiceVolume (COPTER, volume [COPTER]);
				break;
			case UP:
				/* increase pitch */
				fnum [COPTER] += 2;
				break;
			case DOWN:
				/* decrease pitch */
				if (fnum [COPTER] > 0) fnum [COPTER] -= 2;
				break;
			case PAGEUP:
				/* increase frequency of note off/on */
				if (heli_wait > 0) heli_wait--;
				break;
			case PAGEDOWN:
				/* decrease frequency of note off/on */
				if (heli_wait < 0x7fff) heli_wait++;
				break;
		 }
	  }
   }
   while (c != ' ');
}


/************************************************************************
Routine which allows one to read in an instrument file and play with
the pitch and volume.  */

Test ()
{
#ifdef TEST
   char fname [40];
   int pitch, octave, volume;
   unsigned int fnum1;
   unsigned char c;

   printf ("\nFile name: ");
   scanf ("%s", fname);
   Read_Ins_File (fname, FAKE_ID);
   if (!Set_Instrument (FAKE_ID, NO_RELEASE)) return;
   printf ("Start pitch: ");
   scanf ("%d", &pitch);

   volume = 0x60;
   octave = pitch / 12;
   fnum1 = Pitch_to_Fnum (pitch);
   SetVoiceVolume (FAKE_ID, volume);
   Out_Freq (FAKE_ID, fnum1, octave);

   printf ("\nTest: press spacebar to stop ");
   do {
	  while (! kbhit ());
	  c = getch ();
	  if (!c) {
		 c = getch ();
		 switch (c) {
			case LEFT:
				/* decrease volume */
				if (volume == 0) break;
				volume -= 2;
				SetVoiceVolume (FAKE_ID, volume);
				break;
			case RIGHT:
				/* increase volume */
				if (volume == 0x7f) break;
				volume += 2;
				SetVoiceVolume (FAKE_ID, volume);
				break;
			case UP:
				/* increase pitch */
				fnum1 += 5;
				Out_Freq (FAKE_ID, fnum1, octave);
				break;
			case DOWN:
				/* decrease pitch */
				fnum1 -= 4;
				Out_Freq (FAKE_ID, fnum1, octave);
				break;
		 }
	  }
   }
   while (c != ' ');
   printf (" fnum=%d, octave=%d, vol=%d ", fnum1, octave, volume);
   Stifle ();
#endif
}


/************************************************************************
This routine must be called constantly (or at a constant interval) in order
to keep producing the sound effects.  In a real application, this routine or
a variation of it would be hooked up to the timer interrupt directly or via
another routine. */

Internal_Driver ()
{
   Drive_Siren ();
   Drive_Airplane ();
   Drive_Bomb ();
   Drive_Helicopter ();
   Drive_Jet ();
   delay (timing);
}


/*-----------------------------------------------------------------------
Main loop.  Displays menu and reads user's selection. */

Choose_Sound ()
{
   unsigned char c;
   printf (
	 "\nTo speed up or slow down, re-execute using /Txxx, where xxx is a"
	 "\nnumber.   xxx is currently %d.", timing);
   printf ("\n\nUse up and down arrows to change pitch.  Use left and\n"
		   "right arrows to change volume.");
   do {
	  printf ("\nChoose one:\n   A)irplane\n   B)omb\n   "
			  "H)elicopter\n   J)et\n   S)iren\n   R)eset all\n   Q)uit\n");
	  while (! kbhit ()) Internal_Driver ();
	  c = getch ();
	  if (!c) getch ();
	  else {
		 c = toupper (c);
		 switch (c) {
			case 'A': Airplane ();
					  break;
			case 'B': Bomb ();
					  break;
			case 'H': Helicopter ();
					  break;
			case 'J': Jet ();
					  break;
			case 'R': Stifle ();
					  Demo_Init ();
					  break;
			case 'S': Siren ();
					  break;
			case 'T': Test ();
					  break;
		 }
	  }
   }
   while (c != 'Q');
}


[ RETURN TO DIRECTORY ]