/* 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 #include #include #include #include #include #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'); }