File: dos\sound.c
1 /* sound functions,
2
3 To use sound in fractint there are three basic functions:
4 soundon(freq) starts a voice sounding with the given frequency (in Hz)
5 soundoff() releases a voice
6 mute() turns off all voices at once
7 polyhony controls how many voices are allowed to sound at once (up to nine)
8
9 for more gory detals see below:
10
11 RB 20/3/99 */
12
13
14
15 /* sound.c largely pinched from fmsample.c as distributed with the creative labs */
16 /* SDK.... see below.. RB */
17
18 /* -------------------------------------------------------------------------- */
19 /* */
20 /* (C) Copyright Creative Technology Ltd 1994-1996. All right reserved */
21 /* */
22 /* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY */
23 /* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE */
24 /* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR */
25 /* PURPOSE. */
26 /* */
27 /* You have a royalty-free right to use, modify, reproduce and */
28 /* distribute the Sample Files (and/or any modified version) in */
29 /* any way you find useful, provided that you agree that */
30 /* Creative has no warranty obligations or liability for any Sample Files. */
31 /* */
32 /*----------------------------------------------------------------------------*/
33
34 /*
35 * This program is not intended to explain all the aspects of FM sound
36 * generation on Sound Blaster cards. The chips are too complicated for
37 * that. This program is just to demonstrate how to generate a tone and
38 * control the left and right channels. For more information on the FM
39 * synthesizer chip, contact Yamaha.
40 *
41 * Here's a brief description of FM: Each sound is created by two operator
42 * cells (called "slots" in the Yamaha documentation), a modulator and a
43 * carrier. When FM synthesis was invented, the output value of the
44 * modulator affected the frequency of the carrier. In the Yamaha chips, the
45 * modulator output actually affects the phase of the carrier instead of
46 * frequency, but this has a similar effect.
47 *
48 * Normally the modulator and carrier would probably be connected in series
49 * for complex sounds. For this program, I wanted a pure sine wave, so I
50 * connected them in parallel and turned the modulator output down all the
51 * way and used just the carrier.
52 *
53 * Sound Blaster 1.0 - 2.0 cards have one OPL-2 FM synthesis chip at
54 * addresses 2x8 and 2x9 (base + 8 and base + 9). Sound Blaster Pro version
55 * 1 cards (CT-1330) achieve stereo FM with two OPL-2 chips, one for each
56 * speaker. The left channel FM chip is at addresses 2x0 and 2x1. The right
57 * is at 2x2 and 2x3. Addresses 2x8 and 2x9 address both chips
58 * simultaneously, thus maintaining compatibility with the monaural Sound
59 * Blaster cards. The OPL-2 contains 18 operator cells which make up the
60 * nine 2-operator channels. Since the CT-1330 SB Pro has two OPL-2 chips,
61 * it is therefore capable of generating 9 voices in each speaker.
62 *
63 * Sound Blaster Pro version 2 (CT-1600) and Sound Blaster 16 cards have one
64 * OPL-3 stereo FM chip at addresses 2x0 - 2x3. The OPL-3 is separated into
65 * two "banks." Ports 2x0 and 2x1 control bank 0, while 2x2 and 2x3 control
66 * bank 1. Each bank can generate nine 2-operator voices. However, when the
67 * OPL-3 is reset, it switches into OPL-2 mode. It must be put into OPL-3
68 * mode to use the voices in bank 1 or the stereo features. For stereo
69 * control, each channel may be sent to the left, the right, or both
70 * speakers, controlled by two bits in registers C0H - C8H.
71 *
72 * The FM chips are controlled through a set of registers. The following
73 * table shows how operator cells and channels are related, and the register
74 * offsets to use.
75 *
76 * FUNCTION MODULATOR- -CARRIER-- MODULATOR- -CARRIER-- MODULATOR- -CARRIER--
77 * OP CELL 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
78 * CHANNEL 1 2 3 1 2 3 4 5 6 4 5 6 7 8 9 7 8 9
79 * OFFSET 00 01 02 03 04 05 08 09 0A 0B 0C 0D 10 11 12 13 14 15
80 *static unsigned char fm_offset[9] = {0,1,2,8,9,10,16,17,18};
81 * An example will make the use of this table clearer: suppose you want to
82 * set the attenuation of both of the operators of channel 4. The KSL/TOTAL LEVEL
83 * registers (which set the attenuation) are 40H - 55H. The modulator for
84 * channel 4 is op cell 7, and the carrier for channel 4 is op cell 10. The
85 * offsets for the modulator and carrier cells are 08H and 0BH, respectively.
86 * Therefore, to set the attenuation of the modulator, you would output a
87 * value to register 40H + 08H == 48H, and to set the carrier's attenuation,
88 * you would output to register 40H + 0BH == 4BH.
89 *
90 * In this program, I used just channel 1, so the registers I used were 20H,
91 * 40H, 60H, etc., and 23H, 43H, 63H, etc.
92 *
93 * The frequencies of each channel are controlled with a frequency number and
94 * a multiplier. The modulator and carrier of a channel both get the same
95 * frequency number, but they may be given different multipliers. Frequency
96 * numbers are programmed in registers A0H - A8H (low 8 bits) and B0H - B8H
97 * (high 2 bits). Those registers control entire channels (2 operators), not
98 * individual operator cells. To turn a note on, the key-on bit in the
99 * appropriate channel register is set. Since these registers deal with
100 * channels, you do not use the offsets listed in the table above. Instead,
101 * add (channel-1) to A0H or B0H. For example, to turn channel 1 on,
102 * program the frequency number in registers A0H and B0H, and set the key-on
103 * bit to 1 in register B0H. For channel 3, use registers A2H and B2H.
104 *
105 * Bits 2 - 4 in registers B0H - B8H are the block (octave) number for the
106 * channel.
107 *
108 * Multipliers for each operator cell are programmed through registers 20H -
109 * 35H. The table below shows what multiple number to program into the
110 * register to get the desired multiplier. The multiple number goes into
111 * bits 0 - 3 in the register. Note that it's a bit strange at the end.
112 *
113 * multiple number multiplier multiple number multiplier
114 * 0 1/2 8 8
115 * 1 1 9 9
116 * 2 2 10 10
117 * 3 3 11 10
118 * 4 4 12 12
119 * 5 5 13 12
120 * 6 6 14 15
121 * 7 7 15 15
122 *
123 * This equation shows how to calculate the required frequency number (to
124 * program into registers A0H - A8H and B0H - B8H) to get the desired
125 * frequency:
126 * fn=(long)f * 1048576 / b / m /50000L
127 * where f is the frequency in Hz,
128 * b is the block (octave) number between 0 and 7 inclusive, and
129 * m is the multiple number between 0 and 15 inclusive.
130 */
131
132
133 #include <stdio.h>
134 #include <stdlib.h>
135 #include <conio.h>
136 #include <ctype.h>
137 #include <dos.h>
138 #include "port.h"
139 #include "prototyp.h"
140 #include "fractype.h"
141 #include "externs.h"
142 #include "helpdefs.h"
143
144 #define STEREO /* Define this for SBPro CT-1330 or later card. */
145 #define OPL3 /* Also define this for SBPro CT-1600 or later card. */
146 #define KEYON 0x20 /* 0010 0000 key-on bit in regs b0 - b8 */
147 #define LEFT 0x10
148 #define RIGHT 0x20
149 #define KEYOFF 0xDF /* 1101 1111 key-off */
150
151 /* These are offsets from the base I/O address. */
152 #define FM 8 /* SB (mono) ports (e.g. 228H and 229H) */
153 #define PROFM1 0 /* On CT-1330, this is left OPL-2. On CT-1600 and */
154 /* later cards, it's OPL-3 bank 0. */
155 #define PROFM2 2 /* On CT-1330, this is right OPL-2. On CT-1600 and */
156 /* later cards, it's OPL-3 bank 1. */
157
158 int menu2;
159 unsigned int IOport; /* Sound Blaster port address */
160 static int get_music_parms(void);
161 static int get_scale_map(void);
162
163 static int base16(char **str, unsigned *val)
164 /* Takes a double pointer to a string, interprets the characters as a
165 * base-16 number, and advances the pointer.
166 * Returns 0 if successful, 1 if not.
167 */
168 {
169 char c;
170 int digit;
171 *val = 0;
172
173 while ( **str != ' ') {
174 c = (char)toupper(**str);
175 if (c >= '0' && c <= '9')
176 digit = c - '0';
177 else if (c >= 'A' && c <= 'F')
178 digit = c - 'A' + 10;
179 else
180 return 1; /* error in string */
181
182 *val = *val * 16 + digit;
183 (*str)++;
184 }
185 return 0;
186 }
187
188 static unsigned ReadBlasterEnv(unsigned *port)
189 /* Gets the Blaster environment statement and stores the values in the
190 * variables whose addresses were passed to it.
191 * Returns:
192 * 0 if successful
193 * 1 if there was an error reading the port address.
194 */
195 /* RB trimmed down just to grab the ioport as that's all we're interested in */
196 {
197 char *env;
198 env = getenv("BLASTER");
199 while (*env) {
200 switch(toupper( *env )) {
201 case 'A':
202 env++;
203 if (base16(&env, port)) /* interpret port value as hex */
204 return 1; /* error */
205 break;
206 default:
207 env++;
208 break;
209 }
210 return(0);
211 }
212
213 return 1; /*RB if we got here then there was no port value*/
214 }
215
216 static void FMoutput(unsigned port, int reg, int val)
217 /* This outputs a value to a specified FM register at a specified FM port. */
218 {
219 outp(port, reg);
220 sleepms(1); /* need to wait at least 3.3 microsec */
221 outp(port+1, val);
222 sleepms(1); /* need to wait at least 23 microsec */
223 }
224
225 static void fm(int reg, int val)
226 /* This function outputs a value to a specified FM register at the Sound
227 * Blaster (mono) port address.
228 */
229 {
230 FMoutput(IOport+FM, reg, val);
231 }
232
233 #if 0
234 static void Profm1(int reg, int val)
235 /* This function outputs a value to a specified FM register at the Sound
236 * Blaster Pro left FM port address (or OPL-3 bank 0).
237 */
238 {
239 FMoutput(IOport+PROFM1, reg, val);
240 } 241 #endif
242
243 static void Profm2(int reg, int val)
244 /* This function outputs a value to a specified FM register at the Sound
245 * Blaster Pro right FM port address (or OPL-3 bank 1).
246 */
247 {
248 FMoutput(IOport+PROFM2, reg, val);
249 }
250
251 /* here endeth the straight stuff from creative labs, RB */
252
253 static unsigned char fmtemp[9];/*temporary vars used to store value used
254 to mask keyoff bit in OPL registers as they're write only and the one
255 with the keyoff bit also has a frequency multiplier in it fmtemp needed
256 to make sure note stays the same when in the release portion of the
257 envelope*/
258
259 /* offsets of registers for each channel in the fm synth */
260 static unsigned char fm_offset[9] = {0,1,2,8,9,10,16,17,18};
261 static char fm_channel=0;/* keeps track of the channel number being used in
262 polyphonic operation */
263 int fm_attack;
264 int fm_decay;
265 int fm_sustain;
266 int fm_release;
267 int fm_vol;
268 int fm_wavetype;
269 int polyphony;
270 int hi_atten;
271 static char offvoice = 0;
272
273 void initsndvars(void)
274 {
275 int k, b;
276 int tmp_atten = hi_atten; /* stupid kludge needed to make attenuation work */
277 if (hi_atten == 1) /* should be: 00 -> none but is: 00 -> none */
278 tmp_atten = 2; /* 01 -> low 01 -> mid */
279 if (hi_atten == 2) /* 10 -> mid 10 -> low */
280 tmp_atten = 1; /* 11 -> high 11 -> high */
281
282 for(k=0;k<9;k++) {
283 /***************************************
284 * Set parameters for the carrier cells *
285 ***************************************/
286
287 fm(0x43+fm_offset[k],(63-fm_vol) | (tmp_atten << 6));
288 /* volume decrease with pitch (D7-D6),
289 * attenuation (D5-D0)
290 */
291 b = ((15 - fm_attack)*0x10) | (15 - fm_decay);
292 fm(0x63+fm_offset[k],b);
293 /* attack (D7-D4) and decay (D3-D0) */
294 b = ((15 - fm_sustain) * 0x10) | (15 - fm_release);
295 fm(0x83+fm_offset[k],b);
296 /* high sustain level (D7-D4=0), release rate (D3-D0) */
297 fm(0xE3+fm_offset[k],fm_wavetype);
298 }
299 }
300
301 int initfm(void)
302 {
303 int k;
304 k = ReadBlasterEnv(&IOport);
305 if (k == 1) { /* BLASTER environment variable not set */
306 static FCODE msg[] = {"No sound hardware or Blaster variable not set"};
307 soundflag = (soundflag & 0xef); /* 1110 1111 */
308 stopmsg(0,msg);
309 return(0); /* no card found */
310 }
311 fm(1,0x00); /* must initialize this to zero */
312 Profm2(5, 1); /* set to OPL3 mode, necessary for stereo */
313 Profm2(4, 0); /* make sure 4-operator mode is disabled */
314 fm(1,0x20); /* set bit five to allow wave shape other than sine */
315 for(k=0;k<9;k++) {
316 fm(0xC0+k,LEFT | RIGHT | 1); /* set both channels, parallel connection */
317
318 /*****************************************
319 * Set parameters for the modulator cells *
320 *****************************************/
321 /* these don't change once they are set */
322 fm(0x20+fm_offset[k],0x21);
323 /* sustained envelope type, frequency multiplier=1 */
324 fm(0x40+fm_offset[k],0x3f);
325 /* maximum attenuation, no volume decrease with pitch */
326 /* Since the modulator signal is attenuated as much as possible, these
327 * next two values shouldn't have any effect.
328 */
329 fm(0x60+fm_offset[k],0x44);
330 /* slow attack and decay */
331 fm(0x80+fm_offset[k],0x05);
332 /* high sustain level, slow release rate */
333 }
334 initsndvars();
335 fm_channel = 0;
336 offvoice = (char)(-polyphony);
337 return(1); /*all sucessfully initialised ok */
338 }
339
340
341 int soundon(int freq)
342 {
343 /* wrapper to previous fractint snd routine, uses fm synth or midi if
344 available and selected */
345 /* Returns a 1 if sound is turned on, a 0 if not. */
346 int note,oct,chrome;
347 unsigned int block,mult,fn;
348 double logbase = log(8.176);
349
350 /* clip to 5 Khz to match the limits set in asm routine that drives pc speaker*/
351 if (freq > 5000) return(0);
352 if(freq<20) return(0);/* and get rid of really silly bass notes too */
353
354 /* convert tone to note number for midi */
355 note =(int)(12 * (log(freq) - logbase) / log(2.0) + 0.5);
356
357 oct = (note / 12) * 12; /* round to nearest octave */
358 chrome = note % 12; /* extract which note in octave it was */
359 note = oct + scale_map[chrome]; /* remap using scale mapping array */
360
361 if (soundflag & 64)
362 freq=(int)(exp(((double)note/12.0)*log(2.0))*8.176);
363 /* pitch quantize note for FM and speaker */
364
365 if (soundflag & 16) { /* fm flag set */
366 double temp_freq = (double)freq * (double)1048576;
367 block = 0;
368 mult = 1;
369 fn=(int)(temp_freq / (1 << block) / mult / 50000.0);
370 while(fn >1023) { /* fn must have ten bit value so tweak mult and block until it fits */
371 if (block < 8) block++; /* go up an octave */
372 else {
373 mult++; /* if we're out of octaves then increment the multiplier*/
374 block = 0; /* reset the block */
375 }
376 fn=(int)(temp_freq / (1 << block) / mult / 50000.0);
377 }
378
379 /* printf("on: fn = %i chn= %i blk = %i mlt = %i ofs = %i ",fn,fm_channel,block,mult,fm_offset[fm_channel]);
380 getch(); */
381 /* then send the right values to the fm registers */
382 fm(0x23+fm_offset[fm_channel],0x20 | (mult & 0xF));
383 /* 0x20 sets sustained envelope, low nibble is multiply number */
384 fm(0xA0+fm_channel,(fn & 0xFF));
385 /* save next to use as keyoff value */
386 fmtemp[fm_channel] = (unsigned char)(((fn >> 8) & 0x3) | (block << 2));
387 fm(0xB0+fm_channel,fmtemp[fm_channel] | KEYON);
388 /* then increment the channel number ready for the next note */
389 /* printf(" fmtemp = %i \n ",fmtemp[fm_channel]); */
390 if (++fm_channel >= 9) fm_channel = 0;
391 /* May have changed some parameters, put them in the registers. */
392 initsndvars();
393 }
394
395 if (soundflag & 8) snd(freq); /* pc spkr flag set */
396 return(1);
397 }
398
399
400 void soundoff(void)
401 {
402 if (soundflag & 16) {/* switch off old note */
403 if(offvoice >= 0){
404 /* printf("off: ofv= %i tmp = %i \n",offvoice,fmtemp[offvoice]);
405 getch(); */
406 fm(0xB0+offvoice,fmtemp[offvoice]);
407 }
408 offvoice++;
409 /* then increment channel number (letting old note die away properly prevents
410 nasty clicks between notes as OPL has no zero crossing logic and switches
411 frequencies immediately thus creating an easily audible glitch, especially
412 in bass notes... also allows chords :-) */
413 if(offvoice >= 9) offvoice = 0;
414 }
415 if (soundflag & 8) nosnd(); /* shut off pc speaker */
416 }
417
418
419 void mute()
420 {
421 /* shut everything up routine */
422 int i;
423 if(soundflag & 16)
424 for (i=0;i<=8;i++) {
425 fm(0xB0+i,fmtemp[i]);
426 fm(0x83+fm_offset[i],0xFF);
427 }
428 if (soundflag & 8) nosnd(); /* shut off pc speaker */
429 fm_channel = 0;
430 offvoice = (char)(-polyphony);
431 }
432
433
434 void buzzer(int tone)
435 {
436 if((soundflag & 7) == 1) {
437
438 if (soundflag & 8) buzzerpcspkr(tone);
439
440 if (soundflag & 16) {
441 int oldsoundflag = soundflag;
442 sleepms(1); /* to allow quiet timer calibration first time */
443 soundflag = soundflag & 0xf7; /*switch off soundon stuff for pc spkr */
444 switch(tone) {
445 case 0:
446 soundon(1047);
447 sleepms(1000);
448 soundoff();
449 soundon(1109);
450 sleepms(1000);
451 soundoff();
452 soundon(1175);
453 sleepms(1000);
454 soundoff();
455 break;
456 case 1:
457 soundon(2093);
458 sleepms(1000);
459 soundoff();
460 soundon(1976);
461 sleepms(1000);
462 soundoff();
463 soundon(1857);
464 sleepms(1000);
465 soundoff();
466 break;
467 default:
468 soundon(40);
469 sleepms(5000);
470 soundoff();
471 break;
472 }
473 soundflag = oldsoundflag;
474 mute(); /*switch off all currently sounding notes*/
475
476 }
477
478 /* must try better FM equiv..
479 maybe some nicer noises like ping, burp, and bong :-) */
480
481 }
482 }
483
484 #define LOADCHOICES(X) {\
485 static FCODE tmp[] = { X };\
486 far_strcpy(ptr,(char far *)tmp);\
487 choices[++k]= ptr;\
488 ptr += sizeof(tmp);\
489 }
490
491 int get_sound_params(void)
492 {
493 /* routine to get sound settings */
494 static FCODE o_hdg[] = {"Sound Control Screen"};
495 char *soundmodes[] = {s_off,s_beep,s_x,s_y,s_z};
496 int old_soundflag,old_orbit_delay;
497 char hdg[sizeof(o_hdg)];
498 char far *choices[15];
499 char far *ptr;
500 struct fullscreenvalues uvalues[15];
501 int k;
502 int i;
503 int oldhelpmode;
504 char old_start_showorbit;
505
506 oldhelpmode = helpmode;
507 old_soundflag = soundflag;
508 old_orbit_delay = orbit_delay;
509 old_start_showorbit = start_showorbit;
510
511 /* soundflag bits 0..7 used as thus:
512 bit 0,1,2 controls sound beep/off and x,y,z
513 (0 == off 1 == beep, 2 == x, 3 == y, 4 == z)
514 bit 3 controls PC speaker
515 bit 4 controls sound card OPL3 FM sound
516 bit 5 controls midi output
517 bit 6 controls pitch quantise
518 bit 7 free! */
519 get_sound_restart:
520 menu2 = 0;
521 k = -1;
522 far_strcpy(hdg,o_hdg);
523 ptr = (char far *)MK_FP(extraseg,0);
524
525 LOADCHOICES("Sound (off, beep, x, y, z)");
526 uvalues[k].type = 'l';
527 uvalues[k].uval.ch.vlen = 4;
528 uvalues[k].uval.ch.llen = 5;
529 uvalues[k].uval.ch.list = soundmodes;
530 uvalues[k].uval.ch.val = soundflag&7;
531
532 LOADCHOICES("Use PC internal speaker?");
533 uvalues[k].type = 'y';
534 uvalues[k].uval.ch.val = (soundflag & 8)?1:0;
535
536 LOADCHOICES("Use soundcard output?");
537 uvalues[k].type = 'y';
538 uvalues[k].uval.ch.val = (soundflag & 16)?1:0;
539 /*
540 LOADCHOICES("Midi...not implemented yet");
541 uvalues[k].type = 'y';
542 uvalues[k].uval.ch.val = (soundflag & 32)?1:0;
543 */
544 LOADCHOICES("Quantize note pitch ?");
545 uvalues[k].type = 'y';
546 uvalues[k].uval.ch.val = (soundflag & 64)?1:0;
547
548 LOADCHOICES("Orbit delay in ms (0 = none)");
549 uvalues[k].type = 'i';
550 uvalues[k].uval.ival = orbit_delay;
551
552 LOADCHOICES("Base Hz Value");
553 uvalues[k].type = 'i';
554 uvalues[k].uval.ival = basehertz;
555
556 LOADCHOICES("Show orbits?");
557 uvalues[k].type = 'y';
558 uvalues[k].uval.ch.val = start_showorbit;
559
560 LOADCHOICES("");
561 uvalues[k].type = '*';
562 LOADCHOICES("Press F6 for FM synth parameters, F7 for scale mappings");
563 uvalues[k].type = '*';
564 LOADCHOICES("Press F4 to reset to default values");
565 uvalues[k].type = '*';
566
567 oldhelpmode = helpmode;
568 helpmode = HELPSOUND;
569 i = fullscreen_prompt(hdg,k+1,choices,uvalues,255,NULL);
570 helpmode = oldhelpmode;
571 if (i <0) {
572 soundflag = old_soundflag;
573 orbit_delay = old_orbit_delay;
574 start_showorbit = old_start_showorbit;
575 return(-1); /*escaped */
576 }
577
578 k = -1;
579
580 soundflag = uvalues[++k].uval.ch.val;
581
582 soundflag = soundflag + (uvalues[++k].uval.ch.val * 8);
583 soundflag = soundflag + (uvalues[++k].uval.ch.val * 16);
584 /* soundflag = soundflag + (uvalues[++k].uval.ch.val * 32); */
585 soundflag = soundflag + (uvalues[++k].uval.ch.val * 64);
586
587 orbit_delay = uvalues[++k].uval.ival;
588 basehertz = uvalues[++k].uval.ival;
589 start_showorbit = (char)uvalues[++k].uval.ch.val;
590
591 /* now do any intialization needed and check for soundcard */
592 if ((soundflag & 16) && !(old_soundflag & 16)) {
593 initfm();
594 }
595
596 if (i == F6) {
597 get_music_parms();/* see below, for controling fmsynth */
598 goto get_sound_restart;
599 }
600
601 if (i == F7) {
602 get_scale_map();/* see below, for setting scale mapping */
603 goto get_sound_restart;
604 }
605
606 if (i == F4) {
607 soundflag = 9; /* reset to default */
608 orbit_delay = 0;
609 basehertz = 440;
610 start_showorbit = 0;
611 goto get_sound_restart;
612 }
613
614 if (soundflag != old_soundflag && ((soundflag&7) > 1 || (old_soundflag&7) > 1))
615 return (1);
616 else
617 return (0);
618 }
619
620 static int get_scale_map(void)
621 {
622 static FCODE o_hdg[] = {"Scale Mapping Screen"};
623 int oldhelpmode;
624 char hdg[sizeof(o_hdg)];
625 char far *choices[15];
626 char far *ptr;
627 struct fullscreenvalues uvalues[15];
628 int k;
629 int i, j;
630
631 menu2++;
632 get_map_restart:
633 k = -1;
634 far_strcpy(hdg,o_hdg);
635 ptr = (char far *)MK_FP(extraseg,0);
636
637 LOADCHOICES("Scale map C (1)");
638 uvalues[k].type = 'i';
639 uvalues[k].uval.ival = scale_map[0];
640
641 LOADCHOICES("Scale map C#(2)");
642 uvalues[k].type = 'i';
643 uvalues[k].uval.ival = scale_map[1];
644
645 LOADCHOICES("Scale map D (3)");
646 uvalues[k].type = 'i';
647 uvalues[k].uval.ival = scale_map[2];
648
649 LOADCHOICES("Scale map D#(4)");
650 uvalues[k].type = 'i';
651 uvalues[k].uval.ival = scale_map[3];
652
653 LOADCHOICES("Scale map E (5)");
654 uvalues[k].type = 'i';
655 uvalues[k].uval.ival = scale_map[4];
656
657 LOADCHOICES("Scale map F (6)");
658 uvalues[k].type = 'i';
659 uvalues[k].uval.ival = scale_map[5];
660
661 LOADCHOICES("Scale map F#(7)");
662 uvalues[k].type = 'i';
663 uvalues[k].uval.ival = scale_map[6];
664
665 LOADCHOICES("Scale map G (8)");
666 uvalues[k].type = 'i';
667 uvalues[k].uval.ival = scale_map[7];
668
669 LOADCHOICES("Scale map G#(9)");
670 uvalues[k].type = 'i';
671 uvalues[k].uval.ival = scale_map[8];
672
673 LOADCHOICES("Scale map A (10)");
674 uvalues[k].type = 'i';
675 uvalues[k].uval.ival = scale_map[9];
676
677 LOADCHOICES("Scale map A#(11)");
678 uvalues[k].type = 'i';
679 uvalues[k].uval.ival = scale_map[10];
680
681 LOADCHOICES("Scale map B (12)");
682 uvalues[k].type = 'i';
683 uvalues[k].uval.ival = scale_map[11];
684
685 LOADCHOICES("");
686 uvalues[k].type = '*';
687 LOADCHOICES("Press F6 for FM synth parameters");
688 uvalues[k].type = '*';
689 LOADCHOICES("Press F4 to reset to default values");
690 uvalues[k].type = '*';
691
692 oldhelpmode = helpmode; /* this prevents HELP from activating */
693 helpmode = HELPMUSIC;
694 i = fullscreen_prompt(hdg,k+1,choices,uvalues,255,NULL);
695 helpmode = oldhelpmode; /* re-enable HELP */
696 if (i < 0) {
697 return(-1);
698 }
699
700 k = -1;
701
702 for(j=0;j<=11;j++) {
703 scale_map[j] = abs(uvalues[++k].uval.ival);
704 if (scale_map[j] > 12)
705 scale_map[j] = 12;
706 }
707
708 if (i == F6 && menu2 == 1) {
709 get_music_parms();/* see below, for controling fmsynth */
710 goto get_map_restart;
711 } else if (i == F6 && menu2 == 2) {
712 menu2--;
713 }
714
715 if (i == F4) {
716 for(j=0;j<=11;j++) scale_map[j] = j + 1;
717 goto get_map_restart;
718 }
719
720 return (0);
721 }
722
723
724 static int get_music_parms(void)
725 {
726 static FCODE o_hdg[] = {"FM Synth Card Control Screen"};
727 char *attenmodes[] = {s_none,s_low,s_mid,s_high};
728 int oldhelpmode;
729 char hdg[sizeof(o_hdg)];
730 char far *choices[11];
731 char far *ptr;
732 struct fullscreenvalues uvalues[11];
733 int k;
734 int i;
735
736 menu2++;
737 get_music_restart:
738 k = -1;
739 far_strcpy(hdg,o_hdg);
740 ptr = (char far *)MK_FP(extraseg,0);
741
742 LOADCHOICES("Polyphony 1..9");
743 uvalues[k].type = 'i';
744 uvalues[k].uval.ival = polyphony+1;
745
746 LOADCHOICES("Wave type 0..7");
747 uvalues[k].type = 'i';
748 uvalues[k].uval.ival = fm_wavetype;
749
750 LOADCHOICES("Note attack time 0..15");
751 uvalues[k].type = 'i';
752 uvalues[k].uval.ival = fm_attack;
753
754 LOADCHOICES("Note decay time 0..15");
755 uvalues[k].type = 'i';
756 uvalues[k].uval.ival = fm_decay;
757
758 LOADCHOICES("Note sustain level 0..15");
759 uvalues[k].type = 'i';
760 uvalues[k].uval.ival = fm_sustain;
761
762 LOADCHOICES("Note release time 0..15");
763 uvalues[k].type = 'i';
764 uvalues[k].uval.ival = fm_release;
765
766 LOADCHOICES("Soundcard volume? 0..63");
767 uvalues[k].type = 'i';
768 uvalues[k].uval.ival = fm_vol ;
769
770 LOADCHOICES("Hi pitch attenuation");
771 uvalues[k].type = 'l';
772 uvalues[k].uval.ch.vlen = 4;
773 uvalues[k].uval.ch.llen = 4;
774 uvalues[k].uval.ch.list = attenmodes;
775 uvalues[k].uval.ch.val = hi_atten;
776
777 LOADCHOICES("");
778 uvalues[k].type = '*';
779 LOADCHOICES("Press F7 for scale mappings");
780 uvalues[k].type = '*';
781 LOADCHOICES("Press F4 to reset to default values");
782 uvalues[k].type = '*';
783
784 oldhelpmode = helpmode; /* this prevents HELP from activating */
785 helpmode = HELPMUSIC;
786 i = fullscreen_prompt(hdg,k+1,choices,uvalues,255,NULL);
787 helpmode = oldhelpmode; /* re-enable HELP */
788 if (i < 0) {
789 return(-1);
790 }
791
792 k = -1;
793 polyphony = abs(uvalues[++k].uval.ival - 1);
794 if (polyphony > 8) polyphony = 8;
795 fm_wavetype = (uvalues[++k].uval.ival)&0x07;
796 fm_attack = (uvalues[++k].uval.ival)&0x0F;
797 fm_decay = (uvalues[++k].uval.ival)&0x0F;
798 fm_sustain = (uvalues[++k].uval.ival)&0x0F;
799 fm_release = (uvalues[++k].uval.ival)&0x0F;
800 fm_vol = (uvalues[++k].uval.ival)&0x3F;
801 hi_atten = uvalues[++k].uval.ch.val;
802 if (soundflag & 16) {
803 initfm();
804 }
805
806 if (i == F7 && menu2 == 1) {
807 get_scale_map();/* see above, for setting scale mapping */
808 goto get_music_restart;
809 } else if (i == F7 && menu2 == 2) {
810 menu2--;
811 }
812
813 if (i == F4) {
814 polyphony = 0;
815 fm_wavetype = 0;
816 fm_attack = 5;
817 fm_decay = 10;
818 fm_sustain = 13;
819 fm_release = 5;
820 fm_vol = 63;
821 hi_atten = 0;
822 if (soundflag & 16) {
823 initfm();
824 }
825 goto get_music_restart;
826 }
827
828 /*testsound(); */
829
830 return (0);
831 }
832
833 /*testsound(void)
834 {
835 int i;
836 for (i=100; (i<5000&&!keypressed()); i+=25)
837 {
838 soundon(i);
839 sleepms(orbit_delay);
840 soundoff();
841 sleepms(orbit_delay);
842 }
843
844 }
845 */
846