/***************************************************************************
*	NAME:  MIXER.C $Revision: 1.43 $
**	COPYRIGHT:
**	"Copyright (c) 1994,1995 by e-Tek Labs"
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
* $Log: mixer.c $
* Revision 1.43  1996/01/25 12:33:53  mleibow
* Revision 1.42  1995/12/04 08:54:27  sdsmith
* Revision 1.41  1995/12/04 08:52:06  sdsmith
* Fixed major gain slider drift
* Revision 1.40  1995/11/20 14:10:32  sdsmith
* Kernel and Windows init changes
* Revision 1.39  1995/11/02 13:28:48  mks
* Removed MASTER attribute from Mono In
* Revision 1.38  1995/10/30 16:02:55  sdsmith
* Fixes for Gain slider levels
* Revision 1.37  1995/10/26 14:59:34  mleibow
* Added mutes for software mixer controls.
* Revision 1.36  1995/10/26 12:04:14  sdsmith
* Changes for Voyetra cont.
* Revision 1.35  1995/10/24 15:34:54  unknown
* Changes to voyetra
* Revision 1.34  1995/10/24 11:29:49  sdsmith
* Fixes for Windows 95 compatability tests
* Revision 1.33  1995/10/17 11:40:59  sdsmith
* Fixed low end volume setting
* Revision 1.32  1995/10/16 14:42:53  sdsmith
* Rearranged nodes so Stereo Output is first
* Revision 1.31  1995/10/13 17:21:03  mleibow
* Added effects mixer slider.
* Added code to make it easier to change mixer node map in future.
* Added code to make master volumes change at 10dB drop offs instead of 6dB.
* Revision 1.30  1995/09/21 11:13:23  sdsmith
* Node map changes
* Revision 1.29  1995/08/09 17:02:34  sdsmith
* Added support for removing gains
* Revision 1.28  1995/07/27 17:22:41  mleibow
* Changed mls_getstring calls to not reopen the mls file every time a string
* is read in.  (Used to cause IWINIT to thrash)
* Revision 1.27  1995/07/21 14:56:32  sdsmith
* Fixed gain settings
* Revision 1.26  1995/07/07 11:14:05  sdsmith
* FullInit fixes for software volumes
* Revision 1.25  1995/07/06 10:51:01  sdsmith
* Fixed defaults
* Revision 1.24  1995/06/28 08:47:08  sdsmith
* Added support for removing nodes based on module inclusion
* Revision 1.23  1995/06/23 11:17:17  sdsmith
* Fixed mute inits
* ,
* Revision 1.22  1995/06/22 08:30:49  sdsmith
* Added master volume query functions
* Revision 1.21  1995/06/21 10:18:57  sdsmith
* Fixed right loopback channel
* Revision 1.20  1995/06/08 11:54:48  mleibow
* Added multiple language support for mixer labels.
* Revision 1.19  1995/05/30 16:27:51  sdsmith
* Mixer node name changes
* Revision 1.18  1995/05/29 17:34:07  sdsmith
* Various bug fixes
* Revision 1.17  1995/05/24 23:44:10  sdsmith
* Fixed wave client volume vs. wave master volume
* Revision 1.16  1995/05/17 01:50:00  sdsmith
* Fixed CODEC DAC volume interaction with digital audio master
* Revision 1.15  1995/05/12 02:16:36  sdsmith
* Added semaphore code
* Revision 1.14  1995/05/11 01:59:49  sdsmith
* Added defines for master volume functionc
* Revision 1.13  1995/05/08 21:28:39  sdsmith
* Fixed DAC volume
* Revision 1.12  1995/05/03 14:21:51  unknown
* Fixed MONO Input volume
* Revision 1.11  1995/04/28 12:17:33  sdsmith
* Added intermediate node for Aux 1
* Revision 1.10  1995/04/26 16:22:33  sdsmith
* Fixed the public function commentary
* Revision 1.9  1995/04/20 15:52:12  mleibow
* Changed >= 0 to != -1 for compilers which treat chars as unsigned numbers
* Revision 1.8  1995/04/19 17:40:15  sdsmith
* Added default init string support
* Revision 1.7  1995/04/19 14:20:51  sdsmith
* Fixed node map and register map
* Revision 1.5  1995/04/17 12:56:08  mleibow
* Added iw_midi_master_volume() & volume query code
* Revision 1.4  1995/04/14 09:19:48  sdsmith
* Added support for B0 silicon
* Revision 1.3  1995/03/30 09:47:50  sdsmith
* New mixer fixes
* Revision 1.2  1995/03/27 07:58:56  sdsmith
* New mixer node stuff
* Revision 1.1  1995/02/23 11:07:49  unknown
* Initial revision
*
* This file contains the hardware specific code for the InterWave CODEC.
***************************************************************************/
#include <stdlib.h>
#include <dos.h>
#include <string.h>

#include "iw.h"
#include "iwl.h"
#include "globals.h"
#include "digital.h"
#include "codec.h"

#define IW_MIX_DB_TABLE_SIZE 64
#define IWL_MIXER_NAME_MAX 64
#define MIXER_NO_GAIN

#define NODEID_DIGITAL 100
#define NODEID_DIGMUS  101
#define NODEID_MIDI    102
#define NODEID_EFFECTS 103

char default_setting[IWL_MIXER_NAME_MAX];
char init_setting[IWL_MIXER_INIT_MAX];
short sleft, sright;
int full_init = 0;
int MAX_CODEC_NODES=0;

struct iw_mix_register_map iw_mix_register_map[] = {
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{MONO_IO_CTRL, -1, 0x40, 6},
	{LEFT_MASTER_OUTPUT, RIGHT_MASTER_OUTPUT, 0x80, 7},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{LEFT_ADC_INPUT, RIGHT_ADC_INPUT, 0xF, 0},
	{LEFT_MASTER_OUTPUT, RIGHT_MASTER_OUTPUT, 0x1F, 0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{LEFT_ADC_INPUT, RIGHT_ADC_INPUT, 0xC0, 6},
	{-1,-1,0,0},
	{LEFT_DAC_OUTPUT, RIGHT_DAC_OUTPUT, 0x80, 7},
	{LEFT_LINE_INPUT, RIGHT_LINE_INPUT, 0x80, 7},
	{LEFT_MIC_INPUT, RIGHT_MIC_INPUT, 0x80, 7},
	{LEFT_AUX1_INPUT, RIGHT_AUX1_INPUT, 0x80, 7},
	{LEFT_AUX2_INPUT, RIGHT_AUX2_INPUT, 0x80, 7},
	{MONO_IO_CTRL, -1, 0x80, 7},
	{LEFT_DAC_OUTPUT, RIGHT_DAC_OUTPUT, 0x3f, 0},
	{LEFT_LINE_INPUT, RIGHT_LINE_INPUT, 0x1f, 0},
	{LEFT_MIC_INPUT, RIGHT_MIC_INPUT, 0x1f, 0},
	{LEFT_AUX1_INPUT, RIGHT_AUX1_INPUT, 0x1f, 0},
	{LEFT_AUX2_INPUT, RIGHT_AUX2_INPUT, 0x1f, 0},
	{MONO_IO_CTRL, -1, 0x0f, 0},
	{-1,-1,0,0},
	{LOOPBACK, -1, 0x1, 0},
	{LOOPBACK, -1, 0xFC, 2},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{DIG_AUDIO_MASTER, -1, 0, 0},
	{DIG_MUSIC_MASTER, -1, 0, 0},
	{MIDI_MASTER, -1, 0, 0},
	{-1,-1,0,0},
	{CONFIG_3, CONFIG_3, 0x02, 1},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{-1,-1,0,0},
	{EFFECTS_MASTER, -1, 0, 0},
	{EFFECTS_MUTE, -1, 0, 0},
	{DIG_AUDIO_MUTE, -1, 0, 0},
	{DIG_MUSIC_MUTE, -1, 0, 0},
	{MIDI_MUTE, -1, 0, 0}
#ifdef IW_MODULE_DIGITAL
	,{-1,-1,0,0}
#endif
#ifdef IW_MODULE_SOUND
	,{-1,-1,0,0}
#endif
#ifdef IW_MODULE_NOTE
	,{-1,-1,0,0}
#endif
#ifdef IW_MODULE_EFFECTS
	,{-1,-1,0,0}
#endif
};

#define MLS_MIXER_STRLEN	20

#define MIXMLS_RECORD_OUTPUT	(MLS_MIXER_BASE	+ 0)
#define MIXMLS_MONO_OUT		(MLS_MIXER_BASE + 2)
#define MIXMLS_STEREO_OUTPUT	(MLS_MIXER_BASE + 4)
#define MIXMLS_RECORD_GAIN	(MLS_MIXER_BASE + 6)
#define MIXMLS_LOOPBACK_MUTE	(MLS_MIXER_BASE + 8)
#define MIXMLS_LOOPBACK_ATTENUATION	(MLS_MIXER_BASE + 10)
#define MIXMLS_SYNTH_AUX1_MUX	(MLS_MIXER_BASE + 12)
#define MIXMLS_LINE_INPUT	(MLS_MIXER_BASE + 14)
#define MIXMLS_MIC_INPUT	(MLS_MIXER_BASE + 16)
#define MIXMLS_AUX1_INPUT	(MLS_MIXER_BASE + 18)
#define MIXMLS_AUX2_INPUT	(MLS_MIXER_BASE + 20)
#define MIXMLS_CODEC		(MLS_MIXER_BASE + 22)
#define MIXMLS_SYNTH_INPUT	(MLS_MIXER_BASE + 24)
#define MIXMLS_MONO_INPUT	(MLS_MIXER_BASE + 26)
#define MIXMLS_DIG_AUDIO	(MLS_MIXER_BASE + 28)
#define MIXMLS_DIG_MUSIC	(MLS_MIXER_BASE + 30)
#define MIXMLS_MIDI_SYNTH	(MLS_MIXER_BASE + 32)
#define MIXMLS_EFFECTS		(MLS_MIXER_BASE + 34)

static char mixer_strings[][MLS_MIXER_STRLEN] = {
"Recording Output",
"Record",
"Mono Output",
"Mono Out",
"Stereo Output",
"Master",
"Record Gain",
"Gain",
"Loopback Mute",
"Loopback",
"Loopback Attenuation",
"Loopback",
"Synth/Aux1 Input",
"Synth/Aux1",
"Line Input",
"Line",
"Mic Input",
"Mic",
"Aux1 Input",
"Aux 1",
"Aux2 Input",
"Aux 2",
"CODEC Playback DAC",
"Codec",
"Synth/Aux1",
"Synth/Aux1",
"Mono Input",
"Mono In",
"Digital Audio",
"Wave",
"Digital Music",
"MOD",
"Midi Synth",
"MIDI",
"Effects",
"Effects",
};

/***************************************************************************

LOCAL DEFINITION:
mixer_nodes - array of mixer nodes for kernel mixer support

DESCRIPTION:
mixer_nodes is an array of mixer nodes that supports the kernel mixer 
functions.

SEE ALSO:
iw_build_mixer
iw_mix_node
iw_mix_attn_gain_struct
iw_mix_struct
iw_mix_terminal_struct
*/
struct iw_mixer_node iw_mixer_nodes[] =
{
  {3,0,0,OUTPUT,LINE,1,1,IW_MIX_ATTR_MASTER|IW_MIX_ATTR_STEREO,{0,0,0,0},{0,0},MIXMLS_STEREO_OUTPUT,0,0},
  {2,0,0,OUTPUT,LINE,2,2,0,{0,0,0,0},{0,0},MIXMLS_MONO_OUT,0,0},
  {1,0,0,OUTPUT,ADC,3,1,IW_MIX_ATTR_MASTER|IW_MIX_ATTR_STEREO,{0,0,0,0},{0,0},MIXMLS_RECORD_OUTPUT,0,0},
  {4,2,2,CONTROL,SWITCH,1,1,0,{0,1,0,0},{0,0},0,0,0},
  {5,3,3,CONTROL,SWITCH,2,2,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0},
  {6,4,2,JUNCTION,MIXER,3,1,0,{0,0,0,0},{0,0},0,0,0},
  {7,1,0,JUNCTION,TEE,1,0,0,{0,0,0,0},{0,0},0,0,0},
  {7,28,0,JUNCTION,TEE,2,0,0,{0,0,0,0},{0,0},0,0,0},
  {8,5,0,JUNCTION,TEE,3,0,0,{0,0,0,0},{0,0},0,0,0},
  {8,6,0,JUNCTION,TEE,4,0,0,{0,0,0,0},{0,0},0,0,0},
  {9,7,1,CONTROL,LEVEL,4,1,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOG,{0,22,1,5},{0,0},MIXMLS_RECORD_GAIN,0,0},
  {10,8,3,CONTROL,LEVEL,5,2,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOG,{-46,0,-1,5},{0,0},0,0,0},
  {83,12,3,CONTROL,DUMMY,2,2,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0},
  {11,10,0,JUNCTION,TEE,5,0,0,{0,0,0,0},{0,0},0,0,0},
  {11,83,0,JUNCTION,TEE,6,0,0,{0,0,0,0},{0,0},0,0,0},
  {12,9,1,JUNCTION,MUX,6,1,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,3,0,0},{0,0},0,0,0},
  {13,11,3,JUNCTION,MIXER,7,2,0,{0,0,0,0},{0,0},0,0,0},
  {14,13,40,CONTROL,SWITCH,8,3,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0},
  {15,13,36,CONTROL,SWITCH,9,4,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0},
  {16,13,37,CONTROL,SWITCH,10,5,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0},
  {17,13,38,CONTROL,SWITCH,11,6,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0},
  {18,13,39,CONTROL,SWITCH,12,7,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0},
  {19,13,0,CONTROL,SWITCH,13,8,0,{0,1,0,0},{0,0},0,0,0},
  {20,14,40,CONTROL,LEVEL,14,3,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LINEAR,{0,127,1,0},{0,0},0,0,0},
#ifdef MIXER_NO_GAIN
  {21,15,36,CONTROL,LEVEL,15,4,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOG,{-34,0,-1,5},{0,0},0,0,0},
  {22,16,37,CONTROL,LEVEL,16,5,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOG,{-34,0,-1,5},{0,0},0,0,0},
  {23,17,38,CONTROL,LEVEL,17,6,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOG,{-34,0,-1,5},{0,0},0,0,0},
  {24,18,39,CONTROL,LEVEL,18,7,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOG,{-34,0,-1,5},{0,0},0,0,0},
#else
  {21,15,36,CONTROL,LEVEL,15,4,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOG,{-34,12,-1,5},{0,0},0,0,0},
  {22,16,37,CONTROL,LEVEL,16,5,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOG,{-34,12,-1,5},{0,0},0,0,0},
  {23,17,38,CONTROL,LEVEL,17,6,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOG,{-34,12,-1,5},{0,0},0,0,0},
  {24,18,39,CONTROL,LEVEL,18,7,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOG,{-34,12,-1,5},{0,0},0,0,0},
#endif
  {25,19,0,CONTROL,LEVEL,19,8,IW_MIX_ATTR_LOG,{-45,0,-3,0},{0,0},0,0,0},
  {26,20,40,JUNCTION,MIXER,20,3,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,0,0,0},{0,0},0,0,0},
  {27,26,1,CONTROL,SWITCH,21,9,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},MIXMLS_LOOPBACK_MUTE,0,0},
  {28,27,1,CONTROL,LEVEL,22,9,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR|IW_MIX_ATTR_LOG,{94,0,-1,5},{0,0},MIXMLS_LOOPBACK_ATTENUATION,0,0},
  {80,12,36,CONTROL,DUMMY,21,0,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0},
  {81,12,37,CONTROL,DUMMY,21,0,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0},
  {82,12,41,CONTROL,DUMMY,21,0,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0},
  {29,80,0,JUNCTION,TEE,7,0,0,{0,0,0,0},{0,0},0,0,0},
  {29,21,0,JUNCTION,TEE,8,0,0,{0,0,0,0},{0,0},0,0,0},
  {30,81,0,JUNCTION,TEE,9,0,0,{0,0,0,0},{0,0},0,0,0},
  {30,22,0,JUNCTION,TEE,10,0,0,{0,0,0,0},{0,0},0,0,0},
  {31,82,0,JUNCTION,TEE,11,0,0,{0,0,0,0},{0,0},0,0,0},
  {31,23,0,JUNCTION,TEE,12,0,0,{0,0,0,0},{0,0},0,0,0},
  {32,13,NODEID_DIGITAL,CONTROL,LEVEL,23,10,IW_MIX_ATTR_LINEAR,{0,127,1,0},{0,0},0,0,0},
  {33,13,NODEID_DIGMUS,CONTROL,LEVEL,24,11,IW_MIX_ATTR_LINEAR,{0,127,1,0},{0,0},0,0,0},
  {34,13,NODEID_MIDI,CONTROL,LEVEL,25,12,IW_MIX_ATTR_LINEAR,{0,127,1,0},{0,0},0,0,0},
  //{46,31,0,INPUT,AUX,11,1,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_INTERMEDIATE,{0,0,0,0},{0,0},MIXMLS_SYNTH_AUX1_MUX,0,0},
  //{35,46,46,JUNCTION,MUX,26,2,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0},
  {44,0,0,INPUT,AUX,11,1,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_INTERMEDIATE,{0,0,0,0},{0,0},MIXMLS_SYNTH_AUX1_MUX,0,0},
  {35,0,46,JUNCTION,MUX,26,2,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0},
  {36,29,0,INPUT,LINE,1,1,IW_MIX_ATTR_MASTER|IW_MIX_ATTR_STEREO,{0,0,0,0},{0,0},MIXMLS_LINE_INPUT,0,0},
  {37,30,0,INPUT,MIC,2,1,IW_MIX_ATTR_MASTER|IW_MIX_ATTR_STEREO,{0,0,0,0},{0,0},MIXMLS_MIC_INPUT,0,0},
  //{38,35,0,INPUT,LINE,3,2,IW_MIX_ATTR_STEREO,{0,0,0,0},{0,0},MIXMLS_AUX1_INPUT,0,0},
  {38,0,0,INPUT,LINE,3,2,IW_MIX_ATTR_STEREO,{0,0,0,0},{0,0},MIXMLS_AUX1_INPUT,0,0},
  {39,24,0,INPUT,AUX,4,2,IW_MIX_ATTR_MASTER|IW_MIX_ATTR_STEREO,{0,0,0,0},{0,0},MIXMLS_AUX2_INPUT,0,0},
  {40,20,0,INPUT,DAC,5,1,IW_MIX_ATTR_MASTER|IW_MIX_ATTR_STEREO,{0,0,0,0},{0,0},MIXMLS_CODEC,0,0},
  //{41,35,0,INPUT,SYNTH,6,1,IW_MIX_ATTR_STEREO,{0,0,0,0},{0,0},MIXMLS_SYNTH_INPUT,0,0},
  {41,31,0,INPUT,SYNTH,6,1,IW_MIX_ATTR_MASTER|IW_MIX_ATTR_STEREO,{0,0,0,0},{0,0},MIXMLS_SYNTH_INPUT,0,0},
  {42,25,0,INPUT,LINE,7,3,0,{0,0,0,0},{0,0},MIXMLS_MONO_INPUT,0,0},
  {43,13,NODEID_EFFECTS,CONTROL,LEVEL,26,13,IW_MIX_ATTR_LINEAR,{0,127,1,0},{0,0},0,0,0},
  {45,43,NODEID_EFFECTS,CONTROL,SWITCH,27,10,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0},
  {46,32,NODEID_DIGITAL,CONTROL,SWITCH,28,11,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0},
  {47,33,NODEID_DIGMUS,CONTROL,SWITCH,29,12,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0},
  {48,34,NODEID_MIDI,CONTROL,SWITCH,30,13,IW_MIX_ATTR_STEREO|IW_MIX_ATTR_LOCKLR,{0,1,0,0},{0,0},0,0,0}
#ifdef IW_MODULE_DIGITAL
  ,{NODEID_DIGITAL,46,0,INPUT,DAC,8,2,IW_MIX_ATTR_MASTER,{0,0,0,0},{0,0},MIXMLS_DIG_AUDIO,0,0}
#endif
#ifdef IW_MODULE_SOUND
  ,{NODEID_DIGMUS,47,0,INPUT,SYNTH,9,4,IW_MIX_ATTR_MASTER,{0,0,0,0},{0,0},MIXMLS_DIG_MUSIC,0,0}
#endif
#ifdef IW_MODULE_NOTE
  ,{NODEID_MIDI,48,0,INPUT,SYNTH,10,5,IW_MIX_ATTR_MASTER,{0,0,0,0},{0,0},MIXMLS_MIDI_SYNTH,0,0}
#endif
#ifdef IW_MODULE_EFFECTS
  ,{NODEID_EFFECTS,45,0,INPUT,EFFECTS,11,6,IW_MIX_ATTR_MASTER,{0,0,0,0},{0,0},MIXMLS_EFFECTS,0,0}
#endif
  ,{(unsigned)-1,(unsigned)-1,(unsigned)-1,(unsigned)-1,(unsigned)-1,(unsigned)-1,(unsigned)-1,(unsigned)-1,{-1,-1,-1,-1},{-1,-1},(unsigned)-1,0,0}
};
/* The following defines MUST be kept in sync with the node map manually */
#define ADC_MUX_LINE  36
#define ADC_MUX_MIC   37
#define ADC_MUX_AUX   41
#define ADC_MUX_MIXED 3
#define CFIG3I_MUX_AUX1  38
#define CFIG3I_MUX_SYNTH 41

/***************************************************************************

FUNCTION DEFINITION:
iwl_get_node_index - find a mixer node with the specified id

DESCRIPTION:
The mixer nodes are stored in a globally available array.  get_node_index 
searches the array to find the node with the id matching the caller's spec.

RETURNS: int - array index of node with id
               -1 if no node with id exists
*/
int iwl_get_node_index(
  int id) /* id of mixer node to search for */
{
	int i;

	for (i=0; i<MAX_CODEC_NODES; i++) {
		if (iw_mixer_nodes[i].id == (unsigned short)id)
			return(i);
	}
	return(-1);
}


int iw_mixer_db_table[IW_MIX_DB_TABLE_SIZE] = {
	0, -1, -3, -4, -6, -7, -9, -10, -12, -13, -15, -16, -18, -19, -21, -22,
	-24, -25, -27, -28, -30, -31, -33, -34, -36, -37, -39, -40, -42, -43, -45,
	-46, -48, -49, -51, -52, -54, -55, -57, -58, -60, -61, -63, -64, -66, -67,
	-69, -70, -72, -73, -75, -76, -78, -79, -81, -82, -84, -85, -87, -88, -90,
	-91, -93, -94
};

unsigned char iwl_db_to_reg(int register_id, int db)
{
	unsigned char rc = 0;
	int i;

	switch (register_id) {
		case LEFT_AUX1_INPUT:
		case RIGHT_AUX1_INPUT:
		case LEFT_AUX2_INPUT:
		case RIGHT_AUX2_INPUT:
		case LEFT_LINE_INPUT:
		case RIGHT_LINE_INPUT:
		case LEFT_MIC_INPUT:
		case RIGHT_MIC_INPUT:
#ifdef MIXER_NO_GAIN
			if (db > 0) db = 0;
#else
			db -= 12;
#endif
			for (i=0; i < IW_MIX_DB_TABLE_SIZE; i++) {
				if (db > iw_mixer_db_table[i])
					break;
			}
			if (i) i--;
#ifdef MIXER_NO_GAIN
			rc = (unsigned char)i & 0x1F;
			rc += 8;
			if (rc > 0x1f) rc = 0x1F;
#else
			rc = (unsigned char)i & 0x1F;
#endif
			break;
		case LEFT_ADC_INPUT:
		case RIGHT_ADC_INPUT:
			db *= -1;
			for (i=0; i < IW_MIX_DB_TABLE_SIZE; i++) {
				if (db > iw_mixer_db_table[i])
					break;
			}
			if (i) i--;
			rc = (unsigned char)i & 0xF;
			break;
		case MONO_IO_CTRL:
			for (i=0; i < IW_MIX_DB_TABLE_SIZE; i++) {
				if (db > iw_mixer_db_table[i])
					break;
			}
			if (i) i = (i - 1)/2;
			rc = (unsigned char)i & 0xF;
			break;
		case LOOPBACK:
			for (i=0; i < IW_MIX_DB_TABLE_SIZE; i++) {
				if (db > iw_mixer_db_table[i])
					break;
			}
			if (i) i--;
			rc = ((unsigned char)i & 0x3f) << 2;
			break;
		case LEFT_DAC_OUTPUT:
		case RIGHT_DAC_OUTPUT:
		case DIG_AUDIO_MASTER:
		case DIG_MUSIC_MASTER:
		case MIDI_MASTER:
		case EFFECTS_MASTER:
			for (i=0; i < IW_MIX_DB_TABLE_SIZE; i++) {
				if (db > iw_mixer_db_table[i])
					break;
			}
			if (i) i--;
			rc = (unsigned char)i & 0x3f;
			break;
		case LEFT_MASTER_OUTPUT:
		case RIGHT_MASTER_OUTPUT:
			for (i=0; i < IW_MIX_DB_TABLE_SIZE; i++) {
				if (db > iw_mixer_db_table[i])
					break;
			}
			if (i) i--;
			rc = (unsigned char)i & 0x1f;
			break;
		default:
			break;
	}
	return(rc);
}

/***************************************************************************

FUNCTION DEFINITION:
iwl_reg_to_db - convert a CODEC register setting to db units

DESCRIPTION:
This routine will convert a CODEC register stetting into a db value.

RETURNS: int - the db value that matches the register setting.
*/
int iwl_reg_to_db(int register_id, int reg_value)
{
	int rc = 0;

	switch (register_id) {
		case LEFT_AUX1_INPUT:
		case RIGHT_AUX1_INPUT:
		case LEFT_AUX2_INPUT:
		case RIGHT_AUX2_INPUT:
		case LEFT_LINE_INPUT:
		case RIGHT_LINE_INPUT:
		case LEFT_MIC_INPUT:
		case RIGHT_MIC_INPUT:
			reg_value &= 0x1F;
			rc = iw_mixer_db_table[reg_value] + 12;
			break;
		case LEFT_ADC_INPUT:
		case RIGHT_ADC_INPUT:
			reg_value &= 0xF;
			rc = iw_mixer_db_table[reg_value] * -1;
			break;
		case MONO_IO_CTRL:
			reg_value &= 0xF;
			rc = reg_value * -3;
			break;
		case LOOPBACK:
			reg_value = (reg_value & 0xFC) >> 2;
			rc = iw_mixer_db_table[reg_value];
			break;
		case LEFT_DAC_OUTPUT:
		case RIGHT_DAC_OUTPUT:
		case DIG_AUDIO_MASTER:
		case DIG_MUSIC_MASTER:
		case MIDI_MASTER:
		case EFFECTS_MASTER:
			reg_value &= 0x3f;
			rc = iw_mixer_db_table[reg_value];
			break;
		case LEFT_MASTER_OUTPUT:
		case RIGHT_MASTER_OUTPUT:
			reg_value &= 0x1f;
			rc = iw_mixer_db_table[reg_value];
			break;
		default:
			break;
	}
	return(rc);
}

/***************************************************************************

FUNCTION DEFINITION:
iwl_linear_to_reg - convert a linear value to a CODEC register setting

DESCRIPTION:
This routine will convert a linear value between 0 and 127 to a CODEC
mixer register value.  0 will represent the smallest register value
and 127 will represent the largest.  The value 63 will be converted to
the mixer value that represents -6db from the maximum (audio) value for that
register.

RETURNS: unsigned char - register setting for that value
*/
unsigned char iwl_linear_to_reg(
	int register_id,
	unsigned short value)
{
	int rc = 0;
	short lookup;

	if (register_id == LEFT_ADC_INPUT || register_id == RIGHT_ADC_INPUT) {
	    value = 127 - value;
	    if (value == 64) value = 63;
	}

	lookup = (short)min((unsigned long)4095, IWL_TO_FMM(iw_atten_tab[value]));
	lookup >>= 6;

	switch (register_id) {
		case LEFT_AUX1_INPUT:
		case RIGHT_AUX1_INPUT:
		case LEFT_AUX2_INPUT:
		case RIGHT_AUX2_INPUT:
		case LEFT_LINE_INPUT:
		case RIGHT_LINE_INPUT:
		case LEFT_MIC_INPUT:
		case RIGHT_MIC_INPUT:
#ifdef MIXER_NO_GAIN
			rc = min(lookup,0x1f);
			rc += 8;
			if (rc > 0x1f) rc = 0x1f;
#else
			rc = min(lookup,0x1f);
#endif
			break;
		case LEFT_MASTER_OUTPUT:
		case RIGHT_MASTER_OUTPUT:
			rc = min(lookup,0x1f);
			break;
		case LEFT_ADC_INPUT:
		case RIGHT_ADC_INPUT:
			rc = min((lookup & 0xf),0xf);
			break;
		case MONO_IO_CTRL:
			rc = min((lookup >> 1),0xf);
			break;
		case LOOPBACK:
			rc = (lookup & 0x3f) << 2;
			break;
		case LEFT_DAC_OUTPUT:
		case RIGHT_DAC_OUTPUT:
		case DIG_AUDIO_MASTER:
		case DIG_MUSIC_MASTER:
		case MIDI_MASTER:
		case EFFECTS_MASTER:
			rc = lookup & 0x3f;
			break;
		default:
			break;
	}
	return(rc);
}

/***************************************************************************

FUNCTION DEFINITION:
iwl_reg_to_linear - convert a CODEC mixer register setting to linear

DESCRIPTION:
This routine convert the value of a mixer register to a linear scale
between 0 and 127.  The maximum register value will be represented
as 127 and the minimum as 0.

RETURNS: int - linear value between 0 and 127
*/
int iwl_reg_to_linear(
	int register_id,
	unsigned short reg)
{
	int rc = 0;
	int i;

	switch (register_id) {
		case LEFT_AUX1_INPUT:
		case RIGHT_AUX1_INPUT:
		case LEFT_AUX2_INPUT:
		case RIGHT_AUX2_INPUT:
		case LEFT_LINE_INPUT:
		case RIGHT_LINE_INPUT:
		case LEFT_MIC_INPUT:
		case RIGHT_MIC_INPUT:
#ifdef MIXER_NO_GAIN
			rc = ((reg & 0x1f) - 8) << 6;
#else
			rc = (reg & 0x1f) << 6;
#endif
			break;
		case LEFT_MASTER_OUTPUT:
		case RIGHT_MASTER_OUTPUT:
			rc = (reg & 0x1f) << 6;
			break;
		case LEFT_ADC_INPUT:
		case RIGHT_ADC_INPUT:
			rc = (reg & 0xf) << 6;
			break;
		case MONO_IO_CTRL:
			rc = (reg & 0xf) << 7;
			break;
		case LOOPBACK:
			rc = ((reg >> 2) & 0x3f) << 6;
			break;
		case LEFT_DAC_OUTPUT:
		case RIGHT_DAC_OUTPUT:
		case DIG_AUDIO_MASTER:
		case DIG_MUSIC_MASTER:
		case MIDI_MASTER:
		case EFFECTS_MASTER:
			rc = (reg & 0x3f) << 6;
			break;
		default:
			break;
	}
	for (i=0; i<=IW_ATTENTABSIZE; i++) {
	    if (i == IW_ATTENTABSIZE) break;
	    if ((int)IWL_FROM_FMM(rc) >= iw_atten_tab[i]) break;
	}
	if (i)
		rc = i - 1;
	else
		rc = 0;
	if (rc < 0)
		rc = 0;
	if (rc > 127)
		rc = 127;
	if (register_id == LEFT_ADC_INPUT || register_id == RIGHT_ADC_INPUT) {
	    rc = 127 - rc;
	    if (rc == 64) rc = 63;
	}
	return(rc);
}

/* WARNING: the constants in this routine must be kept in sync with
 *          the mixer node map.
 */
int iwl_mux_to_reg(
	int register_id,
	int mux_setting)
{
	int rc;

	switch (register_id) {
		case LEFT_ADC_INPUT:
		case RIGHT_ADC_INPUT:
			switch (mux_setting) {
				case ADC_MUX_LINE:
					rc = 0;
					break;
				case ADC_MUX_MIC:
					rc = 2;
					break;
				case ADC_MUX_AUX:
					rc = 1;
					break;
				case ADC_MUX_MIXED:
					rc = 3;
					break;
				default:
					rc = 0;
					break;
			}
			break;
		case CONFIG_3:
			switch (mux_setting) {
				case CFIG3I_MUX_AUX1:
					rc = 0;
					break;
				case CFIG3I_MUX_SYNTH:
					rc = 1;
					break;
			}
			break;
		default:
			rc = 0;
			break;
	}
	return(rc);
}

/* WARNING: the constants in this routine must be kept in sync with
 *          the mixer node map.
 */
int iwl_reg_to_mux(
	int register_id,
	int reg)
{
	int rc;

	switch (register_id) {
		case LEFT_ADC_INPUT:
		case RIGHT_ADC_INPUT:
			reg = (reg >> 6) & 0x03;
			switch (reg) {
				case 0:
					rc = ADC_MUX_LINE;
					break;
				case 1:
					rc = ADC_MUX_AUX;
					break;
				case 2:
					rc = ADC_MUX_MIC;
					break;
				case 3:
					rc = ADC_MUX_MIXED;
					break;
				default:
					rc = 0;
					break;
			}
			break;
		case CONFIG_3:
			reg = (reg >> 1) & 0x01;
			switch (reg) {
				case 0:
					rc = CFIG3I_MUX_AUX1;
					break;
				case 1:
					rc = CFIG3I_MUX_SYNTH;
					break;
			}
			break;
		default:
			rc = 0;
			break;
	}
	return(rc);
}

/***************************************************************************

FUNCTION DEFINITION:
iwl_init_mixer_nodes - initialize data in the the mixer_nodes array

DESCRIPTION:
iwl_init_mixer_nodes initializes the codec nodes array to model the codec
mixer.  In cases where there are codec registers that correspond to one
on the codec mixer node, the current value os read from the register
and the appropriate conversion is done to initialize the description
structure in the mixer node.

RETURNS: void

*/
void iwl_init_mixer_nodes(void)
{
	int i, mls_file_open;
	char left_index, right_index;
	unsigned char left, right;
	unsigned short mlsid;
	char mixer_string[MLS_MIXER_STRLEN];

	mls_file_open = iw_mls_open("kernel.mls") == IW_OK;
	for (i=0; i<MAX_CODEC_NODES; i++) {
	    if ((mlsid=iw_mixer_nodes[i].mls_info) != 0) {
		if (mls_file_open) {
		    if (iw_mls_getstring(0, mlsid, mixer_string, MLS_MIXER_STRLEN) == IW_OK) {
			iwu_strcpy(mixer_strings[mlsid-MLS_MIXER_BASE], mixer_string);
		    }
		}
		iw_mixer_nodes[i].description = mixer_strings[mlsid-MLS_MIXER_BASE];
		if (mls_file_open) {
		    if (iw_mls_getstring(0, mlsid+1, mixer_string, MLS_MIXER_STRLEN) == IW_OK) {
			iwu_strcpy(mixer_strings[mlsid-MLS_MIXER_BASE+1], mixer_string);
		    }
		}
		iw_mixer_nodes[i].name = mixer_strings[mlsid-MLS_MIXER_BASE+1];
	    }
	}
	if (mls_file_open) iw_mls_close();
	for (i=0; i<MAX_CODEC_NODES; i++) {
		if (iw_mixer_nodes[i].type == CONTROL ||
			iw_mixer_nodes[i].type == JUNCTION) {
			left_index = iw_mix_register_map[i].left;
			if (left_index == DIG_AUDIO_MASTER) {
#ifdef IW_MODULE_DIGITAL
			        left = (unsigned char)iw_dig_get_master_vol();
				if (iw_mixer_nodes[i].attributes & IW_MIX_ATTR_LOG) {
					iw_mixer_nodes[i].current.stereo.left = iw_linear_to_db(left_index, left);
				}
				else iw_mixer_nodes[i].current.stereo.left = left;
#endif
			}
			else if (left_index == DIG_MUSIC_MASTER) {
#ifdef IW_MODULE_SOUND
			        left = (unsigned char)iw_sound_get_master_volume();
				if (iw_mixer_nodes[i].attributes & IW_MIX_ATTR_LOG) {
					iw_mixer_nodes[i].current.stereo.left = iw_linear_to_db(left_index, left);
				}
				else iw_mixer_nodes[i].current.stereo.left = left;
#endif
			}
			else if (left_index == MIDI_MASTER) {
#ifdef IW_MODULE_NOTE
			        left = (unsigned char)iw_midi_get_master_volume();
				if (iw_mixer_nodes[i].attributes & IW_MIX_ATTR_LOG) {
					iw_mixer_nodes[i].current.stereo.left = iw_linear_to_db(left_index, left);
				}
				else iw_mixer_nodes[i].current.stereo.left = left;
#endif
			}
			else if (left_index == EFFECTS_MASTER) {
#ifdef IW_MODULE_EFFECTS
			        left = (unsigned char)iw_effects_get_master_volume();
				if (iw_mixer_nodes[i].attributes & IW_MIX_ATTR_LOG) {
					iw_mixer_nodes[i].current.stereo.left = iw_linear_to_db(left_index, left);
				}
				else iw_mixer_nodes[i].current.stereo.left = left;
#endif
			}
			else if (left_index == LEFT_DAC_OUTPUT && iw_mixer_nodes[i].subtype == LEVEL) {
				iwl_dig_codec_get_master_vol(&sleft, &sright);
				left = (unsigned char)sleft;
				right = (unsigned char)sright;
				if (iw_mixer_nodes[i].attributes & IW_MIX_ATTR_LOG) {
					iw_mixer_nodes[i].current.stereo.left = iw_linear_to_db(left_index, left);
				}
				else iw_mixer_nodes[i].current.stereo.left = left;
				if (iw_mixer_nodes[i].attributes & IW_MIX_ATTR_LOG) {
					iw_mixer_nodes[i].current.stereo.right = iw_linear_to_db(left_index, right);
				}
				else iw_mixer_nodes[i].current.stereo.right = right;
			}
			else {
				if (left_index != (char)-1) {
					IWL_CODEC_IN(left_index, left);
					if (iw_mixer_nodes[i].attributes & IW_MIX_ATTR_LOG) {
						iw_mixer_nodes[i].current.stereo.left = iwl_reg_to_db(left_index, left);
					}
					else if (iw_mixer_nodes[i].attributes & IW_MIX_ATTR_LINEAR) {
						iw_mixer_nodes[i].current.stereo.left = iwl_reg_to_linear(left_index, left);
					}
					else {
						if (iw_mixer_nodes[i].subtype == MUX) {
							iw_mixer_nodes[i].current.stereo.left = iwl_reg_to_mux(left_index, left);
						}
						else {
							iw_mixer_nodes[i].current.stereo.left = (left & iw_mix_register_map[i].bit_mask) >> iw_mix_register_map[i].shift;
							if (left_index == LOOPBACK) iw_mixer_nodes[i].current.stereo.left = iw_mixer_nodes[i].current.stereo.left?0:1;
						}
					}
				}
				if (iw_mixer_nodes[i].attributes & IW_MIX_ATTR_STEREO) {
					if (iw_mixer_nodes[i].attributes & IW_MIX_ATTR_LOCKLR) {
						iw_mixer_nodes[i].current.stereo.right = iw_mixer_nodes[i].current.stereo.left;
					}
					else {
						right_index = iw_mix_register_map[i].right;
						if (right_index != (char)-1) {
							IWL_CODEC_IN(right_index, right);
							if (iw_mixer_nodes[i].attributes & IW_MIX_ATTR_LOG) {
								iw_mixer_nodes[i].current.stereo.right = iwl_reg_to_db(right_index, right);
							}
							else if (iw_mixer_nodes[i].attributes & IW_MIX_ATTR_LINEAR) {
								iw_mixer_nodes[i].current.stereo.right = iwl_reg_to_linear(right_index, right);
							}
							else {
								if (iw_mixer_nodes[i].subtype == MUX) {
									iw_mixer_nodes[i].current.stereo.right = iwl_reg_to_mux(right_index, right);
								}
								else {
									iw_mixer_nodes[i].current.stereo.right = (right & iw_mix_register_map[i].bit_mask) >> iw_mix_register_map[i].shift;
								}
							}
						}
					}
				}
			}
		}
	}
}

/***************************************************************************

FUNCTION DEFINITION:
iwl_mixer_set_mux - set multiplexer on codec mixer

DESCRIPTION:
*/
void iwl_mixer_set_mux(
	int index,
	int left_value,
	int right_value)
{
	char left_index = iw_mix_register_map[index].left;
	char right_index = iw_mix_register_map[index].right;
	unsigned char shift = iw_mix_register_map[index].shift;
	unsigned char mask = iw_mix_register_map[index].bit_mask;
	unsigned char reg, left, right;

	ENTER;
	if (left_index != (char)-1) {
		left = iwl_mux_to_reg(left_index, left_value);
		IWL_CODEC_IN(left_index, reg);
		reg = (reg & ~mask) | ((left << shift) & mask);
		IWL_CODEC_OUT(left_index, reg);
		iw_mixer_nodes[index].current.stereo.left = left_value;
	}
	if (right_index != (char)-1) {
		right = iwl_mux_to_reg(right_index, right_value);
		IWL_CODEC_IN(right_index, reg);
		reg = (reg & ~mask) | ((right << shift) & mask);
		IWL_CODEC_OUT(right_index, reg);
		iw_mixer_nodes[index].current.stereo.right = right_value;
	}
	LEAVE;
}

/***************************************************************************

FUNCTION DEFINITION:
iwl_mixer_set_mute - set mute on codec mixer

DESCRIPTION:
*/
void iwl_mixer_set_mute(
	int index,
	int left_value,
	int right_value)
{
	char left_index = iw_mix_register_map[index].left;
	char right_index = iw_mix_register_map[index].right;
	unsigned char shift = iw_mix_register_map[index].shift;
	unsigned char mask, reg;

	ENTER;
	mask = 1 << shift;
	switch (left_index) {
		case DIG_AUDIO_MUTE:
                    if (iwl_flags & F_IW_OS_LOADED) {
#ifdef IW_MODULE_DIGITAL
			iw_dig_mute(left_value);
#endif
			iw_mixer_nodes[index].current.stereo.left = left_value;
			iw_mixer_nodes[index].current.stereo.right = right_value;
			right_index = left_index = (char)-1;
		    }
			break;
		case DIG_MUSIC_MUTE:
                    if (iwl_flags & F_IW_OS_LOADED) {
#ifdef IW_MODULE_SOUND
			iw_sound_mute(left_value);
#endif
			iw_mixer_nodes[index].current.stereo.left = left_value;
			iw_mixer_nodes[index].current.stereo.right = right_value;
			right_index = left_index = (char)-1;
		    }
			break;
		case MIDI_MUTE:
                    if (iwl_flags & F_IW_OS_LOADED) {
#ifdef IW_MODULE_NOTE
			iw_midi_mute(left_value);
#endif
			iw_mixer_nodes[index].current.stereo.left = left_value;
			iw_mixer_nodes[index].current.stereo.right = right_value;
			right_index = left_index = (char)-1;
		    }
			break;
		case EFFECTS_MUTE:
                    if (iwl_flags & F_IW_OS_LOADED) {
#ifdef IW_MODULE_EFFECTS
			iw_effects_mute(left_value);
#endif
			iw_mixer_nodes[index].current.stereo.left = left_value;
			iw_mixer_nodes[index].current.stereo.right = right_value;
			right_index = left_index = (char)-1;
		    }
			break;
	}
	if (left_index != (char)-1) {
		IWL_CODEC_IN(left_index, reg);
		if (left_index != LOOPBACK) {
			if (left_value)
				reg |= mask;
			else
				reg &= ~mask;
		}
		else {
			if (left_value)
				reg &= ~mask;
			else
				reg |= mask;
		}
		IWL_CODEC_OUT(left_index, reg);
		iw_mixer_nodes[index].current.stereo.left = left_value;
	}
	if ((iw_mixer_nodes[index].attributes & IW_MIX_ATTR_STEREO) &&
		right_index != (char)-1) {
		IWL_CODEC_IN(right_index, reg);
		if (right_value)
			reg |= mask;
		else
			reg &= ~mask;
		IWL_CODEC_OUT(right_index, reg);
		iw_mixer_nodes[index].current.stereo.right = right_value;
	}
	LEAVE;
}

/***************************************************************************

FUNCTION DEFINITION:
iwl_mixer_set_level - set attenuator on codec mixer

DESCRIPTION:
*/
void iwl_mixer_set_level(
	int index,
	int left_value,
	int right_value)
{
	char left_index = iw_mix_register_map[index].left;
	char right_index = iw_mix_register_map[index].right;
	unsigned char bit_mask = iw_mix_register_map[index].bit_mask;
	unsigned char reg,left,right;

	if (left_index != (char)-1) {
		switch (left_index) {
			case DIG_AUDIO_MASTER:
			    if (iwl_flags & F_IW_OS_LOADED) {
#ifdef IW_MODULE_DIGITAL
				iw_dig_master_vol(left_value);
#endif
				iw_mixer_nodes[index].current.stereo.left = left_value;
				iw_mixer_nodes[index].current.stereo.right = right_value;
				left_index = (char)-1;
			    }
				break;
			case DIG_MUSIC_MASTER:
			    if (iwl_flags & F_IW_OS_LOADED) {
#ifdef IW_MODULE_SOUND
				iw_sound_master_volume(left_value);
#endif
				iw_mixer_nodes[index].current.stereo.left = left_value;
				iw_mixer_nodes[index].current.stereo.right = right_value;
				left_index = (char)-1;
			    }
				break;
			case MIDI_MASTER:
			    if (iwl_flags & F_IW_OS_LOADED) {
#ifdef IW_MODULE_NOTE
				iw_midi_master_volume(left_value);
#endif
				iw_mixer_nodes[index].current.stereo.left = left_value;
				iw_mixer_nodes[index].current.stereo.right = right_value;
				left_index = (char)-1;
			    }
				break;
			case EFFECTS_MASTER:
			    if (iwl_flags & F_IW_OS_LOADED) {
#ifdef IW_MODULE_EFFECTS
				iw_effects_master_volume(left_value);
#endif
				iw_mixer_nodes[index].current.stereo.left = left_value;
				iw_mixer_nodes[index].current.stereo.right = right_value;
				left_index = (char)-1;
			    }
				break;
			case LOOPBACK:
			case MONO_IO_CTRL:
			case LEFT_ADC_INPUT:
			case LEFT_MIC_INPUT:
			case LEFT_LINE_INPUT:
			case LEFT_AUX1_INPUT:
			case LEFT_AUX2_INPUT:
			case LEFT_MASTER_OUTPUT:
			case LEFT_DAC_OUTPUT:
				break;
			default:
				left_index = (char)-1;
				right_index = (char)-1;
				break;
		}
		if (left_index != (char)-1) {
			if (left_index == LEFT_DAC_OUTPUT && (iwl_flags & F_IW_OS_LOADED)) {
				iwl_dig_codec_master_vol(left_value, right_value, 1);
			}
			else if (full_init) {
				if (iw_mixer_nodes[index].attributes & IW_MIX_ATTR_LINEAR) {
					left = iwl_linear_to_reg(left_index, left_value);
				}
				else if (iw_mixer_nodes[index].attributes & IW_MIX_ATTR_LOG) {
					left = iwl_db_to_reg(left_index, left_value);
				}
				ENTER;
				IWL_CODEC_IN(left_index, reg);
				reg = (reg & ~bit_mask) | left;
				IWL_CODEC_OUT(left_index, reg);
				LEAVE;
			}
			iw_mixer_nodes[index].current.stereo.left = left_value;
		}
		if ((iw_mixer_nodes[index].attributes & IW_MIX_ATTR_STEREO) &&
			right_index != (char)-1) {
			if (right_index != RIGHT_DAC_OUTPUT && full_init) {
				if (iw_mixer_nodes[index].attributes & IW_MIX_ATTR_LINEAR) {
					right = iwl_linear_to_reg(right_index, right_value);
				}
				else if (iw_mixer_nodes[index].attributes & IW_MIX_ATTR_LOG) {
					right = iwl_db_to_reg(right_index, right_value);
				}
				ENTER;
				IWL_CODEC_IN(right_index, reg);
				reg = (reg & ~bit_mask) | right;
				IWL_CODEC_OUT(right_index, reg);
				LEAVE;
			}
			iw_mixer_nodes[index].current.stereo.right = right_value;
		}
	}
}

/***************************************************************************

FUNCTION DEFINITION:
iw_linear_to_db - converts a linear value to a db value

DESCRIPTION:
converts a linear value between 0 and 127 to a db value that is within
the range of the node specified by the id parameter.

PARAMETERS:
	id - mixer node id used to convert value
	value - a value in the linear range of 0 - 127

RETURNS: int - db value
*/
int iw_linear_to_db(short id, int value)
{
	int index;
	int register_id;
	unsigned char reg;


	index = iwl_get_node_index(id);
	register_id = iw_mix_register_map[index].left;
	reg = iwl_linear_to_reg(register_id, value);
	return(iwl_reg_to_db(register_id, reg));
}

/***************************************************************************

FUNCTION DEFINITION:
iw_db_to_linear - converts a db value to a linear value

DESCRIPTION:
converts a db value in the range of the node specified by the id
parameter into a linear value between 0 and 127.

PARAMETERS:
	id - mixer node id used to convert value
	value - a db value in the range specified by id

RETURNS: int - linear value
*/
int iw_db_to_linear(short id, int value)
{
	int index;
	int register_id;
	unsigned char reg;

	index = iwl_get_node_index(id);
	register_id = iw_mix_register_map[index].left;
	reg = iwl_db_to_reg(register_id, value);
	return(iwl_reg_to_linear(register_id, reg));
}

#ifdef IW_MODULE_EFFECTS
void iwl_mixer_remove_effects_node(void)
{
	struct iw_mixer_node *mn;
	int i;

	// wipe out effects node from mixer if effects not activated
	for (i=0, mn=iw_mixer_nodes; i < MAX_CODEC_NODES && mn->id != NODEID_EFFECTS; i++, mn++) ;
	if (i != MAX_CODEC_NODES) mn->id = (unsigned short)-2;
}
#endif

/***************************************************************************

FUNCTION DEFINITION:
iw_init_mixer - set up pointers to mixer support code and data

DESCRIPTION:
This routine will initialize the mixer settings based on the FullInit
field of the cfg structure passed in by the caller.  If the FullInit
field is non-zero, this routine searches for the default= line in
the mixer settings section of iw.ini.  If this is not found the CODEC
is initialized to transparent mode.  If the default= line exists, look
for the entry equal to the value of the default= line.  If that entry
does not exist initialize the CODEC to transparent.  If the default
entry does exists, those settings are passed to iw_restore_mixer_settings.

RETURNS: int
			IW_OK - mixer settings initialized properly
			IW_ILLEGAL - bad match inside init string
			IW_CODEC_VERSION_MISMATCH - the codec version at the beginning
				of the mixer init string did not match that returned by
				from the CODEC.

PARAMETERS:
	cfg - a pointer to a load_kernel_cfg structure.

SEE ALSO:
iw_mix_set_control
iw_mix_get_control
*/
int iw_init_mixer(
  struct load_kernel_cfg RFAR *cfg)
{
	int rc = IW_OK;
	int i;
	struct iw_mixer_node *mn;

	if (iwl_codec_base) {
			// count the number of nodes in mixer array
			for (i=0, mn=iw_mixer_nodes; mn->id != (USHORT)-1; i++, mn++) ;
			MAX_CODEC_NODES = i;
			// initialize mixer
	        full_init = cfg->FullInit;
	        iwl_codec_data = iwl_codec_base + 1;
			iwu_get_profile_string("mixer settings","default","None",default_setting,IWL_MIXER_NAME_MAX,os_getenv("INTERWAVE"));
			if (strcmp(default_setting, "None") != 0) {
				iwu_get_profile_string("mixer settings",default_setting,"None",init_setting,IWL_MIXER_INIT_MAX,os_getenv("INTERWAVE"));
				if (strcmp(init_setting, "None") != 0) {
					rc = iw_restore_mixer_settings(init_setting);
					if (rc == IW_ILLEGAL) {
						iwl_codec_transparent();
						rc = IW_OK;
					}
					else if (rc != IW_OK)
						return(rc);
				}
				else iwl_codec_transparent();
			}
			else iwl_codec_transparent();
		iwl_init_mixer_nodes();
	}
	else
		rc = IW_CODEC_NOT_FOUND;

	return(rc);
}
