;	/*\
;	|*| $Author:   BCRANE  $
;	|*| 
;	|*| $Date:   09 Sep 1992 13:44:30  $
;	|*| 
;	|*| $Header:   X:/sccs/mixers/dialog.c_v   1.11   09 Sep 1992 13:44:30   BCRANE  $
;	|*| 
;	|*| $Log:   X:/sccs/mixers/dialog.c_v  $
 * 
 *    Rev 1.11   09 Sep 1992 13:44:30   BCRANE
 * changed MixerRect, RecordRect, and all mixer elements start and end x-coords
 * up 1.
 * 
 *    Rev 1.10   04 Sep 1992 13:24:42   BCRANE
 * changed bottom coordinate of MixerRect
 * 
 *    Rev 1.9   02 Sep 1992 10:03:04   BCRANE
 * added SB volume control
 * 
 *    Rev 1.8   22 Jul 1992 08:21:14   BCRANE
 * added #if USEMIXERGET to use either MixerGetSettings with 
 * callback to "subsavecurrent" or "savecurrent".  Currently set
 * to 1 to use MixerGetSettings.
 * 
 *    Rev 1.7   13 Jul 1992 11:57:02   BCRANE
 * changed F3/SF3 to F5-F8/SF5-SF8
 * now saves setting#.pas where # is 0-3
 * modified F1 help to indicate this new feature 
 * 
 *    Rev 1.6   13 Jul 1992 09:41:56   DCODY
 * GetMixerSettings now reports dead mixer settings
 * 
 *    Rev 1.5   10 Jul 1992 16:57:44   BCRANE
 * changed INPUT to MIXER
 * 
 *    Rev 1.4   10 Jul 1992 16:38:52   BCRANE
 * finalized (!) save and load defaults - handles input/output mixers
 * last-one-active situation, as well as absolute vs. percent values
 * 
 *    Rev 1.3   09 Jul 1992 17:45:16   BCRANE
 * added load and save current state and getdriverpath
 * not fully complete
 * 
 *    Rev 1.2   01 Jul 1992 14:40:58   DCODY
 * added OEM specific wording
* 
*    Rev 1.1   23 Jun 1992 16:44:32   DCODY
* PAS2 update
* 
*    Rev 1.0   15 Jun 1992 09:41:16   BCRANE
* Initial revision.
;	|*| 
;	|*| /*$Logfile:   X:/sccs/mixers/dialog.c_v  $
;	|*| 
;	|*| /*$Modtimes$
;	|*| 
;	\*/

	/*\
	|*|---====< DIALOG.C  --  PAS User interface module >====----
	|*|
	|*| Pro Audio Spectrum Mixer User Interface program. This
	|*| program provides mixer control from the command line.
	|*|
	|*| Media Vision, Inc. Copyright (c) 1991, All rights reserved
	|*|
	\*/

	/* minor revisions to allow integration with audiolnk (large model) */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <bios.h>

#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <io.h>

#include "dialog.h"
#include <binary.h>

#ifndef OEM
#define OEM 0
#endif

	/*\
	|*|----====< some prototypes >====----
	\*/

		int 	EqualizerLevels 	( );
        int     ScrollObjectHandler ( );
        int     SwitchObjectHandler ( );
        int     VolumeLevels        ( );
		int 	VolumeButtons		( );
		int 	EffectsButtons		( );


	/*\
	|*|----====< global data >====----
	\*/

#define TRUE    -1
#define FALSE	0

	static char makemenull[]="";

	static char *screenlayout[] = {

		"ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿",
#if OEM
		"³           Spectrum            ³",
#else
		"³Media Vision Pro AudioSpectrum ³",
#endif
		"ÃÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ",
		"³ FM         ³³³",
		"³ Synthesizer³³³",
		"ÃÄÄÄÄÄÄÄÄÄÄÄÄÅÄ´                Ã",
		"³ External   ³³³",
		"³ Jack       ³³³",
		"ÃÄÄÄÄÄÄÄÄÄÄÄÄÅÄ´                Ã",
		"³ Internal   ³³³",
		"³ Connector  ³³³",
		"ÃÄÄÄÄÄÄÄÄÄÄÄÄÅÄ´                Ã",
		"³ Microphone ³³³",
		"³ Jack       ³³³",
		"ÃÄÄÄÄÄÄÄÄÄÄÄÄÅÄ´                Ã",
		"³ PC         ³³³",
		"³ Speaker    ³³³",
		"ÃÄÄÄÄÄÄÄÄÄÄÄÄÅÄ´                Ã",
		"³ SB Digital ³³³",
		"³ Audio      ³³³",
		"ÃÄÄÄÄÄÄÄÄÄÄÄÄÅÄ´                Ã",
		"³ Digital    ³³³",
		"³ Audio      ³³³",
		"ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"
	};

	static char *screen2[] = {

		"ÚÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿",
		"³Volume  ³ Volume Control ³ ³",
		"ÃÄÄÄÄÄÄÂÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄ´",
		"³Left  ³³³³",
		"³Right ³³³³",
		"ÃÄÄÄÄÄÄÅÄ´                ÃÄ´",
		"³Bass  ³³³³",
		"ÃÄÄÄÄÄÄÅÄ´                ÃÄ´",
		"³Treble³³³³",
		"ÀÄÄÄÄÄÄÁÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ",
		"ÚÄÄÄÄÄÄÄÄÄÄÄÂÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄ¿",
		"³ Loudness  ³³ Enhanced  ³³",
		"ÀÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÄÄÄÄÄÄÄÄÄÄÁÄÙ",

	};

	static char *screen3[] = {

		"ÄÉÍÍÍÍÍÍ»",
		"ºPlay &º",
		"ºRecordº",
		"ÄÌÍÍÍÍÍÍ¹",
		"ºPlay &º",
		"ºRecordº",
		"ÄÌÍÍÍÍÍÍ¹",
		"ºPlay &º",
		"ºRecordº",
		"ÄÌÍÍÍÍÍÍ¹",
		"ºPlay &º",
		"ºRecordº",
		"ÄÌÍÍÍÍÍÍ¹",
		"ºPlay &º",
		"ºRecordº",
		"ÄÌÍÍÍÍÍÍ¹",
		"ºPlay &º",
		"ºRecordº",
		"ÄÌËËËËËË¹",
		"ÌÎÎÎÎÎÎ¹",
		"ÌÎÎÎÎÎÎ¹",
		"ÄÈÊÊÊÊÊÊ¼",

	};

	static char *screen4[] = {	   /* Help2rect 						*/

		"ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿",
		"³ Type the F1 key for help. ³",
		"ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"
	};

	static char EffectsString[] = "±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±";

	static char *effectsbkgn[] = {
		EffectsString,
		EffectsString,
		EffectsString,
		EffectsString,
		EffectsString,
      EffectsString,
		EffectsString,
		EffectsString,
		EffectsString,
		EffectsString,
		EffectsString,
		EffectsString,
		EffectsString,
		EffectsString,
		EffectsString,
		EffectsString,
		EffectsString,
      EffectsString,
      EffectsString,
      EffectsString,
      EffectsString,
		EffectsString
	};

	static char *screen5[] = {	   /* input mixer helps 				*/

		"ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿",
		"³    Channel Connections    ³",
		"ÃÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄ´",
		"³ Left   ³³ Left   ³",
		"³ Left   ³³ Right  ³",
		"³ Right  ³³ Left   ³",
		"³ Right  ³³ Right  ³",
		"ÀÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÙ"
    };

	static char *screen6[] = {	   /* input mixer helps 				*/
		"ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿",
		"³  Recording Monitor Level  ³",
		"ÃÄÄÄÄÄÄÂÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ´",
		"³Left  ³³³³",
		"³Right ³³³³",
		"ÀÄÄÄÄÄÄÁÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ"
	};

	static char *screen7[] = {	   /* realsound switch					*/

		"ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿",
		"³   Real Sound Support    ³³",
		"ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ"
	};

	static char playmsgi1[] = "Play &";
	static char playmsgi2[] = "Record";
	static char playmsgo1[] = "Play  ";
	static char playmsgo2[] = "Only  ";

	static char *helpscreen[] = {

		"ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»",
#if OEM
		"º           Spectrum            º",
		"º                               º",
#else
		"º    Pro AudioSpectrum,  V1.21  º",
		"º      By Media Vision, Inc.    º",
#endif
		"ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¶",
		"º        Keyboard Control       º",
		"º       ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ      º",
		"ºHOME  to decrease left volume  º",
		"º     to decrease both volumes º",
		"º END  to decrease right volume º",
		"ºPGUP  to increase left volume  º",
		"º  3   to increase both volumes º",
		"ºPGDN  to increase right volume º",
		"ºENTER to toggle a button on/offº",
		"º TAB  to move between fields   º",
		"º F4   reset mixer to defaults  º",
		"º F2   special effects          º",
		"ºF5-F8 load mixer settings (use º",
		"º      Shifted-FKEY to save)    º",
		"º ESC  will exit all dialogs    º",
		"º       ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ      º",
		"º       Copyright (c) 1991      º",
		"º      All Rights Reserved.     º",
		"ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼"
	};


		static char twobar[]			= "úúúúúùùùùù";
		static char channelconnect[]	= "";
		static char channeldisconnect[] = "ú ú ú ú ú";

		extern char leftswitch;
		extern int	leftvolume;
		extern char ritswitch;
		extern int	ritvolume;
		extern int	VolumeNumber;
		extern char VolumeSwitch;
		extern int	Left2LeftState;
		extern int	Left2RightState;
		extern int	Right2LeftState;
		extern int	Right2RightState;

	//	static char NumLockState;

        static VideoStruct OurWnd = { 0,0,0x18,0x4f, 0,0, 0x1f,0, 0,0xb800 };
		VideoStruct *CurWnd = &OurWnd;
		unsigned int VideoSegment = 0xb800;

		static rect Help1Rect  = { 1,23, 23,57 };
		static rect Help2Rect  = {19,47, 21,77 };
		static rect MixerRect  = { 0, 2, 23,36 };
		static rect RecordRect = { 2,35, 23,45 };
		static rect VolRect    = { 4,47, 16,77 };
		static rect EffectsRect = { 1,20, 22,60 };
		static rect Screen6Rect = { 2,25,  7,55 };
		static rect Screen5Rect = { 9,25, 16,55 };
		static rect Screen7Rect = {18,25, 20,55 };

	/* file handle for accessing MVPROAS								*/

		static int	mv; 			/* mvsound dos driver				*/

	/* objects															*/

#define OBJ_SCROLL		1
#define OBJ_VOLUME		2
#define OBJ_BUTTON		3

        typedef struct {
			int  type;				// structure type
			void *next; 			// next structure pointer
			void *back; 			// prior structure pointer
			int (*scr)();			// object processor
			int (*swi)();			// object processor
			rect namr;				// channel name rectangle
			rect scrr;				// scroll bar rectangle
			rect swir;				// record mixer select switch rectangle
			char name[10];			// channel name "mic"/"ext", etc
			int leftchannel;		// current left channel setting
			int ritchannel; 		// current right channel setting
			int mixerselect;		// choosen mixer
			int deadlchannel;		// current left channel setting
			int deadrchannel;		// current right channel setting
			int deadmixer;			// choosen mixer

        } Scroll, *SPtr;

		typedef struct {
			int  type;				// structure type
			void *next; 			// next structure pointer
			void *back; 			// prior structure pointer
			int (*scr)();			// object processor
			rect namr;				// channel name rectangle
			rect scrr;				// scroll bar rectangle
			char name[20];			// channel name "mic"/"ext", etc
			int channel;			// current channel setting
		} Volume, *VPtr;

		typedef struct {
			int  type;				// structure type
			void *next; 			// next structure pointer
			void *back; 			// prior structure pointer
			int (*scr)();			// object processor
			rect namr;				// channel name rectangle
			rect scrr;				// scroll bar rectangle
			char name[20];			// channel name "mic"/"ext", etc
			int state;				// current button state
		} Button, *BPtr;

        typedef struct {
			void *head;
			void *tail;
			void *nextlist;
		} ObjectList, *OLPtr;

	// Scroll bar objects

		static Scroll SynthScroll;		   // structure prototypes
		static Scroll ExtScroll;
		static Scroll IntScroll;
		static Scroll MicScroll;
		static Scroll DigitalScroll;
		static Scroll TBScroll;
		static Scroll SpkrScroll;
		static Scroll OutputMixerScroll;

		static Volume LeftVolumeLevel;
		static Volume RitVolumeLevel;
		static Volume BassVolume;
		static Volume TrebVolume;

		static Button Loudness;
		static Button Enhanced;
		static Button Left2Left;
		static Button Left2Right;
		static Button Right2Left;
		static Button Right2Right;
		static Button RealSoundButton;

		static ObjectList MainList;
		static ObjectList EffectsList;

		static ObjectList MainList = {
			&SynthScroll,
			&Enhanced,
			&EffectsList
        };

		static ObjectList EffectsList = {
			&OutputMixerScroll,
			&RealSoundButton,
			0
		};

		static OLPtr   CurrList = &MainList;

		static Scroll *CurrentObject = &SynthScroll;  // current object pointer

		static Scroll SynthScroll = {
			OBJ_SCROLL,
			&ExtScroll,
            0,
			&ScrollObjectHandler,
			&SwitchObjectHandler,
			 3, 3, 4,14,
			 3,18, 4,33,
			 3,37, 4,42,
			"FM ",
			0,
			0,
			0,0,0,0
		};

		static Scroll ExtScroll = {
			OBJ_SCROLL,
			&IntScroll,
			&SynthScroll,
			&ScrollObjectHandler,
            &SwitchObjectHandler,
			 6, 3, 7,14,
			 6,18, 7,33,
			 6,37, 7,42,
			"EXT ",
			0,
			0,
			0,0,0,0
		};

		static Scroll IntScroll = {
			OBJ_SCROLL,
			&MicScroll,
			&ExtScroll,
			&ScrollObjectHandler,
            &SwitchObjectHandler,
			 9, 3,10,14,
			 9,18,10,33,
			 9,37,10,42,
			"INT ",
			0,
			0,
			0,0,0,0
		};

		static Scroll MicScroll = {
			OBJ_SCROLL,
			&SpkrScroll,
			&IntScroll,
			&ScrollObjectHandler,
            &SwitchObjectHandler,
			12, 3,13,14,
			12,18,13,33,
			12,37,13,42,
			"MIC ",
			0,
			0,
			0,0,0,0
		};

		static Scroll SpkrScroll = {
			OBJ_SCROLL,
			&TBScroll,
			&MicScroll,
			&ScrollObjectHandler,
            &SwitchObjectHandler,
			15, 3,16,14,
			15,18,16,33,
			15,37,16,42,
			"SPEAKER ",
			0,
			0,
			0,0,0,0
		};

		static Scroll TBScroll = {
			OBJ_SCROLL,
			&DigitalScroll,
			&SpkrScroll,
			&ScrollObjectHandler,
            &SwitchObjectHandler,
			18, 3,19,14,
			18,18,19,33,
			18,37,19,42,
			"SB      ",
			0,
			0,
			0,0,0,0
		};

		static Scroll DigitalScroll = {
			OBJ_SCROLL,
			&LeftVolumeLevel,
			&TBScroll,
			&ScrollObjectHandler,
			0,
			21, 3,22,14,
			21,18,22,33,
			21,37,22,42,
			"PCM ",
			0,
			0,
			0,0,0,0
		};

		static Volume LeftVolumeLevel = {
			OBJ_VOLUME,
			&BassVolume,
			&DigitalScroll,
			&VolumeLevels,
			 7,48, 7,53,
			 7,57, 7,72,
			"LEVEL ",
			0
		};

			// this object is owned byte LeftVolumeLevel

				static Volume RitVolumeLevel  = {
					OBJ_VOLUME,
					0,
					0,
					&VolumeLevels,
					 8,48, 8,53,
					 8,57, 8,72,
					"LEVEL ",
					0
				};

		static Volume BassVolume = {
			OBJ_VOLUME,
			&TrebVolume,
			&LeftVolumeLevel,
			&EqualizerLevels,
			10,48,10,53,
			10,57,10,72,
			"BASS ",
			0
		};

		static Volume TrebVolume = {
			OBJ_VOLUME,
			&Loudness,
			&BassVolume,
			&EqualizerLevels,
			12,48,12,53,
			12,57,12,72,
			"TREBLE ",
			0
		};

		static Button Loudness	= {
			OBJ_BUTTON,
			&Enhanced,
			&TrebVolume,
			&VolumeButtons,
			15,48,15,58,
			15,60,15,60,
			"LOUDNESS ",
			0
		};

		static Button Enhanced = {
			OBJ_BUTTON,
			0,
			&Loudness,
			&VolumeButtons,
			15,63,15,72,
			15,74,15,74,
			"ENHANCED ",
			0
		};


	/*\
	|*| effects dialog box structures
	\*/

		static Scroll OutputMixerScroll = {
			OBJ_SCROLL,
			&Left2Left,
			0,
			&ScrollObjectHandler,
			0,
			5,26,6,31,
			5,35,6,50,
			 0, 0, 0, 0,
			"MIXER ",
			0,
			0,
			0,0,0,0
		};

		static Button Left2Left = {
			OBJ_BUTTON,
			&Left2Right,
			&OutputMixerScroll,
			&EffectsButtons,
			12,26,12,33,
			12,35,12,43,
			"Left to Left ",
			0
		};

		static Button Left2Right = {
			OBJ_BUTTON,
			&Right2Left,
			&Left2Left,
			&EffectsButtons,
			13,26,13,33,
			13,35,13,43,
			"Left to Right ",
			0
		};

		static Button Right2Left = {
			OBJ_BUTTON,
			&Right2Right,
			&Left2Right,
			&EffectsButtons,
			14,26,14,33,
			14,35,14,43,
			"Right to Left ",
			0
		};

		static Button Right2Right = {
			OBJ_BUTTON,
			&RealSoundButton,
			&Right2Left,
			&EffectsButtons,
			15,26,15,33,
			15,35,15,43,
			"Right to Right ",
			0
		};

		static Button RealSoundButton = {
			OBJ_BUTTON,
			0,
			&Right2Left,
			&EffectsButtons,
			19,26,19,49,
			19,52,19,52,
			" ",
			0
		};

		static int OrigRow; 		   // original row position
		static int OrigCol; 		   // original column position

		static char CommandString[80]; // text buffer that holds commands to MVPROAS
		static char MVResponse[80];    // text buffer that holds the MVPROAS responses

		static int screenbuffer[2048]; // screen backup buffer

#define MAIN_DLG		0x0001	// main screen dialog boxes
#define EFFECTS_DLG 	0x0002	// recording effects dialog box

		static int DialogBox=MAIN_DLG; // bit field indicating witch dialog box is up


		typedef struct {

			int Filler; 		// all other screen area

            int BkGn_AND;       // background AND mask
			int BkGn_XOR;		// background XOR mask

            int Shdw_AND;       // shadow AND mask
			int Shdw_XOR;		// shadow XOR mask

            int TmpHi_AND;      // temp High AND mask
			int TmpHi_XOR;		// temp high XOR mask

            int TmpLo_AND;      // temp low  AND mask
			int TmpLo_XOR;		// temp low  XOR mask

			int ScrTmpHi_AND;	// temp High AND mask
			int ScrTmpHi_XOR;	// temp high XOR mask

			int ScrTmpLo_AND;	// temp low  AND mask
			int ScrTmpLo_XOR;	// temp low  XOR mask

            int ButLo_AND;      // button low AND mask
			int ButLo_XOR;		// button low XOR mask

            int ButHi_AND;      // button high AND mask
			int ButHi_XOR;		// button high XOR mask

		} scheme, *SCMPtr;

		static scheme MonoScheme = {   // attribute control for MONO screens

			0x0e,				// full bright

			0x00,				// background AND mask
			0x07,				// background XOR mask

			0x77,				// shadow AND mask
			0x00,				// shadow XOR mask

			0x77,				// temp High AND mask
			0x08,				// temp high XOR mask

			0x77,				// temp low  AND mask
			0x00,				// temp low  XOR mask

			0x77,				// scroll temp High AND mask
			0x08,				// scroll temp high XOR mask

			0x77,				// scroll temp low	AND mask
			0x00,				// scroll temp low	XOR mask

			0x00,				// button low AND mask
			0x1f,				// button low XOR mask

			0x08,				// button high AND mask
			0x70,				// button high XOR mask

		};

		static scheme ColorScheme = {  // attribute control for COLOR screens

			0x09,				// bright blue background

            0x00,               // background AND mask
			0x30,				// background XOR mask

			0x00,				// shadow AND mask
			0x00,				// shadow XOR mask

			0x70,				// temp High AND mask
			0x0e,				// temp high XOR mask

			0x70,				// temp low  AND mask
			0x00,				// temp low  XOR mask

			0x70,				// scroll temp High AND mask
			0x0e,				// scroll temp high XOR mask

			0x70,				// scroll temp low	AND mask
			0x0a,				// scroll temp low	XOR mask

			0x00,				// button low AND mask
			0x37,				// button low XOR mask

			0x00,				// button high AND mask
			0x3f,				// button high XOR mask

		};

		static scheme HelpsColorScheme = {	// attribute control for COLOR screens

			0x09,				// bright blue

            0x00,               // background AND mask
			0x20,				// background XOR mask

			0x00,				// shadow AND mask
			0x00,				// shadow XOR mask

			0x77,				// temp High AND mask
			0x08,				// temp high XOR mask

			0x77,				// temp low  AND mask
			0x00,				// temp low  XOR mask

			0x77,				// scroll temp High AND mask
			0x08,				// scroll temp high XOR mask

			0x77,				// scroll temp low	AND mask
			0x00,				// scroll temp low	XOR mask

			0x08,				// button low AND mask
			0x17,				// button low XOR mask

			0x08,				// button high AND mask
			0x70,				// button high XOR mask

		};

		static SCMPtr  Colors = &ColorScheme;  // default to color adapber scheme


	/*\
	|*|----====< more prototypes >====----
	\*/

		int 		   MixerDialogInit	   ( );
		int 		   MixerDialogHalt	   ( );
		static int	   BroadcastMsg 	   ( int );
		static int	   BroadcastToLlist    ( int, ObjectList * );
		static void    DrawScreen		   ( rect *, char *([]) );
		static int	   GetEvent 		   ( EPtr );
		static void    MatchObj 		   ( EPtr );
		static void    PaintScreen		   ( int );
		static int	   PtInRect 		   ( point *,rect * );
		static int	   SendMixer		   ( char *, int, int, SPtr, int, int );
		static int	   SendVolume		   ( char *, int, VPtr, int, int );

		static int	   SystemKey		   ( EPtr );
		static int	   SystemInit		   ( );
		static void    SystemShutDown	   ( );

		extern void    BackupVideo		   ( rect *, char far *, int, int );
		extern void    RestoreVideo 	   ( rect *, char far *, int, int );
		long extern    _videogetcurs	   ( );  /* ***** was near kdn */


	/*\
	|*|----------------==============================----------------
	|*|----------------====< Start of Execution >====----------------
	|*|----------------==============================----------------
	\*/

		static int exitcode = FALSE;	// our exit flag
		static int CallersFillChar; 	// Callers screen fill char (0) for none

MixerDialogBox(fill)
	int fill;
{
Event ev;
char *s;

	/* make the fill character available to the masses					*/

		s = EffectsString;
		if ((CallersFillChar = fill) != 0)
			while (*s) *s++ = fill;

	/* initialize the hardware. 										*/

		SystemInit();

	/* go forever														*/

		while (!exitcode) {

			/* if a keyboard/mouse action, the pass to handlers 		*/

				if (GetEvent (&ev)) {
					if (!SystemKey(&ev)) {
						if (CurrentObject)
							(*CurrentObject->scr)(OPEVENT,CurrentObject,&ev);
					}
				}

			/* if no action, just move the cross hairs					*/

				else
					UpdateTotalVolume();		/* changes via keyboard */
                    MatchObj ( &ev );           /* find a new object    */
		}

	/* exit back to caller												*/

		SystemShutDown();

	/* reset the exit code so we can process again...					*/

		exitcode=FALSE;

}


	/*\
	|*|----====< MixerGetSettings() >====----
	|*|
	|*| call the user back with the appropriate data
	|*|
	|*| Entry Conditions:
	|*| 	None
	|*|
	|*| Exit Conditions:
	|*| 	0 = okay, !0 = failure
	|*|
	\*/

MixerGetSettings(c)
	void (*c)();
{
SPtr o;
OLPtr l;

	/* start at the top & send to all objects, even the caller			*/

		l = &MainList;

		while (l) {

			o = l->head;

			while (o) {

				// send the message to the next object

					(*o->scr)(OPENINIT,o);

					// if the dead mixer, then send this string too!

                    if (o->type == OBJ_SCROLL) {
						SendMixer ("SET ",o->deadmixer,1,o,BI_SETTO,o->deadlchannel);
						(*c)(&CommandString);
						SendMixer ("SET ",o->deadmixer,2,o,BI_SETTO,o->deadrchannel);
                        (*c)(&CommandString);
					}
					(*o->scr)(SENDIT,o,c);

				// go to next object

					o = o->next;
			}

			l = l->nextlist;
		}
}


	/*\
	|*|----====< MixerDialogInit() >====----
	|*|
	|*| Perform any startup needed
	|*|
	|*| Entry Conditions:
	|*| 	None
	|*|
	|*| Exit Conditions:
	|*| 	0 = okay, !0 = failure
	|*|
	\*/
int MixerDialogInit()
{

    // open the device

		//if ((mv = open ("MVPROAS",O_RDWR, S_IREAD | S_IWRITE)) == -1) {

		if ((mv = open ("MVPROAS",O_RDWR )) == -1) {
		  //_ttyout ("\acannot open the MVPROAS device!\n",0);
			return(1);
		}

	// make an IBM PC right arrow appear in some text

		helpscreen[10][3]= 26;	/* used to be:		hackaline[3] = 26; */

	// everything is fine...

		return (0);

}


	/*\
	|*|----====< MixerDialogHalt() >====----
	|*|
	|*| Perform any other shutdown
	|*|
	|*| Entry Conditions:
	|*| 	None
	|*|
	|*| Exit Conditions:
	|*| 	0 = okay, !0 = failure
	|*|
	\*/
int  MixerDialogHalt()
{

	// close down the file handle

		close (mv);

	// everything is fine...

		return (0);

}


	/*\
	|*|--------------------=======================--------------------
	|*|--------------------====< Subroutines >====--------------------
	|*|--------------------=======================--------------------
	\*/


	/*\
	|*|----====< void BroadcastMsg ( int ) >====----
	|*|
	|*|    This routine broadcasts a message to all objects.
	|*|
	\*/
static int BroadcastMsg (msg)
	int msg;
{
SPtr o;

	/* send to all linked lists of objects								*/

		BroadcastToList (msg,&MainList);
		BroadcastToList (msg,&EffectsList);
}


    /*\
	|*|----====< void BroadcastToList ( int, ObjectList * ) >====----
	|*|
	|*|    This routine broadcasts a message to all objects.
	|*|
	\*/
static int BroadcastToList (msg,l)
	int msg;
	 ObjectList *l;
{
SPtr o;

	/* start at the top & send to all objects, even the caller			*/

		o = l->head;

		while (o) {

			/* send the message to the next object						*/

				(*o->scr)(msg,o);

			/* go to next object										*/

				o = o->next;
		}
}


#if 0
	/*\
	|*|----====< void DupTheEvent ( EPtr, EPtr ); >====----
	|*|
	|*|    Duplicate our event record.
	|*|
	\*/
		void	DupTheEvent 		( EPtr, EPtr );
static void DupTheEvent(src,dst)
	EPtr src,dst;
{

	/* duplicate the event using an intrinsic function					*/

		memcpy
		  (
			(char *) dst,
			(char *) src,
			sizeof (Event)
		  );
}
#endif


	/*\
	|*|----====< int EffectsButtons() >====----
	|*|
	|*| Cross Channel object control
	|*|
	|*| Entry Conditions:
	|*| 	None
	|*|
	|*| Exit Conditions:
	|*| 	None
	|*|
	\*/
static int EffectsButtons(msg,o,ptr)
	int msg;
	BPtr o;
	void *ptr;
{
void **pptr;
rect r;
int l,ri,i;
char c1,c2;

	// get a pointer to the stack

		pptr = &ptr;

    // process the message

        switch (msg) {

			case OPEVENT:

                switch (((EPtr)pptr[0])->buttons) {

					case ENTER:

						// ENTER here can toggle the buttons

					   o->state = ((o->state) ? FALSE : TRUE );

						(*o->scr) (DRAWIT,o);

						if (o == &RealSoundButton)
							SendOnOff ("SET ","REALSOUND ",o);
						else
							SendOnOff ("SET ","CROSS ",o);

                        break;

					default:
                        break;
				}

				break;

			case SENDIT:

				if (o == &RealSoundButton)
					SendOnOff ("SET ","REALSOUND ",o);
				else
					SendOnOff ("SET ","CROSS ",o);

                (*(void(*)())ptr)(&CommandString);

                break;

			case FOCUS_GIVEN:

				// highlight the entire field

					if (o == &RealSoundButton) {

					  //r.row2 = o->namr.row2;
					  //r.row1 = o->namr.row1;
					  //r.col2 = r.col1 = o->namr.col2+2;
					  //_videoattr ( Colors->TmpHi_AND,    Colors->TmpHi_XOR,	 &r 	  );
						_videoattr ( Colors->TmpHi_AND,    Colors->TmpHi_XOR,	 &o->namr );
						_videoattr ( Colors->ScrTmpHi_AND, Colors->ScrTmpHi_XOR, &o->scrr );

					}
					else {

						r.row1 = o->scrr.row1;
						r.row2 = o->scrr.row2;
						r.col2 = (r.col1 = o->scrr.col2+2) + 7;

						_videoattr ( Colors->TmpHi_AND,    Colors->TmpHi_XOR,	 &o->namr );
						_videoattr ( Colors->TmpHi_AND,    Colors->TmpHi_XOR,	 &r );
						_videoattr ( Colors->ScrTmpHi_AND, Colors->ScrTmpHi_XOR, &o->scrr );
					}

					break;

			case FOCUS_TAKEN:

				// remove the highlight from the entire field

					if (o == &RealSoundButton) {

					  //r.row1 = o->namr.row1;
					  //r.row2 = o->namr.row2;
					  //r.col2 = r.col1 = o->namr.col2+2;
					  //_videoattr ( Colors->TmpLo_AND,    Colors->TmpLo_XOR,	 &r 	  );
						_videoattr ( Colors->TmpLo_AND,    Colors->TmpLo_XOR,	 &o->namr );
						_videoattr ( Colors->ScrTmpLo_AND, Colors->ScrTmpLo_XOR, &o->scrr );

					}
					else {
						r.row1 = o->scrr.row1;
						r.row2 = o->scrr.row2;
						r.col2 = (r.col1 = o->scrr.col2+2) + 7;

						_videoattr ( Colors->TmpLo_AND,    Colors->TmpLo_XOR,	 &o->namr );
						_videoattr ( Colors->ScrTmpLo_AND, Colors->ScrTmpLo_XOR, &o->scrr );
						_videoattr ( Colors->TmpLo_AND,    Colors->TmpLo_XOR,	 &r );
                    }

					break;

			case DRAWIT:

				// move the cursor to the slide bar area

					if (o == &RealSoundButton) {

						_videosetcurs ( o->scrr.row1, o->scrr.col1 );

                        if (o->state)
							_zipout   ( "X" );
						else
							_zipout   ( "ù" );
					}

                    else {

						_videosetcurs ( o->scrr.row1, o->scrr.col1 );

                        if (o->state)
							_zipout ( channelconnect, 0);
						else
							_zipout ( channeldisconnect, 0);
					}

                break;

			case OPENINIT:

				// get the output mixer current state

					if (o == &RealSoundButton) {

						SendOnOff ("GET ","REALSOUND ",o);

						o->state = (MVResponse[0] == '+') ? TRUE : FALSE;

					}
					else {

						SendOnOff ("GET ","CROSS ",o);

						DecodeCrossChannel (MVResponse);

						if (o == &Left2Left)
							o->state = Left2LeftState;

						if (o == &Left2Right)
							o->state = Left2RightState;

						if (o == &Right2Left)
							o->state = Right2LeftState;

						if (o == &Right2Right)
							o->state = Right2RightState;
					}

					break;

			case CLEARIT:
			default:
				break;
		}
}



	/*\
	|*|----====< int EqualizerLevels() >====----
	|*|
	|*| BASS/TREBLE slide bar object control
	|*|
	|*| Entry Conditions:
	|*| 	None
	|*|
	|*| Exit Conditions:
	|*| 	None
	|*|
	\*/
static int EqualizerLevels(msg,o,ptr)
	int msg;
	VPtr o;
	void *ptr;
{
void **pptr;
rect r;
int l,ri,i;
char c1,c2;

	// get a pointer to the stack

		pptr = &ptr;

    // process the message

        switch (msg) {

			case OPEVENT:

                switch (((EPtr)pptr[0])->buttons) {

					case ENDKEY:
					case HOMEKEY:
                    case LFARROW:

						if (o->channel > 0)
							o->channel -= 4;

						SendVolume
						  (
							"SET ",
							0,
							o,
							BI_SETTO,
							o->channel
						  );

                        (*o->scr) (DRAWIT,o);

						break;

					case PGDNKEY:
					case PGUPKEY:
					case RIARROW:

						if (o->channel < 100)
							o->channel += 4;

						SendVolume
						  (
							"SET ",
							0,
							o,
							BI_SETTO,
							o->channel
						  );

                        (*o->scr) (DRAWIT,o);

						break;

					case ENTER:

						// ENTER here can toggle the buttons

                        if (o == &BassVolume)
							(*Loudness.scr)(msg,&Loudness,ptr);

						if (o == &TrebVolume)
							(*Enhanced.scr)(msg,&Enhanced,ptr);
						break;

					default:
                        break;
				}

				break;

			case SENDIT:

				SendVolume
				  (
					"SET ",
					0,
					o,
					BI_SETTO,
					o->channel
				  );
				(*(void(*)())ptr)(&CommandString);

				break;

			case FOCUS_GIVEN:

				// highlight the entire field

                    r.row1 = o->namr.row1;
                    r.row2 = o->namr.row2;
					r.col2 = r.col1 = o->namr.col2+2;
					_videoattr ( Colors->TmpHi_AND,    Colors->TmpHi_XOR,	 &o->namr );
					_videoattr ( Colors->TmpHi_AND,    Colors->TmpHi_XOR,	 &r 	  );

					r.row1 = o->scrr.row1;
					r.row2 = o->scrr.row2;
					r.col2 = r.col1 = o->scrr.col2+2;
                    _videoattr ( Colors->ScrTmpHi_AND, Colors->ScrTmpHi_XOR, &o->scrr );
                    _videoattr ( Colors->ScrTmpHi_AND, Colors->ScrTmpHi_XOR, &r       );

					break;

			case FOCUS_TAKEN:

				// highlight the entire field

					r.row1 = o->namr.row1;
                    r.row2 = o->namr.row2;
					r.col2 = r.col1 = o->namr.col2+2;
                    _videoattr ( Colors->TmpLo_AND,    Colors->TmpLo_XOR,    &o->namr );
                    _videoattr ( Colors->ScrTmpLo_AND, Colors->ScrTmpLo_XOR, &r       );

                    r.row1 = o->scrr.row1;
					r.row2 = o->scrr.row2;
					r.col2 = r.col1 = o->scrr.col2+2;
                    _videoattr ( Colors->ScrTmpLo_AND, Colors->ScrTmpLo_XOR, &o->scrr );
					_videoattr ( Colors->ScrTmpLo_AND, Colors->ScrTmpLo_XOR, &r 	  );

					break;

			case DRAWIT:

				// move the cursor to the slide bar area

					_videosetcurs ( o->scrr.row1, o->scrr.col1 );
					_zipout 	  ( twobar, 0);

					l = (o->channel * 31) / 100;
					_videosetcurs ( o->scrr.row1, o->scrr.col1+(l>>1) );
					_zipout (((l & 1) ? "Þ" : "Ý"), 0);

					break;

			case OPENINIT:

				// get the output mixer current state

					SendVolume ("GET ",0,o,0,0);
					DecodeVolumeNumber (MVResponse);
					o->channel = VolumeNumber;

					break;

			case CLEARIT:
			default:
				break;
		}
}


	/*\
	|*|----====< int GetEvent ( EPtr ) >====----
	|*|
	|*|   This routine fetches the next event from the mouse driver
	|*|
	|*| This routine is used by permission from Douglas S. Cody
	|*| Douglas S. Cody, Copyright 1989,1990 (c), All Rights Reserved.
	|*|
	\*/
static int GetEvent (e)
	EPtr e;
{

	// if there is a key waiting...

		if (_bios_keybrd( _KEYBRD_READY )) {

			// ...return it

			e->type = EV_KEYB;
			return (e->buttons =  _bios_keybrd ( _KEYBRD_READ ));
		}

	// ...else return 0

        return( 0 );

}


	/*\
	|*|----====< void MatchObj(EPtr) >====----
	|*|
	|*|    This routine matches the mouse pointer to a screen object
	|*|
	\*/
static void MatchObj(e)
	EPtr e;
{
SPtr o;

#if 0
	// if this is a mouse type, match it to the list

		if (e->type == EV_MOUS) {

			// if point is still within the current object, just return

				if (PtInRect ((point *)&e->vpos,&CurrentObject->scrr) ||
					PtInRect ((point *)&e->vpos,&CurrentObject->scrr))
						return;

			// we must take the focus away so we don't send bogus mouse events

				(*CurrentObject->scr) (FOCUS_TAKEN,CurrentObject);

			// the mouse is pointing to something else. try to find it

				o = CurrList->head;
				while (o) {

					if (PtInRect ((point *)&e->vpos,&o->scrr) ||
						PtInRect ((point *)&e->vpos,&o->swir)) {

						// focus given if the object takes mouse events

						CurrentObject = o;
						(*o->scr) (FOCUS_GIVEN,o);
						break;

					}
					o = o->next;
				}
		}
#endif
}


	/*\
	|*|----====< void DrawScreen (rect *, char *argv[]) >====----
	|*|
	|*| Perform the screen draw for this screen
	|*|
	|*| Entry Conditions:
	|*| 	2 parms
	|*|
	|*| Exit Conditions:
	|*| 	None
	|*|
	\*/
static void DrawScreen (r,txt)
	rect *r;
	char *txt[];
{
rect wr;
int x;

	// inner box color

		wr.row1 = r->row1;
		wr.row2 = r->row2;
		wr.col1 = r->col1;
		wr.col2 = r->col2-2;
		_videoattr (Colors->BkGn_AND,Colors->BkGn_XOR,&wr);

	// vertical right side shadow

		wr.row1 = r->row1+1;
		wr.row2 = r->row2+1;
		wr.col1 = r->col2-1;
		wr.col2 = r->col2+0;
		_videoattr (Colors->Shdw_AND,Colors->Shdw_XOR,&wr);

	// horizontal bottom shadow

		wr.row1 = wr.row2;
		wr.col1 = r->col1+2;
		_videoattr (Colors->Shdw_AND,Colors->Shdw_XOR,&wr);

	// actual text

		if (txt) {
			for (x=r->row1;x<=r->row2;x++) {
				_videosetcurs (x,r->col1);
				_zipout (txt[x-r->row1],0);
			}
        }

	// change some attributes on the color screen

		if (VideoSegment == 0xb800)
			ChangeAttributes (r->row1, r->col1, r->row2, r->col2-2);
}


	/*\
	|*|----====< PaintScreen (int) >====----
	|*|
	|*| Draw/Restore the screen
	|*|
	|*| Entry Conditions:
	|*| 	INT is a TRUE/FALSE flag
	|*|
	|*| Exit Conditions:
	|*| 	None
	|*|
	\*/
static void PaintScreen (tf)
	int tf;
{
rect r;

static int backedup = FALSE;

	if (tf) {			// paint the screen

		if (!backedup)
			BackupVideo ( &OurWnd.wndr, (char far *)&screenbuffer[0], 0, 0 );

		backedup = TRUE;

		if (CallersFillChar)
			_videofill ( CallersFillChar, Colors->Filler, &OurWnd.wndr );

		DrawScreen ( &MixerRect,  &screenlayout[0] );
		r.row2 = r.row1 = MixerRect.row1+1;
		r.col1 = MixerRect.col1+1;
		r.col2 = MixerRect.col2-3;
		_videoattr ( Colors->ButHi_AND, Colors->ButHi_XOR,&r);

		DrawScreen ( &RecordRect, &screen3[0] );
		DrawScreen ( &VolRect,	  &screen2[0] );
		DrawScreen ( &Help2Rect,  &screen4[0] );

    }

	else {
		if (backedup) {
			RestoreVideo ( &OurWnd.wndr, (char far *) &screenbuffer[0], 0, 0 );
			backedup = FALSE;
		}
	}
}


	/*\
	|*|----====< ProcessRecordEffects (int) >====----
	|*|
	|*| Process the Effects dialog box
	|*|
	|*| Entry Conditions:
	|*| 	None
	|*|
	|*| Exit Conditions:
	|*| 	None
	|*|
	\*/
static void ProcessRecordEffects(msg)
	int msg;
{
rect r;

static void *ol;
static void *effectslastobj;
static void *effectscurrobj;

		switch (msg) {

			case DRAWIT:
				DrawScreen ( &EffectsRect, &effectsbkgn[0] );
				DrawScreen ( &Screen6Rect, &screen6[0]	   );
				DrawScreen ( &Screen5Rect, &screen5[0]	   );
				DrawScreen ( &Screen7Rect, &screen7[0]	   );
				BroadcastToList (DRAWIT,CurrList);	// draw the controls
				break;

			case FOCUS_GIVEN:

				// let the current object know its not in foreground

					(*CurrentObject->scr)(FOCUS_TAKEN,CurrentObject);

				// switch linked lists and current objects

					ol = CurrList;					// switch object lists
					CurrList = &EffectsList;

					effectslastobj = CurrentObject; // switch objects
					if ((CurrentObject = effectscurrobj) == 0)
						CurrentObject = CurrList->head;

				// paint the screen

					ProcessRecordEffects(DRAWIT);

					(*CurrentObject->scr)(FOCUS_GIVEN,CurrentObject); // highlight

				// all done till a FOCUS_TAKEN is received

					break;

			case FOCUS_TAKEN:

				// let the current object know its not in foreground

					(*CurrentObject->scr)(FOCUS_TAKEN,CurrentObject);

				// switch linked lists and current objects

					CurrList = ol;					// restore the old list

					effectscurrobj = CurrentObject; // switch objects
					if (effectslastobj)
						CurrentObject  = effectslastobj;

                // restore the screen

					PaintScreen 	(FALSE);			// restore the screen
					PaintScreen 	(TRUE); 			// put up the screen
					BroadcastToList (DRAWIT,CurrList);	// draw the controls

					(*CurrentObject->scr)(FOCUS_GIVEN,CurrentObject); // highlight

					break;

				default:
					break;

			}
}



	/*\
	|*|----====< PtInRect (point *, rect *) >====----
	|*|
	|*| return TRUE if point is within the rectangle
	|*|
	|*| Entry Conditions:
	|*| 	None
	|*|
	|*| Exit Conditions:
	|*| 	None
	|*|
	\*/
static int PtInRect (p,r)
	point *p;
	rect  *r;
{

	if ((p->row < r->row1) && (p->row >= r->row2))
		return(FALSE);
	if ((p->col < r->col1) && (p->col >= r->col2))
		return(FALSE);

	return (TRUE);
}


	/*\
	|*|----====< int ScrollObjectHandler() >====----
	|*|
	|*| Scroll bar object control
	|*|
	|*| Entry Conditions:
	|*| 	None
	|*|
	|*| Exit Conditions:
	|*| 	None
	|*|
	\*/
static int ScrollObjectHandler (msg,o,ptr)
	int msg;
	SPtr o;
	void *ptr;
{
void **pptr;
rect r;
int l,ri,i,key;
char c1,c2;

	// get a pointer to the stack

		pptr = &ptr;

    // process the message

        switch (msg) {

			case OPEVENT:

				key = 0;

                switch (((EPtr)pptr[0])->buttons) {

					case ENDKEY:				// right = 2;
                        key++;
					case HOMEKEY:				// left  = 1;
						key++;
					case LFARROW:				// both  = 0;

						if (key != 2) { 		// do LEFT on left/both cases

							if (o->leftchannel > 0)
								o->leftchannel -= 3;

							SendMixer
							  (
								"SET ",
								o->mixerselect,
								1,
								o,
								BI_SETTO,
								o->leftchannel
							  );

						}

						if (key != 1) { 		// do RIGHT on right/both cases

							if (o->ritchannel > 0)
								o->ritchannel -= 3;

							SendMixer
							  (
								"SET ",
								o->mixerselect,
								2,
								o,
								BI_SETTO,
								o->ritchannel
							  );

						}

                        (*o->scr) (DRAWIT,o);

						break;

					case PGDNKEY:				// right = 2;
                        key++;
                    case PGUPKEY:               // left  = 1;
                        key++;
					case RIARROW:				// both  = 0;

                        if (key != 2) {         // do LEFT on left/both cases

							if (o->leftchannel < 100)
								o->leftchannel += 3;

							SendMixer
							  (
								"SET ",
								o->mixerselect,
								1,
								o,
								BI_SETTO,
								o->leftchannel
							  );
						}

                        if (key != 1) {         // do RIGHT on right/both cases

							if (o->ritchannel < 100)
								o->ritchannel += 3;

							SendMixer
							  (
								"SET ",
								o->mixerselect,
								2,
								o,
								BI_SETTO,
								o->ritchannel
							  );
						}

						(*o->scr) (DRAWIT,o);

						break;

					default:
                        break;
				}

				break;

			case SENDIT:

				SendMixer ("SET ",o->mixerselect,1,o,BI_SETTO,o->leftchannel);
				(*(void(*)())ptr)(&CommandString);
				SendMixer ("SET ",o->mixerselect,2,o,BI_SETTO,o->ritchannel);
				(*(void(*)())ptr)(&CommandString);
				break;

			case FOCUS_GIVEN:

				// highlight the entire field

                    r.row1 = o->namr.row1;
                    r.row2 = o->namr.row2;
					r.col2 = r.col1 = o->namr.col2+2;
					_videoattr ( Colors->TmpHi_AND,    Colors->TmpHi_XOR,	 &o->namr );
					_videoattr ( Colors->TmpHi_AND,    Colors->TmpHi_XOR,	 &r 	  );

					r.row1 = o->scrr.row1;
					r.row2 = o->scrr.row2;
					r.col2 = r.col1 = o->scrr.col2+2;
					_videoattr ( Colors->ScrTmpHi_AND, Colors->ScrTmpHi_XOR, &o->scrr );
                    _videoattr ( Colors->ScrTmpHi_AND, Colors->ScrTmpHi_XOR, &r       );

                    if (o->swi)
						_videoattr ( Colors->TmpHi_AND,    Colors->TmpHi_XOR,	 &o->swir );

					break;

			case FOCUS_TAKEN:

				// highlight the entire field

					r.row1 = o->namr.row1;
                    r.row2 = o->namr.row2;
					r.col2 = r.col1 = o->namr.col2+2;
					_videoattr ( Colors->TmpLo_AND,    Colors->TmpLo_XOR,	 &o->namr );
                    _videoattr ( Colors->ScrTmpLo_AND, Colors->ScrTmpLo_XOR, &r       );

                    r.row1 = o->scrr.row1;
					r.row2 = o->scrr.row2;
					r.col2 = r.col1 = o->scrr.col2+2;
					_videoattr ( Colors->ScrTmpLo_AND, Colors->ScrTmpLo_XOR, &o->scrr );
					_videoattr ( Colors->ScrTmpLo_AND, Colors->ScrTmpLo_XOR, &r 	  );

					if (o->swi)
						_videoattr ( Colors->TmpLo_AND,    Colors->TmpLo_XOR,	 &o->swir );

					break;

			case DRAWIT:

				// move the cursor to the slide bar area

					_videosetcurs ( o->scrr.row1, o->scrr.col1 );
					_zipout 	  ( twobar, 0);
					_videosetcurs ( o->scrr.row1+1, o->scrr.col1 );
					_zipout 	  ( twobar, 0);

					l = (o->leftchannel * 31) / 100;
					_videosetcurs ( o->scrr.row1, o->scrr.col1+(l>>1) );
					_zipout (((l & 1) ? "Þ" : "Ý"), 0);
					l = (o->ritchannel * 31) / 100;
					_videosetcurs ( o->scrr.row1+1, o->scrr.col1+(l>>1) );
					_zipout (((l & 1) ? "Þ" : "Ý"), 0);

				// display a mixer bar

					if (o->swi) {

					  //if (o->mixerselect == BI_INPUTMIXER)
					  //	_videoattr (Colors->ButHi_AND,Colors->ButHi_XOR,&o->swir);
					  //else
					  //	_videoattr (Colors->ButLo_AND,Colors->ButLo_XOR,&o->swir);

						if (o->mixerselect == BI_INPUTMIXER) {
							_videosetcurs (o->swir.row1,o->swir.col1);
							_zipout (playmsgi1);
							_videosetcurs (o->swir.row1+1,o->swir.col1);
							_zipout (playmsgi2);
						}
						else {
							_videosetcurs (o->swir.row1,o->swir.col1);
							_zipout (playmsgo1);
							_videosetcurs (o->swir.row1+1,o->swir.col1);
							_zipout (playmsgo2);
						}
                    }

                break;

			case OPENINIT:

				// get the output mixer current state

				SendMixer ("GET ",BI_OUTPUTMIXER,0,o,0,0);
                DecodeMixer (MVResponse);

                if (leftswitch == '+') {
					o->mixerselect	= BI_OUTPUTMIXER;
					o->leftchannel	= leftvolume;
                }
				else {
					o->deadmixer	= BI_OUTPUTMIXER;
					o->deadlchannel = leftvolume;
				}

				if (ritswitch == '+') {
					o->mixerselect	= BI_OUTPUTMIXER;
					o->ritchannel	= ritvolume;
				}
				else {
					o->deadmixer	= BI_OUTPUTMIXER;
					o->deadrchannel = ritvolume;
				}

				// get the input mixer current state

				SendMixer ("GET ",BI_INPUTMIXER,0,o,0,0);
				DecodeMixer (MVResponse);

                if (leftswitch == '+') {
					o->mixerselect	= BI_INPUTMIXER;
					o->leftchannel	= leftvolume;
                }
				else {
					o->deadmixer	= BI_INPUTMIXER;
					o->deadlchannel = leftvolume;
				}

                if (ritswitch == '+') {
					o->mixerselect	= BI_INPUTMIXER;
					o->ritchannel	= ritvolume;
				}
				else {
					o->deadmixer	= BI_INPUTMIXER;
					o->deadrchannel = ritvolume;
				}

				// make sure both inputs are on the same mixer

				SendMixer ("SET ",o->mixerselect,1,o,BI_SETTO,o->leftchannel);
				SendMixer ("SET ",o->mixerselect,2,o,BI_SETTO,o->ritchannel);
				break;

			case CLEARIT:
				break;

            default:
				break;
		}
}


	/*\
	|*|----====< int SendOnOff( char *, char *, int, VPtr, int, int ) >====----
	|*|
	|*| Send a mixer selection
	|*|
	|*| Entry Conditions:
	|*| 	char *cmd = "SET", "GET"
	|*| 	VPtr o	  = Volume device name
	|*| 	int  v	  = new volume setting
    |*|
	|*| Exit Conditions:
	|*| 	MVResponse string holds the result
	|*|
	\*/
static int SendOnOff(cmd,dev,o)
	char *cmd;	// command
	char *dev;	// device
	BPtr o; 	// channel name & state

{

    // build the string

		strcpy (CommandString,cmd); 	   // command
		strcat (CommandString,dev); 	   // device name
		strcat (CommandString,o->name);    // input channel
		strcat (CommandString,((o->state) ? "ON" : "OFF"));

		SendTextOut (CommandString);
}


	/*\
	|*|----====< int SendMixer( char *, int, int, SPtr, int, int ) >====----
	|*|
	|*| Send a mixer selection
	|*|
	|*| 	char *cmd = "SET", "GET"
	|*| 	int  dev  = INPUTMIXER or OUTPUTMIXER
    |*|     int  lr   = both(0),left(1),right(2)
	|*| 	VPtr o	  = Volume device name
	|*| 	int  f	  = function (UP/DOWN/TO)
	|*| 	int  v	  = new volume setting
    |*|
	|*| Exit Conditions:
	|*| 	MVResponse string holds the result
	|*|
    \*/
static int SendMixer (cmd,dev,lr,o,f,v)
	char *cmd;	// command
	int dev;	// device name
	int lr; 	// left/right/both
	SPtr o; 	// input channel name
	int f;		// movement function
	int v;		// new mixer value

{
char *fs,*ms,*lrs;
char val[7];

	// get the text string equvalent for the function

		switch (lr) {

			case 1:
				lrs = "LEFT ";  // LEFT side only
				break;

			case 2:
				lrs = "RIGHT "; // RIGHT side only
                break;

			case 0: 			// BOTH
			default:
				lrs = makemenull;
                break;
		}

	// get the text string equvalent for the function

		switch (f) {
			case BI_SETTO:
				fs = "TO ";
				break;

            case BI_UPTO:
				fs = "UP ";
				break;

            case BI_DOWNTO:
				fs = "DOWN ";
                break;

			default:
				fs = makemenull;
                break;
		}

	// correct the value

		if (v < 0)	 v = 0;
		if (v > 100) v = 100;

	// build the string

		ms = (dev == BI_OUTPUTMIXER) ? "OUTPUT MIXER " : "INPUT MIXER ";
		itoa   (v,val,10);

		strcpy (CommandString,cmd); 		 // command
		strcat (CommandString,ms);			 // device name
		strcat (CommandString,lrs); 		 // left/right
		strcat (CommandString,o->name); 	 // input channel
		strcat (CommandString,fs);			 // function (up/down/to)
		strcat (CommandString,val); 		 // new #
		strcat (CommandString," PERCENT");

		SendTextOut (CommandString);
}


	/*\
	|*|----====< int SendVolume( char *, int, VPtr, int, int ) >====----
	|*|
	|*| Send a mixer selection
	|*|
	|*| Entry Conditions:
	|*| 	char *cmd = "SET", "GET"
	|*| 	int  lr   = both(0),left(1),right(2)
	|*| 	VPtr o	  = Volume device name
	|*| 	int  f	  = function (UP/DOWN/TO)
	|*| 	int  v	  = new volume setting
    |*|
	|*| Exit Conditions:
	|*| 	MVResponse string holds the result
	|*|
	\*/
static int SendVolume (cmd,lr,o,f,v)
	char *cmd;	// command
	int lr; 	// left/right/both
	VPtr o; 	// channel name
	int f;		// movement function
	int v;		// new mixer value

{
char *fs,*ms,*lrs;
char val[7];

    // get the text string equvalent for the function

		switch (lr) {

			case 1:
				lrs = "LEFT ";  // LEFT side only
				break;

			case 2:
				lrs = "RIGHT "; // RIGHT side only
                break;

			case 0: 			// BOTH
			default:
				lrs = makemenull;
                break;
		}

	// get the text string equvalent for the function

		switch (f) {
			case BI_SETTO:
				fs = "TO ";
				break;

            case BI_UPTO:
				fs = "UP ";
				break;

            case BI_DOWNTO:
				fs = "DOWN ";
                break;

			default:
				fs = makemenull;
                break;
		}

	// correct the value on slide bars

        if (o->type == OBJ_VOLUME) {

			// fudge the volume to correct the rounding errors

			if (++v < 0)   v = 0;
			if (v > 100) v = 100;
		}

    // build the string

		itoa   (v,val,10);

		strcpy (CommandString,cmd); 		 // command
		strcat (CommandString,"VOLUME ");    // device name
		strcat (CommandString,lrs); 		 // left/right
		strcat (CommandString,o->name); 	 // input channel
		strcat (CommandString,fs);			 // function (up/down/to)

		if (o->type == OBJ_VOLUME) {
			strcat (CommandString,val); 	 // new #
			strcat (CommandString," PERCENT");
		}
		else
			strcat (CommandString,((v) ? "ON" : "OFF"));

		SendTextOut (CommandString);
}


	/*\
	|*|----====< int SendTextOut( char * ) >====----
	|*|
	|*| Send a text string to MVPROAS
	|*|
	|*| Entry Conditions:
	|*| 	None
	|*|
	|*| Exit Conditions:
	|*| 	None
	|*|
	\*/
static int SendTextOut(s)
	char *s;
{
char *b;

		b = s;
		while (*b++) ;
		write (mv,s,b-s);

		_dlgflushfile(mv);	// the below code is now in dialoga.asm
		//	_asm {
		//		mov 	bx,mv
		//		mov 	ah,45h
		//		int 	21h
		//		jnc 	thisisbad
		//		mov 	bx,ax
		//		mov 	ah,3eh
		//		int 	21h
		//	thisisbad:
		//	}

	   read (mv,MVResponse,80);

			{
			char *m= MVResponse;

			do
				if (*m == '\n')
					*m= ' ';
			while (*++m);
			do
				if (*m == ' ')
					*m--= '\0';
				else
					m--;
			while (isspace(*m));
			}

}


	/*\
	|*|----====< int SwitchObjectHandler() >====----
	|*|
	|*| mixer selection switch object control
	|*|
	|*| Entry Conditions:
	|*| 	None
	|*|
	|*| Exit Conditions:
	|*| 	None
	|*|
	\*/

static int SwitchObjectHandler(msg,o,ptr)
    int msg;
	SPtr o;
	void *ptr;
{
void **pptr;

	// get a pointer to the stack

		pptr = &ptr;

	// process the message

        switch (msg) {

            case OPEVENT:

				switch (((EPtr)pptr[0])->buttons) {

					case ENTER:

						// toggle the input/output mixer selection

						if (o == &DigitalScroll)
							break;

						if (o->mixerselect == BI_OUTPUTMIXER)
							o->mixerselect = BI_INPUTMIXER;
						else
							o->mixerselect = BI_OUTPUTMIXER;

						SendMixer
						  (
							"SET ",
							o->mixerselect,
							1,
							o,
							BI_SETTO,
							o->leftchannel
						  );

						SendMixer
						  (
							"SET ",
							o->mixerselect,
							2,
							o,
							BI_SETTO,
							o->ritchannel
						  );

                        (*o->scr) (DRAWIT,o);

					default:
						break;
                }
				break;

			case SENDIT:
			case FOCUS_GIVEN:
			case FOCUS_TAKEN:
			case DRAWIT:
			case CLEARIT:
			case OPENINIT:
			default:
				break;
		}
}



	/*\
	|*|----====< int  SystemKey(EPtr); >====----
	|*|
	|*|    Check the event for a system keystroke.
	|*|
	\*/
static int	SystemKey(e)
	EPtr e;
{
Event ev;
rect r;
OLPtr ol;

	// exit if not a keyboard event

		if (e->type != EV_KEYB)
			return(0);

	// process it...

		switch (e->buttons) {

			case ESCAPE:

				if (DialogBox & EFFECTS_DLG) {
					ProcessRecordEffects(FOCUS_TAKEN);
					DialogBox &= ~EFFECTS_DLG;
				}
				else
					exitcode = TRUE;

                return(1);

            case ENTER:
				if (CurrentObject->type == OBJ_SCROLL) {
					if (CurrentObject->swi)
						(*CurrentObject->swi) (OPEVENT,CurrentObject,e);
				}
				else
					(*CurrentObject->scr) (OPEVENT,CurrentObject,e);
                return(1);

			case HOMEKEY:
			case PGDNKEY:
			case ENDKEY:
			case PGUPKEY:
            case PLUSKEY1:
			case PLUSKEY2:
			case MINUSKEY1:
			case MINUSKEY2:
			case RIARROW:
			case LFARROW:
				(*CurrentObject->scr) (OPEVENT,CurrentObject,e);
				return(1);

			case SH_TABKEY:
			case UPARROW:

				(*CurrentObject->scr) (FOCUS_TAKEN,CurrentObject);

				if (CurrentObject->back)
					CurrentObject = CurrentObject->back;
				else
					CurrentObject = CurrList->tail;

				(*CurrentObject->scr) (FOCUS_GIVEN,CurrentObject);
                return(TRUE);

            case TABKEY:
            case DNARROW:

				(*CurrentObject->scr) (FOCUS_TAKEN,CurrentObject);

                if (CurrentObject->next)
					CurrentObject = CurrentObject->next;
				else
					CurrentObject = CurrList->head;

                (*CurrentObject->scr) (FOCUS_GIVEN,CurrentObject);
				return(TRUE);

			case F1KEY:

				// draw the help screen

					if (VideoSegment == 0xb800)
						Colors = &HelpsColorScheme;

					DrawScreen	( &Help1Rect, &helpscreen[0] );

                // do some special highlighting for enhanced looks

					if (VideoSegment == 0xb800) {

						r.row2 = ( r.row1 = Help1Rect.row1 + 1) + 1;
						r.col1 = Help1Rect.col1 + 1;
						r.col2 = Help1Rect.col2 - 3;

                        _videoattr
						  (
							0x00,
							0x3f,
							&r
						  );
					}

				// wait for a keystroke

					while (!GetEvent (&ev))  ;

                    if (VideoSegment == 0xb800)
						Colors = &ColorScheme;

				// restore the screen

					PaintScreen  (FALSE);	 // restore the screen
                    PaintScreen  (TRUE);     // put up the screen
					BroadcastToList (DRAWIT,&MainList); // draw the controls

					if (DialogBox & EFFECTS_DLG) {
						ProcessRecordEffects(DRAWIT);
						BroadcastToList (DRAWIT,&EffectsList);
					}

					(*CurrentObject->scr)(FOCUS_GIVEN,CurrentObject); // re-highlight
					return(TRUE);

			case F2KEY:

				// try to open and process the effects windows

					if (!(DialogBox & EFFECTS_DLG)) {

						// do not re-enter

							DialogBox |= EFFECTS_DLG;

							ProcessRecordEffects(FOCUS_GIVEN);
					}
					return(TRUE);

			case F5KEY:	/* F5-F8 load mixer settings from DOS file */
			case F6KEY: /* subtract F5 scancode from key pressed */
			case F7KEY: /* move high byte to low and use as index (0-3) */
			case F8KEY:
				loadcurrent((e->buttons- F5KEY)>> 8);	/* open, read setting[0-3].pas and SendTextOut */
				BroadcastMsg (OPENINIT); // tell everyone we are starting
				BroadcastToList (DRAWIT,CurrList);	 // draw the controls
				return(TRUE);

			case SF5KEY: /* Shift F5-F8 save mixer settings into DOS file */
			case SF6KEY: /* subtract SF5 scancode from key pressed */
			case SF7KEY: /* move high byte to load and use as index (0-3) */
			case SF8KEY:
				savecurrent((e->buttons- SF5KEY)>> 8); /* save setting[0-3].pas */
				return(TRUE);

			case F4KEY:

				// have all objects re-init themselves, then redraw

					SendTextOut  ("RESET");  // reset the mixers
					BroadcastMsg (OPENINIT); // tell everyone we are starting
					BroadcastToList (DRAWIT,CurrList);	 // draw the controls
					return(TRUE);

			default:
				return(FALSE);
		}

}


	/*\
	|*|----====< void SystemShutDown(); >====----
	|*|
	|*|    Exits to the swapper to exit for good, or loads a program.
	|*|
	\*/
static void SystemShutDown()
{
   //	MouseInit		( OFF,OFF );			// kill the mouse

   // restore the video

		PaintScreen   ( FALSE );			// take down the screen
		_videosetcurs ( OrigRow, OrigCol ); // restore the cursor position
		_videocshape  ( -1, -1);			// now, restore the original shape

	// restore numlock

		_dlgrestorenumlock();	// the below code is now in dialoga.asm
		//	_asm {
		//		push	es						; save the numlock state
		//		sub 	ax,ax
		//		mov 	es,ax
		//		mov 	al,NumLockState
		//		or		es:[0x417],al			; possibly set it
		//		pop 	es
		//	}
}


	/*\
	|*|----====< void SystemInit() >====----
	|*|
	|*| System wide initialization of the program
	|*|
	|*| Entry Conditions:
	|*| 	None
	|*|
	|*| Exit Conditions:
	|*| 	None
	|*|
	\*/
static int SystemInit()
{
int n;
long l;

    /* setup the mouse & move to the middle of the wave window          */

	//	MouseInit	(ON,OFF);
	//	MouseAction (0x12); 			// left button down/up action

		if (_videocard() == 1) {		// if mono, point to that segment
			VideoSegment = OurWnd.sseg = 0xb000;
			Colors = &MonoScheme;
		}

	// get the original row, column and kill numlock


		l = _videogetcurs();
		OrigCol = (int) (l >> 16) & 0xffff;
		OrigRow = (int) l & 0xffff;

		_dlgsavenumlock();		// the below code is now in dialoga.asm
		//	_asm {
		//		push	es							; save the numlock state
		//		sub 	ax,ax
		//		mov 	es,ax
		//		mov 	al,0x20
		//		and 	al,es:[0x417]
		//		and 	byte ptr es:[0x417],0xdf	; clear it...
		//		mov 	NumLockState,al
		//		pop 	es
		//	}

	// turn off the cursor

		_videocshape (0,0x20);			// make it invisible

	/* init all objects, then paint them on the screen					*/

		BroadcastMsg (OPENINIT);		// tell everyone we are starting

		PaintScreen  (TRUE);			// put up the screen

		BroadcastToList (DRAWIT,CurrList);// draw the controls

		(*CurrentObject->scr) (FOCUS_GIVEN,CurrentObject);

	// return good

		return (0);
}


	/*\
	|*|----====< int VolumeButtons() >====----
	|*|
	|*| Volume slide bar object control
	|*|
	|*| Entry Conditions:
	|*| 	None
	|*|
	|*| Exit Conditions:
	|*| 	None
	|*|
	\*/
static int VolumeButtons (msg,o,ptr)
	int msg;
	BPtr o;
	void *ptr;
{
void **pptr;
rect r;
int l,ri,i;
char c1,c2;

	// get a pointer to the stack

		pptr = &ptr;

    // process the message

        switch (msg) {

			case OPEVENT:

                switch (((EPtr)pptr[0])->buttons) {

					case ENTER:

						if (o->state)
							o->state = 0;
						else
							o->state = -1;

						SendVolume
						  (
							"SET ",
							0,
							(VPtr) o,
							BI_SETTO,
							o->state
						  );

                        (*o->scr) (DRAWIT,o);

						break;

					case ENDKEY:
					case HOMEKEY:
					case LFARROW:
					case PGDNKEY:
					case PGUPKEY:
					case RIARROW:

						// Direction keys can move these sliders

						if (o == &Loudness)
							(*BassVolume.scr)(msg,&BassVolume,ptr);

						if (o == &Enhanced)
							(*TrebVolume.scr)(msg,&TrebVolume,ptr);
						break;

					default:
                        break;
				}

				break;

			case SENDIT:

				SendVolume
				  (
					"SET ",
					0,
					(VPtr) o,
					BI_SETTO,
					o->state
				  );
				(*(void(*)())ptr)(&CommandString);

				break;

            case FOCUS_GIVEN:

				// highlight the entire field

                    r.row1 = o->namr.row1;
                    r.row2 = o->namr.row2;
					r.col2 = r.col1 = o->namr.col2+2;
					_videoattr ( Colors->TmpHi_AND,    Colors->TmpHi_XOR,	 &o->namr );
					_videoattr ( Colors->TmpHi_AND,    Colors->TmpHi_XOR,	 &r 	  );

				//	r.row1 = o->scrr.row1;
				//	r.row2 = o->scrr.row2;
				//	r.col2 = r.col1 = o->scrr.col2+2;
					_videoattr ( Colors->ScrTmpHi_AND, Colors->ScrTmpHi_XOR, &o->scrr );
				//	_videoattr ( Colors->ScrTmpHi_AND, Colors->ScrTmpHi_XOR, &r 	  );

					break;

			case FOCUS_TAKEN:

				// highlight the entire field

					r.row1 = o->namr.row1;
                    r.row2 = o->namr.row2;
					r.col2 = r.col1 = o->namr.col2+2;
					_videoattr ( Colors->TmpLo_AND,    Colors->TmpLo_XOR,	 &o->namr );
                    _videoattr ( Colors->ScrTmpLo_AND, Colors->ScrTmpLo_XOR, &r       );

				//	r.row1 = o->scrr.row1;
				//	r.row2 = o->scrr.row2;
				//	r.col2 = r.col1 = o->scrr.col2+2;
					_videoattr ( Colors->ScrTmpLo_AND, Colors->ScrTmpLo_XOR, &o->scrr );
				//	_videoattr ( Colors->ScrTmpLo_AND, Colors->ScrTmpLo_XOR, &r 	  );

					break;

			case DRAWIT:

				// move the cursor to the slide bar area

					_videosetcurs ( o->scrr.row1, o->scrr.col1 );
					if (o->state)
						_zipout   ( "X" );
					else
						_zipout   ( "ù" );

                break;

			case OPENINIT:

				// get the output mixer current state

					SendVolume ("GET ",0,(VPtr)o,0,0);

					DecodeVolumeSwitch (MVResponse);
					o->state = (VolumeSwitch == '+') ? -1 : 0;
					break;


			case CLEARIT:
			default:
				break;
		}
}


	/*\
	|*|----====< int UpdateTotalVolume() >====----
	|*|
	|*| update the screen if the user is typing the hot keys
	|*|
	|*| Entry Conditions:
	|*| 	None
	|*|
	|*| Exit Conditions:
	|*| 	None
	|*|
	\*/
static int UpdateTotalVolume()
{
int lv,rv;

static int delta;
static int mintic	= 18;
static int minrest	= 0;
//static int lasttime = 0;

	// get the clock tic, this whole routine is done once a second

		delta += _dlggettimedelta();	// the below code is now in dialoga.asm
		//	_asm {
		//		mov ah,0
		//		int 1Ah
		//		cmp lasttime,dx 	; ax = delta of current/last time
		//		mov lasttime,dx
		//		adc delta,0
		//	}

	// if the main dialog box is covered, we cannot draw, so just return

		if (DialogBox & EFFECTS_DLG)
			return(0);

    // if the change is greater than 1 second, update the screen now...

		if (delta > mintic) {

            delta    = 0;

			lv = LeftVolumeLevel.channel;
			rv = RitVolumeLevel.channel;

			(*LeftVolumeLevel.scr)(OPENINIT,&LeftVolumeLevel);

			if((lv != LeftVolumeLevel.channel) ||
			   (rv != RitVolumeLevel.channel)) {
					minrest = (1*19)/4; 		// restore speed after 1 second
					mintic	= 2;				// speed up polling
					(*LeftVolumeLevel.scr)(DRAWIT,&LeftVolumeLevel);
			}

			if (minrest)						// if fast poll
				if (!--minrest) 				// check to see if we should
					mintic = 18;				// slow it down.
		}
}


	/*\
	|*|----====< int VolumeLevels() >====----
	|*|
	|*| VOLUME LEFT/RIGHT control
	|*|
	|*| Entry Conditions:
	|*| 	None
	|*|
	|*| Exit Conditions:
	|*| 	None
	|*|
	\*/
static int VolumeLevels(msg,o,ptr)
	int msg;
	VPtr o;
	void *ptr;
{
void **pptr;
rect r;
int l,ri,i;
char c1,c2;

#define VOLE_RAMPUP 0x4001		// ramp the volume up
#define VOLE_RAMPDN 0x4002		// ramp the volume down

	// get a pointer to the stack

		pptr = &ptr;

    // process the message

        switch (msg) {

			case OPEVENT:

				switch (((EPtr)pptr[0])->buttons) {

					case ENDKEY:
						VolumeLevels (VOLE_RAMPUP,&RitVolumeLevel,ptr);
						break;

					case HOMEKEY:
						VolumeLevels (VOLE_RAMPUP,&LeftVolumeLevel,ptr);
						break;

					case LFARROW:
                        VolumeLevels (VOLE_RAMPUP,&LeftVolumeLevel,ptr);
						VolumeLevels (VOLE_RAMPUP,&RitVolumeLevel,ptr);
						break;

					case PGDNKEY:
						VolumeLevels (VOLE_RAMPDN,&RitVolumeLevel,ptr);
						break;

					case PGUPKEY:
						VolumeLevels (VOLE_RAMPDN,&LeftVolumeLevel,ptr);
                        break;

					case RIARROW:
                        VolumeLevels (VOLE_RAMPDN,&LeftVolumeLevel,ptr);
                        VolumeLevels (VOLE_RAMPDN,&RitVolumeLevel,ptr);
                        break;

					default:
						break;
				}

				break;

			case SENDIT:

				SendVolume					// set the left
				  (
					"SET ",
					1,
					&LeftVolumeLevel,
					BI_SETTO,
					LeftVolumeLevel.channel
				  );
                (*(void(*)())ptr)(&CommandString);

				SendVolume					// set the right
				  (
					"SET ",
					2,
					&RitVolumeLevel,
					BI_SETTO,
					RitVolumeLevel.channel
				  );

				(*(void(*)())ptr)(&CommandString);

				break;

			case FOCUS_GIVEN:

				// highlight the entire field

                    r.row1 = o->namr.row1;
                    r.row2 = o->namr.row2;
					r.col2 = r.col1 = o->namr.col2+2;
					_videoattr ( Colors->TmpHi_AND,    Colors->TmpHi_XOR,	 &o->namr );
					_videoattr ( Colors->TmpHi_AND,    Colors->TmpHi_XOR,	 &r 	  );

					r.row1 = o->scrr.row1;
					r.row2 = o->scrr.row2;
					r.col2 = r.col1 = o->scrr.col2+2;
					_videoattr ( Colors->ScrTmpHi_AND, Colors->ScrTmpHi_XOR, &o->scrr );
                    _videoattr ( Colors->ScrTmpHi_AND, Colors->ScrTmpHi_XOR, &r       );

				// let the right channel know too!

					if (o == &LeftVolumeLevel)
						(*RitVolumeLevel.scr)(msg,&RitVolumeLevel,ptr);

					break;

			case FOCUS_TAKEN:

				// highlight the entire field

					r.row1 = o->namr.row1;
                    r.row2 = o->namr.row2;
					r.col2 = r.col1 = o->namr.col2+2;
					_videoattr ( Colors->TmpLo_AND,    Colors->TmpLo_XOR,	 &o->namr );
                    _videoattr ( Colors->ScrTmpLo_AND, Colors->ScrTmpLo_XOR, &r       );

                    r.row1 = o->scrr.row1;
					r.row2 = o->scrr.row2;
					r.col2 = r.col1 = o->scrr.col2+2;
					_videoattr ( Colors->ScrTmpLo_AND, Colors->ScrTmpLo_XOR, &o->scrr );
					_videoattr ( Colors->ScrTmpLo_AND, Colors->ScrTmpLo_XOR, &r 	  );

				// let the right channel know too!

					if (o == &LeftVolumeLevel)
						(*RitVolumeLevel.scr)(msg,&RitVolumeLevel,ptr);

					break;

			case DRAWIT:

				// move the cursor to the slide bar area

					_videosetcurs ( o->scrr.row1, o->scrr.col1 );
					_zipout 	  ( twobar, 0);

					l = (o->channel * 31) / 100;
					_videosetcurs ( o->scrr.row1, o->scrr.col1+(l>>1) );
					_zipout (((l & 1) ? "Þ" : "Ý"), 0);

				// let the right channel know too!

					if (o == &LeftVolumeLevel)
						(*RitVolumeLevel.scr)(msg,&RitVolumeLevel,ptr);

                break;

			case OPENINIT:

				// get the output mixer current state

					SendVolume
					  (
						"GET ",
						((o == &LeftVolumeLevel) ? 1 : 2),
						o,
						0,
						0
					  );

					DecodeVolumeNumber (MVResponse);
					o->channel = VolumeNumber;

				// let the right channel know too!

					if (o == &LeftVolumeLevel)
						(*RitVolumeLevel.scr)(msg,&RitVolumeLevel,ptr);

                    break;

			case CLEARIT:
				break;

			case VOLE_RAMPUP:

				// send the volume up

					if (o->channel > 0)
						o->channel -= 4;

					SendVolume					// set left/both/right
					  (
						"SET ",
                        ((o == &LeftVolumeLevel) ? 1 : 2),
						o,
						BI_SETTO,
						o->channel
					);

					(*o->scr) (DRAWIT,o);		// redraw it

					break;

			case VOLE_RAMPDN:

				// send the volume down

					if (o->channel < 100)
						o->channel += 4;

					SendVolume
					  (
						"SET ",
						((o == &LeftVolumeLevel) ? 1 : 2),
						o,
						BI_SETTO,
						o->channel
					  );

					(*o->scr) (DRAWIT,o);

					break;

            default:
				break;
		}
}

	/*\
	|*| end DIALOG.C
	\*/

clrbuf(char *buf, int size)
{
	int s;

	for (s= 0; s < size; s++)
		*buf++= '\0';
}

/* filename is catted onto driver pathname, and the "s." is changed to "#." */
/* where "#" is from 0 to 3, allowing 4 different mixer settings to be saved. */

char *filename= "settings.pas";

/* miscellaneous words used with mvproas */

#define WORD_GET 0
#define WORD_MIXER 1
#define WORD_TO 2
#define WORD_ON 3
#define WORD_OFF 4
#define WORD_LEVEL 5
#define WORD_PERCENT 6

char *words[]= 	{"GET ", "MIXER ", "TO ", "ON ", "OFF ", "LEVEL ", " PERCENT ", ""};

/* these words are used to retrieve/save the mixer settings */

char *direct[]= 	{"INPUT ", 	"OUTPUT ", 	""};
char *output[]= 	{"PCM ", 	"MIXER "};
char *channel[]= 	{"LEFT ", 	"RIGHT ", 	""};
char *source[]= 	{"FM ", 		"INT ", "EXT ", "SPEAKER ", "MIC ", "" };
char *levels[]= 	{"BASS ", 	"TREBLE ", 	"ENHANCED ", ""};
char *buttons[]= 	{"REALSOUND ", ""};
char *crossch[]= 	{"CROSSCHANNEL ", ""};
char *volume[]= 	{"VOLUME ", ""};
char *onoff[]= 	{"-", "+", ""};

/* getdriverpath() - use int 2F function BC0B to get path to mvsound.sys */
/* return !0 and fill "pathname" buffer with path to driver if successful */
/* return 0 if failed */

getdriverpath(char far *pathname)
{
	int status;
	char far *p= pathname;

	_asm
		{
		mov ax, 0BC0Bh
		int 2Fh

		xor ax, 'M' SHL 8 + 'V'
		mov status, ax
		jnz sorry

		push ds
		push es


		mov di, word ptr p[2]
		mov es, di
		mov di, word ptr p[0]
		mov ds, dx
		mov si, bx

		mov cx, 79
again:
		lodsb
		stosb
		or al, al
		jz done
		loop again

		jmp short done

done:
		pop es
		pop ds

sorry:
		}

	return(!status);
}

/* evaluate the "##   +/-   ##   +/-" string */
/* the first "## +/-" pair is the left channel, the second is the right */
/* set up four pointers to point to each of the four elements */
/* passed in is "channel", either 'L' or 'R', and "onoroff", either "+" or "-" */
/* if the L/R "onoroff" matches the passed "onoroff", return the L/R val */

char *isonoroff(char channel, char onoroff)
{
	char *lfvalue, *lfonoff, *rtvalue, *rtonoff;
	char *val, *oo;

	/* MVResponse= "LeftVal   LeftOnOff   RightVal   RightOnOff" */
	lfvalue= MVResponse;
	while (isspace(*lfvalue)) lfvalue++;

	lfonoff= lfvalue;
	while (!isspace(*lfonoff)) lfonoff++;
	while (isspace(*lfonoff)) lfonoff++;

	rtvalue= lfonoff;
	while (!isspace(*rtvalue)) rtvalue++;
	while (isspace(*rtvalue)) rtvalue++;

	rtonoff= rtvalue;
	while (!isspace(*rtonoff)) rtonoff++;
	while (isspace(*rtonoff)) rtonoff++;

	/* select val and oo according to Left or Right channel */
	switch (channel)
		{
		case 'l':
		case 'L': val= lfvalue; oo= lfonoff; break;
		case 'r':
		case 'R': val= rtvalue; oo= rtonoff; break;
		default: val= NULL; oo= NULL; break;
		}

	if (*oo == onoroff)
		return(val);

	return(NULL);

}

/* put an ascii number for "num" into the character left of the "." */
/* from the end of the string, search backwards to the first "." */
/* if not at the start of the string, stuff the ascii number */

putnuminname(char *pathname, int num)
{
	int i= strlen(pathname);
	while (i && pathname[i] != '.')
		i--;
	if (i)
		pathname[--i]= num+ '0';
}

/* get the driver path, stuff the number into the name, and create the file */
/* save the master L/R volume, the "dead" then the "live" mixer settings */
/* the bass, treble and enhanced status, the realsound setting, and the */
/* crosschannel settings */
/* These are saved as strings in a DOS file suitable for "cat FILE > mvproas" */
/* which is what the "loadcurrent()" function does */
/* The filename is "X:\driver\path\setting#.pas", where "#" is 0-3, allowing */
/* four different settings to be maintained */

#define USEMIXERGET 1
#if USEMIXERGET
FILE *fout;

subsavecurrent(char *cmdstring)
{
	if (*cmdstring)
		{
		fprintf(fout, "%s\n", cmdstring);
		}

	return(0);
}

savecurrent(int num)
{
	char pathname[128];

	if (!getdriverpath(pathname))
		return(0);

	if (strlen(pathname) > 3)
		strcat(pathname, "\\");
	strcat(pathname, filename);
	putnuminname(pathname, num);

	fout= fopen(pathname, "w");
	if (fout == NULL)
  		return(0);

	MixerGetSettings(subsavecurrent);

	fclose(fout);

	return(0);
}

#else

savecurrent(int num)
{
	int o, d, c, s;
	int okay;
	FILE *fout;
	char pathname[128];
	char cmdstring[128];


	if (!getdriverpath(pathname))
		return(0);

	if (strlen(pathname) > 3)
		strcat(pathname, "\\");
	strcat(pathname, filename);
	putnuminname(pathname, num);

	fout= fopen(pathname, "w");
	if (fout == NULL)
  		return(0);

	/* get LEFT/RIGHT VOLUME */
	for (c= 0; *channel[c]; c++)
		{
		strcpy(cmdstring, words[WORD_GET]);
		strcat(cmdstring, volume[0]);
		strcat(cmdstring, channel[c]);
		strcat(cmdstring, words[WORD_LEVEL]);
		clrbuf(MVResponse, 80);
		SendTextOut(cmdstring);
		fprintf(fout, "S%s%s%s%s\n", &cmdstring[1], words[WORD_TO], MVResponse, words[WORD_PERCENT]);
		}

	/* get OFF/ON, INPUT/OUTPUT and LEFT/RIGHT and FM/INT/EXT/SPEAKER/MIC */
	for (o= 0; *onoff[o]; o++)
		for (d= 0; *direct[d]; d++)
			for (c= 0; *channel[c]; c++)
				for (s= 0; *source[s]; s++)
					{
					char *val;

					strcpy(cmdstring, words[WORD_GET]);		/* GET */
					strcat(cmdstring, direct[d]);				/* INPUT/OUTPUT */
					strcat(cmdstring, words[WORD_MIXER]);	/* MIXER */
					strcat(cmdstring, channel[c]);			/* LEFT/RIGHT */
					strcat(cmdstring, source[s]);				/* FM/INT/EXT/SPEAKER/MIC */
					clrbuf(MVResponse, 80);						
					SendTextOut(cmdstring);

					/* if is ON and want ON or is OFF and want OFF, print */
					if (val= isonoroff(channel[c][0], *onoff[o]))
						{
						fprintf(fout, "S%s%s", &cmdstring[1], words[WORD_TO]);
						while (!isspace(*val))
							{
							fputc(*val, fout);
							val++;
							}
						fprintf(fout, "%s\n", words[WORD_PERCENT]);
						}
					}

	/* get ON/OFF, INPUT/OUTPUT for PCM/INPUT */
	for (o= 0; *onoff[o]; o++)
		for (d= 0; *direct[d]; d++)
			for (c= 0; *channel[c]; c++)
				{
				char *val;

				strcpy(cmdstring, words[WORD_GET]);
				strcat(cmdstring, direct[d]);
				strcat(cmdstring, words[WORD_MIXER]);
				strcat(cmdstring, channel[c]);
				strcat(cmdstring, output[d]);
				clrbuf(MVResponse, 80);
				SendTextOut(cmdstring);

				if (val= isonoroff(channel[c][0], *onoff[o]))
					{
					fprintf(fout, "S%s%s", &cmdstring[1], words[WORD_TO]);
					while (!isspace(*val))
						{
						fputc(*val, fout);
						val++;
						}
					fprintf(fout, "%s\n", words[WORD_PERCENT]);
					}
				}

	/* get BASS/TREBLE/ENHANCED */
	for (c= 0; *levels[c]; c++)
		{
		strcpy(cmdstring, words[WORD_GET]);
		strcat(cmdstring, volume[0]);
		strcat(cmdstring, levels[c]);
		clrbuf(MVResponse, 80);
		SendTextOut(cmdstring);
		fprintf(fout, "S%s%s", &cmdstring[1], words[WORD_TO]);
		switch (*MVResponse)
			{
			case '+': fprintf(fout, "%s\n", words[WORD_ON]); break;
			case '-': fprintf(fout, "%s\n", words[WORD_OFF]); break;
			default: fprintf(fout, "%s%s\n", MVResponse, words[WORD_PERCENT]);
			}
		}

	/* get REALSOUND */
	for (c= 0; *buttons[c]; c++)
		{
		strcpy(cmdstring, words[WORD_GET]);
		strcat(cmdstring, buttons[c]);
		clrbuf(MVResponse, 80);
		SendTextOut(cmdstring);
		fprintf(fout, "S%s%s", &cmdstring[1], words[WORD_TO]);
		switch(*MVResponse)
			{
			case '+': fprintf(fout, "%s\n", words[WORD_ON]); break;
			case '-': fprintf(fout, "%s\n", words[WORD_OFF]); break;
			}
		}

	strcpy(cmdstring, words[WORD_GET]);
	strcat(cmdstring, crossch[0]);
	clrbuf(MVResponse, 80);
	SendTextOut(cmdstring);
		{
		int s= 0;
		int t= 0;
		int i= 0;

		for (s= 0; *channel[s]; s++)
			for (t= 0; *channel[t]; t++)
				{
				fprintf(fout, "S%s%s%s%s%s", &cmdstring[1], channel[s], words[WORD_TO], channel[t], words[WORD_TO]);
				while (isspace(MVResponse[i])) i++;
				switch (MVResponse[i++])
					{
					case '+': fprintf(fout, "%s\n", words[WORD_ON]); break;
					case '-': fprintf(fout, "%s\n", words[WORD_OFF]); break;
					}
				}
		}

	fclose(fout);

}

#endif

/* load setting #num from file "setting#.pas" */
/* open the file, read each line and send it to mvproas */
/* if the file does not exist, save the current settings and then proceed */
/* The file contains mvproas strings, so "cat setting#.pas > mvproas" works */

loadcurrent(int num)
{
	int okay;
	FILE *finp;
	char pathname[128];
	char cmdstring[128];

	if (!getdriverpath(pathname))
		return(0);

	if (strlen(pathname) > 3)
		strcat(pathname, "\\");
	strcat(pathname, filename);
	putnuminname(pathname, num);

	finp= fopen(pathname, "r");
	if (finp == NULL)
		{
		savecurrent(num);
		finp= fopen(pathname, "r");
		if (finp == NULL)
			return(0);
		}

	while ((fgets(cmdstring, 127, finp)) != NULL)
		SendTextOut(cmdstring);

	fclose(finp);

}

/*The GET commands and parameters are:
       GET {LEFT or RIGHT} [FM     ]
       GET {LEFT or RIGHT} [PCM    ]
       GET {LEFT or RIGHT} [INT    ]
       GET {LEFT or RIGHT} [EXT    ]
       GET {LEFT or RIGHT} [SPEAKER]
       GET {LEFT or RIGHT} [MIC    ]
       GET [BASS]                   
       GET [TREBLE]                 
       GET {LEFT or RIGHT} [VOLUME ]
       GET [MUTE]
       GET [ENHANCED]
       GET [REALSOUND]
       GET [CROSSCHANNEL]
The following demonstrates several ways to GET volume, button, or mixer levels:
       GET FM
       GET VOLUME
       GET MUTE
       GET CROSSCHANNEL
       GET REAL
*/

