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